Re-enabled, debugged, and optimized Javascript compression code
authorDan
Wed, 13 Feb 2008 21:59:07 -0500
changeset 420 301f546688d1
parent 419 b8b4e38825db
child 421 dbae4d327846
Re-enabled, debugged, and optimized Javascript compression code
includes/clientside/jsres.php
includes/clientside/static/ajax.js
includes/clientside/static/autofill.js
includes/clientside/static/comments.js
includes/clientside/static/dropdown.js
includes/clientside/static/editor.js
includes/clientside/static/enano-lib-basic.js
includes/clientside/static/flyin.js
includes/clientside/static/loader.js
includes/clientside/static/misc.js
includes/clientside/static/paginate.js
includes/clientside/static/toolbar.js
includes/clientside/static/windows.js
includes/js-compressor.php
themes/admin/header.tpl
themes/oxygen/header.tpl
themes/printable/header.tpl
themes/stpatty/header.tpl
--- a/includes/clientside/jsres.php	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/jsres.php	Wed Feb 13 21:59:07 2008 -0500
@@ -13,82 +13,144 @@
  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
  */
 
-if(!isset($_GET['title'])) $_GET['title'] = 'null';
-require('../common.php');
+// Setup Enano
 
-define('ENABLE_COMPRESSION', '');
+//
+// Determine the location of Enano as an absolute path.
+//
 
-ob_start();
-header('Content-type: text/javascript');
+// We need to see if this is a specially marked Enano development server. You can create an Enano
+// development server by cloning the Mercurial repository into a directory named repo, and then
+// using symlinks to reference the original files so as to segregate unique files from non-unique
+// and distribution-standard ones. Enano will pivot its root directory accordingly if the file
+// .enanodev is found in the Enano root (not /repo/).
+if ( strpos(__FILE__, '/repo/') && ( file_exists('../../.enanodev') || file_exists('../../../.enanodev') ) )
+{
+  // We have a development directory. Remove /repo/ from the picture.
+  $filename = str_replace('/repo/', '/', __FILE__);
+}
+else
+{
+  // Standard Enano installation
+  $filename = __FILE__;
+}
 
-$file = ( isset($_GET['file']) ) ? $_GET['file'] : 'enano-lib-basic.js';
+// ENANO_ROOT is sometimes defined by plugins like AjIM that need the constant before the Enano API is initialized
+if ( !defined('ENANO_ROOT') )
+  define('ENANO_ROOT', dirname(dirname(dirname($filename))));
+
+chdir(ENANO_ROOT);
+
+// CONFIG
 
-if(!preg_match('/^([a-z0-9_-]+)\.js$/i', $file))
-  die('// ERROR: Hacking attempt');
+// Files safe to run full (aggressive) compression on
+$full_compress_safe = array('ajax.js', 'editor.js', 'acl.js', 'rijndael.js', 'admin-menu.js', 'autofill.js', 'comments.js', 'misc.js', 'faders.js', 'dropdown.js');
+
+// Files that should NOT be compressed due to already being compressed, licensing, or invalid produced code
+$compress_unsafe = array('SpryEffects.js', 'json.js', 'fat.js');
+
+require('includes/functions.php');
+require('includes/json2.php');
+require('includes/js-compressor.php');
+
+// Output format will always be JS
+header('Content-type: text/javascript');
+$everything = '';
 
-$fname = './static/' . $file;
-if ( !file_exists($fname) )
-  die('// ERROR: File not found: ' . $file);
+// Load and parse enano_lib_basic
+$file = @file_get_contents('includes/clientside/static/enano-lib-basic.js');
+
+$pos_start_includes = strpos($file, '/*!START_INCLUDER*/');
+$pos_end_includes = strpos($file, '/*!END_INCLUDER*/');
 
-$everything = file_get_contents($fname);
+if ( !$pos_start_includes || !$pos_end_includes )
+{
+  die('// Error: enano-lib-basic does not have required metacomments');
+}
 
-$mtime = filemtime($fname);
-header('Last-Modified: '.enano_date('D, d M Y H:i:s T', $mtime));
-header('Content-disposition: attachment; filename=' . $file);
+$pos_end_includes += strlen('/*!END_INCLUDER*/');
+
+preg_match('/var thefiles = (\[([^\]]+?)\]);/', $file, $match);
+
+if ( empty($match) )
+  die('// Error: could not retrieve file list from enano-lib-basic');
 
-if(defined('ENABLE_COMPRESSION'))
+// Decode file list
+try
+{
+  $file_list = enano_json_decode($match[1]);
+}
+catch ( Exception $e )
 {
-  echo "/*
- * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.2 (Caoineag alpha 2)
- * [Aggressively compressed] Javascript client code
- * Copyright (C) 2006-2007 Dan Fuhry
- * Enano is Free Software, licensed under the GNU General Public License; see http://enanocms.org/ for details.
- */
+  die("// Exception caught during file list parsing");
+}
+
+$apex = filemtime('includes/clientside/static/enano-lib-basic.js');
+
+$before_includes = substr($file, 0, $pos_start_includes);
+$after_includes = substr($file, $pos_end_includes);
+
+$everything .= $before_includes;
+$everything .= $after_includes;
 
-";
+foreach ( $file_list as $js_file )
+{
+  $file_contents = file_get_contents("includes/clientside/static/$js_file");
+  $file_md5 = md5($file_contents);
+  $time = filemtime("includes/clientside/static/$js_file");
+  if ( $time > $apex )
+    $apex = $time;
+  // Is this file cached?
+  $cache_path = ENANO_ROOT . "/cache/jsres_$js_file.json";
+  $loaded_cache = false;
   
-  $cache_file = ENANO_ROOT . '/cache/jsres-' . $file . '.php';
-  
-  if ( file_exists($cache_file) )
+  if ( file_exists($cache_path) )
   {
-    $cached = file_get_contents ( $cache_file );
-    $data = unserialize ( $cached );
-    if ( $data['md5'] == md5 ( $everything ) )
+    // Load the cache file and parse it.
+    $cache_file = file_get_contents($cache_path);
+    try
+    {
+      $cache_file = enano_json_decode($cache_file);
+    }
+    catch ( Exception $e )
+    {
+      // Don't do anything - let our fallbacks come into place
+    }
+    if ( is_array($cache_file) && isset($cache_file['md5']) && isset($cache_file['src']) )
     {
-      echo "// The code in this file was fetched from cache\n\n";
-      echo $data['code'];
-      exit;
+      if ( $cache_file['md5'] === $file_md5 )
+      {
+        $loaded_cache = true;
+        $file_contents = $cache_file['src'];
+      }
+    }
+  }
+  if ( !$loaded_cache )
+  {
+    // Try to open the cache file and write to it. If we can't do that, just don't compress the code.
+    $handle = @fopen($cache_path, 'w');
+    if ( $handle )
+    {
+      $aggressive = in_array($js_file, $full_compress_safe);
+      if ( !in_array($js_file, $compress_unsafe) )
+        $file_contents = perform_js_compress($file_contents, $aggressive);
+      
+      $payload = enano_json_encode(array(
+          'md5' => $file_md5,
+          'src' => $file_contents
+        ));
+      fwrite($handle, $payload);
+      fclose($handle);
     }
   }
   
-  if ( getConfig('cache_thumbs') == '1' )
-  {
-    $js_compressor = new JavascriptCompressor();
-    $packed = $js_compressor->getPacked($everything);
-    $data = Array(
-      'md5' => md5 ( $everything ),
-      'code' => $packed
-      );
-    echo "// The code in this file was fetched from the static scripts and compressed (packed code cached)\n\n";
-    echo $packed;
-    
-    $fh = @fopen($cache_file, 'w');
-    if (!$fh)
-      die('// ERROR: Can\'t open cache file for writing');
-    fwrite($fh, serialize ( $data ) );
-    fclose($fh);
-    
-    exit;
-  }
-  
-  echo "// The code in this file was not compressed because packed-script caching is disabled\n\n";
-  echo $everything;
-  
+  $everything .= "\n // $js_file\n";
+  $everything .= "\n" . $file_contents;
 }
-else
-{
-  echo "// The code in this file was not compressed because all script compression is disabled\n\n";
-  echo $everything;
-}
-?>
+
+$date = date('r', $apex);
+header("Date: $date");
+header("Last-Modified: $date");
+
+echo $everything;
+
--- a/includes/clientside/static/ajax.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/ajax.js	Wed Feb 13 21:59:07 2008 -0500
@@ -80,7 +80,7 @@
 
 function handle_invalid_json(response, customerror)
 {
-  var mainwin = $('ajaxEditContainer').object;
+  var mainwin = $dynano('ajaxEditContainer').object;
   mainwin.innerHTML = '';
   
   // Title
@@ -136,7 +136,7 @@
     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 = $('invalidjson_link').object._resp;
+      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);
     }
@@ -616,8 +616,8 @@
   // IE <6 pseudo-compatibility
   if ( KILL_SWITCH )
     return true;
-  var theme = $('chtheme_sel_theme');
-  var style = $('chtheme_sel_style');
+  var theme = $dynano('chtheme_sel_theme');
+  var style = $dynano('chtheme_sel_style');
   if ( !theme.object || !style.object )
   {
     alert($lang.get('ajax_changestyle_pleaseselect_theme'));
@@ -1176,7 +1176,7 @@
       return true;
     var mydiv = document.getElementById('autoCaptcha');
     var width = getWidth();
-    var divw = $(mydiv).Width();
+    var divw = $dynano(mydiv).Width();
     var left = ( width / 2 ) - ( divw / 2 );
     mydiv.style.left = left + 'px';
     fly_in_top(mydiv, false, true);
--- a/includes/clientside/static/autofill.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/autofill.js	Wed Feb 13 21:59:07 2008 -0500
@@ -58,7 +58,7 @@
         if ( autofill.event.keyCode == autofill.KEY_ENTER && autofill.boxes.length < 1 && !autofill.box_id )
         {
           // user hit enter after accepting a suggestion - submit the form
-          var frm = findParentForm($(autofill.field_id).object);
+          var frm = findParentForm($dynano(autofill.field_id).object);
           frm._af_acting = false;
           frm.submit();
           // window.console.info('Submitting form');
@@ -86,7 +86,7 @@
       try
       {
         var user = resp_json.users_real[i].toLowerCase();
-        var inp  = $(autofill.field_id).object.value;
+        var inp  = $dynano(autofill.field_id).object.value;
         inp = inp.toLowerCase();
         if ( user.indexOf(inp) > -1 )
         {
@@ -150,10 +150,10 @@
       }
       
     // Finalize div
-    var tb_top    = $(autofill.field_id).Top();
-    var tb_height = $(autofill.field_id).Height();
+    var tb_top    = $dynano(autofill.field_id).Top();
+    var tb_height = $dynano(autofill.field_id).Height();
     var af_top    = tb_top + tb_height - 9;
-    var tb_left   = $(autofill.field_id).Left();
+    var tb_left   = $dynano(autofill.field_id).Left();
     var af_left   = tb_left;
     
     div.style.position = 'absolute';
@@ -193,15 +193,15 @@
           new messagebox(MB_OK|MB_ICONSTOP, 'Invalid response', 'Invalid or unexpected JSON response from server:<pre>' + ajax.responseText + '</pre>');
           return false;
         }
-        if ( $(afobj.field_id).object.value.length < 3 )
+        if ( $dynano(afobj.field_id).object.value.length < 3 )
           return false;
         var resp_json = parseJSON(response);
-        var resp_code = $(afobj.field_id).object.value.toLowerCase().substr(0, 3);
+        var resp_code = $dynano(afobj.field_id).object.value.toLowerCase().substr(0, 3);
         afobj.responses[resp_code] = resp_json;
         afobj.process_dataset(resp_json);
       }
     }
-    var usernamefragment = ajaxEscape($(this.field_id).object.value);
+    var usernamefragment = ajaxEscape($dynano(this.field_id).object.value);
     ajaxGet(stdAjaxPrefix + '&_mode=fillusername&name=' + usernamefragment + '&allowanon=' + ( this.allowanon ? '1' : '0' ), processResponse);
   }
   
@@ -216,7 +216,7 @@
     if ( af_current )
       return false;
     
-    var resp_code = $(this.field_id).object.value.toLowerCase().substr(0, 3);
+    var resp_code = $dynano(this.field_id).object.value.toLowerCase().substr(0, 3);
     if ( this.responses.length < 1 || ! this.responses[ resp_code ] )
     {
       // window.console.info('Cannot find dataset ' + resp_code + ' in cache, sending AJAX request');
@@ -261,7 +261,7 @@
     if ( key == this.KEY_ENTER && !this.repeat )
     {
       submitAuthorized = true;
-      var form = findParentForm($(this.field_id).object);
+      var form = findParentForm($dynano(this.field_id).object);
       form._af_acting = false;
       return true;
     }
@@ -284,7 +284,7 @@
         break;
     }
     
-    var form = findParentForm($(this.field_id).object);
+    var form = findParentForm($dynano(this.field_id).object);
       form._af_acting = false;
   }
   
@@ -327,19 +327,19 @@
       state_td.parentNode.nextSibling.firstChild.className = 'row2';
       
       // Exception - automatically scroll around if the item is off-screen
-      var height = $(this.box_id).Height();
-      var top = $(this.box_id).object.scrollTop;
+      var height = $dynano(this.box_id).Height();
+      var top = $dynano(this.box_id).object.scrollTop;
       var scroll_bottom = height + top;
       
-      var td_top = $(state_td.parentNode.nextSibling.firstChild).Top() - $(this.box_id).Top();
-      var td_height = $(state_td.parentNode.nextSibling.firstChild).Height();
+      var td_top = $dynano(state_td.parentNode.nextSibling.firstChild).Top() - $dynano(this.box_id).Top();
+      var td_height = $dynano(state_td.parentNode.nextSibling.firstChild).Height();
       var td_bottom = td_top + td_height;
       
       if ( td_bottom > scroll_bottom )
       {
         var scrollY = td_top - height + 2*td_height - 7;
         // window.console.debug(scrollY);
-        $(this.box_id).object.scrollTop = scrollY;
+        $dynano(this.box_id).object.scrollTop = scrollY;
         /*
         var newtd = state_td.parentNode.nextSibling.firstChild;
         var a = document.createElement('a');
@@ -351,7 +351,7 @@
         */
         
         // In firefox, scrolling like that makes the field get unfocused
-        $(this.field_id).object.focus();
+        $dynano(this.field_id).object.focus();
       }
     }
     else
@@ -382,13 +382,13 @@
       state_td.parentNode.previousSibling.firstChild.className = 'row2';
       
       // Exception - automatically scroll around if the item is off-screen
-      var top = $(this.box_id).object.scrollTop;
+      var top = $dynano(this.box_id).object.scrollTop;
       
-      var td_top = $(state_td.parentNode.previousSibling.firstChild).Top() - $(this.box_id).Top();
+      var td_top = $dynano(state_td.parentNode.previousSibling.firstChild).Top() - $dynano(this.box_id).Top();
       
       if ( td_top < top )
       {
-        $(this.box_id).object.scrollTop = td_top - 10;
+        $dynano(this.box_id).object.scrollTop = td_top - 10;
         /*
         var newtd = state_td.parentNode.previousSibling.firstChild;
         var a = document.createElement('a');
@@ -400,12 +400,12 @@
         */
         
         // In firefox, scrolling like that makes the field get unfocused
-        $(this.field_id).object.focus();
+        $dynano(this.field_id).object.focus();
       }
     }
     else
     {
-      $(this.box_id).object.scrollTop = 0;
+      $dynano(this.box_id).object.scrollTop = 0;
       return false;
     }
   }
@@ -439,7 +439,7 @@
     else if ( this.state )
       ta.value = this.state;
     this.destroy();
-    findParentForm($(this.field_id.object))._af_acting = false;
+    findParentForm($dynano(this.field_id.object))._af_acting = false;
   }
   
   this.sleep = function()
@@ -449,7 +449,7 @@
       var div = document.getElementById(this.box_id);
       div.style.display = 'none';
     }
-    var el = $(this.field_id).object;
+    var el = $dynano(this.field_id).object;
     var fr = findParentForm(el);
     el._af_acting = false;
   }
--- a/includes/clientside/static/comments.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/comments.js	Wed Feb 13 21:59:07 2008 -0500
@@ -74,7 +74,7 @@
     
     var ns = ENANO_PAGE_TYPE;
   
-    // Counters
+  // Counters
     if ( data.auth_mod_comments )
     {
       var cnt = ( data.auth_mod_comments ) ? data.count_total : data.count_appr;
@@ -122,7 +122,6 @@
   
   if ( data.auth_post_comments )
   {
-    
     // Posting form
   
     html += '<h3>' + $lang.get('comment_postform_title') + '</h3>';
@@ -149,7 +148,6 @@
     html += '    <tr><td colspan="2" style="text-align: center;"><input type="button" onclick="submitComment();" value="' + $lang.get('comment_postform_btn_submit') + '" /></td></tr>';
     html += '  </table>';
     html += '</div>';
-    
   }
     
   document.getElementById('ajaxEditContainer').innerHTML = html;
@@ -245,7 +243,12 @@
   parser.assign_vars(tplvars);
   parser.assign_bool(tplbool);
   
-  return '<div id="comment_holder_' + i + '"><input type="hidden" value="'+this_comment.comment_id+'" /><input type="hidden" id="comment_source_'+i+'" />' + parser.run() + '</div>';
+  var ret = '<div id="comment_holder_' + i + '">';
+  ret += '<input type="hidden" value="'+this_comment.comment_id+'" />';
+  ret += '<input type="hidden" id="comment_source_'+i+'" />';
+  ret += parser.run();
+  ret += '</div>';
+  return ret;
 }
 
 function displayCommentForm()
@@ -388,7 +391,7 @@
   }
   if ( data.ip_addr )
   {
-    var span = $('comment_ip_' + data.local_id).object;
+    var span = $dynano('comment_ip_' + data.local_id).object;
     if ( !span )
       return false;
     span.innerHTML = $lang.get('comment_msg_ip_address') + ' <a href="#rdns" onclick="ajaxReverseDNS(this); return false;">' + data.ip_addr + '</a>';
@@ -605,7 +608,7 @@
 function viewCommentIP(id, local_id)
 {
   // set "loading" indicator on IP button
-  var span = $('comment_ip_' + local_id).object;
+  var span = $dynano('comment_ip_' + local_id).object;
   if ( !span )
     return false;
   span.innerHTML = '<img alt="..." src="' + ajax_load_icon + '" />';
--- a/includes/clientside/static/dropdown.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/dropdown.js	Wed Feb 13 21:59:07 2008 -0500
@@ -57,7 +57,7 @@
 // Initializes a div with a jBox menu in it.
 function jBoxSetup(obj)
 {
-  $(obj).addClass('menu');
+  $dynano(obj).addClass('menu');
   removeTextNodes(obj);
   
   for ( var i = 0; i < obj.childNodes.length; i++ )
@@ -123,7 +123,7 @@
     if(typeof(others[i]) == 'object')
     {
       others[i].style.display = 'none';
-      $(others[i].previousSibling).rmClass('liteselected');
+      $dynano(others[i].previousSibling).rmClass('liteselected');
     }
   }
   var others = obj.parentNode.getElementsByTagName('div');
@@ -134,13 +134,13 @@
       if ( others[i].className == 'submenu' )
       {
         others[i].style.display = 'none';
-        $(others[i].previousSibling).rmClass('liteselected');
+        $dynano(others[i].previousSibling).rmClass('liteselected');
       }
     }
   }
   if(obj.nextSibling.tagName.toLowerCase() == 'ul' || ( obj.nextSibling.tagName.toLowerCase() == 'div' && obj.nextSibling.className == 'submenu' ))
   {
-    $(a).addClass('liteselected');
+    $dynano(a).addClass('liteselected');
     //obj.className = 'liteselected';
     var ul = obj.nextSibling;
     var dim = fetch_dimensions(obj);
@@ -198,7 +198,7 @@
   
   if (!isOverObj(a, false, event) && !isOverObj(ul, true, event))
   {
-    $(a).rmClass('liteselected');
+    $dynano(a).rmClass('liteselected');
     
     if ( jBox_slide_enable )
     {
@@ -390,7 +390,7 @@
         {
           if ( !isOverObj(uls[j], false, e) )
           {
-            $(uls[j].previousSibling).rmClass('liteselected');
+            $dynano(uls[j].previousSibling).rmClass('liteselected');
             //uls[j].style.display = 'none';
             slideIn(uls[j]);
           }
@@ -403,7 +403,7 @@
         {
           if ( !isOverObj(uls[j], false, e) )
           {
-            $(uls[j].previousSibling).rmClass('liteselected');
+            $dynano(uls[j].previousSibling).rmClass('liteselected');
             //uls[j].style.display = 'none';
             slideIn(uls[j]);
           }
@@ -530,3 +530,21 @@
     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;
+}
+
--- a/includes/clientside/static/editor.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/editor.js	Wed Feb 13 21:59:07 2008 -0500
@@ -457,8 +457,8 @@
   {
     if ( this.needReset )
     {
-      var img = $('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
-      var lbl = $('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
+      var img = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
+      var lbl = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
       img.src = editor_img_path + '/savedraft.gif';
       lbl.innerHTML = $lang.get('editor_btn_savedraft');
     }
@@ -510,7 +510,7 @@
 {
   if ( !is_draft )
     ajaxSetEditorLoading();
-  var ta_content = $('ajaxEditArea').getContent();
+  var ta_content = $dynano('ajaxEditArea').getContent();
   
   if ( !is_draft && ( ta_content == '' || ta_content == '<p></p>' || ta_content == '<p>&nbsp;</p>' ) )
   {
@@ -522,8 +522,8 @@
   if ( is_draft )
   {
     // ajaxSetEditorLoading();
-    var img = $('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
-    var lbl = $('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
+    var img = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
+    var lbl = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
     img.src = scriptPath + '/images/loading.gif';
     var d = new Date();
     var m = String(d.getMinutes());
@@ -533,12 +533,12 @@
     lbl.innerHTML = $lang.get('editor_msg_draft_saving');
   }
   
-  var edit_summ = $('enano_editor_field_summary').object.value;
+  var edit_summ = $dynano('enano_editor_field_summary').object.value;
   if ( !edit_summ )
     edit_summ = '';
-  var is_minor = ( $('enano_editor_field_minor').object.checked ) ? 1 : 0;
-  var timestamp = $('ajaxEditArea').object._edTimestamp;
-  var used_draft = $('ajaxEditArea').object.used_draft;
+  var is_minor = ( $dynano('enano_editor_field_minor').object.checked ) ? 1 : 0;
+  var timestamp = $dynano('ajaxEditArea').object._edTimestamp;
+  var used_draft = $dynano('ajaxEditArea').object.used_draft;
   
   var json_packet = {
     src: ta_content,
@@ -608,7 +608,7 @@
         if ( response.mode == 'obsolete' )
         {
           // Update the local timestamp to allow override
-          $('ajaxEditArea').object._edTimestamp = response.time;
+          $dynano('ajaxEditArea').object._edTimestamp = response.time;
           new messagebox(MB_OK | MB_ICONEXCLAMATION, $lang.get('editor_err_obsolete_title'), $lang.get('editor_err_obsolete_body', { author: response.author, timestamp: response.date_string, page_url: makeUrl(title, false, true) }));
           return false;
         }
@@ -618,8 +618,8 @@
           {
             document.getElementById('ajaxEditArea').used_draft = true;
             document.getElementById('ajaxEditArea').needReset = true;
-            var img = $('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
-            var lbl = $('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
+            var img = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
+            var lbl = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
             img.src = scriptPath + '/images/mini-info.png';
             var d = new Date();
             var m = String(d.getMinutes());
@@ -657,9 +657,9 @@
 function ajaxEditorGenPreview()
 {
   ajaxSetEditorLoading();
-  var ta_content = $('ajaxEditArea').getContent();
+  var ta_content = $dynano('ajaxEditArea').getContent();
   ta_content = ajaxEscape(ta_content);
-  if ( $('enano_editor_preview').object.innerHTML != '' )
+  if ( $dynano('enano_editor_preview').object.innerHTML != '' )
   {
     opacity('enano_editor_preview', 100, 0, 500);
   }
@@ -669,7 +669,7 @@
       {
         ajaxUnSetEditorLoading();
         changeOpac(0, 'enano_editor_preview');
-        $('enano_editor_preview').object.innerHTML = ajax.responseText;
+        $dynano('enano_editor_preview').object.innerHTML = ajax.responseText;
         window.location.hash = '#ajax_preview';
         opacity('enano_editor_preview', 0, 100, 500);
       }
@@ -716,7 +716,7 @@
           return false;
         }
         
-        $('ajaxEditArea').setContent(response.src);
+        $dynano('ajaxEditArea').setContent(response.src);
       }
     }, true);
 }
@@ -724,9 +724,9 @@
 function ajaxEditorShowDiffs()
 {
   ajaxSetEditorLoading();
-  var ta_content = $('ajaxEditArea').getContent();
+  var ta_content = $dynano('ajaxEditArea').getContent();
   ta_content = ajaxEscape(ta_content);
-  if ( $('enano_editor_preview').object.innerHTML != '' )
+  if ( $dynano('enano_editor_preview').object.innerHTML != '' )
   {
     opacity('enano_editor_preview', 100, 0, 500);
   }
@@ -736,7 +736,7 @@
       {
         ajaxUnSetEditorLoading();
         changeOpac(0, 'enano_editor_preview');
-        $('enano_editor_preview').object.innerHTML = ajax.responseText;
+        $dynano('enano_editor_preview').object.innerHTML = ajax.responseText;
         window.location.hash = '#ajax_preview';
         opacity('enano_editor_preview', 0, 100, 500);
       }
@@ -828,10 +828,10 @@
     ed = document.getElementById('ajaxEditArea');
     var blackout = document.createElement('div');
     blackout.style.position = 'absolute';
-    blackout.style.top = $('ajaxEditArea').Top() + 'px';
-    blackout.style.left = $('ajaxEditArea').Left() + 'px';
-    blackout.style.width = $('ajaxEditArea').Width() + 'px';
-    blackout.style.height = $('ajaxEditArea').Height() + 'px';
+    blackout.style.top = $dynano('ajaxEditArea').Top() + 'px';
+    blackout.style.left = $dynano('ajaxEditArea').Left() + 'px';
+    blackout.style.width = $dynano('ajaxEditArea').Width() + 'px';
+    blackout.style.height = $dynano('ajaxEditArea').Height() + 'px';
     blackout.style.backgroundColor = '#FFFFFF';
     domObjChangeOpac(60, blackout);
     blackout.style.backgroundImage = 'url(' + scriptPath + '/includes/clientside/tinymce/themes/advanced/skins/default/img/progress.gif)';
@@ -882,7 +882,7 @@
   var now = unix_time();
   aed.as_last_save = now;
   
-  var ta_content = $('ajaxEditArea').getContent();
+  var ta_content = $dynano('ajaxEditArea').getContent();
   
   if ( ta_content == '' || ta_content == '<p></p>' || ta_content == '<p>&nbsp;</p>' )
   {
@@ -919,8 +919,8 @@
           return false;
         }
         
-        $('ajaxEditArea').setContent(response.src);
-        $('ajaxEditArea').object.used_draft = true;
+        $dynano('ajaxEditArea').setContent(response.src);
+        $dynano('ajaxEditArea').object.used_draft = true;
         
         var es = document.getElementById('enano_editor_field_summary');
         if ( es.value == '' )
@@ -928,7 +928,7 @@
           es.value = response.edit_summary;
         }
         
-        var dn = $('ajax_edit_draft_notice').object;
+        var dn = $dynano('ajax_edit_draft_notice').object;
         dn.parentNode.removeChild(dn);
       }
     }, true);
--- a/includes/clientside/static/enano-lib-basic.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/enano-lib-basic.js	Wed Feb 13 21:59:07 2008 -0500
@@ -10,8 +10,9 @@
  * 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/.
- * 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 under the GPL. See the page Special:About_Enano on this website for more information.
+ * 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')
@@ -264,8 +265,13 @@
   head.appendChild(script);
 }
 
+// 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',
   'admin-menu.js',
   'ajax.js',
@@ -280,14 +286,12 @@
   'md5.js',
   'sliders.js',
   'toolbar.js',
-  'windows.js',
   'rijndael.js',
   'l10n.js',
   'template-compiler.js',
   'acl.js',
   'comments.js',
   'editor.js',
-  'dynano.js',
   'flyin.js',
   'paginate.js',
   'pwstrength.js',
@@ -315,6 +319,9 @@
   head.appendChild(script);
 }
 
+// Do not remove the following comment, it is used by jsres.php.
+/*!END_INCLUDER*/
+
 addOnloadHook(function() {
   if ( $_REQUEST['do'] )
   {
--- a/includes/clientside/static/flyin.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/flyin.js	Wed Feb 13 21:59:07 2008 -0500
@@ -83,8 +83,8 @@
   // setup element
   element.style.position = 'absolute';
   
-  dim = [ $(element).Height(), $(element).Width() ];
-  off = [ $(element).Top(), $(element).Left() ];
+  dim = [ $dynano(element).Height(), $dynano(element).Width() ];
+  off = [ $dynano(element).Top(), $dynano(element).Left() ];
   
   if ( height_taken_care_of )
   {
--- a/includes/clientside/static/loader.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/loader.js	Wed Feb 13 21:59:07 2008 -0500
@@ -2,7 +2,6 @@
 
 function mdgInnerLoader(e)
 {
-  jws.startup();
   if(window.location.hash == '#comments') ajaxComments();
   window.onkeydown=isKeyPressed;
   window.onkeyup=function(e) { isKeyPressed(e); };
@@ -18,10 +17,14 @@
   initSliders();
   runOnloadHooks(e);
 }
-if(window.onload) var ld = window.onload;
-else var ld = function() {return;};
+var ld;
+if(window.onload) ld = window.onload;
+else ld = function() {return;};
 function enano_init(e) {
-  ld(e);
+  if ( typeof(ld) == 'function' )
+  {
+    ld(e);
+  }
   mdgInnerLoader(e);
 }
 
--- a/includes/clientside/static/misc.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/misc.js	Wed Feb 13 21:59:07 2008 -0500
@@ -477,17 +477,17 @@
             <input type="hidden" id="ajaxlogin_crypt_challenge" value="' + response.challenge + '" /> \
           </form>';
         ajax_auth_mb_cache.updateContent(form_html);
-        $('messageBox').object.nextSibling.firstChild.tabindex = '3';
+        $dynano('messageBox').object.nextSibling.firstChild.tabindex = '3';
         if ( typeof(response.username) == 'string' )
         {
-          $('ajaxlogin_user').object.value = response.username;
+          $dynano('ajaxlogin_user').object.value = response.username;
           if ( IE )
           {
             setTimeout("document.forms['ajax_login_form'].password.focus();", 200);
           }
           else
           {
-            $('ajaxlogin_pass').object.focus();
+            $dynano('ajaxlogin_pass').object.focus();
           }
         }
         else
@@ -498,12 +498,12 @@
           }
           else
           {
-            $('ajaxlogin_user').object.focus();
+            $dynano('ajaxlogin_user').object.focus();
           }
         }
         var enter_obj = ( ajax_auth_show_captcha ) ? 'ajaxlogin_captcha_code' : 'ajaxlogin_pass';
-        $(enter_obj).object.onblur = function(e) { if ( !shift ) $('messageBox').object.nextSibling.firstChild.focus(); };
-        $(enter_obj).object.onkeypress = function(e)
+        $dynano(enter_obj).object.onblur = function(e) { if ( !shift ) $dynano('messageBox').object.nextSibling.firstChild.focus(); };
+        $dynano(enter_obj).object.onkeypress = function(e)
         {
           // Trigger a form submit when the password field is focused and the user presses enter
           
@@ -793,3 +793,82 @@
   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;
+}
--- a/includes/clientside/static/paginate.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/paginate.js	Wed Feb 13 21:59:07 2008 -0500
@@ -269,10 +269,10 @@
 
 function paginator_goto(parentobj, this_page, num_pages, perpage, url_string)
 {
-  var height = $(parentobj).Height();
-  var width  = $(parentobj).Width();
-  var left   = $(parentobj).Left();
-  var top    = $(parentobj).Top();
+  var height = $dynano(parentobj).Height();
+  var width  = $dynano(parentobj).Width();
+  var left   = $dynano(parentobj).Left();
+  var top    = $dynano(parentobj).Top();
   var left_pos = left + width ;
   var top_pos = height + top;
   var div = document.createElement('div');
@@ -307,7 +307,7 @@
   
   fly_in_bottom(div, false, true);
   
-  var divh = $(div).Width();
+  var divh = $dynano(div).Width();
   left_pos = left_pos - divh;
   div.style.left = left_pos + 'px';
 }
--- a/includes/clientside/static/toolbar.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/toolbar.js	Wed Feb 13 21:59:07 2008 -0500
@@ -9,7 +9,7 @@
   {
     if(obj.id == 'mdgToolbar_article' || obj.id == 'mdgToolbar_discussion')
     {
-      $(obj).rmClass('selected');
+      $dynano(obj).rmClass('selected');
     }
     obj = obj.nextSibling;
   }
@@ -22,7 +22,7 @@
   obj = document.getElementById('pagebar_main').firstChild.nextSibling;
   while(obj)
   {
-    if ( !$(obj).hasClass('selected') )
+    if ( !$dynano(obj).hasClass('selected') )
     {
       obj = obj.nextSibling;
       continue;
@@ -30,7 +30,7 @@
     if(obj.id != 'mdgToolbar_article' && obj.id != 'mdgToolbar_discussion')
     {
       if ( obj.className )
-        $(obj).rmClass('selected');
+        $dynano(obj).rmClass('selected');
     }
     obj = obj.nextSibling;
   }
@@ -46,7 +46,7 @@
   if(typeof(dom) == 'object')
   {
     unselectAllButtonsMajor();
-    $('mdgToolbar_'+which).addClass('selected');
+    $dynano('mdgToolbar_'+which).addClass('selected');
   }
 }
 
@@ -57,7 +57,7 @@
   if(typeof(document.getElementById('mdgToolbar_'+which)) == 'object')
   {
     unselectAllButtonsMinor();
-    $('mdgToolbar_'+which).addClass('selected');
+    $dynano('mdgToolbar_'+which).addClass('selected');
   }
 }
 
--- a/includes/clientside/static/windows.js	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/clientside/static/windows.js	Wed Feb 13 21:59:07 2008 -0500
@@ -6,23 +6,6 @@
  */
 
   var position;
-  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;
-  }
   position = getScrollOffset();
   
   var jws = {
@@ -150,86 +133,6 @@
 
 //window.onscroll=jws['scrollHandler'];
 
-/*
- * Utility functions
- */
- 
-// 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;
-}
-
 /**************************************************
  * dom-drag.js
  * 09.25.2001
--- a/includes/js-compressor.php	Tue Feb 12 22:17:58 2008 -0500
+++ b/includes/js-compressor.php	Wed Feb 13 21:59:07 2008 -0500
@@ -107,7 +107,7 @@
 	 * public constructor
          * 	creates a new BaseConvert class variable (base 36)
 	 */
-	function JavaScriptCompressor() {
+	function __construct() {
 		$this->__SourceMap = new SourceMap();
 		$this->__BC = new BaseConvert('0123456789abcdefghijklmnopqrstuvwxyz');
 		$this->__delimeter = array(
@@ -493,4 +493,35 @@
         return (($next - 1) % 2 === 0);
     }
 }
+
+/**
+ * Wrapper for the JavaScriptCompressor class.
+ * @param string Javascript code to compact. If this doesn't contain any newline characters, will be treated as a filename.
+ * @param bool If true, aggressively compresses the code. Otherwise, just strips comments and some whitespace.
+ * @return string Compressed JS
+ */
+
+function perform_js_compress($text_or_file, $aggressive = false)
+{
+  static $compressor = false;
+  
+  if ( !is_object($compressor) )
+    $compressor = new JavaScriptCompressor();
+  
+  if ( strpos($text_or_file, "\n") )
+  {
+    $text =& $text_or_file;
+  }
+  else if ( file_exists($text_or_file) )
+  {
+    $text = file_get_contents($text_or_file);
+  }
+  else
+  {
+    $text =& $text_or_file;
+  }
+  
+  return ( $aggressive ) ? $compressor->getPacked($text) : $compressor->getClean($text);
+}
+
 ?>
\ No newline at end of file
--- a/themes/admin/header.tpl	Tue Feb 12 22:17:58 2008 -0500
+++ b/themes/admin/header.tpl	Wed Feb 13 21:59:07 2008 -0500
@@ -9,7 +9,8 @@
     <link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css-ie/iefixes.css" />
     <![endif]-->
     {JS_DYNAMIC_VARS}
-    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
+    <!-- jsres.php is a wrapper script that compresses and caches single JS files to minimize requests -->
+    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/jsres.php"></script>
     <script type="text/javascript" src="{SCRIPTPATH}/themes/admin/js/menu.js"></script>
     {ADDITIONAL_HEADERS}
     </head>
--- a/themes/oxygen/header.tpl	Tue Feb 12 22:17:58 2008 -0500
+++ b/themes/oxygen/header.tpl	Wed Feb 13 21:59:07 2008 -0500
@@ -11,8 +11,8 @@
       var tinymce_skin = 'o2k7';
     </script>
     
-    <!-- This script automatically loads the other 15 JS files -->
-    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
+    <!-- jsres.php is a wrapper script that compresses and caches single JS files to minimize requests -->
+    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/jsres.php"></script>
     {ADDITIONAL_HEADERS}
     
     <script type="text/javascript">
--- a/themes/printable/header.tpl	Tue Feb 12 22:17:58 2008 -0500
+++ b/themes/printable/header.tpl	Wed Feb 13 21:59:07 2008 -0500
@@ -7,8 +7,8 @@
     <link id="mdgCss" rel="stylesheet" href="{SCRIPTPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" type="text/css" />
     <link rel="stylesheet" media="print" href="{SCRIPTPATH}/themes/{THEME_ID}/css-simple/printbits.css" type="text/css" />
     {JS_DYNAMIC_VARS}
-    <!-- This script automatically loads the other 15 JS files -->
-    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
+    <!-- jsres.php is a wrapper script that compresses and caches single JS files to minimize requests -->
+    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/jsres.php"></script>
     {ADDITIONAL_HEADERS}
     
   </head>
--- a/themes/stpatty/header.tpl	Tue Feb 12 22:17:58 2008 -0500
+++ b/themes/stpatty/header.tpl	Wed Feb 13 21:59:07 2008 -0500
@@ -6,8 +6,8 @@
     {JS_DYNAMIC_VARS}
     <link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/includes/clientside/css/enano-shared.css" />
     <link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
-    <!-- This script automatically loads the other 15 JS files -->
-    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
+    <!-- jsres.php is a wrapper script that compresses and caches single JS files to minimize requests -->
+    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/jsres.php"></script>
     <!--[if lt IE 7]>
     <link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css-extra/ie-fixes-{STYLE_ID}.css" />
     <![endif]-->