Added CDN support: a URL to a CDN can now be specified and Enano will load all images, CSS, and javascript (except TinyMCE) from that server
authorDan
Sun, 20 Jul 2008 13:32:04 -0400
changeset 650 e45183014778
parent 649 74e03196fd43
child 651 ce9d78d7251d
Added CDN support: a URL to a CDN can now be specified and Enano will load all images, CSS, and javascript (except TinyMCE) from that server
includes/clientside/jscompress.php
includes/clientside/jsres.php
includes/clientside/static/ajax.js
includes/clientside/static/editor.js
includes/clientside/static/enano-lib-basic.js
includes/clientside/static/functions.js
includes/common.php
includes/common_cli.php
includes/paths.php
includes/template.php
language/english/admin.json
plugins/SpecialAdmin.php
themes/admin/footer.tpl
themes/admin/header.tpl
themes/admin/simple-header.tpl
themes/oxygen/elements.tpl
themes/oxygen/footer.tpl
themes/oxygen/header.tpl
themes/oxygen/simple-header.tpl
themes/printable/css-simple/printbits.css
themes/printable/css/default.css
themes/printable/footer.tpl
themes/printable/header.tpl
themes/stpatty/footer.tpl
themes/stpatty/header.tpl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/clientside/jscompress.php	Sun Jul 20 13:32:04 2008 -0400
@@ -0,0 +1,188 @@
+<?php
+
+/*
+ * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
+ * Version 1.1.4 (Caoineag alpha 4)
+ * Copyright (C) 2006-2008 Dan Fuhry
+ * jsres.php - the Enano client-side runtime, a.k.a. AJAX on steroids
+ *
+ * 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.
+ */
+
+/**
+ * Returns a floating-point number with the current UNIX timestamp in microseconds. Defined very early because we gotta call it
+ * from very early on in the script to measure the starting time of Enano.
+ * @return float
+ */
+
+// First check to see if something already declared this function.... it happens often.
+if ( !function_exists('microtime_float') )
+{
+  function microtime_float()
+  {
+    list($usec, $sec) = explode(" ", microtime());
+    return ((float)$usec + (float)$sec);
+  }
+}
+
+$local_start = microtime_float();
+
+// Setup Enano
+
+//
+// Determine the location of Enano as an absolute path.
+//
+
+// 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__;
+}
+
+// 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);
+
+require('includes/common.php');
+if ( !defined('ENANO_CLI') )
+{
+  die_friendly('Not for web use', '<p>This script is designed to be run from a command-line environment.</p>');
+}
+
+if ( !getConfig('cdn_path') )
+{
+  die_friendly('CDN support not enabled', 'This script is for compressing the Enano Javascript runtimes for CDN use.');
+}
+
+echo "\x1B[1mCreating zip file with compressed Javascript runtimes.\x1B[0m\n";
+echo "\x1B[0;32mWhen finished, upload the contents of enano-lib.zip to:\n\x1B[1;34m  " . getConfig('cdn_path') . "/includes/clientside/static/\x1B[0m\n";
+
+echo "\x1B[0;33mChecking for zip support...";
+
+// do we have zip file support?
+$have_zip = false;
+$path = ( isset($_SERVER['PATH']) ) ? $_SERVER['PATH'] : false;
+if ( !$path )
+{
+  die_semicritical('Can\'t get your PATH', 'Unable to get the PATH environment variable');
+}
+
+$path = ( strtolower(PHP_OS) === 'win32' ) ? explode(';', $path) : explode(':', $path);
+$pathext = ( strtolower(PHP_OS) === 'win32' ) ? '.exe' : '';
+
+foreach ( $path as $dir )
+{
+  if ( file_exists("$dir/zip$pathext") )
+  {
+    $have_zip = true;
+    break;
+  }
+}
+
+if ( !$have_zip )
+{
+  // no zupport zor zipping ziles
+  echo "\x1B[31;1mnot found\x1B[0m\n\x1B[1mPlease install the zip utility using your distribution's package manager\nand then rerun this script.\x1B[0m";
+  exit(1);
+}
+
+echo "\x1B[1mall good\x1B[0m\n";
+echo "\x1B[0;33mMinifying Javascript files...";
+
+if ( !@mkdir('includes/clientside/staticmin') )
+{
+  echo "\x1B[31;1mcouldn't create temp directory\x1B[0m\n\x1B[1mCheck permissions please, we couldn't create includes/clientside/staticmin.\x1B[0m";
+  exit(1);
+}
+
+require('includes/clientside/jsres.php');
+
+// $everything now contains the core runtimes
+// hack to lie about compression, this keeps load_component() from doing jsres.php?f=...
+$everything = str_replace('ENANO_JSRES_COMPRESSED = true', 'ENANO_JSRES_COMPRESSED = false', $everything);
+
+chdir('includes/clientside/staticmin');
+$handle = @fopen('./enano-lib-basic.js', 'w');
+if ( !$handle )
+{
+  echo "\x1B[31;1mcouldn't open file\x1B[0m\n\x1B[1mCheck permissions please, we couldn't create a file inside includes/clientside/staticmin.\x1B[0m";
+  exit(1);
+}
+
+fwrite($handle, $everything);
+fclose($handle);
+
+// for each JS file in includes/clientside/static, compress & write
+if ( $dr = @opendir('../static') )
+{
+  while ( $dh = @readdir($dr) )
+  {
+    if ( !preg_match('/\.js$/', $dh) || $dh === 'enano-lib-basic.js' )
+      continue;
+    
+    $contents = @file_get_contents("../static/$dh");
+    $compressed = jsres_cache_check($dh, $contents);
+    $compressed = str_replace('/* JavaScriptCompressor 0.8 [www.devpro.it], thanks to Dean Edwards for idea [dean.edwards.name] */' . "\r\n", '', $compressed);
+    
+    $handle = @fopen("./$dh", 'w');
+    if ( !$handle )
+    {
+      echo "\x1B[31;1mcouldn't open file\x1B[0m\n\x1B[1mCheck permissions please, we couldn't create a file inside includes/clientside/staticmin.\x1B[0m";
+      exit(1);
+    }
+    fwrite($handle, $compressed);
+    fclose($handle);
+  }
+}
+else
+{
+  echo "\x1B[31;1mcouldn't open includes directory\x1B[0m\n\x1B[1mUnable to get our hands into includes/clientside/static/ to compress everything.\x1B[0m";
+  exit(1);
+}
+
+echo "\x1B[1mdone\x1B[0m\n";
+echo "\x1B[0;33mCompressing into enano-lib.zip...";
+
+$result = system('zip -yrq9 ../enano-lib.zip *.js');
+if ( $result != 0 )
+{
+  // failure
+  echo "\x1B[31;1mzip creation failed\x1B[0m\n\x1B[1mzip returned result $result\x1B[0m";
+  exit(1);
+}
+
+echo "\x1B[1mdone\x1B[0m\n";
+
+// done, clean up
+
+echo "\x1B[0;33mCleaning up...";
+chdir('..');
+
+if ( $dr = @opendir('./staticmin') )
+{
+  while ( $dh = @readdir($dr) )
+  {
+    if ( preg_match('/\.js$/', $dh) )
+      unlink("./staticmin/$dh");
+  }
+}
+
+@rmdir('./staticmin');
+
+echo "\x1B[1mdone\x1B[0m\n";
--- a/includes/clientside/jsres.php	Sat Jul 19 21:14:54 2008 -0400
+++ b/includes/clientside/jsres.php	Sun Jul 20 13:32:04 2008 -0400
@@ -15,6 +15,12 @@
 
 // define('ENANO_JS_DEBUG', 1);
 
+// if Enano's already loaded, we've been included from a helper script
+if ( defined('ENANO_CONFIG_FETCHED') )
+  define('ENANO_JSRES_SETUP_ONLY', 1);
+
+if ( !defined('ENANO_JSRES_SETUP_ONLY') ):
+
 /**
  * Returns a floating-point number with the current UNIX timestamp in microseconds. Defined very early because we gotta call it
  * from very early on in the script to measure the starting time of Enano.
@@ -73,6 +79,8 @@
 define('ENANO_EXIT_AFTER_CONFIG', 1);
 require('includes/common.php');
 
+endif; // ENANO_JSRES_SETUP_ONLY
+
 // CONFIG
 
 // Files safe to run full (aggressive) compression on
@@ -101,9 +109,10 @@
 // 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', 'admin-menu.js', 'autofill.js');
 
-require('includes/js-compressor.php');
+require_once('includes/js-compressor.php');
 
 // try to gzip the output
+if ( !defined('ENANO_JSRES_SETUP_ONLY') ):
 $do_gzip = false;
 if ( isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
 {
@@ -118,6 +127,9 @@
 
 // Output format will always be JS
 header('Content-type: text/javascript');
+
+endif; // ENANO_JSRES_SETUP_ONLY
+
 $everything = "/* The code represented in this file is compressed for optimization purposes. The full source code is available in includes/clientside/static/. */\n\nvar ENANO_JSRES_COMPRESSED = true;\n\n";
 
 // if we only want the tiny version of the API (just enough to get by until the full one is loaded), send that
@@ -226,6 +238,12 @@
 $everything = str_replace('/* JavaScriptCompressor 0.8 [www.devpro.it], thanks to Dean Edwards for idea [dean.edwards.name] */' . "\r\n", '', $everything);
 
 $date = date('r', $apex);
+
+if ( defined('ENANO_JSRES_SETUP_ONLY') )
+{
+  return; // we're done setting up, break out
+}
+
 header("Date: $date");
 header("Last-Modified: $date");
 header("ETag: \"$etag\"");
--- a/includes/clientside/static/ajax.js	Sat Jul 19 21:14:54 2008 -0400
+++ b/includes/clientside/static/ajax.js	Sun Jul 20 13:32:04 2008 -0400
@@ -1054,7 +1054,7 @@
   var target = document.getElementById(targetelement);
   target.innerHTML = '';
   var img = document.createElement('img');
-  img.src = scriptPath + '/images/loading.gif';
+  img.src = cdnPath + '/images/loading.gif';
   img.alt = 'Loading...';
   target.appendChild(img);
   ajaxGet(makeUrlNS('Admin', 'Home/updates.xml'), function()
--- a/includes/clientside/static/editor.js	Sat Jul 19 21:14:54 2008 -0400
+++ b/includes/clientside/static/editor.js	Sun Jul 20 13:32:04 2008 -0400
@@ -534,7 +534,7 @@
     // ajaxSetEditorLoading();
     var img = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
     var lbl = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
-    img.src = scriptPath + '/images/loading.gif';
+    img.src = cdnPath + '/images/loading.gif';
     var d = new Date();
     var m = String(d.getMinutes());
     if ( m.length < 2 )
--- a/includes/clientside/static/enano-lib-basic.js	Sat Jul 19 21:14:54 2008 -0400
+++ b/includes/clientside/static/enano-lib-basic.js	Sun Jul 20 13:32:04 2008 -0400
@@ -137,7 +137,7 @@
 var startwidth  = false;
 var startheight = false;
 var do_width    = false;
-var ajax_load_icon = scriptPath + '/images/loading.gif';
+var ajax_load_icon = cdnPath + '/images/loading.gif';
 var editor_use_modal_window = false;
 var Spry = {};
 
@@ -220,14 +220,14 @@
   _load_component_running = true;
   file = file.replace(/\.js$/, '');
   
-  console.info('Loading component %s via AJAX', file);
-  
-  if ( loaded_components[file] )
+  if ( loaded_components[file + '.js'] )
   {
     // already loaded
     return true;
   }
   
+  console.info('Loading component %s via AJAX', file);
+  
   load_show_win(file);
   
   // get an XHR instance
@@ -252,7 +252,7 @@
 
 function load_show_win(file)
 {
-  var img = '<img style="margin-right: 5px" src="' + scriptPath + '/images/loading.gif" />';
+  var img = '<img style="margin-right: 5px" src="' + cdnPath + '/images/loading.gif" />';
   if ( document.getElementById('_js_load_component') )
   {
     document.getElementById('_js_load_component').innerHTML = img + msg_loading_component.replace('%component%', file);
@@ -268,6 +268,7 @@
   ld.innerHTML = img + msg_loading_component.replace('%component%', file);
   ld.id = '_js_load_component';
   
+  // FYI: The base64 encoded image is a 70% opacity 1x1px white PNG.
   ld.style.backgroundImage = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAA1JREFUCNdj+P///xkACcgDypG+nnEAAAAASUVORK5CYII=)';
   
   document.body.appendChild(ld);
@@ -343,6 +344,37 @@
   return true;
 }
 
+var autofill_check = function()
+{
+  var inputs = document.getElementsByTagName('input');
+  for ( var i = 0; i < inputs.length; i++ )
+  {
+    if ( inputs[i].className )
+    {
+      if ( inputs[i].className.match(/^autofill/) )
+      {
+        load_component('autofill');
+        return;
+      }
+    }
+    /*
+    else if ( typeof(inputs[i].onkeyup) == 'function' )
+    {
+      var f = new String(inputs[i].onkeyup);
+      if ( f.match(/AutofillUsername/) )
+      {
+        delete(f.onkeyup);
+        f.className = 'autofill username';
+        autofill_check();
+        return;
+      }
+    }
+    */
+  }
+}
+
+addOnloadHook(autofill_check);
+
 var head = document.getElementsByTagName('head')[0];
 
 // safari has window.console but not the .debug() method
@@ -377,7 +409,7 @@
     // alert('kill switch and problem script');
     continue;
   }
-  script.src=scriptPath+"/includes/clientside/static/"+thefiles[f];
+  script.src=cdnPath+"/includes/clientside/static/"+thefiles[f];
   head.appendChild(script);
 }
 
--- a/includes/clientside/static/functions.js	Sat Jul 19 21:14:54 2008 -0400
+++ b/includes/clientside/static/functions.js	Sun Jul 20 13:32:04 2008 -0400
@@ -629,7 +629,7 @@
 {
   if ( document.getElementById('ajaxloadicon') )
   {
-    document.getElementById('ajaxloadicon').src=scriptPath + '/images/spacer.gif';
+    document.getElementById('ajaxloadicon').src=cdnPath + '/images/spacer.gif';
   }
 }
 
--- a/includes/common.php	Sat Jul 19 21:14:54 2008 -0400
+++ b/includes/common.php	Sun Jul 20 13:32:04 2008 -0400
@@ -263,6 +263,9 @@
   grinding_halt('Version mismatch', '<p>It seems that the Enano release we\'re trying to run ('.$version.') is different from the version specified in your database ('.enano_version().'). Perhaps you need to <a href="'.scriptPath.'/install/upgrade.php">upgrade</a>?</p>');
 }
 
+// Set our CDN path
+define('cdnPath', getConfig('cdn_path', scriptPath));
+
 //
 // Low level maintenance
 //
--- a/includes/common_cli.php	Sat Jul 19 21:14:54 2008 -0400
+++ b/includes/common_cli.php	Sun Jul 20 13:32:04 2008 -0400
@@ -75,6 +75,9 @@
   grinding_halt('Version mismatch', 'Trying to run Enano version '.$version.' on database version '.enano_version().', you might need to upgrade.');
 }
 
+// Set our CDN path
+define('cdnPath', getConfig('cdn_path', scriptPath));
+
 //
 // Low level maintenance
 //
--- a/includes/paths.php	Sat Jul 19 21:14:54 2008 -0400
+++ b/includes/paths.php	Sun Jul 20 13:32:04 2008 -0400
@@ -475,7 +475,7 @@
       {
         $i++;
         $name = ( preg_match('/^[a-z0-9_]+$/', $key) ) ? $lang->get($c['name']) : $c['name'];
-        if ( $c['icon'] && $c['icon'] != scriptPath . '/images/spacer.gif' )
+        if ( $c['icon'] && $c['icon'] != cdnPath . '/images/spacer.gif' )
         {
           if ( is_array($c['icon']) )
           {
@@ -520,7 +520,7 @@
   {
     $xpos = 16 * ( $ix - 1 );
     $ypos = 16 * ( $iy - 1 );
-    return "<img alt=\"\" src=\"" . scriptPath . "/images/spacer.gif\" class=\"adminiconsprite\" style=\"border-width: 0; margin-right: 3px; background-position: -{$xpos}px -{$ypos}px;\" /> ";
+    return "<img alt=\"\" src=\"" . cdnPath . "/images/spacer.gif\" class=\"adminiconsprite\" style=\"border-width: 0; margin-right: 3px; background-position: -{$xpos}px -{$ypos}px;\" /> ";
   }
   
   /**
@@ -535,7 +535,7 @@
   {
     if ( !$icon )
     {
-      $icon = scriptPath . '/images/spacer.gif';
+      $icon = cdnPath . '/images/spacer.gif';
     }
     if(!isset($this->admin_tree[$section]))
     {
--- a/includes/template.php	Sat Jul 19 21:14:54 2008 -0400
+++ b/includes/template.php	Sun Jul 20 13:32:04 2008 -0400
@@ -56,8 +56,8 @@
     $this->plugin_blocks = Array();
     $this->theme_loaded = false;
     
-    $this->fading_button = '<div style="background-image: url('.scriptPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
-                              <a style="background-image: none; padding-right: 0;" href="http://enanocms.org/" onclick="window.open(this.href); return false;"><img style="border-width: 0;" alt=" " src="'.scriptPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
+    $this->fading_button = '<div style="background-image: url('.cdnPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
+                              <a style="background-image: none; padding-right: 0;" href="http://enanocms.org/" onclick="window.open(this.href); return false;"><img style="border-width: 0;" alt=" " src="'.cdnPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
                             </div>';
     
     $this->theme_list = Array();
@@ -986,6 +986,54 @@
       eval($cmd);
     }
     
+    // Set up javascript includes
+    // these depend heavily on whether we have a CDN to work with or not
+    if ( getConfig('cdn_path') )
+    {
+      // we're on a CDN, point to static includes
+      // probably should have a way to compress stuff like this before uploading to CDN
+      $js_head = '<script type="text/javascript" src="' . cdnPath . '/includes/clientside/static/enano-lib-basic.js"></script>';
+      $js_foot = <<<JSEOF
+    <script type="text/javascript">
+      // This initializes the Javascript runtime when the DOM is ready - not when the page is
+      // done loading, because enano-lib-basic still has to load some 15 other script files
+      // check for the init function - this is a KHTML fix
+      // This doesn't seem to work properly in IE in 1.1.x - there are some problems with
+      // tinyMCE and l10n.
+      if ( typeof ( enano_init ) == 'function' && !IE )
+      {
+        enano_init();
+        window.onload = function(e) {  };
+      }
+    </script>
+JSEOF;
+    }
+    else
+    {
+      $cdnpath = cdnPath;
+      // point to jsres compressor
+      $js_head = <<<JSEOF
+      <!-- Only load a basic set of functions for now. Let the rest of the API load when the page is finished. -->
+      <script type="text/javascript" src="$cdnpath/includes/clientside/jsres.php?early"></script>
+JSEOF;
+      $js_foot = <<<JSEOF
+    <!-- jsres.php is a wrapper script that compresses and caches single JS files to minimize requests -->
+    <script type="text/javascript" src="$cdnpath/includes/clientside/jsres.php"></script>
+    <script type="text/javascript">
+      // This initializes the Javascript runtime when the DOM is ready - not when the page is
+      // done loading, because enano-lib-basic still has to load some 15 other script files
+      // check for the init function - this is a KHTML fix
+      // This doesn't seem to work properly in IE in 1.1.x - there are some problems with
+      // tinyMCE and l10n.
+      if ( typeof ( enano_init ) == 'function' && !IE )
+      {
+        enano_init();
+        window.onload = function(e) {  };
+      }
+    </script>
+JSEOF;
+    }
+    
     // Some additional sidebar processing
     if ( $this->sidebar_extra != '' )
     {
@@ -1077,8 +1125,9 @@
       var title = \''. $urlname_jssafe .'\';
       var physical_title = \'' . $physical_urlname_jssafe . '\';
       var page_exists = '. ( ( $local_page_exists) ? 'true' : 'false' ) .';
-      var scriptPath = \''. scriptPath .'\';
-      var contentPath = \''.contentPath.'\';
+      var scriptPath = \'' . addslashes(scriptPath) . '\';
+      var contentPath = \'' . addslashes(contentPath) . '\';
+      var cdnPath = \'' . addslashes(cdnPath) . '\';
       var ENANO_SID = \'' . $SID . '\';
       var user_level = ' . $session->user_level . ';
       var auth_level = ' . $session->auth_level . ';
@@ -1128,6 +1177,7 @@
       'TOOLBAR'=>$tb,
       'SCRIPTPATH'=>scriptPath,
       'CONTENTPATH'=>contentPath,
+      'CDNPATH' => cdnPath,
       'ADMIN_SID_QUES'=>$asq,
       'ADMIN_SID_AMP'=>$asa,
       'ADMIN_SID_AMP_HTML'=>$ash,
@@ -1147,6 +1197,8 @@
       'TEMPLATE_DIR'=>scriptPath.'/themes/'.$this->theme,
       'THEME_ID'=>$this->theme,
       'STYLE_ID'=>$this->style,
+      'JS_HEADER' => $js_head,
+      'JS_FOOTER' => $js_foot,
       'JS_DYNAMIC_VARS'=>$js_dynamic,
       'UNREAD_PMS'=>$session->unread_pms,
       'URL_ABOUT_ENANO' => makeUrlNS('Special', 'About_Enano', '', true),
@@ -2444,8 +2496,8 @@
     $this->toolbar_menu = '';
     $this->additional_headers = '<style type="text/css">div.pagenav { border-top: 1px solid #CCC; padding-top: 7px; margin-top: 10px; }</style>';
     
-    $this->fading_button = '<div style="background-image: url('.scriptPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
-                              <a href="http://enanocms.org/" onclick="window.open(this.href); return false;"><img style="border-width: 0;" alt=" " src="'.scriptPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
+    $this->fading_button = '<div style="background-image: url('.cdnPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
+                              <a href="http://enanocms.org/" onclick="window.open(this.href); return false;"><img style="border-width: 0;" alt=" " src="'.cdnPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
                             </div>';
     
     // get list of themes
--- a/language/english/admin.json	Sat Jul 19 21:14:54 2008 -0400
+++ b/language/english/admin.json	Sun Jul 20 13:32:04 2008 -0400
@@ -259,6 +259,12 @@
       field_breadcrumb_mode_always: 'Always',
       field_breadcrumb_mode_never: 'Never',
       
+      // Section: CDN
+      
+      field_cdn_path: 'URL to <acronym title="Content Delivery Network">CDN</acronym> server:',
+      field_cdn_path_hint: 'A CDN, or Content Delivery Network, allows downloading of shared Enano components from a server designed to serve out only images, CSS, and script files. Since a browser can open separate connections for the page and for images and scripts, the page loads faster. Leave this blank to just use Enano\'s local files (default).',
+      field_cdn_path_example: 'Example: <tt>http://cdn.mycompany.com/enano/',
+      
       // Main section: users and communication
       heading_users: 'Users and communication',
       
--- a/plugins/SpecialAdmin.php	Sat Jul 19 21:14:54 2008 -0400
+++ b/plugins/SpecialAdmin.php	Sun Jul 20 13:32:04 2008 -0400
@@ -317,6 +317,13 @@
       setConfig('breadcrumb_mode', $_POST['breadcrumb_mode']);
     }
     
+    // CDN path
+    if ( preg_match('/^http:\/\//', $_POST['cdn_path']) || $_POST['cdn_path'] === '' )
+    {
+      // trim off a trailing slash
+      setConfig('cdn_path', preg_replace('#/$#', '', $_POST['cdn_path']));
+    }
+    
     setConfig('register_tou', RenderMan::preprocess_text($_POST['register_tou'], true, false));
     
     // Account lockout policy
@@ -567,6 +574,23 @@
           </select>
         </td>
       </tr>
+    
+    <!-- CDN settings -->
+    
+      <tr>
+        <td class="row2">
+          <p>
+            <?php echo $lang->get('acpgc_field_cdn_path'); ?><br />
+            <small><?php echo $lang->get('acpgc_field_cdn_path_hint'); ?></small>
+          </p>
+          <p>
+            <small><?php echo $lang->get('acpgc_field_cdn_path_example'); ?></small>
+          </p>
+        </td>
+        <td class="row2">
+          <input type="text" name="cdn_path" value="<?php echo htmlspecialchars(getConfig('cdn_path', '')); ?>" style="width: 98%;" />
+        </td>
+      </tr>
       
     </table>
     </div>
@@ -1995,7 +2019,7 @@
   }
   else
   {
-    $template->add_header('<script type="text/javascript" src="' . scriptPath . '/includes/clientside/static/admin-menu.js"></script>');
+    $template->add_header('<script type="text/javascript" src="' . cdnPath . '/includes/clientside/static/admin-menu.js"></script>');
     if( !isset( $_GET['noheaders'] ) ) 
     {
       $template->header();
@@ -2088,28 +2112,28 @@
       'target'  : '_self',  // name of the frame links will be opened in
                   // other possible values are: _blank, _parent, _search, _self and _top
     
-      'icon_e'  : '<?php echo scriptPath; ?>/images/icons/empty.gif',      // empty image
-      'icon_l'  : '<?php echo scriptPath; ?>/images/icons/line.gif',       // vertical line
-      'icon_32' : '<?php echo scriptPath; ?>/images/icons/base.gif',       // root leaf icon normal
-      'icon_36' : '<?php echo scriptPath; ?>/images/icons/base.gif',       // root leaf icon selected
-      'icon_48' : '<?php echo scriptPath; ?>/images/icons/base.gif',       // root icon normal
-      'icon_52' : '<?php echo scriptPath; ?>/images/icons/base.gif',       // root icon selected
-      'icon_56' : '<?php echo scriptPath; ?>/images/icons/base.gif',       // root icon opened
-      'icon_60' : '<?php echo scriptPath; ?>/images/icons/base.gif',       // root icon selected
-      'icon_16' : '<?php echo scriptPath; ?>/images/icons/folder.gif',     // node icon normal
-      'icon_20' : '<?php echo scriptPath; ?>/images/icons/folderopen.gif', // node icon selected
-      'icon_24' : '<?php echo scriptPath; ?>/images/icons/folder.gif',     // node icon opened
-      'icon_28' : '<?php echo scriptPath; ?>/images/icons/folderopen.gif', // node icon selected opened
-      'icon_0'  : '<?php echo scriptPath; ?>/images/icons/page.gif',       // leaf icon normal
-      'icon_4'  : '<?php echo scriptPath; ?>/images/icons/page.gif',       // leaf icon selected
-      'icon_8'  : '<?php echo scriptPath; ?>/images/icons/page.gif',       // leaf icon opened
-      'icon_12' : '<?php echo scriptPath; ?>/images/icons/page.gif',       // leaf icon selected
-      'icon_2'  : '<?php echo scriptPath; ?>/images/icons/joinbottom.gif', // junction for leaf
-      'icon_3'  : '<?php echo scriptPath; ?>/images/icons/join.gif',       // junction for last leaf
-      'icon_18' : '<?php echo scriptPath; ?>/images/icons/plusbottom.gif', // junction for closed node
-      'icon_19' : '<?php echo scriptPath; ?>/images/icons/plus.gif',       // junction for last closed node
-      '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
+      'icon_e'  : '<?php echo cdnPath; ?>/images/icons/empty.gif',      // empty image
+      'icon_l'  : '<?php echo cdnPath; ?>/images/icons/line.gif',       // vertical line
+      'icon_32' : '<?php echo cdnPath; ?>/images/icons/base.gif',       // root leaf icon normal
+      'icon_36' : '<?php echo cdnPath; ?>/images/icons/base.gif',       // root leaf icon selected
+      'icon_48' : '<?php echo cdnPath; ?>/images/icons/base.gif',       // root icon normal
+      'icon_52' : '<?php echo cdnPath; ?>/images/icons/base.gif',       // root icon selected
+      'icon_56' : '<?php echo cdnPath; ?>/images/icons/base.gif',       // root icon opened
+      'icon_60' : '<?php echo cdnPath; ?>/images/icons/base.gif',       // root icon selected
+      'icon_16' : '<?php echo cdnPath; ?>/images/icons/folder.gif',     // node icon normal
+      'icon_20' : '<?php echo cdnPath; ?>/images/icons/folderopen.gif', // node icon selected
+      'icon_24' : '<?php echo cdnPath; ?>/images/icons/folder.gif',     // node icon opened
+      'icon_28' : '<?php echo cdnPath; ?>/images/icons/folderopen.gif', // node icon selected opened
+      'icon_0'  : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon normal
+      'icon_4'  : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon selected
+      'icon_8'  : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon opened
+      'icon_12' : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon selected
+      'icon_2'  : '<?php echo cdnPath; ?>/images/icons/joinbottom.gif', // junction for leaf
+      'icon_3'  : '<?php echo cdnPath; ?>/images/icons/join.gif',       // junction for last leaf
+      'icon_18' : '<?php echo cdnPath; ?>/images/icons/plusbottom.gif', // junction for closed node
+      'icon_19' : '<?php echo cdnPath; ?>/images/icons/plus.gif',       // junction for last closed node
+      'icon_26' : '<?php echo cdnPath; ?>/images/icons/minusbottom.gif',// junction for opened node
+      'icon_27' : '<?php echo cdnPath; ?>/images/icons/minus.gif'       // junction for last opended node
     };
     
     addOnloadHook(function()
@@ -2199,10 +2223,10 @@
   else 
   {
     
-    $template->add_header('<script type="text/javascript" src="'.scriptPath.'/includes/clientside/dbx.js"></script>');
-    $template->add_header('<script type="text/javascript" src="'.scriptPath.'/includes/clientside/dbx-key.js"></script>');
-    $template->add_header('<script type="text/javascript" src="'.scriptPath.'/includes/clientside/sbedit.js"></script>');
-    $template->add_header('<link rel="stylesheet" type="text/css" href="'.scriptPath.'/includes/clientside/dbx.css" />');
+    $template->add_header('<script type="text/javascript" src="'.cdnPath.'/includes/clientside/dbx.js"></script>');
+    $template->add_header('<script type="text/javascript" src="'.cdnPath.'/includes/clientside/dbx-key.js"></script>');
+    $template->add_header('<script type="text/javascript" src="'.cdnPath.'/includes/clientside/sbedit.js"></script>');
+    $template->add_header('<link rel="stylesheet" type="text/css" href="'.cdnPath.'/includes/clientside/dbx.css" />');
     
     $template->load_theme('oxygen', 'bleu');
     $template->init_vars();
--- a/themes/admin/footer.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/admin/footer.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -38,5 +38,6 @@
     </tr>
     </table>
     
+    {JS_FOOTER}
   </body>
 </html>
--- a/themes/admin/header.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/admin/header.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -3,15 +3,14 @@
   <head>
     <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/includes/clientside/css/enano-shared.css" />
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
+    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css" />
+    <link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
     <!--[if IE]>
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css-ie/iefixes.css" />
+    <link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-ie/iefixes.css" />
     <![endif]-->
     {JS_DYNAMIC_VARS}
-    <!-- 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>
+    {JS_HEADER}
+    <script type="text/javascript" src="{CDNPATH}/themes/admin/js/menu.js"></script>
     {ADDITIONAL_HEADERS}
     </head>
   <body>
@@ -40,7 +39,7 @@
         <td class="left"></td>
         <td class="main">
           <div style="float: right;">
-            <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
+            <img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
           </div>
           <h2 class="pagename">{PAGE_NAME}</h2>
           <div id="ajaxEditContainer">
--- a/themes/admin/simple-header.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/admin/simple-header.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -39,7 +39,7 @@
         <td class="left"></td>
         <td class="main">
           <div style="float: right;">
-            <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
+            <img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
           </div>
           <h2 class="pagename">{PAGE_NAME}</h2>
           <div id="ajaxEditContainer">
--- a/themes/oxygen/elements.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/oxygen/elements.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -18,9 +18,9 @@
           <div class="recttop">
             <table border="0" width="100%" cellspacing="0" cellpadding="0" style="font-size: 1px;">
               <tr>
-                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" style="background-image: url({SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: 0 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
+                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: 0 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
                 <td style="margin: 0; padding: 0; height: 12px;" class="recttoptop" onclick="var id = this.parentNode.parentNode.parentNode.parentNode.parentNode.id; var side = id.substr(0, id.indexOf('-')); collapseSidebar(side);"></td>
-                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" style="background-image: url({SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -12px 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
+                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -12px 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
               </tr>
             </table>
           </div>
@@ -55,9 +55,9 @@
           <div class="rectbot">
             <table border="0" width="100%" cellspacing="0" cellpadding="0" style="font-size: 1px;">
               <tr>
-                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" style="background-image: url({SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -24px 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
+                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -24px 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
                 <td style="margin: 0; padding: 0; height: 12px;" class="rectbottop"></td>
-                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" style="background-image: url({SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -36px 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
+                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -36px 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
               </tr>
             </table>
           </div>
--- a/themes/oxygen/footer.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/oxygen/footer.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -51,19 +51,6 @@
         Loading...
       </div>
     </div>
-    <!-- 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">
-      // This initializes the Javascript runtime when the DOM is ready - not when the page is
-      // done loading, because enano-lib-basic still has to load some 15 other script files
-      // check for the init function - this is a KHTML fix
-      // This doesn't seem to work properly in IE in 1.1.x - there are some problems with
-      // tinyMCE and l10n.
-      if ( typeof ( enano_init ) == 'function' && !IE )
-      {
-        enano_init();
-        window.onload = function(e) {  };
-      }
-    </script>
+    {JS_FOOTER}
   </body>
 </html>
--- a/themes/oxygen/header.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/oxygen/header.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -3,15 +3,13 @@
   <head>
     <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/includes/clientside/css/enano-shared.css" />
+    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css" />
     <!-- BEGIN msie -->
-    <link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/includes/clientside/css/enano-shared.css" />
+    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css" />
     <!-- END msie -->
-    <link id="mdgCss" rel="stylesheet" href="{SCRIPTPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" type="text/css" />
+    <link id="mdgCss" rel="stylesheet" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" type="text/css" />
     {JS_DYNAMIC_VARS}
-    
-    <!-- Only load a basic set of functions for now. Let the rest of the API load when the page is finished. -->
-    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/jsres.php?early"></script>
+    {JS_HEADER}
     
     <script type="text/javascript">
       var tinymce_skin = 'o2k7';
@@ -180,7 +178,7 @@
         <tr><td id="mdg-ml"></td><td style="background-color: #FFFFFF;">
           <div class="pad"><div class="contentDiv">
           <div style="float: right;">
-            <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
+            <img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
           </div>
           <h2 <!-- BEGIN auth_rename --> ondblclick="ajaxRenameInline();" title="Double-click to rename this page" <!-- END auth_rename --> id="h2PageName">{PAGE_NAME}</h2>
             <div id="ajaxEditContainer">
--- a/themes/oxygen/simple-header.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/oxygen/simple-header.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -3,10 +3,10 @@
   <head>
     <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" href="{SCRIPTPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" type="text/css" id="mdgCss" />
+    <link rel="stylesheet" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" type="text/css" id="mdgCss" />
     {JS_DYNAMIC_VARS}
     <!-- This script automatically loads the other 15 JS files -->
-    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
+    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/jsres.php"></script>
     {ADDITIONAL_HEADERS}
   </head>
   <body>
--- a/themes/printable/css-simple/printbits.css	Sat Jul 19 21:14:54 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-span.normallink {
-  display: none;
-}
-div.mdg-comment {
-  display: none;
-}
-
--- a/themes/printable/css/default.css	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/printable/css/default.css	Sun Jul 20 13:32:04 2008 -0400
@@ -350,3 +350,10 @@
   padding-right: 5px;
 }
 
+span.normallink {
+  display: none;
+}
+div.mdg-comment {
+  display: none;
+}
+
--- a/themes/printable/footer.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/printable/footer.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -9,18 +9,8 @@
                  -->
             <div id="credits">
               {COPYRIGHT}<br />
-              Powered by <a href="http://www.enanocms.org">Enano</a>  |  <a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.1</a>  |  <a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">Valid CSS</a>  |  [[Stats]]
+              Powered by <a href="http://enanocms.org">Enano</a>  |  <a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.1</a>  |  <a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">Valid CSS</a>  |  [[Stats]]
             </div>
             
-    <script type="text/javascript">
-      // This initializes the Javascript runtime when the DOM is ready - not when the page is
-      // done loading, because enano-lib-basic still has to load some 15 other script files
-      // check for the init function - this is a KHTML fix
-      if ( typeof ( enano_init ) == 'function' )
-      {
-        enano_init();
-        window.onload = function(e) {  };
-      }
-    </script>
   </body>
 </html>
--- a/themes/printable/header.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/printable/header.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -3,19 +3,15 @@
   <head>
     <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/includes/clientside/css/enano-shared.css" />
-    <link id="mdgCss" rel="stylesheet" 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}
-    <!-- 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>
+    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css" />
+    <link id="mdgCss" rel="stylesheet" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" type="text/css" />
     {ADDITIONAL_HEADERS}
     
   </head>
   <body>
     <div class="pad"><div class="contentDiv">
       <div style="float: right;">
-        <span class="normallink"><a href="#" onclick="window.print(); return false;">print page</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="{CONTENTPATH}{PAGE_URLNAME}{ADMIN_SID_AUTO}">view normal version</a></span>&nbsp;<image alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
+        <span class="normallink"><a href="#" onclick="window.print(); return false;">print page</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="{CONTENTPATH}{PAGE_URLNAME}{ADMIN_SID_AUTO}">view normal version</a></span>&nbsp;<image alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
       </div>
       <h2>{PAGE_NAME}</h2>
         <div id="ajaxEditContainer">
--- a/themes/stpatty/footer.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/stpatty/footer.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -33,12 +33,7 @@
         Loading...
       </div>
     </div>
-    <script type="text/javascript">
-      // This initializes the Javascript runtime when the DOM is ready - not when the page is
-      // done loading, because enano-lib-basic still has to load some 15 other script files
-      enano_init();
-      window.onload = function(e) {  };
-    </script>
+    {JS_FOOTER}
   </body>
 </html>
 
--- a/themes/stpatty/header.tpl	Sat Jul 19 21:14:54 2008 -0400
+++ b/themes/stpatty/header.tpl	Sun Jul 20 13:32:04 2008 -0400
@@ -4,12 +4,11 @@
     <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
     {JS_DYNAMIC_VARS}
-    <link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/includes/clientside/css/enano-shared.css" />
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
-    <!-- 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>
+    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css" />
+    <link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
+    {JS_HEADER}
     <!--[if lt IE 7]>
-    <link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css-extra/ie-fixes-{STYLE_ID}.css" />
+    <link rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-extra/ie-fixes-{STYLE_ID}.css" />
     <![endif]-->
     <script type="text/javascript">
     // <![CDATA[
@@ -77,7 +76,7 @@
     <div id="bg">
       <div id="rap">
         <div id="title">
-          <img id="clover" src="{SCRIPTPATH}/themes/{THEME_ID}/images/clover.png" alt=" " />
+          <img id="clover" src="{CDNPATH}/themes/{THEME_ID}/images/clover.png" alt=" " />
           <h1>{SITE_NAME}</h1>
           <h2>{SITE_DESC}</h2>
         </div>
@@ -101,7 +100,7 @@
         </div>
         <div id="maincontent">
           <div style="float: right;">
-            <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
+            <img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
           </div>
           <h2 id="pagetitle" <!-- BEGIN auth_rename --> ondblclick="ajaxRenameInline();" title="Double-click to rename this page" <!-- END auth_rename -->>{PAGE_NAME}</h2>
           <div id="ajaxEditContainer">