[pLog-svn] r2767 - in plog/trunk: class/template/menu js js/jscookmenu js/jscookmenu/themeoffice js/prototype js/rico js/ui styles templates/admin

mark at devel.lifetype.net mark at devel.lifetype.net
Wed Jan 11 10:39:11 GMT 2006


Author: mark
Date: 2006-01-11 10:39:10 +0000 (Wed, 11 Jan 2006)
New Revision: 2767

Added:
   plog/trunk/js/jscookmenu/
   plog/trunk/js/jscookmenu/JSCookMenu.js
   plog/trunk/js/jscookmenu/themeoffice/
   plog/trunk/js/jscookmenu/themeoffice/arrow.gif
   plog/trunk/js/jscookmenu/themeoffice/arrowdown.gif
   plog/trunk/js/jscookmenu/themeoffice/blank.gif
   plog/trunk/js/jscookmenu/themeoffice/copy.gif
   plog/trunk/js/jscookmenu/themeoffice/copyshadow.gif
   plog/trunk/js/jscookmenu/themeoffice/cut.gif
   plog/trunk/js/jscookmenu/themeoffice/cutshadow.gif
   plog/trunk/js/jscookmenu/themeoffice/help.gif
   plog/trunk/js/jscookmenu/themeoffice/helpshadow.gif
   plog/trunk/js/jscookmenu/themeoffice/new.gif
   plog/trunk/js/jscookmenu/themeoffice/open.gif
   plog/trunk/js/jscookmenu/themeoffice/openshadow.gif
   plog/trunk/js/jscookmenu/themeoffice/paste.gif
   plog/trunk/js/jscookmenu/themeoffice/pasteshadow.gif
   plog/trunk/js/jscookmenu/themeoffice/save.gif
   plog/trunk/js/jscookmenu/themeoffice/saveshadow.gif
   plog/trunk/js/jscookmenu/themeoffice/spacer.gif
   plog/trunk/js/jscookmenu/themeoffice/theme.css
   plog/trunk/js/jscookmenu/themeoffice/theme.js
   plog/trunk/js/rico/
   plog/trunk/js/rico/rico.js
Modified:
   plog/trunk/class/template/menu/menurenderer.class.php
   plog/trunk/js/prototype/prototype.js
   plog/trunk/js/ui/plogui.js
   plog/trunk/styles/admin.css
   plog/trunk/templates/admin/editpost.template
   plog/trunk/templates/admin/header.template
   plog/trunk/templates/admin/newpost.template
Log:
This is another big commit about UI:
1. Change the admin.css for better tab effect
2. Add JsCookMenu.js for adding the drop down menu to LifeType
3. Change the header.template to enable the Drop-Down menu. The default is disable.
4. Change the header.template to include the rico ajax library
5. Add show/hide capability to newpost/editpost page based on Rico Ajax library
6. Upgrade prototype.js to 1.4.0

That's All!

ToDo:
1. Add a new blogsetting to control to use the original menu or drop-down menu
2. Add cookie to record user behavior about hide/show option (or how about use blogsetting to remember it, too?)

Modified: plog/trunk/class/template/menu/menurenderer.class.php
===================================================================
--- plog/trunk/class/template/menu/menurenderer.class.php	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/class/template/menu/menurenderer.class.php	2006-01-11 10:39:10 UTC (rev 2767)
@@ -1,209 +1,294 @@
-<?php
-
-    
-    include_once( PLOG_CLASS_PATH."class/locale/locales.class.php" );
-    include_once( PLOG_CLASS_PATH."class/template/templateservice.class.php" );	
-    include_once( PLOG_CLASS_PATH."class/template/menu/menu.class.php" );	
-    include_once( PLOG_CLASS_PATH."class/template/menu/menuentry.class.php" );
-	include_once( PLOG_CLASS_PATH."class/locale/locales.class.php" );
-    
-    /**
-     * \ingroup Template
-     *
-     * dumps the contents of the menu in a tree-fashion way using unordered html lists. It is necessary
-     * to use some additional CSS styling to give some formatting to the menu.
-     *
-     * This class is only used at the presentation layer of pLog to generate the menu structure,
-     * and class users should never need to create an instance of this class.
-     *
-     * See the "Menu API reference" document in the wiki for more information:
-     * http://wiki.plogworld.net/index.php/PLog_1.0/The_Menu_API
-     */
-    class MenuRenderer 
-    {
-        var $_menu;
-		var $_blogInfo;
-		var $_userInfo;
-		
-		/**
-		 * Initializes the renderer
-		 *
-		 * @param menu A vaid Menu object
-		 * @param blogInfo A reference to a BlogInfo object containing information of the blog for whom
-		 * we are going to render this menu tree.
-		 * @param userInfo A reference to a UserInfo object containing information of the user for whom
-		 * we are going to render this menu tree, because we need to know which options he/she is allowed
-		 * to see and which not.
-		 */
-        function MenuRenderer( $menu, $blogInfo, $userInfo )
-        {
-            
-            $this->_menu = $menu;
-			$this->_blogInfo = $blogInfo;
-			$this->_userInfo = $userInfo;
-			
-			$config =& Config::getConfig();			
-			
-			// load the right locale
-			if( $blogInfo != null ) {
-				$this->_locale =& $blogInfo->getLocale();
-			}
-			else { 
-				$localeCode = $config->getValue( "default_locale" );
-				$this->_locale =& Locales::getLocale( $localeCode );
-			}
-        }
-        
-        /**
-         * renders the contents of the menu
-         *
-         * @param depth How many depth levels we should generate.
-         */
-        function generate( $depth = 99 )
-        {
-            $node = $this->_menu->getRoot();
-            return $this->_render( $node, $depth );
-        }
-		
-		/**
-		 * Generates a tree starting from another node which can be different than the
-		 * root node
-		 *
-		 * @param nodeId
-		 * @param depth
-		 * @param activeOpt
-		 */
-		function generateAt( $nodeId, $depth = 1, $activeOpt = "" )
-		{
-			$nodePath = $this->_menu->entryPath( $nodeId );
-			$node = $this->_menu->getEntryAt( $nodePath );
-			return $this->_render( $node, $depth, $template );
-		}
-
-		/**
-		 * returns true if the user has enough permissions to see this node from the menu
-		 *
-		 * @param node
-		 * @param userInfo
-		 * @return true if the user can, false otherwise
-		 */
-		function userCanSee( $node, $userInfo )
-		{
-			// check if the node is for admins and if the user is an admin
-			$nodeIsAdminOnly = $node->getAttribute( "siteAdmin" );
-			if( $nodeIsAdminOnly && !$userInfo->isSiteAdmin())
-				return false;
-
-			// check if the node is only for blog owners and if the user is a blog owner
-			$nodeIsBlogOwnerOnly = $node->getAttribute( "blogOwner" );
-			if( $nodeIsBlogOwnerOnly && ( $this->_blogInfo->getOwner() != $userInfo->getId()))
-				return false;
-
-			// if none of the above is true, then the user does not have enough permissions!
-			return true;
-		}
-        
-        /**
-         * @private
-         * Used by render() to really render the tree
-         */
-        function _render( $node, $depth = 0, $activeOpt = "" )
-        {
-            $result = "<ul class=\"menuTop\">";
-            $depth--;
-            foreach( $node->children as $child ) {
-                if( $child->name != "" ) {
-					// check whether the user has the right permissions to see this
-					if( $this->userCanSee( $child, $this->_userInfo )) {
-						$url = $child->getAttribute( "url" );
-						$localeId = $this->getLocaleId( $child );
-						$cssClass = "Level_".$depth;
-						if( $url != "" ) 
-							$result .= "<li class=\"$cssClass\"><a href=\"".$child->getAttribute("url")."\">".$this->_locale->tr($localeId)."</a></li>";
-						else
-							$result .= "<li class=\"$cssClass\">".$this->_locale->tr($child->name)."</li>";
-						
-						if( $depth > 0 )
-							$result .= $this->_render( $child, $depth, $activeOpt );
-					}
-				}
-			}
-			$result .= "</ul>";
-			
-			return $result;
-        }
-		
-		/**
-		 * gets the locale id, given a node. The locale id can be either the value of the
-		 * "localeId" attribute if specified or the name of the tag otherwise
-		 *
-		 * @param entry A valid menu node
-		 * @return the locale identifier
-		 */
-		function getLocaleId( $entry )
-		{
-			$localeId = $entry->getAttribute("localeId");
-			if( $localeId == "" )
-				$localeId = $entry->name;
-				
-			return $localeId;			
-		}
-		
-        /**
-         * simple version of a "breadcrumbs" feature
-         *
-         * @param entryId
-         */
-        function breadCrumbs( $entryId )
-        {
-           $nodePath = $this->_menu->entryPath( $entryId );
-		   $path = "";
-		   
-           if( $nodePath ) {
-                $parts = explode( "/", $nodePath );
-				$totalParts = count($parts);
-				if( $totalParts == 1 ) $insertBlogLink = true;
-				else $insertBlogLink = false;
-				
-				$count=0;
-                foreach( $parts as $part ) {
-					$path .= $part;
-					$menuEntry = $this->_menu->getEntryAt( $path );
-					if( $menuEntry->getAttribute("ignoreBreadCrumbs") != "1" ) {
-						$localeId = $this->getLocaleId( $menuEntry );
-						if( $entryId == $part ) $result .= "<b>";
-						if( $menuEntry->getAttribute("url") != "" )
-							$result .= "<a href=\"".$menuEntry->getAttribute("url")."\">".$this->_locale->tr($localeId)."</a>";
-						else
-							$result .= $this->_locale->tr($part);
-						if( $entryId == $part ) $result .= "</b>";						
-						if( $count == 0 ) $result .= " &raquo; <a href=\"?op=blogSelect&amp;blogId=".$this->_blogInfo->getId()."\">".$this->_blogInfo->getBlog()."</a>";
-						if( $count < $totalParts-1 ) $result .= " &raquo; ";
-					}
-					$count++;
-					$path .= "/";
-				}
-           }
-		   
-		   return $result;
-        }
-		
-		/**
-	 	 * @param entryId
-		 */
-	   function getOpts( $entryId) 
-	   {
-			$nodePath = $this->_menu->entryPath( $entryId );
-			$parts = explode( "/", $nodePath );
-			array_pop( $parts );	
-			$parentPath = implode( "/", $parts );
-			
-			$parentNode = $this->_menu->getNodeAt( $parentPath );
-			
-			$children = $parentNode->children;
-			
-			// the children of my parent are... my brothers :)))
-			return $children;
-	   }
-    }
-?>
+<?php
+
+    
+    include_once( PLOG_CLASS_PATH."class/locale/locales.class.php" );
+    include_once( PLOG_CLASS_PATH."class/template/templateservice.class.php" );	
+    include_once( PLOG_CLASS_PATH."class/template/menu/menu.class.php" );	
+    include_once( PLOG_CLASS_PATH."class/template/menu/menuentry.class.php" );
+	include_once( PLOG_CLASS_PATH."class/locale/locales.class.php" );
+    
+    /**
+     * \ingroup Template
+     *
+     * dumps the contents of the menu in a tree-fashion way using unordered html lists. It is necessary
+     * to use some additional CSS styling to give some formatting to the menu.
+     *
+     * This class is only used at the presentation layer of pLog to generate the menu structure,
+     * and class users should never need to create an instance of this class.
+     *
+     * See the "Menu API reference" document in the wiki for more information:
+     * http://wiki.plogworld.net/index.php/PLog_1.0/The_Menu_API
+     */
+    class MenuRenderer 
+    {
+        var $_menu;
+		var $_blogInfo;
+		var $_userInfo;
+		
+		/**
+		 * Initializes the renderer
+		 *
+		 * @param menu A vaid Menu object
+		 * @param blogInfo A reference to a BlogInfo object containing information of the blog for whom
+		 * we are going to render this menu tree.
+		 * @param userInfo A reference to a UserInfo object containing information of the user for whom
+		 * we are going to render this menu tree, because we need to know which options he/she is allowed
+		 * to see and which not.
+		 */
+        function MenuRenderer( $menu, $blogInfo, $userInfo )
+        {
+            
+            $this->_menu = $menu;
+			$this->_blogInfo = $blogInfo;
+			$this->_userInfo = $userInfo;
+			
+			$config =& Config::getConfig();			
+			
+			// load the right locale
+			if( $blogInfo != null ) {
+				$this->_locale =& $blogInfo->getLocale();
+			}
+			else { 
+				$localeCode = $config->getValue( "default_locale" );
+				$this->_locale =& Locales::getLocale( $localeCode );
+			}
+        }
+        
+        /**
+         * renders the contents of the menu
+         *
+         * @param depth How many depth levels we should generate.
+         * @param renderFunc render method for menu tree , the value is "JavaScript" and "Plain"         
+         */
+        function generate( $depth = 99, $renderFunc = "Plain" )
+        {
+            $node = $this->_menu->getRoot();
+            
+            if ( $renderFunc != "JavaScript" ) {
+            	return $this->_render( $node, $depth );
+            } else {
+            	$menu = "<SCRIPT LANGUAGE=\"JavaScript\" SRC=\"js/JSCookMenu/JSCookMenu.js\"></SCRIPT>\n";
+            	$menu .= "<LINK REL=\"stylesheet\" HREF=\"js/JSCookMenu/ThemeOffice/theme.css\" TYPE=\"text/css\">";
+				$menu .= "<SCRIPT LANGUAGE=\"JavaScript\" SRC=\"js/JSCookMenu/ThemeOffice/theme.js\"></SCRIPT>";
+            	$menu .= "<SCRIPT LANGUAGE=\"JavaScript\">\n";
+            	$menu .= "<!-- \n";
+            	$menu .= "var mainMenu = [\n";
+            	$menu .= $this->_renderJS( $node, $depth );
+            	$menu .= "];\n";
+            	$menu .= " -->\n";
+            	$menu .= "</SCRIPT>\n";
+            	
+            	$menu .= "<SCRIPT LANGUAGE=\"JavaScript\">\n";
+            	$menu .= "<!--\n";
+				$menu .= "cmDraw ('menu', mainMenu, 'hbr', cmThemeOffice, 'ThemeOffice');\n";
+				$menu .= "-->\n";
+				$menu .= "</SCRIPT>\n";
+            	
+            	return $menu;
+        	}
+        }
+		
+		/**
+		 * Generates a tree starting from another node which can be different than the
+		 * root node
+		 *
+		 * @param nodeId
+		 * @param depth
+		 * @param activeOpt
+		 * @param renderFunc render method for menu tree , the value is "JavaScript" and "Plain"		 
+		 */
+		function generateAt( $nodeId, $depth = 1, $activeOpt = "", $renderFunc = "Plain"  )
+		{
+			$nodePath = $this->_menu->entryPath( $nodeId );
+			$node = $this->_menu->getEntryAt( $nodePath );
+			$start = 0;
+            
+            if ( $renderFunc != "JavaScript" ) {
+            	return $this->_render( $node, $depth, $activeOpt );
+            } else {
+            	$menu = "<SCRIPT LANGUAGE=\"JavaScript\" SRC=\"js/JSCookMenu/JSCookMenu.js\"></SCRIPT>\n";
+            	$menu .= "<LINK REL=\"stylesheet\" HREF=\"js/JSCookMenu/ThemeOffice/theme.css\" TYPE=\"text/css\">";
+				$menu .= "<SCRIPT LANGUAGE=\"JavaScript\" SRC=\"js/JSCookMenu/ThemeOffice/theme.js\"></SCRIPT>";
+            	$menu .= "<SCRIPT LANGUAGE=\"JavaScript\">\n";
+            	$menu .= "<!-- \n";
+            	$menu .= "var mainMenu = [\n";
+            	$menu .= $this->_renderJS( $node, $depth, $start, $activeOpt );
+            	$menu .= "];\n";
+            	$menu .= " -->\n";
+            	$menu .= "</SCRIPT>\n";
+            	
+            	$menu .= "<SCRIPT LANGUAGE=\"JavaScript\">\n";
+            	$menu .= "<!--\n";
+				$menu .= "cmDraw ('menu', mainMenu, 'hbr', cmThemeOffice, 'ThemeOffice');\n";
+				$menu .= "-->\n";
+				$menu .= "</SCRIPT>\n";
+            	
+            	return $menu;
+        	}
+		}
+
+		/**
+		 * returns true if the user has enough permissions to see this node from the menu
+		 *
+		 * @param node
+		 * @param userInfo
+		 * @return true if the user can, false otherwise
+		 */
+		function userCanSee( $node, $userInfo )
+		{
+			// check if the node is for admins and if the user is an admin
+			$nodeIsAdminOnly = $node->getAttribute( "siteAdmin" );
+			if( $nodeIsAdminOnly && !$userInfo->isSiteAdmin())
+				return false;
+
+			// check if the node is only for blog owners and if the user is a blog owner
+			$nodeIsBlogOwnerOnly = $node->getAttribute( "blogOwner" );
+			if( $nodeIsBlogOwnerOnly && ( $this->_blogInfo->getOwner() != $userInfo->getId()))
+				return false;
+
+			// if none of the above is true, then the user does not have enough permissions!
+			return true;
+		}
+        
+        /**
+         * @private
+         * Used by render() to really render the tree
+         */
+        function _render( $node, $depth = 0, $activeOpt = "" )
+        {
+            $result = "<ul class=\"menuTop\">";
+            $depth--;
+            foreach( $node->children as $child ) {
+                if( $child->name != "" ) {
+					// check whether the user has the right permissions to see this
+					if( $this->userCanSee( $child, $this->_userInfo )) {
+						$url = $child->getAttribute( "url" );
+						$localeId = $this->getLocaleId( $child );
+						$cssClass = "Level_".$depth;
+						if( $url != "" ) 
+							$result .= "<li class=\"$cssClass\"><a href=\"".$child->getAttribute("url")."\">".$this->_locale->tr($localeId)."</a></li>";
+						else
+							$result .= "<li class=\"$cssClass\">".$this->_locale->tr($child->name)."</li>";
+						
+						if( $depth > 0 )
+							$result .= $this->_render( $child, $depth, $activeOpt );
+					}
+				}
+			}
+			$result .= "</ul>";
+			
+			return $result;
+        }
+
+        /**
+         * @private
+         * Used by renderJS() to really render the Javascriipt menu tree
+         */
+        function _renderJS( $node, $depth = 0, $start = 0, $activeOpt = "" )
+        {
+			$result  = "";
+            $depth--;
+            foreach( $node->children as $child ) {
+                if( $child->name != "" ) {
+					// check whether the user has the right permissions to see this
+					if( $this->userCanSee( $child, $this->_userInfo )) {
+		            	if( $start == 0 ) {
+		            		$result .= "[";
+		            		$start = 1;
+		            	} else {
+				    		$result .= ",[";
+				    	}
+				    	
+						$url = $child->getAttribute( "url" );
+						$localeId = $this->getLocaleId( $child );
+						
+						if( $url != "" ) 
+							$result .= "null, '".$this->_locale->tr($localeId)."', '".$child->getAttribute("url")."', '_self', null";
+						else
+							$result .= "null, '".$this->_locale->tr($localeId)."', '#', '_self', null";
+						
+						if( $depth > 0 )
+							$result .= $this->_renderJS( $child, $depth, $start, $activeOpt );
+
+						$result .= "]";
+					}
+				}
+			}
+			
+			return $result;
+        }        
+		
+		/**
+		 * gets the locale id, given a node. The locale id can be either the value of the
+		 * "localeId" attribute if specified or the name of the tag otherwise
+		 *
+		 * @param entry A valid menu node
+		 * @return the locale identifier
+		 */
+		function getLocaleId( $entry )
+		{
+			$localeId = $entry->getAttribute("localeId");
+			if( $localeId == "" )
+				$localeId = $entry->name;
+				
+			return $localeId;			
+		}
+		
+        /**
+         * simple version of a "breadcrumbs" feature
+         *
+         * @param entryId
+         */
+        function breadCrumbs( $entryId )
+        {
+           $nodePath = $this->_menu->entryPath( $entryId );
+		   $path = "";
+		   
+           if( $nodePath ) {
+                $parts = explode( "/", $nodePath );
+				$totalParts = count($parts);
+				if( $totalParts == 1 ) $insertBlogLink = true;
+				else $insertBlogLink = false;
+				
+				$count=0;
+                foreach( $parts as $part ) {
+					$path .= $part;
+					$menuEntry = $this->_menu->getEntryAt( $path );
+					if( $menuEntry->getAttribute("ignoreBreadCrumbs") != "1" ) {
+						$localeId = $this->getLocaleId( $menuEntry );
+						if( $entryId == $part ) $result .= "<b>";
+						if( $menuEntry->getAttribute("url") != "" )
+							$result .= "<a href=\"".$menuEntry->getAttribute("url")."\">".$this->_locale->tr($localeId)."</a>";
+						else
+							$result .= $this->_locale->tr($part);
+						if( $entryId == $part ) $result .= "</b>";						
+						if( $count == 0 ) $result .= " &raquo; <a href=\"?op=blogSelect&amp;blogId=".$this->_blogInfo->getId()."\">".$this->_blogInfo->getBlog()."</a>";
+						if( $count < $totalParts-1 ) $result .= " &raquo; ";
+					}
+					$count++;
+					$path .= "/";
+				}
+           }
+		   
+		   return $result;
+        }
+		
+		/**
+	 	 * @param entryId
+		 */
+	   function getOpts( $entryId) 
+	   {
+			$nodePath = $this->_menu->entryPath( $entryId );
+			$parts = explode( "/", $nodePath );
+			array_pop( $parts );	
+			$parentPath = implode( "/", $parts );
+			
+			$parentNode = $this->_menu->getNodeAt( $parentPath );
+			
+			$children = $parentNode->children;
+			
+			// the children of my parent are... my brothers :)))
+			return $children;
+	   }
+    }
+?>

Added: plog/trunk/js/jscookmenu/JSCookMenu.js
===================================================================
--- plog/trunk/js/jscookmenu/JSCookMenu.js	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/js/jscookmenu/JSCookMenu.js	2006-01-11 10:39:10 UTC (rev 2767)
@@ -0,0 +1,1146 @@
+/*
+	JSCookMenu v1.4.4.  (c) Copyright 2002-2005 by Heng Yuan
+
+	Permission is hereby granted, free of charge, to any person obtaining a
+	copy of this software and associated documentation files (the "Software"),
+	to deal in the Software without restriction, including without limitation
+	the rights to use, copy, modify, merge, publish, distribute, sublicense,
+	and/or sell copies of the Software, and to permit persons to whom the
+	Software is furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be included
+	in all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	ITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+	DEALINGS IN THE SOFTWARE.
+*/
+
+// Globals
+var _cmIDCount = 0;
+var _cmIDName = 'cmSubMenuID';		// for creating submenu id
+
+var _cmTimeOut = null;			// how long the menu would stay
+var _cmCurrentItem = null;		// the current menu item being selected;
+
+var _cmNoAction = new Object ();	// indicate that the item cannot be hovered.
+var _cmNoClick = new Object ();		// similar to _cmNoAction but does not respond to mouseup/mousedown events
+var _cmSplit = new Object ();		// indicate that the item is a menu split
+
+var _cmItemList = new Array ();		// a simple list of items
+
+// default node properties
+var _cmNodeProperties =
+{
+  	// main menu display attributes
+  	//
+  	// Note.  When the menu bar is horizontal,
+  	// mainFolderLeft and mainFolderRight are
+  	// put in <span></span>.  When the menu
+  	// bar is vertical, they would be put in
+  	// a separate TD cell.
+
+  	// HTML code to the left of the folder item
+  	mainFolderLeft: '',
+  	// HTML code to the right of the folder item
+  	mainFolderRight: '',
+	// HTML code to the left of the regular item
+	mainItemLeft: '',
+	// HTML code to the right of the regular item
+	mainItemRight: '',
+
+	// sub menu display attributes
+
+	// HTML code to the left of the folder item
+	folderLeft: '',
+	// HTML code to the right of the folder item
+	folderRight: '',
+	// HTML code to the left of the regular item
+	itemLeft: '',
+	// HTML code to the right of the regular item
+	itemRight: '',
+	// cell spacing for main menu
+	mainSpacing: 0,
+	// cell spacing for sub menus
+	subSpacing: 0,
+	// auto disappear time for submenus in milli-seconds
+	delay: 500,
+
+	// act on click to open sub menu
+	// not yet implemented
+	// 0 : use default behavior
+	// 1 : hover open in all cases
+	// 2 : click on main, hover on sub
+	// 3 : click open in all cases
+	clickOpen: 1
+};
+
+//////////////////////////////////////////////////////////////////////
+//
+// Drawing Functions and Utility Functions
+//
+//////////////////////////////////////////////////////////////////////
+
+//
+// produce a new unique id
+//
+function cmNewID ()
+{
+	return _cmIDName + (++_cmIDCount);
+}
+
+//
+// return the property string for the menu item
+//
+function cmActionItem (item, prefix, isMain, idSub, orient, nodeProperties)
+{
+	var clickOpen = _cmNodeProperties.clickOpen;
+	if (nodeProperties.clickOpen)
+		clickOpen = nodeProperties.clickOpen;
+
+	// var index = _cmItemList.push (item) - 1;
+	_cmItemList[_cmItemList.length] = item;
+	var index = _cmItemList.length - 1;
+	idSub = (!idSub) ? 'null' : ('\'' + idSub + '\'');
+	orient = '\'' + orient + '\'';
+	prefix = '\'' + prefix + '\'';
+	var onClick = (clickOpen == 3) || (clickOpen == 2 && isMain);
+	var returnStr;
+	if (onClick)
+		returnStr = ' onmouseover="cmItemMouseOver (this,' + prefix + ',' + isMain + ',' + idSub + ',' + index + ')" onmousedown="cmItemMouseDownOpenSub (this,' + index + ',' + prefix + ',' + orient + ',' + idSub + ')"';
+	else
+		returnStr = ' onmouseover="cmItemMouseOverOpenSub (this,' + prefix + ',' + isMain + ',' + idSub + ',' + orient + ',' + index + ')" onmousedown="cmItemMouseDown (this,' + index + ')"';
+	return returnStr + ' onmouseout="cmItemMouseOut (this,' + nodeProperties.delay + ')" onmouseup="cmItemMouseUp (this,' + index + ')"';
+}
+
+//
+// this one is used by _cmNoClick to only take care of onmouseover and onmouseout
+// events which are associated with menu but not actions associated with menu clicking/closing
+//
+function cmNoClickItem (item, prefix, isMain, idSub, orient, nodeProperties)
+{
+	// var index = _cmItemList.push (item) - 1;
+	_cmItemList[_cmItemList.length] = item;
+	var index = _cmItemList.length - 1;
+	idSub = (!idSub) ? 'null' : ('\'' + idSub + '\'');
+	orient = '\'' + orient + '\'';
+	prefix = '\'' + prefix + '\'';
+	return ' onmouseover="cmItemMouseOver (this,' + prefix + ',' + isMain + ',' + idSub + ',' + index + ')" onmouseout="cmItemMouseOut (this,' + nodeProperties.delay + ')"';
+}
+
+function cmNoActionItem (item, prefix)
+{
+	return item[1];
+}
+
+function cmSplitItem (prefix, isMain, vertical)
+{
+	var classStr = 'cm' + prefix;
+	if (isMain)
+	{
+		classStr += 'Main';
+		if (vertical)
+			classStr += 'HSplit';
+		else
+			classStr += 'VSplit';
+	}
+	else
+		classStr += 'HSplit';
+	return eval (classStr);
+}
+
+//
+// draw the sub menu recursively
+//
+function cmDrawSubMenu (subMenu, prefix, id, orient, nodeProperties)
+{
+	var str = '<div class="' + prefix + 'SubMenu" id="' + id + '"><table summary="sub menu" cellspacing="' + nodeProperties.subSpacing + '" class="' + prefix + 'SubMenuTable">';
+	var strSub = '';
+
+	var item;
+	var idSub;
+	var hasChild;
+
+	var i;
+
+	var classStr;
+
+	for (i = 5; i < subMenu.length; ++i)
+	{
+		item = subMenu[i];
+		if (!item)
+			continue;
+
+		if (item == _cmSplit)
+			item = cmSplitItem (prefix, 0, true);
+
+		hasChild = (item.length > 5);
+		idSub = hasChild ? cmNewID () : null;
+
+		str += '<tr class="' + prefix + 'MenuItem"';
+		if (item[0] != _cmNoClick)
+			str += cmActionItem (item, prefix, 0, idSub, orient, nodeProperties);
+		else
+			str += cmNoClickItem (item, prefix, 0, idSub, orient, nodeProperties);
+		str += '>'
+
+		if (item[0] == _cmNoAction || item[0] == _cmNoClick)
+		{
+			str += cmNoActionItem (item, prefix);
+			str += '</tr>';
+			continue;
+		}
+
+		classStr = prefix + 'Menu';
+		classStr += hasChild ? 'Folder' : 'Item';
+
+		str += '<td class="' + classStr + 'Left">';
+
+		if (item[0] != null)
+			str += item[0];
+		else
+			str += hasChild ? nodeProperties.folderLeft : nodeProperties.itemLeft;
+
+		str += '</td><td class="' + classStr + 'Text">' + item[1];
+
+		str += '</td><td class="' + classStr + 'Right">';
+
+		if (hasChild)
+		{
+			str += nodeProperties.folderRight;
+			strSub += cmDrawSubMenu (item, prefix, idSub, orient, nodeProperties);
+		}
+		else
+			str += nodeProperties.itemRight;
+		str += '</td></tr>';
+	}
+
+	str += '</table></div>' + strSub;
+	return str;
+}
+
+//
+// The function that builds the menu inside the specified element id.
+//
+// @param	id	id of the element
+//		orient	orientation of the menu in [hv][ab][lr] format
+//		menu	the menu object to be drawn
+//		nodeProperties	properties for each menu node
+//
+function cmDraw (id, menu, orient, nodeProperties, prefix)
+{
+	var obj = cmGetObject (id);
+
+	if (!nodeProperties)
+		nodeProperties = _cmNodeProperties;
+	if (!prefix)
+		prefix = '';
+
+	var str = '<table summary="main menu" class="' + prefix + 'Menu" cellspacing="' + nodeProperties.mainSpacing + '">';
+	var strSub = '';
+
+	if (!orient)
+		orient = 'hbr';
+
+	var orientStr = String (orient);
+	var orientSub;
+	var vertical;
+
+	// draw the main menu items
+	if (orientStr.charAt (0) == 'h')
+	{
+		// horizontal menu
+		orientSub = 'v' + orientStr.substr (1, 2);
+		str += '<tr>';
+		vertical = false;
+	}
+	else
+	{
+		// vertical menu
+		orientSub = 'v' + orientStr.substr (1, 2);
+		vertical = true;
+	}
+
+	var i;
+	var item;
+	var idSub;
+	var hasChild;
+
+	var classStr;
+
+	for (i = 0; i < menu.length; ++i)
+	{
+		item = menu[i];
+
+		if (!item)
+			continue;
+
+		str += vertical ? '<tr' : '<td';
+		str += ' class="' + prefix + 'MainItem"';
+
+		hasChild = (item.length > 5);
+		idSub = hasChild ? cmNewID () : null;
+
+		str += cmActionItem (item, prefix, 1, idSub, orient, nodeProperties) + '>';
+
+		if (item == _cmSplit)
+			item = cmSplitItem (prefix, 1, vertical);
+
+		if (item[0] == _cmNoAction || item[0] == _cmNoClick)
+		{
+			str += cmNoActionItem (item, prefix);
+			str += vertical? '</tr>' : '</td>';
+			continue;
+		}
+
+		classStr = prefix + 'Main' + (hasChild ? 'Folder' : 'Item');
+
+		str += vertical ? '<td' : '<span';
+		str += ' class="' + classStr + 'Left">';
+
+		str += (item[0] == null) ? (hasChild ? nodeProperties.mainFolderLeft : nodeProperties.mainItemLeft)
+					 : item[0];
+		str += vertical ? '</td>' : '</span>';
+
+		str += vertical ? '<td' : '<span';
+		str += ' class="' + classStr + 'Text">';
+		str += item[1];
+
+		str += vertical ? '</td>' : '</span>';
+
+		str += vertical ? '<td' : '<span';
+		str += ' class="' + classStr + 'Right">';
+
+		str += hasChild ? nodeProperties.mainFolderRight : nodeProperties.mainItemRight;
+
+		str += vertical ? '</td>' : '</span>';
+
+		str += vertical ? '</tr>' : '</td>';
+
+		if (hasChild)
+			strSub += cmDrawSubMenu (item, prefix, idSub, orientSub, nodeProperties);
+	}
+	if (!vertical)
+		str += '</tr>';
+	str += '</table>' + strSub;
+	obj.innerHTML = str;
+	//document.write ("<xmp>" + str + "</xmp>");
+}
+
+//
+// The function builds the menu inside the specified element id.
+//
+// This function is similar to cmDraw except that menu is taken from HTML node
+// rather a javascript tree.  This feature allows links to be scanned by search
+// bots.
+//
+// This function basically converts HTML node to a javascript tree, and then calls
+// cmDraw to draw the actual menu, replacing the hidden menu tree.
+//
+// Format:
+//	<div id="menu">
+//		<ul style="visibility: hidden">
+//			<li><span>icon</span><a href="link" title="description">main menu text</a>
+//				<ul>
+//					<li><span>icon</span><a href="link" title="description">submenu item</a>
+//					</li>
+//				</ul>
+//			</li>
+//		</ul>
+//	</div>
+//
+function cmDrawFromText (id, orient, nodeProperties, prefix)
+{
+	var domMenu = cmGetObject (id);
+	var menu = null;
+	for (var currentDomItem = domMenu.firstChild; currentDomItem; currentDomItem = currentDomItem.nextSibling)
+	{
+		if (!currentDomItem.tagName || currentDomItem.tagName.toLowerCase () != 'ul')
+			continue;
+		menu = cmDrawFromTextSubMenu (currentDomItem);
+		break;
+	}
+	if (menu)
+		cmDraw (id, menu, orient, nodeProperties, prefix);
+}
+
+//
+// a recursive function that build menu tree structure
+//
+function cmDrawFromTextSubMenu (domMenu)
+{
+	var items = new Array ();
+	for (var currentDomItem = domMenu.firstChild; currentDomItem; currentDomItem = currentDomItem.nextSibling)
+	{
+		if (!currentDomItem.tagName || currentDomItem.tagName.toLowerCase () != 'li')
+			continue;
+		if (currentDomItem.firstChild == null)
+		{
+			items[items.length] = _cmSplit;
+			continue;
+		}
+		var item = new Array ();
+		var currentItem = currentDomItem.firstChild;
+		for (; currentItem; currentItem = currentItem.nextSibling)
+		{
+			// scan for span tag
+			if (!currentItem.tagName || currentItem.tagName.toLowerCase () != 'span')
+				continue;
+			if (!currentItem.firstChild)
+				item[0] = null;
+			else
+				item[0] = currentItem.innerHTML;
+			break;
+		}
+		if (!currentItem)
+			continue;
+		for (; currentItem; currentItem = currentItem.nextSibling)
+		{
+			// scan for span tag
+			if (!currentItem.tagName || currentItem.tagName.toLowerCase () != 'a')
+				continue;
+			item[1] = currentItem.innerHTML;
+			item[2] = currentItem.href;
+			item[3] = currentItem.target;
+			item[4] = currentItem.title;
+			if (item[4] == '')
+				item[4] = null;
+			break;
+		}
+
+		for (; currentItem; currentItem = currentItem.nextSibling)
+		{
+			// scan for span tag
+			if (!currentItem.tagName || currentItem.tagName.toLowerCase () != 'ul')
+				continue;
+			var subMenuItems = cmDrawFromTextSubMenu (currentItem);
+			for (i = 0; i < subMenuItems.length; ++i)
+				item[i + 5] = subMenuItems[i];
+			break;
+		}
+		items[items.length] = item;
+	}
+	return items;
+}
+
+//////////////////////////////////////////////////////////////////////
+//
+// Mouse Event Handling Functions
+//
+//////////////////////////////////////////////////////////////////////
+
+//
+// action should be taken for mouse moving in to the menu item
+//
+// Here we just do things concerning this menu item, w/o opening sub menus.
+//
+function cmItemMouseOver (obj, prefix, isMain, idSub, index)
+{
+	clearTimeout (_cmTimeOut);
+
+	if (!obj.cmPrefix)
+	{
+		obj.cmPrefix = prefix;
+		obj.cmIsMain = isMain;
+	}
+
+	var thisMenu = cmGetThisMenu (obj, prefix);
+
+	// insert obj into cmItems if cmItems doesn't have obj
+	if (!thisMenu.cmItems)
+		thisMenu.cmItems = new Array ();
+	var i;
+	for (i = 0; i < thisMenu.cmItems.length; ++i)
+	{
+		if (thisMenu.cmItems[i] == obj)
+			break;
+	}
+	if (i == thisMenu.cmItems.length)
+	{
+		//thisMenu.cmItems.push (obj);
+		thisMenu.cmItems[i] = obj;
+	}
+
+	// hide the previous submenu that is not this branch
+	if (_cmCurrentItem)
+	{
+		// occationally, we get this case when user
+		// move the mouse slowly to the border
+		if (_cmCurrentItem == obj || _cmCurrentItem == thisMenu)
+		{
+			var item = _cmItemList[index];
+			cmSetStatus (item);
+			return;
+		}
+
+		var thatPrefix = _cmCurrentItem.cmPrefix;
+		var thatMenu = cmGetThisMenu (_cmCurrentItem, thatPrefix);
+
+		if (thatMenu != thisMenu.cmParentMenu)
+		{
+			if (_cmCurrentItem.cmIsMain)
+				_cmCurrentItem.className = thatPrefix + 'MainItem';
+			else
+				_cmCurrentItem.className = thatPrefix + 'MenuItem';
+			if (thatMenu.id != idSub)
+				cmHideMenu (thatMenu, thisMenu, thatPrefix);
+		}
+	}
+
+	// okay, set the current menu to this obj
+	_cmCurrentItem = obj;
+
+	// just in case, reset all items in this menu to MenuItem
+	cmResetMenu (thisMenu, prefix);
+
+	var item = _cmItemList[index];
+	var isDefaultItem = cmIsDefaultItem (item);
+
+	if (isDefaultItem)
+	{
+		if (isMain)
+			obj.className = prefix + 'MainItemHover';
+		else
+			obj.className = prefix + 'MenuItemHover';
+	}
+
+	cmSetStatus (item);
+}
+
+//
+// action should be taken for mouse moving in to the menu item
+//
+// This function also opens sub menu
+//
+function cmItemMouseOverOpenSub (obj, prefix, isMain, idSub, orient, index)
+{
+	cmItemMouseOver (obj, prefix, isMain, idSub, index);
+
+	if (idSub)
+	{
+		var subMenu = cmGetObject (idSub);
+		cmShowSubMenu (obj, prefix, subMenu, orient);
+	}
+}
+
+//
+// action should be taken for mouse moving out of the menu item
+//
+function cmItemMouseOut (obj, delayTime)
+{
+	if (!delayTime)
+		delayTime = _cmNodeProperties.delay;
+	_cmTimeOut = window.setTimeout ('cmHideMenuTime ()', delayTime);
+	window.defaultStatus = '';
+}
+
+//
+// action should be taken for mouse button down at a menu item
+//
+function cmItemMouseDown (obj, index)
+{
+	if (cmIsDefaultItem (_cmItemList[index]))
+	{
+		if (obj.cmIsMain)
+			obj.className = obj.cmPrefix + 'MainItemActive';
+		else
+			obj.className = obj.cmPrefix + 'MenuItemActive';
+	}
+}
+
+//
+// action should be taken for mouse button down at a menu item
+// this is one also opens submenu if needed
+//
+function cmItemMouseDownOpenSub (obj, index, prefix, orient, idSub)
+{
+	cmItemMouseDown (obj, index);
+
+	if (idSub)
+	{
+		var subMenu = cmGetObject (idSub);
+		cmShowSubMenu (obj, prefix, subMenu, orient);
+	}
+}
+
+//
+// action should be taken for mouse button up at a menu item
+//
+function cmItemMouseUp (obj, index)
+{
+	var item = _cmItemList[index];
+
+	var link = null, target = '_self';
+
+	if (item.length > 2)
+		link = item[2];
+	if (item.length > 3 && item[3])
+		target = item[3];
+
+	if (link != null)
+	{
+		window.open (link, target);
+	}
+
+	var prefix = obj.cmPrefix;
+	var thisMenu = cmGetThisMenu (obj, prefix);
+
+	var hasChild = (item.length > 5);
+	if (!hasChild)
+	{
+		if (cmIsDefaultItem (item))
+		{
+			if (obj.cmIsMain)
+				obj.className = prefix + 'MainItem';
+			else
+				obj.className = prefix + 'MenuItem';
+		}
+		cmHideMenu (thisMenu, null, prefix);
+	}
+	else
+	{
+		if (cmIsDefaultItem (item))
+		{
+			if (obj.cmIsMain)
+				obj.className = prefix + 'MainItemHover';
+			else
+				obj.className = prefix + 'MenuItemHover';
+		}
+	}
+}
+
+//////////////////////////////////////////////////////////////////////
+//
+// Mouse Event Support Utility Functions
+//
+//////////////////////////////////////////////////////////////////////
+
+//
+// move submenu to the appropriate location
+//
+// @param	obj	the menu item that opens up the subMenu
+//		subMenu	the sub menu to be shown
+//		orient	the orientation of the subMenu
+//
+function cmMoveSubMenu (obj, subMenu, orient)
+{
+	var mode = String (orient);
+	var p = subMenu.offsetParent;
+	var subMenuWidth = cmGetWidth (subMenu);
+	var horiz = cmGetHorizontalAlign (obj, mode, p, subMenuWidth);
+	if (mode.charAt (0) == 'h')
+	{
+		if (mode.charAt (1) == 'b')
+			subMenu.style.top = (cmGetYAt (obj, p) + cmGetHeight (obj)) + 'px';
+		else
+			subMenu.style.top = (cmGetYAt (obj, p) - cmGetHeight (subMenu)) + 'px';
+		if (horiz == 'r')
+			subMenu.style.left = (cmGetXAt (obj, p)) + 'px';
+		else
+			subMenu.style.left = (cmGetXAt (obj, p) + cmGetWidth (obj) - subMenuWidth) + 'px';
+	}
+	else
+	{
+		if (horiz == 'r')
+			subMenu.style.left = (cmGetXAt (obj, p) + cmGetWidth (obj)) + 'px';
+		else
+			subMenu.style.left = (cmGetXAt (obj, p) - subMenuWidth) + 'px';
+		if (mode.charAt (1) == 'b')
+			subMenu.style.top = (cmGetYAt (obj, p)) + 'px';
+		else
+			subMenu.style.top = (cmGetYAt (obj, p) + cmGetHeight (obj) - cmGetHeight (subMenu)) + 'px';
+	}
+}
+
+//
+// automatically re-adjust the menu position based on available screen size.
+//
+function cmGetHorizontalAlign (obj, mode, p, subMenuWidth)
+{
+	var horiz = mode.charAt (2);
+	if (!(document.body))
+		return horiz;
+	var body = document.body;
+	var browserLeft;
+	var browserRight;
+	if (window.innerWidth)
+	{
+		// DOM window attributes
+		browserLeft = window.pageXOffset;
+		browserRight = window.innerWidth + browserLeft;
+	}
+	else if (body.clientWidth)
+	{
+		// IE attributes
+		browserLeft = body.clientLeft;
+		browserRight = body.clientWidth + browserLeft;
+	}
+	else
+		return horiz;
+	if (mode.charAt (0) == 'h')
+	{
+		if (horiz == 'r' && (cmGetXAt (obj) + subMenuWidth) > browserRight)
+			horiz = 'l';
+		if (horiz == 'l' && (cmGetXAt (obj) + cmGetWidth (obj) - subMenuWidth) < browserLeft)
+			horiz = 'r';
+		return horiz;
+	}
+	else
+	{
+		if (horiz == 'r' && (cmGetXAt (obj, p) + cmGetWidth (obj) + subMenuWidth) > browserRight)
+			horiz = 'l';
+		if (horiz == 'l' && (cmGetXAt (obj, p) - subMenuWidth) < browserLeft)
+			horiz = 'r';
+		return horiz;
+	}
+}
+
+//
+// show the subMenu w/ specified orientation
+// also move it to the correct coordinates
+//
+// @param	obj	the menu item that opens up the subMenu
+//		subMenu	the sub menu to be shown
+//		orient	the orientation of the subMenu
+//
+function cmShowSubMenu (obj, prefix, subMenu, orient)
+{
+	if (!subMenu.cmParentMenu)
+	{
+		// establish the tree w/ back edge
+		var thisMenu = cmGetThisMenu (obj, prefix);
+		subMenu.cmParentMenu = thisMenu;
+		if (!thisMenu.cmSubMenu)
+			thisMenu.cmSubMenu = new Array ();
+		//thisMenu.cmSubMenu.push (subMenu);
+		thisMenu.cmSubMenu[thisMenu.cmSubMenu.length] = subMenu;
+	}
+
+	// position the sub menu
+	cmMoveSubMenu (obj, subMenu, orient);
+	subMenu.style.visibility = 'visible';
+
+	//
+	// On IE, controls such as SELECT, OBJECT, IFRAME (before 5.5)
+	// are window based controls.  So, if the sub menu and these
+	// controls overlap, sub menu would be hidden behind them.  Thus
+	// one needs to turn the visibility of these controls off when the
+	// sub menu is showing, and turn their visibility back on
+	// when the sub menu is hiding.
+	//
+	if (document.all)	// it is IE
+	{
+		/* part of Felix Zaslavskiy's fix on hiding controls
+		   not really sure if this part is necessary, but shouldn't
+		   hurt. */
+		if (!subMenu.cmOverlap)
+			subMenu.cmOverlap = new Array ();
+/*@cc_on @*/
+/*@if (@_jscript_version >= 5.5)
+ at else @*/
+		cmHideControl ("IFRAME", subMenu);
+/*@end @*/
+		cmHideControl ("SELECT", subMenu);
+		cmHideControl ("OBJECT", subMenu);
+	}
+}
+
+//
+// reset all the menu items to class MenuItem in thisMenu
+//
+function cmResetMenu (thisMenu, prefix)
+{
+	if (thisMenu.cmItems)
+	{
+		var i;
+		var str;
+		var items = thisMenu.cmItems;
+		for (i = 0; i < items.length; ++i)
+		{
+			if (items[i].cmIsMain)
+				str = prefix + 'MainItem';
+			else
+				str = prefix + 'MenuItem';
+			if (items[i].className != str)
+				items[i].className = str;
+		}
+	}
+}
+
+//
+// called by the timer to hide the menu
+//
+function cmHideMenuTime ()
+{
+	if (_cmCurrentItem)
+	{
+		var prefix = _cmCurrentItem.cmPrefix;
+		cmHideMenu (cmGetThisMenu (_cmCurrentItem, prefix), null, prefix);
+		_cmCurrentItem = null;
+	}
+}
+
+//
+// hide thisMenu, children of thisMenu, as well as the ancestor
+// of thisMenu until currentMenu is encountered.  currentMenu
+// will not be hidden
+//
+function cmHideMenu (thisMenu, currentMenu, prefix)
+{
+	var str = prefix + 'SubMenu';
+
+	// hide the down stream menus
+	if (thisMenu.cmSubMenu)
+	{
+		var i;
+		for (i = 0; i < thisMenu.cmSubMenu.length; ++i)
+		{
+			cmHideSubMenu (thisMenu.cmSubMenu[i], prefix);
+		}
+	}
+
+	// hide the upstream menus
+	while (thisMenu && thisMenu != currentMenu)
+	{
+		cmResetMenu (thisMenu, prefix);
+		if (thisMenu.className == str)
+		{
+			thisMenu.style.visibility = 'hidden';
+			cmShowControl (thisMenu);
+		}
+		else
+			break;
+		thisMenu = cmGetThisMenu (thisMenu.cmParentMenu, prefix);
+	}
+}
+
+//
+// hide thisMenu as well as its sub menus if thisMenu is not
+// already hidden
+//
+function cmHideSubMenu (thisMenu, prefix)
+{
+	if (thisMenu.style.visibility == 'hidden')
+		return;
+	if (thisMenu.cmSubMenu)
+	{
+		var i;
+		for (i = 0; i < thisMenu.cmSubMenu.length; ++i)
+		{
+			cmHideSubMenu (thisMenu.cmSubMenu[i], prefix);
+		}
+	}
+	cmResetMenu (thisMenu, prefix);
+	thisMenu.style.visibility = 'hidden';
+	cmShowControl (thisMenu);
+}
+
+//
+// hide a control such as IFRAME
+//
+function cmHideControl (tagName, subMenu)
+{
+	var x = cmGetX (subMenu);
+	var y = cmGetY (subMenu);
+	var w = subMenu.offsetWidth;
+	var h = subMenu.offsetHeight;
+
+	var i;
+	for (i = 0; i < document.all.tags(tagName).length; ++i)
+	{
+		var obj = document.all.tags(tagName)[i];
+		if (!obj || !obj.offsetParent)
+			continue;
+
+		// check if the object and the subMenu overlap
+
+		var ox = cmGetX (obj);
+		var oy = cmGetY (obj);
+		var ow = obj.offsetWidth;
+		var oh = obj.offsetHeight;
+
+		if (ox > (x + w) || (ox + ow) < x)
+			continue;
+		if (oy > (y + h) || (oy + oh) < y)
+			continue;
+
+		// if object is already made hidden by a different
+		// submenu then we dont want to put it on overlap list of
+		// of a submenu a second time.
+		// - bug fixed by Felix Zaslavskiy
+		if(obj.style.visibility == "hidden")
+			continue;
+
+		//subMenu.cmOverlap.push (obj);
+		subMenu.cmOverlap[subMenu.cmOverlap.length] = obj;
+		obj.style.visibility = "hidden";
+	}
+}
+
+//
+// show the control hidden by the subMenu
+//
+function cmShowControl (subMenu)
+{
+	if (subMenu.cmOverlap)
+	{
+		var i;
+		for (i = 0; i < subMenu.cmOverlap.length; ++i)
+			subMenu.cmOverlap[i].style.visibility = "";
+	}
+	subMenu.cmOverlap = null;
+}
+
+//
+// returns the main menu or the submenu table where this obj (menu item)
+// is in
+//
+function cmGetThisMenu (obj, prefix)
+{
+	var str1 = prefix + 'SubMenu';
+	var str2 = prefix + 'Menu';
+	while (obj)
+	{
+		if (obj.className == str1 || obj.className == str2)
+			return obj;
+		obj = obj.parentNode;
+	}
+	return null;
+}
+
+//
+// return true if this item is handled using default handlers
+//
+function cmIsDefaultItem (item)
+{
+	if (item == _cmSplit || item[0] == _cmNoAction || item[0] == _cmNoClick)
+		return false;
+	return true;
+}
+
+//
+// returns the object baring the id
+//
+function cmGetObject (id)
+{
+	if (document.all)
+		return document.all[id];
+	return document.getElementById (id);
+}
+
+//
+// functions that obtain the width of an HTML element.
+//
+function cmGetWidth (obj)
+{
+	var width = obj.offsetWidth;
+	if (width > 0 || !cmIsTRNode (obj))
+		return width;
+	if (!obj.firstChild)
+		return 0;
+	// use TABLE's length can cause an extra pixel gap
+	//return obj.parentNode.parentNode.offsetWidth;
+
+	// use the left and right child instead
+	return obj.lastChild.offsetLeft - obj.firstChild.offsetLeft + cmGetWidth (obj.lastChild);
+}
+
+//
+// functions that obtain the height of an HTML element.
+//
+function cmGetHeight (obj)
+{
+	var height = obj.offsetHeight;
+	if (height > 0 || !cmIsTRNode (obj))
+		return height;
+	if (!obj.firstChild)
+		return 0;
+	// use the first child's height
+	return obj.firstChild.offsetHeight;
+}
+
+//
+// functions that obtain the coordinates of an HTML element
+//
+function cmGetX (obj)
+{
+	var x = 0;
+
+	do
+	{
+		x += obj.offsetLeft;
+		obj = obj.offsetParent;
+	}
+	while (obj);
+	return x;
+}
+
+function cmGetXAt (obj, elm)
+{
+	var x = 0;
+
+	while (obj && obj != elm)
+	{
+		x += obj.offsetLeft;
+		obj = obj.offsetParent;
+	}
+	if (obj == elm)
+		return x;
+	return x - cmGetX (elm);
+}
+
+function cmGetY (obj)
+{
+	var y = 0;
+	do
+	{
+		y += obj.offsetTop;
+		obj = obj.offsetParent;
+	}
+	while (obj);
+	return y;
+}
+
+function cmIsTRNode (obj)
+{
+	var tagName = obj.tagName;
+	return tagName == "TR" || tagName == "tr" || tagName == "Tr" || tagName == "tR";
+}
+
+//
+// get the Y position of the object.  In case of TR element though,
+// we attempt to adjust the value.
+//
+function cmGetYAt (obj, elm)
+{
+	var y = 0;
+
+	if (!obj.offsetHeight && cmIsTRNode (obj))
+	{
+		var firstTR = obj.parentNode.firstChild;
+		obj = obj.firstChild;
+		y -= firstTR.firstChild.offsetTop;
+	}
+
+	while (obj && obj != elm)
+	{
+		y += obj.offsetTop;
+		obj = obj.offsetParent;
+	}
+
+	if (obj == elm)
+		return y;
+	return y - cmGetY (elm);
+}
+
+//
+// extract description from the menu item and set the status text
+// @param	item	the menu item
+//
+function cmSetStatus (item)
+{
+	var descript = '';
+	if (item.length > 4)
+		descript = (item[4] != null) ? item[4] : (item[2] ? item[2] : descript);
+	else if (item.length > 2)
+		descript = (item[2] ? item[2] : descript);
+
+	window.defaultStatus = descript;
+}
+
+//
+// debug function, ignore :)
+//
+function cmGetProperties (obj)
+{
+	if (obj == undefined)
+		return 'undefined';
+	if (obj == null)
+		return 'null';
+
+	var msg = obj + ':\n';
+	var i;
+	for (i in obj)
+		msg += i + ' = ' + obj[i] + '; ';
+	return msg;
+}
+
+/* v1.4.4			1. a quick fix for a bug for _cmSplit checking.  reported by
+						Son Nguyen.
+*/
+/* v1.4.3			1. changed how _cmSplit is handled a bit so that _cmNoClick can work
+						properly.  All splits in predefined themes are changed to use
+						_cmNoClick instead of _cmNoAction.
+*/
+/* v1.4.2			1. fixed _cmNoClick mouse hoover bug.
+					2. fixed a statusbar text problem that cause text to disappear when
+						hoovering mouse within the same menu item.
+					3. changed the behavior of cmDrawFromText s.t. if the title of the
+						of a link is empty, the actual url is used as text.  To clear
+						this link information, title needs to be ' '.
+*/
+/* v1.4.1			1. fixed a problem introduced in 1.4 where re-entering a main menu
+						item which doesn't have a child can disable its hover setting.
+						Apparently I deleted an extra line of code when I was doing
+						cleaning up.  Reported by David Maliachi and a few others.
+*/
+/* JSCookMenu v1.4	1. fixed a minor td cell closure problem.  Thanks to Georg Lorenz
+					   <georg at lonux.de> for discovering that.
+					2. added clickOpen to nodeProperties.  See _cmNodeProperties for
+						description.  Basically menus can be opened on click only.
+					3. added an ability to draw menu from an html node instead of a javascript
+						tree, making this script search bot friendly (I hope?).
+*/
+/* JSCookMenu v1.31 1. fix a bug on IE with causes submenus to display at the top
+					   left corner due to doctype.  The fix was provided by
+					   Burton Strauss <Burton at ntopsupport.com>.
+*/
+/* JSCookMenu v1.3	1. automatically realign (left and right) the submenu when
+					   client space is not enough.
+					2. add _cmNoClick to get rid of menu closing behavior
+					   on the particular menu item, to make it possible for things
+					   such as search box to be inside the menu.
+*/
+/* JSCookMenu v1.25	1. fix Safari positioning issue.  The problem is that all TR elements are located
+					   at the top left corner.  Thus, need to obtain the "virtual"
+					   position of these element could be at.
+*/
+/* JSCookMenu v1.24	1. fix window based control hiding bug
+					   thanks to Felix Zaslavskiy <felix at bebinary.com> for the fix.
+*/
+/* JSCookMenu v1.23	1. correct a position bug when the container is positioned.
+					  thanks to Andre <anders at netspace.net.au> for narrowing down
+					  the problem.
+*/
+/* JSCookMenu v1.22	1. change Array.push (obj) call to Array[length] = obj.
+					   Suggestion from Dick van der Kaaden <dick at netrex.nl> to
+					   make the script compatible with IE 5.0
+					2. Changed theme files a little to add z-index: 100 for sub
+					   menus.  This change is necessary for Netscape to avoid
+					   a display problem.
+					3. some changes to the DOM structure to make this menu working
+					   on Netscape 6.0 (tested).  The main reason is that NN6 does
+					   not do absolute positioning with tables.  Therefore an extra
+					   div layer must be put around the table.
+*/
+/* JSCookMenu v1.21	1. fixed a bug that didn't add 'px' as part of coordinates.
+					   JSCookMenu should be XHTML validator friendly now.
+					2. removed unnecessary display attribute and corresponding
+					   theme entry to fix a problem that Netscape sometimes
+					   render Office theme incorrectly
+*/
+/* JSCookMenu v1.2.	1. fix the problem of showing status in Netscape
+					2. changed the handler parameters a bit to allow
+					   string literals to be passed to javascript based
+					   links
+					3. having null in target field would cause the link
+					   to be opened in the current window, but this behavior
+					   could change in the future releases
+*/
+/* JSCookMenu v1.1.		added ability to hide controls in IE to show submenus properly */
+/* JSCookMenu v1.01.	cmDraw generates XHTML code */
+/* JSCookMenu v1.0.		(c) Copyright 2002 by Heng Yuan */

Added: plog/trunk/js/jscookmenu/themeoffice/arrow.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/arrow.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/arrowdown.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/arrowdown.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/blank.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/blank.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/copy.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/copy.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/copyshadow.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/copyshadow.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/cut.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/cut.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/cutshadow.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/cutshadow.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/help.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/help.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/helpshadow.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/helpshadow.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/new.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/new.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/open.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/open.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/openshadow.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/openshadow.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/paste.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/paste.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/pasteshadow.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/pasteshadow.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/save.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/save.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/saveshadow.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/saveshadow.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/spacer.gif
===================================================================
(Binary files differ)


Property changes on: plog/trunk/js/jscookmenu/themeoffice/spacer.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: plog/trunk/js/jscookmenu/themeoffice/theme.css
===================================================================
--- plog/trunk/js/jscookmenu/themeoffice/theme.css	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/js/jscookmenu/themeoffice/theme.css	2006-01-11 10:39:10 UTC (rev 2767)
@@ -0,0 +1,291 @@
+/* ThemeOfficeMenu Style Sheet */
+
+.ThemeOfficeMenu,.ThemeOfficeSubMenuTable
+{
+	padding:	0;
+
+	white-space:	nowrap;
+	cursor:		default;
+}
+
+.ThemeOfficeSubMenu
+{
+	position:	absolute;
+	visibility:	hidden;
+
+	/*
+	   Netscape/Mozilla renders borders by increasing
+	   their z-index.  The following line is necessary
+	   to cover any borders underneath
+	*/
+	z-index:	100;
+	border:		0;
+	padding:	0;
+
+	overflow:	visible;
+	border:		1px solid #8C867B;
+
+	filter:progid:DXImageTransform.Microsoft.Shadow(color=#BDC3BD, Direction=135, Strength=4);
+}
+
+.ThemeOfficeSubMenuTable
+{
+	overflow:	visible;
+}
+
+.ThemeOfficeMainItem,.ThemeOfficeMainItemHover,.ThemeOfficeMainItemActive,
+.ThemeOfficeMenuItem,.ThemeOfficeMenuItemHover,.ThemeOfficeMenuItemActive
+{
+	border:		0;
+	cursor:		default;
+	white-space:	nowrap;
+}
+
+.ThemeOfficeMainItem
+{
+	background-color:	inherit;
+	font-weight: bold;
+}
+
+.ThemeOfficeMainItemHover,.ThemeOfficeMainItemActive
+{
+	background-color:	#C6D3EF;
+	font-weight: bold;	
+}
+
+.ThemeOfficeMenuItem
+{
+	background-color:	WHITE;
+}
+
+.ThemeOfficeMenuItemHover,.ThemeOfficeMenuItemActive
+{
+	background-color:	#C6D3EF;
+}
+
+
+/* horizontal main menu */
+
+.ThemeOfficeMainItem
+{
+	padding:	1px;
+	border:		0;
+}
+
+td.ThemeOfficeMainItemHover,td.ThemeOfficeMainItemActive
+{
+	padding:	0px;
+	border:		1px solid #3169C6;
+}
+
+.ThemeOfficeMainFolderLeft,.ThemeOfficeMainItemLeft,
+.ThemeOfficeMainFolderText,.ThemeOfficeMainItemText,
+.ThemeOfficeMainFolderRight,.ThemeOfficeMainItemRight
+{
+	background-color:	inherit;
+}
+
+/* vertical main menu sub components */
+
+td.ThemeOfficeMainFolderLeft,td.ThemeOfficeMainItemLeft
+{
+	padding-top:	0px;
+	padding-bottom:	0px;
+	padding-left:	0px;
+	padding-right:	2px;
+
+	border-top:	1px solid #3169C6;
+	border-bottom:	1px solid #3169C6;
+	border-left:	1px solid #3169C6;
+
+	background-color:	inherit;
+}
+
+td.ThemeOfficeMainFolderText,td.ThemeOfficeMainItemText
+{
+	padding-top:	0px;
+	padding-bottom:	0px;
+	padding-left:	5px;
+	padding-right:	5px;
+
+	border-top:	1px solid #3169C6;
+	border-bottom:	1px solid #3169C6;
+
+	background-color:	inherit;
+	white-space:	nowrap;
+}
+
+td.ThemeOfficeMainFolderRight,td.ThemeOfficeMainItemRight
+{
+	padding-top:	0px;
+	padding-bottom:	0px;
+	padding-left:	0px;
+	padding-right:	0px;
+
+	border-top:	1px solid #3169C6;
+	border-bottom:	1px solid #3169C6;
+	border-right:	1px solid #3169C6;
+
+	background-color:	inherit;
+}
+
+tr.ThemeOfficeMainItem td.ThemeOfficeMainFolderLeft,
+tr.ThemeOfficeMainItem td.ThemeOfficeMainItemLeft
+{
+	padding-top:	1px;
+	padding-bottom:	1px;
+	padding-left:	1px;
+	padding-right:	2px;
+
+	white-space:	nowrap;
+
+	border:		0;
+	background-color:	inherit;
+}
+
+tr.ThemeOfficeMainItem td.ThemeOfficeMainFolderText,
+tr.ThemeOfficeMainItem td.ThemeOfficeMainItemText
+{
+	padding-top:	1px;
+	padding-bottom:	1px;
+	padding-left:	5px;
+	padding-right:	5px;
+
+	border:		0;
+	background-color:	inherit;
+}
+
+tr.ThemeOfficeMainItem td.ThemeOfficeMainItemRight,
+tr.ThemeOfficeMainItem td.ThemeOfficeMainFolderRight
+{
+	padding-top:	1px;
+	padding-bottom:	1px;
+	padding-left:	0px;
+	padding-right:	1px;
+
+	border:		0;
+	background-color:	inherit;
+}
+
+/* sub menu sub components */
+
+.ThemeOfficeMenuFolderLeft,.ThemeOfficeMenuItemLeft
+{
+	padding-top:	0px;
+	padding-bottom:	0px;
+	padding-left:	1px;
+	padding-right:	3px;
+
+	border-top:	1px solid #3169C6;
+	border-bottom:	1px solid #3169C6;
+	border-left:	1px solid #3169C6;
+
+	background-color:	inherit;
+	white-space:	nowrap;
+}
+
+.ThemeOfficeMenuFolderText,.ThemeOfficeMenuItemText
+{
+	padding-top:	0px;
+	padding-bottom:	0px;
+	padding-left:	5px;
+	padding-right:	5px;
+
+	border-top:	1px solid #3169C6;
+	border-bottom:	1px solid #3169C6;
+
+	background-color:	inherit;
+	white-space:	nowrap;
+}
+
+.ThemeOfficeMenuFolderRight,.ThemeOfficeMenuItemRight
+{
+	padding-top:	0px;
+	padding-bottom:	0px;
+	padding-left:	0px;
+	padding-right:	0px;
+
+	border-top:	1px solid #3169C6;
+	border-bottom:	1px solid #3169C6;
+	border-right:	1px solid #3169C6;
+
+	background-color:	inherit;
+	white-space:	nowrap;
+}
+
+.ThemeOfficeMenuItem .ThemeOfficeMenuFolderLeft,
+.ThemeOfficeMenuItem .ThemeOfficeMenuItemLeft
+{
+	padding-top:	1px;
+	padding-bottom:	1px;
+	padding-left:	2px;
+	padding-right:	3px;
+
+	white-space:	nowrap;
+
+	border:		0;
+	background-color:	#EFEFDE;
+}
+
+.ThemeOfficeMenuItem .ThemeOfficeMenuFolderText,
+.ThemeOfficeMenuItem .ThemeOfficeMenuItemText
+{
+	padding-top:	1px;
+	padding-bottom:	1px;
+	padding-left:	5px;
+	padding-right:	5px;
+
+	border:		0;
+	background-color:	inherit;
+}
+
+.ThemeOfficeMenuItem .ThemeOfficeMenuFolderRight,
+.ThemeOfficeMenuItem .ThemeOfficeMenuItemRight
+{
+	padding-top:	1px;
+	padding-bottom:	1px;
+	padding-left:	0px;
+	padding-right:	1px;
+
+	border:		0;
+	background-color:	inherit;
+}
+
+/* menu splits */
+
+.ThemeOfficeMenuSplit
+{
+	margin:		2px;
+	height:		1px;
+	overflow:	hidden;
+	background-color:	inherit;
+	border-top:	1px solid #C6C3BD;
+}
+
+/* image shadow animation */
+
+/*
+	seq1:	image for normal
+	seq2:	image for hover and active
+
+	To use, in the icon field, input the following:
+	<img class="seq1" src="normal.gif" /><img class="seq2" src="hover.gif" />
+*/
+
+.ThemeOfficeMenuItem img.seq1
+{
+	display:	inline;
+}
+
+.ThemeOfficeMenuItemHover seq2,
+.ThemeOfficeMenuItemActive seq2
+{
+	display:	inline;
+}
+
+.ThemeOfficeMenuItem .seq2,
+.ThemeOfficeMenuItemHover .seq1,
+.ThemeOfficeMenuItemActive .seq1
+{
+	display:	none;
+}

Added: plog/trunk/js/jscookmenu/themeoffice/theme.js
===================================================================
--- plog/trunk/js/jscookmenu/themeoffice/theme.js	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/js/jscookmenu/themeoffice/theme.js	2006-01-11 10:39:10 UTC (rev 2767)
@@ -0,0 +1,58 @@
+
+// directory of where all the images are
+var cmThemeOfficeBase = 'js/jscookmenu/themeoffice/';
+
+// the follow block allows user to re-define theme base directory
+// before it is loaded.
+try
+{
+	if (myThemeOfficeBase)
+	{
+		cmThemeOfficeBase = myThemeOfficeBase;
+	}
+}
+catch (e)
+{
+}
+
+var cmThemeOffice =
+{
+  	// main menu display attributes
+  	//
+  	// Note.  When the menu bar is horizontal,
+  	// mainFolderLeft and mainFolderRight are
+  	// put in <span></span>.  When the menu
+  	// bar is vertical, they would be put in
+  	// a separate TD cell.
+
+  	// HTML code to the left of the folder item
+  	mainFolderLeft: '&nbsp;',
+  	// HTML code to the right of the folder item
+  	mainFolderRight: '&nbsp;',
+	// HTML code to the left of the regular item
+	mainItemLeft: '&nbsp;',
+	// HTML code to the right of the regular item
+	mainItemRight: '&nbsp;',
+
+	// sub menu display attributes
+
+	// 0, HTML code to the left of the folder item
+	folderLeft: '<img alt="" src="' + cmThemeOfficeBase + 'spacer.gif">',
+	// 1, HTML code to the right of the folder item
+	folderRight: '<img alt="" src="' + cmThemeOfficeBase + 'arrow.gif">',
+	// 2, HTML code to the left of the regular item
+	itemLeft: '<img alt="" src="' + cmThemeOfficeBase + 'spacer.gif">',
+	// 3, HTML code to the right of the regular item
+	itemRight: '<img alt="" src="' + cmThemeOfficeBase + 'blank.gif">',
+	// 4, cell spacing for main menu
+	mainSpacing: 0,
+	// 5, cell spacing for sub menus
+	subSpacing: 0,
+	// 6, auto dispear time for submenus in milli-seconds
+	delay: 500
+};
+
+// for horizontal menu split
+var cmThemeOfficeHSplit = [_cmNoClick, '<td class="ThemeOfficeMenuItemLeft"></td><td colspan="2"><div class="ThemeOfficeMenuSplit"></div></td>'];
+var cmThemeOfficeMainHSplit = [_cmNoClick, '<td class="ThemeOfficeMainItemLeft"></td><td colspan="2"><div class="ThemeOfficeMenuSplit"></div></td>'];
+var cmThemeOfficeMainVSplit = [_cmNoClick, '|'];

Modified: plog/trunk/js/prototype/prototype.js
===================================================================
--- plog/trunk/js/prototype/prototype.js	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/js/prototype/prototype.js	2006-01-11 10:39:10 UTC (rev 2767)
@@ -1,8 +1,8 @@
-/*  Prototype JavaScript framework, version 1.3.1
+/*  Prototype JavaScript framework, version 1.4.0
  *  (c) 2005 Sam Stephenson <sam at conio.net>
  *
  *  THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
- *  against the source tree, available from the Prototype darcs repository. 
+ *  against the source tree, available from the Prototype darcs repository.
  *
  *  Prototype is freely distributable under the terms of an MIT-style license.
  *
@@ -11,13 +11,16 @@
 /*--------------------------------------------------------------------------*/
 
 var Prototype = {
-  Version: '1.3.1',
-  emptyFunction: function() {}
+  Version: '1.4.0',
+  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+
+  emptyFunction: function() {},
+  K: function(x) {return x}
 }
 
 var Class = {
   create: function() {
-    return function() { 
+    return function() {
       this.initialize.apply(this, arguments);
     }
   }
@@ -32,30 +35,48 @@
   return destination;
 }
 
-Object.prototype.extend = function(object) {
-  return Object.extend.apply(this, [this, object]);
+Object.inspect = function(object) {
+  try {
+    if (object == undefined) return 'undefined';
+    if (object == null) return 'null';
+    return object.inspect ? object.inspect() : object.toString();
+  } catch (e) {
+    if (e instanceof RangeError) return '...';
+    throw e;
+  }
 }
 
-Function.prototype.bind = function(object) {
-  var __method = this;
+Function.prototype.bind = function() {
+  var __method = this, args = $A(arguments), object = args.shift();
   return function() {
-    __method.apply(object, arguments);
+    return __method.apply(object, args.concat($A(arguments)));
   }
 }
 
 Function.prototype.bindAsEventListener = function(object) {
   var __method = this;
   return function(event) {
-    __method.call(object, event || window.event);
+    return __method.call(object, event || window.event);
   }
 }
 
-Number.prototype.toColorPart = function() {
-  var digits = this.toString(16);
-  if (this < 16) return '0' + digits;
-  return digits;
-}
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    var digits = this.toString(16);
+    if (this < 16) return '0' + digits;
+    return digits;
+  },
 
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  }
+});
+
 var Try = {
   these: function() {
     var returnValue;
@@ -90,10 +111,10 @@
 
   onTimerEvent: function() {
     if (!this.currentlyExecuting) {
-      try { 
+      try {
         this.currentlyExecuting = true;
-        this.callback(); 
-      } finally { 
+        this.callback();
+      } finally {
         this.currentlyExecuting = false;
       }
     }
@@ -110,7 +131,7 @@
     if (typeof element == 'string')
       element = document.getElementById(element);
 
-    if (arguments.length == 1) 
+    if (arguments.length == 1)
       return element;
 
     elements.push(element);
@@ -118,38 +139,25 @@
 
   return elements;
 }
+Object.extend(String.prototype, {
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
 
-if (!Array.prototype.push) {
-  Array.prototype.push = function() {
-		var startLength = this.length;
-		for (var i = 0; i < arguments.length; i++)
-      this[startLength + i] = arguments[i];
-	  return this.length;
-  }
-}
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
 
-if (!Function.prototype.apply) {
-  // Based on code from http://www.youngpup.net/
-  Function.prototype.apply = function(object, parameters) {
-    var parameterStrings = new Array();
-    if (!object)     object = window;
-    if (!parameters) parameters = new Array();
-    
-    for (var i = 0; i < parameters.length; i++)
-      parameterStrings[i] = 'parameters[' + i + ']';
-    
-    object.__apply__ = this;
-    var result = eval('object.__apply__(' + 
-      parameterStrings.join(', ') + ')');
-    object.__apply__ = null;
-    
-    return result;
-  }
-}
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
 
-String.prototype.extend({
-  stripTags: function() {
-    return this.replace(/<\/?[^>]+>/gi, '');
+  evalScripts: function() {
+    return this.extractScripts().map(eval);
   },
 
   escapeHTML: function() {
@@ -162,10 +170,382 @@
   unescapeHTML: function() {
     var div = document.createElement('div');
     div.innerHTML = this.stripTags();
-    return div.childNodes[0].nodeValue;
+    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+  },
+
+  toQueryParams: function() {
+    var pairs = this.match(/^\??(.*)$/)[1].split('&');
+    return pairs.inject({}, function(params, pairString) {
+      var pair = pairString.split('=');
+      params[pair[0]] = pair[1];
+      return params;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  camelize: function() {
+    var oStringList = this.split('-');
+    if (oStringList.length == 1) return oStringList[0];
+
+    var camelizedString = this.indexOf('-') == 0
+      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+      : oStringList[0];
+
+    for (var i = 1, len = oStringList.length; i < len; i++) {
+      var s = oStringList[i];
+      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+    }
+
+    return camelizedString;
+  },
+
+  inspect: function() {
+    return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
   }
 });
 
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var $break    = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+  each: function(iterator) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        try {
+          iterator(value, index++);
+        } catch (e) {
+          if (e != $continue) throw e;
+        }
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+  },
+
+  all: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!(iterator || Prototype.K)(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      if (result = !!(iterator || Prototype.K)(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  detect: function (iterator) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(pattern, iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      var stringValue = value.toString();
+      if (stringValue.match(pattern))
+        results.push((iterator || Prototype.K)(value, index));
+    })
+    return results;
+  },
+
+  include: function(object) {
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inject: function(memo, iterator) {
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.collect(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (value >= (result || value))
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (value <= (result || value))
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator) {
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      ((iterator || Prototype.K)(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator) {
+    return this.collect(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.collect(Prototype.K);
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (typeof args.last() == 'function')
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      iterator(value = collections.pluck(index));
+      return value;
+    });
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+}
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0; i < iterable.length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0; i < this.length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != undefined || value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(value.constructor == Array ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  indexOf: function(object) {
+    for (var i = 0; i < this.length; i++)
+      if (this[i] == object) return i;
+    return -1;
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  shift: function() {
+    var result = this[0];
+    for (var i = 0; i < this.length - 1; i++)
+      this[i] = this[i + 1];
+    this.length--;
+    return result;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  }
+});
+var Hash = {
+  _each: function(iterator) {
+    for (key in this) {
+      var value = this[key];
+      if (typeof value == 'function') continue;
+
+      var pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  },
+
+  keys: function() {
+    return this.pluck('key');
+  },
+
+  values: function() {
+    return this.pluck('value');
+  },
+
+  merge: function(hash) {
+    return $H(hash).inject($H(this), function(mergedHash, pair) {
+      mergedHash[pair.key] = pair.value;
+      return mergedHash;
+    });
+  },
+
+  toQueryString: function() {
+    return this.map(function(pair) {
+      return pair.map(encodeURIComponent).join('=');
+    }).join('&');
+  },
+
+  inspect: function() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  }
+}
+
+function $H(object) {
+  var hash = Object.extend({}, object || {});
+  Object.extend(hash, Enumerable);
+  Object.extend(hash, Hash);
+  return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    do {
+      iterator(value);
+      value = value.succ();
+    } while (this.include(value));
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
 var Ajax = {
   getTransport: function() {
     return Try.these(
@@ -173,9 +553,50 @@
       function() {return new ActiveXObject('Microsoft.XMLHTTP')},
       function() {return new XMLHttpRequest()}
     ) || false;
-  }
+  },
+
+  activeRequestCount: 0
 }
 
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responderToAdd) {
+    if (!this.include(responderToAdd))
+      this.responders.push(responderToAdd);
+  },
+
+  unregister: function(responderToRemove) {
+    this.responders = this.responders.without(responderToRemove);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (responder[callback] && typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate: function() {
+    Ajax.activeRequestCount++;
+  },
+
+  onComplete: function() {
+    Ajax.activeRequestCount--;
+  }
+});
+
 Ajax.Base = function() {};
 Ajax.Base.prototype = {
   setOptions: function(options) {
@@ -183,12 +604,13 @@
       method:       'post',
       asynchronous: true,
       parameters:   ''
-    }.extend(options || {});
+    }
+    Object.extend(this.options, options || {});
   },
 
   responseIsSuccess: function() {
     return this.transport.status == undefined
-        || this.transport.status == 0 
+        || this.transport.status == 0
         || (this.transport.status >= 200 && this.transport.status < 300);
   },
 
@@ -198,10 +620,10 @@
 }
 
 Ajax.Request = Class.create();
-Ajax.Request.Events = 
+Ajax.Request.Events =
   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
 
-Ajax.Request.prototype = (new Ajax.Base()).extend({
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
   initialize: function(url, options) {
     this.transport = Ajax.getTransport();
     this.setOptions(options);
@@ -213,10 +635,13 @@
     if (parameters.length > 0) parameters += '&_=';
 
     try {
-      if (this.options.method == 'get')
-        url += '?' + parameters;
+      this.url = url;
+      if (this.options.method == 'get' && parameters.length > 0)
+        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
 
-      this.transport.open(this.options.method, url,
+      Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.options.method, this.url,
         this.options.asynchronous);
 
       if (this.options.asynchronous) {
@@ -230,21 +655,22 @@
       this.transport.send(this.options.method == 'post' ? body : null);
 
     } catch (e) {
+      this.dispatchException(e);
     }
   },
 
   setRequestHeaders: function() {
-    var requestHeaders = 
+    var requestHeaders =
       ['X-Requested-With', 'XMLHttpRequest',
        'X-Prototype-Version', Prototype.Version];
 
     if (this.options.method == 'post') {
-      requestHeaders.push('Content-type', 
+      requestHeaders.push('Content-type',
         'application/x-www-form-urlencoded');
 
       /* Force "Connection: close" for Mozilla browsers to work around
        * a bug where XMLHttpReqeuest sends an incorrect Content-length
-       * header. See Mozilla Bugzilla #246651. 
+       * header. See Mozilla Bugzilla #246651.
        */
       if (this.transport.overrideMimeType)
         requestHeaders.push('Connection', 'close');
@@ -263,26 +689,64 @@
       this.respondToReadyState(this.transport.readyState);
   },
 
+  header: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) {}
+  },
+
+  evalJSON: function() {
+    try {
+      return eval(this.header('X-JSON'));
+    } catch (e) {}
+  },
+
+  evalResponse: function() {
+    try {
+      return eval(this.transport.responseText);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
   respondToReadyState: function(readyState) {
     var event = Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
 
-    if (event == 'Complete')
-      (this.options['on' + this.transport.status]
-       || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
-       || Prototype.emptyFunction)(this.transport);
+    if (event == 'Complete') {
+      try {
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
 
-    (this.options['on' + event] || Prototype.emptyFunction)(this.transport);
+      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+        this.evalResponse();
+    }
 
+    try {
+      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + event, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
     /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
     if (event == 'Complete')
       this.transport.onreadystatechange = Prototype.emptyFunction;
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
   }
 });
 
 Ajax.Updater = Class.create();
-Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
 
-Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
   initialize: function(container, url, options) {
     this.containers = {
       success: container.success ? $(container.success) : $(container),
@@ -294,9 +758,9 @@
     this.setOptions(options);
 
     var onComplete = this.options.onComplete || Prototype.emptyFunction;
-    this.options.onComplete = (function() {
+    this.options.onComplete = (function(transport, object) {
       this.updateContent();
-      onComplete(this.transport);
+      onComplete(transport, object);
     }).bind(this);
 
     this.request(url);
@@ -305,43 +769,34 @@
   updateContent: function() {
     var receiver = this.responseIsSuccess() ?
       this.containers.success : this.containers.failure;
+    var response = this.transport.responseText;
 
-    var match    = new RegExp(Ajax.Updater.ScriptFragment, 'img');
-    var response = this.transport.responseText.replace(match, '');
-    var scripts  = this.transport.responseText.match(match);
+    if (!this.options.evalScripts)
+      response = response.stripScripts();
 
     if (receiver) {
       if (this.options.insertion) {
         new this.options.insertion(receiver, response);
       } else {
-        receiver.innerHTML = response;
+        Element.update(receiver, response);
       }
     }
 
     if (this.responseIsSuccess()) {
       if (this.onComplete)
-        setTimeout((function() {this.onComplete(
-          this.transport)}).bind(this), 10);
+        setTimeout(this.onComplete.bind(this), 10);
     }
-
-    if (this.options.evalScripts && scripts) {
-      match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
-      setTimeout((function() {
-        for (var i = 0; i < scripts.length; i++)
-          eval(scripts[i].match(match)[1]);
-      }).bind(this), 10);
-    }
   }
 });
 
 Ajax.PeriodicalUpdater = Class.create();
-Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
   initialize: function(container, url, options) {
     this.setOptions(options);
     this.onComplete = this.options.onComplete;
 
     this.frequency = (this.options.frequency || 2);
-    this.decay = 1;
+    this.decay = (this.options.decay || 1);
 
     this.updater = {};
     this.container = container;
@@ -358,17 +813,17 @@
   stop: function() {
     this.updater.onComplete = undefined;
     clearTimeout(this.timer);
-    (this.onComplete || Ajax.emptyFunction).apply(this, arguments);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
   },
 
   updateComplete: function(request) {
     if (this.options.decay) {
-      this.decay = (request.responseText == this.lastText ? 
+      this.decay = (request.responseText == this.lastText ?
         this.decay * this.options.decay : 1);
 
       this.lastText = request.responseText;
     }
-    this.timer = setTimeout(this.onTimerEvent.bind(this), 
+    this.timer = setTimeout(this.onTimerEvent.bind(this),
       this.decay * this.frequency * 1000);
   },
 
@@ -376,23 +831,13 @@
     this.updater = new Ajax.Updater(this.container, this.url, this.options);
   }
 });
-
-document.getElementsByClassName = function(className) {
-  var children = document.getElementsByTagName('*') || document.all;
-  var elements = new Array();
-  
-  for (var i = 0; i < children.length; i++) {
-    var child = children[i];
-    var classNames = child.className.split(' ');
-    for (var j = 0; j < classNames.length; j++) {
-      if (classNames[j] == className) {
-        elements.push(child);
-        break;
-      }
-    }
-  }
-  
-  return elements;
+document.getElementsByClassName = function(className, parentElement) {
+  var children = ($(parentElement) || document.body).getElementsByTagName('*');
+  return $A(children).inject([], function(elements, child) {
+    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      elements.push(child);
+    return elements;
+  });
 }
 
 /*--------------------------------------------------------------------------*/
@@ -402,11 +847,14 @@
 }
 
 Object.extend(Element, {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
   toggle: function() {
     for (var i = 0; i < arguments.length; i++) {
       var element = $(arguments[i]);
-      element.style.display = 
-        (element.style.display == 'none' ? '' : 'none');
+      Element[Element.visible(element) ? 'hide' : 'show'](element);
     }
   },
 
@@ -428,54 +876,142 @@
     element = $(element);
     element.parentNode.removeChild(element);
   },
-   
+
+  update: function(element, html) {
+    $(element).innerHTML = html.stripScripts();
+    setTimeout(function() {html.evalScripts()}, 10);
+  },
+
   getHeight: function(element) {
     element = $(element);
-    return element.offsetHeight; 
+    return element.offsetHeight;
   },
 
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
   hasClassName: function(element, className) {
-    element = $(element);
-    if (!element)
-      return;
-    var a = element.className.split(' ');
-    for (var i = 0; i < a.length; i++) {
-      if (a[i] == className)
-        return true;
-    }
-    return false;
+    if (!(element = $(element))) return;
+    return Element.classNames(element).include(className);
   },
 
   addClassName: function(element, className) {
-    element = $(element);
-    Element.removeClassName(element, className);
-    element.className += ' ' + className;
+    if (!(element = $(element))) return;
+    return Element.classNames(element).add(className);
   },
 
   removeClassName: function(element, className) {
-    element = $(element);
-    if (!element)
-      return;
-    var newClassName = '';
-    var a = element.className.split(' ');
-    for (var i = 0; i < a.length; i++) {
-      if (a[i] != className) {
-        if (i > 0)
-          newClassName += ' ';
-        newClassName += a[i];
-      }
-    }
-    element.className = newClassName;
+    if (!(element = $(element))) return;
+    return Element.classNames(element).remove(className);
   },
-  
+
   // removes whitespace-only text node children
   cleanWhitespace: function(element) {
-    var element = $(element);
+    element = $(element);
     for (var i = 0; i < element.childNodes.length; i++) {
       var node = element.childNodes[i];
-      if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) 
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
         Element.remove(node);
     }
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.match(/^\s*$/);
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var x = element.x ? element.x : element.offsetLeft,
+        y = element.y ? element.y : element.offsetTop;
+    window.scrollTo(x, y);
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    var value = element.style[style.camelize()];
+    if (!value) {
+      if (document.defaultView && document.defaultView.getComputedStyle) {
+        var css = document.defaultView.getComputedStyle(element, null);
+        value = css ? css.getPropertyValue(style) : null;
+      } else if (element.currentStyle) {
+        value = element.currentStyle[style.camelize()];
+      }
+    }
+
+    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+    return value == 'auto' ? null : value;
+  },
+
+  setStyle: function(element, style) {
+    element = $(element);
+    for (name in style)
+      element.style[name.camelize()] = style[name];
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    if (Element.getStyle(element, 'display') != 'none')
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = '';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = 'none';
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return;
+    element._overflow = element.style.overflow;
+    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+      element.style.overflow = 'hidden';
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return;
+    element.style.overflow = element._overflow;
+    element._overflow = undefined;
   }
 });
 
@@ -491,68 +1027,127 @@
 Abstract.Insertion.prototype = {
   initialize: function(element, content) {
     this.element = $(element);
-    this.content = content;
-    
+    this.content = content.stripScripts();
+
     if (this.adjacency && this.element.insertAdjacentHTML) {
-      this.element.insertAdjacentHTML(this.adjacency, this.content);
+      try {
+        this.element.insertAdjacentHTML(this.adjacency, this.content);
+      } catch (e) {
+        if (this.element.tagName.toLowerCase() == 'tbody') {
+          this.insertContent(this.contentFromAnonymousTable());
+        } else {
+          throw e;
+        }
+      }
     } else {
       this.range = this.element.ownerDocument.createRange();
       if (this.initializeRange) this.initializeRange();
-      this.fragment = this.range.createContextualFragment(this.content);
-      this.insertContent();
+      this.insertContent([this.range.createContextualFragment(this.content)]);
     }
+
+    setTimeout(function() {content.evalScripts()}, 10);
+  },
+
+  contentFromAnonymousTable: function() {
+    var div = document.createElement('div');
+    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+    return $A(div.childNodes[0].childNodes[0].childNodes);
   }
 }
 
 var Insertion = new Object();
 
 Insertion.Before = Class.create();
-Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
   initializeRange: function() {
     this.range.setStartBefore(this.element);
   },
-  
-  insertContent: function() {
-    this.element.parentNode.insertBefore(this.fragment, this.element);
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment, this.element);
+    }).bind(this));
   }
 });
 
 Insertion.Top = Class.create();
-Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
   initializeRange: function() {
     this.range.selectNodeContents(this.element);
     this.range.collapse(true);
   },
-  
-  insertContent: function() {  
-    this.element.insertBefore(this.fragment, this.element.firstChild);
+
+  insertContent: function(fragments) {
+    fragments.reverse(false).each((function(fragment) {
+      this.element.insertBefore(fragment, this.element.firstChild);
+    }).bind(this));
   }
 });
 
 Insertion.Bottom = Class.create();
-Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
   initializeRange: function() {
     this.range.selectNodeContents(this.element);
     this.range.collapse(this.element);
   },
-  
-  insertContent: function() {
-    this.element.appendChild(this.fragment);
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.appendChild(fragment);
+    }).bind(this));
   }
 });
 
 Insertion.After = Class.create();
-Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
   initializeRange: function() {
     this.range.setStartAfter(this.element);
   },
-  
-  insertContent: function() {
-    this.element.parentNode.insertBefore(this.fragment, 
-      this.element.nextSibling);
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment,
+        this.element.nextSibling);
+    }).bind(this));
   }
 });
 
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set(this.toArray().concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set(this.select(function(className) {
+      return className != classNameToRemove;
+    }).join(' '));
+  },
+
+  toString: function() {
+    return this.toArray().join(' ');
+  }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
 var Field = {
   clear: function() {
     for (var i = 0; i < arguments.length; i++)
@@ -562,20 +1157,22 @@
   focus: function(element) {
     $(element).focus();
   },
-  
+
   present: function() {
     for (var i = 0; i < arguments.length; i++)
       if ($(arguments[i]).value == '') return false;
     return true;
   },
-  
+
   select: function(element) {
     $(element).select();
   },
-   
+
   activate: function(element) {
-    $(element).focus();
-    $(element).select();
+    element = $(element);
+    element.focus();
+    if (element.select)
+      element.select();
   }
 }
 
@@ -585,18 +1182,18 @@
   serialize: function(form) {
     var elements = Form.getElements($(form));
     var queryComponents = new Array();
-    
+
     for (var i = 0; i < elements.length; i++) {
       var queryComponent = Form.Element.serialize(elements[i]);
       if (queryComponent)
         queryComponents.push(queryComponent);
     }
-    
+
     return queryComponents.join('&');
   },
-  
+
   getElements: function(form) {
-    var form = $(form);
+    form = $(form);
     var elements = new Array();
 
     for (tagName in Form.Element.Serializers) {
@@ -606,19 +1203,19 @@
     }
     return elements;
   },
-  
+
   getInputs: function(form, typeName, name) {
-    var form = $(form);
+    form = $(form);
     var inputs = form.getElementsByTagName('input');
-    
+
     if (!typeName && !name)
       return inputs;
-      
+
     var matchingInputs = new Array();
     for (var i = 0; i < inputs.length; i++) {
       var input = inputs[i];
       if ((typeName && input.type != typeName) ||
-          (name && input.name != name)) 
+          (name && input.name != name))
         continue;
       matchingInputs.push(input);
     }
@@ -643,16 +1240,15 @@
     }
   },
 
+  findFirstElement: function(form) {
+    return Form.getElements(form).find(function(element) {
+      return element.type != 'hidden' && !element.disabled &&
+        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
   focusFirstElement: function(form) {
-    var form = $(form);
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      if (element.type != 'hidden' && !element.disabled) {
-        Field.activate(element);
-        break;
-      }
-    }
+    Field.activate(Form.findFirstElement(form));
   },
 
   reset: function(form) {
@@ -662,21 +1258,29 @@
 
 Form.Element = {
   serialize: function(element) {
-    var element = $(element);
+    element = $(element);
     var method = element.tagName.toLowerCase();
     var parameter = Form.Element.Serializers[method](element);
-    
-    if (parameter)
-      return encodeURIComponent(parameter[0]) + '=' + 
-        encodeURIComponent(parameter[1]);                   
+
+    if (parameter) {
+      var key = encodeURIComponent(parameter[0]);
+      if (key.length == 0) return;
+
+      if (parameter[1].constructor != Array)
+        parameter[1] = [parameter[1]];
+
+      return parameter[1].map(function(value) {
+        return key + '=' + encodeURIComponent(value);
+      }).join('&');
+    }
   },
-  
+
   getValue: function(element) {
-    var element = $(element);
+    element = $(element);
     var method = element.tagName.toLowerCase();
     var parameter = Form.Element.Serializers[method](element);
-    
-    if (parameter) 
+
+    if (parameter)
       return parameter[1];
   }
 }
@@ -689,7 +1293,7 @@
       case 'password':
       case 'text':
         return Form.Element.Serializers.textarea(element);
-      case 'checkbox':  
+      case 'checkbox':
       case 'radio':
         return Form.Element.Serializers.inputSelector(element);
     }
@@ -706,17 +1310,30 @@
   },
 
   select: function(element) {
-    var value = '';
-    if (element.type == 'select-one') {
-      var index = element.selectedIndex;
-      if (index >= 0)
-        value = element.options[index].value || element.options[index].text;
-    } else {
-      value = new Array();
-      for (var i = 0; i < element.length; i++) {
-        var opt = element.options[i];
-        if (opt.selected)
-          value.push(opt.value || opt.text);
+    return Form.Element.Serializers[element.type == 'select-one' ?
+      'selectOne' : 'selectMany'](element);
+  },
+
+  selectOne: function(element) {
+    var value = '', opt, index = element.selectedIndex;
+    if (index >= 0) {
+      opt = element.options[index];
+      value = opt.value;
+      if (!value && !('value' in opt))
+        value = opt.text;
+    }
+    return [element.name, value];
+  },
+
+  selectMany: function(element) {
+    var value = new Array();
+    for (var i = 0; i < element.length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) {
+        var optValue = opt.value;
+        if (!optValue && !('value' in opt))
+          optValue = opt.text;
+        value.push(optValue);
       }
     }
     return [element.name, value];
@@ -735,15 +1352,15 @@
     this.frequency = frequency;
     this.element   = $(element);
     this.callback  = callback;
-    
+
     this.lastValue = this.getValue();
     this.registerCallback();
   },
-  
+
   registerCallback: function() {
     setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
   },
-  
+
   onTimerEvent: function() {
     var value = this.getValue();
     if (this.lastValue != value) {
@@ -754,14 +1371,14 @@
 }
 
 Form.Element.Observer = Class.create();
-Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
   getValue: function() {
     return Form.Element.getValue(this.element);
   }
 });
 
 Form.Observer = Class.create();
-Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
   getValue: function() {
     return Form.serialize(this.element);
   }
@@ -774,14 +1391,14 @@
   initialize: function(element, callback) {
     this.element  = $(element);
     this.callback = callback;
-    
+
     this.lastValue = this.getValue();
     if (this.element.tagName.toLowerCase() == 'form')
       this.registerFormCallbacks();
     else
       this.registerCallback(this.element);
   },
-  
+
   onElementEvent: function() {
     var value = this.getValue();
     if (this.lastValue != value) {
@@ -789,57 +1406,45 @@
       this.lastValue = value;
     }
   },
-  
+
   registerFormCallbacks: function() {
     var elements = Form.getElements(this.element);
     for (var i = 0; i < elements.length; i++)
       this.registerCallback(elements[i]);
   },
-  
+
   registerCallback: function(element) {
     if (element.type) {
       switch (element.type.toLowerCase()) {
-        case 'checkbox':  
+        case 'checkbox':
         case 'radio':
-          element.target = this;
-          element.prev_onclick = element.onclick || Prototype.emptyFunction;
-          element.onclick = function() {
-            this.prev_onclick(); 
-            this.target.onElementEvent();
-          }
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
           break;
         case 'password':
         case 'text':
         case 'textarea':
         case 'select-one':
         case 'select-multiple':
-          element.target = this;
-          element.prev_onchange = element.onchange || Prototype.emptyFunction;
-          element.onchange = function() {
-            this.prev_onchange(); 
-            this.target.onElementEvent();
-          }
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
           break;
       }
-    }    
+    }
   }
 }
 
 Form.Element.EventObserver = Class.create();
-Form.Element.EventObserver.prototype = (new Abstract.EventObserver()).extend({
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
   getValue: function() {
     return Form.Element.getValue(this.element);
   }
 });
 
 Form.EventObserver = Class.create();
-Form.EventObserver.prototype = (new Abstract.EventObserver()).extend({
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
   getValue: function() {
     return Form.serialize(this.element);
   }
 });
-
-
 if (!window.Event) {
   var Event = new Object();
 }
@@ -865,21 +1470,22 @@
   },
 
   pointerX: function(event) {
-    return event.pageX || (event.clientX + 
+    return event.pageX || (event.clientX +
       (document.documentElement.scrollLeft || document.body.scrollLeft));
   },
 
   pointerY: function(event) {
-    return event.pageY || (event.clientY + 
+    return event.pageY || (event.clientY +
       (document.documentElement.scrollTop || document.body.scrollTop));
   },
 
   stop: function(event) {
-    if (event.preventDefault) { 
-      event.preventDefault(); 
-      event.stopPropagation(); 
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
     } else {
       event.returnValue = false;
+      event.cancelBubble = true;
     }
   },
 
@@ -894,7 +1500,7 @@
   },
 
   observers: false,
-  
+
   _observeAndCache: function(element, name, observer, useCapture) {
     if (!this.observers) this.observers = [];
     if (element.addEventListener) {
@@ -905,7 +1511,7 @@
       element.attachEvent('on' + name, observer);
     }
   },
-  
+
   unloadCache: function() {
     if (!Event.observers) return;
     for (var i = 0; i < Event.observers.length; i++) {
@@ -918,24 +1524,24 @@
   observe: function(element, name, observer, useCapture) {
     var element = $(element);
     useCapture = useCapture || false;
-    
+
     if (name == 'keypress' &&
-        ((navigator.appVersion.indexOf('AppleWebKit') > 0) 
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
         || element.attachEvent))
       name = 'keydown';
-    
+
     this._observeAndCache(element, name, observer, useCapture);
   },
 
   stopObserving: function(element, name, observer, useCapture) {
     var element = $(element);
     useCapture = useCapture || false;
-    
+
     if (name == 'keypress' &&
-        ((navigator.appVersion.indexOf('AppleWebKit') > 0) 
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
         || element.detachEvent))
       name = 'keydown';
-    
+
     if (element.removeEventListener) {
       element.removeEventListener(name, observer, useCapture);
     } else if (element.detachEvent) {
@@ -946,24 +1552,22 @@
 
 /* prevent memory leaks in IE */
 Event.observe(window, 'unload', Event.unloadCache, false);
-
 var Position = {
-
   // set to true if needed, warning: firefox performance problems
   // NOT neeeded for page scrolling, only if draggable contained in
   // scrollable elements
-  includeScrollOffsets: false, 
+  includeScrollOffsets: false,
 
   // must be called before calling withinIncludingScrolloffset, every time the
   // page is scrolled
   prepare: function() {
-    this.deltaX =  window.pageXOffset 
-                || document.documentElement.scrollLeft 
-                || document.body.scrollLeft 
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
                 || 0;
-    this.deltaY =  window.pageYOffset 
-                || document.documentElement.scrollTop 
-                || document.body.scrollTop 
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
                 || 0;
   },
 
@@ -971,7 +1575,7 @@
     var valueT = 0, valueL = 0;
     do {
       valueT += element.scrollTop  || 0;
-      valueL += element.scrollLeft || 0; 
+      valueL += element.scrollLeft || 0;
       element = element.parentNode;
     } while (element);
     return [valueL, valueT];
@@ -987,6 +1591,31 @@
     return [valueL, valueT];
   },
 
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  offsetParent: function(element) {
+    if (element.offsetParent) return element.offsetParent;
+    if (element == document.body) return element;
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return element;
+
+    return document.body;
+  },
+
   // caches x/y coordinate pair to use with overlap
   within: function(element, x, y) {
     if (this.includeScrollOffsets)
@@ -997,7 +1626,7 @@
 
     return (y >= this.offset[1] &&
             y <  this.offset[1] + element.offsetHeight &&
-            x >= this.offset[0] && 
+            x >= this.offset[0] &&
             x <  this.offset[0] + element.offsetWidth);
   },
 
@@ -1010,18 +1639,18 @@
 
     return (this.ycomp >= this.offset[1] &&
             this.ycomp <  this.offset[1] + element.offsetHeight &&
-            this.xcomp >= this.offset[0] && 
+            this.xcomp >= this.offset[0] &&
             this.xcomp <  this.offset[0] + element.offsetWidth);
   },
 
   // within must be called directly before
-  overlap: function(mode, element) {  
-    if (!mode) return 0;  
-    if (mode == 'vertical') 
-      return ((this.offset[1] + element.offsetHeight) - this.ycomp) / 
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
         element.offsetHeight;
     if (mode == 'horizontal')
-      return ((this.offset[0] + element.offsetWidth) - this.xcomp) / 
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
         element.offsetWidth;
   },
 
@@ -1034,5 +1663,123 @@
     target.style.left   = offsets[0] + 'px';
     target.style.width  = source.offsetWidth + 'px';
     target.style.height = source.offsetHeight + 'px';
+  },
+
+  page: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent==document.body)
+        if (Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      valueT -= element.scrollTop  || 0;
+      valueL -= element.scrollLeft || 0;
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+  },
+
+  clone: function(source, target) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || {})
+
+    // find page position of source
+    source = $(source);
+    var p = Position.page(source);
+
+    // find coordinate system to use
+    target = $(target);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(target,'position') == 'absolute') {
+      parent = Position.offsetParent(target);
+      delta = Position.page(parent);
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
+    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.style.position == 'absolute') return;
+    Position.prepare();
+
+    var offsets = Position.positionedOffset(element);
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';;
+    element.style.left   = left + 'px';;
+    element.style.width  = width + 'px';;
+    element.style.height = height + 'px';;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.style.position == 'relative') return;
+    Position.prepare();
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
   }
 }
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned.  For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+  Position.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return [valueL, valueT];
+  }
+}
\ No newline at end of file

Added: plog/trunk/js/rico/rico.js
===================================================================
--- plog/trunk/js/rico/rico.js	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/js/rico/rico.js	2006-01-11 10:39:10 UTC (rev 2767)
@@ -0,0 +1,2933 @@
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+  *  file except in compliance with the License. You may obtain a copy of the License at
+  *
+  *         http://www.apache.org/licenses/LICENSE-2.0
+  *
+  *  Unless required by applicable law or agreed to in writing, software distributed under the
+  *  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+  *  either express or implied. See the License for the specific language governing permissions
+  *  and limitations under the License.
+  **/
+
+
+//-------------------- rico.js
+var Rico = {
+  Version: '1.1.0',
+  prototypeVersion: parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1])
+}
+
+if((typeof Prototype=='undefined') || Rico.prototypeVersion < 1.3)
+      throw("Rico requires the Prototype JavaScript framework >= 1.3");
+
+Rico.ArrayExtensions = new Array();
+
+if (Object.prototype.extend) {
+   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
+}else{
+  Object.prototype.extend = function(object) {
+    return Object.extend.apply(this, [this, object]);
+  }
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
+}
+
+if (Array.prototype.push) {
+   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push;
+}
+
+if (!Array.prototype.remove) {
+   Array.prototype.remove = function(dx) {
+      if( isNaN(dx) || dx > this.length )
+         return false;
+      for( var i=0,n=0; i<this.length; i++ )
+         if( i != dx )
+            this[n++]=this[i];
+      this.length-=1;
+   };
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.remove;
+}
+
+if (!Array.prototype.removeItem) {
+   Array.prototype.removeItem = function(item) {
+      for ( var i = 0 ; i < this.length ; i++ )
+         if ( this[i] == item ) {
+            this.remove(i);
+            break;
+         }
+   };
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.removeItem;
+}
+
+if (!Array.prototype.indices) {
+   Array.prototype.indices = function() {
+      var indexArray = new Array();
+      for ( index in this ) {
+         var ignoreThis = false;
+         for ( var i = 0 ; i < Rico.ArrayExtensions.length ; i++ ) {
+            if ( this[index] == Rico.ArrayExtensions[i] ) {
+               ignoreThis = true;
+               break;
+            }
+         }
+         if ( !ignoreThis )
+            indexArray[ indexArray.length ] = index;
+      }
+      return indexArray;
+   }
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.indices;
+}
+
+// Create the loadXML method and xml getter for Mozilla
+if ( window.DOMParser &&
+	  window.XMLSerializer &&
+	  window.Node && Node.prototype && Node.prototype.__defineGetter__ ) {
+
+   if (!Document.prototype.loadXML) {
+      Document.prototype.loadXML = function (s) {
+         var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
+         while (this.hasChildNodes())
+            this.removeChild(this.lastChild);
+
+         for (var i = 0; i < doc2.childNodes.length; i++) {
+            this.appendChild(this.importNode(doc2.childNodes[i], true));
+         }
+      };
+	}
+
+	Document.prototype.__defineGetter__( "xml",
+	   function () {
+		   return (new XMLSerializer()).serializeToString(this);
+	   }
+	 );
+}
+
+document.getElementsByTagAndClassName = function(tagName, className) {
+  if ( tagName == null )
+     tagName = '*';
+
+  var children = document.getElementsByTagName(tagName) || document.all;
+  var elements = new Array();
+
+  if ( className == null )
+    return children;
+
+  for (var i = 0; i < children.length; i++) {
+    var child = children[i];
+    var classNames = child.className.split(' ');
+    for (var j = 0; j < classNames.length; j++) {
+      if (classNames[j] == className) {
+        elements.push(child);
+        break;
+      }
+    }
+  }
+
+  return elements;
+}
+
+
+//-------------------- ricoAccordion.js
+Rico.Accordion = Class.create();
+
+Rico.Accordion.prototype = {
+
+   initialize: function(container, options) {
+      this.container            = $(container);
+      this.lastExpandedTab      = null;
+      this.accordionTabs        = new Array();
+      this.setOptions(options);
+      this._attachBehaviors();
+      if(!container) return;
+
+      this.container.style.borderBottom = '1px solid ' + this.options.borderColor;
+      // validate onloadShowTab
+       if (this.options.onLoadShowTab >= this.accordionTabs.length)
+        this.options.onLoadShowTab = 0;
+
+      // set the initial visual state...
+      for ( var i=0 ; i < this.accordionTabs.length ; i++ )
+      {
+        if (i != this.options.onLoadShowTab){
+         this.accordionTabs[i].collapse();
+         this.accordionTabs[i].content.style.display = 'none';
+        }
+      }
+      this.lastExpandedTab = this.accordionTabs[this.options.onLoadShowTab];
+      if (this.options.panelHeight == 'auto'){
+          var tabToCheck = (this.options.onloadShowTab === 0)? 1 : 0;
+          var titleBarSize = parseInt(RicoUtil.getElementsComputedStyle(this.accordionTabs[tabToCheck].titleBar, 'height'));
+          if (isNaN(titleBarSize))
+            titleBarSize = this.accordionTabs[tabToCheck].titleBar.offsetHeight;
+          
+          var totalTitleBarSize = this.accordionTabs.length * titleBarSize;
+          var parentHeight = parseInt(RicoUtil.getElementsComputedStyle(this.container.parentNode, 'height'));
+          if (isNaN(parentHeight))
+            parentHeight = this.container.parentNode.offsetHeight;
+          
+          this.options.panelHeight = parentHeight - totalTitleBarSize-2;
+      }
+      
+      this.lastExpandedTab.content.style.height = this.options.panelHeight + "px";
+      this.lastExpandedTab.showExpanded();
+      this.lastExpandedTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
+
+   },
+
+   setOptions: function(options) {
+      this.options = {
+         expandedBg          : '#63699c',
+         hoverBg             : '#63699c',
+         collapsedBg         : '#6b79a5',
+         expandedTextColor   : '#ffffff',
+         expandedFontWeight  : 'bold',
+         hoverTextColor      : '#ffffff',
+         collapsedTextColor  : '#ced7ef',
+         collapsedFontWeight : 'normal',
+         hoverTextColor      : '#ffffff',
+         borderColor         : '#1f669b',
+         panelHeight         : 200,
+         onHideTab           : null,
+         onShowTab           : null,
+         onLoadShowTab       : 0
+      }
+      Object.extend(this.options, options || {});
+   },
+
+   showTabByIndex: function( anIndex, animate ) {
+      var doAnimate = arguments.length == 1 ? true : animate;
+      this.showTab( this.accordionTabs[anIndex], doAnimate );
+   },
+
+   showTab: function( accordionTab, animate ) {
+
+      var doAnimate = arguments.length == 1 ? true : animate;
+
+      if ( this.options.onHideTab )
+         this.options.onHideTab(this.lastExpandedTab);
+
+      this.lastExpandedTab.showCollapsed(); 
+      var accordion = this;
+      var lastExpandedTab = this.lastExpandedTab;
+
+      this.lastExpandedTab.content.style.height = (this.options.panelHeight - 1) + 'px';
+      accordionTab.content.style.display = '';
+
+      accordionTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
+
+      if ( doAnimate ) {
+         new Rico.Effect.AccordionSize( this.lastExpandedTab.content,
+                                   accordionTab.content,
+                                   1,
+                                   this.options.panelHeight,
+                                   100, 10,
+                                   { complete: function() {accordion.showTabDone(lastExpandedTab)} } );
+         this.lastExpandedTab = accordionTab;
+      }
+      else {
+         this.lastExpandedTab.content.style.height = "1px";
+         accordionTab.content.style.height = this.options.panelHeight + "px";
+         this.lastExpandedTab = accordionTab;
+         this.showTabDone(lastExpandedTab);
+      }
+   },
+
+   showTabDone: function(collapsedTab) {
+      collapsedTab.content.style.display = 'none';
+      this.lastExpandedTab.showExpanded();
+      if ( this.options.onShowTab )
+         this.options.onShowTab(this.lastExpandedTab);
+   },
+
+   _attachBehaviors: function() {
+      var panels = this._getDirectChildrenByTag(this.container, 'DIV');
+      for ( var i = 0 ; i < panels.length ; i++ ) {
+
+         var tabChildren = this._getDirectChildrenByTag(panels[i],'DIV');
+         if ( tabChildren.length != 2 )
+            continue; // unexpected
+
+         var tabTitleBar   = tabChildren[0];
+         var tabContentBox = tabChildren[1];
+         this.accordionTabs.push( new Rico.Accordion.Tab(this,tabTitleBar,tabContentBox) );
+      }
+   },
+
+   _getDirectChildrenByTag: function(e, tagName) {
+      var kids = new Array();
+      var allKids = e.childNodes;
+      for( var i = 0 ; i < allKids.length ; i++ )
+         if ( allKids[i] && allKids[i].tagName && allKids[i].tagName == tagName )
+            kids.push(allKids[i]);
+      return kids;
+   }
+
+};
+
+Rico.Accordion.Tab = Class.create();
+
+Rico.Accordion.Tab.prototype = {
+
+   initialize: function(accordion, titleBar, content) {
+      this.accordion = accordion;
+      this.titleBar  = titleBar;
+      this.content   = content;
+      this._attachBehaviors();
+   },
+
+   collapse: function() {
+      this.showCollapsed();
+      this.content.style.height = "1px";
+   },
+
+   showCollapsed: function() {
+      this.expanded = false;
+      this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
+      this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
+      this.titleBar.style.fontWeight      = this.accordion.options.collapsedFontWeight;
+      this.content.style.overflow = "hidden";
+   },
+
+   showExpanded: function() {
+      this.expanded = true;
+      this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
+      this.titleBar.style.color           = this.accordion.options.expandedTextColor;
+      this.content.style.overflow         = "visible";
+   },
+
+   titleBarClicked: function(e) {
+      if ( this.accordion.lastExpandedTab == this )
+         return;
+      this.accordion.showTab(this);
+   },
+
+   hover: function(e) {
+		this.titleBar.style.backgroundColor = this.accordion.options.hoverBg;
+		this.titleBar.style.color           = this.accordion.options.hoverTextColor;
+   },
+
+   unhover: function(e) {
+      if ( this.expanded ) {
+         this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
+         this.titleBar.style.color           = this.accordion.options.expandedTextColor;
+      }
+      else {
+         this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
+         this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
+      }
+   },
+
+   _attachBehaviors: function() {
+      this.content.style.border = "1px solid " + this.accordion.options.borderColor;
+      this.content.style.borderTopWidth    = "0px";
+      this.content.style.borderBottomWidth = "0px";
+      this.content.style.margin            = "0px";
+
+      this.titleBar.onclick     = this.titleBarClicked.bindAsEventListener(this);
+      this.titleBar.onmouseover = this.hover.bindAsEventListener(this);
+      this.titleBar.onmouseout  = this.unhover.bindAsEventListener(this);
+   }
+
+};
+
+
+//-------------------- ricoAjaxEngine.js
+Rico.AjaxEngine = Class.create();
+
+Rico.AjaxEngine.prototype = {
+
+   initialize: function() {
+      this.ajaxElements = new Array();
+      this.ajaxObjects  = new Array();
+      this.requestURLS  = new Array();
+      this.options = {};
+   },
+
+   registerAjaxElement: function( anId, anElement ) {
+      if ( !anElement )
+         anElement = $(anId);
+      this.ajaxElements[anId] = anElement;
+   },
+
+   registerAjaxObject: function( anId, anObject ) {
+      this.ajaxObjects[anId] = anObject;
+   },
+
+   registerRequest: function (requestLogicalName, requestURL) {
+      this.requestURLS[requestLogicalName] = requestURL;
+   },
+
+   sendRequest: function(requestName, options) {
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 2 )
+       if (typeof arguments[1] == 'string')
+         options = {parameters: this._createQueryString(arguments, 1)};
+      this.sendRequestWithData(requestName, null, options);
+   },
+
+   sendRequestWithData: function(requestName, xmlDocument, options) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 3 )
+        if (typeof arguments[2] == 'string')
+          options.parameters = this._createQueryString(arguments, 2);
+
+      new Ajax.Request(requestURL, this._requestOptions(options,xmlDocument));
+   },
+
+   sendRequestAndUpdate: function(requestName,container,options) {
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 3 )
+        if (typeof arguments[2] == 'string')
+          options.parameters = this._createQueryString(arguments, 2);
+
+      this.sendRequestWithDataAndUpdate(requestName, null, container, options);
+   },
+
+   sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      // Allow for backwards Compatibility
+      if ( arguments.length >= 4 )
+        if (typeof arguments[3] == 'string')
+          options.parameters = this._createQueryString(arguments, 3);
+
+      var updaterOptions = this._requestOptions(options,xmlDocument);
+
+      new Ajax.Updater(container, requestURL, updaterOptions);
+   },
+
+   // Private -- not part of intended engine API --------------------------------------------------------------------
+
+   _requestOptions: function(options,xmlDoc) {
+      var requestHeaders = ['X-Rico-Version', Rico.Version ];
+      var sendMethod = 'post';
+      if ( xmlDoc == null )
+        if (Rico.prototypeVersion < 1.4)
+        requestHeaders.push( 'Content-type', 'text/xml' );
+      else
+          sendMethod = 'get';
+      (!options) ? options = {} : '';
+
+      if (!options._RicoOptionsProcessed){
+      // Check and keep any user onComplete functions
+        if (options.onComplete)
+             options.onRicoComplete = options.onComplete;
+        // Fix onComplete
+        if (options.overrideOnComplete)
+          options.onComplete = options.overrideOnComplete;
+        else
+          options.onComplete = this._onRequestComplete.bind(this);
+        options._RicoOptionsProcessed = true;
+      }
+
+     // Set the default options and extend with any user options
+     this.options = {
+                     requestHeaders: requestHeaders,
+                     parameters:     options.parameters,
+                     postBody:       xmlDoc,
+                     method:         sendMethod,
+                     onComplete:     options.onComplete
+                    };
+     // Set any user options:
+     Object.extend(this.options, options);
+     return this.options;
+   },
+
+   _createQueryString: function( theArgs, offset ) {
+      var queryString = ""
+      for ( var i = offset ; i < theArgs.length ; i++ ) {
+          if ( i != offset )
+            queryString += "&";
+
+          var anArg = theArgs[i];
+
+          if ( anArg.name != undefined && anArg.value != undefined ) {
+            queryString += anArg.name +  "=" + escape(anArg.value);
+          }
+          else {
+             var ePos  = anArg.indexOf('=');
+             var argName  = anArg.substring( 0, ePos );
+             var argValue = anArg.substring( ePos + 1 );
+             queryString += argName + "=" + escape(argValue);
+          }
+      }
+      return queryString;
+   },
+
+   _onRequestComplete : function(request) {
+      if(!request)
+          return;
+      // User can set an onFailure option - which will be called by prototype
+      if (request.status != 200)
+        return;
+
+      var response = request.responseXML.getElementsByTagName("ajax-response");
+      if (response == null || response.length != 1)
+         return;
+      this._processAjaxResponse( response[0].childNodes );
+      
+      // Check if user has set a onComplete function
+      var onRicoComplete = this.options.onRicoComplete;
+      if (onRicoComplete != null)
+          onRicoComplete();
+   },
+
+   _processAjaxResponse: function( xmlResponseElements ) {
+      for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) {
+         var responseElement = xmlResponseElements[i];
+
+         // only process nodes of type element.....
+         if ( responseElement.nodeType != 1 )
+            continue;
+
+         var responseType = responseElement.getAttribute("type");
+         var responseId   = responseElement.getAttribute("id");
+
+         if ( responseType == "object" )
+            this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement );
+         else if ( responseType == "element" )
+            this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement );
+         else
+            alert('unrecognized AjaxResponse type : ' + responseType );
+      }
+   },
+
+   _processAjaxObjectUpdate: function( ajaxObject, responseElement ) {
+      ajaxObject.ajaxUpdate( responseElement );
+   },
+
+   _processAjaxElementUpdate: function( ajaxElement, responseElement ) {
+      ajaxElement.innerHTML = RicoUtil.getContentAsString(responseElement);
+   }
+
+}
+
+var ajaxEngine = new Rico.AjaxEngine();
+
+
+//-------------------- ricoColor.js
+Rico.Color = Class.create();
+
+Rico.Color.prototype = {
+
+   initialize: function(red, green, blue) {
+      this.rgb = { r: red, g : green, b : blue };
+   },
+
+   setRed: function(r) {
+      this.rgb.r = r;
+   },
+
+   setGreen: function(g) {
+      this.rgb.g = g;
+   },
+
+   setBlue: function(b) {
+      this.rgb.b = b;
+   },
+
+   setHue: function(h) {
+
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.h = h;
+
+      // convert back to RGB...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setSaturation: function(s) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.s = s;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setBrightness: function(b) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.b = b;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
+   },
+
+   darken: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
+   },
+
+   brighten: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
+   },
+
+   blend: function(other) {
+      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
+      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
+      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
+   },
+
+   isBright: function() {
+      var hsb = this.asHSB();
+      return this.asHSB().b > 0.5;
+   },
+
+   isDark: function() {
+      return ! this.isBright();
+   },
+
+   asRGB: function() {
+      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
+   },
+
+   asHex: function() {
+      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
+   },
+
+   asHSB: function() {
+      return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
+   },
+
+   toString: function() {
+      return this.asHex();
+   }
+
+};
+
+Rico.Color.createFromHex = function(hexCode) {
+  if(hexCode.length==4) {
+    var shortHexCode = hexCode; 
+    var hexCode = '#';
+    for(var i=1;i<4;i++) hexCode += (shortHexCode.charAt(i) + 
+shortHexCode.charAt(i));
+  }
+   if ( hexCode.indexOf('#') == 0 )
+      hexCode = hexCode.substring(1);
+   var red   = hexCode.substring(0,2);
+   var green = hexCode.substring(2,4);
+   var blue  = hexCode.substring(4,6);
+   return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
+}
+
+/**
+ * Factory method for creating a color from the background of
+ * an HTML element.
+ */
+Rico.Color.createColorFromBackground = function(elem) {
+
+   var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color");
+
+   if ( actualColor == "transparent" && elem.parentNode )
+      return Rico.Color.createColorFromBackground(elem.parentNode);
+
+   if ( actualColor == null )
+      return new Rico.Color(255,255,255);
+
+   if ( actualColor.indexOf("rgb(") == 0 ) {
+      var colors = actualColor.substring(4, actualColor.length - 1 );
+      var colorArray = colors.split(",");
+      return new Rico.Color( parseInt( colorArray[0] ),
+                            parseInt( colorArray[1] ),
+                            parseInt( colorArray[2] )  );
+
+   }
+   else if ( actualColor.indexOf("#") == 0 ) {
+      return Rico.Color.createFromHex(actualColor);
+   }
+   else
+      return new Rico.Color(255,255,255);
+}
+
+Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
+
+   var red   = 0;
+	var green = 0;
+	var blue  = 0;
+
+   if (saturation == 0) {
+      red = parseInt(brightness * 255.0 + 0.5);
+	   green = red;
+	   blue = red;
+	}
+	else {
+      var h = (hue - Math.floor(hue)) * 6.0;
+      var f = h - Math.floor(h);
+      var p = brightness * (1.0 - saturation);
+      var q = brightness * (1.0 - saturation * f);
+      var t = brightness * (1.0 - (saturation * (1.0 - f)));
+
+      switch (parseInt(h)) {
+         case 0:
+            red   = (brightness * 255.0 + 0.5);
+            green = (t * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 1:
+            red   = (q * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 2:
+            red   = (p * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (t * 255.0 + 0.5);
+            break;
+         case 3:
+            red   = (p * 255.0 + 0.5);
+            green = (q * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+         case 4:
+            red   = (t * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+          case 5:
+            red   = (brightness * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (q * 255.0 + 0.5);
+            break;
+	    }
+	}
+
+   return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
+}
+
+Rico.Color.RGBtoHSB = function(r, g, b) {
+
+   var hue;
+   var saturation;
+   var brightness;
+
+   var cmax = (r > g) ? r : g;
+   if (b > cmax)
+      cmax = b;
+
+   var cmin = (r < g) ? r : g;
+   if (b < cmin)
+      cmin = b;
+
+   brightness = cmax / 255.0;
+   if (cmax != 0)
+      saturation = (cmax - cmin)/cmax;
+   else
+      saturation = 0;
+
+   if (saturation == 0)
+      hue = 0;
+   else {
+      var redc   = (cmax - r)/(cmax - cmin);
+    	var greenc = (cmax - g)/(cmax - cmin);
+    	var bluec  = (cmax - b)/(cmax - cmin);
+
+    	if (r == cmax)
+    	   hue = bluec - greenc;
+    	else if (g == cmax)
+    	   hue = 2.0 + redc - bluec;
+      else
+    	   hue = 4.0 + greenc - redc;
+
+    	hue = hue / 6.0;
+    	if (hue < 0)
+    	   hue = hue + 1.0;
+   }
+
+   return { h : hue, s : saturation, b : brightness };
+}
+
+
+//-------------------- ricoCorner.js
+Rico.Corner = {
+
+   round: function(e, options) {
+      var e = $(e);
+      this._setOptions(options);
+
+      var color = this.options.color;
+      if ( this.options.color == "fromElement" )
+         color = this._background(e);
+
+      var bgColor = this.options.bgColor;
+      if ( this.options.bgColor == "fromParent" )
+         bgColor = this._background(e.offsetParent);
+
+      this._roundCornersImpl(e, color, bgColor);
+   },
+
+   _roundCornersImpl: function(e, color, bgColor) {
+      if(this.options.border)
+         this._renderBorder(e,bgColor);
+      if(this._isTopRounded())
+         this._roundTopCorners(e,color,bgColor);
+      if(this._isBottomRounded())
+         this._roundBottomCorners(e,color,bgColor);
+   },
+
+   _renderBorder: function(el,bgColor) {
+      var borderValue = "1px solid " + this._borderColor(bgColor);
+      var borderL = "border-left: "  + borderValue;
+      var borderR = "border-right: " + borderValue;
+      var style   = "style='" + borderL + ";" + borderR +  "'";
+      el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
+   },
+
+   _roundTopCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=0 ; i < this.options.numSlices ; i++ )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
+      el.style.paddingTop = 0;
+      el.insertBefore(corner,el.firstChild);
+   },
+
+   _roundBottomCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
+      el.style.paddingBottom = 0;
+      el.appendChild(corner);
+   },
+
+   _createCorner: function(bgColor) {
+      var corner = document.createElement("div");
+      corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
+      return corner;
+   },
+
+   _createCornerSlice: function(color,bgColor, n, position) {
+      var slice = document.createElement("span");
+
+      var inStyle = slice.style;
+      inStyle.backgroundColor = color;
+      inStyle.display  = "block";
+      inStyle.height   = "1px";
+      inStyle.overflow = "hidden";
+      inStyle.fontSize = "1px";
+
+      var borderColor = this._borderColor(color,bgColor);
+      if ( this.options.border && n == 0 ) {
+         inStyle.borderTopStyle    = "solid";
+         inStyle.borderTopWidth    = "1px";
+         inStyle.borderLeftWidth   = "0px";
+         inStyle.borderRightWidth  = "0px";
+         inStyle.borderBottomWidth = "0px";
+         inStyle.height            = "0px"; // assumes css compliant box model
+         inStyle.borderColor       = borderColor;
+      }
+      else if(borderColor) {
+         inStyle.borderColor = borderColor;
+         inStyle.borderStyle = "solid";
+         inStyle.borderWidth = "0px 1px";
+      }
+
+      if ( !this.options.compact && (n == (this.options.numSlices-1)) )
+         inStyle.height = "2px";
+
+      this._setMargin(slice, n, position);
+      this._setBorder(slice, n, position);
+      return slice;
+   },
+
+   _setOptions: function(options) {
+      this.options = {
+         corners : "all",
+         color   : "fromElement",
+         bgColor : "fromParent",
+         blend   : true,
+         border  : false,
+         compact : false
+      }
+      Object.extend(this.options, options || {});
+
+      this.options.numSlices = this.options.compact ? 2 : 4;
+      if ( this._isTransparent() )
+         this.options.blend = false;
+   },
+
+   _whichSideTop: function() {
+      if ( this._hasString(this.options.corners, "all", "top") )
+         return "";
+
+      if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
+         return "";
+
+      if (this.options.corners.indexOf("tl") >= 0)
+         return "left";
+      else if (this.options.corners.indexOf("tr") >= 0)
+          return "right";
+      return "";
+   },
+
+   _whichSideBottom: function() {
+      if ( this._hasString(this.options.corners, "all", "bottom") )
+         return "";
+
+      if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
+         return "";
+
+      if(this.options.corners.indexOf("bl") >=0)
+         return "left";
+      else if(this.options.corners.indexOf("br")>=0)
+         return "right";
+      return "";
+   },
+
+   _borderColor : function(color,bgColor) {
+      if ( color == "transparent" )
+         return bgColor;
+      else if ( this.options.border )
+         return this.options.border;
+      else if ( this.options.blend )
+         return this._blend( bgColor, color );
+      else
+         return "";
+   },
+
+
+   _setMargin: function(el, n, corners) {
+      var marginSize = this._marginSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+
+      if ( whichSide == "left" ) {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
+      }
+      else {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
+      }
+   },
+
+   _setBorder: function(el,n,corners) {
+      var borderSize = this._borderSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+      if ( whichSide == "left" ) {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
+      }
+      else {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+      }
+      if (this.options.border != false)
+        el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+   },
+
+   _marginSize: function(n) {
+      if ( this._isTransparent() )
+         return 0;
+
+      var marginSizes          = [ 5, 3, 2, 1 ];
+      var blendedMarginSizes   = [ 3, 2, 1, 0 ];
+      var compactMarginSizes   = [ 2, 1 ];
+      var smBlendedMarginSizes = [ 1, 0 ];
+
+      if ( this.options.compact && this.options.blend )
+         return smBlendedMarginSizes[n];
+      else if ( this.options.compact )
+         return compactMarginSizes[n];
+      else if ( this.options.blend )
+         return blendedMarginSizes[n];
+      else
+         return marginSizes[n];
+   },
+
+   _borderSize: function(n) {
+      var transparentBorderSizes = [ 5, 3, 2, 1 ];
+      var blendedBorderSizes     = [ 2, 1, 1, 1 ];
+      var compactBorderSizes     = [ 1, 0 ];
+      var actualBorderSizes      = [ 0, 2, 0, 0 ];
+
+      if ( this.options.compact && (this.options.blend || this._isTransparent()) )
+         return 1;
+      else if ( this.options.compact )
+         return compactBorderSizes[n];
+      else if ( this.options.blend )
+         return blendedBorderSizes[n];
+      else if ( this.options.border )
+         return actualBorderSizes[n];
+      else if ( this._isTransparent() )
+         return transparentBorderSizes[n];
+      return 0;
+   },
+
+   _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
+   _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
+   _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
+   _isTransparent: function() { return this.options.color == "transparent"; },
+   _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
+   _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
+   _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
+}
+
+
+//-------------------- ricoDragAndDrop.js
+Rico.DragAndDrop = Class.create();
+
+Rico.DragAndDrop.prototype = {
+
+   initialize: function() {
+      this.dropZones                = new Array();
+      this.draggables               = new Array();
+      this.currentDragObjects       = new Array();
+      this.dragElement              = null;
+      this.lastSelectedDraggable    = null;
+      this.currentDragObjectVisible = false;
+      this.interestedInMotionEvents = false;
+      this._mouseDown = this._mouseDownHandler.bindAsEventListener(this);
+      this._mouseMove = this._mouseMoveHandler.bindAsEventListener(this);
+      this._mouseUp = this._mouseUpHandler.bindAsEventListener(this);
+   },
+
+   registerDropZone: function(aDropZone) {
+      this.dropZones[ this.dropZones.length ] = aDropZone;
+   },
+
+   deregisterDropZone: function(aDropZone) {
+      var newDropZones = new Array();
+      var j = 0;
+      for ( var i = 0 ; i < this.dropZones.length ; i++ ) {
+         if ( this.dropZones[i] != aDropZone )
+            newDropZones[j++] = this.dropZones[i];
+      }
+
+      this.dropZones = newDropZones;
+   },
+
+   clearDropZones: function() {
+      this.dropZones = new Array();
+   },
+
+   registerDraggable: function( aDraggable ) {
+      this.draggables[ this.draggables.length ] = aDraggable;
+      this._addMouseDownHandler( aDraggable );
+   },
+
+   clearSelection: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].deselect();
+      this.currentDragObjects = new Array();
+      this.lastSelectedDraggable = null;
+   },
+
+   hasSelection: function() {
+      return this.currentDragObjects.length > 0;
+   },
+
+   setStartDragFromElement: function( e, mouseDownElement ) {
+      this.origPos = RicoUtil.toDocumentPosition(mouseDownElement);
+      this.startx = e.screenX - this.origPos.x
+      this.starty = e.screenY - this.origPos.y
+      //this.startComponentX = e.layerX ? e.layerX : e.offsetX;
+      //this.startComponentY = e.layerY ? e.layerY : e.offsetY;
+      //this.adjustedForDraggableSize = false;
+
+      this.interestedInMotionEvents = this.hasSelection();
+      this._terminateEvent(e);
+   },
+
+   updateSelection: function( draggable, extendSelection ) {
+      if ( ! extendSelection )
+         this.clearSelection();
+
+      if ( draggable.isSelected() ) {
+         this.currentDragObjects.removeItem(draggable);
+         draggable.deselect();
+         if ( draggable == this.lastSelectedDraggable )
+            this.lastSelectedDraggable = null;
+      }
+      else {
+         this.currentDragObjects[ this.currentDragObjects.length ] = draggable;
+         draggable.select();
+         this.lastSelectedDraggable = draggable;
+      }
+   },
+
+   _mouseDownHandler: function(e) {
+      if ( arguments.length == 0 )
+         e = event;
+
+      // if not button 1 ignore it...
+      var nsEvent = e.which != undefined;
+      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
+         return;
+
+      var eventTarget      = e.target ? e.target : e.srcElement;
+      var draggableObject  = eventTarget.draggable;
+
+      var candidate = eventTarget;
+      while (draggableObject == null && candidate.parentNode) {
+         candidate = candidate.parentNode;
+         draggableObject = candidate.draggable;
+      }
+   
+      if ( draggableObject == null )
+         return;
+
+      this.updateSelection( draggableObject, e.ctrlKey );
+
+      // clear the drop zones postion cache...
+      if ( this.hasSelection() )
+         for ( var i = 0 ; i < this.dropZones.length ; i++ )
+            this.dropZones[i].clearPositionCache();
+
+      this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() );
+   },
+
+
+   _mouseMoveHandler: function(e) {
+      var nsEvent = e.which != undefined;
+      if ( !this.interestedInMotionEvents ) {
+         //this._terminateEvent(e);
+         return;
+      }
+
+      if ( ! this.hasSelection() )
+         return;
+
+      if ( ! this.currentDragObjectVisible )
+         this._startDrag(e);
+
+      if ( !this.activatedDropZones )
+         this._activateRegisteredDropZones();
+
+      //if ( !this.adjustedForDraggableSize )
+      //   this._adjustForDraggableSize(e);
+
+      this._updateDraggableLocation(e);
+      this._updateDropZonesHover(e);
+
+      this._terminateEvent(e);
+   },
+
+   _makeDraggableObjectVisible: function(e)
+   {
+      if ( !this.hasSelection() )
+         return;
+
+      var dragElement;
+      if ( this.currentDragObjects.length > 1 )
+         dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects);
+      else
+         dragElement = this.currentDragObjects[0].getSingleObjectDragGUI();
+
+      // go ahead and absolute position it...
+      if ( RicoUtil.getElementsComputedStyle(dragElement, "position")  != "absolute" )
+         dragElement.style.position = "absolute";
+
+      // need to parent him into the document...
+      if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 )
+         document.body.appendChild(dragElement);
+
+      this.dragElement = dragElement;
+      this._updateDraggableLocation(e);
+
+      this.currentDragObjectVisible = true;
+   },
+
+   /**
+   _adjustForDraggableSize: function(e) {
+      var dragElementWidth  = this.dragElement.offsetWidth;
+      var dragElementHeight = this.dragElement.offsetHeight;
+      if ( this.startComponentX > dragElementWidth )
+         this.startx -= this.startComponentX - dragElementWidth + 2;
+      if ( e.offsetY ) {
+         if ( this.startComponentY > dragElementHeight )
+            this.starty -= this.startComponentY - dragElementHeight + 2;
+      }
+      this.adjustedForDraggableSize = true;
+   },
+   **/
+
+   _leftOffset: function(e) {
+	   return e.offsetX ? document.body.scrollLeft : 0
+	},
+
+   _topOffset: function(e) {
+	   return e.offsetY ? document.body.scrollTop:0
+	},
+
+		
+   _updateDraggableLocation: function(e) {
+      var dragObjectStyle = this.dragElement.style;
+      dragObjectStyle.left = (e.screenX + this._leftOffset(e) - this.startx) + "px"
+      dragObjectStyle.top  = (e.screenY + this._topOffset(e) - this.starty) + "px";
+   },
+
+   _updateDropZonesHover: function(e) {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) )
+            this.dropZones[i].hideHover();
+      }
+
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
+            if ( this.dropZones[i].canAccept(this.currentDragObjects) )
+               this.dropZones[i].showHover();
+         }
+      }
+   },
+
+   _startDrag: function(e) {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].startDrag();
+
+      this._makeDraggableObjectVisible(e);
+   },
+
+   _mouseUpHandler: function(e) {
+      if ( ! this.hasSelection() )
+         return;
+
+      var nsEvent = e.which != undefined;
+      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
+         return;
+
+      this.interestedInMotionEvents = false;
+
+      if ( this.dragElement == null ) {
+         this._terminateEvent(e);
+         return;
+      }
+
+      if ( this._placeDraggableInDropZone(e) )
+         this._completeDropOperation(e);
+      else {
+         this._terminateEvent(e);
+         new Rico.Effect.Position( this.dragElement,
+                              this.origPos.x,
+                              this.origPos.y,
+                              200,
+                              20,
+                              { complete : this._doCancelDragProcessing.bind(this) } );
+      }
+
+     Event.stopObserving(document.body, "mousemove", this._mouseMove);
+     Event.stopObserving(document.body, "mouseup",  this._mouseUp);
+   },
+
+   _retTrue: function () {
+      return true;
+   },
+
+   _completeDropOperation: function(e) {
+      if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
+         if ( this.dragElement.parentNode != null )
+            this.dragElement.parentNode.removeChild(this.dragElement);
+      }
+
+      this._deactivateRegisteredDropZones();
+      this._endDrag();
+      this.clearSelection();
+      this.dragElement = null;
+      this.currentDragObjectVisible = false;
+      this._terminateEvent(e);
+   },
+
+   _doCancelDragProcessing: function() {
+      this._cancelDrag();
+
+        if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() && this.dragElement)
+           if ( this.dragElement.parentNode != null )
+              this.dragElement.parentNode.removeChild(this.dragElement);
+
+
+      this._deactivateRegisteredDropZones();
+      this.dragElement = null;
+      this.currentDragObjectVisible = false;
+   },
+
+   _placeDraggableInDropZone: function(e) {
+      var foundDropZone = false;
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
+            if ( this.dropZones[i].canAccept(this.currentDragObjects) ) {
+               this.dropZones[i].hideHover();
+               this.dropZones[i].accept(this.currentDragObjects);
+               foundDropZone = true;
+               break;
+            }
+         }
+      }
+
+      return foundDropZone;
+   },
+
+   _cancelDrag: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].cancelDrag();
+   },
+
+   _endDrag: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].endDrag();
+   },
+
+   _mousePointInDropZone: function( e, dropZone ) {
+
+      var absoluteRect = dropZone.getAbsoluteRect();
+
+      return e.clientX  > absoluteRect.left + this._leftOffset(e) &&
+             e.clientX  < absoluteRect.right + this._leftOffset(e) &&
+             e.clientY  > absoluteRect.top + this._topOffset(e)   &&
+             e.clientY  < absoluteRect.bottom + this._topOffset(e);
+   },
+
+   _addMouseDownHandler: function( aDraggable )
+   {
+       htmlElement  = aDraggable.getMouseDownHTMLElement();
+      if ( htmlElement  != null ) { 
+         htmlElement.draggable = aDraggable;
+         Event.observe(htmlElement , "mousedown", this._onmousedown.bindAsEventListener(this));
+         Event.observe(htmlElement, "mousedown", this._mouseDown);
+      }
+   },
+
+   _activateRegisteredDropZones: function() {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         var dropZone = this.dropZones[i];
+         if ( dropZone.canAccept(this.currentDragObjects) )
+            dropZone.activate();
+      }
+
+      this.activatedDropZones = true;
+   },
+
+   _deactivateRegisteredDropZones: function() {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ )
+         this.dropZones[i].deactivate();
+      this.activatedDropZones = false;
+   },
+
+   _onmousedown: function () {
+     Event.observe(document.body, "mousemove", this._mouseMove);
+     Event.observe(document.body, "mouseup",  this._mouseUp);
+   },
+
+   _terminateEvent: function(e) {
+      if ( e.stopPropagation != undefined )
+         e.stopPropagation();
+      else if ( e.cancelBubble != undefined )
+         e.cancelBubble = true;
+
+      if ( e.preventDefault != undefined )
+         e.preventDefault();
+      else
+         e.returnValue = false;
+   },
+
+
+	   initializeEventHandlers: function() {
+	      if ( typeof document.implementation != "undefined" &&
+	         document.implementation.hasFeature("HTML",   "1.0") &&
+	         document.implementation.hasFeature("Events", "2.0") &&
+	         document.implementation.hasFeature("CSS",    "2.0") ) {
+	         document.addEventListener("mouseup",   this._mouseUpHandler.bindAsEventListener(this),  false);
+	         document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false);
+	      }
+	      else {
+	         document.attachEvent( "onmouseup",   this._mouseUpHandler.bindAsEventListener(this) );
+	         document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) );
+	      }
+	   }
+	}
+
+	var dndMgr = new Rico.DragAndDrop();
+	dndMgr.initializeEventHandlers();
+
+
+//-------------------- ricoDraggable.js
+Rico.Draggable = Class.create();
+
+Rico.Draggable.prototype = {
+
+   initialize: function( type, htmlElement ) {
+      this.type          = type;
+      this.htmlElement   = $(htmlElement);
+      this.selected      = false;
+   },
+
+   /**
+    *   Returns the HTML element that should have a mouse down event
+    *   added to it in order to initiate a drag operation
+    *
+    **/
+   getMouseDownHTMLElement: function() {
+      return this.htmlElement;
+   },
+
+   select: function() {
+      this.selected = true;
+
+      if ( this.showingSelected )
+         return;
+
+      var htmlElement = this.getMouseDownHTMLElement();
+
+      var color = Rico.Color.createColorFromBackground(htmlElement);
+      color.isBright() ? color.darken(0.033) : color.brighten(0.033);
+
+      this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
+      htmlElement.style.backgroundColor = color.asHex();
+      this.showingSelected = true;
+   },
+
+   deselect: function() {
+      this.selected = false;
+      if ( !this.showingSelected )
+         return;
+
+      var htmlElement = this.getMouseDownHTMLElement();
+
+      htmlElement.style.backgroundColor = this.saveBackground;
+      this.showingSelected = false;
+   },
+
+   isSelected: function() {
+      return this.selected;
+   },
+
+   startDrag: function() {
+   },
+
+   cancelDrag: function() {
+   },
+
+   endDrag: function() {
+   },
+
+   getSingleObjectDragGUI: function() {
+      return this.htmlElement;
+   },
+
+   getMultiObjectDragGUI: function( draggables ) {
+      return this.htmlElement;
+   },
+
+   getDroppedGUI: function() {
+      return this.htmlElement;
+   },
+
+   toString: function() {
+      return this.type + ":" + this.htmlElement + ":";
+   }
+
+}
+
+
+//-------------------- ricoDropzone.js
+Rico.Dropzone = Class.create();
+
+Rico.Dropzone.prototype = {
+
+   initialize: function( htmlElement ) {
+      this.htmlElement  = $(htmlElement);
+      this.absoluteRect = null;
+   },
+
+   getHTMLElement: function() {
+      return this.htmlElement;
+   },
+
+   clearPositionCache: function() {
+      this.absoluteRect = null;
+   },
+
+   getAbsoluteRect: function() {
+      if ( this.absoluteRect == null ) {
+         var htmlElement = this.getHTMLElement();
+         var pos = RicoUtil.toViewportPosition(htmlElement);
+
+         this.absoluteRect = {
+            top:    pos.y,
+            left:   pos.x,
+            bottom: pos.y + htmlElement.offsetHeight,
+            right:  pos.x + htmlElement.offsetWidth
+         };
+      }
+      return this.absoluteRect;
+   },
+
+   activate: function() {
+      var htmlElement = this.getHTMLElement();
+      if (htmlElement == null  || this.showingActive)
+         return;
+
+      this.showingActive = true;
+      this.saveBackgroundColor = htmlElement.style.backgroundColor;
+
+      var fallbackColor = "#ffea84";
+      var currentColor = Rico.Color.createColorFromBackground(htmlElement);
+      if ( currentColor == null )
+         htmlElement.style.backgroundColor = fallbackColor;
+      else {
+         currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2);
+         htmlElement.style.backgroundColor = currentColor.asHex();
+      }
+   },
+
+   deactivate: function() {
+      var htmlElement = this.getHTMLElement();
+      if (htmlElement == null || !this.showingActive)
+         return;
+
+      htmlElement.style.backgroundColor = this.saveBackgroundColor;
+      this.showingActive = false;
+      this.saveBackgroundColor = null;
+   },
+
+   showHover: function() {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null || this.showingHover )
+         return;
+
+      this.saveBorderWidth = htmlElement.style.borderWidth;
+      this.saveBorderStyle = htmlElement.style.borderStyle;
+      this.saveBorderColor = htmlElement.style.borderColor;
+
+      this.showingHover = true;
+      htmlElement.style.borderWidth = "1px";
+      htmlElement.style.borderStyle = "solid";
+      //htmlElement.style.borderColor = "#ff9900";
+      htmlElement.style.borderColor = "#ffff00";
+   },
+
+   hideHover: function() {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null || !this.showingHover )
+         return;
+
+      htmlElement.style.borderWidth = this.saveBorderWidth;
+      htmlElement.style.borderStyle = this.saveBorderStyle;
+      htmlElement.style.borderColor = this.saveBorderColor;
+      this.showingHover = false;
+   },
+
+   canAccept: function(draggableObjects) {
+      return true;
+   },
+
+   accept: function(draggableObjects) {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null )
+         return;
+
+      n = draggableObjects.length;
+      for ( var i = 0 ; i < n ; i++ )
+      {
+         var theGUI = draggableObjects[i].getDroppedGUI();
+         if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" )
+         {
+            theGUI.style.position = "static";
+            theGUI.style.top = "";
+            theGUI.style.top = "";
+         }
+         htmlElement.appendChild(theGUI);
+      }
+   }
+}
+
+
+//-------------------- ricoEffects.js
+
+/**
+  *  Use the Effect namespace for effects.  If using scriptaculous effects
+  *  this will already be defined, otherwise we'll just create an empty
+  *  object for it...
+ **/
+if ( window.Effect == undefined )
+   Rico.Effect = {};
+
+Rico.Effect.SizeAndPosition = Class.create();
+Rico.Effect.SizeAndPosition.prototype = {
+
+   initialize: function(element, x, y, w, h, duration, steps, options) {
+      this.element = $(element);
+      this.x = x;
+      this.y = y;
+      this.w = w;
+      this.h = h;
+      this.duration = duration;
+      this.steps    = steps;
+      this.options  = arguments[7] || {};
+
+      this.sizeAndPosition();
+   },
+
+   sizeAndPosition: function() {
+      if (this.isFinished()) {
+         if(this.options.complete) this.options.complete(this);
+         return;
+      }
+
+      if (this.timer)
+         clearTimeout(this.timer);
+
+      var stepDuration = Math.round(this.duration/this.steps) ;
+
+      // Get original values: x,y = top left corner;  w,h = width height
+      var currentX = this.element.offsetLeft;
+      var currentY = this.element.offsetTop;
+      var currentW = this.element.offsetWidth;
+      var currentH = this.element.offsetHeight;
+
+      // If values not set, or zero, we do not modify them, and take original as final as well
+      this.x = (this.x) ? this.x : currentX;
+      this.y = (this.y) ? this.y : currentY;
+      this.w = (this.w) ? this.w : currentW;
+      this.h = (this.h) ? this.h : currentH;
+
+      // how much do we need to modify our values for each step?
+      var difX = this.steps >  0 ? (this.x - currentX)/this.steps : 0;
+      var difY = this.steps >  0 ? (this.y - currentY)/this.steps : 0;
+      var difW = this.steps >  0 ? (this.w - currentW)/this.steps : 0;
+      var difH = this.steps >  0 ? (this.h - currentH)/this.steps : 0;
+
+      this.moveBy(difX, difY);
+      this.resizeBy(difW, difH);
+
+      this.duration -= stepDuration;
+      this.steps--;
+
+      this.timer = setTimeout(this.sizeAndPosition.bind(this), stepDuration);
+   },
+
+   isFinished: function() {
+      return this.steps <= 0;
+   },
+
+   moveBy: function( difX, difY ) {
+      var currentLeft = this.element.offsetLeft;
+      var currentTop  = this.element.offsetTop;
+      var intDifX     = parseInt(difX);
+      var intDifY     = parseInt(difY);
+
+      var style = this.element.style;
+      if ( intDifX != 0 )
+         style.left = (currentLeft + intDifX) + "px";
+      if ( intDifY != 0 )
+         style.top  = (currentTop + intDifY) + "px";
+   },
+
+   resizeBy: function( difW, difH ) {
+      var currentWidth  = this.element.offsetWidth;
+      var currentHeight = this.element.offsetHeight;
+      var intDifW       = parseInt(difW);
+      var intDifH       = parseInt(difH);
+
+      var style = this.element.style;
+      if ( intDifW != 0 )
+         style.width   = (currentWidth  + intDifW) + "px";
+      if ( intDifH != 0 )
+         style.height  = (currentHeight + intDifH) + "px";
+   }
+}
+
+Rico.Effect.Size = Class.create();
+Rico.Effect.Size.prototype = {
+
+   initialize: function(element, w, h, duration, steps, options) {
+      new Rico.Effect.SizeAndPosition(element, null, null, w, h, duration, steps, options);
+  }
+}
+
+Rico.Effect.Position = Class.create();
+Rico.Effect.Position.prototype = {
+
+   initialize: function(element, x, y, duration, steps, options) {
+      new Rico.Effect.SizeAndPosition(element, x, y, null, null, duration, steps, options);
+  }
+}
+
+Rico.Effect.Round = Class.create();
+Rico.Effect.Round.prototype = {
+
+   initialize: function(tagName, className, options) {
+      var elements = document.getElementsByTagAndClassName(tagName,className);
+      for ( var i = 0 ; i < elements.length ; i++ )
+         Rico.Corner.round( elements[i], options );
+   }
+};
+
+Rico.Effect.FadeTo = Class.create();
+Rico.Effect.FadeTo.prototype = {
+
+   initialize: function( element, opacity, duration, steps, options) {
+      this.element  = $(element);
+      this.opacity  = opacity;
+      this.duration = duration;
+      this.steps    = steps;
+      this.options  = arguments[4] || {};
+      this.fadeTo();
+   },
+
+   fadeTo: function() {
+      if (this.isFinished()) {
+         if(this.options.complete) this.options.complete(this);
+         return;
+      }
+
+      if (this.timer)
+         clearTimeout(this.timer);
+
+      var stepDuration = Math.round(this.duration/this.steps) ;
+      var currentOpacity = this.getElementOpacity();
+      var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0;
+
+      this.changeOpacityBy(delta);
+      this.duration -= stepDuration;
+      this.steps--;
+
+      this.timer = setTimeout(this.fadeTo.bind(this), stepDuration);
+   },
+
+   changeOpacityBy: function(v) {
+      var currentOpacity = this.getElementOpacity();
+      var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1));
+      this.element.ricoOpacity = newOpacity;
+
+      this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")";
+      this.element.style.opacity = newOpacity; /*//*/;
+   },
+
+   isFinished: function() {
+      return this.steps <= 0;
+   },
+
+   getElementOpacity: function() {
+      if ( this.element.ricoOpacity == undefined ) {
+         var opacity = RicoUtil.getElementsComputedStyle(this.element, 'opacity');
+         this.element.ricoOpacity = opacity != undefined ? opacity : 1.0;
+      }
+      return parseFloat(this.element.ricoOpacity);
+   }
+}
+
+Rico.Effect.AccordionSize = Class.create();
+
+Rico.Effect.AccordionSize.prototype = {
+
+   initialize: function(e1, e2, start, end, duration, steps, options) {
+      this.e1       = $(e1);
+      this.e2       = $(e2);
+      this.start    = start;
+      this.end      = end;
+      this.duration = duration;
+      this.steps    = steps;
+      this.options  = arguments[6] || {};
+
+      this.accordionSize();
+   },
+
+   accordionSize: function() {
+
+      if (this.isFinished()) {
+         // just in case there are round errors or such...
+         this.e1.style.height = this.start + "px";
+         this.e2.style.height = this.end + "px";
+
+         if(this.options.complete)
+            this.options.complete(this);
+         return;
+      }
+
+      if (this.timer)
+         clearTimeout(this.timer);
+
+      var stepDuration = Math.round(this.duration/this.steps) ;
+
+      var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0;
+      this.resizeBy(diff);
+
+      this.duration -= stepDuration;
+      this.steps--;
+
+      this.timer = setTimeout(this.accordionSize.bind(this), stepDuration);
+   },
+
+   isFinished: function() {
+      return this.steps <= 0;
+   },
+
+   resizeBy: function(diff) {
+      var h1Height = this.e1.offsetHeight;
+      var h2Height = this.e2.offsetHeight;
+      var intDiff = parseInt(diff);
+      if ( diff != 0 ) {
+         this.e1.style.height = (h1Height - intDiff) + "px";
+         this.e2.style.height = (h2Height + intDiff) + "px";
+      }
+   }
+
+};
+
+
+//-------------------- ricoLiveGrid.js
+// Rico.LiveGridMetaData -----------------------------------------------------
+
+Rico.LiveGridMetaData = Class.create();
+
+Rico.LiveGridMetaData.prototype = {
+
+   initialize: function( pageSize, totalRows, columnCount, options ) {
+      this.pageSize  = pageSize;
+      this.totalRows = totalRows;
+      this.setOptions(options);
+      this.ArrowHeight = 16;
+      this.columnCount = columnCount;
+   },
+
+   setOptions: function(options) {
+      this.options = {
+         largeBufferSize    : 7.0,   // 7 pages
+         nearLimitFactor    : 0.2    // 20% of buffer
+      };
+      Object.extend(this.options, options || {});
+   },
+
+   getPageSize: function() {
+      return this.pageSize;
+   },
+
+   getTotalRows: function() {
+      return this.totalRows;
+   },
+
+   setTotalRows: function(n) {
+      this.totalRows = n;
+   },
+
+   getLargeBufferSize: function() {
+      return parseInt(this.options.largeBufferSize * this.pageSize);
+   },
+
+   getLimitTolerance: function() {
+      return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor);
+   }
+};
+
+// Rico.LiveGridScroller -----------------------------------------------------
+
+Rico.LiveGridScroller = Class.create();
+
+Rico.LiveGridScroller.prototype = {
+
+   initialize: function(liveGrid, viewPort) {
+      this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0;
+      this.liveGrid = liveGrid;
+      this.metaData = liveGrid.metaData;
+      this.createScrollBar();
+      this.scrollTimeout = null;
+      this.lastScrollPos = 0;
+      this.viewPort = viewPort;
+      this.rows = new Array();
+   },
+
+   isUnPlugged: function() {
+      return this.scrollerDiv.onscroll == null;
+   },
+
+   plugin: function() {
+      this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
+   },
+
+   unplug: function() {
+      this.scrollerDiv.onscroll = null;
+   },
+
+   sizeIEHeaderHack: function() {
+      if ( !this.isIE ) return;
+      var headerTable = $(this.liveGrid.tableId + "_header");
+      if ( headerTable )
+         headerTable.rows[0].cells[0].style.width =
+            (headerTable.rows[0].cells[0].offsetWidth + 1) + "px";
+   },
+
+   createScrollBar: function() {
+      var visibleHeight = this.liveGrid.viewPort.visibleHeight();
+      // create the outer div...
+      this.scrollerDiv  = document.createElement("div");
+      var scrollerStyle = this.scrollerDiv.style;
+      scrollerStyle.borderRight = this.liveGrid.options.scrollerBorderRight;
+      scrollerStyle.position    = "relative";
+      scrollerStyle.left        = this.isIE ? "-6px" : "-3px";
+      scrollerStyle.width       = "19px";
+      scrollerStyle.height      = visibleHeight + "px";
+      scrollerStyle.overflow    = "auto";
+
+      // create the inner div...
+      this.heightDiv = document.createElement("div");
+      this.heightDiv.style.width  = "1px";
+
+      this.heightDiv.style.height = parseInt(visibleHeight *
+                        this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px" ;
+      this.scrollerDiv.appendChild(this.heightDiv);
+      this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
+
+     var table = this.liveGrid.table;
+     table.parentNode.parentNode.insertBefore( this.scrollerDiv, table.parentNode.nextSibling );
+  	  var eventName = this.isIE ? "mousewheel" : "DOMMouseScroll";
+	  Event.observe(table, eventName, 
+	                function(evt) {
+	                   if (evt.wheelDelta>=0 || evt.detail < 0) //wheel-up
+	                      this.scrollerDiv.scrollTop -= (2*this.viewPort.rowHeight);
+	                   else
+	                      this.scrollerDiv.scrollTop += (2*this.viewPort.rowHeight);
+	                   this.handleScroll(false);
+	                }.bindAsEventListener(this), 
+	                false);
+     },
+
+   updateSize: function() {
+      var table = this.liveGrid.table;
+      var visibleHeight = this.viewPort.visibleHeight();
+      this.heightDiv.style.height = parseInt(visibleHeight *
+                                  this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px";
+   },
+
+   rowToPixel: function(rowOffset) {
+      return (rowOffset / this.metaData.getTotalRows()) * this.heightDiv.offsetHeight
+   },
+   
+   moveScroll: function(rowOffset) {
+      this.scrollerDiv.scrollTop = this.rowToPixel(rowOffset);
+      if ( this.metaData.options.onscroll )
+         this.metaData.options.onscroll( this.liveGrid, rowOffset );
+   },
+
+   handleScroll: function() {
+     if ( this.scrollTimeout )
+         clearTimeout( this.scrollTimeout );
+
+    var scrollDiff = this.lastScrollPos-this.scrollerDiv.scrollTop;
+    if (scrollDiff != 0.00) {
+       var r = this.scrollerDiv.scrollTop % this.viewPort.rowHeight;
+       if (r != 0) {
+          this.unplug();
+          if ( scrollDiff < 0 ) {
+             this.scrollerDiv.scrollTop += (this.viewPort.rowHeight-r);
+          } else {
+             this.scrollerDiv.scrollTop -= r;
+          }
+          this.plugin();
+       }
+    }
+    var contentOffset = parseInt(this.scrollerDiv.scrollTop / this.viewPort.rowHeight);
+    this.liveGrid.requestContentRefresh(contentOffset);
+    this.viewPort.scrollTo(this.scrollerDiv.scrollTop);
+
+    if ( this.metaData.options.onscroll )
+       this.metaData.options.onscroll( this.liveGrid, contentOffset );
+
+    this.scrollTimeout = setTimeout(this.scrollIdle.bind(this), 1200 );
+    this.lastScrollPos = this.scrollerDiv.scrollTop;
+
+   },
+
+   scrollIdle: function() {
+      if ( this.metaData.options.onscrollidle )
+         this.metaData.options.onscrollidle();
+   }
+};
+
+// Rico.LiveGridBuffer -----------------------------------------------------
+
+Rico.LiveGridBuffer = Class.create();
+
+Rico.LiveGridBuffer.prototype = {
+
+   initialize: function(metaData, viewPort) {
+      this.startPos = 0;
+      this.size     = 0;
+      this.metaData = metaData;
+      this.rows     = new Array();
+      this.updateInProgress = false;
+      this.viewPort = viewPort;
+      this.maxBufferSize = metaData.getLargeBufferSize() * 2;
+      this.maxFetchSize = metaData.getLargeBufferSize();
+      this.lastOffset = 0;
+   },
+
+   getBlankRow: function() {
+      if (!this.blankRow ) {
+         this.blankRow = new Array();
+         for ( var i=0; i < this.metaData.columnCount ; i++ ) 
+            this.blankRow[i] = "&nbsp;";
+     }
+     return this.blankRow;
+   },
+
+   loadRows: function(ajaxResponse) {
+      var rowsElement = ajaxResponse.getElementsByTagName('rows')[0];
+      this.updateUI = rowsElement.getAttribute("update_ui") == "true"
+      var newRows = new Array()
+      var trs = rowsElement.getElementsByTagName("tr");
+      for ( var i=0 ; i < trs.length; i++ ) {
+         var row = newRows[i] = new Array(); 
+         var cells = trs[i].getElementsByTagName("td");
+         for ( var j=0; j < cells.length ; j++ ) {
+            var cell = cells[j];
+            var convertSpaces = cell.getAttribute("convert_spaces") == "true";
+            var cellContent = RicoUtil.getContentAsString(cell);
+            row[j] = convertSpaces ? this.convertSpaces(cellContent) : cellContent;
+            if (!row[j]) 
+               row[j] = '&nbsp;';
+         }
+      }
+      return newRows;
+   },
+      
+   update: function(ajaxResponse, start) {
+     var newRows = this.loadRows(ajaxResponse);
+      if (this.rows.length == 0) { // initial load
+         this.rows = newRows;
+         this.size = this.rows.length;
+         this.startPos = start;
+         return;
+      }
+      if (start > this.startPos) { //appending
+         if (this.startPos + this.rows.length < start) {
+            this.rows =  newRows;
+            this.startPos = start;//
+         } else {
+              this.rows = this.rows.concat( newRows.slice(0, newRows.length));
+            if (this.rows.length > this.maxBufferSize) {
+               var fullSize = this.rows.length;
+               this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length)
+               this.startPos = this.startPos +  (fullSize - this.rows.length);
+            }
+         }
+      } else { //prepending
+         if (start + newRows.length < this.startPos) {
+            this.rows =  newRows;
+         } else {
+            this.rows = newRows.slice(0, this.startPos).concat(this.rows);
+            if (this.rows.length > this.maxBufferSize) 
+               this.rows = this.rows.slice(0, this.maxBufferSize)
+         }
+         this.startPos =  start;
+      }
+      this.size = this.rows.length;
+   },
+   
+   clear: function() {
+      this.rows = new Array();
+      this.startPos = 0;
+      this.size = 0;
+   },
+
+   isOverlapping: function(start, size) {
+      return ((start < this.endPos()) && (this.startPos < start + size)) || (this.endPos() == 0)
+   },
+
+   isInRange: function(position) {
+      return (position >= this.startPos) && (position + this.metaData.getPageSize() <= this.endPos()); 
+             //&& this.size()  != 0;
+   },
+
+   isNearingTopLimit: function(position) {
+      return position - this.startPos < this.metaData.getLimitTolerance();
+   },
+
+   endPos: function() {
+      return this.startPos + this.rows.length;
+   },
+   
+   isNearingBottomLimit: function(position) {
+      return this.endPos() - (position + this.metaData.getPageSize()) < this.metaData.getLimitTolerance();
+   },
+
+   isAtTop: function() {
+      return this.startPos == 0;
+   },
+
+   isAtBottom: function() {
+      return this.endPos() == this.metaData.getTotalRows();
+   },
+
+   isNearingLimit: function(position) {
+      return ( !this.isAtTop()    && this.isNearingTopLimit(position)) ||
+             ( !this.isAtBottom() && this.isNearingBottomLimit(position) )
+   },
+
+   getFetchSize: function(offset) {
+      var adjustedOffset = this.getFetchOffset(offset);
+      var adjustedSize = 0;
+      if (adjustedOffset >= this.startPos) { //apending
+         var endFetchOffset = this.maxFetchSize  + adjustedOffset;
+         if (endFetchOffset > this.metaData.totalRows)
+            endFetchOffset = this.metaData.totalRows;
+         adjustedSize = endFetchOffset - adjustedOffset;  
+			if(adjustedOffset == 0 && adjustedSize < this.maxFetchSize){
+			   adjustedSize = this.maxFetchSize;
+			}
+      } else {//prepending
+         var adjustedSize = this.startPos - adjustedOffset;
+         if (adjustedSize > this.maxFetchSize)
+            adjustedSize = this.maxFetchSize;
+      }
+      return adjustedSize;
+   }, 
+
+   getFetchOffset: function(offset) {
+      var adjustedOffset = offset;
+      if (offset > this.startPos)  //apending
+         adjustedOffset = (offset > this.endPos()) ? offset :  this.endPos(); 
+      else { //prepending
+         if (offset + this.maxFetchSize >= this.startPos) {
+            var adjustedOffset = this.startPos - this.maxFetchSize;
+            if (adjustedOffset < 0)
+               adjustedOffset = 0;
+         }
+      }
+      this.lastOffset = adjustedOffset;
+      return adjustedOffset;
+   },
+
+   getRows: function(start, count) {
+      var begPos = start - this.startPos
+      var endPos = begPos + count
+
+      // er? need more data...
+      if ( endPos > this.size )
+         endPos = this.size
+
+      var results = new Array()
+      var index = 0;
+      for ( var i=begPos ; i < endPos; i++ ) {
+         results[index++] = this.rows[i]
+      }
+      return results
+   },
+
+   convertSpaces: function(s) {
+      return s.split(" ").join("&nbsp;");
+   }
+
+};
+
+
+//Rico.GridViewPort --------------------------------------------------
+Rico.GridViewPort = Class.create();
+
+Rico.GridViewPort.prototype = {
+
+   initialize: function(table, rowHeight, visibleRows, buffer, liveGrid) {
+      this.lastDisplayedStartPos = 0;
+      this.div = table.parentNode;
+      this.table = table
+      this.rowHeight = rowHeight;
+      this.div.style.height = this.rowHeight * visibleRows;
+      this.div.style.overflow = "hidden";
+      this.buffer = buffer;
+      this.liveGrid = liveGrid;
+      this.visibleRows = visibleRows + 1;
+      this.lastPixelOffset = 0;
+      this.startPos = 0;
+   },
+
+   populateRow: function(htmlRow, row) {
+      for (var j=0; j < row.length; j++) {
+         htmlRow.cells[j].innerHTML = row[j]
+      }
+   },
+   
+   bufferChanged: function() {
+      this.refreshContents( parseInt(this.lastPixelOffset / this.rowHeight));
+   },
+   
+   clearRows: function() {
+      if (!this.isBlank) {
+         this.liveGrid.table.className = this.liveGrid.options.loadingClass;
+         for (var i=0; i < this.visibleRows; i++)
+            this.populateRow(this.table.rows[i], this.buffer.getBlankRow());
+         this.isBlank = true;
+      }
+   },
+   
+   clearContents: function() {   
+      this.clearRows();
+      this.scrollTo(0);
+      this.startPos = 0;
+      this.lastStartPos = -1;   
+   },
+   
+   refreshContents: function(startPos) {
+      if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) {
+         return;
+      }
+      if ((startPos + this.visibleRows < this.buffer.startPos)  
+          || (this.buffer.startPos + this.buffer.size < startPos) 
+          || (this.buffer.size == 0)) {
+         this.clearRows();
+         return;
+      }
+      this.isBlank = false;
+      var viewPrecedesBuffer = this.buffer.startPos > startPos
+      var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos; 
+      var contentEndPos = (this.buffer.startPos + this.buffer.size < startPos + this.visibleRows) 
+                                 ? this.buffer.startPos + this.buffer.size
+                                 : startPos + this.visibleRows;
+      var rowSize = contentEndPos - contentStartPos;
+      var rows = this.buffer.getRows(contentStartPos, rowSize ); 
+      var blankSize = this.visibleRows - rowSize;
+      var blankOffset = viewPrecedesBuffer ? 0: rowSize;
+      var contentOffset = viewPrecedesBuffer ? blankSize: 0;
+
+      for (var i=0; i < rows.length; i++) {//initialize what we have
+        this.populateRow(this.table.rows[i + contentOffset], rows[i]);
+      }
+      for (var i=0; i < blankSize; i++) {// blank out the rest 
+        this.populateRow(this.table.rows[i + blankOffset], this.buffer.getBlankRow());
+      }
+      this.isPartialBlank = blankSize > 0;
+      this.lastRowPos = startPos;
+
+       this.liveGrid.table.className = this.liveGrid.options.tableClass;
+       // Check if user has set a onRefreshComplete function
+       var onRefreshComplete = this.liveGrid.options.onRefreshComplete;
+       if (onRefreshComplete != null)
+           onRefreshComplete();
+   },
+
+   scrollTo: function(pixelOffset) {      
+      if (this.lastPixelOffset == pixelOffset)
+         return;
+
+      this.refreshContents(parseInt(pixelOffset / this.rowHeight))
+      this.div.scrollTop = pixelOffset % this.rowHeight        
+      
+      this.lastPixelOffset = pixelOffset;
+   },
+   
+   visibleHeight: function() {
+      return parseInt(RicoUtil.getElementsComputedStyle(this.div, 'height'));
+   }
+
+};
+
+
+Rico.LiveGridRequest = Class.create();
+Rico.LiveGridRequest.prototype = {
+   initialize: function( requestOffset, options ) {
+      this.requestOffset = requestOffset;
+   }
+};
+
+// Rico.LiveGrid -----------------------------------------------------
+
+Rico.LiveGrid = Class.create();
+
+Rico.LiveGrid.prototype = {
+
+   initialize: function( tableId, visibleRows, totalRows, url, options, ajaxOptions ) {
+
+     this.options = {
+                tableClass:           $(tableId).className,
+                loadingClass:         $(tableId).className,
+                scrollerBorderRight: '1px solid #ababab',
+                bufferTimeout:        20000,
+                sortAscendImg:        'images/sort_asc.gif',
+                sortDescendImg:       'images/sort_desc.gif',
+                sortImageWidth:       9,
+                sortImageHeight:      5,
+                ajaxSortURLParms:     [],
+                onRefreshComplete:    null,
+                requestParameters:    null,
+                inlineStyles:         true
+                };
+      Object.extend(this.options, options || {});
+
+      this.ajaxOptions = {parameters: null};
+      Object.extend(this.ajaxOptions, ajaxOptions || {});
+
+      this.tableId     = tableId; 
+      this.table       = $(tableId);
+
+      this.addLiveGridHtml();
+
+      var columnCount  = this.table.rows[0].cells.length;
+      this.metaData    = new Rico.LiveGridMetaData(visibleRows, totalRows, columnCount, options);
+      this.buffer      = new Rico.LiveGridBuffer(this.metaData);
+
+      var rowCount = this.table.rows.length;
+      this.viewPort =  new Rico.GridViewPort(this.table, 
+                                            this.table.offsetHeight/rowCount,
+                                            visibleRows,
+                                            this.buffer, this);
+      this.scroller    = new Rico.LiveGridScroller(this,this.viewPort);
+      this.options.sortHandler = this.sortHandler.bind(this);
+
+      if ( $(tableId + '_header') )
+         this.sort = new Rico.LiveGridSort(tableId + '_header', this.options)
+
+      this.processingRequest = null;
+      this.unprocessedRequest = null;
+
+      this.initAjax(url);
+      if ( this.options.prefetchBuffer || this.options.prefetchOffset > 0) {
+         var offset = 0;
+         if (this.options.offset ) {
+            offset = this.options.offset;            
+            this.scroller.moveScroll(offset);
+            this.viewPort.scrollTo(this.scroller.rowToPixel(offset));            
+         }
+         if (this.options.sortCol) {
+             this.sortCol = options.sortCol;
+             this.sortDir = options.sortDir;
+         }
+         this.requestContentRefresh(offset);
+      }
+   },
+
+   addLiveGridHtml: function() {
+     // Check to see if need to create a header table.
+     if (this.table.getElementsByTagName("thead").length > 0){
+       // Create Table this.tableId+'_header'
+       var tableHeader = this.table.cloneNode(true);
+       tableHeader.setAttribute('id', this.tableId+'_header');
+       tableHeader.setAttribute('class', this.table.className+'_header');
+
+       // Clean up and insert
+       for( var i = 0; i < tableHeader.tBodies.length; i++ ) 
+       tableHeader.removeChild(tableHeader.tBodies[i]);
+       this.table.deleteTHead();
+       this.table.parentNode.insertBefore(tableHeader,this.table);
+     }
+
+    new Insertion.Before(this.table, "<div id='"+this.tableId+"_container'></div>");
+    this.table.previousSibling.appendChild(this.table);
+    new Insertion.Before(this.table,"<div id='"+this.tableId+"_viewport' style='float:left;'></div>");
+    this.table.previousSibling.appendChild(this.table);
+   },
+
+
+   resetContents: function() {
+      this.scroller.moveScroll(0);
+      this.buffer.clear();
+      this.viewPort.clearContents();
+   },
+   
+   sortHandler: function(column) {
+	   if(!column) return ;
+      this.sortCol = column.name;
+      this.sortDir = column.currentSort;
+
+      this.resetContents();
+      this.requestContentRefresh(0) 
+   },
+
+   adjustRowSize: function() {
+	  
+	},
+	
+   setTotalRows: function( newTotalRows ) {
+      this.resetContents();
+      this.metaData.setTotalRows(newTotalRows);
+      this.scroller.updateSize();
+   },
+
+   initAjax: function(url) {
+      ajaxEngine.registerRequest( this.tableId + '_request', url );
+      ajaxEngine.registerAjaxObject( this.tableId + '_updater', this );
+   },
+
+   invokeAjax: function() {
+   },
+
+   handleTimedOut: function() {
+      //server did not respond in 4 seconds... assume that there could have been
+      //an error or something, and allow requests to be processed again...
+      this.processingRequest = null;
+      this.processQueuedRequest();
+   },
+
+   fetchBuffer: function(offset) {
+      if ( this.buffer.isInRange(offset) &&
+         !this.buffer.isNearingLimit(offset)) {
+         return;
+         }
+      if (this.processingRequest) {
+          this.unprocessedRequest = new Rico.LiveGridRequest(offset);
+         return;
+      }
+      var bufferStartPos = this.buffer.getFetchOffset(offset);
+      this.processingRequest = new Rico.LiveGridRequest(offset);
+      this.processingRequest.bufferOffset = bufferStartPos;   
+      var fetchSize = this.buffer.getFetchSize(offset);
+      var partialLoaded = false;
+      
+      var queryString
+      if (this.options.requestParameters)
+         queryString = this._createQueryString(this.options.requestParameters, 0);
+
+        queryString = (queryString == null) ? '' : queryString+'&';
+        queryString  = queryString+'id='+this.tableId+'&page_size='+fetchSize+'&offset='+bufferStartPos;
+        if (this.sortCol)
+            queryString = queryString+'&sort_col='+escape(this.sortCol)+'&sort_dir='+this.sortDir;
+
+        this.ajaxOptions.parameters = queryString;
+
+       ajaxEngine.sendRequest( this.tableId + '_request', this.ajaxOptions );
+
+       this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout);
+
+   },
+
+   setRequestParams: function() {
+      this.options.requestParameters = [];
+      for ( var i=0 ; i < arguments.length ; i++ )
+         this.options.requestParameters[i] = arguments[i];
+   },
+
+   requestContentRefresh: function(contentOffset) {
+      this.fetchBuffer(contentOffset);
+   },
+
+   ajaxUpdate: function(ajaxResponse) {
+      try {
+         clearTimeout( this.timeoutHandler );
+         this.buffer.update(ajaxResponse,this.processingRequest.bufferOffset);
+         this.viewPort.bufferChanged();
+      }
+      catch(err) {}
+      finally {this.processingRequest = null; }
+      this.processQueuedRequest();
+   },
+
+   _createQueryString: function( theArgs, offset ) {
+      var queryString = ""
+      if (!theArgs)
+          return queryString;
+
+      for ( var i = offset ; i < theArgs.length ; i++ ) {
+          if ( i != offset )
+            queryString += "&";
+
+          var anArg = theArgs[i];
+
+          if ( anArg.name != undefined && anArg.value != undefined ) {
+            queryString += anArg.name +  "=" + escape(anArg.value);
+          }
+          else {
+             var ePos  = anArg.indexOf('=');
+             var argName  = anArg.substring( 0, ePos );
+             var argValue = anArg.substring( ePos + 1 );
+             queryString += argName + "=" + escape(argValue);
+          }
+      }
+      return queryString;
+   },
+
+   processQueuedRequest: function() {
+      if (this.unprocessedRequest != null) {
+         this.requestContentRefresh(this.unprocessedRequest.requestOffset);
+         this.unprocessedRequest = null
+      }
+   }
+};
+
+
+//-------------------- ricoLiveGridSort.js
+Rico.LiveGridSort = Class.create();
+
+Rico.LiveGridSort.prototype = {
+
+   initialize: function(headerTableId, options) {
+      this.headerTableId = headerTableId;
+      this.headerTable   = $(headerTableId);
+      this.options = options;
+      this.setOptions();
+      this.applySortBehavior();
+
+      if ( this.options.sortCol ) {
+         this.setSortUI( this.options.sortCol, this.options.sortDir );
+      }
+   },
+
+   setSortUI: function( columnName, sortDirection ) {
+      var cols = this.options.columns;
+      for ( var i = 0 ; i < cols.length ; i++ ) {
+         if ( cols[i].name == columnName ) {
+            this.setColumnSort(i, sortDirection);
+            break;
+         }
+      }
+   },
+
+   setOptions: function() {
+      // preload the images...
+      new Image().src = this.options.sortAscendImg;
+      new Image().src = this.options.sortDescendImg;
+
+      this.sort = this.options.sortHandler;
+      if ( !this.options.columns )
+         this.options.columns = this.introspectForColumnInfo();
+      else {
+         // allow client to pass { columns: [ ["a", true], ["b", false] ] }
+         // and convert to an array of Rico.TableColumn objs...
+         this.options.columns = this.convertToTableColumns(this.options.columns);
+      }
+   },
+
+   applySortBehavior: function() {
+      var headerRow   = this.headerTable.rows[0];
+      var headerCells = headerRow.cells;
+      for ( var i = 0 ; i < headerCells.length ; i++ ) {
+         this.addSortBehaviorToColumn( i, headerCells[i] );
+      }
+   },
+
+   addSortBehaviorToColumn: function( n, cell ) {
+      if ( this.options.columns[n].isSortable() ) {
+         cell.id            = this.headerTableId + '_' + n;
+         cell.style.cursor  = 'pointer';
+         cell.onclick       = this.headerCellClicked.bindAsEventListener(this);
+         cell.innerHTML     = cell.innerHTML + '<span id="' + this.headerTableId + '_img_' + n + '">'
+                           + '&nbsp;&nbsp;&nbsp;</span>';
+      }
+   },
+
+   // event handler....
+   headerCellClicked: function(evt) {
+      var eventTarget = evt.target ? evt.target : evt.srcElement;
+      var cellId = eventTarget.id;
+      var columnNumber = parseInt(cellId.substring( cellId.lastIndexOf('_') + 1 ));
+      var sortedColumnIndex = this.getSortedColumnIndex();
+      if ( sortedColumnIndex != -1 ) {
+         if ( sortedColumnIndex != columnNumber ) {
+            this.removeColumnSort(sortedColumnIndex);
+            this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC);
+         }
+         else
+            this.toggleColumnSort(sortedColumnIndex);
+      }
+      else
+         this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC);
+
+      if (this.options.sortHandler) {
+         this.options.sortHandler(this.options.columns[columnNumber]);
+      }
+   },
+
+   removeColumnSort: function(n) {
+      this.options.columns[n].setUnsorted();
+      this.setSortImage(n);
+   },
+
+   setColumnSort: function(n, direction) {
+   	if(isNaN(n)) return ;
+      this.options.columns[n].setSorted(direction);
+      this.setSortImage(n);
+   },
+
+   toggleColumnSort: function(n) {
+      this.options.columns[n].toggleSort();
+      this.setSortImage(n);
+   },
+
+   setSortImage: function(n) {
+      var sortDirection = this.options.columns[n].getSortDirection();
+
+      var sortImageSpan = $( this.headerTableId + '_img_' + n );
+      if ( sortDirection == Rico.TableColumn.UNSORTED )
+         sortImageSpan.innerHTML = '&nbsp;&nbsp;';
+      else if ( sortDirection == Rico.TableColumn.SORT_ASC )
+         sortImageSpan.innerHTML = '&nbsp;&nbsp;<img width="'  + this.options.sortImageWidth    + '" ' +
+                                                     'height="'+ this.options.sortImageHeight   + '" ' +
+                                                     'src="'   + this.options.sortAscendImg + '"/>';
+      else if ( sortDirection == Rico.TableColumn.SORT_DESC )
+         sortImageSpan.innerHTML = '&nbsp;&nbsp;<img width="'  + this.options.sortImageWidth    + '" ' +
+                                                     'height="'+ this.options.sortImageHeight   + '" ' +
+                                                     'src="'   + this.options.sortDescendImg + '"/>';
+   },
+
+   getSortedColumnIndex: function() {
+      var cols = this.options.columns;
+      for ( var i = 0 ; i < cols.length ; i++ ) {
+         if ( cols[i].isSorted() )
+            return i;
+      }
+
+      return -1;
+   },
+
+   introspectForColumnInfo: function() {
+      var columns = new Array();
+      var headerRow   = this.headerTable.rows[0];
+      var headerCells = headerRow.cells;
+      for ( var i = 0 ; i < headerCells.length ; i++ )
+         columns.push( new Rico.TableColumn( this.deriveColumnNameFromCell(headerCells[i],i), true ) );
+      return columns;
+   },
+
+   convertToTableColumns: function(cols) {
+      var columns = new Array();
+      for ( var i = 0 ; i < cols.length ; i++ )
+         columns.push( new Rico.TableColumn( cols[i][0], cols[i][1] ) );
+      return columns;
+   },
+
+   deriveColumnNameFromCell: function(cell,columnNumber) {
+      var cellContent = cell.innerText != undefined ? cell.innerText : cell.textContent;
+      return cellContent ? cellContent.toLowerCase().split(' ').join('_') : "col_" + columnNumber;
+   }
+};
+
+Rico.TableColumn = Class.create();
+
+Rico.TableColumn.UNSORTED  = 0;
+Rico.TableColumn.SORT_ASC  = "ASC";
+Rico.TableColumn.SORT_DESC = "DESC";
+
+Rico.TableColumn.prototype = {
+   initialize: function(name, sortable) {
+      this.name        = name;
+      this.sortable    = sortable;
+      this.currentSort = Rico.TableColumn.UNSORTED;
+   },
+
+   isSortable: function() {
+      return this.sortable;
+   },
+
+   isSorted: function() {
+      return this.currentSort != Rico.TableColumn.UNSORTED;
+   },
+
+   getSortDirection: function() {
+      return this.currentSort;
+   },
+
+   toggleSort: function() {
+      if ( this.currentSort == Rico.TableColumn.UNSORTED || this.currentSort == Rico.TableColumn.SORT_DESC )
+         this.currentSort = Rico.TableColumn.SORT_ASC;
+      else if ( this.currentSort == Rico.TableColumn.SORT_ASC )
+         this.currentSort = Rico.TableColumn.SORT_DESC;
+   },
+
+   setUnsorted: function(direction) {
+      this.setSorted(Rico.TableColumn.UNSORTED);
+   },
+
+   setSorted: function(direction) {
+      // direction must by one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC...
+      this.currentSort = direction;
+   }
+
+};
+
+
+//-------------------- ricoUtil.js
+Rico.ArrayExtensions = new Array();
+
+if (Object.prototype.extend) {
+   // in prototype.js...
+   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
+}else{
+  Object.prototype.extend = function(object) {
+    return Object.extend.apply(this, [this, object]);
+  }
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
+}
+
+if (Array.prototype.push) {
+   // in prototype.js...
+   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push;
+}
+
+if (!Array.prototype.remove) {
+   Array.prototype.remove = function(dx) {
+      if( isNaN(dx) || dx > this.length )
+         return false;
+      for( var i=0,n=0; i<this.length; i++ )
+         if( i != dx )
+            this[n++]=this[i];
+      this.length-=1;
+   };
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.remove;
+}
+
+if (!Array.prototype.removeItem) {
+   Array.prototype.removeItem = function(item) {
+      for ( var i = 0 ; i < this.length ; i++ )
+         if ( this[i] == item ) {
+            this.remove(i);
+            break;
+         }
+   };
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.removeItem;
+}
+
+if (!Array.prototype.indices) {
+   Array.prototype.indices = function() {
+      var indexArray = new Array();
+      for ( index in this ) {
+         var ignoreThis = false;
+         for ( var i = 0 ; i < Rico.ArrayExtensions.length ; i++ ) {
+            if ( this[index] == Rico.ArrayExtensions[i] ) {
+               ignoreThis = true;
+               break;
+            }
+         }
+         if ( !ignoreThis )
+            indexArray[ indexArray.length ] = index;
+      }
+      return indexArray;
+   }
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.indices;
+}
+
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.unique;
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.inArray;
+
+
+// Create the loadXML method and xml getter for Mozilla
+if ( window.DOMParser &&
+	  window.XMLSerializer &&
+	  window.Node && Node.prototype && Node.prototype.__defineGetter__ ) {
+
+   if (!Document.prototype.loadXML) {
+      Document.prototype.loadXML = function (s) {
+         var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
+         while (this.hasChildNodes())
+            this.removeChild(this.lastChild);
+
+         for (var i = 0; i < doc2.childNodes.length; i++) {
+            this.appendChild(this.importNode(doc2.childNodes[i], true));
+         }
+      };
+	}
+
+	Document.prototype.__defineGetter__( "xml",
+	   function () {
+		   return (new XMLSerializer()).serializeToString(this);
+	   }
+	 );
+}
+
+document.getElementsByTagAndClassName = function(tagName, className) {
+  if ( tagName == null )
+     tagName = '*';
+
+  var children = document.getElementsByTagName(tagName) || document.all;
+  var elements = new Array();
+
+  if ( className == null )
+    return children;
+
+  for (var i = 0; i < children.length; i++) {
+    var child = children[i];
+    var classNames = child.className.split(' ');
+    for (var j = 0; j < classNames.length; j++) {
+      if (classNames[j] == className) {
+        elements.push(child);
+        break;
+      }
+    }
+  }
+
+  return elements;
+}
+
+var RicoUtil = {
+
+   getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
+      if ( arguments.length == 2 )
+         mozillaEquivalentCSS = cssProperty;
+
+      var el = $(htmlElement);
+      if ( el.currentStyle )
+         return el.currentStyle[cssProperty];
+      else
+         return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS);
+   },
+
+   createXmlDocument : function() {
+      if (document.implementation && document.implementation.createDocument) {
+         var doc = document.implementation.createDocument("", "", null);
+
+         if (doc.readyState == null) {
+            doc.readyState = 1;
+            doc.addEventListener("load", function () {
+               doc.readyState = 4;
+               if (typeof doc.onreadystatechange == "function")
+                  doc.onreadystatechange();
+            }, false);
+         }
+
+         return doc;
+      }
+
+      if (window.ActiveXObject)
+          return Try.these(
+            function() { return new ActiveXObject('MSXML2.DomDocument')   },
+            function() { return new ActiveXObject('Microsoft.DomDocument')},
+            function() { return new ActiveXObject('MSXML.DomDocument')    },
+            function() { return new ActiveXObject('MSXML3.DomDocument')   }
+          ) || false;
+
+      return null;
+   },
+
+   getContentAsString: function( parentNode ) {
+      return parentNode.xml != undefined ? 
+         this._getContentAsStringIE(parentNode) :
+         this._getContentAsStringMozilla(parentNode);
+   },
+
+  _getContentAsStringIE: function(parentNode) {
+     var contentStr = "";
+     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
+         var n = parentNode.childNodes[i];
+         if (n.nodeType == 4) {
+             contentStr += n.nodeValue;
+         }
+         else {
+           contentStr += n.xml;
+       }
+     }
+     return contentStr;
+  },
+
+  _getContentAsStringMozilla: function(parentNode) {
+     var xmlSerializer = new XMLSerializer();
+     var contentStr = "";
+     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
+          var n = parentNode.childNodes[i];
+          if (n.nodeType == 4) { // CDATA node
+              contentStr += n.nodeValue;
+          }
+          else {
+            contentStr += xmlSerializer.serializeToString(n);
+        }
+     }
+     return contentStr;
+  },
+
+   toViewportPosition: function(element) {
+      return this._toAbsolute(element,true);
+   },
+
+   toDocumentPosition: function(element) {
+      return this._toAbsolute(element,false);
+   },
+
+   /**
+    *  Compute the elements position in terms of the window viewport
+    *  so that it can be compared to the position of the mouse (dnd)
+    *  This is additions of all the offsetTop,offsetLeft values up the
+    *  offsetParent hierarchy, ...taking into account any scrollTop,
+    *  scrollLeft values along the way...
+    *
+    * IE has a bug reporting a correct offsetLeft of elements within a
+    * a relatively positioned parent!!!
+    **/
+   _toAbsolute: function(element,accountForDocScroll) {
+
+      if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
+         return this._toAbsoluteMozilla(element,accountForDocScroll);
+
+      var x = 0;
+      var y = 0;
+      var parent = element;
+      while ( parent ) {
+
+         var borderXOffset = 0;
+         var borderYOffset = 0;
+         if ( parent != element ) {
+            var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" ));
+            var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" ));
+            borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
+            borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
+         }
+
+         x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
+         y += parent.offsetTop - parent.scrollTop + borderYOffset;
+         parent = parent.offsetParent;
+      }
+
+      if ( accountForDocScroll ) {
+         x -= this.docScrollLeft();
+         y -= this.docScrollTop();
+      }
+
+      return { x:x, y:y };
+   },
+
+   /**
+    *  Mozilla did not report all of the parents up the hierarchy via the
+    *  offsetParent property that IE did.  So for the calculation of the
+    *  offsets we use the offsetParent property, but for the calculation of
+    *  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
+    *  property instead so as to get the scroll offsets...
+    *
+    **/
+   _toAbsoluteMozilla: function(element,accountForDocScroll) {
+      var x = 0;
+      var y = 0;
+      var parent = element;
+      while ( parent ) {
+         x += parent.offsetLeft;
+         y += parent.offsetTop;
+         parent = parent.offsetParent;
+      }
+
+      parent = element;
+      while ( parent &&
+              parent != document.body &&
+              parent != document.documentElement ) {
+         if ( parent.scrollLeft  )
+            x -= parent.scrollLeft;
+         if ( parent.scrollTop )
+            y -= parent.scrollTop;
+         parent = parent.parentNode;
+      }
+
+      if ( accountForDocScroll ) {
+         x -= this.docScrollLeft();
+         y -= this.docScrollTop();
+      }
+
+      return { x:x, y:y };
+   },
+
+   docScrollLeft: function() {
+      if ( window.pageXOffset )
+         return window.pageXOffset;
+      else if ( document.documentElement && document.documentElement.scrollLeft )
+         return document.documentElement.scrollLeft;
+      else if ( document.body )
+         return document.body.scrollLeft;
+      else
+         return 0;
+   },
+
+   docScrollTop: function() {
+      if ( window.pageYOffset )
+         return window.pageYOffset;
+      else if ( document.documentElement && document.documentElement.scrollTop )
+         return document.documentElement.scrollTop;
+      else if ( document.body )
+         return document.body.scrollTop;
+      else
+         return 0;
+   }
+
+};

Modified: plog/trunk/js/ui/plogui.js
===================================================================
--- plog/trunk/js/ui/plogui.js	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/js/ui/plogui.js	2006-01-11 10:39:10 UTC (rev 2767)
@@ -102,3 +102,19 @@
 	    $( 'newArticleCategory' ).value = '';
 	}
 }
+
+/**
+ * this function is the one called when clicking the "add category" button
+ */
+function hideOptionPanel()
+{
+	Element.hide($('optionPanel'));
+	startWidth = $('mainPanel').offsetWidth;
+	new Rico.Effect.Size( $('mainPanel'), 660, null, 500, 10, {complete:function() {}} );
+}
+
+function showOptionPanel()
+{
+	startWidth = $('mainPanel').offsetWidth;
+	new Rico.Effect.Size( $('mainPanel'), 489, null, 500, 10, {complete:function() { Element.show($('optionPanel')); }} );
+}
\ No newline at end of file

Modified: plog/trunk/styles/admin.css
===================================================================
--- plog/trunk/styles/admin.css	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/styles/admin.css	2006-01-11 10:39:10 UTC (rev 2767)
@@ -254,7 +254,7 @@
 
 #tablist
 {
-    padding          : 3px 0;
+    padding          : 3px 5px;
     margin-left      : 0;
     font             : 11px trebuchet ms, verdana, tahoma, arial, sans-serif;
     border-bottom    : 1px solid #DEDEDE;
@@ -270,7 +270,7 @@
 #tablist li a
 {
     padding          : 3px 0.5em;
-    margin-left      : 3px;
+    margin-left      : 0px;
     border           : 1px solid #DEDEDE;
     background       : #F0F0F0;
     text-decoration  : none;
@@ -297,6 +297,7 @@
 {
     background       : white;
     border-bottom    : 1px solid white;
+    border-top       : 3px solid #FFCC33;    
 }
 
 .menuTop_level1 ul, li

Modified: plog/trunk/templates/admin/editpost.template
===================================================================
--- plog/trunk/templates/admin/editpost.template	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/templates/admin/editpost.template	2006-01-11 10:39:10 UTC (rev 2767)
@@ -21,7 +21,8 @@
   <form name="newPost" id="editPost" action="admin.php" method="post">
    <fieldset class="inputField">
    <legend>{$locale->tr("editPost")}</legend>
-   <div style="float:left; width: 73%; border-right: 1px solid #DEDEDE;">  
+   <a href="#" onclick="hideOptionPanel()">Hide</a>&nbsp;|&nbsp;<a href="#" onclick="showOptionPanel()">Show</a>
+   <div name="mainPanel" id="mainPanel" style="float:left; width: 73%; border-right: 1px solid #DEDEDE;">  
      {include file="$admintemplatepath/formvalidate.template" message=$locale->tr("error_updating_post")}    
 	 
 	   <div class="field">
@@ -68,7 +69,7 @@
       {include file="$admintemplatepath/newpost_customfields.template" type=2 fields=$customfields}
    </div>	   
    
-   <div style="float:left; width: 23%; margin-left: 8px;">   
+   <div name="optionPanel" id="optionPanel" style="float:left; width: 23%; margin-left: 8px;">   
 	 
 	   <div class="field">
 	     <label for="postSlug">{$locale->tr("post_slug")}</label>

Modified: plog/trunk/templates/admin/header.template
===================================================================
--- plog/trunk/templates/admin/header.template	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/templates/admin/header.template	2006-01-11 10:39:10 UTC (rev 2767)
@@ -30,6 +30,7 @@
 	var plogAdminBaseUrl = "{$url->getBaseUrl(false)}/admin.php";	
 </script>
 <script type="text/javascript" src="js/prototype/prototype.js"></script>
+<script type="text/javascript" src="js/rico/rico.js"></script>
 </head>
 <body>
 
@@ -57,6 +58,9 @@
             {$menu->generateAt("menu",1)}
         </div>
     </div>
+    {if $useDownMenuEnabled}
+        {$menu->generateAt("menu","3","","JavaScript")}
+    {/if}
 
    	<div style="text-align: right;">
    		<img style="vertical-align: middle;" src="imgs/admin/icon_home-24.png" alt="Blog" />&nbsp;

Modified: plog/trunk/templates/admin/newpost.template
===================================================================
--- plog/trunk/templates/admin/newpost.template	2006-01-10 15:27:10 UTC (rev 2766)
+++ plog/trunk/templates/admin/newpost.template	2006-01-11 10:39:10 UTC (rev 2767)
@@ -42,9 +42,9 @@
   <form name="newPost" id="newPost" action="admin.php" method="post">
    <fieldset class="inputField">
    <legend>{$locale->tr("newPost")}</legend>
-   <div style="float:left; width: 73%; border-right: 1px solid #DEDEDE;">
+   <a href="#" onclick="hideOptionPanel()">Hide</a>&nbsp;|&nbsp;<a href="#" onclick="showOptionPanel()">Show</a>
+   <div name="mainPanel" id="mainPanel" style="float:left; width: 73%; border-right: 1px solid #DEDEDE;">
 	   {include file="$admintemplatepath/formvalidate.template" message=$locale->tr("error_adding_post")}  
-	   
 	   <div class="field">
 	     <label for="postTopic">{$locale->tr("topic")}</label>
 		 <span class="required">*</span>
@@ -87,9 +87,9 @@
 	   <!-- text area custom fields -->
       {include file="$admintemplatepath/newpost_customfields.template" type=2 fields=$customfields}
    </div>
+
+   <div name="optionPanel" id="optionPanel" style="float:left; width: 23%; margin-left: 8px;">
    
-   <div style="float:left; width: 23%; margin-left: 8px;">
-   
 	   <div class="field">
 	     <label for="postSlug">{$locale->tr("post_slug")}</label>
 		 <div class="formHelp">{$locale->tr("post_slug_help")}</div>



More information about the pLog-svn mailing list