Majorly reworked Javascript runtime stuff to use on-demand loading.
authorDan
Tue, 24 Jun 2008 23:37:23 -0400
changeset 582 a38876c0793c
parent 581 5e8fd89c02ea
child 583 c97d5f0d6636
Majorly reworked Javascript runtime stuff to use on-demand loading. - Runtime reduced to only AJAX library + very common functions, ~50K total - Almost all specific functionality loaded on demand using synchronous XHR - Crypto functions consolidated into crypto.js - Much testing still to be done - ACL editor known not working under firefox - Some other components (autofill, theme/rank managers) not ported yet
includes/clientside/jsres.php
includes/clientside/static/acl.js
includes/clientside/static/admin-menu.js
includes/clientside/static/ajax.js
includes/clientside/static/autocomplete.js
includes/clientside/static/autofill.js
includes/clientside/static/base64.js
includes/clientside/static/comments.js
includes/clientside/static/crypto.js
includes/clientside/static/diffiehellman.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/enanomath.js
includes/clientside/static/expander.js
includes/clientside/static/fadefilter.js
includes/clientside/static/fat.js
includes/clientside/static/functions.js
includes/clientside/static/json.js
includes/clientside/static/l10n.js
includes/clientside/static/libbigint.js
includes/clientside/static/loader.js
includes/clientside/static/login.js
includes/clientside/static/md5.js
includes/clientside/static/messagebox.js
includes/clientside/static/misc.js
includes/clientside/static/paginate.js
includes/clientside/static/rijndael.js
includes/clientside/static/sha256.js
includes/clientside/static/sliders.js
includes/clientside/static/template-compiler.js
includes/clientside/static/tinymce-init.js
includes/clientside/static/toolbar.js
includes/clientside/tinymce/tiny_mce_gzip.js
includes/clientside/tinymce/tiny_mce_gzip_src.js
includes/functions.php
includes/pageutils.php
includes/template.php
index.php
language/english/core.json
language/english/user.json
plugins/SpecialAdmin.php
plugins/admin/PluginManager.php
--- a/includes/clientside/jsres.php	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/jsres.php	Tue Jun 24 23:37:23 2008 -0400
@@ -14,7 +14,7 @@
  */
 
 // Disable for IE, it causes problems.
-if ( strstr(@$_SERVER['HTTP_USER_AGENT'], 'MSIE') )
+if ( ( strstr(@$_SERVER['HTTP_USER_AGENT'], 'MSIE') /*|| true*/ ) && !isset($_GET['early']) )
 {
   header('HTTP/1.1 302 Redirect');
   header('Location: static/enano-lib-basic.js');
@@ -54,12 +54,10 @@
 // Files safe to run full (aggressive) compression on
 $full_compress_safe = array(
   // Sorted by file size, descending (du -b *.js | sort -n)
-  // 'SpryData.js',
-  // 'SpryEffects.js',
   'ajax.js',
   'libbigint.js',
   'editor.js',
-  // 'SpryAutoSuggest.js',
+  'functions.js',
   'login.js',
   'acl.js',
   'misc.js',
--- a/includes/clientside/static/acl.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/acl.js	Tue Jun 24 23:37:23 2008 -0400
@@ -4,17 +4,15 @@
 var aclPermList = false;
 var aclDataCache = false;
 
-// Can be set to true by slow themes (St. Patty)
-if ( typeof(pref_disable_js_fx) != 'boolean' )
-{
-  var pref_disable_js_fx = false;
-}
-var aclDisableTransitionFX = ( is_firefox2 || pref_disable_js_fx ) ? true : false;
-
 function ajaxOpenACLManager(page_id, namespace)
 {
   if(IE)
     return true;
+  load_component('messagebox');
+  load_component('fadefilter');
+  load_component('template-compiler');
+  load_component('l10n');
+  
   if(!page_id || !namespace)
   {
     var data = strToPageID(title);
@@ -1063,18 +1061,3 @@
     keys.push(i);
   return keys;
 }
-
-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;
-}
-
--- a/includes/clientside/static/admin-menu.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/admin-menu.js	Tue Jun 24 23:37:23 2008 -0400
@@ -212,15 +212,14 @@
 	return this.o_root.a_tpl['icon_' + ((this.n_depth ? 0 : 32) + (this.a_children.length ? 16 : 0) + (this.a_children.length && this.b_opened ? 8 : 0) + (!b_junction && this.o_root.o_selected == this ? 4 : 0) + (b_junction ? 2 : 0) + (b_junction && this.is_last() ? 1 : 0))];
 }
 
-function addslashes (text) {
-  text = text.replace(/\\/g, '\\\\');
-  text = text.replace(/"/g, '\\"');
-  return text;
-}
-
 var trees = [];
 get_element = document.all ?
 	function (s_id) { return document.all[s_id] } :
 	function (s_id) { return document.getElementById(s_id) };
 
-
+function addslashes(text)
+{
+  text = text.replace(/\\/g, '\\\\');
+  text = text.replace(/"/g, '\\"');
+  return text;
+}
--- a/includes/clientside/static/ajax.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/ajax.js	Tue Jun 24 23:37:23 2008 -0400
@@ -2,187 +2,7 @@
  * AJAX applets
  */
  
-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;
-}
-
-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();
-  }
-  ajax = ajaxMakeXHR();
-  if ( !ajax )
-  {
-    console.error('ajaxMakeXHR() failed');
-    return false;
-  }
-  ajax.onreadystatechange = f;
-  ajax.open('GET', uri, true);
-  ajax.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );
-  ajax.send(null);
-}
-
-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();
-  }
-  ajax = ajaxMakeXHR();
-  if ( !ajax )
-  {
-    console.error('ajaxMakeXHR() failed');
-    return false;
-  }
-  ajax.onreadystatechange = f;
-  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);
-  }
-  ajax.setRequestHeader("Connection", "close");
-  ajax.send(parms);
-}
-
-/**
- * Show a friendly error message depicting an AJAX response that is not valid JSON
- * @param string Response text
- * @param string Custom error message. If omitted, the default will be shown.
- */
-
-function handle_invalid_json(response, customerror)
-{
-  var mainwin = $dynano('ajaxEditContainer').object;
-  mainwin.innerHTML = '';
-  
-  // Title
-  var h3 = document.createElement('h3');
-  h3.appendChild(document.createTextNode('The site encountered an error while processing your request.'));
-  mainwin.appendChild(h3);
-  
-  if ( typeof(customerror) == 'string' )
-  {
-    var el = document.createElement('p');
-    el.appendChild(document.createTextNode(customerror));
-    mainwin.appendChild(el);
-  }
-  else
-  {
-    customerror  = 'We unexpectedly received the following response from the server. The response should have been in the JSON ';
-    customerror += 'serialization format, but the response wasn\'t composed only of the JSON response. There are three possible triggers ';
-    customerror += 'for this problem:';
-    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');
-    li1.appendChild(document.createTextNode('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.'));
-    var osc_exception = ( window.location.hostname == 'demo.opensourcecms.com' ) ? ' This is KNOWN to be the case with the OpenSourceCMS.com demo version of Enano.' : '';
-    li2.appendChild(document.createTextNode('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.' + osc_exception));
-    li3.appendChild(document.createTextNode('It\'s possible that Enano triggered a PHP error or warning. In this case, you may be looking at a bug in Enano.'));
-      
-    ul.appendChild(li1);
-    ul.appendChild(li2);
-    ul.appendChild(li3);
-    mainwin.appendChild(ul);
-  }
-  
-  var p2 = document.createElement('p');
-  p2.appendChild(document.createTextNode('The response received from the server is as follows:'));
-  mainwin.appendChild(p2);
-  
-  var pre = document.createElement('pre');
-  pre.appendChild(document.createTextNode(response));
-  mainwin.appendChild(pre);
-  
-  var p3 = document.createElement('p');
-  p3.appendChild(document.createTextNode('You may also choose to view the response as HTML. '));
-  var a = document.createElement('a');
-  a.appendChild(document.createTextNode('View as HTML...'));
-  a._resp = response;
-  a.id = 'invalidjson_link';
-  a.onclick = function()
-  {
-    var mb = new MessageBox(MB_YESNO | MB_ICONEXCLAMATION, 'Do you really want to view this response as HTML?', 'If the response was changed during transmission to include malicious code, you may be allowing that malicious code to run by viewing the response as HTML. Only do this if you have reviewed the response text and have found no suspicious code in it.');
-    mb.onclick['Yes'] = function()
-    {
-      var html = $dynano('invalidjson_link').object._resp;
-      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(html);
-    }
-    return false;
-  }
-  a.href = '#';
-  p3.appendChild(a);
-  mainwin.appendChild(p3);
-}
-
-function ajaxEscape(text)
-{
-  /*
-  text = escape(text);
-  text = text.replace(/\+/g, '%2B', text);
-  */
-  text = window.encodeURIComponent(text);
-  return text;
-}
-
-function ajaxAltEscape(text)
-{
-  text = escape(text);
-  text = text.replace(/\+/g, '%2B', text);
-  return text;
-}
-
-function ajaxDiscard()
-{
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  c = confirm($lang.get('editor_msg_discard_confirm'));
-  if(!c) return;
-  ajaxReset();
-}
-
-function ajaxReset()
+window.ajaxReset = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
@@ -207,10 +27,12 @@
 
 // Miscellaneous AJAX applets
 
-function ajaxProtect(l) {
+window.ajaxProtect = function(l) {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
+  load_component('l10n');
+  
   if(shift) {
     r = 'NO_REASON';
   } else {
@@ -243,13 +65,15 @@
   }, true);
 }
 
-function ajaxRename()
+window.ajaxRename = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
   
   // updated - 1.1.4 to use miniPrompt
+  load_component('l10n');
+  load_component('messagebox');
   miniPrompt(ajaxRenameConstructDialog);
 }
 
@@ -319,7 +143,7 @@
     }, 200);
 }
 
-function ajaxRenameSubmit(obj)
+window.ajaxRenameSubmit = function(obj)
 {
   var box = miniPromptGetParent(obj);
   if ( !box )
@@ -372,7 +196,7 @@
     }, true);
 }
 
-function ajaxRenameDoClientTransform(newname)
+window.ajaxRenameDoClientTransform = function(newname)
 {
   var obj = document.getElementById('h2PageName');
   if ( obj )
@@ -382,25 +206,12 @@
   document.title = newname;
 }
 
-function ajaxMakePage()
+window.ajaxDeletePage = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
-  setAjaxLoading();
-  ajaxPost(ENANO_SPECIAL_CREATEPAGE, ENANO_CREATEPAGE_PARAMS, function() {
-    if ( ajax.readyState == 4 && ajax.status == 200 ) {
-      unsetAjaxLoading();
-      window.location.reload();
-    }
-  });
-}
-
-function ajaxDeletePage()
-{
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
+  load_component('l10n');
   var reason = prompt($lang.get('ajax_delete_prompt_reason'));
   if ( !reason || reason == '' )
   {
@@ -421,8 +232,11 @@
   });
 }
 
-function ajaxDelVote()
+window.ajaxDelVote = function()
 {
+  load_component('l10n');
+  load_component('messagebox');
+  
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
@@ -459,8 +273,11 @@
     });
 }
 
-function ajaxResetDelVotes()
+window.ajaxResetDelVotes = function()
 {
+  load_component('l10n');
+  load_component('messagebox');
+  
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
@@ -503,31 +320,11 @@
     });
 }
 
-function ajaxSetWikiMode(val) {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  setAjaxLoading();
-  document.getElementById('wikibtn_0').style.textDecoration = 'none';
-  document.getElementById('wikibtn_1').style.textDecoration = 'none';
-  document.getElementById('wikibtn_2').style.textDecoration = 'none';
-  document.getElementById('wikibtn_'+val).style.textDecoration = 'underline';
-  ajaxGet(stdAjaxPrefix+'&_mode=setwikimode&mode='+val, function() {
-    if ( ajax.readyState == 4 && ajax.status == 200 ) {
-      unsetAjaxLoading();
-      if(ajax.responseText!='GOOD')
-      {
-        alert(ajax.responseText);
-      }
-    }
-  });
-}
-
 // Editing/saving category information
 // This was not easy to write, I hope enjoy it, and dang I swear I'm gonna
 // find someone to work on just the Javascript part of Enano...
 
-function ajaxCatEdit()
+window.ajaxCatEdit = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
@@ -542,7 +339,7 @@
   });
 }
 
-function ajaxCatSave()
+window.ajaxCatSave = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
@@ -572,7 +369,7 @@
 
 // History stuff
 
-function ajaxHistory()
+window.ajaxHistory = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
@@ -590,7 +387,7 @@
   });
 }
 
-function ajaxHistView(oldid, ttl) {
+window.ajaxHistView = function(oldid, ttl) {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
@@ -605,7 +402,7 @@
   });
 }
 
-function ajaxRollback(id) {
+window.ajaxRollback = function(id) {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
@@ -641,12 +438,15 @@
   });
 }
 
-function ajaxClearLogs()
+window.ajaxClearLogs = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
   
+  load_component('l10n');
+  load_component('messagebox');
+  
   miniPromptMessage({
       title: $lang.get('ajax_clearlogs_confirm_title'),
       message: $lang.get('ajax_clearlogs_confirm_body'),
@@ -681,9 +481,7 @@
     });
 }
 
-var timelist;
-
-function buildDiffList()
+window.buildDiffList = function()
 {
   arrDiff1Buttons = getElementsByClassName(document, 'input', 'clsDiff1Radio');
   arrDiff2Buttons = getElementsByClassName(document, 'input', 'clsDiff2Radio');
@@ -704,7 +502,7 @@
   }
 }
 
-function selectDiff1Button(obj)
+window.selectDiff1Button = function(obj)
 {
   var this_time = obj.id.substr(6);
   var index = parseInt(in_array(this_time, timelist));
@@ -721,7 +519,7 @@
   }
 }
 
-function selectDiff2Button(obj)
+window.selectDiff2Button = function(obj)
 {
   var this_time = obj.id.substr(6);
   var index = parseInt(in_array(this_time, timelist));
@@ -738,7 +536,7 @@
   }
 }
 
-function ajaxHistDiff()
+window.ajaxHistDiff = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
@@ -767,11 +565,14 @@
 
 // Change the user's preferred style/theme
 
-function ajaxChangeStyle()
+window.ajaxChangeStyle = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
+  load_component('l10n');
+  load_component('messagebox');
+  
   var inner_html = '';
   inner_html += '<p><label>' + $lang.get('ajax_changestyle_lbl_theme') + ' ';
   inner_html += '  <select id="chtheme_sel_theme" onchange="ajaxGetStyles(this.value);">';
@@ -783,7 +584,7 @@
   chtheme_mb.onbeforeclick['OK'] = ajaxChangeStyleComplete;
 }
 
-function ajaxGetStyles(id)
+window.ajaxGetStyles = function(id)
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
@@ -844,7 +645,7 @@
     }, true);
 }
 
-function ajaxChangeStyleComplete()
+window.ajaxChangeStyleComplete = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
@@ -892,17 +693,7 @@
   
 }
 
-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;
-}
-
-function ajaxSwapCSS()
+window.ajaxSwapCSS = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
@@ -919,11 +710,12 @@
   menuOff();
 }
 
-function ajaxSetPassword()
+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()
@@ -936,103 +728,7 @@
     }, true);
 }
 
-function ajaxStartLogin()
-{
-  ajaxLogonToMember();
-}
-
-function ajaxStartAdminLogin()
-{
-  // 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;
-}
-
-function ajaxAdminPage()
-{
-  // 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;
-}
-
-var navto_ns;
-var navto_pg;
-var navto_ul;
-
-function ajaxLoginNavTo(namespace, page_id, min_level)
-{
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  navto_pg = page_id;
-  navto_ns = namespace;
-  navto_ul = min_level;
-  if ( auth_level < min_level )
-  {
-    ajaxPromptAdminAuth(function(k) {
-      ENANO_SID = k;
-      auth_level = navto_ul;
-      var loc = makeUrlNS(navto_ns, navto_pg);
-      if ( (ENANO_SID + ' ').length > 1 )
-        window.location = loc;
-    }, min_level);
-    return false;
-  }
-  var loc = makeUrlNS(navto_ns, navto_pg);
-  window.location = loc;
-}
-
-function ajaxAdminUser(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;
-}
-
-function ajaxDisableEmbeddedPHP()
+window.ajaxDisableEmbeddedPHP = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
@@ -1073,7 +769,7 @@
 
 var catHTMLBuf = false;
 
-function ajaxCatToTag()
+window.ajaxCatToTag = function()
 {
   if ( KILL_SWITCH )
     return false;
@@ -1135,7 +831,7 @@
 
 var addtag_open = false;
 
-function ajaxAddTagStage1()
+window.ajaxAddTagStage1 = function()
 {
   if ( addtag_open )
     return false;
@@ -1167,7 +863,7 @@
 
 var addtag_nukeme = false;
 
-function ajaxAddTagStage2(tag, nukeme)
+window.ajaxAddTagStage2 = function(tag, nukeme)
 {
   if ( !addtag_open )
     return false;
@@ -1233,7 +929,7 @@
     });
 }
 
-function ajaxDeleteTag(parentobj, tag_id)
+window.ajaxDeleteTag = function(parentobj, tag_id)
 {
   var arrDelete = [ parentobj, parentobj.previousSibling, parentobj.previousSibling.previousSibling ];
   var parent = parentobj.parentNode;
@@ -1277,7 +973,7 @@
     });
 }
 
-function ajaxTagToCat()
+window.ajaxTagToCat = function()
 {
   if ( !catHTMLBuf )
     return false;
@@ -1294,14 +990,14 @@
 
 var keepalive_interval = false;
 
-function ajaxPingServer()
+window.ajaxPingServer = function()
 {
   ajaxGet(stdAjaxPrefix + '&_mode=ping', function()
     {
     });
 }
 
-function ajaxToggleKeepalive()
+window.ajaxToggleKeepalive = function()
 {
   if ( readCookie('admin_keepalive') == '1' )
   {
@@ -1340,12 +1036,12 @@
   }
 };
 
-function aboutKeepAlive()
+window.aboutKeepAlive = function()
 {
   new MessageBox(MB_OK|MB_ICONINFORMATION, $lang.get('user_keepalive_info_title'), $lang.get('user_keepalive_info_body'));
 }
 
-function ajaxShowCaptcha(code)
+window.ajaxShowCaptcha = function(code)
 {
   var mydiv = document.createElement('div');
   mydiv.style.backgroundColor = '#FFFFFF';
@@ -1376,7 +1072,7 @@
   body.appendChild(mydiv);
 }
 
-function ajaxUpdateCheck(targetelement)
+window.ajaxUpdateCheck = function(targetelement)
 {
   if ( !document.getElementById(targetelement) )
   {
@@ -1547,7 +1243,7 @@
     });
 }
 
-function ajaxPluginAction(action, plugin_filename, btnobj)
+window.ajaxPluginAction = function(action, plugin_filename, btnobj)
 {
   // if installing, uninstalling, or re-importing, confirm
   if ( action == 'install' || action == 'uninstall' || action == 'reimport' )
@@ -1662,3 +1358,50 @@
       }
     });
 }
+
+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() {
+      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);
+        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();
+      }
+    });
+}
+
+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;
+}
--- a/includes/clientside/static/autocomplete.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/autocomplete.js	Tue Jun 24 23:37:23 2008 -0400
@@ -8,21 +8,4 @@
 // Replaced with Spry-based mechanism.
 //
 
-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;
-  }
-}
 
--- a/includes/clientside/static/autofill.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/autofill.js	Tue Jun 24 23:37:23 2008 -0400
@@ -8,7 +8,7 @@
 
 // default, generic schema
 autofill_schemas.generic = {
-  template: '<div id="--ID--_region" spry:region="autofill_region_--ID--" class="tblholder">' + "\n" +
+  template: '<div id="--ID--_region" spry:region="autofill_ds_--CLASS--" class="tblholder">' + "\n" +
             '  <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" +
             '    <tr spry:repeat="autofill_region_--ID--">' + "\n" +
             '      <td class="row1" spry:suggest="{name}">{name}</td>' + "\n" +
@@ -18,23 +18,30 @@
   
   init: function(element, fillclass)
   {
+    // calculate positions before spry f***s everything up
+    var top = $dynano(element).Top() + $dynano(element).Height() - 10; // tblholder has 10px top margin
+    var left = $dynano(element).Left();
+    
+    // dataset name
+    var ds_name = 'autofill_ds_' + fillclass;
+    
+    var allow_anon = ( params.allow_anon ) ? '1' : '0';
     // setup the dataset
-    window["autofill_region_" + element.id] = new Spry.Data.JSONDataSet(makeUrlNS('Special', 'Autofill', 'type=' + fillclass));
+    window[ds_name] = new Spry.Data.JSONDataSet(makeUrlNS('Special', 'Autofill', 'type=' + fillclass + '&allow_anon' + allow_anon));
     
     // inject our HTML wrapper
-    var template = this.template.replace(new RegExp('--ID--', 'g'), element.id);
+    var template = this.template.replace(new RegExp('--ID--', 'g'), element.id).replace(new RegExp('--CLASS--', 'g', fillclass));
     var wrapper = element.parentNode; // document.createElement('div');
     wrapper.id = 'autofill_wrap_' + element.id;
     
     // a bunch of hacks to add a spry wrapper
-    // var el2 = element.cloneNode(false);
-    // wrapper.appendChild(el2);
-    wrapper.innerHTML += template;
-    // insertAfter(element.parentNode, wrapper, element);
-    // element.parentNode.removeChild(element);
-    // element = el2;
+    wrapper.innerHTML = template + wrapper.innerHTML;
     
-    var autosuggest = new Spry.Widget.AutoSuggest("autofill_wrap_" + element.id, element.id + '_region', window["autofill_region_" + element.id], 'name', {loadFromServer: true, urlParam: 'userinput', hoverSuggestClass: 'row2', minCharsType: 3});
+    var autosuggest = new Spry.Widget.AutoSuggest("autofill_wrap_" + element.id, element.id + '_region', window[ds_name], 'name', {loadFromServer: true, urlParam: 'userinput', hoverSuggestClass: 'row2', minCharsType: 3});
+    var regiondiv = document.getElementById(element.id + '_region');
+    regiondiv.style.position = 'absolute';
+    regiondiv.style.top = top + 'px';
+    regiondiv.style.left = left + 'px';
   }
 };
 
@@ -43,6 +50,9 @@
   if ( element.parentNode.id.match(/^autofill_wrap_/) )
     return false;
   
+  if ( !Spry.Data );
+    load_spry_data();
+  
   params = params || {};
   // assign an ID if it doesn't have one yet
   if ( !element.id )
@@ -68,8 +78,13 @@
 
 var autofill_onload = function()
 {
+  if ( this.loaded )
+  {
+    return true;
+  }
+  
   autofill_schemas.username = {
-    template: '<div id="--ID--_region" spry:region="autofill_region_--ID--" class="tblholder">' + "\n" +
+    template: '<div id="--ID--_region" spry:region="autofill_ds_username" class="tblholder">' + "\n" +
               '  <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" +
               '    <tr>' + "\n" +
               '      <th>' + $lang.get('user_autofill_heading_suggestions') + '</th>' + "\n" +
@@ -88,9 +103,10 @@
       
       var allow_anon = ( params.allow_anon ) ? '1' : '0';
       // setup the dataset
-      window["autofill_region_" + element.id] = new Spry.Data.JSONDataSet(makeUrlNS('Special', 'Autofill', 'type=' + fillclass + '&allow_anon' + allow_anon));
-      Spry.Data.initRegions(document.body);
-      (window["autofill_region_" + element.id]).loadData();
+      if ( !window.autofill_ds_username )
+      {
+        window.autofill_ds_username = new Spry.Data.JSONDataSet(makeUrlNS('Special', 'Autofill', 'type=' + fillclass + '&allow_anon' + allow_anon));
+      }
       
       // inject our HTML wrapper
       var template = this.template.replace(new RegExp('--ID--', 'g'), element.id);
@@ -100,7 +116,7 @@
       // a bunch of hacks to add a spry wrapper
       wrapper.innerHTML = template + wrapper.innerHTML;
       
-      var autosuggest = new Spry.Widget.AutoSuggest("autofill_wrap_" + element.id, element.id + '_region', window["autofill_region_" + element.id], 'name', {loadFromServer: true, urlParam: 'userinput', hoverSuggestClass: 'row2', minCharsType: 3});
+      var autosuggest = new Spry.Widget.AutoSuggest("autofill_wrap_" + element.id, element.id + '_region', window.autofill_ds_username, 'name', {loadFromServer: true, urlParam: 'userinput', hoverSuggestClass: 'row2', minCharsType: 3});
       var regiondiv = document.getElementById(element.id + '_region');
       regiondiv.style.position = 'absolute';
       regiondiv.style.top = top + 'px';
@@ -123,6 +139,19 @@
   
   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?
+    if ( !Spry.Data )
+    {
+      load_spry_data();
+      return true;
+    }
+  }
+  
+  this.loaded = true;
+  
   for ( var i = 0; i < inputs.length; i++ )
   {
     autofill_init_element(inputs[i]);
@@ -131,6 +160,11 @@
 
 addOnloadHook(autofill_onload);
 
+function autofill_force_region_refresh()
+{
+  Spry.Data.initRegions();
+}
+
 function AutofillUsername(element, event, allowanon)
 {
   element.onkeyup = element.onkeydown = element.onkeypress = function(e) {};
@@ -143,18 +177,13 @@
     });
 }
 
-function findParentForm(o)
+// load spry data components
+function load_spry_data()
 {
-  if ( o.tagName == 'FORM' )
-    return o;
-  while(true)
+  var scripts = [ 'SpryData.js', 'SpryJSONDataSet.js', 'SpryAutoSuggest.js' ];
+  for ( var i = 0; i < scripts.length; i++ )
   {
-    o = o.parentNode;
-    if ( !o )
-      return false;
-    if ( o.tagName == 'FORM' )
-      return o;
+    load_component(scripts[i]);
   }
-  return false;
+  autofill_onload();
 }
-
--- a/includes/clientside/static/base64.js	Sun Jun 22 18:13:59 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-// Base64 encoder/decoder - pulled from http://ostermiller.org/calc/encode.html. Licensed under the GPL.
-
-var END_OF_INPUT = -1;
-
-var base64Chars = new Array(
-    'A','B','C','D','E','F','G','H',
-    'I','J','K','L','M','N','O','P',
-    'Q','R','S','T','U','V','W','X',
-    'Y','Z','a','b','c','d','e','f',
-    'g','h','i','j','k','l','m','n',
-    'o','p','q','r','s','t','u','v',
-    'w','x','y','z','0','1','2','3',
-    '4','5','6','7','8','9','+','/'
-);
-
-var reverseBase64Chars = new Array();
-for (var i=0; i < base64Chars.length; i++){
-    reverseBase64Chars[base64Chars[i]] = i;
-}
-
-var base64Str;
-var base64Count;
-
-function setBase64Str(str){
-    base64Str = str;
-    base64Count = 0;
-}
-function readBase64(){    
-    if (!base64Str) return END_OF_INPUT;
-    if (base64Count >= base64Str.length) return END_OF_INPUT;
-    var c = base64Str.charCodeAt(base64Count) & 0xff;
-    base64Count++;
-    return c;
-}
-function encodeBase64(str){
-    setBase64Str(str);
-    var result = '';
-    var inBuffer = new Array(3);
-    var lineCount = 0;
-    var done = false;
-    while (!done && (inBuffer[0] = readBase64()) != END_OF_INPUT){
-        inBuffer[1] = readBase64();
-        inBuffer[2] = readBase64();
-        result += (base64Chars[ inBuffer[0] >> 2 ]);
-        if (inBuffer[1] != END_OF_INPUT){
-            result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30) | (inBuffer[1] >> 4) ]);
-            if (inBuffer[2] != END_OF_INPUT){
-                result += (base64Chars [((inBuffer[1] << 2) & 0x3c) | (inBuffer[2] >> 6) ]);
-                result += (base64Chars [inBuffer[2] & 0x3F]);
-            } else {
-                result += (base64Chars [((inBuffer[1] << 2) & 0x3c)]);
-                result += ('=');
-                done = true;
-            }
-        } else {
-            result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30)]);
-            result += ('=');
-            result += ('=');
-            done = true;
-        }
-        lineCount += 4;
-        if (lineCount >= 76){
-            result += ('\n');
-            lineCount = 0;
-        }
-    }
-    return result;
-}
-function readReverseBase64(){   
-    if (!base64Str) return END_OF_INPUT;
-    while (true){      
-        if (base64Count >= base64Str.length) return END_OF_INPUT;
-        var nextCharacter = base64Str.charAt(base64Count);
-        base64Count++;
-        if (reverseBase64Chars[nextCharacter]){
-            return reverseBase64Chars[nextCharacter];
-        }
-        if (nextCharacter == 'A') return 0;
-    }
-    return END_OF_INPUT;
-}
-
-function ntos(n){
-    n=n.toString(16);
-    if (n.length == 1) n="0"+n;
-    n="%"+n;
-    return unescape(n);
-}
-
-function decodeBase64(str){
-    setBase64Str(str);
-    var result = "";
-    var inBuffer = new Array(4);
-    var done = false;
-    while (!done && (inBuffer[0] = readReverseBase64()) != END_OF_INPUT
-        && (inBuffer[1] = readReverseBase64()) != END_OF_INPUT){
-        inBuffer[2] = readReverseBase64();
-        inBuffer[3] = readReverseBase64();
-        result += ntos((((inBuffer[0] << 2) & 0xff)| inBuffer[1] >> 4));
-        if (inBuffer[2] != END_OF_INPUT){
-            result +=  ntos((((inBuffer[1] << 4) & 0xff)| inBuffer[2] >> 2));
-            if (inBuffer[3] != END_OF_INPUT){
-                result +=  ntos((((inBuffer[2] << 6)  & 0xff) | inBuffer[3]));
-            } else {
-                done = true;
-            }
-        } else {
-            done = true;
-        }
-    }
-    return result;
-}
-
--- a/includes/clientside/static/comments.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/comments.js	Tue Jun 24 23:37:23 2008 -0400
@@ -3,8 +3,11 @@
 var comment_template = false;
 var comment_render_track = 0;
 
-function ajaxComments(parms)
+window.ajaxComments = function(parms)
 {
+  load_component('l10n');
+  load_component('paginate');
+  load_component('template-compiler');
   setAjaxLoading();
   var pid = strToPageID(title);
   if(!parms)
@@ -63,7 +66,7 @@
   });
 }
 
-function renderComments(data)
+window.renderComments = function(data)
 {
   
   var html = '';
@@ -254,19 +257,19 @@
   return ret;
 }
 
-function displayCommentForm()
+window.displayCommentForm = function()
 {
   document.getElementById('leave_comment_button').style.display = 'none';
   document.getElementById('comment_form').style.display = 'block';
 }
 
-function hideCommentForm()
+window.hideCommentForm = function()
 {
   document.getElementById('leave_comment_button').style.display = 'inline';
   document.getElementById('comment_form').style.display = 'none';
 }
 
-function editComment(id, link)
+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
@@ -292,7 +295,7 @@
   link.onclick = function() { var id = this.id.substr(this.id.indexOf('_')+1); saveComment(id, this); return false; };
 }
 
-function saveComment(id, link)
+window.saveComment = function(id, link)
 {
   var data = document.getElementById('comment_edit_'+id).value;
   var subj = document.getElementById('subject_edit_'+id).value;
@@ -311,7 +314,7 @@
   ajaxComments(req);
 }
 
-function deleteComment(id)
+window.deleteComment = function(id)
 {
   if ( !shift )
   {
@@ -329,7 +332,7 @@
   ajaxComments(req);
 }
 
-function submitComment()
+window.submitComment = function()
 {
   var name = document.getElementById('commentform_name').value;
   var subj = document.getElementById('commentform_subject').value;
@@ -365,7 +368,7 @@
   ajaxComments(req);
 }
 
-function redrawComment(data)
+window.redrawComment = function(data)
 {
   if ( data.subj )
   {
@@ -402,7 +405,7 @@
   }
 }
 
-function approveComment(id)
+window.approveComment = function(id)
 {
   var div = document.getElementById('comment_holder_'+id);
   var real_id = div.getElementsByTagName('input')[0]['value'];
@@ -415,7 +418,7 @@
 }
 
 // Does the actual DOM object removal
-function annihiliateComment(id) // Did I spell that right?
+window.annihiliateComment = function(id) // Did I spell that right?
 {
   var approved = true;
   if(document.getElementById('comment_approve_'+id))
@@ -437,7 +440,7 @@
   }
 }
 
-function materializeComment(data)
+window.materializeComment = function(data)
 {
   // Intelligently get an ID
 
@@ -561,7 +564,7 @@
   
 }
 
-function comment_decrement_unapproval()
+window.comment_decrement_unapproval = function()
 {
   if ( document.getElementById('comment_count_unapp_inner') )
   {
@@ -584,7 +587,7 @@
   }
 }
 
-function comment_increment_unapproval()
+window.comment_increment_unapproval = function()
 {
   if ( document.getElementById('comment_count_unapp_inner') )
   {
@@ -609,7 +612,7 @@
   }
 }
 
-function viewCommentIP(id, local_id)
+window.viewCommentIP = function(id, local_id)
 {
   // set "loading" indicator on IP button
   var span = $dynano('comment_ip_' + local_id).object;
@@ -625,43 +628,3 @@
   ajaxComments(parms);
 }
 
-function htmlspecialchars(text)
-{
-  text = text.replace(/</g, '&lt;');
-  text = text.replace(/>/g, '&gt;');
-  return text;
-}
-
-// Equivalent to PHP trim() function
-function trim(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;
-}
-
-function nl2br(text)
-{
-  var regex = new RegExp(unescape('%0A'), 'g');
-  return text.replace(regex, '<br />' + unescape('%0A'));
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/clientside/static/crypto.js	Tue Jun 24 23:37:23 2008 -0400
@@ -0,0 +1,2220 @@
+////////////////////////////////////////////////////////////////////////////////////////
+// Big Integer Library v. 5.1
+// Created 2000, last modified 2007
+// Leemon Baird
+// www.leemon.com
+//
+// Version history:
+//
+// v 5.1  8 Oct 2007 
+//   - renamed inverseModInt_ to inverseModInt since it doesn't change its parameters
+//   - added functions GCD and randBigInt, which call GCD_ and randBigInt_
+//   - fixed a bug found by Rob Visser (see comment with his name below)
+//   - improved comments
+//
+// This file is public domain.   You can use it for any purpose without restriction.
+// I do not guarantee that it is correct, so use it at your own risk.  If you use 
+// it for something interesting, I'd appreciate hearing about it.  If you find 
+// any bugs or make any improvements, I'd appreciate hearing about those too.
+// It would also be nice if my name and address were left in the comments.
+// But none of that is required.
+//
+// This code defines a bigInt library for arbitrary-precision integers.
+// A bigInt is an array of integers storing the value in chunks of bpe bits, 
+// little endian (buff[0] is the least significant word).
+// Negative bigInts are stored two's complement.
+// Some functions assume their parameters have at least one leading zero element.
+// Functions with an underscore at the end of the name have unpredictable behavior in case of overflow, 
+// so the caller must make sure the arrays must be big enough to hold the answer.
+// For each function where a parameter is modified, that same 
+// variable must not be used as another argument too.
+// So, you cannot square x by doing multMod_(x,x,n).  
+// You must use squareMod_(x,n) instead, or do y=dup(x); multMod_(x,y,n).
+//
+// These functions are designed to avoid frequent dynamic memory allocation in the inner loop.
+// For most functions, if it needs a BigInt as a local variable it will actually use
+// a global, and will only allocate to it only when it's not the right size.  This ensures
+// that when a function is called repeatedly with same-sized parameters, it only allocates
+// memory on the first call.
+//
+// Note that for cryptographic purposes, the calls to Math.random() must 
+// be replaced with calls to a better pseudorandom number generator.
+//
+// In the following, "bigInt" means a bigInt with at least one leading zero element,
+// and "integer" means a nonnegative integer less than radix.  In some cases, integer 
+// can be negative.  Negative bigInts are 2s complement.
+// 
+// The following functions do not modify their inputs.
+// Those returning a bigInt, string, or Array will dynamically allocate memory for that value.
+// Those returning a boolean will return the integer 0 (false) or 1 (true).
+// Those returning boolean or int will not allocate memory except possibly on the first time they're called with a given parameter size.
+// 
+// bigInt  add(x,y)               //return (x+y) for bigInts x and y.  
+// bigInt  addInt(x,n)            //return (x+n) where x is a bigInt and n is an integer.
+// string  bigInt2str(x,base)     //return a string form of bigInt x in a given base, with 2 <= base <= 95
+// int     bitSize(x)             //return how many bits long the bigInt x is, not counting leading zeros
+// bigInt  dup(x)                 //return a copy of bigInt x
+// boolean equals(x,y)            //is the bigInt x equal to the bigint y?
+// boolean equalsInt(x,y)         //is bigint x equal to integer y?
+// bigInt  expand(x,n)            //return a copy of x with at least n elements, adding leading zeros if needed
+// Array   findPrimes(n)          //return array of all primes less than integer n
+// bigInt  GCD(x,y)               //return greatest common divisor of bigInts x and y (each with same number of elements).
+// boolean greater(x,y)           //is x>y?  (x and y are nonnegative bigInts)
+// boolean greaterShift(x,y,shift)//is (x <<(shift*bpe)) > y?
+// bigInt  int2bigInt(t,n,m)      //return a bigInt equal to integer t, with at least n bits and m array elements
+// bigInt  inverseMod(x,n)        //return (x**(-1) mod n) for bigInts x and n.  If no inverse exists, it returns null
+// int     inverseModInt(x,n)     //return x**(-1) mod n, for integers x and n.  Return 0 if there is no inverse
+// boolean isZero(x)              //is the bigInt x equal to zero?
+// boolean millerRabin(x,b)       //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime (as opposed to definitely composite)?
+// bigInt  mod(x,n)               //return a new bigInt equal to (x mod n) for bigInts x and n.
+// int     modInt(x,n)            //return x mod n for bigInt x and integer n.
+// bigInt  mult(x,y)              //return x*y for bigInts x and y. This is faster when y<x.
+// bigInt  multMod(x,y,n)         //return (x*y mod n) for bigInts x,y,n.  For greater speed, let y<x.
+// boolean negative(x)            //is bigInt x negative?
+// bigInt  powMod(x,y,n)          //return (x**y mod n) where x,y,n are bigInts and ** is exponentiation.  0**0=1. Faster for odd n.
+// bigInt  randBigInt(n,s)        //return an n-bit random BigInt (n>=1).  If s=1, then the most significant of those n bits is set to 1.
+// bigInt  randTruePrime(k)       //return a new, random, k-bit, true prime bigInt using Maurer's algorithm.
+// bigInt  str2bigInt(s,b,n,m)    //return a bigInt for number represented in string s in base b with at least n bits and m array elements
+// bigInt  sub(x,y)               //return (x-y) for bigInts x and y.  Negative answers will be 2s complement
+// bigInt  bigint_trim(x,k)              //return a copy of x with exactly k leading zero elements
+//
+//
+// The following functions each have a non-underscored version, which most users should call instead.
+// These functions each write to a single parameter, and the caller is responsible for ensuring the array 
+// passed in is large enough to hold the result. 
+//
+// void    addInt_(x,n)          //do x=x+n where x is a bigInt and n is an integer
+// void    add_(x,y)             //do x=x+y for bigInts x and y
+// void    copy_(x,y)            //do x=y on bigInts x and y
+// void    copyInt_(x,n)         //do x=n on bigInt x and integer n
+// void    GCD_(x,y)             //set x to the greatest common divisor of bigInts x and y, (y is destroyed).  (This never overflows its array).
+// boolean inverseMod_(x,n)      //do x=x**(-1) mod n, for bigInts x and n. Returns 1 (0) if inverse does (doesn't) exist
+// void    mod_(x,n)             //do x=x mod n for bigInts x and n. (This never overflows its array).
+// void    mult_(x,y)            //do x=x*y for bigInts x and y.
+// void    multMod_(x,y,n)       //do x=x*y  mod n for bigInts x,y,n.
+// void    powMod_(x,y,n)        //do x=x**y mod n, where x,y,n are bigInts (n is odd) and ** is exponentiation.  0**0=1.
+// void    randBigInt_(b,n,s)    //do b = an n-bit random BigInt. if s=1, then nth bit (most significant bit) is set to 1. n>=1.
+// void    randTruePrime_(ans,k) //do ans = a random k-bit true random prime (not just probable prime) with 1 in the msb.
+// void    sub_(x,y)             //do x=x-y for bigInts x and y. Negative answers will be 2s complement.
+//
+// The following functions do NOT have a non-underscored version. 
+// They each write a bigInt result to one or more parameters.  The caller is responsible for
+// ensuring the arrays passed in are large enough to hold the results. 
+//
+// void addShift_(x,y,ys)       //do x=x+(y<<(ys*bpe))
+// void carry_(x)               //do carries and borrows so each element of the bigInt x fits in bpe bits.
+// void divide_(x,y,q,r)        //divide x by y giving quotient q and remainder r
+// int  divInt_(x,n)            //do x=floor(x/n) for bigInt x and integer n, and return the remainder. (This never overflows its array).
+// int  eGCD_(x,y,d,a,b)        //sets a,b,d to positive bigInts such that d = GCD_(x,y) = a*x-b*y
+// void halve_(x)               //do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement.  (This never overflows its array).
+// void leftShift_(x,n)         //left shift bigInt x by n bits.  n<bpe.
+// void linComb_(x,y,a,b)       //do x=a*x+b*y for bigInts x and y and integers a and b
+// void linCombShift_(x,y,b,ys) //do x=x+b*(y<<(ys*bpe)) for bigInts x and y, and integers b and ys
+// void mont_(x,y,n,np)         //Montgomery multiplication (see comments where the function is defined)
+// void multInt_(x,n)           //do x=x*n where x is a bigInt and n is an integer.
+// void rightShift_(x,n)        //right shift bigInt x by n bits.  0 <= n < bpe. (This never overflows its array).
+// void squareMod_(x,n)         //do x=x*x  mod n for bigInts x,n
+// void subShift_(x,y,ys)       //do x=x-(y<<(ys*bpe)). Negative answers will be 2s complement.
+//
+// The following functions are based on algorithms from the _Handbook of Applied Cryptography_
+//    powMod_()           = algorithm 14.94, Montgomery exponentiation
+//    eGCD_,inverseMod_() = algorithm 14.61, Binary extended GCD_
+//    GCD_()              = algorothm 14.57, Lehmer's algorithm
+//    mont_()             = algorithm 14.36, Montgomery multiplication
+//    divide_()           = algorithm 14.20  Multiple-precision division
+//    squareMod_()        = algorithm 14.16  Multiple-precision squaring
+//    randTruePrime_()    = algorithm  4.62, Maurer's algorithm
+//    millerRabin()       = algorithm  4.24, Miller-Rabin algorithm
+//
+// Profiling shows:
+//     randTruePrime_() spends:
+//         10% of its time in calls to powMod_()
+//         85% of its time in calls to millerRabin()
+//     millerRabin() spends:
+//         99% of its time in calls to powMod_()   (always with a base of 2)
+//     powMod_() spends:
+//         94% of its time in calls to mont_()  (almost always with x==y)
+//
+// This suggests there are several ways to speed up this library slightly:
+//     - convert powMod_ to use a Montgomery form of k-ary window (or maybe a Montgomery form of sliding window)
+//         -- this should especially focus on being fast when raising 2 to a power mod n
+//     - convert randTruePrime_() to use a minimum r of 1/3 instead of 1/2 with the appropriate change to the test
+//     - tune the parameters in randTruePrime_(), including c, m, and recLimit
+//     - speed up the single loop in mont_() that takes 95% of the runtime, perhaps by reducing checking
+//       within the loop when all the parameters are the same length.
+//
+// There are several ideas that look like they wouldn't help much at all:
+//     - replacing trial division in randTruePrime_() with a sieve (that speeds up something taking almost no time anyway)
+//     - increase bpe from 15 to 30 (that would help if we had a 32*32->64 multiplier, but not with JavaScript's 32*32->32)
+//     - speeding up mont_(x,y,n,np) when x==y by doing a non-modular, non-Montgomery square
+//       followed by a Montgomery reduction.  The intermediate answer will be twice as long as x, so that
+//       method would be slower.  This is unfortunate because the code currently spends almost all of its time
+//       doing mont_(x,x,...), both for randTruePrime_() and powMod_().  A faster method for Montgomery squaring
+//       would have a large impact on the speed of randTruePrime_() and powMod_().  HAC has a couple of poorly-worded
+//       sentences that seem to imply it's faster to do a non-modular square followed by a single
+//       Montgomery reduction, but that's obviously wrong.
+////////////////////////////////////////////////////////////////////////////////////////
+
+//globals
+bpe=0;         //bits stored per array element
+mask=0;        //AND this with an array element to chop it down to bpe bits
+radix=mask+1;  //equals 2^bpe.  A single 1 bit to the left of the last bit of mask.
+
+//the digits for converting to different bases
+digitsStr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=!@#$%^&*()[]{}|;:,.<>/?`~ \\\'\"+-';
+
+//initialize the global variables
+for (bpe=0; (1<<(bpe+1)) > (1<<bpe); bpe++);  //bpe=number of bits in the mantissa on this platform
+bpe>>=1;                   //bpe=number of bits in one element of the array representing the bigInt
+mask=(1<<bpe)-1;           //AND the mask with an integer to get its bpe least significant bits
+radix=mask+1;              //2^bpe.  a single 1 bit to the left of the first bit of mask
+one=int2bigInt(1,1,1);     //constant used in powMod_()
+
+//the following global variables are scratchpad memory to 
+//reduce dynamic memory allocation in the inner loop
+t=new Array(0);
+ss=t;       //used in mult_()
+s0=t;       //used in multMod_(), squareMod_() 
+s1=t;       //used in powMod_(), multMod_(), squareMod_() 
+s2=t;       //used in powMod_(), multMod_()
+s3=t;       //used in powMod_()
+s4=t; s5=t; //used in mod_()
+s6=t;       //used in bigInt2str()
+s7=t;       //used in powMod_()
+T=t;        //used in GCD_()
+sa=t;       //used in mont_()
+mr_x1=t; mr_r=t; mr_a=t;                                      //used in millerRabin()
+eg_v=t; eg_u=t; eg_A=t; eg_B=t; eg_C=t; eg_D=t;               //used in eGCD_(), inverseMod_()
+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_()
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+//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;
+}
+
+//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;
+
+  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);
+
+  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++;
+
+  if (s)                
+    rightShift_(mr_r,s);
+
+  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;  
+}
+
+//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;
+}
+
+//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;
+}
+
+//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);
+}
+
+//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);
+}
+
+//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);
+}
+
+//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);
+}
+
+//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);
+}
+
+//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);
+}
+
+//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);
+}
+
+//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;
+}
+
+//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);
+}
+
+//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;
+
+  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.);
+    }
+  }
+
+  //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 (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;
+
+  //simulation suggests the more complex algorithm using r=.333 is only slightly faster.
+
+  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))
+
+  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]
+
+    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;
+      }      
+
+    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
+        }
+      }
+    }
+  }
+}
+
+//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;
+}
+
+//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));
+}
+
+//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;
+}
+
+//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);
+
+  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;
+  }
+}
+
+//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);
+
+  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);
+  }
+
+  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);
+      }
+    }
+
+    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;
+    }
+  }
+}
+
+//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;
+
+    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);
+}
+
+
+//Given positive bigInts x and y, change the bigints v, a, and b to positive bigInts such that:
+//     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);
+      }
+    }
+
+    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;
+    }
+  }
+}
+
+
+//is bigInt x negative?
+function negative(x) {
+  return ((x[x.length-1]>>(bpe-1))&1);
+}
+
+
+//is (x << (shift*bpe)) > y?
+//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;
+}
+
+//is x > y? (x and y both nonnegative)
+function greater(x,y) {
+  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=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;
+}
+
+//divide x by y giving quotient q and remainder r.  (q=floor(x/y),  r=x mod y).  All 4 are bigints.
+//x must have at least one leading zero element.
+//y must be nonzero.
+//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
+
+  //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
+
+  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]);	
+
+    //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;
+    }
+
+    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
+}
+
+//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;
+  }
+}
+
+//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;
+}
+
+//convert the integer t into a bigInt with at least the given number of bits.
+//the returned array stores the bigInt in bpe-bit chunks, little endian (buff[0] is least significant word)
+//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;
+}
+
+//return the bigInt given a string representation in a given base.  
+//Pad the array with leading zeros so that it has at least minSize elements.
+//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;
+  }
+
+  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;
+}
+
+//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;
+}
+
+//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;
+}
+
+//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;
+}
+
+//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="";
+
+  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;
+}
+
+//returns a duplicate of bigInt x
+function dup(x) {
+  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;
+}
+
+//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;
+  }
+}
+
+//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
+  }
+}
+
+//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;
+}
+
+//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
+}
+
+//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);
+}
+
+//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;
+  }
+}
+
+//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;
+}
+
+//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;
+  }
+}
+
+//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;
+  }
+}
+
+//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;
+  }
+}
+
+//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;
+  }
+}
+
+//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;
+  }
+}
+
+//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;
+  }
+}
+
+//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);
+}
+
+//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
+}
+
+//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);
+}
+
+//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);
+}
+
+//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;
+}
+
+//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);
+
+  //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
+
+  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);
+
+    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, 
+//  where Ri = 2**(-kn*bpe) mod n, and kn is the 
+//  number of elements in the n array, not 
+//  counting leading zeros.  
+//x must be large enough to hold the answer.
+//It's OK if x and y are the same variable.
+//must have:
+//  x,y < n
+//  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;
+
+  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
+
+  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];
+
+    //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);
+}
+
+
+/* rijndael.js      Rijndael Reference Implementation
+   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
+ without fee, for any purpose and by any individual or organization, is hereby
+ granted, provided that the above copyright notice and this paragraph appear 
+ in all copies. Distribution as a part of an application or binary must
+ include the above copyright notice in the documentation and/or other materials
+ 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_ :)
+
+   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. 
+
+   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.
+
+                                               Fritz Schneider
+                                               fritz at cs.ucsd.edu
+ 
+*/
+
+// Rijndael parameters --  Valid values are 128, 192, or 256
+
+var keySizeInBits =   ( typeof AES_BITS == 'number' ) ? AES_BITS : 128;
+var blockSizeInBits = ( typeof AES_BLOCKSIZE == 'number' ) ? AES_BLOCKSIZE : 128;
+
+///////  You shouldn't have to modify anything below this line except for
+///////  the function getRandomBytes().
+//
+// Note: in the following code the two dimensional arrays are indexed as
+//       you would probably expect, as array[row][column]. The state arrays
+//       are 2d arrays of the form state[4][Nb].
+
+
+// The number of rounds for the cipher, indexed by [Nk][Nb]
+var roundsArray = [ ,,,,[,,,,10,, 12,, 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] ];
+
+// The round constants used in subkey expansion
+var Rcon = [ 
+0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 
+0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 
+0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 
+0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 
+0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ];
+
+// Precomputed lookup table for the SBox
+var SBox = [
+ 99, 124, 119, 123, 242, 107, 111, 197,  48,   1, 103,  43, 254, 215, 171, 
+118, 202, 130, 201, 125, 250,  89,  71, 240, 173, 212, 162, 175, 156, 164, 
+114, 192, 183, 253, 147,  38,  54,  63, 247, 204,  52, 165, 229, 241, 113, 
+216,  49,  21,   4, 199,  35, 195,  24, 150,   5, 154,   7,  18, 128, 226, 
+235,  39, 178, 117,   9, 131,  44,  26,  27, 110,  90, 160,  82,  59, 214, 
+179,  41, 227,  47, 132,  83, 209,   0, 237,  32, 252, 177,  91, 106, 203, 
+190,  57,  74,  76,  88, 207, 208, 239, 170, 251,  67,  77,  51, 133,  69, 
+249,   2, 127,  80,  60, 159, 168,  81, 163,  64, 143, 146, 157,  56, 245, 
+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, 
+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,
+248, 152,  17, 105, 217, 142, 148, 155,  30, 135, 233, 206,  85,  40, 223,
+140, 161, 137,  13, 191, 230,  66, 104,  65, 153,  45,  15, 176,  84, 187,  
+ 22 ];
+
+// Precomputed lookup table for the inverse SBox
+var SBoxInverse = [
+ 82,   9, 106, 213,  48,  54, 165,  56, 191,  64, 163, 158, 129, 243, 215, 
+251, 124, 227,  57, 130, 155,  47, 255, 135,  52, 142,  67,  68, 196, 222, 
+233, 203,  84, 123, 148,  50, 166, 194,  35,  61, 238,  76, 149,  11,  66, 
+250, 195,  78,   8,  46, 161, 102,  40, 217,  36, 178, 118,  91, 162,  73, 
+109, 139, 209,  37, 114, 248, 246, 100, 134, 104, 152,  22, 212, 164,  92, 
+204,  93, 101, 182, 146, 108, 112,  72,  80, 253, 237, 185, 218,  94,  21,  
+ 70,  87, 167, 141, 157, 132, 144, 216, 171,   0, 140, 188, 211,  10, 247, 
+228,  88,   5, 184, 179,  69,   6, 208,  44,  30, 143, 202,  63,  15,   2, 
+193, 175, 189,   3,   1,  19, 138, 107,  58, 145,  17,  65,  79, 103, 220, 
+234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116,  34, 231, 173,
+ 53, 133, 226, 249,  55, 232,  28, 117, 223, 110,  71, 241,  26, 113,  29, 
+ 41, 197, 137, 111, 183,  98,  14, 170,  24, 190,  27, 252,  86,  62,  75, 
+198, 210, 121,  32, 154, 219, 192, 254, 120, 205,  90, 244,  31, 221, 168,
+ 51, 136,   7, 199,  49, 177,  18,  16,  89,  39, 128, 236,  95,  96,  81,
+127, 169,  25, 181,  74,  13,  45, 229, 122, 159, 147, 201, 156, 239, 160,
+224,  59,  77, 174,  42, 245, 176, 200, 235, 187,  60, 131,  83, 153,  97, 
+ 23,  43,   4, 126, 186, 119, 214,  38, 225, 105,  20,  99,  85,  33,  12,
+125 ];
+
+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;
+}
+
+// This method circularly shifts the array left by the number of elements
+// given in its parameter. It returns the resulting array and is used for 
+// the ShiftRow step. Note that shift() and push() could be used for a more 
+// 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;
+}
+
+// Cipher parameters ... do not change these
+var Nk = keySizeInBits / 32;                   
+var Nb = blockSizeInBits / 32;
+var Nr = roundsArray[Nk][Nb];
+
+// 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));
+}
+
+// Multiplies the two elements of GF(2^8) together and returns the result.
+// See the Rijndael spec, but should be straightforward: for each power of
+// the indeterminant that has a 1 coefficient in x, add y times that power
+// 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;
+}
+
+// Performs the substitution step of the cipher. State is the 2d array of
+// state information (see spec) and direction is string indicating whether
+// we are performing the forward substitution ("encrypt") or inverse 
+// 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]];
+}
+
+// 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]);
+
+}
+
+// Performs the column mixing step of the cipher. Most of these steps can
+// be combined into table lookups on 32bit values (at least for encryption)
+// 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];
+  }
+}
+
+// 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);
+  }
+}
+
+// This function creates the expanded key from the input (128/192/256-bit)
+// key. The parameter key is an array of bytes holding the value of the key.
+// The returned value is an array whose elements are the 32-bit words that 
+// make up the expanded key.
+
+function keyExpansion(key) {
+  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];
+
+  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;
+}
+
+// Rijndael's round functions... 
+
+function Round(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");
+}
+
+function FinalRound(state, roundKey) {
+  byteSub(state, "encrypt");
+  shiftRow(state, "encrypt");
+  addRoundKey(state, roundKey);
+}
+
+function InverseFinalRound(state, roundKey){
+  addRoundKey(state, roundKey);
+  shiftRow(state, "decrypt");
+  byteSub(state, "decrypt");  
+}
+
+// encrypt is the basic encryption function. It takes parameters
+// block, an array of bytes representing a plaintext block, and expandedKey,
+// an array of words representing the expanded key previously returned by
+// 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;
+
+  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
+// block, an array of bytes representing a ciphertext block, and expandedKey,
+// an array of words representing the expanded key previously returned by
+// 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;
+
+  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 method takes a byte array (byteArray) and converts it to a string by
+// applying String.fromCharCode() to each value and concatenating the result.
+// The resulting string is returned. Note that this function SKIPS zero bytes
+// under the assumption that they are padding added in formatPlaintext().
+// Obviously, do not invoke this method on raw data that can contain zero
+// bytes. It is really only appropriate for printable ASCII/Latin-1 
+// 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 += String.fromCharCode(byteArray[i]);
+  return result;
+}
+
+// This function takes an array of bytes (byteArray) and converts them
+// to a hexadecimal string. Array element 0 is found at the beginning of 
+// the resulting string, high nibble first. Consecutive elements follow
+// similarly, for example [16, 255] --> "10ff". The function returns a 
+// 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);
+
+  return result;
+}
+
+// This function converts a string containing hexadecimal digits to an 
+// array of bytes. The resulting byte array is filled in the order the
+// values occur in the string, for example "10FF" --> [16, 255]. This
+// 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;
+}
+
+// This function packs an array of bytes into the four row form defined by
+// Rijndael. It assumes the length of the array of bytes is divisible by
+// four. Bytes are filled in according to the Rijndael spec (starting with
+// 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;
+
+  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
+// by Rijndael into a single 1d array of bytes. It assumes the input "packed"
+// is a packed array. Bytes are filled in according to the Rijndael spec. 
+// 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;
+}
+
+// This function takes a prospective plaintext (string or array of bytes)
+// and pads it with zero bytes if its length is not a multiple of the block 
+// size. If plaintext is a string, it is converted to an array of bytes
+// in the process. The type checking can be made much nicer using the 
+// instanceof operator, but this operator is not available until IE5.0 so I 
+// chose to use the heuristic below. 
+
+function formatPlaintext(plaintext) {
+  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;
+  } 
+
+  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
+// TO RETURN HIGHER QUALITY RANDOM BYTES IF YOU ARE USING THIS FOR A "REAL"
+// 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;
+}
+
+// rijndaelEncrypt(plaintext, key, mode)
+// Encrypts the plaintext using the given key and in the given mode. 
+// The parameter "plaintext" can either be a string or an array of bytes. 
+// The parameter "key" must be an array of key bytes. If you have a hex 
+// string representing the key, invoke hexToByteArray() on it to convert it 
+// to an array of bytes. The third parameter "mode" is a string indicating
+// the encryption mode to use, either "ECB" or "CBC". If the parameter is
+// omitted, ECB is assumed.
+// 
+// An array of bytes representing the cihpertext is returned. To convert 
+// this array to hex, invoke byteArrayToHex() on it. If you are using this 
+// "for real" it is a good idea to change the function getRandomBytes() to 
+// 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
+
+  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);
+
+  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;
+}
+
+// rijndaelDecrypt(ciphertext, key, mode)
+// Decrypts the using the given key and mode. The parameter "ciphertext" 
+// must be an array of bytes. The parameter "key" must be an array of key 
+// bytes. If you have a hex string representing the ciphertext or key, 
+// invoke hexToByteArray() on it to convert it to an array of bytes. The
+// parameter "mode" is a string, either "CBC" or "ECB".
+// 
+// An array of bytes representing the plaintext is returned. To convert 
+// this array to a hex string, invoke byteArrayToHex() on it. To convert it 
+// 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
+
+  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);
+ 
+  // 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);
+
+  return pt;
+}
+
+function stringToByteArray(text)
+{
+  result = new Array();
+  for ( i=0; i<text.length; i++ )
+  {
+    result[result.length] = text.charCodeAt(i);
+  }
+  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() );
+}
+
+/*
+ * EnanoMath, an abstraction layer for big-integer (arbitrary precision)
+ * mathematics.
+ */
+
+var EnanoMathLayers = {};
+
+// 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);
+  }
+}
+
+var EnanoMath = EnanoMathLayers.Leemon;
+
+/*
+ * The Diffie-Hellman key exchange protocol.
+ */
+
+// Our prime number as a base for operations.
+var dh_prime = '82818079787776757473727170696867666564636261605958575655545352515049484746454443424140393837363534333231302928272625242322212019181716151413121110987654321';
+
+// g, a primitive root used as an exponent
+// (2 and 5 are acceptable, but BigInt is faster with odd numbers)
+var dh_g = '5';
+
+/**
+ * Generates a Diffie-Hellman private key
+ * @return string(BigInt)
+ */
+
+function dh_gen_private()
+{
+  return EnanoMath.RandomInt(256);
+}
+
+/**
+ * Calculates the public key from the private key
+ * @param string(BigInt)
+ * @return string(BigInt)
+ */
+
+function dh_gen_public(b)
+{
+  return EnanoMath.PowMod(dh_g, b, dh_prime);
+}
+
+/**
+ * Calculates the shared secret.
+ * @param string(BigInt) Our private key
+ * @param string(BigInt) Remote party's public key
+ * @return string(BigInt)
+ */
+
+function dh_gen_shared_secret(b, A)
+{
+  return EnanoMath.PowMod(A, b, dh_prime);
+}
+
+/* A JavaScript implementation of the Secure Hash Algorithm, SHA-256
+ * Version 0.3 Copyright Angel Marin 2003-2004 - http://anmar.eu.org/
+ * Distributed under the BSD License
+ * Some bits taken from Paul Johnston's SHA-1 implementation
+ */
+/*
+Copyright (c) 2003-2004, Angel Marin
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+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.
+ * 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.
+ * 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.
+
+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
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+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);
+}
+function S (X, n) {return ( X >>> n ) | (X << (32 - n));}
+function R (X, n) {return ( 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 (S(x, 2) ^ S(x, 13) ^ S(x, 22));}
+function Sigma1256(x) {return (S(x, 6) ^ S(x, 11) ^ S(x, 25));}
+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;
+}
+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;
+}
+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;
+}
+function hex_sha256(s){return binb2hex(core_sha256(str2binb(s),s.length * chrsz));}
+
+// Javascript implementation of the and SHA1 hash algorithms - both written by Paul Johnston, licensed under the BSD license
+
+// MD5
+var hexcase = 0; var b64pad  = ""; var chrsz   = 8;
+function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+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); }
+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); }
+function md5_hh(a, b, c, d, x, s, t) { return md5_cmn(b ^ c ^ d, a, b, x, s, t); }
+function md5_ii(a, b, c, d, x, s, t) { return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); }
+function core_hmac_md5(key, data) { var bkey = str2binl(key); if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz); return core_md5(opad.concat(hash), 512 + 128); }
+function safe_add(x, y) {var lsw = (x & 0xFFFF) + (y & 0xFFFF);var msw = (x >> 16) + (y >> 16) + (lsw >> 16);return (msw << 16) | (lsw & 0xFFFF); }
+function bit_rol(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); }
+function str2binl(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) << (i%32); return bin;}
+function binl2str(bin) { var str = ""; var mask = (1 << chrsz) - 1; for(var i = 0; i < bin.length * 32; i += chrsz) str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask); return str; }
+function binl2hex(binarray) { var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; var str = ""; for(var i = 0; i < binarray.length * 4; i++) { str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF); } return str; }
+function binl2b64(binarray) { var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; for(var i = 0; i < binarray.length * 4; i += 3) { var triplet = (((binarray[i >> 2] >> 8 * ( i   %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } } return str; }
+
+// SHA1
+function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
+function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
+function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
+function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
+function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
+function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
+function sha1_vm_test() {   return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; }
+function core_sha1(x, len) { x[len >> 5] |= 0x80 << (24 - len % 32); x[((len + 64 >> 9) << 4) + 15] = len; var w = Array(80); var a =  1732584193; var b = -271733879; var c = -1732584194; var d =  271733878; var e = -1009589776; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; var olde = e; for(var j = 0; j < 80; j++) { if(j < 16) w[j] = x[i + j]; else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j))); e = d; d = c; c = rol(b, 30); b = a; a = t; } a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); e = safe_add(e, olde); } return Array(a, b, c, d, e);}
+function sha1_ft(t, b, c, d){ if(t < 20) return (b & c) | ((~b) & d); if(t < 40) return b ^ c ^ d; if(t < 60) return (b & c) | (b & d) | (c & d); return b ^ c ^ d;}
+function sha1_kt(t){ return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 : (t < 60) ? -1894007588 : -899497514;}
+function core_hmac_sha1(key, data){ var bkey = str2binb(key); if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); return core_sha1(opad.concat(hash), 512 + 160);}
+function safe_add(x, y){ var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF);}
+function rol(num, cnt){ return (num << cnt) | (num >>> (32 - cnt));}
+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) << (32 - chrsz - i%32); return bin;}
+function binb2str(bin){ var str = ""; var mask = (1 << chrsz) - 1; for(var i = 0; i < bin.length * 32; i += chrsz) str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); return str;}
+function binb2hex(binarray){ 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 binb2b64(binarray){ var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; for(var i = 0; i < binarray.length * 4; i += 3) { var triplet = (((binarray[i   >> 2] >> 8 * (3 -  i   %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) |  ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } } return str;}
+
--- a/includes/clientside/static/diffiehellman.js	Sun Jun 22 18:13:59 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * The Diffie-Hellman key exchange protocol.
- */
-
-// Our prime number as a base for operations.
-var dh_prime = '82818079787776757473727170696867666564636261605958575655545352515049484746454443424140393837363534333231302928272625242322212019181716151413121110987654321';
-
-// g, a primitive root used as an exponent
-// (2 and 5 are acceptable, but BigInt is faster with odd numbers)
-var dh_g = '5';
-
-/**
- * Generates a Diffie-Hellman private key
- * @return string(BigInt)
- */
-
-function dh_gen_private()
-{
-  return EnanoMath.RandomInt(256);
-}
-
-/**
- * Calculates the public key from the private key
- * @param string(BigInt)
- * @return string(BigInt)
- */
-
-function dh_gen_public(b)
-{
-  return EnanoMath.PowMod(dh_g, b, dh_prime);
-}
-
-/**
- * Calculates the shared secret.
- * @param string(BigInt) Our private key
- * @param string(BigInt) Remote party's public key
- * @return string(BigInt)
- */
-
-function dh_gen_shared_secret(b, A)
-{
-  return EnanoMath.PowMod(A, b, dh_prime);
-}
-
--- a/includes/clientside/static/dropdown.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/dropdown.js	Tue Jun 24 23:37:23 2008 -0400
@@ -36,6 +36,7 @@
 {
   setTimeout('jBoxBatchSetup();', 200);
 }
+addOnloadHook(jBoxInit);
 
 // Initializes each menu.
 function jBoxBatchSetup()
@@ -144,7 +145,7 @@
   }
   if(obj.nextSibling.tagName.toLowerCase() == 'ul' || ( obj.nextSibling.tagName.toLowerCase() == 'div' && obj.nextSibling.className == 'submenu' ))
   {
-    $dynano(a).addClass('liteselected');
+    $dynano(obj).addClass('liteselected');
     //obj.className = 'liteselected';
     var ul = obj.nextSibling;
     var dim = fetch_dimensions(obj);
@@ -419,28 +420,6 @@
 
 document.onclick = jBoxGarbageCollection;
 
-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]);
-      }
-    }
-  }
-}
-
 var getElementsByClassName = function(parent, type, cls) {
   if(!type)
     type = '*';
@@ -526,29 +505,25 @@
   setMousePos(e);
 };
 
-function domObjChangeOpac(opacity, id) {
-    var object = id.style;
-    object.opacity = (opacity / 100);
-    object.MozOpacity = (opacity / 100);
-    object.KhtmlOpacity = (opacity / 100);
-    object.filter = "alpha(opacity=" + opacity + ")";
+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]);
+      }
+    }
+  }
 }
 
-function getScrollOffset()
-{
-  var position;
-  if (self.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;
-}
-
--- a/includes/clientside/static/dynano.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/dynano.js	Tue Jun 24 23:37:23 2008 -0400
@@ -7,6 +7,10 @@
 var $dynano = $;
 function DNobj(id)
 {
+  if ( id == undefined )
+  {
+    return {};
+  }
   this.object = ( typeof(id) == 'object' ) ? id : document.getElementById(id);
   if ( !this.object )
   {
--- a/includes/clientside/static/editor.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/editor.js	Tue Jun 24 23:37:23 2008 -0400
@@ -1,112 +1,19 @@
 // Javascript routines for the page editor
 
-if ( document.getElementById('mdgCss') )
-{
-  var css_url = document.getElementById('mdgCss').href;
-}
-else
-{
-  var css_url = scriptPath + '/includes/clientside/css/enano_shared.css';
-}
-
-var do_popups = ( is_Safari ) ? '' : ',inlinepopups';
-var _skin = ( typeof(tinymce_skin) == 'string' ) ? tinymce_skin : 'default';
-var editor_img_path = scriptPath + '/images/editor';
-
 // Idle time required for autosave, in seconds
 var AUTOSAVE_TIMEOUT = 15;
 var AutosaveTimeoutObj = null;
-var tinymce_initted = false;
-
-var enano_tinymce_options = {
-  mode : "none",
-  plugins : 'table,save,safari,pagebreak,style,layer,advhr,insertdatetime,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras' + 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,|,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
-};
-
-var enano_tinymce_gz_options = {
-	plugins : 'table,save,safari,pagebreak,style,layer,advhr,insertdatetime,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras' + do_popups,
-	themes : 'advanced',
-	languages : 'en',
-	disk_cache : true,
-	debug : false
-};
+var editor_img_path = scriptPath + '/images/editor';
 
-if ( !KILL_SWITCH && !DISABLE_MCE )
-{
-  if ( IE )
-  {
-    document.write('<script type="text/javascript" src="' + scriptPath + '/includes/clientside/tinymce/tiny_mce.js"></script>');
-  }
-  else
-  {
-    var script = document.createElement('script');
-    script.type="text/javascript";
-    script.src=scriptPath+"/includes/clientside/tinymce/tiny_mce_gzip.js";
-    script.onload = function(e)
-    {
-      tinyMCE_GZ.init(enano_tinymce_gz_options);
-    }
-    head.appendChild(script);
-  }
-}
-
-// Check tinyMCE to make sure its init is finished
-function tinymce_preinit_check()
-{
-  if ( typeof(tinyMCE.init) != 'function' )
-    return false;
-  if ( typeof(tinymce.DOM) != 'object' )
-    return false;
-  if ( typeof(tinymce.DOM.get) != 'function' )
-    return false;
-  if ( typeof(enano_tinymce_gz_options) != 'object' )
-    return false;
-  return true;
-}
-
-var initTinyMCE = function(e)
-{
-  if ( typeof(tinyMCE) == 'object' )
-  {
-    if ( !KILL_SWITCH && !DISABLE_MCE )
-    {
-      if ( !tinymce_preinit_check() && !force )
-      {
-        setTimeout('initTinyMCE(false);', 200);
-        return false;
-      }
-      tinyMCE.init(enano_tinymce_options);
-      tinymce_initted = true;
-    }
-  }
-};
-
-// Safari doesn't fire the init on demand so call it on page load
-if ( is_Safari )
-{
-  addOnloadHook(initTinyMCE);
-}
-
-var editor_open = false;
-
-function ajaxEditor(revid)
+window.ajaxEditor = function(revid)
 {
   if ( KILL_SWITCH )
     return true;
   if ( editor_open )
     return true;
+  load_component('l10n');
+  load_component('template-compiler');
+  load_component('messagebox');
   var rev_id_uri = ( revid ) ? '&revid=' + revid : '';
   selectButtonMinor('edit');
   selectButtonMajor('article');
@@ -128,14 +35,14 @@
         if ( response.mode == 'error' )
         {
           unselectAllButtonsMinor();
-          new messagebox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
+          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'));
+          new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_access_denied_title'), $lang.get('editor_err_access_denied_body'));
           return false;
         }
         
@@ -147,7 +54,7 @@
     });
 }
 
-function ajaxBuildEditor(readonly, timestamp, allow_wysiwyg, captcha_hash, revid, 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
@@ -594,7 +501,7 @@
   setInterval('ajaxPerformAutosave();', ( 5 * 60 * 1000 ));
 }
 
-function ajaxEditorDestroyModalWindow()
+window.ajaxEditorDestroyModalWindow = function()
 {
   if ( editor_use_modal_window )
   {
@@ -608,7 +515,7 @@
   }
 }
 
-function ajaxEditorSave(is_draft, text_override)
+window.ajaxEditorSave = function(is_draft, text_override)
 {
   if ( !is_draft )
     ajaxSetEditorLoading();
@@ -616,7 +523,7 @@
   
   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'));
+    new MessageBox(MB_OK|MB_ICONSTOP, $lang.get('editor_err_no_text_title'), $lang.get('editor_err_no_text_body'));
     ajaxUnSetEditorLoading();
     return false;
   }
@@ -657,7 +564,7 @@
     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'));
+      new MessageBox(MB_OK|MB_ICONSTOP, $lang.get('editor_err_need_captcha_title'), $lang.get('editor_err_need_captcha_body'));
       ajaxUnSetEditorLoading();
       return false;
     }
@@ -682,7 +589,7 @@
         // This will only be used if there was a lower-level error.
         if ( response.mode == 'error' )
         {
-          new messagebox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
+          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)
@@ -704,7 +611,7 @@
             }
           }
           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);
+          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
@@ -712,7 +619,7 @@
         {
           // 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) }));
+          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' )
@@ -772,7 +679,7 @@
 }
 
 // Delete the draft (this is a massive server-side hack)
-function ajaxEditorDeleteDraft()
+window.ajaxEditorDeleteDraft = function()
 {
   miniPromptMessage({
       title: $lang.get('editor_msg_confirm_delete_draft_title'),
@@ -799,12 +706,12 @@
     });
 }
 
-function ajaxEditorDeleteDraftReal()
+window.ajaxEditorDeleteDraftReal = function()
 {
   return ajaxEditorSave(true, -1);
 }
 
-function ajaxEditorGenPreview()
+window.ajaxEditorGenPreview = function()
 {
   ajaxSetEditorLoading();
   var ta_content = $dynano('ajaxEditArea').getContent();
@@ -826,16 +733,16 @@
     }, true);
 }
 
-function ajaxEditorRevertToLatest()
+window.ajaxEditorRevertToLatest = function()
 {
-  var mb = new messagebox(MB_YESNO | MB_ICONQUESTION, $lang.get('editor_msg_revert_confirm_title'), $lang.get('editor_msg_revert_confirm_body'));
+  var mb = new MessageBox(MB_YESNO | MB_ICONQUESTION, $lang.get('editor_msg_revert_confirm_title'), $lang.get('editor_msg_revert_confirm_body'));
   mb.onclick['Yes'] = function()
   {
     setTimeout('ajaxEditorRevertToLatestReal();', 750);
   }
 }
 
-function ajaxEditorRevertToLatestReal()
+window.ajaxEditorRevertToLatestReal = function()
 {
   ajaxSetEditorLoading();
   ajaxGet(stdAjaxPrefix + '&_mode=getsource', function()
@@ -855,14 +762,14 @@
         if ( response.mode == 'error' )
         {
           unselectAllButtonsMinor();
-          new messagebox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
+          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'));
+          new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_access_denied_title'), $lang.get('editor_err_access_denied_body'));
           return false;
         }
         
@@ -871,7 +778,7 @@
     }, true);
 }
 
-function ajaxEditorShowDiffs()
+window.ajaxEditorShowDiffs = function()
 {
   ajaxSetEditorLoading();
   var ta_content = $dynano('ajaxEditArea').getContent();
@@ -893,9 +800,9 @@
     }, true);
 }
 
-function ajaxEditorCancel()
+window.ajaxEditorCancel = function()
 {
-  var mb = new messagebox(MB_YESNO | MB_ICONQUESTION, $lang.get('editor_msg_cancel_confirm_title'), $lang.get('editor_msg_cancel_confirm_body'));
+  var mb = new MessageBox(MB_YESNO | MB_ICONQUESTION, $lang.get('editor_msg_cancel_confirm_title'), $lang.get('editor_msg_cancel_confirm_body'));
   mb.onclick['Yes'] = function()
   {
     setAjaxLoading();
@@ -906,7 +813,7 @@
   }
 }
 
-function ajaxSetEditorMCE()
+window.ajaxSetEditorMCE = function()
 {
   if ( editor_loading )
     return false;
@@ -935,7 +842,7 @@
   createCookie('enano_editor_mode', 'tinymce', 365);
 }
 
-function ajaxSetEditorPlain()
+window.ajaxSetEditorPlain = function()
 {
   if ( editor_loading )
     return false;
@@ -966,7 +873,7 @@
 
 var editor_loading = false;
 
-function ajaxSetEditorLoading()
+window.ajaxSetEditorLoading = function()
 {
   var ed = tinyMCE.get('ajaxEditArea');
   editor_loading = true;
@@ -996,7 +903,7 @@
   }
 }
 
-function ajaxUnSetEditorLoading()
+window.ajaxUnSetEditorLoading = function()
 {
   editor_loading = false;
   var ed = tinyMCE.get('ajaxEditArea');
@@ -1012,7 +919,7 @@
   }
 }
 
-function ajaxAutosaveDraft()
+window.ajaxAutosaveDraft = function()
 {
   var aed = document.getElementById('ajaxEditArea');
   if ( !aed )
@@ -1025,7 +932,7 @@
   }
 }
 
-function ajaxPerformAutosave()
+window.ajaxPerformAutosave = function()
 {
   var aed = document.getElementById('ajaxEditArea');
   if ( !aed )
@@ -1043,7 +950,7 @@
   ajaxEditorSave(true);
 }
 
-function ajaxEditorUseDraft()
+window.ajaxEditorUseDraft = function()
 {
   var aed = document.getElementById('ajaxEditArea');
   if ( !aed )
@@ -1066,7 +973,7 @@
         if ( response.mode == 'error' )
         {
           unselectAllButtonsMinor();
-          new messagebox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
+          new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
           return false;
         }
         
@@ -1085,3 +992,12 @@
     }, true);
 }
 
+/**
+ * Equivalent of PHP's time()
+ * @return int
+ */
+
+function unix_time()
+{
+  return parseInt((new Date()).getTime()/1000);
+}
--- a/includes/clientside/static/enano-lib-basic.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/enano-lib-basic.js	Tue Jun 24 23:37:23 2008 -0400
@@ -1,378 +1,465 @@
-/*
- * Enano - an open source wiki-like CMS
- * Copyright (C) 2006-2007 Dan Fuhry
- * Javascript client library
- *
- * This program is Free Software; you can redistribute 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.
- *
- * 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.
- *
- * For more information about Enano, please visit http://enanocms.org/.
- * Unless otherwise noted, all of the code in these script files may be used freely so long as the above license block
- * is displayed and your modified code is distributed in compliance with the GPL. See the special page "About Enano" on
- * this website for more information.
- */
-
-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.');
-}
-
-// Run-time variables
-
-var detect = navigator.userAgent.toLowerCase();
-var IE;
-var is_Safari;
-
-// Detect whether the user is running the Evil One or not...
-
-function checkIt(string) {
-  place = detect.indexOf(string) + 1;
-  thestring = string;
-  return place;
-}
-if (checkIt('msie')) IE = true;
-else IE = false;
-
-var is_Opera = ( checkIt('opera') ) ? true : false;
-var is_iPhone = ( checkIt('iphone') || checkIt('ipod') ) ? true : false;
-var is_firefox2 = ( checkIt('firefox/2.') ) ? true : false;
-
-var KILL_SWITCH = false;
-
-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;
-  }
-}
-
-// dummy tinyMCE object
-var tinyMCE = new Object();
-
-if ( typeof(DISABLE_MCE) == undefined )
-{
-  var DISABLE_MCE = false;
-}
-
-// Obsolete JSON kill switch
-function disableJSONExts() { };
-
-is_Safari = checkIt('safari') ? true : false;
-
-var cmt_open;
-var list;
-var edit_open = false;
-var catlist = new Array();
-var arrDiff1Buttons = new Array();
-var arrDiff2Buttons = new Array();
-var arrTimeIdList   = new Array();
-var list;
-var unObj;
-var unSelectMenuOn = false;
-var unObjDivCurrentId = false;
-var unObjCurrentSelection = false;
-var userlist = new Array();
-var submitAuthorized = true;
-var rDnsObj;
-var rDnsBannerObj;
-var ns4 = document.layers;
-var op5 = (navigator.userAgent.indexOf("Opera 5")!=-1) ||(navigator.userAgent.indexOf("Opera/5")!=-1);
-var op6 = (navigator.userAgent.indexOf("Opera 6")!=-1) ||(navigator.userAgent.indexOf("Opera/6")!=-1);
-var agt=navigator.userAgent.toLowerCase();
-var mac = (agt.indexOf("mac")!=-1);
-var ie = (agt.indexOf("msie") != -1);
-var mac_ie = mac && ie;
-var mouseX = 0;
-var mouseY = 0;
-var menuheight;
-var inertiabase = 1;
-var inertiainc = 1;
-var slideintervalinc = 20;
-var inertiabaseoriginal = inertiabase;
-var heightnow;
-var targetheight;
-var block;
-var slideinterval;
-var divheights = new Array();
-var __menutimeout = false;
-var startmouseX = false;
-var startmouseY = false;
-var startScroll = false;
-var is_dragging = false;
-var current_ta  = false;
-var startwidth  = false;
-var startheight = false;
-var do_width    = false;
-var ajax_load_icon = scriptPath + '/images/loading.gif';
-var editor_use_modal_window = false;
-
-// You have an NSIS coder in your midst...
-var MB_OK = 1;
-var MB_OKCANCEL = 2;
-var MB_YESNO = 4;
-var MB_YESNOCANCEL = 8;
-var MB_ABORTRETRYIGNORE = 16;
-var MB_ICONINFORMATION = 32;
-var MB_ICONEXCLAMATION = 64;
-var MB_ICONSTOP = 128;
-var MB_ICONQUESTION = 256;
-var MB_ICONLOCK = 512;
-
-// Syntax:
-// messagebox(MB_OK|MB_ICONINFORMATION, 'Title', 'Text');
-// :-D
-
-var main_css = document.getElementById('mdgCss').href;
-if(main_css.indexOf('?') > -1) {
-  sep = '&';
-} else sep = '?';
-var _css = false;
-var print_css = main_css + sep + 'printable';
-
-var shift;
-
-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 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);
-}
-
-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'];
-}
-
-function append_sid(url)
-{
-  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;
-  }
-  return url;
-}
-
-var stdAjaxPrefix = append_sid(scriptPath+'/ajax.php?title='+title);
-
-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 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;
-    }
-  }
-}
-
-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);
-    }
-  }
-}
-
-var head = document.getElementsByTagName('head')[0];
-
-var script = document.createElement('script');
-script.type="text/javascript";
-script.src=scriptPath+"/includes/clientside/firebug/firebug.js";
-head.appendChild(script);
-
-// 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"];
-
-    window.console = {};
-    for (var i = 0; i < names.length; ++i)
-        window.console[names[i]] = function() {}
-}
-
-// safari has window.console but not the .debug() method
-if ( is_Safari && !window.console.debug )
-{
-  window.console.debug = function() {};
-}
-
-// Do not remove the following comments, they are used by jsres.php.
-/*!START_INCLUDER*/
-
-// Start loading files
-// The string from the [ to the ] needs to be valid JSON, it's parsed by jsres.php.
-var thefiles = [
-  'dynano.js',
-  'misc.js',
-  'l10n.js',
-  'login.js',
-  'admin-menu.js',
-  'ajax.js',
-  'autofill.js',
-  'base64.js',
-  'dropdown.js',
-  'faders.js',
-  'fat.js',
-  'grippy.js',
-  'json.js',
-  'md5.js',
-  'libbigint.js',
-  'enanomath.js',
-  'diffiehellman.js',
-  'sha256.js',
-  'sliders.js',
-  'toolbar.js',
-  'rijndael.js',
-  'template-compiler.js',
-  'acl.js',
-  'comments.js',
-  'editor.js',
-  'flyin.js',
-  'paginate.js',
-  'pwstrength.js',
-  'theme-manager.js',
-  'rank-manager.js',
-  'SpryEffects.js',
-  'SpryData.js',
-  'SpryJSONDataSet.js',
-  'SpryAutoSuggest.js',
-  'loader.js'
-];
-
-var problem_scripts = {
-  'json.js' : true,
-  'template-compiler.js' : true
-};
-
-for(var f in thefiles)
-{
-  if ( typeof(thefiles[f]) != 'string' )
-    continue;
-  var script = document.createElement('script');
-  script.type="text/javascript";
-  if ( problem_scripts[thefiles[f]] && KILL_SWITCH )
-  {
-    // alert('kill switch and problem script');
-    continue;
-  }
-  script.src=scriptPath+"/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['do'] )
-  {
-    var act = $_REQUEST['do'];
-    switch(act)
-    {
-      case 'comments':
-        ajaxComments();
-        break;
-      case 'edit':
-        ajaxEditor();
-        break;
-      case 'login':
-        ajaxStartLogin();
-        break;
-      case 'history':
-        ajaxHistory();
-        break;
-      case 'catedit':
-        ajaxCatEdit();
-        break;
-    }
-  }
-});
-
-//*/
+/*
+ * Enano - an open source wiki-like CMS
+ * Copyright (C) 2006-2007 Dan Fuhry
+ * Javascript client library
+ *
+ * This program is Free Software; you can redistribute 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.
+ *
+ * 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.
+ *
+ * For more information about Enano, please visit http://enanocms.org/.
+ * Unless otherwise noted, all of the code in these script files may be used freely so long as the above license block
+ * is displayed and your modified code is distributed in compliance with the GPL. See the special page "About Enano" on
+ * this website for more information.
+ */
+
+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.');
+}
+
+// Run-time variables
+
+var detect = navigator.userAgent.toLowerCase();
+var IE;
+var is_Safari;
+
+// Detect whether the user is running the Evil One or not...
+
+function checkIt(string) {
+  place = detect.indexOf(string) + 1;
+  thestring = string;
+  return place;
+}
+if (checkIt('msie')) IE = true;
+else IE = false;
+
+var is_Opera = ( checkIt('opera') ) ? true : false;
+var is_iPhone = ( checkIt('iphone') || checkIt('ipod') ) ? true : false;
+var is_firefox2 = ( checkIt('firefox/2.') ) ? true : false;
+
+var KILL_SWITCH = false;
+
+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;
+  }
+}
+
+// dummy tinyMCE object
+var tinyMCE = new Object();
+
+if ( typeof(DISABLE_MCE) == undefined )
+{
+  var DISABLE_MCE = false;
+}
+
+is_Safari = checkIt('safari') ? true : false;
+
+var cmt_open;
+var editor_open = false;
+var list;
+var edit_open = false;
+var catlist = new Array();
+var arrDiff1Buttons = new Array();
+var arrDiff2Buttons = new Array();
+var arrTimeIdList   = new Array();
+var list;
+var unObj;
+var unSelectMenuOn = false;
+var unObjDivCurrentId = false;
+var unObjCurrentSelection = false;
+var userlist = new Array();
+var submitAuthorized = true;
+var timelist = [];
+var rDnsObj;
+var rDnsBannerObj;
+var ns4 = document.layers;
+var op5 = (navigator.userAgent.indexOf("Opera 5")!=-1) ||(navigator.userAgent.indexOf("Opera/5")!=-1);
+var op6 = (navigator.userAgent.indexOf("Opera 6")!=-1) ||(navigator.userAgent.indexOf("Opera/6")!=-1);
+var agt=navigator.userAgent.toLowerCase();
+var mac = (agt.indexOf("mac")!=-1);
+var ie = (agt.indexOf("msie") != -1);
+var mac_ie = mac && ie;
+var mouseX = 0;
+var mouseY = 0;
+var menuheight;
+var inertiabase = 1;
+var inertiainc = 1;
+var slideintervalinc = 20;
+var inertiabaseoriginal = inertiabase;
+var heightnow;
+var targetheight;
+var block;
+var slideinterval;
+var divheights = new Array();
+var __menutimeout = false;
+var startmouseX = false;
+var startmouseY = false;
+var startScroll = false;
+var is_dragging = false;
+var current_ta  = false;
+var startwidth  = false;
+var startheight = false;
+var do_width    = false;
+var ajax_load_icon = scriptPath + '/images/loading.gif';
+var editor_use_modal_window = false;
+var Spry = {};
+
+// You have an NSIS coder in your midst...
+var MB_OK = 1;
+var MB_OKCANCEL = 2;
+var MB_YESNO = 4;
+var MB_YESNOCANCEL = 8;
+var MB_ABORTRETRYIGNORE = 16;
+var MB_ICONINFORMATION = 32;
+var MB_ICONEXCLAMATION = 64;
+var MB_ICONSTOP = 128;
+var MB_ICONQUESTION = 256;
+var MB_ICONLOCK = 512;
+
+// Can be set to true by slow themes (St. Patty)
+if ( typeof(pref_disable_js_fx) != 'boolean' )
+{
+  var pref_disable_js_fx = false;
+}
+var aclDisableTransitionFX = ( is_firefox2 || pref_disable_js_fx ) ? true : false;
+
+// Syntax:
+// messagebox(MB_OK|MB_ICONINFORMATION, 'Title', 'Text');
+// :-D
+
+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;
+  }
+}
+
+if ( !onload_hooks )
+  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;
+    }
+  }
+}
+
+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);
+    }
+  }
+}
+
+var loaded_components = {};
+function load_component(file)
+{
+  if ( !file.match(/\.js$/) )
+    file = file + '.js';
+  
+  console.info('Loading component %s via AJAX', file.replace(/\.js$/, ''));
+  
+  if ( loaded_components[file] )
+  {
+    // already loaded
+    return true;
+  }
+  
+  load_show_win(file);
+  
+  // get an XHR instance
+  var ajax = ajaxMakeXHR();
+  
+  var uri = scriptPath + '/includes/clientside/static/' + file;
+  ajax.open('GET', uri, false);
+  ajax.send(null);
+  if ( ajax.readyState == 4 && ajax.status == 200 )
+  {
+    onload_hooks = new Array();
+    eval_global(ajax.responseText);
+    load_hide_win();
+    runOnloadHooks();
+  }
+  
+  loaded_components[file] = true;
+  return true;
+}
+
+function load_show_win(file)
+{
+  var img = '<img style="margin-right: 5px" src="' + scriptPath + '/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$/, '');
+  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';
+  
+  ld.style.backgroundImage = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAA1JREFUCNdj+P///xkACcgDypG+nnEAAAAASUVORK5CYII=)';
+  
+  document.body.appendChild(ld);
+}
+
+function load_hide_win()
+{
+  var ld = document.getElementById('_js_load_component');
+  ld.parentNode.removeChild(ld);
+}
+
+// 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;
+  }
+
+  // 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;
+}
+
+var head = document.getElementsByTagName('head')[0];
+
+// 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"];
+
+    window.console = {};
+    for (var i = 0; i < names.length; ++i)
+        window.console[names[i]] = function() {}
+}
+
+// safari has window.console but not the .debug() method
+if ( is_Safari && !window.console.debug )
+{
+  window.console.debug = function() {};
+}
+
+// Do not remove the following comments, they are used by jsres.php.
+/*!START_INCLUDER*/
+
+// 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',
+  'pwstrength.js',
+  'loader.js'
+];
+
+var problem_scripts = {
+  'json.js' : true,
+  'template-compiler.js' : true
+};
+
+for(var f in thefiles)
+{
+  if ( typeof(thefiles[f]) != 'string' )
+    continue;
+  var script = document.createElement('script');
+  script.type="text/javascript";
+  if ( problem_scripts[thefiles[f]] && KILL_SWITCH )
+  {
+    // alert('kill switch and problem script');
+    continue;
+  }
+  script.src=scriptPath+"/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['do'] )
+  {
+    var act = $_REQUEST['do'];
+    switch(act)
+    {
+      case 'comments':
+        ajaxComments();
+        break;
+      case 'edit':
+        ajaxEditor();
+        break;
+      case 'login':
+        ajaxStartLogin();
+        break;
+      case 'history':
+        ajaxHistory();
+        break;
+      case 'catedit':
+        ajaxCatEdit();
+        break;
+      case 'rename':
+        ajaxRename();
+        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);
+    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',
+  ajaxResetDelVotes: 'ajax.js',
+  ajaxDeletePage: 'ajax.js',
+  ajaxSetPassword: 'ajax.js',
+  ajaxChangeStyle: 'ajax.js',
+  ajaxOpenACLManager: 'acl.js',
+  ajaxAdminPage: 'login.js',
+  ajaxInitLogout: 'login.js',
+  ajaxStartLogin: 'login.js',
+  ajaxStartAdminLogin: 'login.js',
+  ajaxAdminPage: 'login.js',
+  mb_logout: 'login.js',
+  selectButtonMajor: 'toolbar.js',
+  selectButtonMinor: 'toolbar.js',
+  unselectAllButtonsMajor: 'toolbar.js',
+  unselectAllButtonsMinor: 'toolbar.js',
+  darken: 'fadefilter.js',
+  enlighten: 'fadefilter.js',
+}
+
+var placeholder_instances = {};
+
+for ( var i in placeholder_list )
+{
+  var file = placeholder_list[i];
+  placeholder_instances[i] = new Placeholder(i, file);
+  window[i] = placeholder_instances[i].go;
+}
+
+//*/
--- a/includes/clientside/static/enanomath.js	Sun Jun 22 18:13:59 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-/*
- * EnanoMath, an abstraction layer for big-integer (arbitrary precision)
- * mathematics.
- */
-
-var EnanoMathLayers = {};
-
-// 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);
-  }
-}
-
-var EnanoMath = EnanoMathLayers.Leemon;
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/clientside/static/expander.js	Tue Jun 24 23:37:23 2008 -0400
@@ -0,0 +1,137 @@
+/*
+ * Expandable fieldsets
+ */
+
+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]);
+  }
+}
+
+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);
+  }
+}
+
+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);
+  }
+}
+
+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];
+      $(a).rmClass('expander-open');
+      $(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];
+      $(a).rmClass('expander-closed');
+      $(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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/clientside/static/fadefilter.js	Tue Jun 24 23:37:23 2008 -0400
@@ -0,0 +1,107 @@
+/**
+ * Darkens the browser screen. This will make the entire page un-clickable except for any floating divs created after this is called. Restore with enlighten().
+ * @param bool Controls whether the fade should be disabled or not. aclDisableTransitionFX will override this if set to true, and fades are never fired on IE.
+ * @param int When specified, represents the numeric opacity value to set the fade layer to. 1-100.
+ */
+
+var darkener_index = 0;
+
+function darken(nofade, opacVal)
+{
+  if(IE)
+    nofade = true;
+  if ( !opacVal )
+    opacVal = 70;
+  darkener_index++;
+  if(document.getElementById('specialLayer_darkener'))
+  {
+    if(nofade)
+    {
+      changeOpac(opacVal, 'specialLayer_darkener');
+      document.getElementById('specialLayer_darkener').style.display = 'block';
+      document.getElementById('specialLayer_darkener').myOpacVal = opacVal;
+    }
+    else
+    {
+      if ( document.getElementById('specialLayer_darkener').style.display != 'none' )
+      {
+        var currentOpac = document.getElementById('specialLayer_darkener').myOpacVal;
+        opacity('specialLayer_darkener', currentOpac, opacVal, 1000);
+        document.getElementById('specialLayer_darkener').myOpacVal = opacVal;
+      }
+      else
+      {
+        document.getElementById('specialLayer_darkener').style.display = 'block';
+        document.getElementById('specialLayer_darkener').myOpacVal = opacVal;
+        opacity('specialLayer_darkener', 0, opacVal, 1000);
+      }
+    }
+  } 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 = '100%';
+    thediv.zIndex = getHighestZ() + 5;
+    thediv.id = 'specialLayer_darkener';
+    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('specialLayer_darkener', 0, opacVal, 1000);
+    }
+  }
+}
+
+/**
+ * Un-darkens the screen and re-enables clicking of on-screen controls.
+ * @param bool If true, disables the fade effect. Fades are always disabled if aclDisableTransitionFX is true and on IE.
+ */
+
+function enlighten(nofade)
+{
+  if(IE)
+    nofade = true;
+  darkener_index -= 1;
+  if ( darkener_index > 0 )
+    return false;
+  if(document.getElementById('specialLayer_darkener'))
+  {
+    if(nofade)
+    {
+      document.getElementById('specialLayer_darkener').style.display = 'none';
+    }
+    else
+    {
+      var from = document.getElementById('specialLayer_darkener').myOpacVal;
+      // console.info('Fading from ' + from);
+      opacity('specialLayer_darkener', from, 0, 1000);
+      setTimeout("document.getElementById('specialLayer_darkener').style.display = 'none';", 1000);
+    }
+  }
+}
--- a/includes/clientside/static/fat.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/fat.js	Tue Jun 24 23:37:23 2008 -0400
@@ -87,7 +87,5 @@
 	}
 }
 
-window.onload = function () 
-	{
-	Fat.fade_all();
-	}
\ No newline at end of file
+addOnloadHook(Fat.fade_all);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/clientside/static/functions.js	Tue Jun 24 23:37:23 2008 -0400
@@ -0,0 +1,798 @@
+// all utility functions go in here
+
+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 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);
+}
+
+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'];
+}
+
+function append_sid(url)
+{
+  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;
+  }
+  return url;
+}
+
+var stdAjaxPrefix = append_sid(scriptPath+'/ajax.php?title='+title);
+
+/**
+ * Core AJAX library
+ */
+
+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;
+}
+
+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();
+  }
+  ajax = ajaxMakeXHR();
+  if ( !ajax )
+  {
+    console.error('ajaxMakeXHR() failed');
+    return false;
+  }
+  ajax.onreadystatechange = f;
+  ajax.open('GET', uri, true);
+  ajax.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );
+  ajax.send(null);
+}
+
+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();
+  }
+  ajax = ajaxMakeXHR();
+  if ( !ajax )
+  {
+    console.error('ajaxMakeXHR() failed');
+    return false;
+  }
+  ajax.onreadystatechange = f;
+  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);
+  }
+  ajax.setRequestHeader("Connection", "close");
+  ajax.send(parms);
+}
+
+/**
+ * Show a friendly error message depicting an AJAX response that is not valid JSON
+ * @param string Response text
+ * @param string Custom error message. If omitted, the default will be shown.
+ */
+
+function handle_invalid_json(response, customerror)
+{
+  var mainwin = $dynano('ajaxEditContainer').object;
+  mainwin.innerHTML = '';
+  
+  // Title
+  var h3 = document.createElement('h3');
+  h3.appendChild(document.createTextNode('The site encountered an error while processing your request.'));
+  mainwin.appendChild(h3);
+  
+  if ( typeof(customerror) == 'string' )
+  {
+    var el = document.createElement('p');
+    el.appendChild(document.createTextNode(customerror));
+    mainwin.appendChild(el);
+  }
+  else
+  {
+    customerror  = 'We unexpectedly received the following response from the server. The response should have been in the JSON ';
+    customerror += 'serialization format, but the response wasn\'t composed only of the JSON response. There are three possible triggers ';
+    customerror += 'for this problem:';
+    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');
+    li1.appendChild(document.createTextNode('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.'));
+    var osc_exception = ( window.location.hostname == 'demo.opensourcecms.com' ) ? ' This is KNOWN to be the case with the OpenSourceCMS.com demo version of Enano.' : '';
+    li2.appendChild(document.createTextNode('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.' + osc_exception));
+    li3.appendChild(document.createTextNode('It\'s possible that Enano triggered a PHP error or warning. In this case, you may be looking at a bug in Enano.'));
+      
+    ul.appendChild(li1);
+    ul.appendChild(li2);
+    ul.appendChild(li3);
+    mainwin.appendChild(ul);
+  }
+  
+  var p2 = document.createElement('p');
+  p2.appendChild(document.createTextNode('The response received from the server is as follows:'));
+  mainwin.appendChild(p2);
+  
+  var pre = document.createElement('pre');
+  pre.appendChild(document.createTextNode(response));
+  mainwin.appendChild(pre);
+  
+  var p3 = document.createElement('p');
+  p3.appendChild(document.createTextNode('You may also choose to view the response as HTML. '));
+  var a = document.createElement('a');
+  a.appendChild(document.createTextNode('View as HTML...'));
+  a._resp = response;
+  a.id = 'invalidjson_link';
+  a.onclick = function()
+  {
+    var mb = new MessageBox(MB_YESNO | MB_ICONEXCLAMATION, 'Do you really want to view this response as HTML?', 'If the response was changed during transmission to include malicious code, you may be allowing that malicious code to run by viewing the response as HTML. Only do this if you have reviewed the response text and have found no suspicious code in it.');
+    mb.onclick['Yes'] = function()
+    {
+      var html = $dynano('invalidjson_link').object._resp;
+      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(html);
+    }
+    return false;
+  }
+  a.href = '#';
+  p3.appendChild(a);
+  mainwin.appendChild(p3);
+}
+
+function ajaxEscape(text)
+{
+  /*
+  text = escape(text);
+  text = text.replace(/\+/g, '%2B', text);
+  */
+  text = window.encodeURIComponent(text);
+  return text;
+}
+
+/**
+ * String functions
+ */
+
+// Equivalent to PHP trim() function
+function trim(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;
+}
+
+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;
+}
+
+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;
+  }
+}
+
+function findParentForm(o)
+{
+  return get_parent_form(o);
+}
+
+function domObjChangeOpac(opacity, id) {
+    var object = id.style;
+    object.opacity = (opacity / 100);
+    object.MozOpacity = (opacity / 100);
+    object.KhtmlOpacity = (opacity / 100);
+    object.filter = "alpha(opacity=" + opacity + ")";
+}
+
+function getScrollOffset()
+{
+  var position;
+  if (self.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 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));
+  }
+}
+
+addOnloadHook(fadeInfoBoxes);
+
+// Alpha fades
+
+function opacity(id, opacStart, opacEnd, millisec)
+{
+    var object = document.getElementById(id);
+    domOpacity(object, opacStart, opacEnd, millisec);
+}
+
+var opacityDOMCache = new Object();
+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;
+
+    //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("var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj)",(timer * speed));
+            timer++;
+        }
+    } else if(opacStart < opacEnd) {
+        for(i = opacStart; i <= opacEnd; i++)
+            {
+            setTimeout("var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj)",(timer * speed));
+            timer++;
+        }
+    }
+    setTimeout("delete(opacityDOMCache["+uniqid+"]);",(timer * speed));
+}
+
+// change the opacity for different browsers
+function changeOpac(opacity, id)
+{
+  var object = document.getElementById(id);
+  return domObjChangeOpac(opacity, object);
+}
+
+// draw a white ajax-ey "loading" box over an object
+function whiteOutElement(el)
+{
+  var top = $(el).Top();
+  var left = $(el).Left();
+  var width = $(el).Width();
+  var height = $(el).Height();
+  
+  var blackout = document.createElement('div');
+  blackout.style.position = '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);
+  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.style.zIndex = getHighestZ() + 2;
+  
+  var body = document.getElementsByTagName('body')[0];
+  body.appendChild(blackout);
+  
+  return blackout;
+}
+
+// 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 };
+}
+
+function fetch_dimensions(o) {
+  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;
+}
+
+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;
+}
+
+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);
+}
+
+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;
+  }
+}
+
+function enableUnload()
+{
+  window._unloadmsg = null;
+  window.onbeforeunload = null;
+}
+
+/**
+ * Gets the highest z-index of all divs in the document
+ * @return integer
+ */
+function getHighestZ()
+{
+  z = 0;
+  var divs = document.getElementsByTagName('div');
+  for(var i = 0; i < divs.length; i++)
+  {
+    if(divs[i].style.zIndex > z) z = divs[i].style.zIndex;
+  }
+  return z;
+}
+
+function isKeyPressed(event)
+{
+  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 busyBannerID;
+function goBusy(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);
+}
+
+function setAjaxLoading()
+{
+  if ( document.getElementById('ajaxloadicon') )
+  {
+    document.getElementById('ajaxloadicon').src=ajax_load_icon;
+  }
+}
+
+function unsetAjaxLoading()
+{
+  if ( document.getElementById('ajaxloadicon') )
+  {
+    document.getElementById('ajaxloadicon').src=scriptPath + '/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;}
+function createCookie(name,value,days){if (days){var date = new Date();date.setTime(date.getTime()+(days*24*60*60*1000));var expires = "; expires="+date.toGMTString();}else var expires = "";document.cookie = name+"="+value+expires+"; path=/";}
+function eraseCookie(name) {createCookie(name,"",-1);}
+
+/*
+ * AJAX login box (experimental)
+ * Moved / rewritten in login.js
+ */
+
+// Included only for API-compatibility
+function ajaxPromptAdminAuth(call_on_ok, level)
+{
+  ajaxLogonInit(call_on_ok, level);
+}
+
+/**
+ * Insert a DOM object _after_ the specified child.
+ * @param object Parent node
+ * @param object Node to insert
+ * @param object Node to insert after
+ */
+
+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);
+    }
+  }
+}
+
+/**
+ * Validates an e-mail address.
+ * @param string E-mail address
+ * @return bool
+ */
+
+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;
+}
+
+/**
+ * Validates a username.
+ * @param string Username to test
+ * @return bool
+ */
+
+function validateUsername(username)
+{
+  var regex = new RegExp('^[^<>&\?\'"%\n\r/]+$', '');
+  return ( username.match(regex) ) ? true : false;
+}
+
+/*
+ * Utility functions, moved from windows.js
+ */
+
+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;
+}
+
+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;
+}
+
+/**
+ * Sanitizes a page URL string so that it can safely be stored in the database.
+ * @param string Page ID to sanitize
+ * @return string Cleaned text
+ */
+
+function sanitize_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);
+
+  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;
+}
+
+/**
+ * Removes character escapes in a page ID string
+ * @param string Page ID string to dirty up
+ * @return string
+ */
+
+function dirtify_page_id(page_id)
+{
+  // 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;
+}
+
+/**
+ * Equivalent to PHP's in_array function.
+ */
+
+function in_array(needle, haystack)
+{
+  for(var i in haystack)
+  {
+    if(haystack[i] == needle) return i;
+  }
+  return false;
+}
--- a/includes/clientside/static/json.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/json.js	Tue Jun 24 23:37:23 2008 -0400
@@ -150,7 +150,8 @@
         var j = eval('(' + string + ')');
         if (typeof filter === 'function') {
   
-            function walk(k, v) {
+            function walk(k, v)
+            {
                 if (v && typeof v === 'object') {
                     for (var i in v) {
                         if (v.hasOwnProperty(i)) {
--- a/includes/clientside/static/l10n.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/l10n.js	Tue Jun 24 23:37:23 2008 -0400
@@ -4,6 +4,17 @@
 
 var Language = function(lang_id)
 {
+  // load the language file
+  load_show_win('strings');
+  var ajax = ajaxMakeXHR();
+  var uri = makeUrlNS('Special', 'LangExportJSON/' + lang_id);
+  ajax.open('GET', uri, false);
+  ajax.send(null);
+  if ( ajax.readyState == 4 && ajax.status == 200 )
+  {
+    eval_global(ajax.responseText);
+  }
+  
   if ( typeof(enano_lang) != 'object' )
     return false;
   if ( typeof(enano_lang[lang_id]) != 'object' )
@@ -56,29 +67,7 @@
 
 var language_onload = function()
 {
-  if ( typeof(enano_lang) != 'object' )
-  {
-    language_onload_resched();
-    return true;
-  }
-  if ( !enano_lang[ENANO_LANG_ID] )
-  {
-    language_onload_resched();
-    return true;
-  }
   $lang = new Language(ENANO_LANG_ID);
 }
 
-// Rescheduler for language onload - allows delaying init if the string list
-// isn't ready yet
-function language_onload_resched()
-{
-  if ( window.console )
-  {
-    // window.console.info('Delaying language init by 0.2s because language_onload decided that enano_lang[ENANO_LANG_ID] isn\'t ready');
-  }
-  setTimeout('language_onload();', 200);
-}
-
 addOnloadHook(language_onload);
-
--- a/includes/clientside/static/libbigint.js	Sun Jun 22 18:13:59 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1400 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////
-// Big Integer Library v. 5.1
-// Created 2000, last modified 2007
-// Leemon Baird
-// www.leemon.com
-//
-// Version history:
-//
-// v 5.1  8 Oct 2007 
-//   - renamed inverseModInt_ to inverseModInt since it doesn't change its parameters
-//   - added functions GCD and randBigInt, which call GCD_ and randBigInt_
-//   - fixed a bug found by Rob Visser (see comment with his name below)
-//   - improved comments
-//
-// This file is public domain.   You can use it for any purpose without restriction.
-// I do not guarantee that it is correct, so use it at your own risk.  If you use 
-// it for something interesting, I'd appreciate hearing about it.  If you find 
-// any bugs or make any improvements, I'd appreciate hearing about those too.
-// It would also be nice if my name and address were left in the comments.
-// But none of that is required.
-//
-// This code defines a bigInt library for arbitrary-precision integers.
-// A bigInt is an array of integers storing the value in chunks of bpe bits, 
-// little endian (buff[0] is the least significant word).
-// Negative bigInts are stored two's complement.
-// Some functions assume their parameters have at least one leading zero element.
-// Functions with an underscore at the end of the name have unpredictable behavior in case of overflow, 
-// so the caller must make sure the arrays must be big enough to hold the answer.
-// For each function where a parameter is modified, that same 
-// variable must not be used as another argument too.
-// So, you cannot square x by doing multMod_(x,x,n).  
-// You must use squareMod_(x,n) instead, or do y=dup(x); multMod_(x,y,n).
-//
-// These functions are designed to avoid frequent dynamic memory allocation in the inner loop.
-// For most functions, if it needs a BigInt as a local variable it will actually use
-// a global, and will only allocate to it only when it's not the right size.  This ensures
-// that when a function is called repeatedly with same-sized parameters, it only allocates
-// memory on the first call.
-//
-// Note that for cryptographic purposes, the calls to Math.random() must 
-// be replaced with calls to a better pseudorandom number generator.
-//
-// In the following, "bigInt" means a bigInt with at least one leading zero element,
-// and "integer" means a nonnegative integer less than radix.  In some cases, integer 
-// can be negative.  Negative bigInts are 2s complement.
-// 
-// The following functions do not modify their inputs.
-// Those returning a bigInt, string, or Array will dynamically allocate memory for that value.
-// Those returning a boolean will return the integer 0 (false) or 1 (true).
-// Those returning boolean or int will not allocate memory except possibly on the first time they're called with a given parameter size.
-// 
-// bigInt  add(x,y)               //return (x+y) for bigInts x and y.  
-// bigInt  addInt(x,n)            //return (x+n) where x is a bigInt and n is an integer.
-// string  bigInt2str(x,base)     //return a string form of bigInt x in a given base, with 2 <= base <= 95
-// int     bitSize(x)             //return how many bits long the bigInt x is, not counting leading zeros
-// bigInt  dup(x)                 //return a copy of bigInt x
-// boolean equals(x,y)            //is the bigInt x equal to the bigint y?
-// boolean equalsInt(x,y)         //is bigint x equal to integer y?
-// bigInt  expand(x,n)            //return a copy of x with at least n elements, adding leading zeros if needed
-// Array   findPrimes(n)          //return array of all primes less than integer n
-// bigInt  GCD(x,y)               //return greatest common divisor of bigInts x and y (each with same number of elements).
-// boolean greater(x,y)           //is x>y?  (x and y are nonnegative bigInts)
-// boolean greaterShift(x,y,shift)//is (x <<(shift*bpe)) > y?
-// bigInt  int2bigInt(t,n,m)      //return a bigInt equal to integer t, with at least n bits and m array elements
-// bigInt  inverseMod(x,n)        //return (x**(-1) mod n) for bigInts x and n.  If no inverse exists, it returns null
-// int     inverseModInt(x,n)     //return x**(-1) mod n, for integers x and n.  Return 0 if there is no inverse
-// boolean isZero(x)              //is the bigInt x equal to zero?
-// boolean millerRabin(x,b)       //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime (as opposed to definitely composite)?
-// bigInt  mod(x,n)               //return a new bigInt equal to (x mod n) for bigInts x and n.
-// int     modInt(x,n)            //return x mod n for bigInt x and integer n.
-// bigInt  mult(x,y)              //return x*y for bigInts x and y. This is faster when y<x.
-// bigInt  multMod(x,y,n)         //return (x*y mod n) for bigInts x,y,n.  For greater speed, let y<x.
-// boolean negative(x)            //is bigInt x negative?
-// bigInt  powMod(x,y,n)          //return (x**y mod n) where x,y,n are bigInts and ** is exponentiation.  0**0=1. Faster for odd n.
-// bigInt  randBigInt(n,s)        //return an n-bit random BigInt (n>=1).  If s=1, then the most significant of those n bits is set to 1.
-// bigInt  randTruePrime(k)       //return a new, random, k-bit, true prime bigInt using Maurer's algorithm.
-// bigInt  str2bigInt(s,b,n,m)    //return a bigInt for number represented in string s in base b with at least n bits and m array elements
-// bigInt  sub(x,y)               //return (x-y) for bigInts x and y.  Negative answers will be 2s complement
-// bigInt  bigint_trim(x,k)              //return a copy of x with exactly k leading zero elements
-//
-//
-// The following functions each have a non-underscored version, which most users should call instead.
-// These functions each write to a single parameter, and the caller is responsible for ensuring the array 
-// passed in is large enough to hold the result. 
-//
-// void    addInt_(x,n)          //do x=x+n where x is a bigInt and n is an integer
-// void    add_(x,y)             //do x=x+y for bigInts x and y
-// void    copy_(x,y)            //do x=y on bigInts x and y
-// void    copyInt_(x,n)         //do x=n on bigInt x and integer n
-// void    GCD_(x,y)             //set x to the greatest common divisor of bigInts x and y, (y is destroyed).  (This never overflows its array).
-// boolean inverseMod_(x,n)      //do x=x**(-1) mod n, for bigInts x and n. Returns 1 (0) if inverse does (doesn't) exist
-// void    mod_(x,n)             //do x=x mod n for bigInts x and n. (This never overflows its array).
-// void    mult_(x,y)            //do x=x*y for bigInts x and y.
-// void    multMod_(x,y,n)       //do x=x*y  mod n for bigInts x,y,n.
-// void    powMod_(x,y,n)        //do x=x**y mod n, where x,y,n are bigInts (n is odd) and ** is exponentiation.  0**0=1.
-// void    randBigInt_(b,n,s)    //do b = an n-bit random BigInt. if s=1, then nth bit (most significant bit) is set to 1. n>=1.
-// void    randTruePrime_(ans,k) //do ans = a random k-bit true random prime (not just probable prime) with 1 in the msb.
-// void    sub_(x,y)             //do x=x-y for bigInts x and y. Negative answers will be 2s complement.
-//
-// The following functions do NOT have a non-underscored version. 
-// They each write a bigInt result to one or more parameters.  The caller is responsible for
-// ensuring the arrays passed in are large enough to hold the results. 
-//
-// void addShift_(x,y,ys)       //do x=x+(y<<(ys*bpe))
-// void carry_(x)               //do carries and borrows so each element of the bigInt x fits in bpe bits.
-// void divide_(x,y,q,r)        //divide x by y giving quotient q and remainder r
-// int  divInt_(x,n)            //do x=floor(x/n) for bigInt x and integer n, and return the remainder. (This never overflows its array).
-// int  eGCD_(x,y,d,a,b)        //sets a,b,d to positive bigInts such that d = GCD_(x,y) = a*x-b*y
-// void halve_(x)               //do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement.  (This never overflows its array).
-// void leftShift_(x,n)         //left shift bigInt x by n bits.  n<bpe.
-// void linComb_(x,y,a,b)       //do x=a*x+b*y for bigInts x and y and integers a and b
-// void linCombShift_(x,y,b,ys) //do x=x+b*(y<<(ys*bpe)) for bigInts x and y, and integers b and ys
-// void mont_(x,y,n,np)         //Montgomery multiplication (see comments where the function is defined)
-// void multInt_(x,n)           //do x=x*n where x is a bigInt and n is an integer.
-// void rightShift_(x,n)        //right shift bigInt x by n bits.  0 <= n < bpe. (This never overflows its array).
-// void squareMod_(x,n)         //do x=x*x  mod n for bigInts x,n
-// void subShift_(x,y,ys)       //do x=x-(y<<(ys*bpe)). Negative answers will be 2s complement.
-//
-// The following functions are based on algorithms from the _Handbook of Applied Cryptography_
-//    powMod_()           = algorithm 14.94, Montgomery exponentiation
-//    eGCD_,inverseMod_() = algorithm 14.61, Binary extended GCD_
-//    GCD_()              = algorothm 14.57, Lehmer's algorithm
-//    mont_()             = algorithm 14.36, Montgomery multiplication
-//    divide_()           = algorithm 14.20  Multiple-precision division
-//    squareMod_()        = algorithm 14.16  Multiple-precision squaring
-//    randTruePrime_()    = algorithm  4.62, Maurer's algorithm
-//    millerRabin()       = algorithm  4.24, Miller-Rabin algorithm
-//
-// Profiling shows:
-//     randTruePrime_() spends:
-//         10% of its time in calls to powMod_()
-//         85% of its time in calls to millerRabin()
-//     millerRabin() spends:
-//         99% of its time in calls to powMod_()   (always with a base of 2)
-//     powMod_() spends:
-//         94% of its time in calls to mont_()  (almost always with x==y)
-//
-// This suggests there are several ways to speed up this library slightly:
-//     - convert powMod_ to use a Montgomery form of k-ary window (or maybe a Montgomery form of sliding window)
-//         -- this should especially focus on being fast when raising 2 to a power mod n
-//     - convert randTruePrime_() to use a minimum r of 1/3 instead of 1/2 with the appropriate change to the test
-//     - tune the parameters in randTruePrime_(), including c, m, and recLimit
-//     - speed up the single loop in mont_() that takes 95% of the runtime, perhaps by reducing checking
-//       within the loop when all the parameters are the same length.
-//
-// There are several ideas that look like they wouldn't help much at all:
-//     - replacing trial division in randTruePrime_() with a sieve (that speeds up something taking almost no time anyway)
-//     - increase bpe from 15 to 30 (that would help if we had a 32*32->64 multiplier, but not with JavaScript's 32*32->32)
-//     - speeding up mont_(x,y,n,np) when x==y by doing a non-modular, non-Montgomery square
-//       followed by a Montgomery reduction.  The intermediate answer will be twice as long as x, so that
-//       method would be slower.  This is unfortunate because the code currently spends almost all of its time
-//       doing mont_(x,x,...), both for randTruePrime_() and powMod_().  A faster method for Montgomery squaring
-//       would have a large impact on the speed of randTruePrime_() and powMod_().  HAC has a couple of poorly-worded
-//       sentences that seem to imply it's faster to do a non-modular square followed by a single
-//       Montgomery reduction, but that's obviously wrong.
-////////////////////////////////////////////////////////////////////////////////////////
-
-//globals
-bpe=0;         //bits stored per array element
-mask=0;        //AND this with an array element to chop it down to bpe bits
-radix=mask+1;  //equals 2^bpe.  A single 1 bit to the left of the last bit of mask.
-
-//the digits for converting to different bases
-digitsStr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=!@#$%^&*()[]{}|;:,.<>/?`~ \\\'\"+-';
-
-//initialize the global variables
-for (bpe=0; (1<<(bpe+1)) > (1<<bpe); bpe++);  //bpe=number of bits in the mantissa on this platform
-bpe>>=1;                   //bpe=number of bits in one element of the array representing the bigInt
-mask=(1<<bpe)-1;           //AND the mask with an integer to get its bpe least significant bits
-radix=mask+1;              //2^bpe.  a single 1 bit to the left of the first bit of mask
-one=int2bigInt(1,1,1);     //constant used in powMod_()
-
-//the following global variables are scratchpad memory to 
-//reduce dynamic memory allocation in the inner loop
-t=new Array(0);
-ss=t;       //used in mult_()
-s0=t;       //used in multMod_(), squareMod_() 
-s1=t;       //used in powMod_(), multMod_(), squareMod_() 
-s2=t;       //used in powMod_(), multMod_()
-s3=t;       //used in powMod_()
-s4=t; s5=t; //used in mod_()
-s6=t;       //used in bigInt2str()
-s7=t;       //used in powMod_()
-T=t;        //used in GCD_()
-sa=t;       //used in mont_()
-mr_x1=t; mr_r=t; mr_a=t;                                      //used in millerRabin()
-eg_v=t; eg_u=t; eg_A=t; eg_B=t; eg_C=t; eg_D=t;               //used in eGCD_(), inverseMod_()
-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_()
-
-////////////////////////////////////////////////////////////////////////////////////////
-
-//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;
-}
-
-//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;
-
-  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);
-
-  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++;
-
-  if (s)                
-    rightShift_(mr_r,s);
-
-  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;  
-}
-
-//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;
-}
-
-//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;
-}
-
-//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);
-}
-
-//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);
-}
-
-//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);
-}
-
-//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);
-}
-
-//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);
-}
-
-//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);
-}
-
-//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);
-}
-
-//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;
-}
-
-//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);
-}
-
-//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;
-
-  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.);
-    }
-  }
-
-  //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 (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;
-
-  //simulation suggests the more complex algorithm using r=.333 is only slightly faster.
-
-  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))
-
-  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]
-
-    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;
-      }      
-
-    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
-        }
-      }
-    }
-  }
-}
-
-//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;
-}
-
-//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));
-}
-
-//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;
-}
-
-//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);
-
-  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;
-  }
-}
-
-//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);
-
-  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);
-  }
-
-  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);
-      }
-    }
-
-    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;
-    }
-  }
-}
-
-//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;
-
-    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);
-}
-
-
-//Given positive bigInts x and y, change the bigints v, a, and b to positive bigInts such that:
-//     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);
-      }
-    }
-
-    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;
-    }
-  }
-}
-
-
-//is bigInt x negative?
-function negative(x) {
-  return ((x[x.length-1]>>(bpe-1))&1);
-}
-
-
-//is (x << (shift*bpe)) > y?
-//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;
-}
-
-//is x > y? (x and y both nonnegative)
-function greater(x,y) {
-  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=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;
-}
-
-//divide x by y giving quotient q and remainder r.  (q=floor(x/y),  r=x mod y).  All 4 are bigints.
-//x must have at least one leading zero element.
-//y must be nonzero.
-//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
-
-  //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
-
-  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]);	
-
-    //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;
-    }
-
-    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
-}
-
-//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;
-  }
-}
-
-//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;
-}
-
-//convert the integer t into a bigInt with at least the given number of bits.
-//the returned array stores the bigInt in bpe-bit chunks, little endian (buff[0] is least significant word)
-//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;
-}
-
-//return the bigInt given a string representation in a given base.  
-//Pad the array with leading zeros so that it has at least minSize elements.
-//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;
-  }
-
-  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;
-}
-
-//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;
-}
-
-//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;
-}
-
-//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;
-}
-
-//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="";
-
-  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;
-}
-
-//returns a duplicate of bigInt x
-function dup(x) {
-  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;
-}
-
-//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;
-  }
-}
-
-//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
-  }
-}
-
-//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;
-}
-
-//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
-}
-
-//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);
-}
-
-//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;
-  }
-}
-
-//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;
-}
-
-//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;
-  }
-}
-
-//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;
-  }
-}
-
-//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;
-  }
-}
-
-//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;
-  }
-}
-
-//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;
-  }
-}
-
-//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;
-  }
-}
-
-//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);
-}
-
-//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
-}
-
-//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);
-}
-
-//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);
-}
-
-//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;
-}
-
-//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);
-
-  //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
-
-  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);
-
-    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, 
-//  where Ri = 2**(-kn*bpe) mod n, and kn is the 
-//  number of elements in the n array, not 
-//  counting leading zeros.  
-//x must be large enough to hold the answer.
-//It's OK if x and y are the same variable.
-//must have:
-//  x,y < n
-//  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;
-
-  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
-
-  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];
-
-    //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);
-}
-
-
--- a/includes/clientside/static/loader.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/loader.js	Tue Jun 24 23:37:23 2008 -0400
@@ -6,35 +6,18 @@
 {
   window.onkeydown = isKeyPressed;
   window.onkeyup = function(e) { isKeyPressed(e); };
-  Fat.fade_all();
-  fadeInfoBoxes();
-  jBoxInit();
-  if(typeof (dbx_set_key) == 'function')
+  
+  if ( typeof(dbx_set_key) == 'function')
   {
     dbx_set_key();
   }
-  initSliders();
+  
   runOnloadHooks(e);
 }
 
-// if some dumb plugin set an onload function, preserve it 
-var ld;
-if ( window.onload)
-{
-  ld = window.onload;
-}
-else
-{
-  ld = function() {return;};
-}
-
 // Enano's main init function.
 function enano_init(e)
 {
-  if ( typeof(ld) == 'function' )
-  {
-    ld(e);
-  }
   mdgInnerLoader(e);
   
   // we're loaded; set flags to true
--- a/includes/clientside/static/login.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/login.js	Tue Jun 24 23:37:23 2008 -0400
@@ -10,7 +10,7 @@
  * Performs a logon as a regular member.
  */
 
-function ajaxLogonToMember()
+window.ajaxLogonToMember = function()
 {
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
@@ -27,7 +27,7 @@
  * Authenticates to the highest level the current user is allowed to go to.
  */
 
-function ajaxLogonToElev()
+window.ajaxLogonToElev = function()
 {
   if ( auth_level == user_level )
     return true;
@@ -88,8 +88,14 @@
  * @param int The security level to authenticate at - see http://docs.enanocms.org/Help:Appendix_B
  */
 
-function ajaxLoginInit(call_on_finish, user_level)
+window.ajaxLoginInit = function(call_on_finish, user_level)
 {
+  load_component('messagebox');
+  load_component('flyin');
+  load_component('SpryEffects');
+  load_component('l10n');
+  load_component('crypto');
+  
   logindata = {};
   
   var title = ( user_level > USER_LEVEL_MEMBER ) ? $lang.get('user_login_ajax_prompt_title_elev') : $lang.get('user_login_ajax_prompt_title');
@@ -143,7 +149,7 @@
  * For compatibility only.
  */
 
-function ajaxLogonInit(call_on_finish, user_level)
+window.ajaxLogonInit = function(call_on_finish, user_level)
 {
   return ajaxLoginInit(call_on_finish, user_level);
 }
@@ -153,7 +159,7 @@
  * @param int One of AJAX_STATUS_*
  */
 
-function ajaxLoginSetStatus(status)
+window.ajaxLoginSetStatus = function(status)
 {
   if ( !logindata.mb_inner )
     return false;
@@ -305,7 +311,7 @@
  * @param object JSON packet to send
  */
 
-function ajaxLoginPerformRequest(json)
+window.ajaxLoginPerformRequest = function(json)
 {
   json = toJSONString(json);
   json = ajaxEscape(json);
@@ -331,7 +337,7 @@
  * @param object JSON response
  */
 
-function ajaxLoginProcessResponse(response)
+window.ajaxLoginProcessResponse = function(response)
 {
   // Did the server send a plaintext error?
   if ( response.mode == 'error' )
@@ -403,7 +409,7 @@
  * @param object Metadata to build off of
  */
 
-function ajaxLoginBuildForm(data)
+window.ajaxLoginBuildForm = function(data)
 {
   // let's hope this effectively preloads the image...
   var _ = document.createElement('img');
@@ -620,7 +626,7 @@
   }
 }
 
-function ajaxLoginSubmitForm(real, username, password, captcha)
+window.ajaxLoginSubmitForm = function(real, username, password, captcha)
 {
   // Perform AES test to make sure it's all working
   if ( !aes_self_test() )
@@ -761,7 +767,7 @@
   ajaxLoginPerformRequest(json_packet);
 }
 
-function ajaxLoginShowFriendlyError(response)
+window.ajaxLoginShowFriendlyError = function(response)
 {
   if ( !response.respawn_info )
     return false;
@@ -803,7 +809,7 @@
   body.appendChild(errbox);
 }
 
-function ajaxLoginGetErrorText(response)
+window.ajaxLoginGetErrorText = function(response)
 {
   switch ( response.error_code )
   {
@@ -868,3 +874,114 @@
   }
 }
 
+window.ajaxInitLogout = function()
+{
+  load_component('messagebox');
+  load_component('l10n');
+  var mb = new MessageBox(MB_YESNO|MB_ICONQUESTION, $lang.get('user_logout_confirm_title'), $lang.get('user_logout_confirm_body'));
+  mb.onclick['Yes'] = function()
+    {
+      window.location = makeUrlNS('Special', 'Logout/' + csrf_token + '/' + title);
+    }
+}
+
+window.mb_logout = function()
+{
+  ajaxInitLogout();
+}
+
+window.ajaxStartLogin = function()
+{
+  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;
+}
+
+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;
+}
+
+var navto_ns;
+var navto_pg;
+var navto_ul;
+
+window.ajaxLoginNavTo = function(namespace, page_id, min_level)
+{
+  // IE <6 pseudo-compatibility
+  if ( KILL_SWITCH )
+    return true;
+  navto_pg = page_id;
+  navto_ns = namespace;
+  navto_ul = min_level;
+  if ( auth_level < min_level )
+  {
+    ajaxPromptAdminAuth(function(k) {
+      ENANO_SID = k;
+      auth_level = navto_ul;
+      var loc = makeUrlNS(navto_ns, navto_pg);
+      if ( (ENANO_SID + ' ').length > 1 )
+        window.location = loc;
+    }, min_level);
+    return false;
+  }
+  var loc = makeUrlNS(navto_ns, navto_pg);
+  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;
+}
--- a/includes/clientside/static/md5.js	Sun Jun 22 18:13:59 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-// Javascript implementation of the and SHA1 hash algorithms - both written by Paul Johnston, licensed under the BSD license
-
-// MD5
-var hexcase = 0; var b64pad  = ""; var chrsz   = 8;
-function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
-function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
-function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
-function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
-function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
-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); }
-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); }
-function md5_hh(a, b, c, d, x, s, t) { return md5_cmn(b ^ c ^ d, a, b, x, s, t); }
-function md5_ii(a, b, c, d, x, s, t) { return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); }
-function core_hmac_md5(key, data) { var bkey = str2binl(key); if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz); return core_md5(opad.concat(hash), 512 + 128); }
-function safe_add(x, y) {var lsw = (x & 0xFFFF) + (y & 0xFFFF);var msw = (x >> 16) + (y >> 16) + (lsw >> 16);return (msw << 16) | (lsw & 0xFFFF); }
-function bit_rol(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); }
-function str2binl(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) << (i%32); return bin;}
-function binl2str(bin) { var str = ""; var mask = (1 << chrsz) - 1; for(var i = 0; i < bin.length * 32; i += chrsz) str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask); return str; }
-function binl2hex(binarray) { var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; var str = ""; for(var i = 0; i < binarray.length * 4; i++) { str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF); } return str; }
-function binl2b64(binarray) { var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; for(var i = 0; i < binarray.length * 4; i += 3) { var triplet = (((binarray[i >> 2] >> 8 * ( i   %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } } return str; }
-
-// SHA1
-function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
-function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
-function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
-function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
-function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
-function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
-function sha1_vm_test() {   return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; }
-function core_sha1(x, len) { x[len >> 5] |= 0x80 << (24 - len % 32); x[((len + 64 >> 9) << 4) + 15] = len; var w = Array(80); var a =  1732584193; var b = -271733879; var c = -1732584194; var d =  271733878; var e = -1009589776; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; var olde = e; for(var j = 0; j < 80; j++) { if(j < 16) w[j] = x[i + j]; else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j))); e = d; d = c; c = rol(b, 30); b = a; a = t; } a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); e = safe_add(e, olde); } return Array(a, b, c, d, e);}
-function sha1_ft(t, b, c, d){ if(t < 20) return (b & c) | ((~b) & d); if(t < 40) return b ^ c ^ d; if(t < 60) return (b & c) | (b & d) | (c & d); return b ^ c ^ d;}
-function sha1_kt(t){ return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 : (t < 60) ? -1894007588 : -899497514;}
-function core_hmac_sha1(key, data){ var bkey = str2binb(key); if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); return core_sha1(opad.concat(hash), 512 + 160);}
-function safe_add(x, y){ var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF);}
-function rol(num, cnt){ return (num << cnt) | (num >>> (32 - cnt));}
-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) << (32 - chrsz - i%32); return bin;}
-function binb2str(bin){ var str = ""; var mask = (1 << chrsz) - 1; for(var i = 0; i < bin.length * 32; i += chrsz) str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); return str;}
-function binb2hex(binarray){ 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 binb2b64(binarray){ var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; for(var i = 0; i < binarray.length * 4; i += 3) { var triplet = (((binarray[i   >> 2] >> 8 * (3 -  i   %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) |  ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } } return str;}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/clientside/static/messagebox.js	Tue Jun 24 23:37:23 2008 -0400
@@ -0,0 +1,586 @@
+// Message box and visual effect system
+
+/**
+ * The ultimate message box framework for Javascript
+ * Syntax is (almost) identical to the MessageBox command in NSIS
+ * @param int type - a bitfield consisting of the MB_* constants
+ * @param string title - the blue text at the top of the window
+ * @param string text - HTML for the body of the message box
+ * Properties:
+ *   onclick - an array of functions to be called on button click events
+ *             NOTE: key names are to be strings, and they must be the value of the input, CaSe-SeNsItIvE
+ *   onbeforeclick - same as onclick but called before the messagebox div is destroyed
+ * Methods:
+ *   destroy: kills the running message box
+ * Example:
+ *   var my_message = new MessageBox(MB_OK|MB_ICONSTOP, 'Error logging in', 'The username and/or password is incorrect. Please check the username and retype your password');
+ *   my_message.onclick['OK'] = function() {
+ *       document.getElementById('password').value = '';
+ *     };
+ * Deps:
+ *   Modern browser that supports DOM
+ *   darken() and enlighten() (above)
+ *   opacity() - required for darken() and enlighten()
+ *   MB_* constants are defined in enano-lib-basic.js
+ */
+
+var mb_current_obj;
+var mb_previously_had_darkener = false;
+
+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 = String(getHighestZ() + 5);
+  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 : getHighestZ();
+  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.removeChild(mbdiv.nextSibling);
+      mbdiv.parentNode.removeChild(mbdiv);
+      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);
+}
+
+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.removeChild(mbdiv.nextSibling); mbdiv.parentNode.removeChild(mbdiv); if ( !mb_previously_had_darkener ) enlighten(true);", to);
+  }
+  if(typeof mb.onclick[val] == 'function')
+  {
+    o = mb.onclick[val];
+    o();
+    o = false;
+  }
+}
+
+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!');
+    }
+}
+
+/**
+ * The miniPrompt function, for creating small prompts and dialogs. The window will be flown in and the window darkened with opac=0.4.
+ * @param function Will be passed an HTMLElement that is the body of the prompt window; the function can do with this as it pleases
+ */
+
+function miniPrompt(call_on_create)
+{
+  if ( !aclDisableTransitionFX )
+  {
+    load_component('flyin');
+  }
+  if ( document.getElementById('specialLayer_darkener') )
+  {
+    if ( document.getElementById('specialLayer_darkener').style.display != 'none' )
+    {
+      var opac = parseFloat(document.getElementById('specialLayer_darkener').style.opacity);
+      opac = opac * 100;
+      darken(aclDisableTransitionFX, opac);
+    }
+    else
+    {
+      darken(aclDisableTransitionFX, 40);
+    }
+  }
+  else
+  {
+    darken(aclDisableTransitionFX, 40);
+  }
+  
+  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);
+  }
+}
+
+/**
+ * For a given element, loops through the element and all of its ancestors looking for a miniPrompt div, and returns it. Returns false on failure.
+ * @param object:HTMLElement Child node to scan
+ * @return object
+ */
+
+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;
+}
+
+/**
+ * Destroys the first miniPrompt div encountered by recursively checking all parent nodes.
+ * Usage: <a href="javascript:miniPromptDestroy(this);">click</a>
+ * @param object:HTMLElement a child of the div.miniprompt
+ * @param bool If true, does not call enlighten().
+ */
+
+function miniPromptDestroy(obj, nofade)
+{
+  obj = miniPromptGetParent(obj);
+  if ( !obj )
+    return false;
+  
+  // found it
+  var parent = obj.parentNode;
+  if ( !nofade )
+    enlighten(aclDisableTransitionFX);
+  if ( aclDisableTransitionFX )
+  {
+    parent.removeChild(obj);
+  }
+  else
+  {
+    var timeout = fly_out_top(obj, true, true);
+    setTimeout(function()
+      {
+        parent.removeChild(obj);
+      }, timeout);
+  }
+}
+
+/**
+ * Simple test case
+ */
+
+function miniPromptTest()
+{
+  miniPrompt(function(div) { div.innerHTML = 'hello world! <a href="#" onclick="miniPromptDestroy(this); return false;">destroy me</a>'; });
+}
+
+/**
+ * Message box system for miniPrompts. Less customization but easier to scale than the regular messageBox framework.
+ * @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);
+       }
+     }
+   ]
+ });
+ </code>
+ */
+
+function miniPromptMessage(parms)
+{
+  if ( !parms.title || !parms.message || !parms.buttons )
+    return false;
+  
+  return miniPrompt(function(parent)
+    {
+      try
+      {
+        var h3 = document.createElement('h3');
+        h3.appendChild(document.createTextNode(parms.title));
+        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';
+        
+        parent.appendChild(h3);
+        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];
+            }
+          }
+          button.input.onclick = function(e)
+          {
+            try
+            {
+              this.clickAction(e);
+            }
+            catch(e)
+            {
+              console.error(e);
+            }
+            return false;
+          }
+          parent.appendChild(button.input);
+        }
+        if ( parms.buttons[0] )
+        {
+          setTimeout(function()
+            {
+              parms.buttons[0].input.focus();
+            }, 300);
+        }
+      }
+      catch ( e )
+      {
+        console.error(e);
+      }
+    });
+}
+
+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'
+        },
+        onclick: function() {
+          miniPromptDestroy(this);
+        }
+      },
+      {
+        text: 'Retry',
+        color: 'blue',
+        onclick: function() {
+          miniPromptDestroy(this);
+        }
+      },
+      {
+        text: 'Ignore',
+        color: 'green',
+        onclick: function() {
+          miniPromptDestroy(this);
+        }
+      }
+    ]
+  });
+}
+
--- a/includes/clientside/static/misc.js	Sun Jun 22 18:13:59 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,655 +0,0 @@
-// Some additional 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 };
-}
-
-function fetch_dimensions(o) {
-  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;
-}
-
-function ajaxReverseDNS(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() {
-      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);
-        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();
-      }
-    });
-}
-
-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;
-}
-
-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);
-}
-
-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;
-  }
-}
-
-function enableUnload()
-{
-  window._unloadmsg = null;
-  window.onbeforeunload = null;
-}
-
-/**
- * Gets the highest z-index of all divs in the document
- * @return integer
- */
-function getHighestZ()
-{
-  z = 0;
-  var divs = document.getElementsByTagName('div');
-  for(var i = 0; i < divs.length; i++)
-  {
-    if(divs[i].style.zIndex > z) z = divs[i].style.zIndex;
-  }
-  return z;
-}
-
-function isKeyPressed(event)
-{
-  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);
-}
-
-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;}
-function createCookie(name,value,days){if (days){var date = new Date();date.setTime(date.getTime()+(days*24*60*60*1000));var expires = "; expires="+date.toGMTString();}else var expires = "";document.cookie = name+"="+value+expires+"; path=/";}
-function eraseCookie(name) {createCookie(name,"",-1);}
-
-var busyBannerID;
-function goBusy(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);
-}
-
-function setAjaxLoading()
-{
-  if ( document.getElementById('ajaxloadicon') )
-  {
-    document.getElementById('ajaxloadicon').src=ajax_load_icon;
-  }
-}
-
-function unsetAjaxLoading()
-{
-  if ( document.getElementById('ajaxloadicon') )
-  {
-    document.getElementById('ajaxloadicon').src=scriptPath + '/images/spacer.gif';
-  }
-}
-
-/*
- * AJAX login box (experimental)
- * Moved / rewritten in login.js
- */
-
-// Included only for API-compatibility
-function ajaxPromptAdminAuth(call_on_ok, level)
-{
-  ajaxLogonInit(call_on_ok, level);
-}
-
-// 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);
-
-    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;
-}
-
-/**
- * Insert a DOM object _after_ the specified child.
- * @param object Parent node
- * @param object Node to insert
- * @param object Node to insert after
- */
-
-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);
-    }
-  }
-}
-
-/**
- * Validates an e-mail address.
- * @param string E-mail address
- * @return bool
- */
-
-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;
-}
-
-/**
- * Validates a username.
- * @param string Username to test
- * @return bool
- */
-
-function validateUsername(username)
-{
-  var regex = new RegExp('^[^<>&\?\'"%\n\r/]+$', '');
-  return ( username.match(regex) ) ? true : false;
-}
-
-/**
- * Equivalent of PHP's time()
- * @return int
- */
-
-function unix_time()
-{
-  return parseInt((new Date()).getTime()/1000);
-}
-
-/*
- * Utility functions, moved from windows.js
- */
- 
-// getElementWidth() and getElementHeight()
-// Source: http://www.aspandjavascript.co.uk/javascript/javascript_api/get_element_width_height.asp
-
-function getElementHeight(Elem) {
-  if (ns4) 
-  {
-    var elem = getObjNN4(document, Elem);
-    return elem.clip.height;
-  } 
-  else
-  {
-    if(document.getElementById) 
-    {
-      var elem = document.getElementById(Elem);
-    }
-    else if (document.all)
-    {
-      var elem = document.all[Elem];
-    }
-    if (op5) 
-    { 
-      xPos = elem.style.pixelHeight;
-    }
-    else
-    {
-      xPos = elem.offsetHeight;
-    }
-    return xPos;
-  } 
-}
-
-function getElementWidth(Elem) {
-  if (ns4) {
-    var elem = getObjNN4(document, Elem);
-    return elem.clip.width;
-  } else {
-    if(document.getElementById) {
-      var elem = document.getElementById(Elem);
-    } else if (document.all){
-      var elem = document.all[Elem];
-    }
-    if (op5) {
-      xPos = elem.style.pixelWidth;
-    } else {
-      xPos = elem.offsetWidth;
-    }
-    return xPos;
-  }
-}
-
-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;
-}
-
-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;
-}
-
-/**
- * Sanitizes a page URL string so that it can safely be stored in the database.
- * @param string Page ID to sanitize
- * @return string Cleaned text
- */
-
-function sanitize_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);
-
-  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;
-}
-
-/**
- * Removes character escapes in a page ID string
- * @param string Page ID string to dirty up
- * @return string
- */
-
-function dirtify_page_id(page_id)
-{
-  // 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;
-}
-
-/*
- * Expandable fieldsets
- */
-
-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]);
-  }
-}
-
-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);
-  }
-}
-
-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);
-  }
-}
-
-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];
-      $(a).rmClass('expander-open');
-      $(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];
-      $(a).rmClass('expander-closed');
-      $(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');
-}
-
-/**
- * Equivalent to PHP's explode function.
- */
-
-function explode(needle, haystack)
-{
-  return haystack.split(needle);
-}
-
-/**
- * Equivalent to PHP's in_array function.
- */
-
-function in_array(needle, haystack)
-{
-  for(var i in haystack)
-  {
-    if(haystack[i] == needle) return i;
-  }
-  return false;
-}
-
-addOnloadHook(expander_onload);
--- a/includes/clientside/static/paginate.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/paginate.js	Tue Jun 24 23:37:23 2008 -0400
@@ -11,7 +11,7 @@
  
 var pagin_objects = new Object();
 
-function paginator(data, callback, offset, perpage, passer)
+window.paginator = function(data, callback, offset, perpage, passer)
 {
   if ( !perpage || typeof(perpage) != 'number' || ( typeof(perpage) == 'number' && perpage < 1 ) )
   {
@@ -65,7 +65,7 @@
  * @access private
  */
 
-function _build_paginator(this_page)
+window._build_paginator = function(this_page)
 {
   var div_styling = ( IE ) ? 'width: 1px; margin: 10px auto 10px 0;' : 'display: table; margin: 10px 0 0 auto;';
   var begin = '<div class="tblholder" style="'+div_styling+'"><table border="0" cellspacing="1" cellpadding="4"><tr><th>' + $lang.get('paginate_lbl_page') + '</th>';
@@ -195,7 +195,7 @@
 
 var __paginateLock = false;
 
-function jspaginator_goto(pagin_id, jump_to)
+window.jspaginator_goto = function(pagin_id, jump_to)
 {
   if ( __paginateLock )
     return false;
@@ -267,7 +267,7 @@
   }
 }
 
-function paginator_goto(parentobj, this_page, num_pages, perpage, url_string)
+window.paginator_goto = function(parentobj, this_page, num_pages, perpage, url_string)
 {
   var height = $dynano(parentobj).Height();
   var width  = $dynano(parentobj).Width();
@@ -312,7 +312,7 @@
   div.style.left = left_pos + 'px';
 }
 
-function paginator_submit(obj, max, perpage, formatstring)
+window.paginator_submit = function(obj, max, perpage, formatstring)
 {
   var userinput = obj.previousSibling.previousSibling.value;
   userinput = parseInt(userinput);
@@ -335,3 +335,57 @@
   }
 }
 
+// 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);
+
+    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/rijndael.js	Sun Jun 22 18:13:59 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,610 +0,0 @@
-/* rijndael.js      Rijndael Reference Implementation
-   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
- without fee, for any purpose and by any individual or organization, is hereby
- granted, provided that the above copyright notice and this paragraph appear 
- in all copies. Distribution as a part of an application or binary must
- include the above copyright notice in the documentation and/or other materials
- 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_ :)
-
-   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. 
-
-   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.
-
-                                               Fritz Schneider
-                                               fritz at cs.ucsd.edu
- 
-*/
-
-// Rijndael parameters --  Valid values are 128, 192, or 256
-
-var keySizeInBits =   ( typeof AES_BITS == 'number' ) ? AES_BITS : 128;
-var blockSizeInBits = ( typeof AES_BLOCKSIZE == 'number' ) ? AES_BLOCKSIZE : 128;
-
-///////  You shouldn't have to modify anything below this line except for
-///////  the function getRandomBytes().
-//
-// Note: in the following code the two dimensional arrays are indexed as
-//       you would probably expect, as array[row][column]. The state arrays
-//       are 2d arrays of the form state[4][Nb].
-
-
-// The number of rounds for the cipher, indexed by [Nk][Nb]
-var roundsArray = [ ,,,,[,,,,10,, 12,, 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] ];
-
-// The round constants used in subkey expansion
-var Rcon = [ 
-0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 
-0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 
-0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 
-0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 
-0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ];
-
-// Precomputed lookup table for the SBox
-var SBox = [
- 99, 124, 119, 123, 242, 107, 111, 197,  48,   1, 103,  43, 254, 215, 171, 
-118, 202, 130, 201, 125, 250,  89,  71, 240, 173, 212, 162, 175, 156, 164, 
-114, 192, 183, 253, 147,  38,  54,  63, 247, 204,  52, 165, 229, 241, 113, 
-216,  49,  21,   4, 199,  35, 195,  24, 150,   5, 154,   7,  18, 128, 226, 
-235,  39, 178, 117,   9, 131,  44,  26,  27, 110,  90, 160,  82,  59, 214, 
-179,  41, 227,  47, 132,  83, 209,   0, 237,  32, 252, 177,  91, 106, 203, 
-190,  57,  74,  76,  88, 207, 208, 239, 170, 251,  67,  77,  51, 133,  69, 
-249,   2, 127,  80,  60, 159, 168,  81, 163,  64, 143, 146, 157,  56, 245, 
-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, 
-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,
-248, 152,  17, 105, 217, 142, 148, 155,  30, 135, 233, 206,  85,  40, 223,
-140, 161, 137,  13, 191, 230,  66, 104,  65, 153,  45,  15, 176,  84, 187,  
- 22 ];
-
-// Precomputed lookup table for the inverse SBox
-var SBoxInverse = [
- 82,   9, 106, 213,  48,  54, 165,  56, 191,  64, 163, 158, 129, 243, 215, 
-251, 124, 227,  57, 130, 155,  47, 255, 135,  52, 142,  67,  68, 196, 222, 
-233, 203,  84, 123, 148,  50, 166, 194,  35,  61, 238,  76, 149,  11,  66, 
-250, 195,  78,   8,  46, 161, 102,  40, 217,  36, 178, 118,  91, 162,  73, 
-109, 139, 209,  37, 114, 248, 246, 100, 134, 104, 152,  22, 212, 164,  92, 
-204,  93, 101, 182, 146, 108, 112,  72,  80, 253, 237, 185, 218,  94,  21,  
- 70,  87, 167, 141, 157, 132, 144, 216, 171,   0, 140, 188, 211,  10, 247, 
-228,  88,   5, 184, 179,  69,   6, 208,  44,  30, 143, 202,  63,  15,   2, 
-193, 175, 189,   3,   1,  19, 138, 107,  58, 145,  17,  65,  79, 103, 220, 
-234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116,  34, 231, 173,
- 53, 133, 226, 249,  55, 232,  28, 117, 223, 110,  71, 241,  26, 113,  29, 
- 41, 197, 137, 111, 183,  98,  14, 170,  24, 190,  27, 252,  86,  62,  75, 
-198, 210, 121,  32, 154, 219, 192, 254, 120, 205,  90, 244,  31, 221, 168,
- 51, 136,   7, 199,  49, 177,  18,  16,  89,  39, 128, 236,  95,  96,  81,
-127, 169,  25, 181,  74,  13,  45, 229, 122, 159, 147, 201, 156, 239, 160,
-224,  59,  77, 174,  42, 245, 176, 200, 235, 187,  60, 131,  83, 153,  97, 
- 23,  43,   4, 126, 186, 119, 214,  38, 225, 105,  20,  99,  85,  33,  12,
-125 ];
-
-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;
-}
-
-// This method circularly shifts the array left by the number of elements
-// given in its parameter. It returns the resulting array and is used for 
-// the ShiftRow step. Note that shift() and push() could be used for a more 
-// 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;
-}
-
-// Cipher parameters ... do not change these
-var Nk = keySizeInBits / 32;                   
-var Nb = blockSizeInBits / 32;
-var Nr = roundsArray[Nk][Nb];
-
-// 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));
-}
-
-// Multiplies the two elements of GF(2^8) together and returns the result.
-// See the Rijndael spec, but should be straightforward: for each power of
-// the indeterminant that has a 1 coefficient in x, add y times that power
-// 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;
-}
-
-// Performs the substitution step of the cipher. State is the 2d array of
-// state information (see spec) and direction is string indicating whether
-// we are performing the forward substitution ("encrypt") or inverse 
-// 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]];
-}
-
-// 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]);
-
-}
-
-// Performs the column mixing step of the cipher. Most of these steps can
-// be combined into table lookups on 32bit values (at least for encryption)
-// 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];
-  }
-}
-
-// 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);
-  }
-}
-
-// This function creates the expanded key from the input (128/192/256-bit)
-// key. The parameter key is an array of bytes holding the value of the key.
-// The returned value is an array whose elements are the 32-bit words that 
-// make up the expanded key.
-
-function keyExpansion(key) {
-  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];
-
-  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;
-}
-
-// Rijndael's round functions... 
-
-function Round(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");
-}
-
-function FinalRound(state, roundKey) {
-  byteSub(state, "encrypt");
-  shiftRow(state, "encrypt");
-  addRoundKey(state, roundKey);
-}
-
-function InverseFinalRound(state, roundKey){
-  addRoundKey(state, roundKey);
-  shiftRow(state, "decrypt");
-  byteSub(state, "decrypt");  
-}
-
-// encrypt is the basic encryption function. It takes parameters
-// block, an array of bytes representing a plaintext block, and expandedKey,
-// an array of words representing the expanded key previously returned by
-// 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;
-
-  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
-// block, an array of bytes representing a ciphertext block, and expandedKey,
-// an array of words representing the expanded key previously returned by
-// 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;
-
-  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 method takes a byte array (byteArray) and converts it to a string by
-// applying String.fromCharCode() to each value and concatenating the result.
-// The resulting string is returned. Note that this function SKIPS zero bytes
-// under the assumption that they are padding added in formatPlaintext().
-// Obviously, do not invoke this method on raw data that can contain zero
-// bytes. It is really only appropriate for printable ASCII/Latin-1 
-// 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 += String.fromCharCode(byteArray[i]);
-  return result;
-}
-
-// This function takes an array of bytes (byteArray) and converts them
-// to a hexadecimal string. Array element 0 is found at the beginning of 
-// the resulting string, high nibble first. Consecutive elements follow
-// similarly, for example [16, 255] --> "10ff". The function returns a 
-// 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);
-
-  return result;
-}
-
-// This function converts a string containing hexadecimal digits to an 
-// array of bytes. The resulting byte array is filled in the order the
-// values occur in the string, for example "10FF" --> [16, 255]. This
-// 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;
-}
-
-// This function packs an array of bytes into the four row form defined by
-// Rijndael. It assumes the length of the array of bytes is divisible by
-// four. Bytes are filled in according to the Rijndael spec (starting with
-// 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;
-
-  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
-// by Rijndael into a single 1d array of bytes. It assumes the input "packed"
-// is a packed array. Bytes are filled in according to the Rijndael spec. 
-// 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;
-}
-
-// This function takes a prospective plaintext (string or array of bytes)
-// and pads it with zero bytes if its length is not a multiple of the block 
-// size. If plaintext is a string, it is converted to an array of bytes
-// in the process. The type checking can be made much nicer using the 
-// instanceof operator, but this operator is not available until IE5.0 so I 
-// chose to use the heuristic below. 
-
-function formatPlaintext(plaintext) {
-  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;
-  } 
-
-  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
-// TO RETURN HIGHER QUALITY RANDOM BYTES IF YOU ARE USING THIS FOR A "REAL"
-// 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;
-}
-
-// rijndaelEncrypt(plaintext, key, mode)
-// Encrypts the plaintext using the given key and in the given mode. 
-// The parameter "plaintext" can either be a string or an array of bytes. 
-// The parameter "key" must be an array of key bytes. If you have a hex 
-// string representing the key, invoke hexToByteArray() on it to convert it 
-// to an array of bytes. The third parameter "mode" is a string indicating
-// the encryption mode to use, either "ECB" or "CBC". If the parameter is
-// omitted, ECB is assumed.
-// 
-// An array of bytes representing the cihpertext is returned. To convert 
-// this array to hex, invoke byteArrayToHex() on it. If you are using this 
-// "for real" it is a good idea to change the function getRandomBytes() to 
-// 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
-
-  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);
-
-  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;
-}
-
-// rijndaelDecrypt(ciphertext, key, mode)
-// Decrypts the using the given key and mode. The parameter "ciphertext" 
-// must be an array of bytes. The parameter "key" must be an array of key 
-// bytes. If you have a hex string representing the ciphertext or key, 
-// invoke hexToByteArray() on it to convert it to an array of bytes. The
-// parameter "mode" is a string, either "CBC" or "ECB".
-// 
-// An array of bytes representing the plaintext is returned. To convert 
-// this array to a hex string, invoke byteArrayToHex() on it. To convert it 
-// 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
-
-  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);
- 
-  // 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);
-
-  return pt;
-}
-
-function stringToByteArray(text)
-{
-  result = new Array();
-  for ( i=0; i<text.length; i++ )
-  {
-    result[result.length] = text.charCodeAt(i);
-  }
-  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() );
-}
-
--- a/includes/clientside/static/sha256.js	Sun Jun 22 18:13:59 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/* A JavaScript implementation of the Secure Hash Algorithm, SHA-256
- * Version 0.3 Copyright Angel Marin 2003-2004 - http://anmar.eu.org/
- * Distributed under the BSD License
- * Some bits taken from Paul Johnston's SHA-1 implementation
- */
-/*
-Copyright (c) 2003-2004, Angel Marin
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-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.
- * 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.
- * 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.
-
-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
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-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);
-}
-function S (X, n) {return ( X >>> n ) | (X << (32 - n));}
-function R (X, n) {return ( 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 (S(x, 2) ^ S(x, 13) ^ S(x, 22));}
-function Sigma1256(x) {return (S(x, 6) ^ S(x, 11) ^ S(x, 25));}
-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;
-}
-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;
-}
-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;
-}
-function hex_sha256(s){return binb2hex(core_sha256(str2binb(s),s.length * chrsz));}
--- a/includes/clientside/static/sliders.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/sliders.js	Tue Jun 24 23:37:23 2008 -0400
@@ -5,7 +5,7 @@
 
 var sliders_initted = false;
       
-function initSliders()
+var initSliders = function()
 {
   sliders_initted = true;
   if ( KILL_SWITCH )
@@ -57,6 +57,8 @@
     }
 }
 
+addOnloadHook(initSliders);
+
 // this is one of our divs, it just has a DOM reference to the element and the original height
 function div(_el, _ht)
 {
--- a/includes/clientside/static/template-compiler.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/template-compiler.js	Tue Jun 24 23:37:23 2008 -0400
@@ -1,7 +1,7 @@
 // An implementation of Enano's template compiler in Javascript. Same exact API
 // as the PHP version - constructor accepts text, then the assign_vars, assign_bool, and run methods.
 
-function templateParser(text)
+window.templateParser = function(text)
 {
   this.tpl_code    = text;
   this.tpl_strings = new Object();
@@ -11,7 +11,7 @@
   this.run         = __tpRun;
 }
 
-function __tpAssignVars(vars)
+window.__tpAssignVars = function(vars)
 {
   for(var i in vars)
   {
@@ -19,7 +19,7 @@
   }
 }
 
-function __tpAssignBool(vars)
+window.__tpAssignBool = function(vars)
 {
   for(var i in vars)
   {
@@ -27,7 +27,7 @@
   }
 }
 
-function __tpRun()
+window.__tpRun = function()
 {
   if(typeof(this.tpl_code) == 'string')
   {
@@ -45,7 +45,7 @@
   return false;
 }
 
-function __tpCompileTemplate(code)
+window.__tpCompileTemplate = function(code)
 {
   // Compile plaintext/template code to javascript code
   code = code.replace(/\\/g, "\\\\");
@@ -60,7 +60,7 @@
   return code;
 }
 
-function __tpExtractVars(code)
+window.__tpExtractVars = function(code)
 {
   code = code.replace('\\', "\\\\");
   code = code.replace("'",  "\\'");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/clientside/static/tinymce-init.js	Tue Jun 24 23:37:23 2008 -0400
@@ -0,0 +1,89 @@
+// init TinyMCE
+var head = document.getElementsByTagName('head')[0];
+
+if ( document.getElementById('mdgCss') )
+{
+  var css_url = document.getElementById('mdgCss').href;
+}
+else
+{
+  var css_url = scriptPath + '/includes/clientside/css/enano_shared.css';
+}
+
+var do_popups = ( is_Safari ) ? '' : ',inlinepopups';
+var _skin = ( typeof(tinymce_skin) == 'string' ) ? tinymce_skin : 'default';
+var tinymce_initted = false;
+
+var enano_tinymce_options = {
+  mode : "none",
+  plugins : 'table,save,safari,pagebreak,style,layer,advhr,insertdatetime,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras' + 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,|,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
+};
+
+var enano_tinymce_gz_options = {
+	plugins : 'table,save,safari,pagebreak,style,layer,advhr,insertdatetime,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras' + do_popups,
+	themes : 'advanced',
+	languages : 'en',
+	disk_cache : true,
+	debug : false
+};
+
+if ( !KILL_SWITCH && !DISABLE_MCE )
+{
+  // load MCE with XHR
+  var ajax = ajaxMakeXHR();
+  var uri = scriptPath + '/includes/clientside/tinymce/tiny_mce_gzip.js';
+  ajax.open('GET', uri, false);
+  ajax.send(null);
+  if ( ajax.readyState == 4 && ajax.status == 200 )
+  {
+    eval_global(ajax.responseText);
+    tinyMCE_GZ.init(enano_tinymce_gz_options);
+  }
+  else
+  {
+    console.error('TinyMCE load failed');
+  }
+}
+
+// Check tinyMCE to make sure its init is finished
+window.tinymce_preinit_check = function()
+{
+  if ( typeof(tinyMCE.init) != 'function' )
+    return false;
+  if ( typeof(tinymce.DOM) != 'object' )
+    return false;
+  if ( typeof(tinymce.DOM.get) != 'function' )
+    return false;
+  if ( typeof(enano_tinymce_gz_options) != 'object' )
+    return false;
+  return true;
+}
+
+var initTinyMCE = function(e)
+{
+  if ( typeof(tinyMCE) == 'object' )
+  {
+    if ( !KILL_SWITCH && !DISABLE_MCE )
+    {
+      if ( !tinymce_preinit_check() )
+      {
+        setTimeout('initTinyMCE(false);', 200);
+        return false;
+      }
+      tinyMCE.init(enano_tinymce_options);
+      tinymce_initted = true;
+    }
+  }
+};
--- a/includes/clientside/static/toolbar.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/static/toolbar.js	Tue Jun 24 23:37:23 2008 -0400
@@ -1,6 +1,6 @@
 // Page toolbar - selecting buttons
 
-function unselectAllButtonsMajor()
+window.unselectAllButtonsMajor = function()
 {
   if ( !document.getElementById('pagebar_main') )
     return false;
@@ -15,7 +15,7 @@
   }
 }
 
-function unselectAllButtonsMinor()
+window.unselectAllButtonsMinor = function()
 {
   if ( !document.getElementById('pagebar_main') )
     return false;
@@ -36,7 +36,7 @@
   }
 }
 
-function selectButtonMajor(which)
+window.selectButtonMajor = function(which)
 {
   if ( !document.getElementById('pagebar_main') )
     return false;
@@ -50,7 +50,7 @@
   }
 }
 
-function selectButtonMinor(which)
+window.selectButtonMinor = function(which)
 {
   if ( !document.getElementById('pagebar_main') )
     return false;
--- a/includes/clientside/tinymce/tiny_mce_gzip.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/tinymce/tiny_mce_gzip.js	Tue Jun 24 23:37:23 2008 -0400
@@ -1,1 +1,1 @@
-var tinyMCE_GZ={settings:{themes:"",plugins:"",languages:"",disk_cache:true,page_name:"tiny_mce_gzip.php",debug:false,suffix:""},init:function(E,A,F){var D=this,G,C,B=document.getElementsByTagName("script");for(G in E){D.settings[G]=E[G]}E=D.settings;for(C=0;C<B.length;C++){G=B[C];if(G.src&&G.src.indexOf("tiny_mce")!=-1){D.baseURL=G.src.substring(0,G.src.lastIndexOf("/"))}}if(!D.coreLoaded){D.loadScripts(1,E.themes,E.plugins,E.languages,A,F)}},loadScripts:function(co,th,pl,la,cb,sc){var t=this,x,w=window,q,c=0,ti,s=t.settings;function get(s){x=0;try{x=new ActiveXObject(s)}catch(s){}return x}q="js=true&diskcache="+(s.disk_cache?"true":"false")+"&core="+(co?"true":"false")+"&suffix="+escape(s.suffix)+"&themes="+escape(th)+"&plugins="+escape(pl)+"&languages="+escape(la);if(co){t.coreLoaded=1}x=w.XMLHttpRequest?new XMLHttpRequest():get("Msxml2.XMLHTTP")||get("Microsoft.XMLHTTP");x.overrideMimeType&&x.overrideMimeType("text/javascript");x.open("GET",t.baseURL+"/"+s.page_name+"?"+q,!!cb);x.send("");if(cb){ti=w.setInterval(function(){if(x.readyState==4||c++>10000){w.clearInterval(ti);if(c<10000&&x.status==200){t.loaded=1;t.eval(x.responseText);tinymce.dom.Event.domLoaded=true;cb.call(sc||t,x)}ti=x=null}},10)}else{t.eval(x.responseText)}},start:function(){var B=this,F=tinymce.each,C=B.settings,A,D=C.languages.split(",");tinymce.suffix=C.suffix;tinymce.create("tinymce.compressor.ScriptLoader:tinymce.dom.ScriptLoader",{loadScripts:function(M,G,J){var I=this,K=[],H=[],L=[];F(M,function(O){var N=O.url;if((!I.lookup[N]||I.lookup[N].state!=2)&&N.indexOf(B.baseURL)===0){if(N.indexOf("editor_template")!=-1){K.push(/\/themes\/([^\/]+)/.exec(N)[1]);E(N,1)}if(N.indexOf("editor_plugin")!=-1){H.push(/\/plugins\/([^\/]+)/.exec(N)[1]);E(N,1)}if(N.indexOf("/langs/")!=-1){L.push(/\/langs\/([^.]+)/.exec(N)[1]);E(N,1)}}});if(K.length+H.length+L.length>0){if(A.settings.strict_mode){B.loadScripts(0,K.join(","),H.join(","),L.join(","),G,J);return }else{B.loadScripts(0,K.join(","),H.join(","),L.join(","),G,J)}}return I.parent(M,G,J)}});A=tinymce.ScriptLoader=new tinymce.compressor.ScriptLoader();function E(G,H){var I;if(!H){G=B.baseURL+G}I={url:G,state:2};A.queue.push(I);A.lookup[I.url]=I}F(D,function(G){if(G){E("/langs/"+G+".js")}});F(C.themes.split(","),function(G){if(G){E("/themes/"+G+"/editor_template"+C.suffix+".js");F(D,function(H){if(H){E("/themes/"+G+"/langs/"+H+".js")}})}});F(C.plugins.split(","),function(G){if(G){E("/plugins/"+G+"/editor_plugin"+C.suffix+".js");F(D,function(H){if(H){E("/plugins/"+G+"/langs/"+H+".js")}})}})},end:function(){},eval:function(co){var w=window;if(!w.execScript){if(/Gecko/.test(navigator.userAgent)){eval(co,w)}else{eval.call(w,co)}}else{w.execScript(co)}}};
\ No newline at end of file
+var tinyMCE_GZ={settings:{themes:"",plugins:"",languages:"",disk_cache:true,page_name:"tiny_mce_gzip.php",debug:false,suffix:""},init:function(E,A,F){var D=this,G,C,B=document.getElementsByTagName("script");for(G in E){D.settings[G]=E[G]}E=D.settings;D.baseURL = scriptPath + '/includes/clientside/tinymce';if(!D.coreLoaded){D.loadScripts(1,E.themes,E.plugins,E.languages,A,F)}},loadScripts:function(co,th,pl,la,cb,sc){var t=this,x,w=window,q,c=0,ti,s=t.settings;function get(s){x=0;try{x=new ActiveXObject(s)}catch(s){}return x}q="js=true&diskcache="+(s.disk_cache?"true":"false")+"&core="+(co?"true":"false")+"&suffix="+escape(s.suffix)+"&themes="+escape(th)+"&plugins="+escape(pl)+"&languages="+escape(la);if(co){t.coreLoaded=1}x=w.XMLHttpRequest?new XMLHttpRequest():get("Msxml2.XMLHTTP")||get("Microsoft.XMLHTTP");x.overrideMimeType&&x.overrideMimeType("text/javascript");x.open("GET",t.baseURL+"/"+s.page_name+"?"+q,!!cb);x.send("");if(cb){ti=w.setInterval(function(){if(x.readyState==4||c++>10000){w.clearInterval(ti);if(c<10000&&x.status==200){t.loaded=1;t.eval(x.responseText);tinymce.dom.Event.domLoaded=true;cb.call(sc||t,x)}ti=x=null}},10)}else{t.eval(x.responseText)}},start:function(){var B=this,F=tinymce.each,C=B.settings,A,D=C.languages.split(",");tinymce.suffix=C.suffix;tinymce.create("tinymce.compressor.ScriptLoader:tinymce.dom.ScriptLoader",{loadScripts:function(M,G,J){var I=this,K=[],H=[],L=[];F(M,function(O){var N=O.url;if((!I.lookup[N]||I.lookup[N].state!=2)&&N.indexOf(B.baseURL)===0){if(N.indexOf("editor_template")!=-1){K.push(/\/themes\/([^\/]+)/.exec(N)[1]);E(N,1)}if(N.indexOf("editor_plugin")!=-1){H.push(/\/plugins\/([^\/]+)/.exec(N)[1]);E(N,1)}if(N.indexOf("/langs/")!=-1){L.push(/\/langs\/([^.]+)/.exec(N)[1]);E(N,1)}}});if(K.length+H.length+L.length>0){if(A.settings.strict_mode){B.loadScripts(0,K.join(","),H.join(","),L.join(","),G,J);return }else{B.loadScripts(0,K.join(","),H.join(","),L.join(","),G,J)}}return I.parent(M,G,J)}});A=tinymce.ScriptLoader=new tinymce.compressor.ScriptLoader();function E(G,H){var I;if(!H){G=B.baseURL+G}I={url:G,state:2};A.queue.push(I);A.lookup[I.url]=I}F(D,function(G){if(G){E("/langs/"+G+".js")}});F(C.themes.split(","),function(G){if(G){E("/themes/"+G+"/editor_template"+C.suffix+".js");F(D,function(H){if(H){E("/themes/"+G+"/langs/"+H+".js")}})}});F(C.plugins.split(","),function(G){if(G){E("/plugins/"+G+"/editor_plugin"+C.suffix+".js");F(D,function(H){if(H){E("/plugins/"+G+"/langs/"+H+".js")}})}})},end:function(){},eval:function(co){var w=window;if(!w.execScript){if(/Gecko/.test(navigator.userAgent)){eval(co,w)}else{eval.call(w,co)}}else{w.execScript(co)}}};
\ No newline at end of file
--- a/includes/clientside/tinymce/tiny_mce_gzip_src.js	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/clientside/tinymce/tiny_mce_gzip_src.js	Tue Jun 24 23:37:23 2008 -0400
@@ -8,7 +8,7 @@
 		debug : false,
 		suffix : ''
 	},
-
+  
 	init : function(s, cb, sc) {
 		var t = this, n, i, nl = document.getElementsByTagName('script');
 
@@ -17,12 +17,7 @@
 
 		s = t.settings;
 
-		for (i=0; i<nl.length; i++) {
-			n = nl[i];
-
-			if (n.src && n.src.indexOf('tiny_mce') != -1)
-				t.baseURL = n.src.substring(0, n.src.lastIndexOf('/'));
-		}
+		t.baseURL = scriptPath + '/includes/clientside/tinymce';
 
 		if (!t.coreLoaded)
 			t.loadScripts(1, s.themes, s.plugins, s.languages, cb, sc);
--- a/includes/functions.php	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/functions.php	Tue Jun 24 23:37:23 2008 -0400
@@ -3053,8 +3053,8 @@
   for ( $i = 0; $i < count($jscript[0]); $i++ )
   {
     $js =& $jscript[2][$i];
-    
-    // echo('<pre>' . "-----------------------------------------------------------------------------\n" . htmlspecialchars($js) . '</pre>');
+    if ( empty($js) )
+      continue;
     
     $js = $jsc->getClean($js);
     
--- a/includes/pageutils.php	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/pageutils.php	Tue Jun 24 23:37:23 2008 -0400
@@ -1977,7 +1977,14 @@
   public static function acl_json($parms = '{ }')
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
-    $parms = enano_json_decode($parms);
+    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/includes/template.php	Sun Jun 22 18:13:59 2008 -0400
+++ b/includes/template.php	Tue Jun 24 23:37:23 2008 -0400
@@ -297,7 +297,12 @@
   }
   function add_header($html)
   {
-    $this->additional_headers .= "\n" . $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)
   {
@@ -977,10 +982,6 @@
     // Add the e-mail address client code to the header
     $this->add_header($email->jscode());
     
-    // Add language file
-    $lang_uri = makeUrlNS('Special', 'LangExportJSON/' . $lang->lang_id, false, true);
-    $this->add_header("<script type=\"text/javascript\" src=\"$lang_uri\"></script>");
-    
     // Generate the code for the Log out and Change theme sidebar buttons
     // Once again, the new template parsing system can be used here
     
@@ -1061,6 +1062,7 @@
       var ENANO_CREATEPAGE_PARAMS = \'_do=&pagename='. $urlname_clean .'&namespace=' . $local_namespace . '\';
       var ENANO_SPECIAL_CHANGESTYLE = \''. makeUrlNS('Special', 'ChangeStyle') .'\';
       var namespace_list = new Array();
+      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']) : '' ) .'\';
@@ -1097,7 +1099,6 @@
       'ADMIN_SID_AMP_HTML'=>$ash,
       'ADMIN_SID_AUTO'=>$as2,
       'ADMIN_SID_RAW'=> ( is_string($session->sid_super) ? $session->sid_super : '' ),
-      'ADDITIONAL_HEADERS'=>$this->additional_headers,
       'COPYRIGHT'=>RenderMan::parse_internal_links(getConfig('copyright_notice')),
       'TOOLBAR_EXTRAS'=>$this->toolbar_menu,
       'REQUEST_URI'=>$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'],
@@ -1130,6 +1131,12 @@
     $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
     
+    // and finally one that needs to be symlinked...
+    if ( !isset($this->tpl_strings['ADDITIONAL_HEADERS']) )
+    {
+      $this->tpl_strings['ADDITIONAL_HEADERS'] =& $this->additional_headers;
+    }
+    
     $code = $plugins->setHook('template_var_init_end');
     foreach ( $code as $cmd )
     {
--- a/index.php	Sun Jun 22 18:13:59 2008 -0400
+++ b/index.php	Tue Jun 24 23:37:23 2008 -0400
@@ -578,7 +578,7 @@
       }
     }
             
-    header("ETag: \"$etag\"");
+    // header("ETag: \"$etag\"");
     
     // Done, send it to the user
     echo( $html );
--- a/language/english/core.json	Sun Jun 22 18:13:59 2008 -0400
+++ b/language/english/core.json	Tue Jun 24 23:37:23 2008 -0400
@@ -456,6 +456,7 @@
       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%...',
       
       // Server-side responses
       rename_too_short: 'The name you entered is too short. Please enter a longer name for this page.',
--- a/language/english/user.json	Sun Jun 22 18:13:59 2008 -0400
+++ b/language/english/user.json	Tue Jun 24 23:37:23 2008 -0400
@@ -509,8 +509,21 @@
       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: {
       
--- a/plugins/SpecialAdmin.php	Sun Jun 22 18:13:59 2008 -0400
+++ b/plugins/SpecialAdmin.php	Tue Jun 24 23:37:23 2008 -0400
@@ -174,6 +174,13 @@
           </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>';
@@ -316,7 +323,7 @@
     
     if ( is_dir(ENANO_ROOT . '/' . $_POST['avatar_directory']) )
     {
-      if ( preg_match('/^([A-z0-9_-]+)(\/([A-z0-9_-]+))*\/?$/', $_POST['avatar_directory']) )
+      if ( preg_match('/^[A-z0-9_-]+(?:\/(?:[A-z0-9_-]+))*\/?$/', $_POST['avatar_directory']) )
       {
         setConfig('avatar_directory', $_POST['avatar_directory']);
       }
@@ -1944,6 +1951,7 @@
   }
   else
   {
+    $template->add_header('<script type="text/javascript" src="' . scriptPath . '/includes/clientside/static/admin-menu.js"></script>');
     $template->load_theme('admin', 'default');
     $template->init_vars();
     if( !isset( $_GET['noheaders'] ) ) 
@@ -1962,6 +1970,7 @@
       }
       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'),
@@ -2023,7 +2032,16 @@
           }
         });
     }
-    function _enanoAdminOnload() { ajaxPage('<?php echo $paths->nslist['Admin']; ?>Home'); }
+    <?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
@@ -2051,10 +2069,18 @@
       'icon_26' : '<?php echo scriptPath; ?>/images/icons/minusbottom.gif',// junction for opened node
       'icon_27' : '<?php echo scriptPath; ?>/images/icons/minus.gif'       // junction for last opended node
     };
-    addOnloadHook(keepalive_onload);
+    
+    addOnloadHook(function()
+      {
+        load_component('ajax');
+        load_component('l10n');
+        load_component('autofill');
+        keepalive_onload();
+      });
+    
     <?php
     echo $paths->parseAdminTree(); // Make a Javascript array that defines the tree
-    if(!isset($_GET['module'])) { echo 'addOnloadHook(_enanoAdminOnload);'; } ?>
+    ?>
     </script>
     <table border="0" width="100%">
       <tr>
--- a/plugins/admin/PluginManager.php	Sun Jun 22 18:13:59 2008 -0400
+++ b/plugins/admin/PluginManager.php	Tue Jun 24 23:37:23 2008 -0400
@@ -425,7 +425,7 @@
                   <div style=\"float: right;\">
                     <b>$status</b>
                   </div>
-                  <div style=\"cursor: pointer;\" onclick=\"if ( !this.fx ) this.fx = new Spry.Effect.Blind('plugininfo_$uuid', { duration: 500, from: '0%', to: '100%', toggle: true }); this.fx.start();\">
+                  <div style=\"cursor: pointer;\" onclick=\"if ( !this.fx ) { load_component('SpryEffects'); load_component('messagebox'); load_component('ajax'); this.fx = new Spry.Effect.Blind('plugininfo_$uuid', { duration: 500, from: '0%', to: '100%', toggle: true }); } this.fx.start();\">
                     $plugin_basics
                   </div>
                   <span class=\"menuclear\"></span>