Index: trunk/CHANGELOG
===================================================================
--- trunk/CHANGELOG	(revision 600)
+++ trunk/CHANGELOG	(revision 601)
@@ -1,749 +1,751 @@
-Change Log
-===============================================================================
-Please note: This change log may not be accurate
-
-$Id$
-
-Legend:
-+ = Added
-- = Removed
-# = Bugfix
-! = Update/Change
-
-------------------------------------- 2.7.0 -------------------------------------
-26-Jan-2008 Matthias Gallas
-+	Added keywords to files where they are missing
-26-Jan-2008 Thomas Hornik
-+	module news: added publish_until field to control the visibility of a news-posting by date and time
-!	added strongly "condensed" fonts with many intersections for ttf-CAPTCHA as default fonts.
-	It should be very difficult for spam-bots to recognize intersected characters.
-25-Jan-2008 Christian Sommer
-!	removed changes introduced with changeset 593 (we need to think off if we want to implement that feature)
-25-Jan-2008 Thomas Hornik
-+	Added new CAPTCHA. Adapted core: signup and modules: news and form.
-+	Added Advanced Spam Protection (ASP) to core: signup and modules news and form.
-#	module news: it was possible to post comments to postings with commenting disabled
-	or to inactive postings. Fixed
-24-Jan-2008 Thomas Hornik
-!	Generate filename in pages/ from page_title instead of menu_title.
-24-Jan-2008 Christian Sommer
-#	fixed bug in Codepress integration (if codepress framework not installed)
-+	added language support to all integrated modules (except for news and form module)
-#	fixed another E_NOTICE warning
-24-Jan-2008 Matthias Gallas
-+	Added keywords to files where they are missing
-23-Jan-2008 Christian Sommer
-+	Added German language file for WB 2.7
-22-Jan-2008 Christian Sommer
-+	Added language support for the Admin tools (backup, reload, mail_filter)
--	Removed all admin tool text outputs from global language file to module language files
-!	Introduced language support for the overview page of the Admin tools
-+	Added function get_variable_content to allow extraction of variable content without including files
-!	Changed language support for Addons -> Moduls -> Details
-22-Jan-2008 Matthias Gallas
-+	Added keywords to new mail filter files
-22-Jan-2008 Thomas Hornik
-+	manage-sections: changed calendar-icon to clock-icon, added second icon to empty date-field.
-21-Jan-2008 Christian Sommer
-!	Removed the manage section icon from the page admin (sections managed by publish by date function) 
-#	Fixed bug with the edit module CSS file implementation (IE7, Opera)
-21-Jan-2008 Thomas Hornik
-+	Unique session-identifier for each website baker installation. fixes #354
-20-Jan-2008 Christian Sommer
-#	fixed E_NOTICE-warning in save.php when input data was wrong
-+	added the new admin tool email output filter which allows to transform
-	emails before displaying them on the frontend
-20-Jan-2008 Thomas Hornik
-!	upgrade-script.php: fixed mysql_error() handling
-!	upgrade-script.php: added code to update menu_links
-#	fixed a typo in upgrade-script.php
-!	updated admin/pages/sections.php to changed jscalendar-api from changeset 571
-!	module menu_link: after adding a menu_link, the anchor dropdown shows "#"; fixed 
-#	fixed E_NOTICE-warning in news-module (about SECTION_ID in comment.php)
-19-Jan-2008 Christian Sommer
-!	Reduced the success time out and set WYSIWSY background to white
-#	Fixed some errors in form module (only in conjunction with E_ALL)
-!	Added support to configure the mailer settings via the backend
-19-Jan-2008 Matthias Gallas
-#	Fixed bug in news modul with missing variable PAGE_DIRECTORY
-+	Added keywords to new files
-19-Jan-2008 Thomas Hornik
--	Module news: removed old calendar
-+	Module news: adapted news to use the new include/jscalendar
-+	added installation-instructions to include/jscalendar
-#	Module code: curly backets get deleted by pparse() from phplib. fixed
-+	Module menu_link: added dropdown-listbox to select anchor
-!	upgrade-script: added code to convert old menu_links to new ones (partly)
-18-Jan-2008 Matthias Gallas
-!	Updated news modul with features from the advanced news modul
-!	Updated form modul with features from the advanced form modul
-#	Fixed hardcoded /pages issue in intro page feature
-+	Added example .htaccess to parse .html files, fixed bug in changeset 555
-!	Revoked parts of changeset 547 and again introduced changeset 540
-+	Added keywords to modules/fckeditor and all other new files
-18-Jan-2008 Thomas Hornik
-#	fixed two E_NOTICE-warnings in admin/pages/index.php
-+	added new module-based search-function
-+	added new publish-by-date code
-17-Jan-2008 Christian Sommer
-#	fixed bug in frontend login and multiple groups (only in conjunction with E_ALL)
-!	removed the PAGE_EXTENSION from frontend login
-17-Jan-2008 Matthias Gallas
-#	Reintroduced changes from changeset 520 and 540 wich where overwritten 
-	by changeset 546
-#	Revoked changeset 541 end removed all PAGE_EXTENSION variables and the related
-	option in WB settings as this function only works on Apache Servers
-	together with .htaccess and mod_rewrite
-+	Added keywords to file menu_link/uninstall.php and new admintools files
-17-Jan-2008 Christian Sommer
-!	added the "users in multiple groups" feature (closes parts of ticket #546)
-16-Jan-2008 Thomas Hornik
-!	changed admin/pages/save.php to allow hidden config-elements in table search.
-!	changed module menu_link: added table mod_menu_link, fixed page-deleting-bug,
-	fixed odd behavior in case of menu_link as default-page.
-#	changing PHP-error-level within website baker does not work - fixed (fixed ticket 532)
-14-Jan-2008 Matthias Gallas
-#	Replaced hardcoded .php with the variable PAGE_EXTENSION for the use in pagenames.
-	(!!Attention!! To get extenions other than .php working additional fixes are necessary!)
-#	Replaced the variable PAGE_EXTENSION with hardcoded .php on all places where the
-	pathes points to WB Corefiles with the page extension .php
-#	Replaced hardcoded text in login.php with language variables (fixes ticket 386)
-14-Jan-2008 Christian Sommer
-!	added a missing </tr> tag
-#	re-introduced bugfix of Changeset 472 (fixes ticket 402) accidentally 
-	removed by Changeset 529 (enhanced pages admin)
-13-Jan-2008 Christian Sommer
-#	moved administration tools from Settings to the new section Admin-Tools
-#	this bugfix closes the ticket 529 (WB upgrades modules with identical version)	
-!	increased redirection time after success message, corrected HOME link of the login panel
-!	updated PHPMailer framework to version 2.0.0 (/include/phpmailer)
-#	this bugfix closes ticket 412 (direct call of admin/settings/save.php clears database)
-#	fixed error with redirection to admin panel after installation
-12-Jan-2008 Christian Sommer
-!	introduced smart URL detection for the 'View' menu in the WB backend
-	(if page_id is set, redirect to this page instead of the root)
-!	introduced the enhanced pages admin dialogue (thanks to icouto)
-11-Jan-2008 Christian Sommer
-+	added the FCKEditor module files missing in Changeset 527 (/modules/fckeditor)
--	removed the hmtlarea files in /modules/htmlarea
-!	set the fckeditor as the new default editor /install/save.php
-!	Updated function: register_frontend_modfiles in /framework/frontend.functions.php
-	(added constants MOD_FRONTEND_CSS_REGISTERED, MOD_FRONTEND_JAVASCRIPT_REGISTERED)
-11-Jan-2008 Matthias Gallas
-!	Update codepress to Version 0.9.6
-!	Update phplib to Version 7.4a
-09-Jan-2008 Matthias Gallas
-!	Upgrade pclzip to the newer more php5 complaint version 2.6, see ticket #526
-#	Fixed ticket #542 (Thanks to Thorn)
-23-Dec-2007 Matthias Gallas
-!	Changed all copyright notices to include now 2008
-21-Sep-2007 Matthias Gallas
-#	Fixed a typo in /framework/frontend.functions.php cause that websites using
-	the new register_frontend_modifles() functions do not validate
-24-Jan-2007 Christian Sommer
-!	Replaced some core files to allow configuration of wbmailer via admin settings panel
-------------------------------------- 2.6.7 -------------------------------------
-02-Jan-2007 Christian Sommer
-+	Added missing converting file for changeset [504]
-------------------------------------- 2.6.6 -------------------------------------
-01-Jul-2007 Matthias Gallas
-#	Fixed converting issue with some ISO-charsets and speeded up converting on large pages (thanks to Thorn)
-01-Jul-2007 Ryan Djurovich
-#	Fixed bug where error message not displayed when script fails to create a page
-#	Fixed bugs introduced in [484]
-14-May-2007 Matthias Gallas
-+	Added support for ISO-8859-6.(Thanks to thorn)
-12-May-2007 Christian Sommer
-+	Replaced functions page_css and page_javascript by register_frontend_modfiles
-+	Added function register_backend_modfiles to allow backend modules to use optional CSS and JS files
-+	Added the JS framework codepress to allow syntax highlighting for JS, HTML, PHP code (textarea)
-+	Added the new function register_frontend_modfiles to the built in templates
-08-May-2007 Matthias Gallas
-+	Added wrapper to functions.php if mb_string is not available (Thanks to thorn)
-+	Added new functions page_css and page_javascript to the built in templates
-+	Added search highlight class to css files of the built in templates
-30-Apr-2007 Matthias Gallas
-#	Fixed ticket #392; root_parent not being set correctly (Thanks to eggsurplus)
-10-Apr-2007 Matthias Gallas
-+	Optimized search and highlighting funktions (Thanks to thorn)
-03-Apr-2007 Matthias Gallas
-#	Fixed a bug in menulink; if you deleted a menulink page the linked page was also deleted
-	in the pages directory (Special thanks to "Funky_MF" for the fix)
-01-Apr-2007 Matthias Gallas
-#	Fixed issues with some languages when UTF8 is used. Removed all htmlentites
-	from the code (revoked changeset 396) and added new functions instead. Adapted the
-	search and the highlighting to use the new functions. With this changes WB can now
-	be used with charset UTF8 for all languages. Very special thanks to Thorn who wrote
-	and tested this changes.
-12-Mar-2007 Matthias Gallas
-+	Added multilingual support to search
-03-Mar-2007 Matthias Gallas
-+	Added new function for highlighting search results in the content area
-	(Special thanks to "thorn" and "Funky_MF")
-#	Fixed some issues in search with special chars
-	(Special thanks to "thorn" and "Funky_MF")
-22-Feb-2007 Christian Sommer
-#	Fixed bug in changeset 428 (page files were not deleted in /pages folder due to a typo in wb/framework/function.php)
-16-Feb-2007 Christian Sommer
-!	Suppressed PHP warnings and errors when reading empty intro.php
-16-Feb-2007 Christian Sommer
-#	Applied fix to enable parsing of PHP code in the intro.php 
-12-Feb-2007 Christian Sommer
-!	Added functions page_css and page_javascript which allow to include optional
-	module files module.css and module.js into the <head> section. This allows to 
-	create valid (X)HTML, as CSS definitions of modules are included in the head section
-12-Feb-2007 Christian Sommer
-!	Security enhancement (reduced number of login trials from 50 to 3)
-02-Feb-2007 Matthias Gallas
-#	Applied fix for ticket #380 (Thanks to pcwacht)
-30-Jan-2007 Ryan Djurovich
-#	Applied fix for ticket #376
-29-Jan-2007 Matthias Gallas
-+	Added new variable $MODULE_DESCRIPTION wich allows to have optional a language
-	specific description of the modules in the language files of the modules.
-21-Jan-2007 Matthias Gallas
-#	Fixed issue in the search table entries of form modul
-13-Jan-2007 Matthias Gallas
-#	Fixed some bugs in rss.php
-07-Jan-2007 Matthias Gallas
-#	Fixed error when uninstalling the news modul
-02-Jan-2007 Matthias Gallas
-+   Added id keywords to all missing files
-#	Fixed issue with Permissions for pages sections
-27-Dec-2006 Matthias Gallas
-#	Fixed form modul stores empty records
-------------------------------------- 2.6.5 -------------------------------------
-25-Dec-2006 Ryan Djurovich
-!	Changed links to WB help website
-24-Dec-2006 Matthias Gallas
-#	Fixed some minor javascript errors in admin (thanks to pcwacht)
-#	Fixed root_parent doesn't get updated when moving item in menu (#305)
-!	Changed all copyright notices to include now 2007
-23-Dec-2006 Matthias Gallas
-#	Fixed IE allows to set a page as parent of itself (#320)
-#	Fixed problem with Page Title has to be escaped (#287)
-#	Fixed the possibility to call the index.php of the templates directly in the browser (#291)
-21-Dec-2006 Matthias Gallas
-#	Fixed missing message when adding a site without page title (#300)
-21-Dec-2006 Ryan Djurovich
-#	Fixed bug with captcha script (#346)
-20-Dec-2006 Matthias Gallas
-#	Fixed Return to Search Results Page (#365)
-!	Set Version to 2.6.5
--	Removed Changeset 365 because this will be a part of WB 2.7.x
-#	Fixed a little typo error in class.admin.php (#364)
-26-Nov-2006 Ryan Djurovich
-+	Added new script that could possibly be apart of 2.7.x as a replacement
-	for having the pages directory 
-17-Nov-2006 Matthias Gallas
-#	Fixed fatal error in line 46 news/comment.php (thanks to eki)(#358)
-15-Nov-2006 Matthias Gallas
-#	Fixed again intropage doesn't work (#71)
-20-Oct-2006 Matthias Gallas
-+	Added phpmailer class (thanks to doc)
-+	Added Remember-expanded-pages-in-admin (thanks to ephraimt)
-+	Added List sorting in admin area (thanks to rsmith)
-#	Fixed table width 100% are wrong displayed in container templates
-	when IE is used (#350)
-#	Fixed large space between menupoints in IE (#349)
-#	Fixed Website description is not displayed when no page description
-	available (#348)
-#	Fixed group images in news modul cannot be stored (#342)
-#	Fixed Typo in form/save_field.php (#341)
-#	Fixed files in /pages/posts folder are not deleted (#340)
-#	Fixed specialchars problem in the news modul (#339)
-#	Fixed news modul stores empty records (#338)
-#	Fixed Captcha didn't work in news comments (#337)
-#	Fixed various php notices (#334)
-+	Added new upgrade_function (thanks to kozmoz)
-#	Fixed addons table isn't updated when a modul is updated (#332)
-#	Fixed spelling errors in the signup2.php (#330)
-#	Fixed MySQL 5.x missing default values for INT fields in all system
-	and modules tables (#329)
-#	Fixed installer does not insert admin user (#328)
-+	Added support for getting page ID of page that referred search
-	request (#327) (thanks to brofield)
-#	Fixed search results generate invalid XHTML and notify warnings (#326)
-Stefan Braunewell
-#	Fixed Field 'last_reset' doesn't have a default value (#313)
-+	Added get_post_escaped to wb class which automatically calls add_slashes
-	on get_post output and used it in form/save_field.php
-------------------------------------- 2.6.4 -------------------------------------
-20-May-2006 Ryan Djurovich
-#	Fixed security issues related to #237
-#	Fixed typo in news save comment page (#282)
-#	Fixed typo in news delete post page (#283)
-!	Added multi-submission protection to form module (#119)
-#	Added notice for when PHP Session Support appears to be disabled in installer,
-	even if it is enabled and the problem actually lies in the users browser (#154)
-19-May-2006 Ryan Djurovich
-#	Fixed problem in forgotten login form where email field is too short (#207)
-#	Fixed typo in forgotten login details email (#190)
-15-May-2006 Stefan Braunewell
-#	Fixed security issues (#237)
-03-May-2006 Stefan Braunewell
-#	Fixed problems with pre-2.6.0 modules in section list
-#	Fixed e-mail bug caused by From: headers in internal mail function calls
-	(#189)
-#	E-mail validation in install script now accepts new top-level domains
-	(#162)
-#	Error status is now overwritten by next database query (#182)
-#	Fixed obscure problem in htmlarea install script
-------------------------------------- 2.6.3 -------------------------------------
-19-Mar-2006 Stefan Braunewell
-#	Fixed ticket #146: problems removing groups from viewers list of a page
-16-Mar-2006 Stefan Braunewell
-!	Changed link target selection. Now self, top and new. (Ticket #145)
-#	Fixed ticket #143. Charset setting is now used in admin login and forgot
-	password pages.
-#	Some changes to the SQL code in the install script for improved 
-	compatibility with new MySQL versions
-#	Fixed ticket #134 - error on browsing media.
-+	Improved backup module: new choice between full database or WB-related
-	backup.
-#	Fixed ticket #141 - admin now sees all folders in media
-#	Fixed e-mail sending problems due to additional parameter in mail call.
-#	Applied fix regarding ticket #138: new users now get deleted if
-	sending e-mail fails.
-#	Fixed ticket #137: Last Reset timer not reset in frontend forgotten 
-	password process
-#	Removed duplicate expression from modules/news/install.php which caused
-	problems in installation
-
-01-Mar-2006 Stefan Braunewell
-#	Fixed ticket #68 - Safari problem with displaying all parents.
-#	Fixed ticket #136 - mailing forgotten password in admin login screen leads 
-	to fatal error.
-!	Renamed tag [PROCESSTIME] to [PROCESS_TIME]
-------------------------------------- 2.6.2 -------------------------------------
-03-Feb-2006 Stefan Braunewell (very big special thanks to John and Alex)
-#	Fixed ticket #104 - commas in form field descriptions not allowed.
-#	Cookie REMEMBER_KEY wasn't cleared in account/logout 
-	and expiration date is now set to time in the past.
-#	Smart login not working.
-!	Changed mail calls to new function $wb->mail.
-#	Form: ticket 124 added captcha settings check 
-#	Ticket 118 added formfield type to check for email/text 
-#	Ticket 115 changed comparision /modules/form/view.php 
-#	Added correct timecheck in form submissions
-!	Changed captcha.php call to include timestamp
-!	Changed the e-mail validation code and added a mail function to class wb.
-!	Forgotten password: if sending of e-mail fails, restore old password.
-	Ticket #110
-!	Added more charset options. Ticket #102 - thanks to ruebenwurzel!
-!	Character encoding setting also affects admin backend.
-	Mentioned in ticket #102.
-!	Reintroduced page languages setting. If switched to off, page
-	languages are only used to determine the language file for the
-	universal texts. If on, displayed page and menu are affected by
-	the language session variable.
-#	Fixed bugs in show_breadcrumbs code and added parameter "depth"
-	to set the number of levels to be show.
-#	Fixed a bug where access settings of registered and private pages
-	were displayed incorrectly.
-#	Fixed captcha being cached.
-#	Added an exit call after every heading("Location:...")
-	redirector to prevent unwanted execution of code.
-#	Bug #122. Replaced empty field check by check that setting name is
-	not "wb_version".
-#	Replaced all remaining occurrences of '/media' by MEDIA_DIRECTORY - bug #108.
-#	Fixed bug #99 and two additional instances of typo 
-	catpcha instead of captcha.
-#	Fixed some typos
-#	Fixed some notices and warnings.
-#	Fixed bug #105 - back buttons in addon detail pages not editable.
-!	Replace 'admin' by 'wb' in all account pages. 
-!/#	Moved print_success and print_error code to class.wb.php.
-	Added correct parameters to these functions in account pages. 
-------------------------------------- 2.6.1 -------------------------------------
-12-Dec-2005 Ryan Djurovich
-#	Fixed security vulnerability in class.login.php
-#	Fixed typo in EN language file
-#	Fixed captcha problems (when feature is disabled) in form module and sign-up
-!	Added charset encoding to admin templates
-!	Added extra code to check for selection of addon upon uninstalling
-#	Fixed bugs in RSS news feeder
-#	Fixed bug with PAGE_DESCRIPTION not being set on any page
-------------------------------------- 2.6.0 -------------------------------------
-28-Nov-2005 Ryan Djurovich
-+	Added default charset option to (advanced) settings
-#	Form module email fields now have email address validation
-#	Fixed spacing in form submissions
-27-Nov-2005 Ryan Djurovich
-+	Added captcha verification to sign-up form
-+	Added Captcha to News module
-24-Nov-2005 Stefan Braunewell
-!	Applied aportale's patch to use label instead of javascript toggle code
-20-Nov-2005 Ryan Djurovich
-!	News mod now hides read more link if no need for it (see ticket #56)
-+	Added support for mailto: links in the menu link mod
-#	Added direct-access redirection on some files (see ticket #37)
-+	Added extra characters to convert.php (see ticket #64)
-#	Fixed ticket #65 (last_reset check in account/forgot_form.php)
-29-Sep-2005 Ryan Djurovich
-!	Cleaned up form buttons in Settings
-!	Moved some options into Advanced Settings
-#	Semi-disabled "separate" page trash option
-+	Created a backup module/tool for backing-up the database
-	(thanks to John (pcwacht) for the original code)
-+	Created new "blank template", which can be used in case where you don't
-	want anything wrapping page-content.
-19-Sep-2005 Ryan Djurovich
-+	Added _license field for all add-ons to specify a license
-!	Renamed _designed_for variables (for all addons) to _platform
-+	Created addons table for faster internal referencing of installed addons
-!	Fixed some links, including the "Help" button in Admin
-#	Used nl2br to display body correctly when viewing form-submissions
-15-Sep-2005 Stefan Braunewell
-+	Added table module with columns 'name','type' and 'directory' as an
-	index.
-+	Added entry to settings table 'wb_version' which holds the version
-	number and can be utilized in future upgrade scripts.
-!	Added upgrade functionality also for templates and languages.
-#/!	Template/modules installation now respects paths.
-!	Moved updates from config.php to database.
-	Created initialize.php (required by config.php) to read settings.
-11-Sep-2005 Stefan Braunewell
-!	HTMLArea is now a module instead of a core component. Files moved
-	from "include" to "modules".
-+	Implemented Installation of modules on top of an older version. This is 
-	done via checking $module_version. Instead of install.php, upgrade.php
-	is then called if it exists in the module package. 
-	For module developers: $module_version and $new_module_version are 
-	accessible in upgrade.php to find out what upgrade steps need to be taken.
-!	Changed column names in mod_news_posts from short,long to content_short,
-	content_long.
-09-Sep-2005 Stefan Braunewell
-+	Added new advanced setting "Rename Files On Upload". File extensions can be
-	given so that respective files will have a ".txt" appended on media upload.
-#	Fixed "None found" message bug when user has no top level page edit
-	rights.
-#	Fixed missing parent option 'none' - ticket #12 - and a minor scope bug.
-+	Added breadcrumbs code. Call using $wb->breadcrumbs().
-+	Added utf-8 character encoding meta tag into all stock templates.
-#	Fixed bug when changing a page's parent
-!	Changed the way blocks are treated. Added new frontend class attribute
-	default_block_content that controls what is shown on pages such as
-	search, login, etc. (Ticket #16)
-+	Added support for WYSIWYG editor modules (wysiwygmod)
-+	When trying to access a registered page, user is automatically redirected
-	there on successful login.
-#	Fixed various issues with system search (mainly related to stripslashes()
-#	Removed stripslashes() in many places in the code. Added check for
-	magic_quotes_gpc to new wb class method add_slashes(). Now database contest
-	is independent of magic_quotes setting..
-05-Sep-2005 Stefan Braunewell
-#	Fixed bug concerning direct access of preferences page.
-#	Reworked page visibility and menu item visibility code (frontend login
-	problem).
-#	Pages in link list in htmlarea popup are now correctly ordered.
-#	Fixed bug where group with existing name can be added.
-04-Sep-2005 Ryan Djurovich
-+	Added and RSS newsfeed script to the News module
-04-Sep-2005 Stefan Braunewell
-!	Rewrote menu function. Parameters are now given as attributes to frontend class.
-#	Fixed some occurrences of potential direct access path disclosure
-#	Added directory check to browse.php to prevent xss exploit by trusted users.
-!	Updated code to reflect move to Subversion repository system.
-27-Aug-2005 Stefan Braunewell
-#	Fixed bugs 4,5,6,8,9 in bug tracker
-!	Removed 'USER_LANGUAGE' and 'GET_LANGUAGE' constants.
-	A GET['lang'] now sets the session language variable.
-26-Aug-2005 Stefan Braunewell
-!	Moved redundant code into the new class functions. Created 
-	'compatibility.php' for backward compatibility with
-	modules and templates. Variables and functions can still be accessed
-	in the old way.
-!/+	Reorganized core frontend files, added new base class 'wb' from
-	which 'admin' and the new 'frontend' class inherit. Moved all frontend
-	function into new class. Completely rewrote core index.php.
-	Now all variables and functions that are available to templates and
-	modules are attributes and methods of the frontend and the wb classes
-------------------------------------- 2.5.2 -------------------------------------
-23-Jun-2005 Ryan Djurovich
-!	create_access_file now creates all parent directories if needed
-#	Fixed bug when moving page with subpages to another level
-#	Fixed bug when saving "Settings" (in Admin) on Windows/IIS
-#	Fixed bug where query was not setting error correctly in class.database.php
-22-Jun-2005 Ryan Djurovich
-#	Fixed bug where template permissions were not saved when a adding group
-21-Jun-2005 Ryan Djurovich
-#	Added htmlspecialchars for modifying WYSIWYG, news, etc. modules (Bug #78)
-#	Fixed language problems in some area's of Admin. (Bug #70)
-#	Added a space in website/page keywords (Bug #69)
-#	Fixed bugs on settings2.php (Bug #52)
-!	Links inserted with HTMLArea now use [wblink--PAGE_ID--] instead of raw URL
-13-Jun-2005 Ryan Djurovich
-#	Fixed bug (#88) with news module
-!	Title of Administration login page now taken from language file (Bug #72)
-#	Fixed redirection admin/home to admin/start on admin/index.php
-#	Fixed bug with forgotten password page in admin (Bug #81)
-25-Apr-2005 Ryan Djurovich
-#	Fixed numerous bugs with module uninstallation
-#	Fixed bug when uploading files in Administration -> Media
-!	Installer no-longer requires you to accept the GNU GPL
-------------------------------------- 2.5.1 -------------------------------------
-16-Apr-2005 Ryan Djurovich
-#	Fixed two bugs with account login/logout
-------------------------------------- 2.5.1 -------------------------------------
-15-Apr-2005 Ryan Djurovich
-#	Fixed bug where non-english characters can get used in page filenames. Many
-	measuers have been added (including a new file: wb/framework/convert.php),
-	to prevent any possible errors that can occur in page filenames.
-#	Fixed invalid meta tags in stock templates (meta tags were not closed)
-#	Removed lines 401, 402, and 425 of wb/index.php - not needed
-#	Fixed bug where search and account pages are shown in every block
-	that is in a template
-#	Fixed numerous bugs with media home directories feature
-10-Apr-2005 Ryan Djurovich
-#	Fixed bugs in account/login.php and logout.php where users
-	gets redirected to /pages
-------------------------------------- 2.5.0 -------------------------------------
-08-Apr-2005 Ryan Djurovich
--	Removed section language feature
-+	Added page language feature (replaces need for sections language feature)
-#	Fixed bug where pages using menu_link module can have the URL changed
--	Page directory no longer stored in link field in pages table, it is now added
-	when the page_link function is called - this makes changing the pages
-	directory much easier and quicker
-!	Pages with visibility of "none" are now no longer directly accessable
-+	Added new visibility setting "hidden", acts exactly like none did previously
-!	Template info file can now specify number of menu's available and relative names
-!	Template info file can now specify number of blocks's available and relative names
-------------------------------------- 2.4.3 -------------------------------------
-07-Apr-2005 Ryan Djurovich
-#	page_filename function has been rewritten using str_replace
-	function, which should be faster and will allow characters
-	from other languages into filenames
-!	Created new media_filename function, which is now used
-	by all media functions (create,upload,rename) to determine
-	which characters should be removed from a desired filename
-+	New button in Administration page list to view specific page
-#	Updated the page_link function to now be compatible with menu link
-	module when setting pages directory to root
-#	Fixed bugs in search when using "Any Words" option
-#	Fixed bug with news module when pages directory set to root
-!	Changed URL of documentation website on Administration Start page
-------------------------------------- 2.4.2 -------------------------------------
-05-Apr-2005 Ryan Djurovich
-#	Fixed bug where file could be renamed to nothing in Media
-!	Optimised Media create folder, upload file, and rename functions
-#	Fixed bug where stripslashes not run on news post titles in admin
-05-Apr-2005 Stefan Braunewell
-#	Fixed bug concerning usage of the private_sql variable
-#	Fixed bug conerning sub-pages being displayed in menus incorrectly
-------------------------------------- 2.4.1 -------------------------------------
-04-Apr-2005 Ryan Djurovich
-!	Pages with visibility of "none" are again directly accessable
-#	Fixed bugs regarding renaming files and directories in Media section
-!	When home folders disabled, all folders now visible in Media section
-------------------------------------- 2.4.0 -------------------------------------
-03-Apr-2005 Ryan Djurovich
--	Removed recently added visibility setting of "heading", and relative config vars
-+	Added new "menu" field to pages table, and new setting "multiple menus"
-	which replaces the need for the menu headings feature
-+	Added links to top of groups and users sections, linking to each other
-!	Change menu width in "Round" template to 170px (was 150px)
-#	Change page "are you sure" deletion message to mention that it will delete
-	all sub-pages as well
-#	Fixed many bugs with news module when viewing posts by group
-!	Pages with visibility of "none" are now no longer directly accessable
-02-Apr-2005 Ryan Djurovich
--	Removed need to specify DB_URL when calling database class
-#	Stopped fields without a type specified from being shown in form mod
-#	Changed '/media' to MEDIA_DIRECTORY on HTMLArea popup windows
-	for insert link and insert image
-+	Added setting which allows you to specify the default WYSIWYG style
-+	Added "Server Email" option, to specify what is used in "From" field when
-	sending emails using the PHP mail function. Default is admins email address.
-#	Search now excludes pages which have a visibility of none or heading
-!	Pages are now given modified_when and modified_by when added
-01-Apr-2005 Ryan Djurovich
-+	Added option to News module to specify how many posts should be listed
-	per page (by default it is set to unlimited, which functions like previous version)
-#	Added stripslashes when display page titles in search
-+	Page descriptions and last updated date now shown in search by default
-30-Mar-2005 Ryan Djurovich
-+	Added new feature for Media home folders, where a folder can be specified
-	for a specific user or group of users only
-!	Changed URL of Help button to http://www.websitebaker.org/docs/
-+	Added new feature for "Page Trash" - two modes available: inline and separate
-!	When pages are deleted, all sub-pages are now deleted (instead of being moved
-	up a level)
-29-Mar-2005 Ryan Djurovich
--	Removed Database Settings from Administration Settings (options will not be
-	available in 3.x, so trying to match interface with WB 3.x plans
-!	Changed the name of Path Settings to Filesystem Settings in Administration
-	Settings, and removed ability to change path/url options - matching interface
-	plans for WB 3.x
-+	Added same options for OS and file permissions as installer to Settings
-!	Sessions now named with APP_NAME.'_session_id' (e.g. default is wb_session_id)
-28-Mar-2005 Ryan Djurovich
-+	New functions available to templates to simplify creation of them. This aims
-	to "future-proof" templates for WB 3.x plans.
-!	Default templates updated to support some of the new functions
-+	Added field to pages table "page_trail" which stores a list of the pages
-	parents. This field was needed by the new page_menu function
-+	Added option for page visibility "heading" to enabled support for multiple
-	menu's. Also, this helps to "future-proof" templates for WB 3.x plans
-+	Added option for page visibility "registered", which acts a little like
-	private, but is still shown in the menu (although users need to log in to view
-	the pages content)
-+	Form module submissions now saved to database, and feature now added to
-	limit number of submissions per hour to prevent spamming
-+	New field for Form module: email. Allows you to specify their email in
-	"from" field on module settings.
-#	Fixed bug when displaying comments in News module (WB Bug #14)
-+	New "under contruction" message if no pages exist
-!	Cleaned-up wb/index.php
-27-Mar-2005 Ryan Djurovich
-+	Created advanced mkdir and chmod functions
-#	Fixed bug in media where wrong file/folder is deleted
-+	Complete overhall of installer - now only one step! It has been greatly
-	simplified in many ways, has much better validation, reports error much more
-	nicely, and automatically logs the user into the Administrations
-+	Must now specify OS type - allows for customizable file permission settings
-+	Sections can now have a language code assigned to them
-+	Sections can now have a block name/id assigned to them
-!	Home section of Administration renamed to Start, to save confusion with saying
-	Homepage (because this term could either mean the main website or Home section
-	in	Administration). Also, this aims to unify the interface with WB 3.x plans
-!	Moved Users and Groups sections under Access section. This aims to unify the
-	interface with WB 3.x plans, which help to unclutter the menu
-!	Interface for Settings section has been imporved for usability purposes
-#	Fixed HTMLArea where no scrollbar for "insert link" and "insert image" dialogs
-	by placing media list inside an iframe
-#	Fixed stripslashes problem for viewing news comments
-!	Added code from Formesque module (an advanced version of the original Form
-	module, modified by Rudolph Lartey from www.carbonect.com), and made further
-	interface improvements for select box/checkbox group/radio group options.
-#	Possibly fixed bugs where an S appears before file and dir modes
-#	Fixed bug where users can be added with same emails (in Administration)
--	Removed support for PEAR, as it was deemed an unnecessary addition which only
-	makes code more bulky, and removing it will decrease package size a lot
-+	New "homepage redirect" option so first page is included and not redirected to
-#	Fixed text not being shown when module uninstalled
-!	Imporved interface for basic group permissions
-26-Mar-2005 Stefan Braunewell
-#	Fixed bug with front-end logins
-#	Fixed bug when saving intro page 
-+	Added check on sign-up if e-mail exists (thanks to P. Melief)
-#	Fixed bugs concerning moving/deleting pages
-#	Fixed list of parents in page settings
-#	Fixed mkdir without mode parameter
-#	Added a check on install to ensure PHP 4.1.x compatibility (thanks to Wanderer)
-+	Added support for PAGES_DIRECTORY set to root
-+	Search form now supports quotes (thanks to Manafta)
-#	Fixed page section ordering
-#	News items are displayed with name of poster instead of "Unknown"
-#	Deletion confirmation popup window in media section now shows correct file name
-------------------------------------- 2.3.1 -------------------------------------
-03-Fed-2005 Ryan Djurovich
-+	Now there is two types of "filesystem modes", one for directories and one
-	for files. Having different settings is very common for most servers, so
-	this addition should fix many problems people had with 2.3.0.
--	Removed filesystem mode options from installation
-!	Removed duplicate text on installation step 3 for timezone
-!	All "access files" for the news module now stored in pages/posts instead
-	of a sub-dir relative to the page (this fixes many bugs)
-#	Fixed major bugs when deleting pages with sub-pages
-!	Removed the restrictions that prevent a user for changing a pages level
-#	Fixed bug with "toggle" plus/minus in IE for pages list
-#	Fixed some bugs where /pages was not replaced with PAGES_DIRECTORY constant
-------------------------------------- 2.3.0 -------------------------------------
-26-Jan-2005 Ryan Djurovich
-#	Fixed bug in framework/functions.php that made root parent always equal 8
-#	Added missing braces in lines 182 and 208 of admin/pages/settings2.php
-#	Fixed SQL-query on line 172, placing a / after $old_link
-#	Added eregi checks for PAGES_DIRECTORY on lines 140 and 150 of
-	admin/pages/settings2.php
-#	Added ordering cleaning to delete_post.php on line 53 for news module
-!	GMT option is selected for "Default Timezone" in the installer, instead
-	of the old "Please select" message (which had the same value as GMT)
-28-Jan-2005 Ryan Djurovich
-!	Ability to specify the chmod number when WB uploads files, etc.
-!	Modified file headers (copyright/license notice) so they now look almost the
-	same, independant of font. Also, it now covers copyright for 2005.
-30-Jan-2005 Ryan Djurovich
-#	Added code on wb/admin/pages/delete.php to remove sections from the sections
-	table when a page is deleted.
-+	New 'Smart Login' prevents users from using external password managers,
-	and can be set to remember the users password using cookies.
-!	SourceForge CVS module now called websitebaker2 (instead of just
-	websitebaker). Also, all file versions have been reset.
-01-Fed-2005 Ryan Djurovich
-!	Added code to prevent from changing a pages level (it simply disables
-	the select box), to prevent many possible bugs.
-------------------------------------- 2.2.4 -------------------------------------
-23-Dec-2004 Ryan Djurovich
-!	Change 'EXACT_PHRASE' to 'EXACT_MATCH' on line 261 of wb/languages/EN.php
-#	Fixed multi-language support in search
-+	Added more detailed options to list of PHP error reporting level's
--	Removed ability to change language and PHP error reporting level
-	on installation to make things easier for newbie's
-+	Ability to select custom spacer for page filename's
-!	Changed the way a language code is found on language installation
-+	Added template permissions to groups
-#	Fixed bug when trying to change email from Preferences (admin and frontend)
-#	Fixed bug with auto-selection of "System Default" for Preferences
-!	News module now use's WYSIWYG for modifying news posts
-+	Ability to specify both the pages and media target directories
------------------------------------- 2.2.3-c ------------------------------------
-22-Dec-2004 Ryan Djurovich
-#	Fixed problem with DB password being reset if Settings saved in basic mode
------------------------------------- 2.2.3-b ------------------------------------
-21-Dec-2004 Ryan Djurovich
-#	Fixed minor bug on admin templates section
-#	Fixed bug on rename.php and rename2.php in admin media section
-------------------------------------- 2.2.3 -------------------------------------
-20-Dec-2004 Ryan Djurovich
-+	Added WB release version in Administration (top right corner)
-!	New option to prevent users from adding level 0 pages
-#	Fixed bug when deleting post's in News module
-+	Added new field in pages table for "root" parent (level 0 parent), for
-	extra flexability in creating templates
-!	Round template now supports unlimited page levels
-------------------------------------- 2.2.2 -------------------------------------
-18-Dec-2004 Ryan Djurovich
-+	New option under Search Settings for selecting custom template for search
-+	New option when changing page settings to set the target
-#	Fixed error when saving a user after editing
-!	Users can now modify sub-page if they dont have permissions on the parent
-------------------------------------- 2.2.1 -------------------------------------
-15-Dec-2004 Ryan Djurovich
-#	Fixed bug when changing password on preferences form (front-end)
-#	Fixed bug when retrieving user details (administration)
-#	Added check to see if module, template, or language is in use when deleting
-#	Fixed up email that is sent to user from a submitted form
-#	Fixed major problem with module permissions which stopped it from working
-------------------------------------- 2.2.0 -------------------------------------
-14-Dec-2004 (Correct date[s] unkown) Ryan Djurovich
-+	Multiple-level page support
-+	Multiple section's for pages (including interface)
-!	Removed text created using two words on all areas
-	(e.g. {Intro} {PAGE} is now {INTRO_PAGE})
-	this is for better language support
-+	Added Languages section
-!	Moved Templates and Modules under Add-ons section
-!	Changed name of "Default" template to "Round"
-+	Added "All CSS" template
-+	Added "Jump"
-+	Added 
-!	Modified "Box" template to support multiple page levels
-+	Added "Menu Link" module
-+	Added "News" module
-+	Added "Code" module
-+	Added "Form" module
-+	Added "Wrapper" module
-!	Changed name of "Normal Page" module to "WYSIWYG"
-+	Created new admin wrapper script to ease module develpment
-+	Media now automatically creates index.php file for every
-	sub-folder made (for security purposes)
-!	Change "Help" link in Administration menu so it now directs
-	to the the new Website Baker documentation website found at:
-	http://www.websitebaker.org/documentation
-!	Password is now required to change email in preferences
-+	User can now select custom Language and Date & Time Formats
-+	Added search functionality, with three different "methods":
-	1. Using all words  2. Using any words  3. Exact match
-+	Added native MySQL database support
+Change Log
+===============================================================================
+Please note: This change log may not be accurate
+
+$Id$
+
+Legend:
++ = Added
+- = Removed
+# = Bugfix
+! = Update/Change
+
+------------------------------------- 2.7.0 -------------------------------------
+26-Jan-2008 Christian Sommer
++	Added Javascript Admin module (developed by Stepan Riha, adapted for WB2.7 by Swen Uth)
+26-Jan-2008 Matthias Gallas
++	Added keywords to files where they are missing
+26-Jan-2008 Thomas Hornik
++	module news: added publish_until field to control the visibility of a news-posting by date and time
+!	added strongly "condensed" fonts with many intersections for ttf-CAPTCHA as default fonts.
+	It should be very difficult for spam-bots to recognize intersected characters.
+25-Jan-2008 Christian Sommer
+!	removed changes introduced with changeset 593 (we need to think off if we want to implement that feature)
+25-Jan-2008 Thomas Hornik
++	Added new CAPTCHA. Adapted core: signup and modules: news and form.
++	Added Advanced Spam Protection (ASP) to core: signup and modules news and form.
+#	module news: it was possible to post comments to postings with commenting disabled
+	or to inactive postings. Fixed
+24-Jan-2008 Thomas Hornik
+!	Generate filename in pages/ from page_title instead of menu_title.
+24-Jan-2008 Christian Sommer
+#	fixed bug in Codepress integration (if codepress framework not installed)
++	added language support to all integrated modules (except for news and form module)
+#	fixed another E_NOTICE warning
+24-Jan-2008 Matthias Gallas
++	Added keywords to files where they are missing
+23-Jan-2008 Christian Sommer
++	Added German language file for WB 2.7
+22-Jan-2008 Christian Sommer
++	Added language support for the Admin tools (backup, reload, mail_filter)
+-	Removed all admin tool text outputs from global language file to module language files
+!	Introduced language support for the overview page of the Admin tools
++	Added function get_variable_content to allow extraction of variable content without including files
+!	Changed language support for Addons -> Moduls -> Details
+22-Jan-2008 Matthias Gallas
++	Added keywords to new mail filter files
+22-Jan-2008 Thomas Hornik
++	manage-sections: changed calendar-icon to clock-icon, added second icon to empty date-field.
+21-Jan-2008 Christian Sommer
+!	Removed the manage section icon from the page admin (sections managed by publish by date function) 
+#	Fixed bug with the edit module CSS file implementation (IE7, Opera)
+21-Jan-2008 Thomas Hornik
++	Unique session-identifier for each website baker installation. fixes #354
+20-Jan-2008 Christian Sommer
+#	fixed E_NOTICE-warning in save.php when input data was wrong
++	added the new admin tool email output filter which allows to transform
+	emails before displaying them on the frontend
+20-Jan-2008 Thomas Hornik
+!	upgrade-script.php: fixed mysql_error() handling
+!	upgrade-script.php: added code to update menu_links
+#	fixed a typo in upgrade-script.php
+!	updated admin/pages/sections.php to changed jscalendar-api from changeset 571
+!	module menu_link: after adding a menu_link, the anchor dropdown shows "#"; fixed 
+#	fixed E_NOTICE-warning in news-module (about SECTION_ID in comment.php)
+19-Jan-2008 Christian Sommer
+!	Reduced the success time out and set WYSIWSY background to white
+#	Fixed some errors in form module (only in conjunction with E_ALL)
+!	Added support to configure the mailer settings via the backend
+19-Jan-2008 Matthias Gallas
+#	Fixed bug in news modul with missing variable PAGE_DIRECTORY
++	Added keywords to new files
+19-Jan-2008 Thomas Hornik
+-	Module news: removed old calendar
++	Module news: adapted news to use the new include/jscalendar
++	added installation-instructions to include/jscalendar
+#	Module code: curly backets get deleted by pparse() from phplib. fixed
++	Module menu_link: added dropdown-listbox to select anchor
+!	upgrade-script: added code to convert old menu_links to new ones (partly)
+18-Jan-2008 Matthias Gallas
+!	Updated news modul with features from the advanced news modul
+!	Updated form modul with features from the advanced form modul
+#	Fixed hardcoded /pages issue in intro page feature
++	Added example .htaccess to parse .html files, fixed bug in changeset 555
+!	Revoked parts of changeset 547 and again introduced changeset 540
++	Added keywords to modules/fckeditor and all other new files
+18-Jan-2008 Thomas Hornik
+#	fixed two E_NOTICE-warnings in admin/pages/index.php
++	added new module-based search-function
++	added new publish-by-date code
+17-Jan-2008 Christian Sommer
+#	fixed bug in frontend login and multiple groups (only in conjunction with E_ALL)
+!	removed the PAGE_EXTENSION from frontend login
+17-Jan-2008 Matthias Gallas
+#	Reintroduced changes from changeset 520 and 540 wich where overwritten 
+	by changeset 546
+#	Revoked changeset 541 end removed all PAGE_EXTENSION variables and the related
+	option in WB settings as this function only works on Apache Servers
+	together with .htaccess and mod_rewrite
++	Added keywords to file menu_link/uninstall.php and new admintools files
+17-Jan-2008 Christian Sommer
+!	added the "users in multiple groups" feature (closes parts of ticket #546)
+16-Jan-2008 Thomas Hornik
+!	changed admin/pages/save.php to allow hidden config-elements in table search.
+!	changed module menu_link: added table mod_menu_link, fixed page-deleting-bug,
+	fixed odd behavior in case of menu_link as default-page.
+#	changing PHP-error-level within website baker does not work - fixed (fixed ticket 532)
+14-Jan-2008 Matthias Gallas
+#	Replaced hardcoded .php with the variable PAGE_EXTENSION for the use in pagenames.
+	(!!Attention!! To get extenions other than .php working additional fixes are necessary!)
+#	Replaced the variable PAGE_EXTENSION with hardcoded .php on all places where the
+	pathes points to WB Corefiles with the page extension .php
+#	Replaced hardcoded text in login.php with language variables (fixes ticket 386)
+14-Jan-2008 Christian Sommer
+!	added a missing </tr> tag
+#	re-introduced bugfix of Changeset 472 (fixes ticket 402) accidentally 
+	removed by Changeset 529 (enhanced pages admin)
+13-Jan-2008 Christian Sommer
+#	moved administration tools from Settings to the new section Admin-Tools
+#	this bugfix closes the ticket 529 (WB upgrades modules with identical version)	
+!	increased redirection time after success message, corrected HOME link of the login panel
+!	updated PHPMailer framework to version 2.0.0 (/include/phpmailer)
+#	this bugfix closes ticket 412 (direct call of admin/settings/save.php clears database)
+#	fixed error with redirection to admin panel after installation
+12-Jan-2008 Christian Sommer
+!	introduced smart URL detection for the 'View' menu in the WB backend
+	(if page_id is set, redirect to this page instead of the root)
+!	introduced the enhanced pages admin dialogue (thanks to icouto)
+11-Jan-2008 Christian Sommer
++	added the FCKEditor module files missing in Changeset 527 (/modules/fckeditor)
+-	removed the hmtlarea files in /modules/htmlarea
+!	set the fckeditor as the new default editor /install/save.php
+!	Updated function: register_frontend_modfiles in /framework/frontend.functions.php
+	(added constants MOD_FRONTEND_CSS_REGISTERED, MOD_FRONTEND_JAVASCRIPT_REGISTERED)
+11-Jan-2008 Matthias Gallas
+!	Update codepress to Version 0.9.6
+!	Update phplib to Version 7.4a
+09-Jan-2008 Matthias Gallas
+!	Upgrade pclzip to the newer more php5 complaint version 2.6, see ticket #526
+#	Fixed ticket #542 (Thanks to Thorn)
+23-Dec-2007 Matthias Gallas
+!	Changed all copyright notices to include now 2008
+21-Sep-2007 Matthias Gallas
+#	Fixed a typo in /framework/frontend.functions.php cause that websites using
+	the new register_frontend_modifles() functions do not validate
+24-Jan-2007 Christian Sommer
+!	Replaced some core files to allow configuration of wbmailer via admin settings panel
+------------------------------------- 2.6.7 -------------------------------------
+02-Jan-2007 Christian Sommer
++	Added missing converting file for changeset [504]
+------------------------------------- 2.6.6 -------------------------------------
+01-Jul-2007 Matthias Gallas
+#	Fixed converting issue with some ISO-charsets and speeded up converting on large pages (thanks to Thorn)
+01-Jul-2007 Ryan Djurovich
+#	Fixed bug where error message not displayed when script fails to create a page
+#	Fixed bugs introduced in [484]
+14-May-2007 Matthias Gallas
++	Added support for ISO-8859-6.(Thanks to thorn)
+12-May-2007 Christian Sommer
++	Replaced functions page_css and page_javascript by register_frontend_modfiles
++	Added function register_backend_modfiles to allow backend modules to use optional CSS and JS files
++	Added the JS framework codepress to allow syntax highlighting for JS, HTML, PHP code (textarea)
++	Added the new function register_frontend_modfiles to the built in templates
+08-May-2007 Matthias Gallas
++	Added wrapper to functions.php if mb_string is not available (Thanks to thorn)
++	Added new functions page_css and page_javascript to the built in templates
++	Added search highlight class to css files of the built in templates
+30-Apr-2007 Matthias Gallas
+#	Fixed ticket #392; root_parent not being set correctly (Thanks to eggsurplus)
+10-Apr-2007 Matthias Gallas
++	Optimized search and highlighting funktions (Thanks to thorn)
+03-Apr-2007 Matthias Gallas
+#	Fixed a bug in menulink; if you deleted a menulink page the linked page was also deleted
+	in the pages directory (Special thanks to "Funky_MF" for the fix)
+01-Apr-2007 Matthias Gallas
+#	Fixed issues with some languages when UTF8 is used. Removed all htmlentites
+	from the code (revoked changeset 396) and added new functions instead. Adapted the
+	search and the highlighting to use the new functions. With this changes WB can now
+	be used with charset UTF8 for all languages. Very special thanks to Thorn who wrote
+	and tested this changes.
+12-Mar-2007 Matthias Gallas
++	Added multilingual support to search
+03-Mar-2007 Matthias Gallas
++	Added new function for highlighting search results in the content area
+	(Special thanks to "thorn" and "Funky_MF")
+#	Fixed some issues in search with special chars
+	(Special thanks to "thorn" and "Funky_MF")
+22-Feb-2007 Christian Sommer
+#	Fixed bug in changeset 428 (page files were not deleted in /pages folder due to a typo in wb/framework/function.php)
+16-Feb-2007 Christian Sommer
+!	Suppressed PHP warnings and errors when reading empty intro.php
+16-Feb-2007 Christian Sommer
+#	Applied fix to enable parsing of PHP code in the intro.php 
+12-Feb-2007 Christian Sommer
+!	Added functions page_css and page_javascript which allow to include optional
+	module files module.css and module.js into the <head> section. This allows to 
+	create valid (X)HTML, as CSS definitions of modules are included in the head section
+12-Feb-2007 Christian Sommer
+!	Security enhancement (reduced number of login trials from 50 to 3)
+02-Feb-2007 Matthias Gallas
+#	Applied fix for ticket #380 (Thanks to pcwacht)
+30-Jan-2007 Ryan Djurovich
+#	Applied fix for ticket #376
+29-Jan-2007 Matthias Gallas
++	Added new variable $MODULE_DESCRIPTION wich allows to have optional a language
+	specific description of the modules in the language files of the modules.
+21-Jan-2007 Matthias Gallas
+#	Fixed issue in the search table entries of form modul
+13-Jan-2007 Matthias Gallas
+#	Fixed some bugs in rss.php
+07-Jan-2007 Matthias Gallas
+#	Fixed error when uninstalling the news modul
+02-Jan-2007 Matthias Gallas
++   Added id keywords to all missing files
+#	Fixed issue with Permissions for pages sections
+27-Dec-2006 Matthias Gallas
+#	Fixed form modul stores empty records
+------------------------------------- 2.6.5 -------------------------------------
+25-Dec-2006 Ryan Djurovich
+!	Changed links to WB help website
+24-Dec-2006 Matthias Gallas
+#	Fixed some minor javascript errors in admin (thanks to pcwacht)
+#	Fixed root_parent doesn't get updated when moving item in menu (#305)
+!	Changed all copyright notices to include now 2007
+23-Dec-2006 Matthias Gallas
+#	Fixed IE allows to set a page as parent of itself (#320)
+#	Fixed problem with Page Title has to be escaped (#287)
+#	Fixed the possibility to call the index.php of the templates directly in the browser (#291)
+21-Dec-2006 Matthias Gallas
+#	Fixed missing message when adding a site without page title (#300)
+21-Dec-2006 Ryan Djurovich
+#	Fixed bug with captcha script (#346)
+20-Dec-2006 Matthias Gallas
+#	Fixed Return to Search Results Page (#365)
+!	Set Version to 2.6.5
+-	Removed Changeset 365 because this will be a part of WB 2.7.x
+#	Fixed a little typo error in class.admin.php (#364)
+26-Nov-2006 Ryan Djurovich
++	Added new script that could possibly be apart of 2.7.x as a replacement
+	for having the pages directory 
+17-Nov-2006 Matthias Gallas
+#	Fixed fatal error in line 46 news/comment.php (thanks to eki)(#358)
+15-Nov-2006 Matthias Gallas
+#	Fixed again intropage doesn't work (#71)
+20-Oct-2006 Matthias Gallas
++	Added phpmailer class (thanks to doc)
++	Added Remember-expanded-pages-in-admin (thanks to ephraimt)
++	Added List sorting in admin area (thanks to rsmith)
+#	Fixed table width 100% are wrong displayed in container templates
+	when IE is used (#350)
+#	Fixed large space between menupoints in IE (#349)
+#	Fixed Website description is not displayed when no page description
+	available (#348)
+#	Fixed group images in news modul cannot be stored (#342)
+#	Fixed Typo in form/save_field.php (#341)
+#	Fixed files in /pages/posts folder are not deleted (#340)
+#	Fixed specialchars problem in the news modul (#339)
+#	Fixed news modul stores empty records (#338)
+#	Fixed Captcha didn't work in news comments (#337)
+#	Fixed various php notices (#334)
++	Added new upgrade_function (thanks to kozmoz)
+#	Fixed addons table isn't updated when a modul is updated (#332)
+#	Fixed spelling errors in the signup2.php (#330)
+#	Fixed MySQL 5.x missing default values for INT fields in all system
+	and modules tables (#329)
+#	Fixed installer does not insert admin user (#328)
++	Added support for getting page ID of page that referred search
+	request (#327) (thanks to brofield)
+#	Fixed search results generate invalid XHTML and notify warnings (#326)
+Stefan Braunewell
+#	Fixed Field 'last_reset' doesn't have a default value (#313)
++	Added get_post_escaped to wb class which automatically calls add_slashes
+	on get_post output and used it in form/save_field.php
+------------------------------------- 2.6.4 -------------------------------------
+20-May-2006 Ryan Djurovich
+#	Fixed security issues related to #237
+#	Fixed typo in news save comment page (#282)
+#	Fixed typo in news delete post page (#283)
+!	Added multi-submission protection to form module (#119)
+#	Added notice for when PHP Session Support appears to be disabled in installer,
+	even if it is enabled and the problem actually lies in the users browser (#154)
+19-May-2006 Ryan Djurovich
+#	Fixed problem in forgotten login form where email field is too short (#207)
+#	Fixed typo in forgotten login details email (#190)
+15-May-2006 Stefan Braunewell
+#	Fixed security issues (#237)
+03-May-2006 Stefan Braunewell
+#	Fixed problems with pre-2.6.0 modules in section list
+#	Fixed e-mail bug caused by From: headers in internal mail function calls
+	(#189)
+#	E-mail validation in install script now accepts new top-level domains
+	(#162)
+#	Error status is now overwritten by next database query (#182)
+#	Fixed obscure problem in htmlarea install script
+------------------------------------- 2.6.3 -------------------------------------
+19-Mar-2006 Stefan Braunewell
+#	Fixed ticket #146: problems removing groups from viewers list of a page
+16-Mar-2006 Stefan Braunewell
+!	Changed link target selection. Now self, top and new. (Ticket #145)
+#	Fixed ticket #143. Charset setting is now used in admin login and forgot
+	password pages.
+#	Some changes to the SQL code in the install script for improved 
+	compatibility with new MySQL versions
+#	Fixed ticket #134 - error on browsing media.
++	Improved backup module: new choice between full database or WB-related
+	backup.
+#	Fixed ticket #141 - admin now sees all folders in media
+#	Fixed e-mail sending problems due to additional parameter in mail call.
+#	Applied fix regarding ticket #138: new users now get deleted if
+	sending e-mail fails.
+#	Fixed ticket #137: Last Reset timer not reset in frontend forgotten 
+	password process
+#	Removed duplicate expression from modules/news/install.php which caused
+	problems in installation
+
+01-Mar-2006 Stefan Braunewell
+#	Fixed ticket #68 - Safari problem with displaying all parents.
+#	Fixed ticket #136 - mailing forgotten password in admin login screen leads 
+	to fatal error.
+!	Renamed tag [PROCESSTIME] to [PROCESS_TIME]
+------------------------------------- 2.6.2 -------------------------------------
+03-Feb-2006 Stefan Braunewell (very big special thanks to John and Alex)
+#	Fixed ticket #104 - commas in form field descriptions not allowed.
+#	Cookie REMEMBER_KEY wasn't cleared in account/logout 
+	and expiration date is now set to time in the past.
+#	Smart login not working.
+!	Changed mail calls to new function $wb->mail.
+#	Form: ticket 124 added captcha settings check 
+#	Ticket 118 added formfield type to check for email/text 
+#	Ticket 115 changed comparision /modules/form/view.php 
+#	Added correct timecheck in form submissions
+!	Changed captcha.php call to include timestamp
+!	Changed the e-mail validation code and added a mail function to class wb.
+!	Forgotten password: if sending of e-mail fails, restore old password.
+	Ticket #110
+!	Added more charset options. Ticket #102 - thanks to ruebenwurzel!
+!	Character encoding setting also affects admin backend.
+	Mentioned in ticket #102.
+!	Reintroduced page languages setting. If switched to off, page
+	languages are only used to determine the language file for the
+	universal texts. If on, displayed page and menu are affected by
+	the language session variable.
+#	Fixed bugs in show_breadcrumbs code and added parameter "depth"
+	to set the number of levels to be show.
+#	Fixed a bug where access settings of registered and private pages
+	were displayed incorrectly.
+#	Fixed captcha being cached.
+#	Added an exit call after every heading("Location:...")
+	redirector to prevent unwanted execution of code.
+#	Bug #122. Replaced empty field check by check that setting name is
+	not "wb_version".
+#	Replaced all remaining occurrences of '/media' by MEDIA_DIRECTORY - bug #108.
+#	Fixed bug #99 and two additional instances of typo 
+	catpcha instead of captcha.
+#	Fixed some typos
+#	Fixed some notices and warnings.
+#	Fixed bug #105 - back buttons in addon detail pages not editable.
+!	Replace 'admin' by 'wb' in all account pages. 
+!/#	Moved print_success and print_error code to class.wb.php.
+	Added correct parameters to these functions in account pages. 
+------------------------------------- 2.6.1 -------------------------------------
+12-Dec-2005 Ryan Djurovich
+#	Fixed security vulnerability in class.login.php
+#	Fixed typo in EN language file
+#	Fixed captcha problems (when feature is disabled) in form module and sign-up
+!	Added charset encoding to admin templates
+!	Added extra code to check for selection of addon upon uninstalling
+#	Fixed bugs in RSS news feeder
+#	Fixed bug with PAGE_DESCRIPTION not being set on any page
+------------------------------------- 2.6.0 -------------------------------------
+28-Nov-2005 Ryan Djurovich
++	Added default charset option to (advanced) settings
+#	Form module email fields now have email address validation
+#	Fixed spacing in form submissions
+27-Nov-2005 Ryan Djurovich
++	Added captcha verification to sign-up form
++	Added Captcha to News module
+24-Nov-2005 Stefan Braunewell
+!	Applied aportale's patch to use label instead of javascript toggle code
+20-Nov-2005 Ryan Djurovich
+!	News mod now hides read more link if no need for it (see ticket #56)
++	Added support for mailto: links in the menu link mod
+#	Added direct-access redirection on some files (see ticket #37)
++	Added extra characters to convert.php (see ticket #64)
+#	Fixed ticket #65 (last_reset check in account/forgot_form.php)
+29-Sep-2005 Ryan Djurovich
+!	Cleaned up form buttons in Settings
+!	Moved some options into Advanced Settings
+#	Semi-disabled "separate" page trash option
++	Created a backup module/tool for backing-up the database
+	(thanks to John (pcwacht) for the original code)
++	Created new "blank template", which can be used in case where you don't
+	want anything wrapping page-content.
+19-Sep-2005 Ryan Djurovich
++	Added _license field for all add-ons to specify a license
+!	Renamed _designed_for variables (for all addons) to _platform
++	Created addons table for faster internal referencing of installed addons
+!	Fixed some links, including the "Help" button in Admin
+#	Used nl2br to display body correctly when viewing form-submissions
+15-Sep-2005 Stefan Braunewell
++	Added table module with columns 'name','type' and 'directory' as an
+	index.
++	Added entry to settings table 'wb_version' which holds the version
+	number and can be utilized in future upgrade scripts.
+!	Added upgrade functionality also for templates and languages.
+#/!	Template/modules installation now respects paths.
+!	Moved updates from config.php to database.
+	Created initialize.php (required by config.php) to read settings.
+11-Sep-2005 Stefan Braunewell
+!	HTMLArea is now a module instead of a core component. Files moved
+	from "include" to "modules".
++	Implemented Installation of modules on top of an older version. This is 
+	done via checking $module_version. Instead of install.php, upgrade.php
+	is then called if it exists in the module package. 
+	For module developers: $module_version and $new_module_version are 
+	accessible in upgrade.php to find out what upgrade steps need to be taken.
+!	Changed column names in mod_news_posts from short,long to content_short,
+	content_long.
+09-Sep-2005 Stefan Braunewell
++	Added new advanced setting "Rename Files On Upload". File extensions can be
+	given so that respective files will have a ".txt" appended on media upload.
+#	Fixed "None found" message bug when user has no top level page edit
+	rights.
+#	Fixed missing parent option 'none' - ticket #12 - and a minor scope bug.
++	Added breadcrumbs code. Call using $wb->breadcrumbs().
++	Added utf-8 character encoding meta tag into all stock templates.
+#	Fixed bug when changing a page's parent
+!	Changed the way blocks are treated. Added new frontend class attribute
+	default_block_content that controls what is shown on pages such as
+	search, login, etc. (Ticket #16)
++	Added support for WYSIWYG editor modules (wysiwygmod)
++	When trying to access a registered page, user is automatically redirected
+	there on successful login.
+#	Fixed various issues with system search (mainly related to stripslashes()
+#	Removed stripslashes() in many places in the code. Added check for
+	magic_quotes_gpc to new wb class method add_slashes(). Now database contest
+	is independent of magic_quotes setting..
+05-Sep-2005 Stefan Braunewell
+#	Fixed bug concerning direct access of preferences page.
+#	Reworked page visibility and menu item visibility code (frontend login
+	problem).
+#	Pages in link list in htmlarea popup are now correctly ordered.
+#	Fixed bug where group with existing name can be added.
+04-Sep-2005 Ryan Djurovich
++	Added and RSS newsfeed script to the News module
+04-Sep-2005 Stefan Braunewell
+!	Rewrote menu function. Parameters are now given as attributes to frontend class.
+#	Fixed some occurrences of potential direct access path disclosure
+#	Added directory check to browse.php to prevent xss exploit by trusted users.
+!	Updated code to reflect move to Subversion repository system.
+27-Aug-2005 Stefan Braunewell
+#	Fixed bugs 4,5,6,8,9 in bug tracker
+!	Removed 'USER_LANGUAGE' and 'GET_LANGUAGE' constants.
+	A GET['lang'] now sets the session language variable.
+26-Aug-2005 Stefan Braunewell
+!	Moved redundant code into the new class functions. Created 
+	'compatibility.php' for backward compatibility with
+	modules and templates. Variables and functions can still be accessed
+	in the old way.
+!/+	Reorganized core frontend files, added new base class 'wb' from
+	which 'admin' and the new 'frontend' class inherit. Moved all frontend
+	function into new class. Completely rewrote core index.php.
+	Now all variables and functions that are available to templates and
+	modules are attributes and methods of the frontend and the wb classes
+------------------------------------- 2.5.2 -------------------------------------
+23-Jun-2005 Ryan Djurovich
+!	create_access_file now creates all parent directories if needed
+#	Fixed bug when moving page with subpages to another level
+#	Fixed bug when saving "Settings" (in Admin) on Windows/IIS
+#	Fixed bug where query was not setting error correctly in class.database.php
+22-Jun-2005 Ryan Djurovich
+#	Fixed bug where template permissions were not saved when a adding group
+21-Jun-2005 Ryan Djurovich
+#	Added htmlspecialchars for modifying WYSIWYG, news, etc. modules (Bug #78)
+#	Fixed language problems in some area's of Admin. (Bug #70)
+#	Added a space in website/page keywords (Bug #69)
+#	Fixed bugs on settings2.php (Bug #52)
+!	Links inserted with HTMLArea now use [wblink--PAGE_ID--] instead of raw URL
+13-Jun-2005 Ryan Djurovich
+#	Fixed bug (#88) with news module
+!	Title of Administration login page now taken from language file (Bug #72)
+#	Fixed redirection admin/home to admin/start on admin/index.php
+#	Fixed bug with forgotten password page in admin (Bug #81)
+25-Apr-2005 Ryan Djurovich
+#	Fixed numerous bugs with module uninstallation
+#	Fixed bug when uploading files in Administration -> Media
+!	Installer no-longer requires you to accept the GNU GPL
+------------------------------------- 2.5.1 -------------------------------------
+16-Apr-2005 Ryan Djurovich
+#	Fixed two bugs with account login/logout
+------------------------------------- 2.5.1 -------------------------------------
+15-Apr-2005 Ryan Djurovich
+#	Fixed bug where non-english characters can get used in page filenames. Many
+	measuers have been added (including a new file: wb/framework/convert.php),
+	to prevent any possible errors that can occur in page filenames.
+#	Fixed invalid meta tags in stock templates (meta tags were not closed)
+#	Removed lines 401, 402, and 425 of wb/index.php - not needed
+#	Fixed bug where search and account pages are shown in every block
+	that is in a template
+#	Fixed numerous bugs with media home directories feature
+10-Apr-2005 Ryan Djurovich
+#	Fixed bugs in account/login.php and logout.php where users
+	gets redirected to /pages
+------------------------------------- 2.5.0 -------------------------------------
+08-Apr-2005 Ryan Djurovich
+-	Removed section language feature
++	Added page language feature (replaces need for sections language feature)
+#	Fixed bug where pages using menu_link module can have the URL changed
+-	Page directory no longer stored in link field in pages table, it is now added
+	when the page_link function is called - this makes changing the pages
+	directory much easier and quicker
+!	Pages with visibility of "none" are now no longer directly accessable
++	Added new visibility setting "hidden", acts exactly like none did previously
+!	Template info file can now specify number of menu's available and relative names
+!	Template info file can now specify number of blocks's available and relative names
+------------------------------------- 2.4.3 -------------------------------------
+07-Apr-2005 Ryan Djurovich
+#	page_filename function has been rewritten using str_replace
+	function, which should be faster and will allow characters
+	from other languages into filenames
+!	Created new media_filename function, which is now used
+	by all media functions (create,upload,rename) to determine
+	which characters should be removed from a desired filename
++	New button in Administration page list to view specific page
+#	Updated the page_link function to now be compatible with menu link
+	module when setting pages directory to root
+#	Fixed bugs in search when using "Any Words" option
+#	Fixed bug with news module when pages directory set to root
+!	Changed URL of documentation website on Administration Start page
+------------------------------------- 2.4.2 -------------------------------------
+05-Apr-2005 Ryan Djurovich
+#	Fixed bug where file could be renamed to nothing in Media
+!	Optimised Media create folder, upload file, and rename functions
+#	Fixed bug where stripslashes not run on news post titles in admin
+05-Apr-2005 Stefan Braunewell
+#	Fixed bug concerning usage of the private_sql variable
+#	Fixed bug conerning sub-pages being displayed in menus incorrectly
+------------------------------------- 2.4.1 -------------------------------------
+04-Apr-2005 Ryan Djurovich
+!	Pages with visibility of "none" are again directly accessable
+#	Fixed bugs regarding renaming files and directories in Media section
+!	When home folders disabled, all folders now visible in Media section
+------------------------------------- 2.4.0 -------------------------------------
+03-Apr-2005 Ryan Djurovich
+-	Removed recently added visibility setting of "heading", and relative config vars
++	Added new "menu" field to pages table, and new setting "multiple menus"
+	which replaces the need for the menu headings feature
++	Added links to top of groups and users sections, linking to each other
+!	Change menu width in "Round" template to 170px (was 150px)
+#	Change page "are you sure" deletion message to mention that it will delete
+	all sub-pages as well
+#	Fixed many bugs with news module when viewing posts by group
+!	Pages with visibility of "none" are now no longer directly accessable
+02-Apr-2005 Ryan Djurovich
+-	Removed need to specify DB_URL when calling database class
+#	Stopped fields without a type specified from being shown in form mod
+#	Changed '/media' to MEDIA_DIRECTORY on HTMLArea popup windows
+	for insert link and insert image
++	Added setting which allows you to specify the default WYSIWYG style
++	Added "Server Email" option, to specify what is used in "From" field when
+	sending emails using the PHP mail function. Default is admins email address.
+#	Search now excludes pages which have a visibility of none or heading
+!	Pages are now given modified_when and modified_by when added
+01-Apr-2005 Ryan Djurovich
++	Added option to News module to specify how many posts should be listed
+	per page (by default it is set to unlimited, which functions like previous version)
+#	Added stripslashes when display page titles in search
++	Page descriptions and last updated date now shown in search by default
+30-Mar-2005 Ryan Djurovich
++	Added new feature for Media home folders, where a folder can be specified
+	for a specific user or group of users only
+!	Changed URL of Help button to http://www.websitebaker.org/docs/
++	Added new feature for "Page Trash" - two modes available: inline and separate
+!	When pages are deleted, all sub-pages are now deleted (instead of being moved
+	up a level)
+29-Mar-2005 Ryan Djurovich
+-	Removed Database Settings from Administration Settings (options will not be
+	available in 3.x, so trying to match interface with WB 3.x plans
+!	Changed the name of Path Settings to Filesystem Settings in Administration
+	Settings, and removed ability to change path/url options - matching interface
+	plans for WB 3.x
++	Added same options for OS and file permissions as installer to Settings
+!	Sessions now named with APP_NAME.'_session_id' (e.g. default is wb_session_id)
+28-Mar-2005 Ryan Djurovich
++	New functions available to templates to simplify creation of them. This aims
+	to "future-proof" templates for WB 3.x plans.
+!	Default templates updated to support some of the new functions
++	Added field to pages table "page_trail" which stores a list of the pages
+	parents. This field was needed by the new page_menu function
++	Added option for page visibility "heading" to enabled support for multiple
+	menu's. Also, this helps to "future-proof" templates for WB 3.x plans
++	Added option for page visibility "registered", which acts a little like
+	private, but is still shown in the menu (although users need to log in to view
+	the pages content)
++	Form module submissions now saved to database, and feature now added to
+	limit number of submissions per hour to prevent spamming
++	New field for Form module: email. Allows you to specify their email in
+	"from" field on module settings.
+#	Fixed bug when displaying comments in News module (WB Bug #14)
++	New "under contruction" message if no pages exist
+!	Cleaned-up wb/index.php
+27-Mar-2005 Ryan Djurovich
++	Created advanced mkdir and chmod functions
+#	Fixed bug in media where wrong file/folder is deleted
++	Complete overhall of installer - now only one step! It has been greatly
+	simplified in many ways, has much better validation, reports error much more
+	nicely, and automatically logs the user into the Administrations
++	Must now specify OS type - allows for customizable file permission settings
++	Sections can now have a language code assigned to them
++	Sections can now have a block name/id assigned to them
+!	Home section of Administration renamed to Start, to save confusion with saying
+	Homepage (because this term could either mean the main website or Home section
+	in	Administration). Also, this aims to unify the interface with WB 3.x plans
+!	Moved Users and Groups sections under Access section. This aims to unify the
+	interface with WB 3.x plans, which help to unclutter the menu
+!	Interface for Settings section has been imporved for usability purposes
+#	Fixed HTMLArea where no scrollbar for "insert link" and "insert image" dialogs
+	by placing media list inside an iframe
+#	Fixed stripslashes problem for viewing news comments
+!	Added code from Formesque module (an advanced version of the original Form
+	module, modified by Rudolph Lartey from www.carbonect.com), and made further
+	interface improvements for select box/checkbox group/radio group options.
+#	Possibly fixed bugs where an S appears before file and dir modes
+#	Fixed bug where users can be added with same emails (in Administration)
+-	Removed support for PEAR, as it was deemed an unnecessary addition which only
+	makes code more bulky, and removing it will decrease package size a lot
++	New "homepage redirect" option so first page is included and not redirected to
+#	Fixed text not being shown when module uninstalled
+!	Imporved interface for basic group permissions
+26-Mar-2005 Stefan Braunewell
+#	Fixed bug with front-end logins
+#	Fixed bug when saving intro page 
++	Added check on sign-up if e-mail exists (thanks to P. Melief)
+#	Fixed bugs concerning moving/deleting pages
+#	Fixed list of parents in page settings
+#	Fixed mkdir without mode parameter
+#	Added a check on install to ensure PHP 4.1.x compatibility (thanks to Wanderer)
++	Added support for PAGES_DIRECTORY set to root
++	Search form now supports quotes (thanks to Manafta)
+#	Fixed page section ordering
+#	News items are displayed with name of poster instead of "Unknown"
+#	Deletion confirmation popup window in media section now shows correct file name
+------------------------------------- 2.3.1 -------------------------------------
+03-Fed-2005 Ryan Djurovich
++	Now there is two types of "filesystem modes", one for directories and one
+	for files. Having different settings is very common for most servers, so
+	this addition should fix many problems people had with 2.3.0.
+-	Removed filesystem mode options from installation
+!	Removed duplicate text on installation step 3 for timezone
+!	All "access files" for the news module now stored in pages/posts instead
+	of a sub-dir relative to the page (this fixes many bugs)
+#	Fixed major bugs when deleting pages with sub-pages
+!	Removed the restrictions that prevent a user for changing a pages level
+#	Fixed bug with "toggle" plus/minus in IE for pages list
+#	Fixed some bugs where /pages was not replaced with PAGES_DIRECTORY constant
+------------------------------------- 2.3.0 -------------------------------------
+26-Jan-2005 Ryan Djurovich
+#	Fixed bug in framework/functions.php that made root parent always equal 8
+#	Added missing braces in lines 182 and 208 of admin/pages/settings2.php
+#	Fixed SQL-query on line 172, placing a / after $old_link
+#	Added eregi checks for PAGES_DIRECTORY on lines 140 and 150 of
+	admin/pages/settings2.php
+#	Added ordering cleaning to delete_post.php on line 53 for news module
+!	GMT option is selected for "Default Timezone" in the installer, instead
+	of the old "Please select" message (which had the same value as GMT)
+28-Jan-2005 Ryan Djurovich
+!	Ability to specify the chmod number when WB uploads files, etc.
+!	Modified file headers (copyright/license notice) so they now look almost the
+	same, independant of font. Also, it now covers copyright for 2005.
+30-Jan-2005 Ryan Djurovich
+#	Added code on wb/admin/pages/delete.php to remove sections from the sections
+	table when a page is deleted.
++	New 'Smart Login' prevents users from using external password managers,
+	and can be set to remember the users password using cookies.
+!	SourceForge CVS module now called websitebaker2 (instead of just
+	websitebaker). Also, all file versions have been reset.
+01-Fed-2005 Ryan Djurovich
+!	Added code to prevent from changing a pages level (it simply disables
+	the select box), to prevent many possible bugs.
+------------------------------------- 2.2.4 -------------------------------------
+23-Dec-2004 Ryan Djurovich
+!	Change 'EXACT_PHRASE' to 'EXACT_MATCH' on line 261 of wb/languages/EN.php
+#	Fixed multi-language support in search
++	Added more detailed options to list of PHP error reporting level's
+-	Removed ability to change language and PHP error reporting level
+	on installation to make things easier for newbie's
++	Ability to select custom spacer for page filename's
+!	Changed the way a language code is found on language installation
++	Added template permissions to groups
+#	Fixed bug when trying to change email from Preferences (admin and frontend)
+#	Fixed bug with auto-selection of "System Default" for Preferences
+!	News module now use's WYSIWYG for modifying news posts
++	Ability to specify both the pages and media target directories
+------------------------------------ 2.2.3-c ------------------------------------
+22-Dec-2004 Ryan Djurovich
+#	Fixed problem with DB password being reset if Settings saved in basic mode
+------------------------------------ 2.2.3-b ------------------------------------
+21-Dec-2004 Ryan Djurovich
+#	Fixed minor bug on admin templates section
+#	Fixed bug on rename.php and rename2.php in admin media section
+------------------------------------- 2.2.3 -------------------------------------
+20-Dec-2004 Ryan Djurovich
++	Added WB release version in Administration (top right corner)
+!	New option to prevent users from adding level 0 pages
+#	Fixed bug when deleting post's in News module
++	Added new field in pages table for "root" parent (level 0 parent), for
+	extra flexability in creating templates
+!	Round template now supports unlimited page levels
+------------------------------------- 2.2.2 -------------------------------------
+18-Dec-2004 Ryan Djurovich
++	New option under Search Settings for selecting custom template for search
++	New option when changing page settings to set the target
+#	Fixed error when saving a user after editing
+!	Users can now modify sub-page if they dont have permissions on the parent
+------------------------------------- 2.2.1 -------------------------------------
+15-Dec-2004 Ryan Djurovich
+#	Fixed bug when changing password on preferences form (front-end)
+#	Fixed bug when retrieving user details (administration)
+#	Added check to see if module, template, or language is in use when deleting
+#	Fixed up email that is sent to user from a submitted form
+#	Fixed major problem with module permissions which stopped it from working
+------------------------------------- 2.2.0 -------------------------------------
+14-Dec-2004 (Correct date[s] unkown) Ryan Djurovich
++	Multiple-level page support
++	Multiple section's for pages (including interface)
+!	Removed text created using two words on all areas
+	(e.g. {Intro} {PAGE} is now {INTRO_PAGE})
+	this is for better language support
++	Added Languages section
+!	Moved Templates and Modules under Add-ons section
+!	Changed name of "Default" template to "Round"
++	Added "All CSS" template
++	Added "Jump"
++	Added 
+!	Modified "Box" template to support multiple page levels
++	Added "Menu Link" module
++	Added "News" module
++	Added "Code" module
++	Added "Form" module
++	Added "Wrapper" module
+!	Changed name of "Normal Page" module to "WYSIWYG"
++	Created new admin wrapper script to ease module develpment
++	Media now automatically creates index.php file for every
+	sub-folder made (for security purposes)
+!	Change "Help" link in Administration menu so it now directs
+	to the the new Website Baker documentation website found at:
+	http://www.websitebaker.org/documentation
+!	Password is now required to change email in preferences
++	User can now select custom Language and Date & Time Formats
++	Added search functionality, with three different "methods":
+	1. Using all words  2. Using any words  3. Exact match
++	Added native MySQL database support
Index: trunk/wb/include/yui/connection/connection.js
===================================================================
--- trunk/wb/include/yui/connection/connection.js	(nonexistent)
+++ trunk/wb/include/yui/connection/connection.js	(revision 601)
@@ -0,0 +1,1369 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+/**
+ * The Connection Manager provides a simplified interface to the XMLHttpRequest
+ * object.  It handles cross-browser instantiantion of XMLHttpRequest, negotiates the
+ * interactive states and server response, returning the results to a pre-defined
+ * callback you create.
+ *
+ * @namespace YAHOO.util
+ * @module connection
+ * @requires yahoo
+ * @requires event
+ */
+
+/**
+ * The Connection Manager singleton provides methods for creating and managing
+ * asynchronous transactions.
+ *
+ * @class Connect
+ */
+
+YAHOO.util.Connect =
+{
+  /**
+   * @description Array of MSFT ActiveX ids for XMLHttpRequest.
+   * @property _msxml_progid
+   * @private
+   * @static
+   * @type array
+   */
+	_msxml_progid:[
+		'Microsoft.XMLHTTP',
+		'MSXML2.XMLHTTP.3.0',
+		'MSXML2.XMLHTTP'
+		],
+
+  /**
+   * @description Object literal of HTTP header(s)
+   * @property _http_header
+   * @private
+   * @static
+   * @type object
+   */
+	_http_headers:{},
+
+  /**
+   * @description Determines if HTTP headers are set.
+   * @property _has_http_headers
+   * @private
+   * @static
+   * @type boolean
+   */
+	_has_http_headers:false,
+
+ /**
+  * @description Determines if a default header of
+  * Content-Type of 'application/x-www-form-urlencoded'
+  * will be added to any client HTTP headers sent for POST
+  * transactions.
+  * @property _use_default_post_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _use_default_post_header:true,
+
+ /**
+  * @description The default header used for POST transactions.
+  * @property _default_post_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',
+
+ /**
+  * @description The default header used for transactions involving the
+  * use of HTML forms.
+  * @property _default_form_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_form_header:'application/x-www-form-urlencoded',
+
+ /**
+  * @description Determines if a default header of
+  * 'X-Requested-With: XMLHttpRequest'
+  * will be added to each transaction.
+  * @property _use_default_xhr_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _use_default_xhr_header:true,
+
+ /**
+  * @description The default header value for the label
+  * "X-Requested-With".  This is sent with each
+  * transaction, by default, to identify the
+  * request as being made by YUI Connection Manager.
+  * @property _default_xhr_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_xhr_header:'XMLHttpRequest',
+
+ /**
+  * @description Determines if custom, default headers
+  * are set for each transaction.
+  * @property _has_default_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _has_default_headers:true,
+
+ /**
+  * @description Determines if custom, default headers
+  * are set for each transaction.
+  * @property _has_default_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_headers:{},
+
+ /**
+  * @description Property modified by setForm() to determine if the data
+  * should be submitted as an HTML form.
+  * @property _isFormSubmit
+  * @private
+  * @static
+  * @type boolean
+  */
+    _isFormSubmit:false,
+
+ /**
+  * @description Property modified by setForm() to determine if a file(s)
+  * upload is expected.
+  * @property _isFileUpload
+  * @private
+  * @static
+  * @type boolean
+  */
+    _isFileUpload:false,
+
+ /**
+  * @description Property modified by setForm() to set a reference to the HTML
+  * form node if the desired action is file upload.
+  * @property _formNode
+  * @private
+  * @static
+  * @type object
+  */
+    _formNode:null,
+
+ /**
+  * @description Property modified by setForm() to set the HTML form data
+  * for each transaction.
+  * @property _sFormData
+  * @private
+  * @static
+  * @type string
+  */
+    _sFormData:null,
+
+ /**
+  * @description Collection of polling references to the polling mechanism in handleReadyState.
+  * @property _poll
+  * @private
+  * @static
+  * @type object
+  */
+    _poll:{},
+
+ /**
+  * @description Queue of timeout values for each transaction callback with a defined timeout value.
+  * @property _timeOut
+  * @private
+  * @static
+  * @type object
+  */
+    _timeOut:{},
+
+  /**
+   * @description The polling frequency, in milliseconds, for HandleReadyState.
+   * when attempting to determine a transaction's XHR readyState.
+   * The default is 50 milliseconds.
+   * @property _polling_interval
+   * @private
+   * @static
+   * @type int
+   */
+     _polling_interval:50,
+
+  /**
+   * @description A transaction counter that increments the transaction id for each transaction.
+   * @property _transaction_id
+   * @private
+   * @static
+   * @type int
+   */
+     _transaction_id:0,
+
+  /**
+   * @description Tracks the name-value pair of the "clicked" submit button if multiple submit
+   * buttons are present in an HTML form; and, if YAHOO.util.Event is available.
+   * @property _submitElementValue
+   * @private
+   * @static
+   * @type string
+   */
+	 _submitElementValue:null,
+
+  /**
+   * @description Determines whether YAHOO.util.Event is available and returns true or false.
+   * If true, an event listener is bound at the document level to trap click events that
+   * resolve to a target type of "Submit".  This listener will enable setForm() to determine
+   * the clicked "Submit" value in a multi-Submit button, HTML form.
+   * @property _hasSubmitListener
+   * @private
+   * @static
+   */
+	 _hasSubmitListener:(function()
+	 {
+		if(YAHOO.util.Event){
+			YAHOO.util.Event.addListener(
+				document,
+				'click',
+				function(e){
+					var obj = YAHOO.util.Event.getTarget(e);
+					if(obj.type && obj.type.toLowerCase() == 'submit'){
+						YAHOO.util.Connect._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
+					}
+				});
+			return true;
+	    }
+	    return false;
+	 })(),
+
+  /**
+   * @description Custom event that fires at the start of a transaction
+   * @property startEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	startEvent: new YAHOO.util.CustomEvent('start'),
+
+  /**
+   * @description Custom event that fires when a transaction response has completed.
+   * @property completeEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	completeEvent: new YAHOO.util.CustomEvent('complete'),
+
+  /**
+   * @description Custom event that fires when handleTransactionResponse() determines a
+   * response in the HTTP 2xx range.
+   * @property successEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	successEvent: new YAHOO.util.CustomEvent('success'),
+
+  /**
+   * @description Custom event that fires when handleTransactionResponse() determines a
+   * response in the HTTP 4xx/5xx range.
+   * @property failureEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	failureEvent: new YAHOO.util.CustomEvent('failure'),
+
+  /**
+   * @description Custom event that fires when handleTransactionResponse() determines a
+   * response in the HTTP 4xx/5xx range.
+   * @property failureEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	uploadEvent: new YAHOO.util.CustomEvent('upload'),
+
+  /**
+   * @description Custom event that fires when a transaction is successfully aborted.
+   * @property abortEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	abortEvent: new YAHOO.util.CustomEvent('abort'),
+
+  /**
+   * @description A reference table that maps callback custom events members to its specific
+   * event name.
+   * @property _customEvents
+   * @private
+   * @static
+   * @type object
+   */
+	_customEvents:
+	{
+		onStart:['startEvent', 'start'],
+		onComplete:['completeEvent', 'complete'],
+		onSuccess:['successEvent', 'success'],
+		onFailure:['failureEvent', 'failure'],
+		onUpload:['uploadEvent', 'upload'],
+		onAbort:['abortEvent', 'abort']
+	},
+
+  /**
+   * @description Member to add an ActiveX id to the existing xml_progid array.
+   * In the event(unlikely) a new ActiveX id is introduced, it can be added
+   * without internal code modifications.
+   * @method setProgId
+   * @public
+   * @static
+   * @param {string} id The ActiveX id to be added to initialize the XHR object.
+   * @return void
+   */
+	setProgId:function(id)
+	{
+		this._msxml_progid.unshift(id);
+	},
+
+  /**
+   * @description Member to override the default POST header.
+   * @method setDefaultPostHeader
+   * @public
+   * @static
+   * @param {boolean} b Set and use default header - true or false .
+   * @return void
+   */
+	setDefaultPostHeader:function(b)
+	{
+		if(typeof b == 'string'){
+			this._default_post_header = b;
+		}
+		else if(typeof b == 'boolean'){
+			this._use_default_post_header = b;
+		}
+	},
+
+  /**
+   * @description Member to override the default transaction header..
+   * @method setDefaultXhrHeader
+   * @public
+   * @static
+   * @param {boolean} b Set and use default header - true or false .
+   * @return void
+   */
+	setDefaultXhrHeader:function(b)
+	{
+		if(typeof b == 'string'){
+			this._default_xhr_header = b;
+		}
+		else{
+			this._use_default_xhr_header = b;
+		}
+	},
+
+  /**
+   * @description Member to modify the default polling interval.
+   * @method setPollingInterval
+   * @public
+   * @static
+   * @param {int} i The polling interval in milliseconds.
+   * @return void
+   */
+	setPollingInterval:function(i)
+	{
+		if(typeof i == 'number' && isFinite(i)){
+			this._polling_interval = i;
+		}
+	},
+
+  /**
+   * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
+   * the XMLHttpRequest instance and the transaction id.
+   * @method createXhrObject
+   * @private
+   * @static
+   * @param {int} transactionId Property containing the transaction id for this transaction.
+   * @return object
+   */
+	createXhrObject:function(transactionId)
+	{
+		var obj,http;
+		try
+		{
+			// Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
+			http = new XMLHttpRequest();
+			//  Object literal with http and tId properties
+			obj = { conn:http, tId:transactionId };
+		}
+		catch(e)
+		{
+			for(var i=0; i<this._msxml_progid.length; ++i){
+				try
+				{
+					// Instantiates XMLHttpRequest for IE and assign to http
+					http = new ActiveXObject(this._msxml_progid[i]);
+					//  Object literal with conn and tId properties
+					obj = { conn:http, tId:transactionId };
+					break;
+				}
+				catch(e){}
+			}
+		}
+		finally
+		{
+			return obj;
+		}
+	},
+
+  /**
+   * @description This method is called by asyncRequest to create a
+   * valid connection object for the transaction.  It also passes a
+   * transaction id and increments the transaction id counter.
+   * @method getConnectionObject
+   * @private
+   * @static
+   * @return {object}
+   */
+	getConnectionObject:function(isFileUpload)
+	{
+		var o;
+		var tId = this._transaction_id;
+
+		try
+		{
+			if(!isFileUpload){
+				o = this.createXhrObject(tId);
+			}
+			else{
+				o = {};
+				o.tId = tId;
+				o.isUpload = true;
+			}
+
+			if(o){
+				this._transaction_id++;
+			}
+		}
+		catch(e){}
+		finally
+		{
+			return o;
+		}
+	},
+
+  /**
+   * @description Method for initiating an asynchronous request via the XHR object.
+   * @method asyncRequest
+   * @public
+   * @static
+   * @param {string} method HTTP transaction method
+   * @param {string} uri Fully qualified path of resource
+   * @param {callback} callback User-defined callback function or object
+   * @param {string} postData POST body
+   * @return {object} Returns the connection object
+   */
+	asyncRequest:function(method, uri, callback, postData)
+	{
+		var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();
+		var args = (callback && callback.argument)?callback.argument:null;
+
+		if(!o){
+			return null;
+		}
+		else{
+
+			// Intialize any transaction-specific custom events, if provided.
+			if(callback && callback.customevents){
+				this.initCustomEvents(o, callback);
+			}
+
+			if(this._isFormSubmit){
+				if(this._isFileUpload){
+					this.uploadFile(o, callback, uri, postData);
+					return o;
+				}
+
+				// If the specified HTTP method is GET, setForm() will return an
+				// encoded string that is concatenated to the uri to
+				// create a querystring.
+				if(method.toUpperCase() == 'GET'){
+					if(this._sFormData.length !== 0){
+						// If the URI already contains a querystring, append an ampersand
+						// and then concatenate _sFormData to the URI.
+						uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
+					}
+				}
+				else if(method.toUpperCase() == 'POST'){
+					// If POST data exist in addition to the HTML form data,
+					// it will be concatenated to the form data.
+					postData = postData?this._sFormData + "&" + postData:this._sFormData;
+				}
+			}
+
+			if(method.toUpperCase() == 'GET' && (callback && callback.cache === false)){
+				// If callback.cache is defined and set to false, a
+				// timestamp value will be added to the querystring.
+				uri += ((uri.indexOf('?') == -1)?'?':'&') + "rnd=" + new Date().valueOf().toString();
+			}
+
+			o.conn.open(method, uri, true);
+
+			// Each transaction will automatically include a custom header of
+			// "X-Requested-With: XMLHttpRequest" to identify the request as
+			// having originated from Connection Manager.
+			if(this._use_default_xhr_header){
+				if(!this._default_headers['X-Requested-With']){
+					this.initHeader('X-Requested-With', this._default_xhr_header, true);
+				}
+			}
+
+			//If the transaction method is POST and the POST header value is set to true
+			//or a custom value, initalize the Content-Type header to this value.
+			if((method.toUpperCase() == 'POST' && this._use_default_post_header) && this._isFormSubmit === false){
+				this.initHeader('Content-Type', this._default_post_header);
+			}
+
+			//Initialize all default and custom HTTP headers,
+			if(this._has_default_headers || this._has_http_headers){
+				this.setHeader(o);
+			}
+
+			this.handleReadyState(o, callback);
+			o.conn.send(postData || null);
+
+
+			// Reset the HTML form data and state properties as
+			// soon as the data are submitted.
+			if(this._isFormSubmit === true){
+				this.resetFormState();
+			}
+
+			// Fire global custom event -- startEvent
+			this.startEvent.fire(o, args);
+
+			if(o.startEvent){
+				// Fire transaction custom event -- startEvent
+				o.startEvent.fire(o, args);
+			}
+
+			return o;
+		}
+	},
+
+  /**
+   * @description This method creates and subscribes custom events,
+   * specific to each transaction
+   * @method initCustomEvents
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {callback} callback The user-defined callback object
+   * @return {void}
+   */
+	initCustomEvents:function(o, callback)
+	{
+		// Enumerate through callback.customevents members and bind/subscribe
+		// events that match in the _customEvents table.
+		for(var prop in callback.customevents){
+			if(this._customEvents[prop][0]){
+				// Create the custom event
+				o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
+
+				// Subscribe the custom event
+				o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
+			}
+		}
+	},
+
+  /**
+   * @description This method serves as a timer that polls the XHR object's readyState
+   * property during a transaction, instead of binding a callback to the
+   * onreadystatechange event.  Upon readyState 4, handleTransactionResponse
+   * will process the response, and the timer will be cleared.
+   * @method handleReadyState
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {callback} callback The user-defined callback object
+   * @return {void}
+   */
+
+    handleReadyState:function(o, callback)
+
+    {
+		var oConn = this;
+		var args = (callback && callback.argument)?callback.argument:null;
+
+		if(callback && callback.timeout){
+			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
+		}
+
+		this._poll[o.tId] = window.setInterval(
+			function(){
+				if(o.conn && o.conn.readyState === 4){
+
+					// Clear the polling interval for the transaction
+					// and remove the reference from _poll.
+					window.clearInterval(oConn._poll[o.tId]);
+					delete oConn._poll[o.tId];
+
+					if(callback && callback.timeout){
+						window.clearTimeout(oConn._timeOut[o.tId]);
+						delete oConn._timeOut[o.tId];
+					}
+
+					// Fire global custom event -- completeEvent
+					oConn.completeEvent.fire(o, args);
+
+					if(o.completeEvent){
+						// Fire transaction custom event -- completeEvent
+						o.completeEvent.fire(o, args);
+					}
+
+					oConn.handleTransactionResponse(o, callback);
+				}
+			}
+		,this._polling_interval);
+    },
+
+  /**
+   * @description This method attempts to interpret the server response and
+   * determine whether the transaction was successful, or if an error or
+   * exception was encountered.
+   * @method handleTransactionResponse
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {object} callback The user-defined callback object
+   * @param {boolean} isAbort Determines if the transaction was terminated via abort().
+   * @return {void}
+   */
+    handleTransactionResponse:function(o, callback, isAbort)
+    {
+		var httpStatus, responseObject;
+		var args = (callback && callback.argument)?callback.argument:null;
+
+		try
+		{
+			if(o.conn.status !== undefined && o.conn.status !== 0){
+				httpStatus = o.conn.status;
+			}
+			else{
+				httpStatus = 13030;
+			}
+		}
+		catch(e){
+
+			 // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
+			 // when the XHR object's status and statusText properties are
+			 // unavailable, and a query attempt throws an exception.
+			httpStatus = 13030;
+		}
+
+		if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){
+			responseObject = this.createResponseObject(o, args);
+			if(callback && callback.success){
+				if(!callback.scope){
+					callback.success(responseObject);
+				}
+				else{
+					// If a scope property is defined, the callback will be fired from
+					// the context of the object.
+					callback.success.apply(callback.scope, [responseObject]);
+				}
+			}
+
+			// Fire global custom event -- successEvent
+			this.successEvent.fire(responseObject);
+
+			if(o.successEvent){
+				// Fire transaction custom event -- successEvent
+				o.successEvent.fire(responseObject);
+			}
+		}
+		else{
+			switch(httpStatus){
+				// The following cases are wininet.dll error codes that may be encountered.
+				case 12002: // Server timeout
+				case 12029: // 12029 to 12031 correspond to dropped connections.
+				case 12030:
+				case 12031:
+				case 12152: // Connection closed by server.
+				case 13030: // See above comments for variable status.
+					responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false));
+					if(callback && callback.failure){
+						if(!callback.scope){
+							callback.failure(responseObject);
+						}
+						else{
+							callback.failure.apply(callback.scope, [responseObject]);
+						}
+					}
+
+					break;
+				default:
+					responseObject = this.createResponseObject(o, args);
+					if(callback && callback.failure){
+						if(!callback.scope){
+							callback.failure(responseObject);
+						}
+						else{
+							callback.failure.apply(callback.scope, [responseObject]);
+						}
+					}
+			}
+
+			// Fire global custom event -- failureEvent
+			this.failureEvent.fire(responseObject);
+
+			if(o.failureEvent){
+				// Fire transaction custom event -- failureEvent
+				o.failureEvent.fire(responseObject);
+			}
+
+		}
+
+		this.releaseObject(o);
+		responseObject = null;
+    },
+
+  /**
+   * @description This method evaluates the server response, creates and returns the results via
+   * its properties.  Success and failure cases will differ in the response
+   * object's property values.
+   * @method createResponseObject
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
+   * @return {object}
+   */
+    createResponseObject:function(o, callbackArg)
+    {
+		var obj = {};
+		var headerObj = {};
+
+		try
+		{
+			var headerStr = o.conn.getAllResponseHeaders();
+			var header = headerStr.split('\n');
+			for(var i=0; i<header.length; i++){
+				var delimitPos = header[i].indexOf(':');
+				if(delimitPos != -1){
+					headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
+				}
+			}
+		}
+		catch(e){}
+
+		obj.tId = o.tId;
+		// Normalize IE's response to HTTP 204 when Win error 1223.
+		obj.status = (o.conn.status == 1223)?204:o.conn.status;
+		// Normalize IE's statusText to "No Content" instead of "Unknown".
+		obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
+		obj.getResponseHeader = headerObj;
+		obj.getAllResponseHeaders = headerStr;
+		obj.responseText = o.conn.responseText;
+		obj.responseXML = o.conn.responseXML;
+
+		if(callbackArg){
+			obj.argument = callbackArg;
+		}
+
+		return obj;
+    },
+
+  /**
+   * @description If a transaction cannot be completed due to dropped or closed connections,
+   * there may be not be enough information to build a full response object.
+   * The failure callback will be fired and this specific condition can be identified
+   * by a status property value of 0.
+   *
+   * If an abort was successful, the status property will report a value of -1.
+   *
+   * @method createExceptionObject
+   * @private
+   * @static
+   * @param {int} tId The Transaction Id
+   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
+   * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
+   * @return {object}
+   */
+    createExceptionObject:function(tId, callbackArg, isAbort)
+    {
+		var COMM_CODE = 0;
+		var COMM_ERROR = 'communication failure';
+		var ABORT_CODE = -1;
+		var ABORT_ERROR = 'transaction aborted';
+
+		var obj = {};
+
+		obj.tId = tId;
+		if(isAbort){
+			obj.status = ABORT_CODE;
+			obj.statusText = ABORT_ERROR;
+		}
+		else{
+			obj.status = COMM_CODE;
+			obj.statusText = COMM_ERROR;
+		}
+
+		if(callbackArg){
+			obj.argument = callbackArg;
+		}
+
+		return obj;
+    },
+
+  /**
+   * @description Method that initializes the custom HTTP headers for the each transaction.
+   * @method initHeader
+   * @public
+   * @static
+   * @param {string} label The HTTP header label
+   * @param {string} value The HTTP header value
+   * @param {string} isDefault Determines if the specific header is a default header
+   * automatically sent with each transaction.
+   * @return {void}
+   */
+	initHeader:function(label, value, isDefault)
+	{
+		var headerObj = (isDefault)?this._default_headers:this._http_headers;
+		headerObj[label] = value;
+
+		if(isDefault){
+			this._has_default_headers = true;
+		}
+		else{
+			this._has_http_headers = true;
+		}
+	},
+
+
+  /**
+   * @description Accessor that sets the HTTP headers for each transaction.
+   * @method setHeader
+   * @private
+   * @static
+   * @param {object} o The connection object for the transaction.
+   * @return {void}
+   */
+	setHeader:function(o)
+	{
+		if(this._has_default_headers){
+			for(var prop in this._default_headers){
+				if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
+					o.conn.setRequestHeader(prop, this._default_headers[prop]);
+				}
+			}
+		}
+
+		if(this._has_http_headers){
+			for(var prop in this._http_headers){
+				if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
+					o.conn.setRequestHeader(prop, this._http_headers[prop]);
+				}
+			}
+			delete this._http_headers;
+
+			this._http_headers = {};
+			this._has_http_headers = false;
+		}
+	},
+
+  /**
+   * @description Resets the default HTTP headers object
+   * @method resetDefaultHeaders
+   * @public
+   * @static
+   * @return {void}
+   */
+	resetDefaultHeaders:function(){
+		delete this._default_headers;
+		this._default_headers = {};
+		this._has_default_headers = false;
+	},
+
+  /**
+   * @description This method assembles the form label and value pairs and
+   * constructs an encoded string.
+   * asyncRequest() will automatically initialize the transaction with a
+   * a HTTP header Content-Type of application/x-www-form-urlencoded.
+   * @method setForm
+   * @public
+   * @static
+   * @param {string || object} form id or name attribute, or form object.
+   * @param {boolean} optional enable file upload.
+   * @param {boolean} optional enable file upload over SSL in IE only.
+   * @return {string} string of the HTML form field name and value pairs..
+   */
+	setForm:function(formId, isUpload, secureUri)
+	{
+		// reset the HTML form data and state properties
+		this.resetFormState();
+
+		var oForm;
+		if(typeof formId == 'string'){
+			// Determine if the argument is a form id or a form name.
+			// Note form name usage is deprecated, but supported
+			// here for backward compatibility.
+			oForm = (document.getElementById(formId) || document.forms[formId]);
+		}
+		else if(typeof formId == 'object'){
+			// Treat argument as an HTML form object.
+			oForm = formId;
+		}
+		else{
+			return;
+		}
+
+		// If the isUpload argument is true, setForm will call createFrame to initialize
+		// an iframe as the form target.
+		//
+		// The argument secureURI is also required by IE in SSL environments
+		// where the secureURI string is a fully qualified HTTP path, used to set the source
+		// of the iframe, to a stub resource in the same domain.
+		if(isUpload){
+
+			// Create iframe in preparation for file upload.
+			var io = this.createFrame(secureUri?secureUri:null);
+			// Set form reference and file upload properties to true.
+			this._isFormSubmit = true;
+			this._isFileUpload = true;
+			this._formNode = oForm;
+
+			return;
+
+		}
+
+		var oElement, oName, oValue, oDisabled;
+		var hasSubmit = false;
+
+		// Iterate over the form elements collection to construct the
+		// label-value pairs.
+		for (var i=0; i<oForm.elements.length; i++){
+			oElement = oForm.elements[i];
+			oDisabled = oElement.disabled;
+			oName = oElement.name;
+			oValue = oElement.value;
+
+			// Do not submit fields that are disabled or
+			// do not have a name attribute value.
+			if(!oDisabled && oName)
+			{
+				switch(oElement.type)
+				{
+					case 'select-one':
+					case 'select-multiple':
+						for(var j=0; j<oElement.options.length; j++){
+							if(oElement.options[j].selected){
+								if(window.ActiveXObject){
+									this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text) + '&';
+								}
+								else{
+									this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&';
+								}
+							}
+						}
+						break;
+					case 'radio':
+					case 'checkbox':
+						if(oElement.checked){
+							this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
+						}
+						break;
+					case 'file':
+						// stub case as XMLHttpRequest will only send the file path as a string.
+					case undefined:
+						// stub case for fieldset element which returns undefined.
+					case 'reset':
+						// stub case for input type reset button.
+					case 'button':
+						// stub case for input type button elements.
+						break;
+					case 'submit':
+						if(hasSubmit === false){
+							if(this._hasSubmitListener && this._submitElementValue){
+								this._sFormData += this._submitElementValue + '&';
+							}
+							else{
+								this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
+							}
+
+							hasSubmit = true;
+						}
+						break;
+					default:
+						this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
+				}
+			}
+		}
+
+		this._isFormSubmit = true;
+		this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);
+
+
+		this.initHeader('Content-Type', this._default_form_header);
+
+		return this._sFormData;
+	},
+
+  /**
+   * @description Resets HTML form properties when an HTML form or HTML form
+   * with file upload transaction is sent.
+   * @method resetFormState
+   * @private
+   * @static
+   * @return {void}
+   */
+	resetFormState:function(){
+		this._isFormSubmit = false;
+		this._isFileUpload = false;
+		this._formNode = null;
+		this._sFormData = "";
+	},
+
+  /**
+   * @description Creates an iframe to be used for form file uploads.  It is remove from the
+   * document upon completion of the upload transaction.
+   * @method createFrame
+   * @private
+   * @static
+   * @param {string} optional qualified path of iframe resource for SSL in IE.
+   * @return {void}
+   */
+	createFrame:function(secureUri){
+
+		// IE does not allow the setting of id and name attributes as object
+		// properties via createElement().  A different iframe creation
+		// pattern is required for IE.
+		var frameId = 'yuiIO' + this._transaction_id;
+		var io;
+		if(window.ActiveXObject){
+			io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
+
+			// IE will throw a security exception in an SSL environment if the
+			// iframe source is undefined.
+			if(typeof secureUri == 'boolean'){
+				io.src = 'javascript:false';
+			}
+			else if(typeof secureURI == 'string'){
+				// Deprecated
+				io.src = secureUri;
+			}
+		}
+		else{
+			io = document.createElement('iframe');
+			io.id = frameId;
+			io.name = frameId;
+		}
+
+		io.style.position = 'absolute';
+		io.style.top = '-1000px';
+		io.style.left = '-1000px';
+
+		document.body.appendChild(io);
+	},
+
+  /**
+   * @description Parses the POST data and creates hidden form elements
+   * for each key-value, and appends them to the HTML form object.
+   * @method appendPostData
+   * @private
+   * @static
+   * @param {string} postData The HTTP POST data
+   * @return {array} formElements Collection of hidden fields.
+   */
+	appendPostData:function(postData)
+	{
+		var formElements = [];
+		var postMessage = postData.split('&');
+		for(var i=0; i < postMessage.length; i++){
+			var delimitPos = postMessage[i].indexOf('=');
+			if(delimitPos != -1){
+				formElements[i] = document.createElement('input');
+				formElements[i].type = 'hidden';
+				formElements[i].name = postMessage[i].substring(0,delimitPos);
+				formElements[i].value = postMessage[i].substring(delimitPos+1);
+				this._formNode.appendChild(formElements[i]);
+			}
+		}
+
+		return formElements;
+	},
+
+  /**
+   * @description Uploads HTML form, inclusive of files/attachments, using the
+   * iframe created in createFrame to facilitate the transaction.
+   * @method uploadFile
+   * @private
+   * @static
+   * @param {int} id The transaction id.
+   * @param {object} callback User-defined callback object.
+   * @param {string} uri Fully qualified path of resource.
+   * @param {string} postData POST data to be submitted in addition to HTML form.
+   * @return {void}
+   */
+	uploadFile:function(o, callback, uri, postData){
+
+		// Each iframe has an id prefix of "yuiIO" followed
+		// by the unique transaction id.
+		var oConn = this;
+		var frameId = 'yuiIO' + o.tId;
+		var uploadEncoding = 'multipart/form-data';
+		var io = document.getElementById(frameId);
+		var args = (callback && callback.argument)?callback.argument:null;
+
+		// Track original HTML form attribute values.
+		var rawFormAttributes =
+		{
+			action:this._formNode.getAttribute('action'),
+			method:this._formNode.getAttribute('method'),
+			target:this._formNode.getAttribute('target')
+		};
+
+		// Initialize the HTML form properties in case they are
+		// not defined in the HTML form.
+		this._formNode.setAttribute('action', uri);
+		this._formNode.setAttribute('method', 'POST');
+		this._formNode.setAttribute('target', frameId);
+
+		if(this._formNode.encoding){
+			// IE does not respect property enctype for HTML forms.
+			// Instead it uses the property - "encoding".
+			this._formNode.setAttribute('encoding', uploadEncoding);
+		}
+		else{
+			this._formNode.setAttribute('enctype', uploadEncoding);
+		}
+
+		if(postData){
+			var oElements = this.appendPostData(postData);
+		}
+
+		// Start file upload.
+		this._formNode.submit();
+
+		// Fire global custom event -- startEvent
+		this.startEvent.fire(o, args);
+
+		if(o.startEvent){
+			// Fire transaction custom event -- startEvent
+			o.startEvent.fire(o, args);
+		}
+
+		// Start polling if a callback is present and the timeout
+		// property has been defined.
+		if(callback && callback.timeout){
+			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
+		}
+
+		// Remove HTML elements created by appendPostData
+		if(oElements && oElements.length > 0){
+			for(var i=0; i < oElements.length; i++){
+				this._formNode.removeChild(oElements[i]);
+			}
+		}
+
+		// Restore HTML form attributes to their original
+		// values prior to file upload.
+		for(var prop in rawFormAttributes){
+			if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
+				if(rawFormAttributes[prop]){
+					this._formNode.setAttribute(prop, rawFormAttributes[prop]);
+				}
+				else{
+					this._formNode.removeAttribute(prop);
+				}
+			}
+		}
+
+		// Reset HTML form state properties.
+		this.resetFormState();
+
+		// Create the upload callback handler that fires when the iframe
+		// receives the load event.  Subsequently, the event handler is detached
+		// and the iframe removed from the document.
+		var uploadCallback = function()
+		{
+			if(callback && callback.timeout){
+				window.clearTimeout(oConn._timeOut[o.tId]);
+				delete oConn._timeOut[o.tId];
+			}
+
+			// Fire global custom event -- completeEvent
+			oConn.completeEvent.fire(o, args);
+
+			if(o.completeEvent){
+				// Fire transaction custom event -- completeEvent
+				o.completeEvent.fire(o, args);
+			}
+
+			var obj = {};
+			obj.tId = o.tId;
+			obj.argument = callback.argument;
+
+			try
+			{
+				// responseText and responseXML will be populated with the same data from the iframe.
+				// Since the HTTP headers cannot be read from the iframe
+				obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
+				obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
+			}
+			catch(e){}
+
+			if(callback && callback.upload){
+				if(!callback.scope){
+					callback.upload(obj);
+				}
+				else{
+					callback.upload.apply(callback.scope, [obj]);
+				}
+			}
+
+			// Fire global custom event -- uploadEvent
+			oConn.uploadEvent.fire(obj);
+
+			if(o.uploadEvent){
+				// Fire transaction custom event -- uploadEvent
+				o.uploadEvent.fire(obj);
+			}
+
+			YAHOO.util.Event.removeListener(io, "load", uploadCallback);
+
+			setTimeout(
+				function(){
+					document.body.removeChild(io);
+					oConn.releaseObject(o);
+				}, 100);
+		};
+
+		// Bind the onload handler to the iframe to detect the file upload response.
+		YAHOO.util.Event.addListener(io, "load", uploadCallback);
+	},
+
+  /**
+   * @description Method to terminate a transaction, if it has not reached readyState 4.
+   * @method abort
+   * @public
+   * @static
+   * @param {object} o The connection object returned by asyncRequest.
+   * @param {object} callback  User-defined callback object.
+   * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
+   * @return {boolean}
+   */
+	abort:function(o, callback, isTimeout)
+	{
+		var abortStatus;
+		var args = (callback && callback.argument)?callback.argument:null;
+
+
+		if(o && o.conn){
+			if(this.isCallInProgress(o)){
+				// Issue abort request
+				o.conn.abort();
+
+				window.clearInterval(this._poll[o.tId]);
+				delete this._poll[o.tId];
+
+				if(isTimeout){
+					window.clearTimeout(this._timeOut[o.tId]);
+					delete this._timeOut[o.tId];
+				}
+
+				abortStatus = true;
+			}
+		}
+		else if(o && o.isUpload === true){
+			var frameId = 'yuiIO' + o.tId;
+			var io = document.getElementById(frameId);
+
+			if(io){
+				// Remove all listeners on the iframe prior to
+				// its destruction.
+				YAHOO.util.Event.removeListener(io, "load");
+				// Destroy the iframe facilitating the transaction.
+				document.body.removeChild(io);
+
+				if(isTimeout){
+					window.clearTimeout(this._timeOut[o.tId]);
+					delete this._timeOut[o.tId];
+				}
+
+				abortStatus = true;
+			}
+		}
+		else{
+			abortStatus = false;
+		}
+
+		if(abortStatus === true){
+			// Fire global custom event -- abortEvent
+			this.abortEvent.fire(o, args);
+
+			if(o.abortEvent){
+				// Fire transaction custom event -- abortEvent
+				o.abortEvent.fire(o, args);
+			}
+
+			this.handleTransactionResponse(o, callback, true);
+		}
+
+		return abortStatus;
+	},
+
+  /**
+   * @description Determines if the transaction is still being processed.
+   * @method isCallInProgress
+   * @public
+   * @static
+   * @param {object} o The connection object returned by asyncRequest
+   * @return {boolean}
+   */
+	isCallInProgress:function(o)
+	{
+		// if the XHR object assigned to the transaction has not been dereferenced,
+		// then check its readyState status.  Otherwise, return false.
+		if(o && o.conn){
+			return o.conn.readyState !== 4 && o.conn.readyState !== 0;
+		}
+		else if(o && o.isUpload === true){
+			var frameId = 'yuiIO' + o.tId;
+			return document.getElementById(frameId)?true:false;
+		}
+		else{
+			return false;
+		}
+	},
+
+  /**
+   * @description Dereference the XHR instance and the connection object after the transaction is completed.
+   * @method releaseObject
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @return {void}
+   */
+	releaseObject:function(o)
+	{
+		if(o && o.conn){
+			//dereference the XHR instance.
+			o.conn = null;
+
+
+			//dereference the connection object.
+			o = null;
+		}
+	}
+};
+
+YAHOO.register("connection", YAHOO.util.Connect, {version: "2.4.1", build: "742"});
Index: trunk/wb/include/yui/connection/connection-min.js
===================================================================
--- trunk/wb/include/yui/connection/connection-min.js	(nonexistent)
+++ trunk/wb/include/yui/connection/connection-min.js	(revision 601)
@@ -0,0 +1,8 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+YAHOO.util.Connect={_msxml_progid:["Microsoft.XMLHTTP","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP"],_http_headers:{},_has_http_headers:false,_use_default_post_header:true,_default_post_header:"application/x-www-form-urlencoded; charset=UTF-8",_default_form_header:"application/x-www-form-urlencoded",_use_default_xhr_header:true,_default_xhr_header:"XMLHttpRequest",_has_default_headers:true,_default_headers:{},_isFormSubmit:false,_isFileUpload:false,_formNode:null,_sFormData:null,_poll:{},_timeOut:{},_polling_interval:50,_transaction_id:0,_submitElementValue:null,_hasSubmitListener:(function(){if(YAHOO.util.Event){YAHOO.util.Event.addListener(document,"click",function(B){var A=YAHOO.util.Event.getTarget(B);if(A.type&&A.type.toLowerCase()=="submit"){YAHOO.util.Connect._submitElementValue=encodeURIComponent(A.name)+"="+encodeURIComponent(A.value);}});return true;}return false;})(),startEvent:new YAHOO.util.CustomEvent("start"),completeEvent:new YAHOO.util.CustomEvent("complete"),successEvent:new YAHOO.util.CustomEvent("success"),failureEvent:new YAHOO.util.CustomEvent("failure"),uploadEvent:new YAHOO.util.CustomEvent("upload"),abortEvent:new YAHOO.util.CustomEvent("abort"),_customEvents:{onStart:["startEvent","start"],onComplete:["completeEvent","complete"],onSuccess:["successEvent","success"],onFailure:["failureEvent","failure"],onUpload:["uploadEvent","upload"],onAbort:["abortEvent","abort"]},setProgId:function(A){this._msxml_progid.unshift(A);},setDefaultPostHeader:function(A){if(typeof A=="string"){this._default_post_header=A;}else{if(typeof A=="boolean"){this._use_default_post_header=A;}}},setDefaultXhrHeader:function(A){if(typeof A=="string"){this._default_xhr_header=A;}else{this._use_default_xhr_header=A;}},setPollingInterval:function(A){if(typeof A=="number"&&isFinite(A)){this._polling_interval=A;}},createXhrObject:function(E){var D,A;try{A=new XMLHttpRequest();D={conn:A,tId:E};}catch(C){for(var B=0;B<this._msxml_progid.length;++B){try{A=new ActiveXObject(this._msxml_progid[B]);D={conn:A,tId:E};break;}catch(C){}}}finally{return D;}},getConnectionObject:function(A){var C;var D=this._transaction_id;try{if(!A){C=this.createXhrObject(D);}else{C={};C.tId=D;C.isUpload=true;}if(C){this._transaction_id++;}}catch(B){}finally{return C;}},asyncRequest:function(F,C,E,A){var D=(this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();var B=(E&&E.argument)?E.argument:null;if(!D){return null;}else{if(E&&E.customevents){this.initCustomEvents(D,E);}if(this._isFormSubmit){if(this._isFileUpload){this.uploadFile(D,E,C,A);return D;}if(F.toUpperCase()=="GET"){if(this._sFormData.length!==0){C+=((C.indexOf("?")==-1)?"?":"&")+this._sFormData;}}else{if(F.toUpperCase()=="POST"){A=A?this._sFormData+"&"+A:this._sFormData;}}}if(F.toUpperCase()=="GET"&&(E&&E.cache===false)){C+=((C.indexOf("?")==-1)?"?":"&")+"rnd="+new Date().valueOf().toString();}D.conn.open(F,C,true);if(this._use_default_xhr_header){if(!this._default_headers["X-Requested-With"]){this.initHeader("X-Requested-With",this._default_xhr_header,true);}}if((F.toUpperCase()=="POST"&&this._use_default_post_header)&&this._isFormSubmit===false){this.initHeader("Content-Type",this._default_post_header);}if(this._has_default_headers||this._has_http_headers){this.setHeader(D);}this.handleReadyState(D,E);D.conn.send(A||null);if(this._isFormSubmit===true){this.resetFormState();}this.startEvent.fire(D,B);if(D.startEvent){D.startEvent.fire(D,B);}return D;}},initCustomEvents:function(A,C){for(var B in C.customevents){if(this._customEvents[B][0]){A[this._customEvents[B][0]]=new YAHOO.util.CustomEvent(this._customEvents[B][1],(C.scope)?C.scope:null);A[this._customEvents[B][0]].subscribe(C.customevents[B]);}}},handleReadyState:function(C,D){var B=this;var A=(D&&D.argument)?D.argument:null;if(D&&D.timeout){this._timeOut[C.tId]=window.setTimeout(function(){B.abort(C,D,true);},D.timeout);}this._poll[C.tId]=window.setInterval(function(){if(C.conn&&C.conn.readyState===4){window.clearInterval(B._poll[C.tId]);delete B._poll[C.tId];if(D&&D.timeout){window.clearTimeout(B._timeOut[C.tId]);delete B._timeOut[C.tId];}B.completeEvent.fire(C,A);if(C.completeEvent){C.completeEvent.fire(C,A);}B.handleTransactionResponse(C,D);}},this._polling_interval);},handleTransactionResponse:function(F,G,A){var D,C;var B=(G&&G.argument)?G.argument:null;try{if(F.conn.status!==undefined&&F.conn.status!==0){D=F.conn.status;}else{D=13030;}}catch(E){D=13030;}if(D>=200&&D<300||D===1223){C=this.createResponseObject(F,B);if(G&&G.success){if(!G.scope){G.success(C);}else{G.success.apply(G.scope,[C]);}}this.successEvent.fire(C);if(F.successEvent){F.successEvent.fire(C);}}else{switch(D){case 12002:case 12029:case 12030:case 12031:case 12152:case 13030:C=this.createExceptionObject(F.tId,B,(A?A:false));if(G&&G.failure){if(!G.scope){G.failure(C);}else{G.failure.apply(G.scope,[C]);}}break;default:C=this.createResponseObject(F,B);if(G&&G.failure){if(!G.scope){G.failure(C);}else{G.failure.apply(G.scope,[C]);}}}this.failureEvent.fire(C);if(F.failureEvent){F.failureEvent.fire(C);}}this.releaseObject(F);C=null;},createResponseObject:function(A,G){var D={};var I={};try{var C=A.conn.getAllResponseHeaders();var F=C.split("\n");for(var E=0;E<F.length;E++){var B=F[E].indexOf(":");if(B!=-1){I[F[E].substring(0,B)]=F[E].substring(B+2);}}}catch(H){}D.tId=A.tId;D.status=(A.conn.status==1223)?204:A.conn.status;D.statusText=(A.conn.status==1223)?"No Content":A.conn.statusText;D.getResponseHeader=I;D.getAllResponseHeaders=C;D.responseText=A.conn.responseText;D.responseXML=A.conn.responseXML;if(G){D.argument=G;}return D;},createExceptionObject:function(H,D,A){var F=0;var G="communication failure";var C=-1;var B="transaction aborted";var E={};E.tId=H;if(A){E.status=C;E.statusText=B;}else{E.status=F;E.statusText=G;}if(D){E.argument=D;}return E;},initHeader:function(A,D,C){var B=(C)?this._default_headers:this._http_headers;B[A]=D;if(C){this._has_default_headers=true;}else{this._has_http_headers=true;}},setHeader:function(A){if(this._has_default_headers){for(var B in this._default_headers){if(YAHOO.lang.hasOwnProperty(this._default_headers,B)){A.conn.setRequestHeader(B,this._default_headers[B]);
+}}}if(this._has_http_headers){for(var B in this._http_headers){if(YAHOO.lang.hasOwnProperty(this._http_headers,B)){A.conn.setRequestHeader(B,this._http_headers[B]);}}delete this._http_headers;this._http_headers={};this._has_http_headers=false;}},resetDefaultHeaders:function(){delete this._default_headers;this._default_headers={};this._has_default_headers=false;},setForm:function(K,E,B){this.resetFormState();var J;if(typeof K=="string"){J=(document.getElementById(K)||document.forms[K]);}else{if(typeof K=="object"){J=K;}else{return ;}}if(E){var F=this.createFrame(B?B:null);this._isFormSubmit=true;this._isFileUpload=true;this._formNode=J;return ;}var A,I,G,L;var H=false;for(var D=0;D<J.elements.length;D++){A=J.elements[D];L=A.disabled;I=A.name;G=A.value;if(!L&&I){switch(A.type){case"select-one":case"select-multiple":for(var C=0;C<A.options.length;C++){if(A.options[C].selected){if(window.ActiveXObject){this._sFormData+=encodeURIComponent(I)+"="+encodeURIComponent(A.options[C].attributes["value"].specified?A.options[C].value:A.options[C].text)+"&";}else{this._sFormData+=encodeURIComponent(I)+"="+encodeURIComponent(A.options[C].hasAttribute("value")?A.options[C].value:A.options[C].text)+"&";}}}break;case"radio":case"checkbox":if(A.checked){this._sFormData+=encodeURIComponent(I)+"="+encodeURIComponent(G)+"&";}break;case"file":case undefined:case"reset":case"button":break;case"submit":if(H===false){if(this._hasSubmitListener&&this._submitElementValue){this._sFormData+=this._submitElementValue+"&";}else{this._sFormData+=encodeURIComponent(I)+"="+encodeURIComponent(G)+"&";}H=true;}break;default:this._sFormData+=encodeURIComponent(I)+"="+encodeURIComponent(G)+"&";}}}this._isFormSubmit=true;this._sFormData=this._sFormData.substr(0,this._sFormData.length-1);this.initHeader("Content-Type",this._default_form_header);return this._sFormData;},resetFormState:function(){this._isFormSubmit=false;this._isFileUpload=false;this._formNode=null;this._sFormData="";},createFrame:function(A){var B="yuiIO"+this._transaction_id;var C;if(window.ActiveXObject){C=document.createElement("<iframe id=\""+B+"\" name=\""+B+"\" />");if(typeof A=="boolean"){C.src="javascript:false";}else{if(typeof secureURI=="string"){C.src=A;}}}else{C=document.createElement("iframe");C.id=B;C.name=B;}C.style.position="absolute";C.style.top="-1000px";C.style.left="-1000px";document.body.appendChild(C);},appendPostData:function(A){var D=[];var B=A.split("&");for(var C=0;C<B.length;C++){var E=B[C].indexOf("=");if(E!=-1){D[C]=document.createElement("input");D[C].type="hidden";D[C].name=B[C].substring(0,E);D[C].value=B[C].substring(E+1);this._formNode.appendChild(D[C]);}}return D;},uploadFile:function(D,M,E,C){var N=this;var H="yuiIO"+D.tId;var I="multipart/form-data";var K=document.getElementById(H);var J=(M&&M.argument)?M.argument:null;var B={action:this._formNode.getAttribute("action"),method:this._formNode.getAttribute("method"),target:this._formNode.getAttribute("target")};this._formNode.setAttribute("action",E);this._formNode.setAttribute("method","POST");this._formNode.setAttribute("target",H);if(this._formNode.encoding){this._formNode.setAttribute("encoding",I);}else{this._formNode.setAttribute("enctype",I);}if(C){var L=this.appendPostData(C);}this._formNode.submit();this.startEvent.fire(D,J);if(D.startEvent){D.startEvent.fire(D,J);}if(M&&M.timeout){this._timeOut[D.tId]=window.setTimeout(function(){N.abort(D,M,true);},M.timeout);}if(L&&L.length>0){for(var G=0;G<L.length;G++){this._formNode.removeChild(L[G]);}}for(var A in B){if(YAHOO.lang.hasOwnProperty(B,A)){if(B[A]){this._formNode.setAttribute(A,B[A]);}else{this._formNode.removeAttribute(A);}}}this.resetFormState();var F=function(){if(M&&M.timeout){window.clearTimeout(N._timeOut[D.tId]);delete N._timeOut[D.tId];}N.completeEvent.fire(D,J);if(D.completeEvent){D.completeEvent.fire(D,J);}var P={};P.tId=D.tId;P.argument=M.argument;try{P.responseText=K.contentWindow.document.body?K.contentWindow.document.body.innerHTML:K.contentWindow.document.documentElement.textContent;P.responseXML=K.contentWindow.document.XMLDocument?K.contentWindow.document.XMLDocument:K.contentWindow.document;}catch(O){}if(M&&M.upload){if(!M.scope){M.upload(P);}else{M.upload.apply(M.scope,[P]);}}N.uploadEvent.fire(P);if(D.uploadEvent){D.uploadEvent.fire(P);}YAHOO.util.Event.removeListener(K,"load",F);setTimeout(function(){document.body.removeChild(K);N.releaseObject(D);},100);};YAHOO.util.Event.addListener(K,"load",F);},abort:function(E,G,A){var D;var B=(G&&G.argument)?G.argument:null;if(E&&E.conn){if(this.isCallInProgress(E)){E.conn.abort();window.clearInterval(this._poll[E.tId]);delete this._poll[E.tId];if(A){window.clearTimeout(this._timeOut[E.tId]);delete this._timeOut[E.tId];}D=true;}}else{if(E&&E.isUpload===true){var C="yuiIO"+E.tId;var F=document.getElementById(C);if(F){YAHOO.util.Event.removeListener(F,"load");document.body.removeChild(F);if(A){window.clearTimeout(this._timeOut[E.tId]);delete this._timeOut[E.tId];}D=true;}}else{D=false;}}if(D===true){this.abortEvent.fire(E,B);if(E.abortEvent){E.abortEvent.fire(E,B);}this.handleTransactionResponse(E,G,true);}return D;},isCallInProgress:function(B){if(B&&B.conn){return B.conn.readyState!==4&&B.conn.readyState!==0;}else{if(B&&B.isUpload===true){var A="yuiIO"+B.tId;return document.getElementById(A)?true:false;}else{return false;}}},releaseObject:function(A){if(A&&A.conn){A.conn=null;A=null;}}};YAHOO.register("connection",YAHOO.util.Connect,{version:"2.4.1",build:"742"});
\ No newline at end of file
Index: trunk/wb/include/yui/connection/README
===================================================================
--- trunk/wb/include/yui/connection/README	(nonexistent)
+++ trunk/wb/include/yui/connection/README	(revision 601)
@@ -0,0 +1,259 @@
+Connection Manager Release Notes
+
+*** version 2.4.1 ***
+
+No change
+
+*** version 2.4.0 ***
+
+* [FIXED] SF1804153.  Transactions initialized with setForm() now properly clear
+the POST data field after each transaction.
+
+* The callback object can accept a new member, cache, defined with a boolean
+value.  If set to false (e.g., var callback = { cache:false };), a timestamp
+will be appended to the URI to override HTTP GET caching.  This timestamp value
+will appear as rnd=timestamp in the request querystring.
+
+* Custom Events startEvent, completeEvent, and abortEvent now receive
+callback.argument, if defined, in addition to the transaction ID.  Each Custom
+Event's function handler receives two arguments -- the event type as the first
+argument, and an array as the second argument.  The first element in the array
+is the transaction ID, and the second element are any arguments defined in the
+callback object.
+
+*** version 2.3.1 ***
+
+* setDefaultPostHeader() can now be overloaded with a boolean, string, or
+number.  By default, POST transactions send the following Content-Type header:
+'application/x-www-form-urlencoded; charset=UTF-8'.
+
+A custom Content-Type header can now be set by passing its value to
+setDefaultPostHeader().
+
+* HTML form submissions now send a Content-Type header of "application/x-www-
+form-urlencoded", omitting the charset=UTF-8 value.
+
+* setDefaultXhrHeader() can now be overloaded with a boolean, string, or number.
+By default, all transactions send a custom header of "X-Requested-
+With:XMLHttpRequest".
+
+This default header value can be overridden by passing the desired value as an
+argument to setDefaultPostHeader().
+
+* The file upload iframe's event listener is now explicitly removed before the
+iframe is destroyed.
+
+*** version 2.3.0 ***
+
+* Custom Events are introduced in Connection Manager.  These events -- for a
+non-file upload transaction -- are:
+
+   * startEvent
+   * completeEvent
+   * successEvent
+   * failureEvent
+   * abortEvent
+
+For transactions involving file upload with an HTML form, the events are:
+
+   * startEvent
+   * completeEvent
+   * uploadEvent
+   * abortEvent
+
+* Event utility is a now Connection Manager dependency.
+
+* abort() and isCallInProgress() are now functional for file upload
+transactions.
+
+* NOTE: The native XHR implementation in Safari 2.0.4 has been confirmed to leak
+memory.
+
+* UPDATE: The XHR implementation in Safari 3.0 beta(and WebKit builds) now
+appear to handle HTTP 204 responses correctly.  XHR in Opera, as of 9.21, still
+does not produce a valid HTTP status code with an HTTP 204 response.
+
+*** version 2.2.2 ***
+
+* No revisions.
+
+*** version 2.2.1 ***
+
+* setForm() will include the correct name-value of the HTML Submit button
+clicked where multiple HTML Submit button options are present in an HTML form.
+To enable this feature, include the Event utility source file as a dependency
+before the Connection Manager source file.
+
+* The XHR implementation in IE6 and IE7, Opera, and Safari do not properly
+handle an HTTP 204 response.  IE6/7 will instead return a Win error 1223.
+handleTransactionResponse() will treat 1223 as an HTTP 204, and route the
+response appropriately to the success callback.  createResponseObject() will
+normalize the response object's status and statusText values to 204 and "No
+Content" respectively.  However, no headers are returned.
+
+Opera and Safari provide no discernable response with HTTP 204(e.g., response
+object's properties are undefined).  This response will trigger the failure
+callback with a status of 0 and statusText of "communication failure".
+
+*** version 2.2.0 ***
+
+* initHeader() now accepts a third argument as a boolean.  When set to true,
+this specific header will automatically be sent with each transaction.
+Otherwise, the header will be set and sent for the specific transaction only.
+Example: initHeader('X-YUI-State','Beta', true); all transactions will send this
+header.
+   * resetDefaultHeaders() will clear the default headers collection.
+
+* All Connection Mananger transactions will broadcast the header: "X-Requested-
+With: XMLHttpRequest".
+   * This can be turned off: YAHOO.util.Connect.setDefaultXhrHeader(false);
+
+* The HTTP method argument in asyncRequest is now case-insensitive.
+
+* uploadFile() will now correctly handle the absence of a callback object,
+allowing the transaction to complete silently.
+
+*** version 0.12.2 ***
+
+* The Opera/Connection Manager concurrent object condition, described in version
+0.12.0, no longer tests applies for Opera, version 9.10.
+
+*** version 0.12.1 ***
+
+* connection-debug.js corrected and synchronized with connection.js.  Code
+inconsistencies between the two files existed in 0.12.0.
+
+*** version 0.12.0 ***
+
+* When uploading files via setForm() and asyncRequest includes a POST data
+argument, appendPostData() will create hidden input fields for each postData
+label/value and append each field to the form object.
+
+* setForm() returns the assembled label/value string of the parsed HTML form
+fields.
+
+* NOTE: Opera 9.02 does not allow for more than 12 concurrent Connection Manager
+objects.
+
+The following example creates 12 requests in a loop:
+for(var n=0; n<=12; i++){
+  conn[n] = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
+}
+
+If n > 13, Opera 9.02 will crash.  Connection manager objects count n must be <=
+12 at all times.  This condition was not present in Opera version 9.01.
+
+This condition does not apply to other A-Grade browsers (
+http://developer.yahoo.com/yui/articles/gbs/gbs_browser-chart.html)
+
+*** version 0.11.3 ***
+
+* YUI Event dependency for file uploading is now optional.
+
+* uploadFile() now sets unique IDs for each file upload transaction to prevent
+iframe collisions with parallel uploads.
+
+* The callback object now has property responseXML to provide support for file
+upload transactions that return an XML document.
+
+* setForm() will verify if a select option value attribute is present and use
+its value, including empty string, before using the text node value.
+
+* Modified polling mechanism in handleReadyState() and
+handleTransactionResponse() to prevent infinite polling if JavaScript errors
+occur in the user-defined callback.
+
+* createFrame() will now accept a boolean argument of true to set the frame
+source to "javascript:false" to prevent IE from throwing security warnings in an
+HTTPS environment.
+
+* setHeader() now enumerates through the _http_header object using
+hasOwnProperty() to prevent collisions with members added to Object via
+prototype.
+
+* If using setForm() and asyncRequest includes a POST data argument, the data
+will be concatenated to the HTML form POST message.
+
+*** version 0.11.2 ***
+
+* No revisions.
+
+*** version 0.11.1 ***
+
+* uploadFile() now verifies the existence of callback.upload before invoking
+callback, with or without object scope.
+
+*** version 0.11.0 ***
+
+* Each transaction can be defined with a timeout threshold, in milliseconds,
+through the callback object.  If the threshold is reached, and the transaction
+hasn't yet completed, the transaction will call abort().
+
+* abort() will now accept a callback object as the second argument.  The
+failure callback will receive a response object to indicate the transaction was
+aborted.
+
+* setForm() will now support file uploads by setting the second argument to
+true (e.g., YAHOO.util.Connect.setForm(formObject, true).  File upload does not
+use the callback success or failure handler.  Instead, it uses a new callback
+object handler: upload.
+
+* HTML form submit will no longer submit form fields without a defined name
+attribute.
+
+* The default POST header of 'Content-Type','application/x-www-form-urlencoded'
+can be overridden by calling setDefaultPostHeader(false).  This
+will remove the default header from non-HTML form, POST submissions.
+
+* setHeader() now enumerates through the _http_header object with
+propertyIsEnumerable to prevent collisions with members added to Object via
+prototype.
+
+*** version 0.10.0 ***
+
+* handleTransactionResponse() now treats the full HTTP 2xx range as a success
+case, instead of just HTTP 200.
+
+* To accommodate multiple field values in Mozilla/Firefox, multiple initHeader
+calls with the same label will now result in the values concatenated to a
+comma- delimited string value.
+Example:
+Setting Content-Type:'application/x-www-form-urlencoded' and Content-
+Type:'text/xml' will result in Content-Type:'application/x-www-form-urlencoded,
+text/xml'.
+
+* Default polling interval lowered to 50ms.
+
+* YAHOO.util.Connect.setPollingInterval() will allow you to set a polling
+interval -- in milliseconds -- to override the default value.
+
+* YAHOO.util.Connect.getResponseHeader[headerLabel] now supported as a response
+object property to provide symmetry with the native XHR object's property.
+Example:
+YAHOO.util.Connect.getResponseHeader['Content-Length'] will return the value
+for the Content-Length header, if the header is available.
+
+* YAHOO.util.Connect.allResponseHeaders property renamed to
+getAllResponseHeaders to provide symmetry with the native XHR object's
+property.
+
+* YAHOO.util.Connect.setForm() now supports HTTP GET as well as HTTP POST.
+
+* YAHOO.util.Connect.setForm() now accepts an HTML form object as well as its
+name attribute value.
+
+* YAHOO.util.Connect.setForm() will not submit HTML form fields that are
+disabled or do not have a name attribute value.
+
+* [FIXED] Response exceptions result in infinite callback loop in
+Mozilla/Firefox.
+
+* [FIXED] YAHOO.util.Connect.abort() now properly clears polling interval.
+
+* [FIXED] isCallInProgress() now verifies whether XHR instance still exists,
+and returns false if the connection object is no longer available.
+
+*** version 0.9.0 ***
+
+* Initial release
+
Index: trunk/wb/include/yui/connection/connection-debug.js
===================================================================
--- trunk/wb/include/yui/connection/connection-debug.js	(nonexistent)
+++ trunk/wb/include/yui/connection/connection-debug.js	(revision 601)
@@ -0,0 +1,1399 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+/**
+ * The Connection Manager provides a simplified interface to the XMLHttpRequest
+ * object.  It handles cross-browser instantiantion of XMLHttpRequest, negotiates the
+ * interactive states and server response, returning the results to a pre-defined
+ * callback you create.
+ *
+ * @namespace YAHOO.util
+ * @module connection
+ * @requires yahoo
+ * @requires event
+ */
+
+/**
+ * The Connection Manager singleton provides methods for creating and managing
+ * asynchronous transactions.
+ *
+ * @class Connect
+ */
+
+YAHOO.util.Connect =
+{
+  /**
+   * @description Array of MSFT ActiveX ids for XMLHttpRequest.
+   * @property _msxml_progid
+   * @private
+   * @static
+   * @type array
+   */
+	_msxml_progid:[
+		'Microsoft.XMLHTTP',
+		'MSXML2.XMLHTTP.3.0',
+		'MSXML2.XMLHTTP'
+		],
+
+  /**
+   * @description Object literal of HTTP header(s)
+   * @property _http_header
+   * @private
+   * @static
+   * @type object
+   */
+	_http_headers:{},
+
+  /**
+   * @description Determines if HTTP headers are set.
+   * @property _has_http_headers
+   * @private
+   * @static
+   * @type boolean
+   */
+	_has_http_headers:false,
+
+ /**
+  * @description Determines if a default header of
+  * Content-Type of 'application/x-www-form-urlencoded'
+  * will be added to any client HTTP headers sent for POST
+  * transactions.
+  * @property _use_default_post_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _use_default_post_header:true,
+
+ /**
+  * @description The default header used for POST transactions.
+  * @property _default_post_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',
+
+ /**
+  * @description The default header used for transactions involving the
+  * use of HTML forms.
+  * @property _default_form_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_form_header:'application/x-www-form-urlencoded',
+
+ /**
+  * @description Determines if a default header of
+  * 'X-Requested-With: XMLHttpRequest'
+  * will be added to each transaction.
+  * @property _use_default_xhr_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _use_default_xhr_header:true,
+
+ /**
+  * @description The default header value for the label
+  * "X-Requested-With".  This is sent with each
+  * transaction, by default, to identify the
+  * request as being made by YUI Connection Manager.
+  * @property _default_xhr_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_xhr_header:'XMLHttpRequest',
+
+ /**
+  * @description Determines if custom, default headers
+  * are set for each transaction.
+  * @property _has_default_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _has_default_headers:true,
+
+ /**
+  * @description Determines if custom, default headers
+  * are set for each transaction.
+  * @property _has_default_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_headers:{},
+
+ /**
+  * @description Property modified by setForm() to determine if the data
+  * should be submitted as an HTML form.
+  * @property _isFormSubmit
+  * @private
+  * @static
+  * @type boolean
+  */
+    _isFormSubmit:false,
+
+ /**
+  * @description Property modified by setForm() to determine if a file(s)
+  * upload is expected.
+  * @property _isFileUpload
+  * @private
+  * @static
+  * @type boolean
+  */
+    _isFileUpload:false,
+
+ /**
+  * @description Property modified by setForm() to set a reference to the HTML
+  * form node if the desired action is file upload.
+  * @property _formNode
+  * @private
+  * @static
+  * @type object
+  */
+    _formNode:null,
+
+ /**
+  * @description Property modified by setForm() to set the HTML form data
+  * for each transaction.
+  * @property _sFormData
+  * @private
+  * @static
+  * @type string
+  */
+    _sFormData:null,
+
+ /**
+  * @description Collection of polling references to the polling mechanism in handleReadyState.
+  * @property _poll
+  * @private
+  * @static
+  * @type object
+  */
+    _poll:{},
+
+ /**
+  * @description Queue of timeout values for each transaction callback with a defined timeout value.
+  * @property _timeOut
+  * @private
+  * @static
+  * @type object
+  */
+    _timeOut:{},
+
+  /**
+   * @description The polling frequency, in milliseconds, for HandleReadyState.
+   * when attempting to determine a transaction's XHR readyState.
+   * The default is 50 milliseconds.
+   * @property _polling_interval
+   * @private
+   * @static
+   * @type int
+   */
+     _polling_interval:50,
+
+  /**
+   * @description A transaction counter that increments the transaction id for each transaction.
+   * @property _transaction_id
+   * @private
+   * @static
+   * @type int
+   */
+     _transaction_id:0,
+
+  /**
+   * @description Tracks the name-value pair of the "clicked" submit button if multiple submit
+   * buttons are present in an HTML form; and, if YAHOO.util.Event is available.
+   * @property _submitElementValue
+   * @private
+   * @static
+   * @type string
+   */
+	 _submitElementValue:null,
+
+  /**
+   * @description Determines whether YAHOO.util.Event is available and returns true or false.
+   * If true, an event listener is bound at the document level to trap click events that
+   * resolve to a target type of "Submit".  This listener will enable setForm() to determine
+   * the clicked "Submit" value in a multi-Submit button, HTML form.
+   * @property _hasSubmitListener
+   * @private
+   * @static
+   */
+	 _hasSubmitListener:(function()
+	 {
+		if(YAHOO.util.Event){
+			YAHOO.util.Event.addListener(
+				document,
+				'click',
+				function(e){
+					var obj = YAHOO.util.Event.getTarget(e);
+					if(obj.type && obj.type.toLowerCase() == 'submit'){
+						YAHOO.util.Connect._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
+					}
+				});
+			return true;
+	    }
+	    return false;
+	 })(),
+
+  /**
+   * @description Custom event that fires at the start of a transaction
+   * @property startEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	startEvent: new YAHOO.util.CustomEvent('start'),
+
+  /**
+   * @description Custom event that fires when a transaction response has completed.
+   * @property completeEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	completeEvent: new YAHOO.util.CustomEvent('complete'),
+
+  /**
+   * @description Custom event that fires when handleTransactionResponse() determines a
+   * response in the HTTP 2xx range.
+   * @property successEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	successEvent: new YAHOO.util.CustomEvent('success'),
+
+  /**
+   * @description Custom event that fires when handleTransactionResponse() determines a
+   * response in the HTTP 4xx/5xx range.
+   * @property failureEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	failureEvent: new YAHOO.util.CustomEvent('failure'),
+
+  /**
+   * @description Custom event that fires when handleTransactionResponse() determines a
+   * response in the HTTP 4xx/5xx range.
+   * @property failureEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	uploadEvent: new YAHOO.util.CustomEvent('upload'),
+
+  /**
+   * @description Custom event that fires when a transaction is successfully aborted.
+   * @property abortEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+	abortEvent: new YAHOO.util.CustomEvent('abort'),
+
+  /**
+   * @description A reference table that maps callback custom events members to its specific
+   * event name.
+   * @property _customEvents
+   * @private
+   * @static
+   * @type object
+   */
+	_customEvents:
+	{
+		onStart:['startEvent', 'start'],
+		onComplete:['completeEvent', 'complete'],
+		onSuccess:['successEvent', 'success'],
+		onFailure:['failureEvent', 'failure'],
+		onUpload:['uploadEvent', 'upload'],
+		onAbort:['abortEvent', 'abort']
+	},
+
+  /**
+   * @description Member to add an ActiveX id to the existing xml_progid array.
+   * In the event(unlikely) a new ActiveX id is introduced, it can be added
+   * without internal code modifications.
+   * @method setProgId
+   * @public
+   * @static
+   * @param {string} id The ActiveX id to be added to initialize the XHR object.
+   * @return void
+   */
+	setProgId:function(id)
+	{
+		this._msxml_progid.unshift(id);
+		YAHOO.log('ActiveX Program Id  ' + id + ' added to _msxml_progid.', 'info', 'Connection');
+	},
+
+  /**
+   * @description Member to override the default POST header.
+   * @method setDefaultPostHeader
+   * @public
+   * @static
+   * @param {boolean} b Set and use default header - true or false .
+   * @return void
+   */
+	setDefaultPostHeader:function(b)
+	{
+		if(typeof b == 'string'){
+			this._default_post_header = b;
+			YAHOO.log('Default POST header set to  ' + b, 'info', 'Connection');
+		}
+		else if(typeof b == 'boolean'){
+			this._use_default_post_header = b;
+		}
+	},
+
+  /**
+   * @description Member to override the default transaction header..
+   * @method setDefaultXhrHeader
+   * @public
+   * @static
+   * @param {boolean} b Set and use default header - true or false .
+   * @return void
+   */
+	setDefaultXhrHeader:function(b)
+	{
+		if(typeof b == 'string'){
+			this._default_xhr_header = b;
+			YAHOO.log('Default XHR header set to  ' + b, 'info', 'Connection');
+		}
+		else{
+			this._use_default_xhr_header = b;
+		}
+	},
+
+  /**
+   * @description Member to modify the default polling interval.
+   * @method setPollingInterval
+   * @public
+   * @static
+   * @param {int} i The polling interval in milliseconds.
+   * @return void
+   */
+	setPollingInterval:function(i)
+	{
+		if(typeof i == 'number' && isFinite(i)){
+			this._polling_interval = i;
+			YAHOO.log('Default polling interval set to ' + i +'ms', 'info', 'Connection');
+		}
+	},
+
+  /**
+   * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
+   * the XMLHttpRequest instance and the transaction id.
+   * @method createXhrObject
+   * @private
+   * @static
+   * @param {int} transactionId Property containing the transaction id for this transaction.
+   * @return object
+   */
+	createXhrObject:function(transactionId)
+	{
+		var obj,http;
+		try
+		{
+			// Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
+			http = new XMLHttpRequest();
+			//  Object literal with http and tId properties
+			obj = { conn:http, tId:transactionId };
+			YAHOO.log('XHR object created for transaction ' + transactionId, 'info', 'Connection');
+		}
+		catch(e)
+		{
+			for(var i=0; i<this._msxml_progid.length; ++i){
+				try
+				{
+					// Instantiates XMLHttpRequest for IE and assign to http
+					http = new ActiveXObject(this._msxml_progid[i]);
+					//  Object literal with conn and tId properties
+					obj = { conn:http, tId:transactionId };
+					YAHOO.log('ActiveX XHR object created for transaction ' + transactionId, 'info', 'Connection');
+					break;
+				}
+				catch(e){}
+			}
+		}
+		finally
+		{
+			return obj;
+		}
+	},
+
+  /**
+   * @description This method is called by asyncRequest to create a
+   * valid connection object for the transaction.  It also passes a
+   * transaction id and increments the transaction id counter.
+   * @method getConnectionObject
+   * @private
+   * @static
+   * @return {object}
+   */
+	getConnectionObject:function(isFileUpload)
+	{
+		var o;
+		var tId = this._transaction_id;
+
+		try
+		{
+			if(!isFileUpload){
+				o = this.createXhrObject(tId);
+			}
+			else{
+				o = {};
+				o.tId = tId;
+				o.isUpload = true;
+			}
+
+			if(o){
+				this._transaction_id++;
+			}
+		}
+		catch(e){}
+		finally
+		{
+			return o;
+		}
+	},
+
+  /**
+   * @description Method for initiating an asynchronous request via the XHR object.
+   * @method asyncRequest
+   * @public
+   * @static
+   * @param {string} method HTTP transaction method
+   * @param {string} uri Fully qualified path of resource
+   * @param {callback} callback User-defined callback function or object
+   * @param {string} postData POST body
+   * @return {object} Returns the connection object
+   */
+	asyncRequest:function(method, uri, callback, postData)
+	{
+		var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();
+		var args = (callback && callback.argument)?callback.argument:null;
+
+		if(!o){
+			YAHOO.log('Unable to create connection object.', 'error', 'Connection');
+			return null;
+		}
+		else{
+
+			// Intialize any transaction-specific custom events, if provided.
+			if(callback && callback.customevents){
+				this.initCustomEvents(o, callback);
+			}
+
+			if(this._isFormSubmit){
+				if(this._isFileUpload){
+					this.uploadFile(o, callback, uri, postData);
+					return o;
+				}
+
+				// If the specified HTTP method is GET, setForm() will return an
+				// encoded string that is concatenated to the uri to
+				// create a querystring.
+				if(method.toUpperCase() == 'GET'){
+					if(this._sFormData.length !== 0){
+						// If the URI already contains a querystring, append an ampersand
+						// and then concatenate _sFormData to the URI.
+						uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
+					}
+				}
+				else if(method.toUpperCase() == 'POST'){
+					// If POST data exist in addition to the HTML form data,
+					// it will be concatenated to the form data.
+					postData = postData?this._sFormData + "&" + postData:this._sFormData;
+				}
+			}
+
+			if(method.toUpperCase() == 'GET' && (callback && callback.cache === false)){
+				// If callback.cache is defined and set to false, a
+				// timestamp value will be added to the querystring.
+				uri += ((uri.indexOf('?') == -1)?'?':'&') + "rnd=" + new Date().valueOf().toString();
+			}
+
+			o.conn.open(method, uri, true);
+
+			// Each transaction will automatically include a custom header of
+			// "X-Requested-With: XMLHttpRequest" to identify the request as
+			// having originated from Connection Manager.
+			if(this._use_default_xhr_header){
+				if(!this._default_headers['X-Requested-With']){
+					this.initHeader('X-Requested-With', this._default_xhr_header, true);
+					YAHOO.log('Initialize transaction header X-Request-Header to XMLHttpRequest.', 'info', 'Connection');
+				}
+			}
+
+			//If the transaction method is POST and the POST header value is set to true
+			//or a custom value, initalize the Content-Type header to this value.
+			if((method.toUpperCase() == 'POST' && this._use_default_post_header) && this._isFormSubmit === false){
+				this.initHeader('Content-Type', this._default_post_header);
+				YAHOO.log('Initialize header Content-Type to application/x-www-form-urlencoded; UTF-8 for POST transaction.', 'info', 'Connection');
+			}
+
+			//Initialize all default and custom HTTP headers,
+			if(this._has_default_headers || this._has_http_headers){
+				this.setHeader(o);
+			}
+
+			this.handleReadyState(o, callback);
+			o.conn.send(postData || null);
+			YAHOO.log('Transaction ' + o.tId + ' sent.', 'info', 'Connection');
+
+
+			// Reset the HTML form data and state properties as
+			// soon as the data are submitted.
+			if(this._isFormSubmit === true){
+				this.resetFormState();
+			}
+
+			// Fire global custom event -- startEvent
+			this.startEvent.fire(o, args);
+
+			if(o.startEvent){
+				// Fire transaction custom event -- startEvent
+				o.startEvent.fire(o, args);
+			}
+
+			return o;
+		}
+	},
+
+  /**
+   * @description This method creates and subscribes custom events,
+   * specific to each transaction
+   * @method initCustomEvents
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {callback} callback The user-defined callback object
+   * @return {void}
+   */
+	initCustomEvents:function(o, callback)
+	{
+		// Enumerate through callback.customevents members and bind/subscribe
+		// events that match in the _customEvents table.
+		for(var prop in callback.customevents){
+			if(this._customEvents[prop][0]){
+				// Create the custom event
+				o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
+				YAHOO.log('Transaction-specific Custom Event ' + o[this._customEvents[prop][1]] + ' created.', 'info', 'Connection');
+
+				// Subscribe the custom event
+				o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
+				YAHOO.log('Transaction-specific Custom Event ' + o[this._customEvents[prop][1]] + ' subscribed.', 'info', 'Connection');
+			}
+		}
+	},
+
+  /**
+   * @description This method serves as a timer that polls the XHR object's readyState
+   * property during a transaction, instead of binding a callback to the
+   * onreadystatechange event.  Upon readyState 4, handleTransactionResponse
+   * will process the response, and the timer will be cleared.
+   * @method handleReadyState
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {callback} callback The user-defined callback object
+   * @return {void}
+   */
+
+    handleReadyState:function(o, callback)
+
+    {
+		var oConn = this;
+		var args = (callback && callback.argument)?callback.argument:null;
+
+		if(callback && callback.timeout){
+			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
+		}
+
+		this._poll[o.tId] = window.setInterval(
+			function(){
+				if(o.conn && o.conn.readyState === 4){
+
+					// Clear the polling interval for the transaction
+					// and remove the reference from _poll.
+					window.clearInterval(oConn._poll[o.tId]);
+					delete oConn._poll[o.tId];
+
+					if(callback && callback.timeout){
+						window.clearTimeout(oConn._timeOut[o.tId]);
+						delete oConn._timeOut[o.tId];
+					}
+
+					// Fire global custom event -- completeEvent
+					oConn.completeEvent.fire(o, args);
+
+					if(o.completeEvent){
+						// Fire transaction custom event -- completeEvent
+						o.completeEvent.fire(o, args);
+					}
+
+					oConn.handleTransactionResponse(o, callback);
+				}
+			}
+		,this._polling_interval);
+    },
+
+  /**
+   * @description This method attempts to interpret the server response and
+   * determine whether the transaction was successful, or if an error or
+   * exception was encountered.
+   * @method handleTransactionResponse
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {object} callback The user-defined callback object
+   * @param {boolean} isAbort Determines if the transaction was terminated via abort().
+   * @return {void}
+   */
+    handleTransactionResponse:function(o, callback, isAbort)
+    {
+		var httpStatus, responseObject;
+		var args = (callback && callback.argument)?callback.argument:null;
+
+		try
+		{
+			if(o.conn.status !== undefined && o.conn.status !== 0){
+				httpStatus = o.conn.status;
+			}
+			else{
+				httpStatus = 13030;
+			}
+		}
+		catch(e){
+
+			 // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
+			 // when the XHR object's status and statusText properties are
+			 // unavailable, and a query attempt throws an exception.
+			httpStatus = 13030;
+		}
+
+		if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){
+			responseObject = this.createResponseObject(o, args);
+			if(callback && callback.success){
+				if(!callback.scope){
+					callback.success(responseObject);
+					YAHOO.log('Success callback. HTTP code is ' + httpStatus, 'info', 'Connection');
+				}
+				else{
+					// If a scope property is defined, the callback will be fired from
+					// the context of the object.
+					callback.success.apply(callback.scope, [responseObject]);
+					YAHOO.log('Success callback with scope. HTTP code is ' + httpStatus, 'info', 'Connection');
+				}
+			}
+
+			// Fire global custom event -- successEvent
+			this.successEvent.fire(responseObject);
+
+			if(o.successEvent){
+				// Fire transaction custom event -- successEvent
+				o.successEvent.fire(responseObject);
+			}
+		}
+		else{
+			switch(httpStatus){
+				// The following cases are wininet.dll error codes that may be encountered.
+				case 12002: // Server timeout
+				case 12029: // 12029 to 12031 correspond to dropped connections.
+				case 12030:
+				case 12031:
+				case 12152: // Connection closed by server.
+				case 13030: // See above comments for variable status.
+					responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false));
+					if(callback && callback.failure){
+						if(!callback.scope){
+							callback.failure(responseObject);
+							YAHOO.log('Failure callback. Exception detected. Status code is ' + httpStatus, 'warn', 'Connection');
+						}
+						else{
+							callback.failure.apply(callback.scope, [responseObject]);
+							YAHOO.log('Failure callback with scope. Exception detected. Status code is ' + httpStatus, 'warn', 'Connection');
+						}
+					}
+
+					break;
+				default:
+					responseObject = this.createResponseObject(o, args);
+					if(callback && callback.failure){
+						if(!callback.scope){
+							callback.failure(responseObject);
+							YAHOO.log('Failure callback. HTTP status code is ' + httpStatus, 'warn', 'Connection');
+						}
+						else{
+							callback.failure.apply(callback.scope, [responseObject]);
+							YAHOO.log('Failure callback with scope. HTTP status code is ' + httpStatus, 'warn', 'Connection');
+						}
+					}
+			}
+
+			// Fire global custom event -- failureEvent
+			this.failureEvent.fire(responseObject);
+
+			if(o.failureEvent){
+				// Fire transaction custom event -- failureEvent
+				o.failureEvent.fire(responseObject);
+			}
+
+		}
+
+		this.releaseObject(o);
+		responseObject = null;
+    },
+
+  /**
+   * @description This method evaluates the server response, creates and returns the results via
+   * its properties.  Success and failure cases will differ in the response
+   * object's property values.
+   * @method createResponseObject
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
+   * @return {object}
+   */
+    createResponseObject:function(o, callbackArg)
+    {
+		var obj = {};
+		var headerObj = {};
+
+		try
+		{
+			var headerStr = o.conn.getAllResponseHeaders();
+			var header = headerStr.split('\n');
+			for(var i=0; i<header.length; i++){
+				var delimitPos = header[i].indexOf(':');
+				if(delimitPos != -1){
+					headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
+				}
+			}
+		}
+		catch(e){}
+
+		obj.tId = o.tId;
+		// Normalize IE's response to HTTP 204 when Win error 1223.
+		obj.status = (o.conn.status == 1223)?204:o.conn.status;
+		// Normalize IE's statusText to "No Content" instead of "Unknown".
+		obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
+		obj.getResponseHeader = headerObj;
+		obj.getAllResponseHeaders = headerStr;
+		obj.responseText = o.conn.responseText;
+		obj.responseXML = o.conn.responseXML;
+
+		if(callbackArg){
+			obj.argument = callbackArg;
+		}
+
+		return obj;
+    },
+
+  /**
+   * @description If a transaction cannot be completed due to dropped or closed connections,
+   * there may be not be enough information to build a full response object.
+   * The failure callback will be fired and this specific condition can be identified
+   * by a status property value of 0.
+   *
+   * If an abort was successful, the status property will report a value of -1.
+   *
+   * @method createExceptionObject
+   * @private
+   * @static
+   * @param {int} tId The Transaction Id
+   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
+   * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
+   * @return {object}
+   */
+    createExceptionObject:function(tId, callbackArg, isAbort)
+    {
+		var COMM_CODE = 0;
+		var COMM_ERROR = 'communication failure';
+		var ABORT_CODE = -1;
+		var ABORT_ERROR = 'transaction aborted';
+
+		var obj = {};
+
+		obj.tId = tId;
+		if(isAbort){
+			obj.status = ABORT_CODE;
+			obj.statusText = ABORT_ERROR;
+		}
+		else{
+			obj.status = COMM_CODE;
+			obj.statusText = COMM_ERROR;
+		}
+
+		if(callbackArg){
+			obj.argument = callbackArg;
+		}
+
+		return obj;
+    },
+
+  /**
+   * @description Method that initializes the custom HTTP headers for the each transaction.
+   * @method initHeader
+   * @public
+   * @static
+   * @param {string} label The HTTP header label
+   * @param {string} value The HTTP header value
+   * @param {string} isDefault Determines if the specific header is a default header
+   * automatically sent with each transaction.
+   * @return {void}
+   */
+	initHeader:function(label, value, isDefault)
+	{
+		var headerObj = (isDefault)?this._default_headers:this._http_headers;
+		headerObj[label] = value;
+
+		if(isDefault){
+			this._has_default_headers = true;
+		}
+		else{
+			this._has_http_headers = true;
+		}
+	},
+
+
+  /**
+   * @description Accessor that sets the HTTP headers for each transaction.
+   * @method setHeader
+   * @private
+   * @static
+   * @param {object} o The connection object for the transaction.
+   * @return {void}
+   */
+	setHeader:function(o)
+	{
+		if(this._has_default_headers){
+			for(var prop in this._default_headers){
+				if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
+					o.conn.setRequestHeader(prop, this._default_headers[prop]);
+					YAHOO.log('Default HTTP header ' + prop + ' set with value of ' + this._default_headers[prop], 'info', 'Connection');
+				}
+			}
+		}
+
+		if(this._has_http_headers){
+			for(var prop in this._http_headers){
+				if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
+					o.conn.setRequestHeader(prop, this._http_headers[prop]);
+					YAHOO.log('HTTP header ' + prop + ' set with value of ' + this._http_headers[prop], 'info', 'Connection');
+				}
+			}
+			delete this._http_headers;
+
+			this._http_headers = {};
+			this._has_http_headers = false;
+		}
+	},
+
+  /**
+   * @description Resets the default HTTP headers object
+   * @method resetDefaultHeaders
+   * @public
+   * @static
+   * @return {void}
+   */
+	resetDefaultHeaders:function(){
+		delete this._default_headers;
+		this._default_headers = {};
+		this._has_default_headers = false;
+	},
+
+  /**
+   * @description This method assembles the form label and value pairs and
+   * constructs an encoded string.
+   * asyncRequest() will automatically initialize the transaction with a
+   * a HTTP header Content-Type of application/x-www-form-urlencoded.
+   * @method setForm
+   * @public
+   * @static
+   * @param {string || object} form id or name attribute, or form object.
+   * @param {boolean} optional enable file upload.
+   * @param {boolean} optional enable file upload over SSL in IE only.
+   * @return {string} string of the HTML form field name and value pairs..
+   */
+	setForm:function(formId, isUpload, secureUri)
+	{
+		// reset the HTML form data and state properties
+		this.resetFormState();
+
+		var oForm;
+		if(typeof formId == 'string'){
+			// Determine if the argument is a form id or a form name.
+			// Note form name usage is deprecated, but supported
+			// here for backward compatibility.
+			oForm = (document.getElementById(formId) || document.forms[formId]);
+		}
+		else if(typeof formId == 'object'){
+			// Treat argument as an HTML form object.
+			oForm = formId;
+		}
+		else{
+			YAHOO.log('Unable to create form object ' + formId, 'warn', 'Connection');
+			return;
+		}
+
+		// If the isUpload argument is true, setForm will call createFrame to initialize
+		// an iframe as the form target.
+		//
+		// The argument secureURI is also required by IE in SSL environments
+		// where the secureURI string is a fully qualified HTTP path, used to set the source
+		// of the iframe, to a stub resource in the same domain.
+		if(isUpload){
+
+			// Create iframe in preparation for file upload.
+			var io = this.createFrame(secureUri?secureUri:null);
+			// Set form reference and file upload properties to true.
+			this._isFormSubmit = true;
+			this._isFileUpload = true;
+			this._formNode = oForm;
+
+			return;
+
+		}
+
+		var oElement, oName, oValue, oDisabled;
+		var hasSubmit = false;
+
+		// Iterate over the form elements collection to construct the
+		// label-value pairs.
+		for (var i=0; i<oForm.elements.length; i++){
+			oElement = oForm.elements[i];
+			oDisabled = oElement.disabled;
+			oName = oElement.name;
+			oValue = oElement.value;
+
+			// Do not submit fields that are disabled or
+			// do not have a name attribute value.
+			if(!oDisabled && oName)
+			{
+				switch(oElement.type)
+				{
+					case 'select-one':
+					case 'select-multiple':
+						for(var j=0; j<oElement.options.length; j++){
+							if(oElement.options[j].selected){
+								if(window.ActiveXObject){
+									this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text) + '&';
+								}
+								else{
+									this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&';
+								}
+							}
+						}
+						break;
+					case 'radio':
+					case 'checkbox':
+						if(oElement.checked){
+							this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
+						}
+						break;
+					case 'file':
+						// stub case as XMLHttpRequest will only send the file path as a string.
+					case undefined:
+						// stub case for fieldset element which returns undefined.
+					case 'reset':
+						// stub case for input type reset button.
+					case 'button':
+						// stub case for input type button elements.
+						break;
+					case 'submit':
+						if(hasSubmit === false){
+							if(this._hasSubmitListener && this._submitElementValue){
+								this._sFormData += this._submitElementValue + '&';
+							}
+							else{
+								this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
+							}
+
+							hasSubmit = true;
+						}
+						break;
+					default:
+						this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
+				}
+			}
+		}
+
+		this._isFormSubmit = true;
+		this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);
+
+		YAHOO.log('Form initialized for transaction. HTML form POST message is: ' + this._sFormData, 'info', 'Connection');
+
+		this.initHeader('Content-Type', this._default_form_header);
+		YAHOO.log('Initialize header Content-Type to application/x-www-form-urlencoded for setForm() transaction.', 'info', 'Connection');
+
+		return this._sFormData;
+	},
+
+  /**
+   * @description Resets HTML form properties when an HTML form or HTML form
+   * with file upload transaction is sent.
+   * @method resetFormState
+   * @private
+   * @static
+   * @return {void}
+   */
+	resetFormState:function(){
+		this._isFormSubmit = false;
+		this._isFileUpload = false;
+		this._formNode = null;
+		this._sFormData = "";
+	},
+
+  /**
+   * @description Creates an iframe to be used for form file uploads.  It is remove from the
+   * document upon completion of the upload transaction.
+   * @method createFrame
+   * @private
+   * @static
+   * @param {string} optional qualified path of iframe resource for SSL in IE.
+   * @return {void}
+   */
+	createFrame:function(secureUri){
+
+		// IE does not allow the setting of id and name attributes as object
+		// properties via createElement().  A different iframe creation
+		// pattern is required for IE.
+		var frameId = 'yuiIO' + this._transaction_id;
+		var io;
+		if(window.ActiveXObject){
+			io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
+
+			// IE will throw a security exception in an SSL environment if the
+			// iframe source is undefined.
+			if(typeof secureUri == 'boolean'){
+				io.src = 'javascript:false';
+			}
+			else if(typeof secureURI == 'string'){
+				// Deprecated
+				io.src = secureUri;
+			}
+		}
+		else{
+			io = document.createElement('iframe');
+			io.id = frameId;
+			io.name = frameId;
+		}
+
+		io.style.position = 'absolute';
+		io.style.top = '-1000px';
+		io.style.left = '-1000px';
+
+		document.body.appendChild(io);
+		YAHOO.log('File upload iframe created. Id is:' + frameId, 'info', 'Connection');
+	},
+
+  /**
+   * @description Parses the POST data and creates hidden form elements
+   * for each key-value, and appends them to the HTML form object.
+   * @method appendPostData
+   * @private
+   * @static
+   * @param {string} postData The HTTP POST data
+   * @return {array} formElements Collection of hidden fields.
+   */
+	appendPostData:function(postData)
+	{
+		var formElements = [];
+		var postMessage = postData.split('&');
+		for(var i=0; i < postMessage.length; i++){
+			var delimitPos = postMessage[i].indexOf('=');
+			if(delimitPos != -1){
+				formElements[i] = document.createElement('input');
+				formElements[i].type = 'hidden';
+				formElements[i].name = postMessage[i].substring(0,delimitPos);
+				formElements[i].value = postMessage[i].substring(delimitPos+1);
+				this._formNode.appendChild(formElements[i]);
+			}
+		}
+
+		return formElements;
+	},
+
+  /**
+   * @description Uploads HTML form, inclusive of files/attachments, using the
+   * iframe created in createFrame to facilitate the transaction.
+   * @method uploadFile
+   * @private
+   * @static
+   * @param {int} id The transaction id.
+   * @param {object} callback User-defined callback object.
+   * @param {string} uri Fully qualified path of resource.
+   * @param {string} postData POST data to be submitted in addition to HTML form.
+   * @return {void}
+   */
+	uploadFile:function(o, callback, uri, postData){
+
+		// Each iframe has an id prefix of "yuiIO" followed
+		// by the unique transaction id.
+		var oConn = this;
+		var frameId = 'yuiIO' + o.tId;
+		var uploadEncoding = 'multipart/form-data';
+		var io = document.getElementById(frameId);
+		var args = (callback && callback.argument)?callback.argument:null;
+
+		// Track original HTML form attribute values.
+		var rawFormAttributes =
+		{
+			action:this._formNode.getAttribute('action'),
+			method:this._formNode.getAttribute('method'),
+			target:this._formNode.getAttribute('target')
+		};
+
+		// Initialize the HTML form properties in case they are
+		// not defined in the HTML form.
+		this._formNode.setAttribute('action', uri);
+		this._formNode.setAttribute('method', 'POST');
+		this._formNode.setAttribute('target', frameId);
+
+		if(this._formNode.encoding){
+			// IE does not respect property enctype for HTML forms.
+			// Instead it uses the property - "encoding".
+			this._formNode.setAttribute('encoding', uploadEncoding);
+		}
+		else{
+			this._formNode.setAttribute('enctype', uploadEncoding);
+		}
+
+		if(postData){
+			var oElements = this.appendPostData(postData);
+		}
+
+		// Start file upload.
+		this._formNode.submit();
+
+		// Fire global custom event -- startEvent
+		this.startEvent.fire(o, args);
+
+		if(o.startEvent){
+			// Fire transaction custom event -- startEvent
+			o.startEvent.fire(o, args);
+		}
+
+		// Start polling if a callback is present and the timeout
+		// property has been defined.
+		if(callback && callback.timeout){
+			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
+		}
+
+		// Remove HTML elements created by appendPostData
+		if(oElements && oElements.length > 0){
+			for(var i=0; i < oElements.length; i++){
+				this._formNode.removeChild(oElements[i]);
+			}
+		}
+
+		// Restore HTML form attributes to their original
+		// values prior to file upload.
+		for(var prop in rawFormAttributes){
+			if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
+				if(rawFormAttributes[prop]){
+					this._formNode.setAttribute(prop, rawFormAttributes[prop]);
+				}
+				else{
+					this._formNode.removeAttribute(prop);
+				}
+			}
+		}
+
+		// Reset HTML form state properties.
+		this.resetFormState();
+
+		// Create the upload callback handler that fires when the iframe
+		// receives the load event.  Subsequently, the event handler is detached
+		// and the iframe removed from the document.
+		var uploadCallback = function()
+		{
+			if(callback && callback.timeout){
+				window.clearTimeout(oConn._timeOut[o.tId]);
+				delete oConn._timeOut[o.tId];
+			}
+
+			// Fire global custom event -- completeEvent
+			oConn.completeEvent.fire(o, args);
+
+			if(o.completeEvent){
+				// Fire transaction custom event -- completeEvent
+				o.completeEvent.fire(o, args);
+			}
+
+			var obj = {};
+			obj.tId = o.tId;
+			obj.argument = callback.argument;
+
+			try
+			{
+				// responseText and responseXML will be populated with the same data from the iframe.
+				// Since the HTTP headers cannot be read from the iframe
+				obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
+				obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
+			}
+			catch(e){}
+
+			if(callback && callback.upload){
+				if(!callback.scope){
+					callback.upload(obj);
+					YAHOO.log('Upload callback.', 'info', 'Connection');
+				}
+				else{
+					callback.upload.apply(callback.scope, [obj]);
+					YAHOO.log('Upload callback with scope.', 'info', 'Connection');
+				}
+			}
+
+			// Fire global custom event -- uploadEvent
+			oConn.uploadEvent.fire(obj);
+
+			if(o.uploadEvent){
+				// Fire transaction custom event -- uploadEvent
+				o.uploadEvent.fire(obj);
+			}
+
+			YAHOO.util.Event.removeListener(io, "load", uploadCallback);
+
+			setTimeout(
+				function(){
+					document.body.removeChild(io);
+					oConn.releaseObject(o);
+					YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection');
+				}, 100);
+		};
+
+		// Bind the onload handler to the iframe to detect the file upload response.
+		YAHOO.util.Event.addListener(io, "load", uploadCallback);
+	},
+
+  /**
+   * @description Method to terminate a transaction, if it has not reached readyState 4.
+   * @method abort
+   * @public
+   * @static
+   * @param {object} o The connection object returned by asyncRequest.
+   * @param {object} callback  User-defined callback object.
+   * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
+   * @return {boolean}
+   */
+	abort:function(o, callback, isTimeout)
+	{
+		var abortStatus;
+		var args = (callback && callback.argument)?callback.argument:null;
+
+
+		if(o && o.conn){
+			if(this.isCallInProgress(o)){
+				// Issue abort request
+				o.conn.abort();
+
+				window.clearInterval(this._poll[o.tId]);
+				delete this._poll[o.tId];
+
+				if(isTimeout){
+					window.clearTimeout(this._timeOut[o.tId]);
+					delete this._timeOut[o.tId];
+				}
+
+				abortStatus = true;
+			}
+		}
+		else if(o && o.isUpload === true){
+			var frameId = 'yuiIO' + o.tId;
+			var io = document.getElementById(frameId);
+
+			if(io){
+				// Remove all listeners on the iframe prior to
+				// its destruction.
+				YAHOO.util.Event.removeListener(io, "load");
+				// Destroy the iframe facilitating the transaction.
+				document.body.removeChild(io);
+				YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection');
+
+				if(isTimeout){
+					window.clearTimeout(this._timeOut[o.tId]);
+					delete this._timeOut[o.tId];
+				}
+
+				abortStatus = true;
+			}
+		}
+		else{
+			abortStatus = false;
+		}
+
+		if(abortStatus === true){
+			// Fire global custom event -- abortEvent
+			this.abortEvent.fire(o, args);
+
+			if(o.abortEvent){
+				// Fire transaction custom event -- abortEvent
+				o.abortEvent.fire(o, args);
+			}
+
+			this.handleTransactionResponse(o, callback, true);
+			YAHOO.log('Transaction ' + o.tId + ' aborted.', 'info', 'Connection');
+		}
+
+		return abortStatus;
+	},
+
+  /**
+   * @description Determines if the transaction is still being processed.
+   * @method isCallInProgress
+   * @public
+   * @static
+   * @param {object} o The connection object returned by asyncRequest
+   * @return {boolean}
+   */
+	isCallInProgress:function(o)
+	{
+		// if the XHR object assigned to the transaction has not been dereferenced,
+		// then check its readyState status.  Otherwise, return false.
+		if(o && o.conn){
+			return o.conn.readyState !== 4 && o.conn.readyState !== 0;
+		}
+		else if(o && o.isUpload === true){
+			var frameId = 'yuiIO' + o.tId;
+			return document.getElementById(frameId)?true:false;
+		}
+		else{
+			return false;
+		}
+	},
+
+  /**
+   * @description Dereference the XHR instance and the connection object after the transaction is completed.
+   * @method releaseObject
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @return {void}
+   */
+	releaseObject:function(o)
+	{
+		if(o && o.conn){
+			//dereference the XHR instance.
+			o.conn = null;
+
+			YAHOO.log('Connection object for transaction ' + o.tId + ' destroyed.', 'info', 'Connection');
+
+			//dereference the connection object.
+			o = null;
+		}
+	}
+};
+
+YAHOO.register("connection", YAHOO.util.Connect, {version: "2.4.1", build: "742"});
Index: trunk/wb/include/yui/dom/dom.js
===================================================================
--- trunk/wb/include/yui/dom/dom.js	(nonexistent)
+++ trunk/wb/include/yui/dom/dom.js	(revision 601)
@@ -0,0 +1,1224 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+/**
+ * The dom module provides helper methods for manipulating Dom elements.
+ * @module dom
+ *
+ */
+
+(function() {
+    var Y = YAHOO.util,     // internal shorthand
+        getStyle,           // for load time browser branching
+        setStyle,           // ditto
+        id_counter = 0,     // for use with generateId
+        propertyCache = {}, // for faster hyphen converts
+        reClassNameCache = {},          // cache regexes for className
+        document = window.document;     // cache for faster lookups
+    
+    // brower detection
+    var isOpera = YAHOO.env.ua.opera,
+        isSafari = YAHOO.env.ua.webkit, 
+        isGecko = YAHOO.env.ua.gecko,
+        isIE = YAHOO.env.ua.ie; 
+    
+    // regex cache
+    var patterns = {
+        HYPHEN: /(-[a-z])/i, // to normalize get/setStyle
+        ROOT_TAG: /^body|html$/i // body for quirks mode, html for standards
+    };
+
+    var toCamel = function(property) {
+        if ( !patterns.HYPHEN.test(property) ) {
+            return property; // no hyphens
+        }
+        
+        if (propertyCache[property]) { // already converted
+            return propertyCache[property];
+        }
+       
+        var converted = property;
+ 
+        while( patterns.HYPHEN.exec(converted) ) {
+            converted = converted.replace(RegExp.$1,
+                    RegExp.$1.substr(1).toUpperCase());
+        }
+        
+        propertyCache[property] = converted;
+        return converted;
+        //return property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase()}) // cant use function as 2nd arg yet due to safari bug
+    };
+    
+    var getClassRegEx = function(className) {
+        var re = reClassNameCache[className];
+        if (!re) {
+            re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
+            reClassNameCache[className] = re;
+        }
+        return re;
+    };
+
+    // branching at load instead of runtime
+    if (document.defaultView && document.defaultView.getComputedStyle) { // W3C DOM method
+        getStyle = function(el, property) {
+            var value = null;
+            
+            if (property == 'float') { // fix reserved word
+                property = 'cssFloat';
+            }
+
+            var computed = document.defaultView.getComputedStyle(el, '');
+            if (computed) { // test computed before touching for safari
+                value = computed[toCamel(property)];
+            }
+            
+            return el.style[property] || value;
+        };
+    } else if (document.documentElement.currentStyle && isIE) { // IE method
+        getStyle = function(el, property) {                         
+            switch( toCamel(property) ) {
+                case 'opacity' :// IE opacity uses filter
+                    var val = 100;
+                    try { // will error if no DXImageTransform
+                        val = el.filters['DXImageTransform.Microsoft.Alpha'].opacity;
+
+                    } catch(e) {
+                        try { // make sure its in the document
+                            val = el.filters('alpha').opacity;
+                        } catch(e) {
+                        }
+                    }
+                    return val / 100;
+                case 'float': // fix reserved word
+                    property = 'styleFloat'; // fall through
+                default: 
+                    // test currentStyle before touching
+                    var value = el.currentStyle ? el.currentStyle[property] : null;
+                    return ( el.style[property] || value );
+            }
+        };
+    } else { // default to inline only
+        getStyle = function(el, property) { return el.style[property]; };
+    }
+    
+    if (isIE) {
+        setStyle = function(el, property, val) {
+            switch (property) {
+                case 'opacity':
+                    if ( YAHOO.lang.isString(el.style.filter) ) { // in case not appended
+                        el.style.filter = 'alpha(opacity=' + val * 100 + ')';
+                        
+                        if (!el.currentStyle || !el.currentStyle.hasLayout) {
+                            el.style.zoom = 1; // when no layout or cant tell
+                        }
+                    }
+                    break;
+                case 'float':
+                    property = 'styleFloat';
+                default:
+                el.style[property] = val;
+            }
+        };
+    } else {
+        setStyle = function(el, property, val) {
+            if (property == 'float') {
+                property = 'cssFloat';
+            }
+            el.style[property] = val;
+        };
+    }
+
+    var testElement = function(node, method) {
+        return node && node.nodeType == 1 && ( !method || method(node) );
+    };
+
+    /**
+     * Provides helper methods for DOM elements.
+     * @namespace YAHOO.util
+     * @class Dom
+     */
+    YAHOO.util.Dom = {
+        /**
+         * Returns an HTMLElement reference.
+         * @method get
+         * @param {String | HTMLElement |Array} el Accepts a string to use as an ID for getting a DOM reference, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @return {HTMLElement | Array} A DOM reference to an HTML element or an array of HTMLElements.
+         */
+        get: function(el) {
+            if (el && (el.tagName || el.item)) { // HTMLElement, or HTMLCollection
+                return el;
+            }
+
+            if (YAHOO.lang.isString(el) || !el) { // HTMLElement or null
+                return document.getElementById(el);
+            }
+            
+            if (el.length !== undefined) { // array-like 
+                var c = [];
+                for (var i = 0, len = el.length; i < len; ++i) {
+                    c[c.length] = Y.Dom.get(el[i]);
+                }
+                
+                return c;
+            }
+
+            return el; // some other object, just pass it back
+        },
+    
+        /**
+         * Normalizes currentStyle and ComputedStyle.
+         * @method getStyle
+         * @param {String | HTMLElement |Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @param {String} property The style property whose value is returned.
+         * @return {String | Array} The current value of the style property for the element(s).
+         */
+        getStyle: function(el, property) {
+            property = toCamel(property);
+            
+            var f = function(element) {
+                return getStyle(element, property);
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+    
+        /**
+         * Wrapper for setting style properties of HTMLElements.  Normalizes "opacity" across modern browsers.
+         * @method setStyle
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @param {String} property The style property to be set.
+         * @param {String} val The value to apply to the given property.
+         */
+        setStyle: function(el, property, val) {
+            property = toCamel(property);
+            
+            var f = function(element) {
+                setStyle(element, property, val);
+                
+            };
+            
+            Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Gets the current position of an element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method getXY
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
+         * @return {Array} The XY position of the element(s)
+         */
+        getXY: function(el) {
+            var f = function(el) {
+                // has to be part of document to have pageXY
+                if ( (el.parentNode === null || el.offsetParent === null ||
+                        this.getStyle(el, 'display') == 'none') && el != el.ownerDocument.body) {
+                    return false;
+                }
+                
+                return getXY(el);
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Gets the current X position of an element based on page coordinates.  The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method getX
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
+         * @return {Number | Array} The X position of the element(s)
+         */
+        getX: function(el) {
+            var f = function(el) {
+                return Y.Dom.getXY(el)[0];
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Gets the current Y position of an element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method getY
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
+         * @return {Number | Array} The Y position of the element(s)
+         */
+        getY: function(el) {
+            var f = function(el) {
+                return Y.Dom.getXY(el)[1];
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Set the position of an html element in page coordinates, regardless of how the element is positioned.
+         * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method setXY
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
+         * @param {Array} pos Contains X & Y values for new position (coordinates are page-based)
+         * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
+         */
+        setXY: function(el, pos, noRetry) {
+            var f = function(el) {
+                var style_pos = this.getStyle(el, 'position');
+                if (style_pos == 'static') { // default to relative
+                    this.setStyle(el, 'position', 'relative');
+                    style_pos = 'relative';
+                }
+
+                var pageXY = this.getXY(el);
+                if (pageXY === false) { // has to be part of doc to have pageXY
+                    return false; 
+                }
+                
+                var delta = [ // assuming pixels; if not we will have to retry
+                    parseInt( this.getStyle(el, 'left'), 10 ),
+                    parseInt( this.getStyle(el, 'top'), 10 )
+                ];
+            
+                if ( isNaN(delta[0]) ) {// in case of 'auto'
+                    delta[0] = (style_pos == 'relative') ? 0 : el.offsetLeft;
+                } 
+                if ( isNaN(delta[1]) ) { // in case of 'auto'
+                    delta[1] = (style_pos == 'relative') ? 0 : el.offsetTop;
+                } 
+        
+                if (pos[0] !== null) { el.style.left = pos[0] - pageXY[0] + delta[0] + 'px'; }
+                if (pos[1] !== null) { el.style.top = pos[1] - pageXY[1] + delta[1] + 'px'; }
+              
+                if (!noRetry) {
+                    var newXY = this.getXY(el);
+
+                    // if retry is true, try one more time if we miss 
+                   if ( (pos[0] !== null && newXY[0] != pos[0]) || 
+                        (pos[1] !== null && newXY[1] != pos[1]) ) {
+                       this.setXY(el, pos, true);
+                   }
+                }        
+        
+            };
+            
+            Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
+         * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method setX
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @param {Int} x The value to use as the X coordinate for the element(s).
+         */
+        setX: function(el, x) {
+            Y.Dom.setXY(el, [x, null]);
+        },
+        
+        /**
+         * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
+         * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method setY
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @param {Int} x To use as the Y coordinate for the element(s).
+         */
+        setY: function(el, y) {
+            Y.Dom.setXY(el, [null, y]);
+        },
+        
+        /**
+         * Returns the region position of the given element.
+         * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
+         * @method getRegion
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @return {Region | Array} A Region or array of Region instances containing "top, left, bottom, right" member data.
+         */
+        getRegion: function(el) {
+            var f = function(el) {
+                if ( (el.parentNode === null || el.offsetParent === null ||
+                        this.getStyle(el, 'display') == 'none') && el != document.body) {
+                    return false;
+                }
+
+                var region = Y.Region.getRegion(el);
+                return region;
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Returns the width of the client (viewport).
+         * @method getClientWidth
+         * @deprecated Now using getViewportWidth.  This interface left intact for back compat.
+         * @return {Int} The width of the viewable area of the page.
+         */
+        getClientWidth: function() {
+            return Y.Dom.getViewportWidth();
+        },
+        
+        /**
+         * Returns the height of the client (viewport).
+         * @method getClientHeight
+         * @deprecated Now using getViewportHeight.  This interface left intact for back compat.
+         * @return {Int} The height of the viewable area of the page.
+         */
+        getClientHeight: function() {
+            return Y.Dom.getViewportHeight();
+        },
+
+        /**
+         * Returns a array of HTMLElements with the given class.
+         * For optimized performance, include a tag and/or root node when possible.
+         * @method getElementsByClassName
+         * @param {String} className The class name to match against
+         * @param {String} tag (optional) The tag name of the elements being collected
+         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
+         * @param {Function} apply (optional) A function to apply to each element when found 
+         * @return {Array} An array of elements that have the given class name
+         */
+        getElementsByClassName: function(className, tag, root, apply) {
+            tag = tag || '*';
+            root = (root) ? Y.Dom.get(root) : null || document; 
+            if (!root) {
+                return [];
+            }
+
+            var nodes = [],
+                elements = root.getElementsByTagName(tag),
+                re = getClassRegEx(className);
+
+            for (var i = 0, len = elements.length; i < len; ++i) {
+                if ( re.test(elements[i].className) ) {
+                    nodes[nodes.length] = elements[i];
+                    if (apply) {
+                        apply.call(elements[i], elements[i]);
+                    }
+                }
+            }
+            
+            return nodes;
+        },
+
+        /**
+         * Determines whether an HTMLElement has the given className.
+         * @method hasClass
+         * @param {String | HTMLElement | Array} el The element or collection to test
+         * @param {String} className the class name to search for
+         * @return {Boolean | Array} A boolean value or array of boolean values
+         */
+        hasClass: function(el, className) {
+            var re = getClassRegEx(className);
+
+            var f = function(el) {
+                return re.test(el.className);
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+    
+        /**
+         * Adds a class name to a given element or collection of elements.
+         * @method addClass         
+         * @param {String | HTMLElement | Array} el The element or collection to add the class to
+         * @param {String} className the class name to add to the class attribute
+         * @return {Boolean | Array} A pass/fail boolean or array of booleans
+         */
+        addClass: function(el, className) {
+            var f = function(el) {
+                if (this.hasClass(el, className)) {
+                    return false; // already present
+                }
+                
+                
+                el.className = YAHOO.lang.trim([el.className, className].join(' '));
+                return true;
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+    
+        /**
+         * Removes a class name from a given element or collection of elements.
+         * @method removeClass         
+         * @param {String | HTMLElement | Array} el The element or collection to remove the class from
+         * @param {String} className the class name to remove from the class attribute
+         * @return {Boolean | Array} A pass/fail boolean or array of booleans
+         */
+        removeClass: function(el, className) {
+            var re = getClassRegEx(className);
+            
+            var f = function(el) {
+                if (!this.hasClass(el, className)) {
+                    return false; // not present
+                }                 
+
+                
+                var c = el.className;
+                el.className = c.replace(re, ' ');
+                if ( this.hasClass(el, className) ) { // in case of multiple adjacent
+                    this.removeClass(el, className);
+                }
+
+                el.className = YAHOO.lang.trim(el.className); // remove any trailing spaces
+                return true;
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Replace a class with another class for a given element or collection of elements.
+         * If no oldClassName is present, the newClassName is simply added.
+         * @method replaceClass  
+         * @param {String | HTMLElement | Array} el The element or collection to remove the class from
+         * @param {String} oldClassName the class name to be replaced
+         * @param {String} newClassName the class name that will be replacing the old class name
+         * @return {Boolean | Array} A pass/fail boolean or array of booleans
+         */
+        replaceClass: function(el, oldClassName, newClassName) {
+            if (!newClassName || oldClassName === newClassName) { // avoid infinite loop
+                return false;
+            }
+            
+            var re = getClassRegEx(oldClassName);
+
+            var f = function(el) {
+            
+                if ( !this.hasClass(el, oldClassName) ) {
+                    this.addClass(el, newClassName); // just add it if nothing to replace
+                    return true; // NOTE: return
+                }
+            
+                el.className = el.className.replace(re, ' ' + newClassName + ' ');
+
+                if ( this.hasClass(el, oldClassName) ) { // in case of multiple adjacent
+                    this.replaceClass(el, oldClassName, newClassName);
+                }
+
+                el.className = YAHOO.lang.trim(el.className); // remove any trailing spaces
+                return true;
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Returns an ID and applies it to the element "el", if provided.
+         * @method generateId  
+         * @param {String | HTMLElement | Array} el (optional) An optional element array of elements to add an ID to (no ID is added if one is already present).
+         * @param {String} prefix (optional) an optional prefix to use (defaults to "yui-gen").
+         * @return {String | Array} The generated ID, or array of generated IDs (or original ID if already present on an element)
+         */
+        generateId: function(el, prefix) {
+            prefix = prefix || 'yui-gen';
+
+            var f = function(el) {
+                if (el && el.id) { // do not override existing ID
+                    return el.id;
+                } 
+
+                var id = prefix + id_counter++;
+
+                if (el) {
+                    el.id = id;
+                }
+                
+                return id;
+            };
+
+            // batch fails when no element, so just generate and return single ID
+            return Y.Dom.batch(el, f, Y.Dom, true) || f.apply(Y.Dom, arguments);
+        },
+        
+        /**
+         * Determines whether an HTMLElement is an ancestor of another HTML element in the DOM hierarchy.
+         * @method isAncestor
+         * @param {String | HTMLElement} haystack The possible ancestor
+         * @param {String | HTMLElement} needle The possible descendent
+         * @return {Boolean} Whether or not the haystack is an ancestor of needle
+         */
+        isAncestor: function(haystack, needle) {
+            haystack = Y.Dom.get(haystack);
+            needle = Y.Dom.get(needle);
+            
+            if (!haystack || !needle) {
+                return false;
+            }
+
+            if (haystack.contains && needle.nodeType && !isSafari) { // safari contains is broken
+                return haystack.contains(needle);
+            }
+            else if ( haystack.compareDocumentPosition && needle.nodeType ) {
+                return !!(haystack.compareDocumentPosition(needle) & 16);
+            } else if (needle.nodeType) {
+                // fallback to crawling up (safari)
+                return !!this.getAncestorBy(needle, function(el) {
+                    return el == haystack; 
+                }); 
+            }
+            return false;
+        },
+        
+        /**
+         * Determines whether an HTMLElement is present in the current document.
+         * @method inDocument         
+         * @param {String | HTMLElement} el The element to search for
+         * @return {Boolean} Whether or not the element is present in the current document
+         */
+        inDocument: function(el) {
+            return this.isAncestor(document.documentElement, el);
+        },
+        
+        /**
+         * Returns a array of HTMLElements that pass the test applied by supplied boolean method.
+         * For optimized performance, include a tag and/or root node when possible.
+         * @method getElementsBy
+         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
+         * @param {String} tag (optional) The tag name of the elements being collected
+         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
+         * @param {Function} apply (optional) A function to apply to each element when found 
+         * @return {Array} Array of HTMLElements
+         */
+        getElementsBy: function(method, tag, root, apply) {
+            tag = tag || '*';
+            root = (root) ? Y.Dom.get(root) : null || document; 
+
+            if (!root) {
+                return [];
+            }
+
+            var nodes = [],
+                elements = root.getElementsByTagName(tag);
+            
+            for (var i = 0, len = elements.length; i < len; ++i) {
+                if ( method(elements[i]) ) {
+                    nodes[nodes.length] = elements[i];
+                    if (apply) {
+                        apply(elements[i]);
+                    }
+                }
+            }
+
+            
+            return nodes;
+        },
+        
+        /**
+         * Runs the supplied method against each item in the Collection/Array.
+         * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) ).
+         * @method batch
+         * @param {String | HTMLElement | Array} el (optional) An element or array of elements to apply the method to
+         * @param {Function} method The method to apply to the element(s)
+         * @param {Any} o (optional) An optional arg that is passed to the supplied method
+         * @param {Boolean} override (optional) Whether or not to override the scope of "method" with "o"
+         * @return {Any | Array} The return value(s) from the supplied method
+         */
+        batch: function(el, method, o, override) {
+            el = (el && (el.tagName || el.item)) ? el : Y.Dom.get(el); // skip get() when possible
+
+            if (!el || !method) {
+                return false;
+            } 
+            var scope = (override) ? o : window;
+            
+            if (el.tagName || el.length === undefined) { // element or not array-like 
+                return method.call(scope, el, o);
+            } 
+
+            var collection = [];
+            
+            for (var i = 0, len = el.length; i < len; ++i) {
+                collection[collection.length] = method.call(scope, el[i], o);
+            }
+            
+            return collection;
+        },
+        
+        /**
+         * Returns the height of the document.
+         * @method getDocumentHeight
+         * @return {Int} The height of the actual document (which includes the body and its margin).
+         */
+        getDocumentHeight: function() {
+            var scrollHeight = (document.compatMode != 'CSS1Compat') ? document.body.scrollHeight : document.documentElement.scrollHeight;
+
+            var h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
+            return h;
+        },
+        
+        /**
+         * Returns the width of the document.
+         * @method getDocumentWidth
+         * @return {Int} The width of the actual document (which includes the body and its margin).
+         */
+        getDocumentWidth: function() {
+            var scrollWidth = (document.compatMode != 'CSS1Compat') ? document.body.scrollWidth : document.documentElement.scrollWidth;
+            var w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
+            return w;
+        },
+
+        /**
+         * Returns the current height of the viewport.
+         * @method getViewportHeight
+         * @return {Int} The height of the viewable area of the page (excludes scrollbars).
+         */
+        getViewportHeight: function() {
+            var height = self.innerHeight; // Safari, Opera
+            var mode = document.compatMode;
+        
+            if ( (mode || isIE) && !isOpera ) { // IE, Gecko
+                height = (mode == 'CSS1Compat') ?
+                        document.documentElement.clientHeight : // Standards
+                        document.body.clientHeight; // Quirks
+            }
+        
+            return height;
+        },
+        
+        /**
+         * Returns the current width of the viewport.
+         * @method getViewportWidth
+         * @return {Int} The width of the viewable area of the page (excludes scrollbars).
+         */
+        
+        getViewportWidth: function() {
+            var width = self.innerWidth;  // Safari
+            var mode = document.compatMode;
+            
+            if (mode || isIE) { // IE, Gecko, Opera
+                width = (mode == 'CSS1Compat') ?
+                        document.documentElement.clientWidth : // Standards
+                        document.body.clientWidth; // Quirks
+            }
+            return width;
+        },
+
+       /**
+         * Returns the nearest ancestor that passes the test applied by supplied boolean method.
+         * For performance reasons, IDs are not accepted and argument validation omitted.
+         * @method getAncestorBy
+         * @param {HTMLElement} node The HTMLElement to use as the starting point 
+         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
+         * @return {Object} HTMLElement or null if not found
+         */
+        getAncestorBy: function(node, method) {
+            while (node = node.parentNode) { // NOTE: assignment
+                if ( testElement(node, method) ) {
+                    return node;
+                }
+            } 
+
+            return null;
+        },
+        
+        /**
+         * Returns the nearest ancestor with the given className.
+         * @method getAncestorByClassName
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @param {String} className
+         * @return {Object} HTMLElement
+         */
+        getAncestorByClassName: function(node, className) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                return null;
+            }
+            var method = function(el) { return Y.Dom.hasClass(el, className); };
+            return Y.Dom.getAncestorBy(node, method);
+        },
+
+        /**
+         * Returns the nearest ancestor with the given tagName.
+         * @method getAncestorByTagName
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @param {String} tagName
+         * @return {Object} HTMLElement
+         */
+        getAncestorByTagName: function(node, tagName) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                return null;
+            }
+            var method = function(el) {
+                 return el.tagName && el.tagName.toUpperCase() == tagName.toUpperCase();
+            };
+
+            return Y.Dom.getAncestorBy(node, method);
+        },
+
+        /**
+         * Returns the previous sibling that is an HTMLElement. 
+         * For performance reasons, IDs are not accepted and argument validation omitted.
+         * Returns the nearest HTMLElement sibling if no method provided.
+         * @method getPreviousSiblingBy
+         * @param {HTMLElement} node The HTMLElement to use as the starting point 
+         * @param {Function} method A boolean function used to test siblings
+         * that receives the sibling node being tested as its only argument
+         * @return {Object} HTMLElement or null if not found
+         */
+        getPreviousSiblingBy: function(node, method) {
+            while (node) {
+                node = node.previousSibling;
+                if ( testElement(node, method) ) {
+                    return node;
+                }
+            }
+            return null;
+        }, 
+
+        /**
+         * Returns the previous sibling that is an HTMLElement 
+         * @method getPreviousSibling
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @return {Object} HTMLElement or null if not found
+         */
+        getPreviousSibling: function(node) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                return null;
+            }
+
+            return Y.Dom.getPreviousSiblingBy(node);
+        }, 
+
+        /**
+         * Returns the next HTMLElement sibling that passes the boolean method. 
+         * For performance reasons, IDs are not accepted and argument validation omitted.
+         * Returns the nearest HTMLElement sibling if no method provided.
+         * @method getNextSiblingBy
+         * @param {HTMLElement} node The HTMLElement to use as the starting point 
+         * @param {Function} method A boolean function used to test siblings
+         * that receives the sibling node being tested as its only argument
+         * @return {Object} HTMLElement or null if not found
+         */
+        getNextSiblingBy: function(node, method) {
+            while (node) {
+                node = node.nextSibling;
+                if ( testElement(node, method) ) {
+                    return node;
+                }
+            }
+            return null;
+        }, 
+
+        /**
+         * Returns the next sibling that is an HTMLElement 
+         * @method getNextSibling
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @return {Object} HTMLElement or null if not found
+         */
+        getNextSibling: function(node) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                return null;
+            }
+
+            return Y.Dom.getNextSiblingBy(node);
+        }, 
+
+        /**
+         * Returns the first HTMLElement child that passes the test method. 
+         * @method getFirstChildBy
+         * @param {HTMLElement} node The HTMLElement to use as the starting point 
+         * @param {Function} method A boolean function used to test children
+         * that receives the node being tested as its only argument
+         * @return {Object} HTMLElement or null if not found
+         */
+        getFirstChildBy: function(node, method) {
+            var child = ( testElement(node.firstChild, method) ) ? node.firstChild : null;
+            return child || Y.Dom.getNextSiblingBy(node.firstChild, method);
+        }, 
+
+        /**
+         * Returns the first HTMLElement child. 
+         * @method getFirstChild
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @return {Object} HTMLElement or null if not found
+         */
+        getFirstChild: function(node, method) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                return null;
+            }
+            return Y.Dom.getFirstChildBy(node);
+        }, 
+
+        /**
+         * Returns the last HTMLElement child that passes the test method. 
+         * @method getLastChildBy
+         * @param {HTMLElement} node The HTMLElement to use as the starting point 
+         * @param {Function} method A boolean function used to test children
+         * that receives the node being tested as its only argument
+         * @return {Object} HTMLElement or null if not found
+         */
+        getLastChildBy: function(node, method) {
+            if (!node) {
+                return null;
+            }
+            var child = ( testElement(node.lastChild, method) ) ? node.lastChild : null;
+            return child || Y.Dom.getPreviousSiblingBy(node.lastChild, method);
+        }, 
+
+        /**
+         * Returns the last HTMLElement child. 
+         * @method getLastChild
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @return {Object} HTMLElement or null if not found
+         */
+        getLastChild: function(node) {
+            node = Y.Dom.get(node);
+            return Y.Dom.getLastChildBy(node);
+        }, 
+
+        /**
+         * Returns an array of HTMLElement childNodes that pass the test method. 
+         * @method getChildrenBy
+         * @param {HTMLElement} node The HTMLElement to start from
+         * @param {Function} method A boolean function used to test children
+         * that receives the node being tested as its only argument
+         * @return {Array} A static array of HTMLElements
+         */
+        getChildrenBy: function(node, method) {
+            var child = Y.Dom.getFirstChildBy(node, method);
+            var children = child ? [child] : [];
+
+            Y.Dom.getNextSiblingBy(child, function(node) {
+                if ( !method || method(node) ) {
+                    children[children.length] = node;
+                }
+                return false; // fail test to collect all children
+            });
+
+            return children;
+        },
+ 
+        /**
+         * Returns an array of HTMLElement childNodes. 
+         * @method getChildren
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @return {Array} A static array of HTMLElements
+         */
+        getChildren: function(node) {
+            node = Y.Dom.get(node);
+            if (!node) {
+            }
+
+            return Y.Dom.getChildrenBy(node);
+        },
+ 
+        /**
+         * Returns the left scroll value of the document 
+         * @method getDocumentScrollLeft
+         * @param {HTMLDocument} document (optional) The document to get the scroll value of
+         * @return {Int}  The amount that the document is scrolled to the left
+         */
+        getDocumentScrollLeft: function(doc) {
+            doc = doc || document;
+            return Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
+        }, 
+
+        /**
+         * Returns the top scroll value of the document 
+         * @method getDocumentScrollTop
+         * @param {HTMLDocument} document (optional) The document to get the scroll value of
+         * @return {Int}  The amount that the document is scrolled to the top
+         */
+        getDocumentScrollTop: function(doc) {
+            doc = doc || document;
+            return Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
+        },
+
+        /**
+         * Inserts the new node as the previous sibling of the reference node 
+         * @method insertBefore
+         * @param {String | HTMLElement} newNode The node to be inserted
+         * @param {String | HTMLElement} referenceNode The node to insert the new node before 
+         * @return {HTMLElement} The node that was inserted (or null if insert fails) 
+         */
+        insertBefore: function(newNode, referenceNode) {
+            newNode = Y.Dom.get(newNode); 
+            referenceNode = Y.Dom.get(referenceNode); 
+            
+            if (!newNode || !referenceNode || !referenceNode.parentNode) {
+                return null;
+            }       
+
+            return referenceNode.parentNode.insertBefore(newNode, referenceNode); 
+        },
+
+        /**
+         * Inserts the new node as the next sibling of the reference node 
+         * @method insertAfter
+         * @param {String | HTMLElement} newNode The node to be inserted
+         * @param {String | HTMLElement} referenceNode The node to insert the new node after 
+         * @return {HTMLElement} The node that was inserted (or null if insert fails) 
+         */
+        insertAfter: function(newNode, referenceNode) {
+            newNode = Y.Dom.get(newNode); 
+            referenceNode = Y.Dom.get(referenceNode); 
+            
+            if (!newNode || !referenceNode || !referenceNode.parentNode) {
+                return null;
+            }       
+
+            if (referenceNode.nextSibling) {
+                return referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); 
+            } else {
+                return referenceNode.parentNode.appendChild(newNode);
+            }
+        },
+
+        /**
+         * Creates a Region based on the viewport relative to the document. 
+         * @method getClientRegion
+         * @return {Region} A Region object representing the viewport which accounts for document scroll
+         */
+        getClientRegion: function() {
+            var t = Y.Dom.getDocumentScrollTop(),
+                l = Y.Dom.getDocumentScrollLeft(),
+                r = Y.Dom.getViewportWidth() + l,
+                b = Y.Dom.getViewportHeight() + t;
+
+            return new Y.Region(t, r, b, l);
+        }
+    };
+    
+    var getXY = function() {
+        if (document.documentElement.getBoundingClientRect) { // IE
+            return function(el) {
+                var box = el.getBoundingClientRect();
+
+                var rootNode = el.ownerDocument;
+                return [box.left + Y.Dom.getDocumentScrollLeft(rootNode), box.top +
+                        Y.Dom.getDocumentScrollTop(rootNode)];
+            };
+        } else {
+            return function(el) { // manually calculate by crawling up offsetParents
+                var pos = [el.offsetLeft, el.offsetTop];
+                var parentNode = el.offsetParent;
+
+                // safari: subtract body offsets if el is abs (or any offsetParent), unless body is offsetParent
+                var accountForBody = (isSafari &&
+                        Y.Dom.getStyle(el, 'position') == 'absolute' &&
+                        el.offsetParent == el.ownerDocument.body);
+
+                if (parentNode != el) {
+                    while (parentNode) {
+                        pos[0] += parentNode.offsetLeft;
+                        pos[1] += parentNode.offsetTop;
+                        if (!accountForBody && isSafari && 
+                                Y.Dom.getStyle(parentNode,'position') == 'absolute' ) { 
+                            accountForBody = true;
+                        }
+                        parentNode = parentNode.offsetParent;
+                    }
+                }
+
+                if (accountForBody) { //safari doubles in this case
+                    pos[0] -= el.ownerDocument.body.offsetLeft;
+                    pos[1] -= el.ownerDocument.body.offsetTop;
+                } 
+                parentNode = el.parentNode;
+
+                // account for any scrolled ancestors
+                while ( parentNode.tagName && !patterns.ROOT_TAG.test(parentNode.tagName) ) 
+                {
+                   // work around opera inline/table scrollLeft/Top bug
+                   if (Y.Dom.getStyle(parentNode, 'display').search(/^inline|table-row.*$/i)) { 
+                        pos[0] -= parentNode.scrollLeft;
+                        pos[1] -= parentNode.scrollTop;
+                    }
+                    
+                    parentNode = parentNode.parentNode; 
+                }
+
+                return pos;
+            };
+        }
+    }() // NOTE: Executing for loadtime branching
+})();
+/**
+ * A region is a representation of an object on a grid.  It is defined
+ * by the top, right, bottom, left extents, so is rectangular by default.  If 
+ * other shapes are required, this class could be extended to support it.
+ * @namespace YAHOO.util
+ * @class Region
+ * @param {Int} t the top extent
+ * @param {Int} r the right extent
+ * @param {Int} b the bottom extent
+ * @param {Int} l the left extent
+ * @constructor
+ */
+YAHOO.util.Region = function(t, r, b, l) {
+
+    /**
+     * The region's top extent
+     * @property top
+     * @type Int
+     */
+    this.top = t;
+    
+    /**
+     * The region's top extent as index, for symmetry with set/getXY
+     * @property 1
+     * @type Int
+     */
+    this[1] = t;
+
+    /**
+     * The region's right extent
+     * @property right
+     * @type int
+     */
+    this.right = r;
+
+    /**
+     * The region's bottom extent
+     * @property bottom
+     * @type Int
+     */
+    this.bottom = b;
+
+    /**
+     * The region's left extent
+     * @property left
+     * @type Int
+     */
+    this.left = l;
+    
+    /**
+     * The region's left extent as index, for symmetry with set/getXY
+     * @property 0
+     * @type Int
+     */
+    this[0] = l;
+};
+
+/**
+ * Returns true if this region contains the region passed in
+ * @method contains
+ * @param  {Region}  region The region to evaluate
+ * @return {Boolean}        True if the region is contained with this region, 
+ *                          else false
+ */
+YAHOO.util.Region.prototype.contains = function(region) {
+    return ( region.left   >= this.left   && 
+             region.right  <= this.right  && 
+             region.top    >= this.top    && 
+             region.bottom <= this.bottom    );
+
+};
+
+/**
+ * Returns the area of the region
+ * @method getArea
+ * @return {Int} the region's area
+ */
+YAHOO.util.Region.prototype.getArea = function() {
+    return ( (this.bottom - this.top) * (this.right - this.left) );
+};
+
+/**
+ * Returns the region where the passed in region overlaps with this one
+ * @method intersect
+ * @param  {Region} region The region that intersects
+ * @return {Region}        The overlap region, or null if there is no overlap
+ */
+YAHOO.util.Region.prototype.intersect = function(region) {
+    var t = Math.max( this.top,    region.top    );
+    var r = Math.min( this.right,  region.right  );
+    var b = Math.min( this.bottom, region.bottom );
+    var l = Math.max( this.left,   region.left   );
+    
+    if (b >= t && r >= l) {
+        return new YAHOO.util.Region(t, r, b, l);
+    } else {
+        return null;
+    }
+};
+
+/**
+ * Returns the region representing the smallest region that can contain both
+ * the passed in region and this region.
+ * @method union
+ * @param  {Region} region The region that to create the union with
+ * @return {Region}        The union region
+ */
+YAHOO.util.Region.prototype.union = function(region) {
+    var t = Math.min( this.top,    region.top    );
+    var r = Math.max( this.right,  region.right  );
+    var b = Math.max( this.bottom, region.bottom );
+    var l = Math.min( this.left,   region.left   );
+
+    return new YAHOO.util.Region(t, r, b, l);
+};
+
+/**
+ * toString
+ * @method toString
+ * @return string the region properties
+ */
+YAHOO.util.Region.prototype.toString = function() {
+    return ( "Region {"    +
+             "top: "       + this.top    + 
+             ", right: "   + this.right  + 
+             ", bottom: "  + this.bottom + 
+             ", left: "    + this.left   + 
+             "}" );
+};
+
+/**
+ * Returns a region that is occupied by the DOM element
+ * @method getRegion
+ * @param  {HTMLElement} el The element
+ * @return {Region}         The region that the element occupies
+ * @static
+ */
+YAHOO.util.Region.getRegion = function(el) {
+    var p = YAHOO.util.Dom.getXY(el);
+
+    var t = p[1];
+    var r = p[0] + el.offsetWidth;
+    var b = p[1] + el.offsetHeight;
+    var l = p[0];
+
+    return new YAHOO.util.Region(t, r, b, l);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * A point is a region that is special in that it represents a single point on 
+ * the grid.
+ * @namespace YAHOO.util
+ * @class Point
+ * @param {Int} x The X position of the point
+ * @param {Int} y The Y position of the point
+ * @constructor
+ * @extends YAHOO.util.Region
+ */
+YAHOO.util.Point = function(x, y) {
+   if (YAHOO.lang.isArray(x)) { // accept input from Dom.getXY, Event.getXY, etc.
+      y = x[1]; // dont blow away x yet
+      x = x[0];
+   }
+   
+    /**
+     * The X position of the point, which is also the right, left and index zero (for Dom.getXY symmetry)
+     * @property x
+     * @type Int
+     */
+
+    this.x = this.right = this.left = this[0] = x;
+     
+    /**
+     * The Y position of the point, which is also the top, bottom and index one (for Dom.getXY symmetry)
+     * @property y
+     * @type Int
+     */
+    this.y = this.top = this.bottom = this[1] = y;
+};
+
+YAHOO.util.Point.prototype = new YAHOO.util.Region();
+
+YAHOO.register("dom", YAHOO.util.Dom, {version: "2.4.1", build: "742"});
Index: trunk/wb/include/yui/dom/dom-min.js
===================================================================
--- trunk/wb/include/yui/dom/dom-min.js	(nonexistent)
+++ trunk/wb/include/yui/dom/dom-min.js	(revision 601)
@@ -0,0 +1,8 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+(function(){var B=YAHOO.util,L,J,H=0,K={},F={},N=window.document;var C=YAHOO.env.ua.opera,M=YAHOO.env.ua.webkit,A=YAHOO.env.ua.gecko,G=YAHOO.env.ua.ie;var E={HYPHEN:/(-[a-z])/i,ROOT_TAG:/^body|html$/i};var O=function(Q){if(!E.HYPHEN.test(Q)){return Q;}if(K[Q]){return K[Q];}var R=Q;while(E.HYPHEN.exec(R)){R=R.replace(RegExp.$1,RegExp.$1.substr(1).toUpperCase());}K[Q]=R;return R;};var P=function(R){var Q=F[R];if(!Q){Q=new RegExp("(?:^|\\s+)"+R+"(?:\\s+|$)");F[R]=Q;}return Q;};if(N.defaultView&&N.defaultView.getComputedStyle){L=function(Q,T){var S=null;if(T=="float"){T="cssFloat";}var R=N.defaultView.getComputedStyle(Q,"");if(R){S=R[O(T)];}return Q.style[T]||S;};}else{if(N.documentElement.currentStyle&&G){L=function(Q,S){switch(O(S)){case"opacity":var U=100;try{U=Q.filters["DXImageTransform.Microsoft.Alpha"].opacity;}catch(T){try{U=Q.filters("alpha").opacity;}catch(T){}}return U/100;case"float":S="styleFloat";default:var R=Q.currentStyle?Q.currentStyle[S]:null;return(Q.style[S]||R);}};}else{L=function(Q,R){return Q.style[R];};}}if(G){J=function(Q,R,S){switch(R){case"opacity":if(YAHOO.lang.isString(Q.style.filter)){Q.style.filter="alpha(opacity="+S*100+")";if(!Q.currentStyle||!Q.currentStyle.hasLayout){Q.style.zoom=1;}}break;case"float":R="styleFloat";default:Q.style[R]=S;}};}else{J=function(Q,R,S){if(R=="float"){R="cssFloat";}Q.style[R]=S;};}var D=function(Q,R){return Q&&Q.nodeType==1&&(!R||R(Q));};YAHOO.util.Dom={get:function(S){if(S&&(S.tagName||S.item)){return S;}if(YAHOO.lang.isString(S)||!S){return N.getElementById(S);}if(S.length!==undefined){var T=[];for(var R=0,Q=S.length;R<Q;++R){T[T.length]=B.Dom.get(S[R]);}return T;}return S;},getStyle:function(Q,S){S=O(S);var R=function(T){return L(T,S);};return B.Dom.batch(Q,R,B.Dom,true);},setStyle:function(Q,S,T){S=O(S);var R=function(U){J(U,S,T);};B.Dom.batch(Q,R,B.Dom,true);},getXY:function(Q){var R=function(S){if((S.parentNode===null||S.offsetParent===null||this.getStyle(S,"display")=="none")&&S!=S.ownerDocument.body){return false;}return I(S);};return B.Dom.batch(Q,R,B.Dom,true);},getX:function(Q){var R=function(S){return B.Dom.getXY(S)[0];};return B.Dom.batch(Q,R,B.Dom,true);},getY:function(Q){var R=function(S){return B.Dom.getXY(S)[1];};return B.Dom.batch(Q,R,B.Dom,true);},setXY:function(Q,T,S){var R=function(W){var V=this.getStyle(W,"position");if(V=="static"){this.setStyle(W,"position","relative");V="relative";}var Y=this.getXY(W);if(Y===false){return false;}var X=[parseInt(this.getStyle(W,"left"),10),parseInt(this.getStyle(W,"top"),10)];if(isNaN(X[0])){X[0]=(V=="relative")?0:W.offsetLeft;}if(isNaN(X[1])){X[1]=(V=="relative")?0:W.offsetTop;}if(T[0]!==null){W.style.left=T[0]-Y[0]+X[0]+"px";}if(T[1]!==null){W.style.top=T[1]-Y[1]+X[1]+"px";}if(!S){var U=this.getXY(W);if((T[0]!==null&&U[0]!=T[0])||(T[1]!==null&&U[1]!=T[1])){this.setXY(W,T,true);}}};B.Dom.batch(Q,R,B.Dom,true);},setX:function(R,Q){B.Dom.setXY(R,[Q,null]);},setY:function(Q,R){B.Dom.setXY(Q,[null,R]);},getRegion:function(Q){var R=function(S){if((S.parentNode===null||S.offsetParent===null||this.getStyle(S,"display")=="none")&&S!=N.body){return false;}var T=B.Region.getRegion(S);return T;};return B.Dom.batch(Q,R,B.Dom,true);},getClientWidth:function(){return B.Dom.getViewportWidth();},getClientHeight:function(){return B.Dom.getViewportHeight();},getElementsByClassName:function(U,Y,V,W){Y=Y||"*";V=(V)?B.Dom.get(V):null||N;if(!V){return[];}var R=[],Q=V.getElementsByTagName(Y),X=P(U);for(var S=0,T=Q.length;S<T;++S){if(X.test(Q[S].className)){R[R.length]=Q[S];if(W){W.call(Q[S],Q[S]);}}}return R;},hasClass:function(S,R){var Q=P(R);var T=function(U){return Q.test(U.className);};return B.Dom.batch(S,T,B.Dom,true);},addClass:function(R,Q){var S=function(T){if(this.hasClass(T,Q)){return false;}T.className=YAHOO.lang.trim([T.className,Q].join(" "));return true;};return B.Dom.batch(R,S,B.Dom,true);},removeClass:function(S,R){var Q=P(R);var T=function(U){if(!this.hasClass(U,R)){return false;}var V=U.className;U.className=V.replace(Q," ");if(this.hasClass(U,R)){this.removeClass(U,R);}U.className=YAHOO.lang.trim(U.className);return true;};return B.Dom.batch(S,T,B.Dom,true);},replaceClass:function(T,R,Q){if(!Q||R===Q){return false;}var S=P(R);var U=function(V){if(!this.hasClass(V,R)){this.addClass(V,Q);return true;}V.className=V.className.replace(S," "+Q+" ");if(this.hasClass(V,R)){this.replaceClass(V,R,Q);}V.className=YAHOO.lang.trim(V.className);return true;};return B.Dom.batch(T,U,B.Dom,true);},generateId:function(Q,S){S=S||"yui-gen";var R=function(T){if(T&&T.id){return T.id;}var U=S+H++;if(T){T.id=U;}return U;};return B.Dom.batch(Q,R,B.Dom,true)||R.apply(B.Dom,arguments);},isAncestor:function(Q,R){Q=B.Dom.get(Q);R=B.Dom.get(R);if(!Q||!R){return false;}if(Q.contains&&R.nodeType&&!M){return Q.contains(R);}else{if(Q.compareDocumentPosition&&R.nodeType){return !!(Q.compareDocumentPosition(R)&16);}else{if(R.nodeType){return !!this.getAncestorBy(R,function(S){return S==Q;});}}}return false;},inDocument:function(Q){return this.isAncestor(N.documentElement,Q);},getElementsBy:function(X,R,S,U){R=R||"*";S=(S)?B.Dom.get(S):null||N;if(!S){return[];}var T=[],W=S.getElementsByTagName(R);for(var V=0,Q=W.length;V<Q;++V){if(X(W[V])){T[T.length]=W[V];if(U){U(W[V]);}}}return T;},batch:function(U,X,W,S){U=(U&&(U.tagName||U.item))?U:B.Dom.get(U);if(!U||!X){return false;}var T=(S)?W:window;if(U.tagName||U.length===undefined){return X.call(T,U,W);}var V=[];for(var R=0,Q=U.length;R<Q;++R){V[V.length]=X.call(T,U[R],W);}return V;},getDocumentHeight:function(){var R=(N.compatMode!="CSS1Compat")?N.body.scrollHeight:N.documentElement.scrollHeight;var Q=Math.max(R,B.Dom.getViewportHeight());return Q;},getDocumentWidth:function(){var R=(N.compatMode!="CSS1Compat")?N.body.scrollWidth:N.documentElement.scrollWidth;var Q=Math.max(R,B.Dom.getViewportWidth());return Q;},getViewportHeight:function(){var Q=self.innerHeight;var R=N.compatMode;if((R||G)&&!C){Q=(R=="CSS1Compat")?N.documentElement.clientHeight:N.body.clientHeight;
+}return Q;},getViewportWidth:function(){var Q=self.innerWidth;var R=N.compatMode;if(R||G){Q=(R=="CSS1Compat")?N.documentElement.clientWidth:N.body.clientWidth;}return Q;},getAncestorBy:function(Q,R){while(Q=Q.parentNode){if(D(Q,R)){return Q;}}return null;},getAncestorByClassName:function(R,Q){R=B.Dom.get(R);if(!R){return null;}var S=function(T){return B.Dom.hasClass(T,Q);};return B.Dom.getAncestorBy(R,S);},getAncestorByTagName:function(R,Q){R=B.Dom.get(R);if(!R){return null;}var S=function(T){return T.tagName&&T.tagName.toUpperCase()==Q.toUpperCase();};return B.Dom.getAncestorBy(R,S);},getPreviousSiblingBy:function(Q,R){while(Q){Q=Q.previousSibling;if(D(Q,R)){return Q;}}return null;},getPreviousSibling:function(Q){Q=B.Dom.get(Q);if(!Q){return null;}return B.Dom.getPreviousSiblingBy(Q);},getNextSiblingBy:function(Q,R){while(Q){Q=Q.nextSibling;if(D(Q,R)){return Q;}}return null;},getNextSibling:function(Q){Q=B.Dom.get(Q);if(!Q){return null;}return B.Dom.getNextSiblingBy(Q);},getFirstChildBy:function(Q,S){var R=(D(Q.firstChild,S))?Q.firstChild:null;return R||B.Dom.getNextSiblingBy(Q.firstChild,S);},getFirstChild:function(Q,R){Q=B.Dom.get(Q);if(!Q){return null;}return B.Dom.getFirstChildBy(Q);},getLastChildBy:function(Q,S){if(!Q){return null;}var R=(D(Q.lastChild,S))?Q.lastChild:null;return R||B.Dom.getPreviousSiblingBy(Q.lastChild,S);},getLastChild:function(Q){Q=B.Dom.get(Q);return B.Dom.getLastChildBy(Q);},getChildrenBy:function(R,T){var S=B.Dom.getFirstChildBy(R,T);var Q=S?[S]:[];B.Dom.getNextSiblingBy(S,function(U){if(!T||T(U)){Q[Q.length]=U;}return false;});return Q;},getChildren:function(Q){Q=B.Dom.get(Q);if(!Q){}return B.Dom.getChildrenBy(Q);},getDocumentScrollLeft:function(Q){Q=Q||N;return Math.max(Q.documentElement.scrollLeft,Q.body.scrollLeft);},getDocumentScrollTop:function(Q){Q=Q||N;return Math.max(Q.documentElement.scrollTop,Q.body.scrollTop);},insertBefore:function(R,Q){R=B.Dom.get(R);Q=B.Dom.get(Q);if(!R||!Q||!Q.parentNode){return null;}return Q.parentNode.insertBefore(R,Q);},insertAfter:function(R,Q){R=B.Dom.get(R);Q=B.Dom.get(Q);if(!R||!Q||!Q.parentNode){return null;}if(Q.nextSibling){return Q.parentNode.insertBefore(R,Q.nextSibling);}else{return Q.parentNode.appendChild(R);}},getClientRegion:function(){var S=B.Dom.getDocumentScrollTop(),R=B.Dom.getDocumentScrollLeft(),T=B.Dom.getViewportWidth()+R,Q=B.Dom.getViewportHeight()+S;return new B.Region(S,T,Q,R);}};var I=function(){if(N.documentElement.getBoundingClientRect){return function(R){var S=R.getBoundingClientRect();var Q=R.ownerDocument;return[S.left+B.Dom.getDocumentScrollLeft(Q),S.top+B.Dom.getDocumentScrollTop(Q)];};}else{return function(S){var T=[S.offsetLeft,S.offsetTop];var R=S.offsetParent;var Q=(M&&B.Dom.getStyle(S,"position")=="absolute"&&S.offsetParent==S.ownerDocument.body);if(R!=S){while(R){T[0]+=R.offsetLeft;T[1]+=R.offsetTop;if(!Q&&M&&B.Dom.getStyle(R,"position")=="absolute"){Q=true;}R=R.offsetParent;}}if(Q){T[0]-=S.ownerDocument.body.offsetLeft;T[1]-=S.ownerDocument.body.offsetTop;}R=S.parentNode;while(R.tagName&&!E.ROOT_TAG.test(R.tagName)){if(B.Dom.getStyle(R,"display").search(/^inline|table-row.*$/i)){T[0]-=R.scrollLeft;T[1]-=R.scrollTop;}R=R.parentNode;}return T;};}}();})();YAHOO.util.Region=function(C,D,A,B){this.top=C;this[1]=C;this.right=D;this.bottom=A;this.left=B;this[0]=B;};YAHOO.util.Region.prototype.contains=function(A){return(A.left>=this.left&&A.right<=this.right&&A.top>=this.top&&A.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(E){var C=Math.max(this.top,E.top);var D=Math.min(this.right,E.right);var A=Math.min(this.bottom,E.bottom);var B=Math.max(this.left,E.left);if(A>=C&&D>=B){return new YAHOO.util.Region(C,D,A,B);}else{return null;}};YAHOO.util.Region.prototype.union=function(E){var C=Math.min(this.top,E.top);var D=Math.max(this.right,E.right);var A=Math.max(this.bottom,E.bottom);var B=Math.min(this.left,E.left);return new YAHOO.util.Region(C,D,A,B);};YAHOO.util.Region.prototype.toString=function(){return("Region {top: "+this.top+", right: "+this.right+", bottom: "+this.bottom+", left: "+this.left+"}");};YAHOO.util.Region.getRegion=function(D){var F=YAHOO.util.Dom.getXY(D);var C=F[1];var E=F[0]+D.offsetWidth;var A=F[1]+D.offsetHeight;var B=F[0];return new YAHOO.util.Region(C,E,A,B);};YAHOO.util.Point=function(A,B){if(YAHOO.lang.isArray(A)){B=A[1];A=A[0];}this.x=this.right=this.left=this[0]=A;this.y=this.top=this.bottom=this[1]=B;};YAHOO.util.Point.prototype=new YAHOO.util.Region();YAHOO.register("dom",YAHOO.util.Dom,{version:"2.4.1",build:"742"});
\ No newline at end of file
Index: trunk/wb/include/yui/dom/README
===================================================================
--- trunk/wb/include/yui/dom/README	(nonexistent)
+++ trunk/wb/include/yui/dom/README	(revision 601)
@@ -0,0 +1,125 @@
+Dom Release Notes
+
+** Known Issues **
+* margin/padding/borders on HTML element cause getXY to misreport
+* margin/borders on BODY element may cause getXY to misreport
+* Fixed postioning causes getXY to misreport in some cases
+
+----------------------------
+
+*** version 2.4.1 ***
+
+No change
+
+*** version 2.4.0 ***
+* no longer accounting for safari body margin when offsetParent == body
+* isAncestor and inDocument no longer use batch
+* added getClientRegion()
+
+*** version 2.3.1 ***
+* allow batch() to work on array-like object
+* return null from Dom.get(undefined)
+
+*** version 2.3.0 ***
+* added getAncestorBy methods
+* added getChildren methods
+* added getSibling methods
+* trimming added for class mgmt methods
+* fixed getXY inside table for Opera 
+
+*** version 2.3.0 ***
+* added getAncestorBy methods
+* added getChildren methods
+* added getSibling methods
+* trimming added for class mgmt methods
+* fixed getXY inside table for Opera 
+
+*** version 2.2.2 ***
+
+* fixed getXY scroll regression
+
+*** version 2.2.1 ***
+* fixed toCamel propertyCache used by set/getStyle
+* added set/getStyle support for float property
+* optimized get() for common use case
+* fixed getXY for safari when el has absolute ancestors
+* using className property instead of string literal for class mgmt methods
+* added getXY/getRegion support for body element
+
+*** version 2.2.0 ***
+* no change
+
+
+*** version 0.12.2 ***
+* no change
+
+*** version 0.12.1 ***
+
+* getElementsByClassName no longer reverts to document when "root" not found
+* setXY no longer makes a second call to getXY unless noRetry is false
+* minified version no longer strips line breaks
+
+*** version 0.12.0 ***
+
+* fixed getXY for IE null parent
+* branching set/getStyle at load time instead of run time 
+
+*** version 0.11.3 ***
+
+* fixed getX and getY returning incorrect values for collections 
+* fixed getXY incorrectly calculated for Opera inline elements
+* fixed isAncestor failure in safari when 2nd arg is document.documentElement
+* fixed infinite loop in replaceClass when oldClassName == newClassName 
+* getDocumentWidth no longer includes scrollbars 
+
+
+*** version 0.11.2 ***
+* limit depth of parent.document crawl to 1 for getXY
+* test offsetParent instead of parentNode for getXY
+* return null if no el fo r get
+* just addClass if no class to replace for replaceClass
+
+
+*** version 0.11.1 ***
+
+* return null if el is null for get()
+* test offsetParent rather than parentNode for getXY()
+* limit depth of parent.document crawl for IE getXY() to 1
+* if no oldClassName to replace, just addClass for replaceClass()
+
+
+*** version 0.11.0 ***
+* Work around Opera 9 broken currentStyle
+* Removed timeout wrapper from setXY retry
+* Tagname tests now case-insensitive
+* Internal "this" references changed to allow for method shorthand
+* get/setStyle now accept both camel and hyphen case
+* Gecko reverted to crawling offsets for getXY
+ 
+
+*** version 0.10.0 ***
+
+* Safari now fails gracefully when querying computedStyle of an unavailable element 
+
+* Class management functions added (hasClass, addClass, removeClass, replaceClass, getElementsByClassName) 
+
+* All methods that accept HTMLElements or IDs now also accept arrays of HTMLElements and/or IDs
+
+* GenerateId method added
+
+* isAncestor method added
+
+* inDocument method added
+
+* getElementsBy method added
+
+* batch method added
+
+* getClientHeight/Width deprecated in favor of getViewportHeight/Width
+
+* getDocumentHeight/Width methods added 
+
+*** version 0.9.0 ***
+
+* Initial release
+
Index: trunk/wb/include/yui/dom/dom-debug.js
===================================================================
--- trunk/wb/include/yui/dom/dom-debug.js	(nonexistent)
+++ trunk/wb/include/yui/dom/dom-debug.js	(revision 601)
@@ -0,0 +1,1260 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+/**
+ * The dom module provides helper methods for manipulating Dom elements.
+ * @module dom
+ *
+ */
+
+(function() {
+    var Y = YAHOO.util,     // internal shorthand
+        getStyle,           // for load time browser branching
+        setStyle,           // ditto
+        id_counter = 0,     // for use with generateId
+        propertyCache = {}, // for faster hyphen converts
+        reClassNameCache = {},          // cache regexes for className
+        document = window.document;     // cache for faster lookups
+    
+    // brower detection
+    var isOpera = YAHOO.env.ua.opera,
+        isSafari = YAHOO.env.ua.webkit, 
+        isGecko = YAHOO.env.ua.gecko,
+        isIE = YAHOO.env.ua.ie; 
+    
+    // regex cache
+    var patterns = {
+        HYPHEN: /(-[a-z])/i, // to normalize get/setStyle
+        ROOT_TAG: /^body|html$/i // body for quirks mode, html for standards
+    };
+
+    var toCamel = function(property) {
+        if ( !patterns.HYPHEN.test(property) ) {
+            return property; // no hyphens
+        }
+        
+        if (propertyCache[property]) { // already converted
+            return propertyCache[property];
+        }
+       
+        var converted = property;
+ 
+        while( patterns.HYPHEN.exec(converted) ) {
+            converted = converted.replace(RegExp.$1,
+                    RegExp.$1.substr(1).toUpperCase());
+        }
+        
+        propertyCache[property] = converted;
+        return converted;
+        //return property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase()}) // cant use function as 2nd arg yet due to safari bug
+    };
+    
+    var getClassRegEx = function(className) {
+        var re = reClassNameCache[className];
+        if (!re) {
+            re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
+            reClassNameCache[className] = re;
+        }
+        return re;
+    };
+
+    // branching at load instead of runtime
+    if (document.defaultView && document.defaultView.getComputedStyle) { // W3C DOM method
+        getStyle = function(el, property) {
+            var value = null;
+            
+            if (property == 'float') { // fix reserved word
+                property = 'cssFloat';
+            }
+
+            var computed = document.defaultView.getComputedStyle(el, '');
+            if (computed) { // test computed before touching for safari
+                value = computed[toCamel(property)];
+            }
+            
+            return el.style[property] || value;
+        };
+    } else if (document.documentElement.currentStyle && isIE) { // IE method
+        getStyle = function(el, property) {                         
+            switch( toCamel(property) ) {
+                case 'opacity' :// IE opacity uses filter
+                    var val = 100;
+                    try { // will error if no DXImageTransform
+                        val = el.filters['DXImageTransform.Microsoft.Alpha'].opacity;
+
+                    } catch(e) {
+                        try { // make sure its in the document
+                            val = el.filters('alpha').opacity;
+                        } catch(e) {
+                            YAHOO.log('getStyle: IE filter failed',
+                                    'error', 'Dom');
+                        }
+                    }
+                    return val / 100;
+                case 'float': // fix reserved word
+                    property = 'styleFloat'; // fall through
+                default: 
+                    // test currentStyle before touching
+                    var value = el.currentStyle ? el.currentStyle[property] : null;
+                    return ( el.style[property] || value );
+            }
+        };
+    } else { // default to inline only
+        getStyle = function(el, property) { return el.style[property]; };
+    }
+    
+    if (isIE) {
+        setStyle = function(el, property, val) {
+            switch (property) {
+                case 'opacity':
+                    if ( YAHOO.lang.isString(el.style.filter) ) { // in case not appended
+                        el.style.filter = 'alpha(opacity=' + val * 100 + ')';
+                        
+                        if (!el.currentStyle || !el.currentStyle.hasLayout) {
+                            el.style.zoom = 1; // when no layout or cant tell
+                        }
+                    }
+                    break;
+                case 'float':
+                    property = 'styleFloat';
+                default:
+                el.style[property] = val;
+            }
+        };
+    } else {
+        setStyle = function(el, property, val) {
+            if (property == 'float') {
+                property = 'cssFloat';
+            }
+            el.style[property] = val;
+        };
+    }
+
+    var testElement = function(node, method) {
+        return node && node.nodeType == 1 && ( !method || method(node) );
+    };
+
+    /**
+     * Provides helper methods for DOM elements.
+     * @namespace YAHOO.util
+     * @class Dom
+     */
+    YAHOO.util.Dom = {
+        /**
+         * Returns an HTMLElement reference.
+         * @method get
+         * @param {String | HTMLElement |Array} el Accepts a string to use as an ID for getting a DOM reference, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @return {HTMLElement | Array} A DOM reference to an HTML element or an array of HTMLElements.
+         */
+        get: function(el) {
+            if (el && (el.tagName || el.item)) { // HTMLElement, or HTMLCollection
+                return el;
+            }
+
+            if (YAHOO.lang.isString(el) || !el) { // HTMLElement or null
+                return document.getElementById(el);
+            }
+            
+            if (el.length !== undefined) { // array-like 
+                var c = [];
+                for (var i = 0, len = el.length; i < len; ++i) {
+                    c[c.length] = Y.Dom.get(el[i]);
+                }
+                
+                return c;
+            }
+
+            return el; // some other object, just pass it back
+        },
+    
+        /**
+         * Normalizes currentStyle and ComputedStyle.
+         * @method getStyle
+         * @param {String | HTMLElement |Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @param {String} property The style property whose value is returned.
+         * @return {String | Array} The current value of the style property for the element(s).
+         */
+        getStyle: function(el, property) {
+            property = toCamel(property);
+            
+            var f = function(element) {
+                return getStyle(element, property);
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+    
+        /**
+         * Wrapper for setting style properties of HTMLElements.  Normalizes "opacity" across modern browsers.
+         * @method setStyle
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @param {String} property The style property to be set.
+         * @param {String} val The value to apply to the given property.
+         */
+        setStyle: function(el, property, val) {
+            property = toCamel(property);
+            
+            var f = function(element) {
+                setStyle(element, property, val);
+                YAHOO.log('setStyle setting ' + property + ' to ' + val, 'info', 'Dom');
+                
+            };
+            
+            Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Gets the current position of an element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method getXY
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
+         * @return {Array} The XY position of the element(s)
+         */
+        getXY: function(el) {
+            var f = function(el) {
+                // has to be part of document to have pageXY
+                if ( (el.parentNode === null || el.offsetParent === null ||
+                        this.getStyle(el, 'display') == 'none') && el != el.ownerDocument.body) {
+                    YAHOO.log('getXY failed: element not available', 'error', 'Dom');
+                    return false;
+                }
+                
+                YAHOO.log('getXY returning ' + getXY(el), 'info', 'Dom');
+                return getXY(el);
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Gets the current X position of an element based on page coordinates.  The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method getX
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
+         * @return {Number | Array} The X position of the element(s)
+         */
+        getX: function(el) {
+            var f = function(el) {
+                return Y.Dom.getXY(el)[0];
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Gets the current Y position of an element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method getY
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
+         * @return {Number | Array} The Y position of the element(s)
+         */
+        getY: function(el) {
+            var f = function(el) {
+                return Y.Dom.getXY(el)[1];
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Set the position of an html element in page coordinates, regardless of how the element is positioned.
+         * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method setXY
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
+         * @param {Array} pos Contains X & Y values for new position (coordinates are page-based)
+         * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
+         */
+        setXY: function(el, pos, noRetry) {
+            var f = function(el) {
+                var style_pos = this.getStyle(el, 'position');
+                if (style_pos == 'static') { // default to relative
+                    this.setStyle(el, 'position', 'relative');
+                    style_pos = 'relative';
+                }
+
+                var pageXY = this.getXY(el);
+                if (pageXY === false) { // has to be part of doc to have pageXY
+                    YAHOO.log('setXY failed: element not available', 'error', 'Dom');
+                    return false; 
+                }
+                
+                var delta = [ // assuming pixels; if not we will have to retry
+                    parseInt( this.getStyle(el, 'left'), 10 ),
+                    parseInt( this.getStyle(el, 'top'), 10 )
+                ];
+            
+                if ( isNaN(delta[0]) ) {// in case of 'auto'
+                    delta[0] = (style_pos == 'relative') ? 0 : el.offsetLeft;
+                } 
+                if ( isNaN(delta[1]) ) { // in case of 'auto'
+                    delta[1] = (style_pos == 'relative') ? 0 : el.offsetTop;
+                } 
+        
+                if (pos[0] !== null) { el.style.left = pos[0] - pageXY[0] + delta[0] + 'px'; }
+                if (pos[1] !== null) { el.style.top = pos[1] - pageXY[1] + delta[1] + 'px'; }
+              
+                if (!noRetry) {
+                    var newXY = this.getXY(el);
+
+                    // if retry is true, try one more time if we miss 
+                   if ( (pos[0] !== null && newXY[0] != pos[0]) || 
+                        (pos[1] !== null && newXY[1] != pos[1]) ) {
+                       this.setXY(el, pos, true);
+                   }
+                }        
+        
+                YAHOO.log('setXY setting position to ' + pos, 'info', 'Dom');
+            };
+            
+            Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
+         * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method setX
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @param {Int} x The value to use as the X coordinate for the element(s).
+         */
+        setX: function(el, x) {
+            Y.Dom.setXY(el, [x, null]);
+        },
+        
+        /**
+         * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
+         * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+         * @method setY
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @param {Int} x To use as the Y coordinate for the element(s).
+         */
+        setY: function(el, y) {
+            Y.Dom.setXY(el, [null, y]);
+        },
+        
+        /**
+         * Returns the region position of the given element.
+         * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
+         * @method getRegion
+         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
+         * @return {Region | Array} A Region or array of Region instances containing "top, left, bottom, right" member data.
+         */
+        getRegion: function(el) {
+            var f = function(el) {
+                if ( (el.parentNode === null || el.offsetParent === null ||
+                        this.getStyle(el, 'display') == 'none') && el != document.body) {
+                    YAHOO.log('getRegion failed: element not available', 'error', 'Dom');
+                    return false;
+                }
+
+                var region = Y.Region.getRegion(el);
+                YAHOO.log('getRegion returning ' + region, 'info', 'Dom');
+                return region;
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Returns the width of the client (viewport).
+         * @method getClientWidth
+         * @deprecated Now using getViewportWidth.  This interface left intact for back compat.
+         * @return {Int} The width of the viewable area of the page.
+         */
+        getClientWidth: function() {
+            return Y.Dom.getViewportWidth();
+        },
+        
+        /**
+         * Returns the height of the client (viewport).
+         * @method getClientHeight
+         * @deprecated Now using getViewportHeight.  This interface left intact for back compat.
+         * @return {Int} The height of the viewable area of the page.
+         */
+        getClientHeight: function() {
+            return Y.Dom.getViewportHeight();
+        },
+
+        /**
+         * Returns a array of HTMLElements with the given class.
+         * For optimized performance, include a tag and/or root node when possible.
+         * @method getElementsByClassName
+         * @param {String} className The class name to match against
+         * @param {String} tag (optional) The tag name of the elements being collected
+         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
+         * @param {Function} apply (optional) A function to apply to each element when found 
+         * @return {Array} An array of elements that have the given class name
+         */
+        getElementsByClassName: function(className, tag, root, apply) {
+            tag = tag || '*';
+            root = (root) ? Y.Dom.get(root) : null || document; 
+            if (!root) {
+                return [];
+            }
+
+            var nodes = [],
+                elements = root.getElementsByTagName(tag),
+                re = getClassRegEx(className);
+
+            for (var i = 0, len = elements.length; i < len; ++i) {
+                if ( re.test(elements[i].className) ) {
+                    nodes[nodes.length] = elements[i];
+                    if (apply) {
+                        apply.call(elements[i], elements[i]);
+                    }
+                }
+            }
+            
+            return nodes;
+        },
+
+        /**
+         * Determines whether an HTMLElement has the given className.
+         * @method hasClass
+         * @param {String | HTMLElement | Array} el The element or collection to test
+         * @param {String} className the class name to search for
+         * @return {Boolean | Array} A boolean value or array of boolean values
+         */
+        hasClass: function(el, className) {
+            var re = getClassRegEx(className);
+
+            var f = function(el) {
+                YAHOO.log('hasClass returning ' + re.test(el.className), 'info', 'Dom');
+                return re.test(el.className);
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+    
+        /**
+         * Adds a class name to a given element or collection of elements.
+         * @method addClass         
+         * @param {String | HTMLElement | Array} el The element or collection to add the class to
+         * @param {String} className the class name to add to the class attribute
+         * @return {Boolean | Array} A pass/fail boolean or array of booleans
+         */
+        addClass: function(el, className) {
+            var f = function(el) {
+                if (this.hasClass(el, className)) {
+                    return false; // already present
+                }
+                
+                YAHOO.log('addClass adding ' + className, 'info', 'Dom');
+                
+                el.className = YAHOO.lang.trim([el.className, className].join(' '));
+                return true;
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+    
+        /**
+         * Removes a class name from a given element or collection of elements.
+         * @method removeClass         
+         * @param {String | HTMLElement | Array} el The element or collection to remove the class from
+         * @param {String} className the class name to remove from the class attribute
+         * @return {Boolean | Array} A pass/fail boolean or array of booleans
+         */
+        removeClass: function(el, className) {
+            var re = getClassRegEx(className);
+            
+            var f = function(el) {
+                if (!this.hasClass(el, className)) {
+                    return false; // not present
+                }                 
+
+                YAHOO.log('removeClass removing ' + className, 'info', 'Dom');
+                
+                var c = el.className;
+                el.className = c.replace(re, ' ');
+                if ( this.hasClass(el, className) ) { // in case of multiple adjacent
+                    this.removeClass(el, className);
+                }
+
+                el.className = YAHOO.lang.trim(el.className); // remove any trailing spaces
+                return true;
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Replace a class with another class for a given element or collection of elements.
+         * If no oldClassName is present, the newClassName is simply added.
+         * @method replaceClass  
+         * @param {String | HTMLElement | Array} el The element or collection to remove the class from
+         * @param {String} oldClassName the class name to be replaced
+         * @param {String} newClassName the class name that will be replacing the old class name
+         * @return {Boolean | Array} A pass/fail boolean or array of booleans
+         */
+        replaceClass: function(el, oldClassName, newClassName) {
+            if (!newClassName || oldClassName === newClassName) { // avoid infinite loop
+                return false;
+            }
+            
+            var re = getClassRegEx(oldClassName);
+
+            var f = function(el) {
+                YAHOO.log('replaceClass replacing ' + oldClassName + ' with ' + newClassName, 'info', 'Dom');
+            
+                if ( !this.hasClass(el, oldClassName) ) {
+                    this.addClass(el, newClassName); // just add it if nothing to replace
+                    return true; // NOTE: return
+                }
+            
+                el.className = el.className.replace(re, ' ' + newClassName + ' ');
+
+                if ( this.hasClass(el, oldClassName) ) { // in case of multiple adjacent
+                    this.replaceClass(el, oldClassName, newClassName);
+                }
+
+                el.className = YAHOO.lang.trim(el.className); // remove any trailing spaces
+                return true;
+            };
+            
+            return Y.Dom.batch(el, f, Y.Dom, true);
+        },
+        
+        /**
+         * Returns an ID and applies it to the element "el", if provided.
+         * @method generateId  
+         * @param {String | HTMLElement | Array} el (optional) An optional element array of elements to add an ID to (no ID is added if one is already present).
+         * @param {String} prefix (optional) an optional prefix to use (defaults to "yui-gen").
+         * @return {String | Array} The generated ID, or array of generated IDs (or original ID if already present on an element)
+         */
+        generateId: function(el, prefix) {
+            prefix = prefix || 'yui-gen';
+
+            var f = function(el) {
+                if (el && el.id) { // do not override existing ID
+                    YAHOO.log('generateId returning existing id ' + el.id, 'info', 'Dom');
+                    return el.id;
+                } 
+
+                var id = prefix + id_counter++;
+                YAHOO.log('generateId generating ' + id, 'info', 'Dom');
+
+                if (el) {
+                    el.id = id;
+                }
+                
+                return id;
+            };
+
+            // batch fails when no element, so just generate and return single ID
+            return Y.Dom.batch(el, f, Y.Dom, true) || f.apply(Y.Dom, arguments);
+        },
+        
+        /**
+         * Determines whether an HTMLElement is an ancestor of another HTML element in the DOM hierarchy.
+         * @method isAncestor
+         * @param {String | HTMLElement} haystack The possible ancestor
+         * @param {String | HTMLElement} needle The possible descendent
+         * @return {Boolean} Whether or not the haystack is an ancestor of needle
+         */
+        isAncestor: function(haystack, needle) {
+            haystack = Y.Dom.get(haystack);
+            needle = Y.Dom.get(needle);
+            
+            if (!haystack || !needle) {
+                return false;
+            }
+
+            if (haystack.contains && needle.nodeType && !isSafari) { // safari contains is broken
+                YAHOO.log('isAncestor returning ' + haystack.contains(needle), 'info', 'Dom');
+                return haystack.contains(needle);
+            }
+            else if ( haystack.compareDocumentPosition && needle.nodeType ) {
+                YAHOO.log('isAncestor returning ' + !!(haystack.compareDocumentPosition(needle) & 16), 'info', 'Dom');
+                return !!(haystack.compareDocumentPosition(needle) & 16);
+            } else if (needle.nodeType) {
+                // fallback to crawling up (safari)
+                return !!this.getAncestorBy(needle, function(el) {
+                    return el == haystack; 
+                }); 
+            }
+            YAHOO.log('isAncestor failed; most likely needle is not an HTMLElement', 'error', 'Dom');
+            return false;
+        },
+        
+        /**
+         * Determines whether an HTMLElement is present in the current document.
+         * @method inDocument         
+         * @param {String | HTMLElement} el The element to search for
+         * @return {Boolean} Whether or not the element is present in the current document
+         */
+        inDocument: function(el) {
+            return this.isAncestor(document.documentElement, el);
+        },
+        
+        /**
+         * Returns a array of HTMLElements that pass the test applied by supplied boolean method.
+         * For optimized performance, include a tag and/or root node when possible.
+         * @method getElementsBy
+         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
+         * @param {String} tag (optional) The tag name of the elements being collected
+         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
+         * @param {Function} apply (optional) A function to apply to each element when found 
+         * @return {Array} Array of HTMLElements
+         */
+        getElementsBy: function(method, tag, root, apply) {
+            tag = tag || '*';
+            root = (root) ? Y.Dom.get(root) : null || document; 
+
+            if (!root) {
+                return [];
+            }
+
+            var nodes = [],
+                elements = root.getElementsByTagName(tag);
+            
+            for (var i = 0, len = elements.length; i < len; ++i) {
+                if ( method(elements[i]) ) {
+                    nodes[nodes.length] = elements[i];
+                    if (apply) {
+                        apply(elements[i]);
+                    }
+                }
+            }
+
+            YAHOO.log('getElementsBy returning ' + nodes, 'info', 'Dom');
+            
+            return nodes;
+        },
+        
+        /**
+         * Runs the supplied method against each item in the Collection/Array.
+         * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) ).
+         * @method batch
+         * @param {String | HTMLElement | Array} el (optional) An element or array of elements to apply the method to
+         * @param {Function} method The method to apply to the element(s)
+         * @param {Any} o (optional) An optional arg that is passed to the supplied method
+         * @param {Boolean} override (optional) Whether or not to override the scope of "method" with "o"
+         * @return {Any | Array} The return value(s) from the supplied method
+         */
+        batch: function(el, method, o, override) {
+            el = (el && (el.tagName || el.item)) ? el : Y.Dom.get(el); // skip get() when possible
+
+            if (!el || !method) {
+                YAHOO.log('batch failed: invalid arguments', 'error', 'Dom');
+                return false;
+            } 
+            var scope = (override) ? o : window;
+            
+            if (el.tagName || el.length === undefined) { // element or not array-like 
+                return method.call(scope, el, o);
+            } 
+
+            var collection = [];
+            
+            for (var i = 0, len = el.length; i < len; ++i) {
+                collection[collection.length] = method.call(scope, el[i], o);
+            }
+            
+            return collection;
+        },
+        
+        /**
+         * Returns the height of the document.
+         * @method getDocumentHeight
+         * @return {Int} The height of the actual document (which includes the body and its margin).
+         */
+        getDocumentHeight: function() {
+            var scrollHeight = (document.compatMode != 'CSS1Compat') ? document.body.scrollHeight : document.documentElement.scrollHeight;
+
+            var h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
+            YAHOO.log('getDocumentHeight returning ' + h, 'info', 'Dom');
+            return h;
+        },
+        
+        /**
+         * Returns the width of the document.
+         * @method getDocumentWidth
+         * @return {Int} The width of the actual document (which includes the body and its margin).
+         */
+        getDocumentWidth: function() {
+            var scrollWidth = (document.compatMode != 'CSS1Compat') ? document.body.scrollWidth : document.documentElement.scrollWidth;
+            var w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
+            YAHOO.log('getDocumentWidth returning ' + w, 'info', 'Dom');
+            return w;
+        },
+
+        /**
+         * Returns the current height of the viewport.
+         * @method getViewportHeight
+         * @return {Int} The height of the viewable area of the page (excludes scrollbars).
+         */
+        getViewportHeight: function() {
+            var height = self.innerHeight; // Safari, Opera
+            var mode = document.compatMode;
+        
+            if ( (mode || isIE) && !isOpera ) { // IE, Gecko
+                height = (mode == 'CSS1Compat') ?
+                        document.documentElement.clientHeight : // Standards
+                        document.body.clientHeight; // Quirks
+            }
+        
+            YAHOO.log('getViewportHeight returning ' + height, 'info', 'Dom');
+            return height;
+        },
+        
+        /**
+         * Returns the current width of the viewport.
+         * @method getViewportWidth
+         * @return {Int} The width of the viewable area of the page (excludes scrollbars).
+         */
+        
+        getViewportWidth: function() {
+            var width = self.innerWidth;  // Safari
+            var mode = document.compatMode;
+            
+            if (mode || isIE) { // IE, Gecko, Opera
+                width = (mode == 'CSS1Compat') ?
+                        document.documentElement.clientWidth : // Standards
+                        document.body.clientWidth; // Quirks
+            }
+            YAHOO.log('getViewportWidth returning ' + width, 'info', 'Dom');
+            return width;
+        },
+
+       /**
+         * Returns the nearest ancestor that passes the test applied by supplied boolean method.
+         * For performance reasons, IDs are not accepted and argument validation omitted.
+         * @method getAncestorBy
+         * @param {HTMLElement} node The HTMLElement to use as the starting point 
+         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
+         * @return {Object} HTMLElement or null if not found
+         */
+        getAncestorBy: function(node, method) {
+            while (node = node.parentNode) { // NOTE: assignment
+                if ( testElement(node, method) ) {
+                    YAHOO.log('getAncestorBy returning ' + node, 'info', 'Dom');
+                    return node;
+                }
+            } 
+
+            YAHOO.log('getAncestorBy returning null (no ancestor passed test)', 'error', 'Dom');
+            return null;
+        },
+        
+        /**
+         * Returns the nearest ancestor with the given className.
+         * @method getAncestorByClassName
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @param {String} className
+         * @return {Object} HTMLElement
+         */
+        getAncestorByClassName: function(node, className) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                YAHOO.log('getAncestorByClassName failed: invalid node argument', 'error', 'Dom');
+                return null;
+            }
+            var method = function(el) { return Y.Dom.hasClass(el, className); };
+            return Y.Dom.getAncestorBy(node, method);
+        },
+
+        /**
+         * Returns the nearest ancestor with the given tagName.
+         * @method getAncestorByTagName
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @param {String} tagName
+         * @return {Object} HTMLElement
+         */
+        getAncestorByTagName: function(node, tagName) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                YAHOO.log('getAncestorByTagName failed: invalid node argument', 'error', 'Dom');
+                return null;
+            }
+            var method = function(el) {
+                 return el.tagName && el.tagName.toUpperCase() == tagName.toUpperCase();
+            };
+
+            return Y.Dom.getAncestorBy(node, method);
+        },
+
+        /**
+         * Returns the previous sibling that is an HTMLElement. 
+         * For performance reasons, IDs are not accepted and argument validation omitted.
+         * Returns the nearest HTMLElement sibling if no method provided.
+         * @method getPreviousSiblingBy
+         * @param {HTMLElement} node The HTMLElement to use as the starting point 
+         * @param {Function} method A boolean function used to test siblings
+         * that receives the sibling node being tested as its only argument
+         * @return {Object} HTMLElement or null if not found
+         */
+        getPreviousSiblingBy: function(node, method) {
+            while (node) {
+                node = node.previousSibling;
+                if ( testElement(node, method) ) {
+                    return node;
+                }
+            }
+            return null;
+        }, 
+
+        /**
+         * Returns the previous sibling that is an HTMLElement 
+         * @method getPreviousSibling
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @return {Object} HTMLElement or null if not found
+         */
+        getPreviousSibling: function(node) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                YAHOO.log('getPreviousSibling failed: invalid node argument', 'error', 'Dom');
+                return null;
+            }
+
+            return Y.Dom.getPreviousSiblingBy(node);
+        }, 
+
+        /**
+         * Returns the next HTMLElement sibling that passes the boolean method. 
+         * For performance reasons, IDs are not accepted and argument validation omitted.
+         * Returns the nearest HTMLElement sibling if no method provided.
+         * @method getNextSiblingBy
+         * @param {HTMLElement} node The HTMLElement to use as the starting point 
+         * @param {Function} method A boolean function used to test siblings
+         * that receives the sibling node being tested as its only argument
+         * @return {Object} HTMLElement or null if not found
+         */
+        getNextSiblingBy: function(node, method) {
+            while (node) {
+                node = node.nextSibling;
+                if ( testElement(node, method) ) {
+                    return node;
+                }
+            }
+            return null;
+        }, 
+
+        /**
+         * Returns the next sibling that is an HTMLElement 
+         * @method getNextSibling
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @return {Object} HTMLElement or null if not found
+         */
+        getNextSibling: function(node) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                YAHOO.log('getNextSibling failed: invalid node argument', 'error', 'Dom');
+                return null;
+            }
+
+            return Y.Dom.getNextSiblingBy(node);
+        }, 
+
+        /**
+         * Returns the first HTMLElement child that passes the test method. 
+         * @method getFirstChildBy
+         * @param {HTMLElement} node The HTMLElement to use as the starting point 
+         * @param {Function} method A boolean function used to test children
+         * that receives the node being tested as its only argument
+         * @return {Object} HTMLElement or null if not found
+         */
+        getFirstChildBy: function(node, method) {
+            var child = ( testElement(node.firstChild, method) ) ? node.firstChild : null;
+            return child || Y.Dom.getNextSiblingBy(node.firstChild, method);
+        }, 
+
+        /**
+         * Returns the first HTMLElement child. 
+         * @method getFirstChild
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @return {Object} HTMLElement or null if not found
+         */
+        getFirstChild: function(node, method) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                YAHOO.log('getFirstChild failed: invalid node argument', 'error', 'Dom');
+                return null;
+            }
+            return Y.Dom.getFirstChildBy(node);
+        }, 
+
+        /**
+         * Returns the last HTMLElement child that passes the test method. 
+         * @method getLastChildBy
+         * @param {HTMLElement} node The HTMLElement to use as the starting point 
+         * @param {Function} method A boolean function used to test children
+         * that receives the node being tested as its only argument
+         * @return {Object} HTMLElement or null if not found
+         */
+        getLastChildBy: function(node, method) {
+            if (!node) {
+                YAHOO.log('getLastChild failed: invalid node argument', 'error', 'Dom');
+                return null;
+            }
+            var child = ( testElement(node.lastChild, method) ) ? node.lastChild : null;
+            return child || Y.Dom.getPreviousSiblingBy(node.lastChild, method);
+        }, 
+
+        /**
+         * Returns the last HTMLElement child. 
+         * @method getLastChild
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @return {Object} HTMLElement or null if not found
+         */
+        getLastChild: function(node) {
+            node = Y.Dom.get(node);
+            return Y.Dom.getLastChildBy(node);
+        }, 
+
+        /**
+         * Returns an array of HTMLElement childNodes that pass the test method. 
+         * @method getChildrenBy
+         * @param {HTMLElement} node The HTMLElement to start from
+         * @param {Function} method A boolean function used to test children
+         * that receives the node being tested as its only argument
+         * @return {Array} A static array of HTMLElements
+         */
+        getChildrenBy: function(node, method) {
+            var child = Y.Dom.getFirstChildBy(node, method);
+            var children = child ? [child] : [];
+
+            Y.Dom.getNextSiblingBy(child, function(node) {
+                if ( !method || method(node) ) {
+                    children[children.length] = node;
+                }
+                return false; // fail test to collect all children
+            });
+
+            return children;
+        },
+ 
+        /**
+         * Returns an array of HTMLElement childNodes. 
+         * @method getChildren
+         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
+         * @return {Array} A static array of HTMLElements
+         */
+        getChildren: function(node) {
+            node = Y.Dom.get(node);
+            if (!node) {
+                YAHOO.log('getChildren failed: invalid node argument', 'error', 'Dom');
+            }
+
+            return Y.Dom.getChildrenBy(node);
+        },
+ 
+        /**
+         * Returns the left scroll value of the document 
+         * @method getDocumentScrollLeft
+         * @param {HTMLDocument} document (optional) The document to get the scroll value of
+         * @return {Int}  The amount that the document is scrolled to the left
+         */
+        getDocumentScrollLeft: function(doc) {
+            doc = doc || document;
+            return Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
+        }, 
+
+        /**
+         * Returns the top scroll value of the document 
+         * @method getDocumentScrollTop
+         * @param {HTMLDocument} document (optional) The document to get the scroll value of
+         * @return {Int}  The amount that the document is scrolled to the top
+         */
+        getDocumentScrollTop: function(doc) {
+            doc = doc || document;
+            return Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
+        },
+
+        /**
+         * Inserts the new node as the previous sibling of the reference node 
+         * @method insertBefore
+         * @param {String | HTMLElement} newNode The node to be inserted
+         * @param {String | HTMLElement} referenceNode The node to insert the new node before 
+         * @return {HTMLElement} The node that was inserted (or null if insert fails) 
+         */
+        insertBefore: function(newNode, referenceNode) {
+            newNode = Y.Dom.get(newNode); 
+            referenceNode = Y.Dom.get(referenceNode); 
+            
+            if (!newNode || !referenceNode || !referenceNode.parentNode) {
+                YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'Dom');
+                return null;
+            }       
+
+            return referenceNode.parentNode.insertBefore(newNode, referenceNode); 
+        },
+
+        /**
+         * Inserts the new node as the next sibling of the reference node 
+         * @method insertAfter
+         * @param {String | HTMLElement} newNode The node to be inserted
+         * @param {String | HTMLElement} referenceNode The node to insert the new node after 
+         * @return {HTMLElement} The node that was inserted (or null if insert fails) 
+         */
+        insertAfter: function(newNode, referenceNode) {
+            newNode = Y.Dom.get(newNode); 
+            referenceNode = Y.Dom.get(referenceNode); 
+            
+            if (!newNode || !referenceNode || !referenceNode.parentNode) {
+                YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'Dom');
+                return null;
+            }       
+
+            if (referenceNode.nextSibling) {
+                return referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); 
+            } else {
+                return referenceNode.parentNode.appendChild(newNode);
+            }
+        },
+
+        /**
+         * Creates a Region based on the viewport relative to the document. 
+         * @method getClientRegion
+         * @return {Region} A Region object representing the viewport which accounts for document scroll
+         */
+        getClientRegion: function() {
+            var t = Y.Dom.getDocumentScrollTop(),
+                l = Y.Dom.getDocumentScrollLeft(),
+                r = Y.Dom.getViewportWidth() + l,
+                b = Y.Dom.getViewportHeight() + t;
+
+            return new Y.Region(t, r, b, l);
+        }
+    };
+    
+    var getXY = function() {
+        if (document.documentElement.getBoundingClientRect) { // IE
+            return function(el) {
+                var box = el.getBoundingClientRect();
+
+                var rootNode = el.ownerDocument;
+                return [box.left + Y.Dom.getDocumentScrollLeft(rootNode), box.top +
+                        Y.Dom.getDocumentScrollTop(rootNode)];
+            };
+        } else {
+            return function(el) { // manually calculate by crawling up offsetParents
+                var pos = [el.offsetLeft, el.offsetTop];
+                var parentNode = el.offsetParent;
+
+                // safari: subtract body offsets if el is abs (or any offsetParent), unless body is offsetParent
+                var accountForBody = (isSafari &&
+                        Y.Dom.getStyle(el, 'position') == 'absolute' &&
+                        el.offsetParent == el.ownerDocument.body);
+
+                if (parentNode != el) {
+                    while (parentNode) {
+                        pos[0] += parentNode.offsetLeft;
+                        pos[1] += parentNode.offsetTop;
+                        if (!accountForBody && isSafari && 
+                                Y.Dom.getStyle(parentNode,'position') == 'absolute' ) { 
+                            accountForBody = true;
+                        }
+                        parentNode = parentNode.offsetParent;
+                    }
+                }
+
+                if (accountForBody) { //safari doubles in this case
+                    pos[0] -= el.ownerDocument.body.offsetLeft;
+                    pos[1] -= el.ownerDocument.body.offsetTop;
+                } 
+                parentNode = el.parentNode;
+
+                // account for any scrolled ancestors
+                while ( parentNode.tagName && !patterns.ROOT_TAG.test(parentNode.tagName) ) 
+                {
+                   // work around opera inline/table scrollLeft/Top bug
+                   if (Y.Dom.getStyle(parentNode, 'display').search(/^inline|table-row.*$/i)) { 
+                        pos[0] -= parentNode.scrollLeft;
+                        pos[1] -= parentNode.scrollTop;
+                    }
+                    
+                    parentNode = parentNode.parentNode; 
+                }
+
+                return pos;
+            };
+        }
+    }() // NOTE: Executing for loadtime branching
+})();
+/**
+ * A region is a representation of an object on a grid.  It is defined
+ * by the top, right, bottom, left extents, so is rectangular by default.  If 
+ * other shapes are required, this class could be extended to support it.
+ * @namespace YAHOO.util
+ * @class Region
+ * @param {Int} t the top extent
+ * @param {Int} r the right extent
+ * @param {Int} b the bottom extent
+ * @param {Int} l the left extent
+ * @constructor
+ */
+YAHOO.util.Region = function(t, r, b, l) {
+
+    /**
+     * The region's top extent
+     * @property top
+     * @type Int
+     */
+    this.top = t;
+    
+    /**
+     * The region's top extent as index, for symmetry with set/getXY
+     * @property 1
+     * @type Int
+     */
+    this[1] = t;
+
+    /**
+     * The region's right extent
+     * @property right
+     * @type int
+     */
+    this.right = r;
+
+    /**
+     * The region's bottom extent
+     * @property bottom
+     * @type Int
+     */
+    this.bottom = b;
+
+    /**
+     * The region's left extent
+     * @property left
+     * @type Int
+     */
+    this.left = l;
+    
+    /**
+     * The region's left extent as index, for symmetry with set/getXY
+     * @property 0
+     * @type Int
+     */
+    this[0] = l;
+};
+
+/**
+ * Returns true if this region contains the region passed in
+ * @method contains
+ * @param  {Region}  region The region to evaluate
+ * @return {Boolean}        True if the region is contained with this region, 
+ *                          else false
+ */
+YAHOO.util.Region.prototype.contains = function(region) {
+    return ( region.left   >= this.left   && 
+             region.right  <= this.right  && 
+             region.top    >= this.top    && 
+             region.bottom <= this.bottom    );
+
+    // this.logger.debug("does " + this + " contain " + region + " ... " + ret);
+};
+
+/**
+ * Returns the area of the region
+ * @method getArea
+ * @return {Int} the region's area
+ */
+YAHOO.util.Region.prototype.getArea = function() {
+    return ( (this.bottom - this.top) * (this.right - this.left) );
+};
+
+/**
+ * Returns the region where the passed in region overlaps with this one
+ * @method intersect
+ * @param  {Region} region The region that intersects
+ * @return {Region}        The overlap region, or null if there is no overlap
+ */
+YAHOO.util.Region.prototype.intersect = function(region) {
+    var t = Math.max( this.top,    region.top    );
+    var r = Math.min( this.right,  region.right  );
+    var b = Math.min( this.bottom, region.bottom );
+    var l = Math.max( this.left,   region.left   );
+    
+    if (b >= t && r >= l) {
+        return new YAHOO.util.Region(t, r, b, l);
+    } else {
+        return null;
+    }
+};
+
+/**
+ * Returns the region representing the smallest region that can contain both
+ * the passed in region and this region.
+ * @method union
+ * @param  {Region} region The region that to create the union with
+ * @return {Region}        The union region
+ */
+YAHOO.util.Region.prototype.union = function(region) {
+    var t = Math.min( this.top,    region.top    );
+    var r = Math.max( this.right,  region.right  );
+    var b = Math.max( this.bottom, region.bottom );
+    var l = Math.min( this.left,   region.left   );
+
+    return new YAHOO.util.Region(t, r, b, l);
+};
+
+/**
+ * toString
+ * @method toString
+ * @return string the region properties
+ */
+YAHOO.util.Region.prototype.toString = function() {
+    return ( "Region {"    +
+             "top: "       + this.top    + 
+             ", right: "   + this.right  + 
+             ", bottom: "  + this.bottom + 
+             ", left: "    + this.left   + 
+             "}" );
+};
+
+/**
+ * Returns a region that is occupied by the DOM element
+ * @method getRegion
+ * @param  {HTMLElement} el The element
+ * @return {Region}         The region that the element occupies
+ * @static
+ */
+YAHOO.util.Region.getRegion = function(el) {
+    var p = YAHOO.util.Dom.getXY(el);
+
+    var t = p[1];
+    var r = p[0] + el.offsetWidth;
+    var b = p[1] + el.offsetHeight;
+    var l = p[0];
+
+    return new YAHOO.util.Region(t, r, b, l);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * A point is a region that is special in that it represents a single point on 
+ * the grid.
+ * @namespace YAHOO.util
+ * @class Point
+ * @param {Int} x The X position of the point
+ * @param {Int} y The Y position of the point
+ * @constructor
+ * @extends YAHOO.util.Region
+ */
+YAHOO.util.Point = function(x, y) {
+   if (YAHOO.lang.isArray(x)) { // accept input from Dom.getXY, Event.getXY, etc.
+      y = x[1]; // dont blow away x yet
+      x = x[0];
+   }
+   
+    /**
+     * The X position of the point, which is also the right, left and index zero (for Dom.getXY symmetry)
+     * @property x
+     * @type Int
+     */
+
+    this.x = this.right = this.left = this[0] = x;
+     
+    /**
+     * The Y position of the point, which is also the top, bottom and index one (for Dom.getXY symmetry)
+     * @property y
+     * @type Int
+     */
+    this.y = this.top = this.bottom = this[1] = y;
+};
+
+YAHOO.util.Point.prototype = new YAHOO.util.Region();
+
+YAHOO.register("dom", YAHOO.util.Dom, {version: "2.4.1", build: "742"});
Index: trunk/wb/include/yui/yahoo/yahoo.js
===================================================================
--- trunk/wb/include/yui/yahoo/yahoo.js	(nonexistent)
+++ trunk/wb/include/yui/yahoo/yahoo.js	(revision 601)
@@ -0,0 +1,952 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+/**
+ * The YAHOO object is the single global object used by YUI Library.  It
+ * contains utility function for setting up namespaces, inheritance, and
+ * logging.  YAHOO.util, YAHOO.widget, and YAHOO.example are namespaces
+ * created automatically for and used by the library.
+ * @module yahoo
+ * @title  YAHOO Global
+ */
+
+/**
+ * YAHOO_config is not included as part of the library.  Instead it is an 
+ * object that can be defined by the implementer immediately before 
+ * including the YUI library.  The properties included in this object
+ * will be used to configure global properties needed as soon as the 
+ * library begins to load.
+ * @class YAHOO_config
+ * @static
+ */
+
+/**
+ * A reference to a function that will be executed every time a YAHOO module
+ * is loaded.  As parameter, this function will receive the version
+ * information for the module. See <a href="YAHOO.env.html#getVersion">
+ * YAHOO.env.getVersion</a> for the description of the version data structure.
+ * @property listener
+ * @type Function
+ * @static
+ * @default undefined
+ */
+
+/**
+ * Set to true if the library will be dynamically loaded after window.onload.
+ * Defaults to false 
+ * @property injecting
+ * @type boolean
+ * @static
+ * @default undefined
+ */
+
+/**
+ * Instructs the yuiloader component to dynamically load yui components and
+ * their dependencies.  See the yuiloader documentation for more information
+ * about dynamic loading
+ * @property load
+ * @static
+ * @default undefined
+ * @see yuiloader
+ */
+
+/**
+ * Forces the use of the supplied locale where applicable in the library
+ * @property locale
+ * @type string
+ * @static
+ * @default undefined
+ */
+
+if (typeof YAHOO == "undefined" || !YAHOO) {
+    /**
+     * The YAHOO global namespace object.  If YAHOO is already defined, the
+     * existing YAHOO object will not be overwritten so that defined
+     * namespaces are preserved.
+     * @class YAHOO
+     * @static
+     */
+    var YAHOO = {};
+}
+
+/**
+ * Returns the namespace specified and creates it if it doesn't exist
+ * <pre>
+ * YAHOO.namespace("property.package");
+ * YAHOO.namespace("YAHOO.property.package");
+ * </pre>
+ * Either of the above would create YAHOO.property, then
+ * YAHOO.property.package
+ *
+ * Be careful when naming packages. Reserved words may work in some browsers
+ * and not others. For instance, the following will fail in Safari:
+ * <pre>
+ * YAHOO.namespace("really.long.nested.namespace");
+ * </pre>
+ * This fails because "long" is a future reserved word in ECMAScript
+ *
+ * @method namespace
+ * @static
+ * @param  {String*} arguments 1-n namespaces to create 
+ * @return {Object}  A reference to the last namespace object created
+ */
+YAHOO.namespace = function() {
+    var a=arguments, o=null, i, j, d;
+    for (i=0; i<a.length; i=i+1) {
+        d=a[i].split(".");
+        o=YAHOO;
+
+        // YAHOO is implied, so it is ignored if it is included
+        for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) {
+            o[d[j]]=o[d[j]] || {};
+            o=o[d[j]];
+        }
+    }
+
+    return o;
+};
+
+/**
+ * Uses YAHOO.widget.Logger to output a log message, if the widget is
+ * available.
+ *
+ * @method log
+ * @static
+ * @param  {String}  msg  The message to log.
+ * @param  {String}  cat  The log category for the message.  Default
+ *                        categories are "info", "warn", "error", time".
+ *                        Custom categories can be used as well. (opt)
+ * @param  {String}  src  The source of the the message (opt)
+ * @return {Boolean}      True if the log operation was successful.
+ */
+YAHOO.log = function(msg, cat, src) {
+    var l=YAHOO.widget.Logger;
+    if(l && l.log) {
+        return l.log(msg, cat, src);
+    } else {
+        return false;
+    }
+};
+
+/**
+ * Registers a module with the YAHOO object
+ * @method register
+ * @static
+ * @param {String}   name    the name of the module (event, slider, etc)
+ * @param {Function} mainClass a reference to class in the module.  This
+ *                             class will be tagged with the version info
+ *                             so that it will be possible to identify the
+ *                             version that is in use when multiple versions
+ *                             have loaded
+ * @param {Object}   data      metadata object for the module.  Currently it
+ *                             is expected to contain a "version" property
+ *                             and a "build" property at minimum.
+ */
+YAHOO.register = function(name, mainClass, data) {
+    var mods = YAHOO.env.modules;
+    if (!mods[name]) {
+        mods[name] = { versions:[], builds:[] };
+    }
+    var m=mods[name],v=data.version,b=data.build,ls=YAHOO.env.listeners;
+    m.name = name;
+    m.version = v;
+    m.build = b;
+    m.versions.push(v);
+    m.builds.push(b);
+    m.mainClass = mainClass;
+    // fire the module load listeners
+    for (var i=0;i<ls.length;i=i+1) {
+        ls[i](m);
+    }
+    // label the main class
+    if (mainClass) {
+        mainClass.VERSION = v;
+        mainClass.BUILD = b;
+    } else {
+        YAHOO.log("mainClass is undefined for module " + name, "warn");
+    }
+};
+
+/**
+ * YAHOO.env is used to keep track of what is known about the YUI library and
+ * the browsing environment
+ * @class YAHOO.env
+ * @static
+ */
+YAHOO.env = YAHOO.env || {
+
+    /**
+     * Keeps the version info for all YUI modules that have reported themselves
+     * @property modules
+     * @type Object[]
+     */
+    modules: [],
+    
+    /**
+     * List of functions that should be executed every time a YUI module
+     * reports itself.
+     * @property listeners
+     * @type Function[]
+     */
+    listeners: []
+};
+
+/**
+ * Returns the version data for the specified module:
+ *      <dl>
+ *      <dt>name:</dt>      <dd>The name of the module</dd>
+ *      <dt>version:</dt>   <dd>The version in use</dd>
+ *      <dt>build:</dt>     <dd>The build number in use</dd>
+ *      <dt>versions:</dt>  <dd>All versions that were registered</dd>
+ *      <dt>builds:</dt>    <dd>All builds that were registered.</dd>
+ *      <dt>mainClass:</dt> <dd>An object that was was stamped with the
+ *                 current version and build. If 
+ *                 mainClass.VERSION != version or mainClass.BUILD != build,
+ *                 multiple versions of pieces of the library have been
+ *                 loaded, potentially causing issues.</dd>
+ *       </dl>
+ *
+ * @method getVersion
+ * @static
+ * @param {String}  name the name of the module (event, slider, etc)
+ * @return {Object} The version info
+ */
+YAHOO.env.getVersion = function(name) {
+    return YAHOO.env.modules[name] || null;
+};
+
+/**
+ * Do not fork for a browser if it can be avoided.  Use feature detection when
+ * you can.  Use the user agent as a last resort.  YAHOO.env.ua stores a version
+ * number for the browser engine, 0 otherwise.  This value may or may not map
+ * to the version number of the browser using the engine.  The value is 
+ * presented as a float so that it can easily be used for boolean evaluation 
+ * as well as for looking for a particular range of versions.  Because of this, 
+ * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9 
+ * reports 1.8).
+ * @class YAHOO.env.ua
+ * @static
+ */
+YAHOO.env.ua = function() {
+    var o={
+
+        /**
+         * Internet Explorer version number or 0.  Example: 6
+         * @property ie
+         * @type float
+         */
+        ie:0,
+
+        /**
+         * Opera version number or 0.  Example: 9.2
+         * @property opera
+         * @type float
+         */
+        opera:0,
+
+        /**
+         * Gecko engine revision number.  Will evaluate to 1 if Gecko 
+         * is detected but the revision could not be found. Other browsers
+         * will be 0.  Example: 1.8
+         * <pre>
+         * Firefox 1.0.0.4: 1.7.8   <-- Reports 1.7
+         * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8
+         * Firefox 2.0.0.3: 1.8.1.3 <-- Reports 1.8
+         * Firefox 3 alpha: 1.9a4   <-- Reports 1.9
+         * </pre>
+         * @property gecko
+         * @type float
+         */
+        gecko:0,
+
+        /**
+         * AppleWebKit version.  KHTML browsers that are not WebKit browsers 
+         * will evaluate to 1, other browsers 0.  Example: 418.9.1
+         * <pre>
+         * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the 
+         *                                   latest available for Mac OSX 10.3.
+         * Safari 2.0.2:         416     <-- hasOwnProperty introduced
+         * Safari 2.0.4:         418     <-- preventDefault fixed
+         * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
+         *                                   different versions of webkit
+         * Safari 2.0.4 (419.3): 419     <-- Current Safari release
+         * Webkit 212 nightly:   522+    <-- Safari 3.0 (with native SVG) should
+         *                                   be higher than this
+         *                                   
+         * </pre>
+         * http://developer.apple.com/internet/safari/uamatrix.html
+         * @property webkit
+         * @type float
+         */
+        webkit:0,
+
+        /**
+         * The mobile property will be set to a string containing any relevant
+         * user agent information when a modern mobile browser is detected.
+         * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
+         * devices with the WebKit-based browser, and Opera Mini.  
+         * @property mobile 
+         * @type string
+         */
+        mobile: null 
+    };
+
+    var ua=navigator.userAgent, m;
+
+    // Modern KHTML browsers should qualify as Safari X-Grade
+    if ((/KHTML/).test(ua)) {
+        o.webkit=1;
+    }
+    // Modern WebKit browsers are at least X-Grade
+    m=ua.match(/AppleWebKit\/([^\s]*)/);
+    if (m&&m[1]) {
+        o.webkit=parseFloat(m[1]);
+
+        // Mobile browser check
+        if (/ Mobile\//.test(ua)) {
+            o.mobile = "Apple"; // iPhone or iPod Touch
+        } else {
+            m=ua.match(/NokiaN[^\/]*/);
+            if (m) {
+                o.mobile = m[0]; // Nokia N-series, ex: NokiaN95
+            }
+        }
+
+    }
+
+    if (!o.webkit) { // not webkit
+        // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
+        m=ua.match(/Opera[\s\/]([^\s]*)/);
+        if (m&&m[1]) {
+            o.opera=parseFloat(m[1]);
+            m=ua.match(/Opera Mini[^;]*/);
+            if (m) {
+                o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
+            }
+        } else { // not opera or webkit
+            m=ua.match(/MSIE\s([^;]*)/);
+            if (m&&m[1]) {
+                o.ie=parseFloat(m[1]);
+            } else { // not opera, webkit, or ie
+                m=ua.match(/Gecko\/([^\s]*)/);
+                if (m) {
+                    o.gecko=1; // Gecko detected, look for revision
+                    m=ua.match(/rv:([^\s\)]*)/);
+                    if (m&&m[1]) {
+                        o.gecko=parseFloat(m[1]);
+                    }
+                }
+            }
+        }
+    }
+    
+    return o;
+}();
+
+/*
+ * Initializes the global by creating the default namespaces and applying
+ * any new configuration information that is detected.  This is the setup
+ * for env.
+ * @method init
+ * @static
+ * @private
+ */
+(function() {
+    YAHOO.namespace("util", "widget", "example");
+    if ("undefined" !== typeof YAHOO_config) {
+        var l=YAHOO_config.listener,ls=YAHOO.env.listeners,unique=true,i;
+        if (l) {
+            // if YAHOO is loaded multiple times we need to check to see if
+            // this is a new config object.  If it is, add the new component
+            // load listener to the stack
+            for (i=0;i<ls.length;i=i+1) {
+                if (ls[i]==l) {
+                    unique=false;
+                    break;
+                }
+            }
+            if (unique) {
+                ls.push(l);
+            }
+        }
+    }
+})();
+/**
+ * Provides the language utilites and extensions used by the library
+ * @class YAHOO.lang
+ */
+YAHOO.lang = YAHOO.lang || {
+    /**
+     * Determines whether or not the provided object is an array.
+     * Testing typeof/instanceof/constructor of arrays across frame 
+     * boundaries isn't possible in Safari unless you have a reference
+     * to the other frame to test against its Array prototype.  To
+     * handle this case, we test well-known array properties instead.
+     * properties.
+     * @method isArray
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isArray: function(o) { 
+
+        if (o) {
+           var l = YAHOO.lang;
+           return l.isNumber(o.length) && l.isFunction(o.splice);
+        }
+        return false;
+    },
+
+    /**
+     * Determines whether or not the provided object is a boolean
+     * @method isBoolean
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isBoolean: function(o) {
+        return typeof o === 'boolean';
+    },
+    
+    /**
+     * Determines whether or not the provided object is a function
+     * @method isFunction
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isFunction: function(o) {
+        return typeof o === 'function';
+    },
+        
+    /**
+     * Determines whether or not the provided object is null
+     * @method isNull
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isNull: function(o) {
+        return o === null;
+    },
+        
+    /**
+     * Determines whether or not the provided object is a legal number
+     * @method isNumber
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isNumber: function(o) {
+        return typeof o === 'number' && isFinite(o);
+    },
+      
+    /**
+     * Determines whether or not the provided object is of type object
+     * or function
+     * @method isObject
+     * @param {any} o The object being testing
+     * @return Boolean
+     */  
+    isObject: function(o) {
+return (o && (typeof o === 'object' || YAHOO.lang.isFunction(o))) || false;
+    },
+        
+    /**
+     * Determines whether or not the provided object is a string
+     * @method isString
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isString: function(o) {
+        return typeof o === 'string';
+    },
+        
+    /**
+     * Determines whether or not the provided object is undefined
+     * @method isUndefined
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isUndefined: function(o) {
+        return typeof o === 'undefined';
+    },
+    
+    /**
+     * Determines whether or not the property was added
+     * to the object instance.  Returns false if the property is not present
+     * in the object, or was inherited from the prototype.
+     * This abstraction is provided to enable hasOwnProperty for Safari 1.3.x.
+     * There is a discrepancy between YAHOO.lang.hasOwnProperty and
+     * Object.prototype.hasOwnProperty when the property is a primitive added to
+     * both the instance AND prototype with the same value:
+     * <pre>
+     * var A = function() {};
+     * A.prototype.foo = 'foo';
+     * var a = new A();
+     * a.foo = 'foo';
+     * alert(a.hasOwnProperty('foo')); // true
+     * alert(YAHOO.lang.hasOwnProperty(a, 'foo')); // false when using fallback
+     * </pre>
+     * @method hasOwnProperty
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    hasOwnProperty: function(o, prop) {
+        if (Object.prototype.hasOwnProperty) {
+            return o.hasOwnProperty(prop);
+        }
+        
+        return !YAHOO.lang.isUndefined(o[prop]) && 
+                o.constructor.prototype[prop] !== o[prop];
+    },
+ 
+    /**
+     * IE will not enumerate native functions in a derived object even if the
+     * function was overridden.  This is a workaround for specific functions 
+     * we care about on the Object prototype. 
+     * @property _IEEnumFix
+     * @param {Function} r  the object to receive the augmentation
+     * @param {Function} s  the object that supplies the properties to augment
+     * @static
+     * @private
+     */
+    _IEEnumFix: function(r, s) {
+        if (YAHOO.env.ua.ie) {
+            var add=["toString", "valueOf"], i;
+            for (i=0;i<add.length;i=i+1) {
+                var fname=add[i],f=s[fname];
+                if (YAHOO.lang.isFunction(f) && f!=Object.prototype[fname]) {
+                    r[fname]=f;
+                }
+            }
+        }
+    },
+       
+    /**
+     * Utility to set up the prototype, constructor and superclass properties to
+     * support an inheritance strategy that can chain constructors and methods.
+     * Static members will not be inherited.
+     *
+     * @method extend
+     * @static
+     * @param {Function} subc   the object to modify
+     * @param {Function} superc the object to inherit
+     * @param {Object} overrides  additional properties/methods to add to the
+     *                              subclass prototype.  These will override the
+     *                              matching items obtained from the superclass 
+     *                              if present.
+     */
+    extend: function(subc, superc, overrides) {
+        if (!superc||!subc) {
+            throw new Error("YAHOO.lang.extend failed, please check that " +
+                            "all dependencies are included.");
+        }
+        var F = function() {};
+        F.prototype=superc.prototype;
+        subc.prototype=new F();
+        subc.prototype.constructor=subc;
+        subc.superclass=superc.prototype;
+        if (superc.prototype.constructor == Object.prototype.constructor) {
+            superc.prototype.constructor=superc;
+        }
+    
+        if (overrides) {
+            for (var i in overrides) {
+                subc.prototype[i]=overrides[i];
+            }
+
+            YAHOO.lang._IEEnumFix(subc.prototype, overrides);
+        }
+    },
+   
+    /**
+     * Applies all properties in the supplier to the receiver if the
+     * receiver does not have these properties yet.  Optionally, one or 
+     * more methods/properties can be specified (as additional 
+     * parameters).  This option will overwrite the property if receiver 
+     * has it already.  If true is passed as the third parameter, all 
+     * properties will be applied and _will_ overwrite properties in 
+     * the receiver.
+     *
+     * @method augmentObject
+     * @static
+     * @since 2.3.0
+     * @param {Function} r  the object to receive the augmentation
+     * @param {Function} s  the object that supplies the properties to augment
+     * @param {String*|boolean}  arguments zero or more properties methods 
+     *        to augment the receiver with.  If none specified, everything
+     *        in the supplier will be used unless it would
+     *        overwrite an existing property in the receiver. If true
+     *        is specified as the third parameter, all properties will
+     *        be applied and will overwrite an existing property in
+     *        the receiver
+     */
+    augmentObject: function(r, s) {
+        if (!s||!r) {
+            throw new Error("Absorb failed, verify dependencies.");
+        }
+        var a=arguments, i, p, override=a[2];
+        if (override && override!==true) { // only absorb the specified properties
+            for (i=2; i<a.length; i=i+1) {
+                r[a[i]] = s[a[i]];
+            }
+        } else { // take everything, overwriting only if the third parameter is true
+            for (p in s) { 
+                if (override || !r[p]) {
+                    r[p] = s[p];
+                }
+            }
+            
+            YAHOO.lang._IEEnumFix(r, s);
+        }
+    },
+ 
+    /**
+     * Same as YAHOO.lang.augmentObject, except it only applies prototype properties
+     * @see YAHOO.lang.augmentObject
+     * @method augmentProto
+     * @static
+     * @param {Function} r  the object to receive the augmentation
+     * @param {Function} s  the object that supplies the properties to augment
+     * @param {String*|boolean}  arguments zero or more properties methods 
+     *        to augment the receiver with.  If none specified, everything 
+     *        in the supplier will be used unless it would overwrite an existing 
+     *        property in the receiver.  if true is specified as the third 
+     *        parameter, all properties will be applied and will overwrite an 
+     *        existing property in the receiver
+     */
+    augmentProto: function(r, s) {
+        if (!s||!r) {
+            throw new Error("Augment failed, verify dependencies.");
+        }
+        //var a=[].concat(arguments);
+        var a=[r.prototype,s.prototype];
+        for (var i=2;i<arguments.length;i=i+1) {
+            a.push(arguments[i]);
+        }
+        YAHOO.lang.augmentObject.apply(this, a);
+    },
+
+      
+    /**
+     * Returns a simple string representation of the object or array.
+     * Other types of objects will be returned unprocessed.  Arrays
+     * are expected to be indexed.  Use object notation for
+     * associative arrays.
+     * @method dump
+     * @since 2.3.0
+     * @param o {Object} The object to dump
+     * @param d {int} How deep to recurse child objects, default 3
+     * @return {String} the dump result
+     */
+    dump: function(o, d) {
+        var l=YAHOO.lang,i,len,s=[],OBJ="{...}",FUN="f(){...}",
+            COMMA=', ', ARROW=' => ';
+
+        // Cast non-objects to string
+        // Skip dates because the std toString is what we want
+        // Skip HTMLElement-like objects because trying to dump 
+        // an element will cause an unhandled exception in FF 2.x
+        if (!l.isObject(o)) {
+            return o + "";
+        } else if (o instanceof Date || ("nodeType" in o && "tagName" in o)) {
+            return o;
+        } else if  (l.isFunction(o)) {
+            return FUN;
+        }
+
+        // dig into child objects the depth specifed. Default 3
+        d = (l.isNumber(d)) ? d : 3;
+
+        // arrays [1, 2, 3]
+        if (l.isArray(o)) {
+            s.push("[");
+            for (i=0,len=o.length;i<len;i=i+1) {
+                if (l.isObject(o[i])) {
+                    s.push((d > 0) ? l.dump(o[i], d-1) : OBJ);
+                } else {
+                    s.push(o[i]);
+                }
+                s.push(COMMA);
+            }
+            if (s.length > 1) {
+                s.pop();
+            }
+            s.push("]");
+        // objects {k1 => v1, k2 => v2}
+        } else {
+            s.push("{");
+            for (i in o) {
+                if (l.hasOwnProperty(o, i)) {
+                    s.push(i + ARROW);
+                    if (l.isObject(o[i])) {
+                        s.push((d > 0) ? l.dump(o[i], d-1) : OBJ);
+                    } else {
+                        s.push(o[i]);
+                    }
+                    s.push(COMMA);
+                }
+            }
+            if (s.length > 1) {
+                s.pop();
+            }
+            s.push("}");
+        }
+
+        return s.join("");
+    },
+
+    /**
+     * Does variable substitution on a string. It scans through the string 
+     * looking for expressions enclosed in { } braces. If an expression 
+     * is found, it is used a key on the object.  If there is a space in
+     * the key, the first word is used for the key and the rest is provided
+     * to an optional function to be used to programatically determine the
+     * value (the extra information might be used for this decision). If 
+     * the value for the key in the object, or what is returned from the
+     * function has a string value, number value, or object value, it is 
+     * substituted for the bracket expression and it repeats.  If this
+     * value is an object, it uses the Object's toString() if this has
+     * been overridden, otherwise it does a shallow dump of the key/value
+     * pairs.
+     * @method substitute
+     * @since 2.3.0
+     * @param s {String} The string that will be modified.
+     * @param o {Object} An object containing the replacement values
+     * @param f {Function} An optional function that can be used to
+     *                     process each match.  It receives the key,
+     *                     value, and any extra metadata included with
+     *                     the key inside of the braces.
+     * @return {String} the substituted string
+     */
+    substitute: function (s, o, f) {
+        var i, j, k, key, v, meta, l=YAHOO.lang, saved=[], token, 
+            DUMP='dump', SPACE=' ', LBRACE='{', RBRACE='}';
+
+
+        for (;;) {
+            i = s.lastIndexOf(LBRACE);
+            if (i < 0) {
+                break;
+            }
+            j = s.indexOf(RBRACE, i);
+            if (i + 1 >= j) {
+                break;
+            }
+
+            //Extract key and meta info 
+            token = s.substring(i + 1, j);
+            key = token;
+            meta = null;
+            k = key.indexOf(SPACE);
+            if (k > -1) {
+                meta = key.substring(k + 1);
+                key = key.substring(0, k);
+            }
+
+            // lookup the value
+            v = o[key];
+
+            // if a substitution function was provided, execute it
+            if (f) {
+                v = f(key, v, meta);
+            }
+
+            if (l.isObject(v)) {
+                if (l.isArray(v)) {
+                    v = l.dump(v, parseInt(meta, 10));
+                } else {
+                    meta = meta || "";
+
+                    // look for the keyword 'dump', if found force obj dump
+                    var dump = meta.indexOf(DUMP);
+                    if (dump > -1) {
+                        meta = meta.substring(4);
+                    }
+
+                    // use the toString if it is not the Object toString 
+                    // and the 'dump' meta info was not found
+                    if (v.toString===Object.prototype.toString||dump>-1) {
+                        v = l.dump(v, parseInt(meta, 10));
+                    } else {
+                        v = v.toString();
+                    }
+                }
+            } else if (!l.isString(v) && !l.isNumber(v)) {
+                // This {block} has no replace string. Save it for later.
+                v = "~-" + saved.length + "-~";
+                saved[saved.length] = token;
+
+                // break;
+            }
+
+            s = s.substring(0, i) + v + s.substring(j + 1);
+
+
+        }
+
+        // restore saved {block}s
+        for (i=saved.length-1; i>=0; i=i-1) {
+            s = s.replace(new RegExp("~-" + i + "-~"), "{"  + saved[i] + "}", "g");
+        }
+
+        return s;
+    },
+
+
+    /**
+     * Returns a string without any leading or trailing whitespace.  If 
+     * the input is not a string, the input will be returned untouched.
+     * @method trim
+     * @since 2.3.0
+     * @param s {string} the string to trim
+     * @return {string} the trimmed string
+     */
+    trim: function(s){
+        try {
+            return s.replace(/^\s+|\s+$/g, "");
+        } catch(e) {
+            return s;
+        }
+    },
+
+    /**
+     * Returns a new object containing all of the properties of
+     * all the supplied objects.  The properties from later objects
+     * will overwrite those in earlier objects.
+     * @method merge
+     * @since 2.3.0
+     * @param arguments {Object*} the objects to merge
+     * @return the new merged object
+     */
+    merge: function() {
+        var o={}, a=arguments;
+        for (var i=0, l=a.length; i<l; i=i+1) {
+            YAHOO.lang.augmentObject(o, a[i], true);
+        }
+        return o;
+    },
+
+    /**
+     * Executes the supplied function in the scope of the supplied 
+     * object 'when' milliseconds later.  Executes the function a 
+     * single time unless periodic is set to true.
+     * @method later
+     * @since 2.4.0
+     * @param when {int} the number of milliseconds to wait until the fn 
+     * is executed
+     * @param o the scope object
+     * @param fn {Function|String} the function to execute or the name of 
+     * the method in the 'o' object to execute
+     * @param data [Array] data that is provided to the function.  This accepts
+     * either a single item or an array.  If an array is provided, the
+     * function is executed with one parameter for each array item.  If
+     * you need to pass a single array parameter, it needs to be wrapped in
+     * an array [myarray]
+     * @param periodic {boolean} if true, executes continuously at supplied 
+     * interval until canceled
+     * @return a timer object. Call the cancel() method on this object to 
+     * stop the timer.
+     */
+    later: function(when, o, fn, data, periodic) {
+        when = when || 0; 
+        o = o || {};
+        var m=fn, d=data, f, r;
+
+        if (YAHOO.lang.isString(fn)) {
+            m = o[fn];
+        }
+
+        if (!m) {
+            throw new TypeError("method undefined");
+        }
+
+        if (!YAHOO.lang.isArray(d)) {
+            d = [data];
+        }
+
+        f = function() {
+            m.apply(o, d);
+        };
+
+        r = (periodic) ? setInterval(f, when) : setTimeout(f, when);
+
+        return {
+            interval: periodic,
+            cancel: function() {
+                if (this.interval) {
+                    clearInterval(r);
+                } else {
+                    clearTimeout(r);
+                }
+            }
+        };
+    },
+
+    /**
+     * A convenience method for detecting a legitimate non-null value.
+     * Returns false for null/undefined/NaN, true for other values, 
+     * including 0/false/''
+     * @method isValue
+     * @since 2.3.0
+     * @param o {any} the item to test
+     * @return {boolean} true if it is not null/undefined/NaN || false
+     */
+    isValue: function(o) {
+        // return (o || o === false || o === 0 || o === ''); // Infinity fails
+        var l = YAHOO.lang;
+return (l.isObject(o) || l.isString(o) || l.isNumber(o) || l.isBoolean(o));
+    }
+
+};
+
+/*
+ * An alias for <a href="YAHOO.lang.html">YAHOO.lang</a>
+ * @class YAHOO.util.Lang
+ */
+YAHOO.util.Lang = YAHOO.lang;
+ 
+/**
+ * Same as YAHOO.lang.augmentObject, except it only applies prototype 
+ * properties.  This is an alias for augmentProto.
+ * @see YAHOO.lang.augmentObject
+ * @method augment
+ * @static
+ * @param {Function} r  the object to receive the augmentation
+ * @param {Function} s  the object that supplies the properties to augment
+ * @param {String*|boolean}  arguments zero or more properties methods to 
+ *        augment the receiver with.  If none specified, everything
+ *        in the supplier will be used unless it would
+ *        overwrite an existing property in the receiver.  if true
+ *        is specified as the third parameter, all properties will
+ *        be applied and will overwrite an existing property in
+ *        the receiver
+ */
+YAHOO.lang.augment = YAHOO.lang.augmentProto;
+
+/**
+ * An alias for <a href="YAHOO.lang.html#augment">YAHOO.lang.augment</a>
+ * @for YAHOO
+ * @method augment
+ * @static
+ * @param {Function} r  the object to receive the augmentation
+ * @param {Function} s  the object that supplies the properties to augment
+ * @param {String*}  arguments zero or more properties methods to 
+ *        augment the receiver with.  If none specified, everything
+ *        in the supplier will be used unless it would
+ *        overwrite an existing property in the receiver
+ */
+YAHOO.augment = YAHOO.lang.augmentProto;
+       
+/**
+ * An alias for <a href="YAHOO.lang.html#extend">YAHOO.lang.extend</a>
+ * @method extend
+ * @static
+ * @param {Function} subc   the object to modify
+ * @param {Function} superc the object to inherit
+ * @param {Object} overrides  additional properties/methods to add to the
+ *        subclass prototype.  These will override the
+ *        matching items obtained from the superclass if present.
+ */
+YAHOO.extend = YAHOO.lang.extend;
+
+YAHOO.register("yahoo", YAHOO, {version: "2.4.1", build: "742"});
Index: trunk/wb/include/yui/yahoo/yahoo-min.js
===================================================================
--- trunk/wb/include/yui/yahoo/yahoo-min.js	(nonexistent)
+++ trunk/wb/include/yui/yahoo/yahoo-min.js	(revision 601)
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+if(typeof YAHOO=="undefined"||!YAHOO){var YAHOO={};}YAHOO.namespace=function(){var A=arguments,E=null,C,B,D;for(C=0;C<A.length;C=C+1){D=A[C].split(".");E=YAHOO;for(B=(D[0]=="YAHOO")?1:0;B<D.length;B=B+1){E[D[B]]=E[D[B]]||{};E=E[D[B]];}}return E;};YAHOO.log=function(D,A,C){var B=YAHOO.widget.Logger;if(B&&B.log){return B.log(D,A,C);}else{return false;}};YAHOO.register=function(A,E,D){var I=YAHOO.env.modules;if(!I[A]){I[A]={versions:[],builds:[]};}var B=I[A],H=D.version,G=D.build,F=YAHOO.env.listeners;B.name=A;B.version=H;B.build=G;B.versions.push(H);B.builds.push(G);B.mainClass=E;for(var C=0;C<F.length;C=C+1){F[C](B);}if(E){E.VERSION=H;E.BUILD=G;}else{YAHOO.log("mainClass is undefined for module "+A,"warn");}};YAHOO.env=YAHOO.env||{modules:[],listeners:[]};YAHOO.env.getVersion=function(A){return YAHOO.env.modules[A]||null;};YAHOO.env.ua=function(){var C={ie:0,opera:0,gecko:0,webkit:0,mobile:null};var B=navigator.userAgent,A;if((/KHTML/).test(B)){C.webkit=1;}A=B.match(/AppleWebKit\/([^\s]*)/);if(A&&A[1]){C.webkit=parseFloat(A[1]);if(/ Mobile\//.test(B)){C.mobile="Apple";}else{A=B.match(/NokiaN[^\/]*/);if(A){C.mobile=A[0];}}}if(!C.webkit){A=B.match(/Opera[\s\/]([^\s]*)/);if(A&&A[1]){C.opera=parseFloat(A[1]);A=B.match(/Opera Mini[^;]*/);if(A){C.mobile=A[0];}}else{A=B.match(/MSIE\s([^;]*)/);if(A&&A[1]){C.ie=parseFloat(A[1]);}else{A=B.match(/Gecko\/([^\s]*)/);if(A){C.gecko=1;A=B.match(/rv:([^\s\)]*)/);if(A&&A[1]){C.gecko=parseFloat(A[1]);}}}}}return C;}();(function(){YAHOO.namespace("util","widget","example");if("undefined"!==typeof YAHOO_config){var B=YAHOO_config.listener,A=YAHOO.env.listeners,D=true,C;if(B){for(C=0;C<A.length;C=C+1){if(A[C]==B){D=false;break;}}if(D){A.push(B);}}}})();YAHOO.lang=YAHOO.lang||{isArray:function(B){if(B){var A=YAHOO.lang;return A.isNumber(B.length)&&A.isFunction(B.splice);}return false;},isBoolean:function(A){return typeof A==="boolean";},isFunction:function(A){return typeof A==="function";},isNull:function(A){return A===null;},isNumber:function(A){return typeof A==="number"&&isFinite(A);},isObject:function(A){return(A&&(typeof A==="object"||YAHOO.lang.isFunction(A)))||false;},isString:function(A){return typeof A==="string";},isUndefined:function(A){return typeof A==="undefined";},hasOwnProperty:function(A,B){if(Object.prototype.hasOwnProperty){return A.hasOwnProperty(B);}return !YAHOO.lang.isUndefined(A[B])&&A.constructor.prototype[B]!==A[B];},_IEEnumFix:function(C,B){if(YAHOO.env.ua.ie){var E=["toString","valueOf"],A;for(A=0;A<E.length;A=A+1){var F=E[A],D=B[F];if(YAHOO.lang.isFunction(D)&&D!=Object.prototype[F]){C[F]=D;}}}},extend:function(D,E,C){if(!E||!D){throw new Error("YAHOO.lang.extend failed, please check that all dependencies are included.");}var B=function(){};B.prototype=E.prototype;D.prototype=new B();D.prototype.constructor=D;D.superclass=E.prototype;if(E.prototype.constructor==Object.prototype.constructor){E.prototype.constructor=E;}if(C){for(var A in C){D.prototype[A]=C[A];}YAHOO.lang._IEEnumFix(D.prototype,C);}},augmentObject:function(E,D){if(!D||!E){throw new Error("Absorb failed, verify dependencies.");}var A=arguments,C,F,B=A[2];if(B&&B!==true){for(C=2;C<A.length;C=C+1){E[A[C]]=D[A[C]];}}else{for(F in D){if(B||!E[F]){E[F]=D[F];}}YAHOO.lang._IEEnumFix(E,D);}},augmentProto:function(D,C){if(!C||!D){throw new Error("Augment failed, verify dependencies.");}var A=[D.prototype,C.prototype];for(var B=2;B<arguments.length;B=B+1){A.push(arguments[B]);}YAHOO.lang.augmentObject.apply(this,A);},dump:function(A,G){var C=YAHOO.lang,D,F,I=[],J="{...}",B="f(){...}",H=", ",E=" => ";if(!C.isObject(A)){return A+"";}else{if(A instanceof Date||("nodeType" in A&&"tagName" in A)){return A;}else{if(C.isFunction(A)){return B;}}}G=(C.isNumber(G))?G:3;if(C.isArray(A)){I.push("[");for(D=0,F=A.length;D<F;D=D+1){if(C.isObject(A[D])){I.push((G>0)?C.dump(A[D],G-1):J);}else{I.push(A[D]);}I.push(H);}if(I.length>1){I.pop();}I.push("]");}else{I.push("{");for(D in A){if(C.hasOwnProperty(A,D)){I.push(D+E);if(C.isObject(A[D])){I.push((G>0)?C.dump(A[D],G-1):J);}else{I.push(A[D]);}I.push(H);}}if(I.length>1){I.pop();}I.push("}");}return I.join("");},substitute:function(Q,B,J){var G,F,E,M,N,P,D=YAHOO.lang,L=[],C,H="dump",K=" ",A="{",O="}";for(;;){G=Q.lastIndexOf(A);if(G<0){break;}F=Q.indexOf(O,G);if(G+1>=F){break;}C=Q.substring(G+1,F);M=C;P=null;E=M.indexOf(K);if(E>-1){P=M.substring(E+1);M=M.substring(0,E);}N=B[M];if(J){N=J(M,N,P);}if(D.isObject(N)){if(D.isArray(N)){N=D.dump(N,parseInt(P,10));}else{P=P||"";var I=P.indexOf(H);if(I>-1){P=P.substring(4);}if(N.toString===Object.prototype.toString||I>-1){N=D.dump(N,parseInt(P,10));}else{N=N.toString();}}}else{if(!D.isString(N)&&!D.isNumber(N)){N="~-"+L.length+"-~";L[L.length]=C;}}Q=Q.substring(0,G)+N+Q.substring(F+1);}for(G=L.length-1;G>=0;G=G-1){Q=Q.replace(new RegExp("~-"+G+"-~"),"{"+L[G]+"}","g");}return Q;},trim:function(A){try{return A.replace(/^\s+|\s+$/g,"");}catch(B){return A;}},merge:function(){var D={},B=arguments;for(var C=0,A=B.length;C<A;C=C+1){YAHOO.lang.augmentObject(D,B[C],true);}return D;},later:function(H,B,I,D,E){H=H||0;B=B||{};var C=I,G=D,F,A;if(YAHOO.lang.isString(I)){C=B[I];}if(!C){throw new TypeError("method undefined");}if(!YAHOO.lang.isArray(G)){G=[D];}F=function(){C.apply(B,G);};A=(E)?setInterval(F,H):setTimeout(F,H);return{interval:E,cancel:function(){if(this.interval){clearInterval(A);}else{clearTimeout(A);}}};},isValue:function(B){var A=YAHOO.lang;return(A.isObject(B)||A.isString(B)||A.isNumber(B)||A.isBoolean(B));}};YAHOO.util.Lang=YAHOO.lang;YAHOO.lang.augment=YAHOO.lang.augmentProto;YAHOO.augment=YAHOO.lang.augmentProto;YAHOO.extend=YAHOO.lang.extend;YAHOO.register("yahoo",YAHOO,{version:"2.4.1",build:"742"});
\ No newline at end of file
Index: trunk/wb/include/yui/yahoo/README
===================================================================
--- trunk/wb/include/yui/yahoo/README	(nonexistent)
+++ trunk/wb/include/yui/yahoo/README	(revision 601)
@@ -0,0 +1,95 @@
+YAHOO Global - Release Notes
+
+2.4.1
+
+No change
+
+2.4.0
+  * Added YAHOO.env.ua.mobile
+  * Removed the hasOwnProperty check in isArray to make it perform a bit better.
+  * YAHOO will be created/overwritten if YAHOO is undefined or the defined YAHOO is falsy
+
+2.3.1
+  * YAHOO.lang.dump casts primitives to strings
+
+2.3.0
+  * Added YAHOO.env.ua - browser detection
+  * Added YAHOO.lang.merge - object merge
+  * Added YAHOO.lang.trim - whitespace removal
+  * Added YAHOO.lang.augmentObject - mixins.  Renamed augment to augmentProto and 
+    made augment an alias to augmentProto
+  * isObject/isArray return false rather than null when testing null
+
+2.2.2
+
+  * No change
+
+2.2.1
+
+  * YAHOO.lang.isObject returns false for null
+  * YAHOO.lang.isArray no longer generates an error when the argument is null/undefined
+  * Better error messages for YAHOO.lang.augment/extend failures that are usually due 
+    to missing dependencies.
+
+2.2.0a
+
+  * Fixed a typo that prevented the proper detection of multiple YAHOO_config listeners
+
+2.2.0
+
+  * Added configuration of the library via a YAHOO_config object defined
+    prior to the reference to the library.  Currently it supports a listener
+    parameter that will execute every time a new module YUI module is loaded.
+
+  * Added YAHOO.register & YAHOO.getVersion for version stamping modules.
+  
+  * Added YAHOO.env which is the home for the information about the loaded
+    YUI modules, and will host more info later.
+    
+  * YAHOO.util.Lang was relocated to this package and is now YAHOO.lang.
+    YAHOO.extend and YAHOO.augment have moved to env (the original
+    functions still work).
+
+0.12.2
+
+  * No change
+
+0.12.1
+
+  * No change
+
+0.12.0
+
+   * Added YAHOO.augment, which copies all or part of the prototype of one
+     object to another.  
+
+   * YAHOO.namespace now can create multiple namespaces.
+
+   * Added an optional third parameter to YAHOO.extend: overrides.  It takes
+     an object literal of properties/methods to apply to the subclass
+     prototype, overriding the superclass if present.
+
+0.11.4
+
+   * Changed window.YAHOO = window.YAHOO || {} to 
+     if (typeof YAHOO == "undefined") YAHOO = {} because the previous statement
+     contributed to a memory leak in IE6 when the library was hosted in an 
+     iframe.
+
+0.11.3
+
+   * Changed var YAHOO = window.YAHOO || {} to window.YAHOO = window.YAHOO || {}.
+     This fixes an issue in IE where YAHOO would get overwritten if previously
+     defined via array notation (window["YAHOO"]).
+
+0.11.0
+
+   * Added YAHOO.extend, which provides an easy way to assign the prototype,
+     constructor, and superclass properties inheritance properties.  It also
+     prevents the constructor of the superclass from being exectuted twice.
+
+0.10.0
+
+   * Added YAHOO.log that provides a safe way to plumb logging statements in
+     code that will work if the logging component isn't available.
+
Index: trunk/wb/include/yui/yahoo/yahoo-debug.js
===================================================================
--- trunk/wb/include/yui/yahoo/yahoo-debug.js	(nonexistent)
+++ trunk/wb/include/yui/yahoo/yahoo-debug.js	(revision 601)
@@ -0,0 +1,952 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+/**
+ * The YAHOO object is the single global object used by YUI Library.  It
+ * contains utility function for setting up namespaces, inheritance, and
+ * logging.  YAHOO.util, YAHOO.widget, and YAHOO.example are namespaces
+ * created automatically for and used by the library.
+ * @module yahoo
+ * @title  YAHOO Global
+ */
+
+/**
+ * YAHOO_config is not included as part of the library.  Instead it is an 
+ * object that can be defined by the implementer immediately before 
+ * including the YUI library.  The properties included in this object
+ * will be used to configure global properties needed as soon as the 
+ * library begins to load.
+ * @class YAHOO_config
+ * @static
+ */
+
+/**
+ * A reference to a function that will be executed every time a YAHOO module
+ * is loaded.  As parameter, this function will receive the version
+ * information for the module. See <a href="YAHOO.env.html#getVersion">
+ * YAHOO.env.getVersion</a> for the description of the version data structure.
+ * @property listener
+ * @type Function
+ * @static
+ * @default undefined
+ */
+
+/**
+ * Set to true if the library will be dynamically loaded after window.onload.
+ * Defaults to false 
+ * @property injecting
+ * @type boolean
+ * @static
+ * @default undefined
+ */
+
+/**
+ * Instructs the yuiloader component to dynamically load yui components and
+ * their dependencies.  See the yuiloader documentation for more information
+ * about dynamic loading
+ * @property load
+ * @static
+ * @default undefined
+ * @see yuiloader
+ */
+
+/**
+ * Forces the use of the supplied locale where applicable in the library
+ * @property locale
+ * @type string
+ * @static
+ * @default undefined
+ */
+
+if (typeof YAHOO == "undefined" || !YAHOO) {
+    /**
+     * The YAHOO global namespace object.  If YAHOO is already defined, the
+     * existing YAHOO object will not be overwritten so that defined
+     * namespaces are preserved.
+     * @class YAHOO
+     * @static
+     */
+    var YAHOO = {};
+}
+
+/**
+ * Returns the namespace specified and creates it if it doesn't exist
+ * <pre>
+ * YAHOO.namespace("property.package");
+ * YAHOO.namespace("YAHOO.property.package");
+ * </pre>
+ * Either of the above would create YAHOO.property, then
+ * YAHOO.property.package
+ *
+ * Be careful when naming packages. Reserved words may work in some browsers
+ * and not others. For instance, the following will fail in Safari:
+ * <pre>
+ * YAHOO.namespace("really.long.nested.namespace");
+ * </pre>
+ * This fails because "long" is a future reserved word in ECMAScript
+ *
+ * @method namespace
+ * @static
+ * @param  {String*} arguments 1-n namespaces to create 
+ * @return {Object}  A reference to the last namespace object created
+ */
+YAHOO.namespace = function() {
+    var a=arguments, o=null, i, j, d;
+    for (i=0; i<a.length; i=i+1) {
+        d=a[i].split(".");
+        o=YAHOO;
+
+        // YAHOO is implied, so it is ignored if it is included
+        for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) {
+            o[d[j]]=o[d[j]] || {};
+            o=o[d[j]];
+        }
+    }
+
+    return o;
+};
+
+/**
+ * Uses YAHOO.widget.Logger to output a log message, if the widget is
+ * available.
+ *
+ * @method log
+ * @static
+ * @param  {String}  msg  The message to log.
+ * @param  {String}  cat  The log category for the message.  Default
+ *                        categories are "info", "warn", "error", time".
+ *                        Custom categories can be used as well. (opt)
+ * @param  {String}  src  The source of the the message (opt)
+ * @return {Boolean}      True if the log operation was successful.
+ */
+YAHOO.log = function(msg, cat, src) {
+    var l=YAHOO.widget.Logger;
+    if(l && l.log) {
+        return l.log(msg, cat, src);
+    } else {
+        return false;
+    }
+};
+
+/**
+ * Registers a module with the YAHOO object
+ * @method register
+ * @static
+ * @param {String}   name    the name of the module (event, slider, etc)
+ * @param {Function} mainClass a reference to class in the module.  This
+ *                             class will be tagged with the version info
+ *                             so that it will be possible to identify the
+ *                             version that is in use when multiple versions
+ *                             have loaded
+ * @param {Object}   data      metadata object for the module.  Currently it
+ *                             is expected to contain a "version" property
+ *                             and a "build" property at minimum.
+ */
+YAHOO.register = function(name, mainClass, data) {
+    var mods = YAHOO.env.modules;
+    if (!mods[name]) {
+        mods[name] = { versions:[], builds:[] };
+    }
+    var m=mods[name],v=data.version,b=data.build,ls=YAHOO.env.listeners;
+    m.name = name;
+    m.version = v;
+    m.build = b;
+    m.versions.push(v);
+    m.builds.push(b);
+    m.mainClass = mainClass;
+    // fire the module load listeners
+    for (var i=0;i<ls.length;i=i+1) {
+        ls[i](m);
+    }
+    // label the main class
+    if (mainClass) {
+        mainClass.VERSION = v;
+        mainClass.BUILD = b;
+    } else {
+        YAHOO.log("mainClass is undefined for module " + name, "warn");
+    }
+};
+
+/**
+ * YAHOO.env is used to keep track of what is known about the YUI library and
+ * the browsing environment
+ * @class YAHOO.env
+ * @static
+ */
+YAHOO.env = YAHOO.env || {
+
+    /**
+     * Keeps the version info for all YUI modules that have reported themselves
+     * @property modules
+     * @type Object[]
+     */
+    modules: [],
+    
+    /**
+     * List of functions that should be executed every time a YUI module
+     * reports itself.
+     * @property listeners
+     * @type Function[]
+     */
+    listeners: []
+};
+
+/**
+ * Returns the version data for the specified module:
+ *      <dl>
+ *      <dt>name:</dt>      <dd>The name of the module</dd>
+ *      <dt>version:</dt>   <dd>The version in use</dd>
+ *      <dt>build:</dt>     <dd>The build number in use</dd>
+ *      <dt>versions:</dt>  <dd>All versions that were registered</dd>
+ *      <dt>builds:</dt>    <dd>All builds that were registered.</dd>
+ *      <dt>mainClass:</dt> <dd>An object that was was stamped with the
+ *                 current version and build. If 
+ *                 mainClass.VERSION != version or mainClass.BUILD != build,
+ *                 multiple versions of pieces of the library have been
+ *                 loaded, potentially causing issues.</dd>
+ *       </dl>
+ *
+ * @method getVersion
+ * @static
+ * @param {String}  name the name of the module (event, slider, etc)
+ * @return {Object} The version info
+ */
+YAHOO.env.getVersion = function(name) {
+    return YAHOO.env.modules[name] || null;
+};
+
+/**
+ * Do not fork for a browser if it can be avoided.  Use feature detection when
+ * you can.  Use the user agent as a last resort.  YAHOO.env.ua stores a version
+ * number for the browser engine, 0 otherwise.  This value may or may not map
+ * to the version number of the browser using the engine.  The value is 
+ * presented as a float so that it can easily be used for boolean evaluation 
+ * as well as for looking for a particular range of versions.  Because of this, 
+ * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9 
+ * reports 1.8).
+ * @class YAHOO.env.ua
+ * @static
+ */
+YAHOO.env.ua = function() {
+    var o={
+
+        /**
+         * Internet Explorer version number or 0.  Example: 6
+         * @property ie
+         * @type float
+         */
+        ie:0,
+
+        /**
+         * Opera version number or 0.  Example: 9.2
+         * @property opera
+         * @type float
+         */
+        opera:0,
+
+        /**
+         * Gecko engine revision number.  Will evaluate to 1 if Gecko 
+         * is detected but the revision could not be found. Other browsers
+         * will be 0.  Example: 1.8
+         * <pre>
+         * Firefox 1.0.0.4: 1.7.8   <-- Reports 1.7
+         * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8
+         * Firefox 2.0.0.3: 1.8.1.3 <-- Reports 1.8
+         * Firefox 3 alpha: 1.9a4   <-- Reports 1.9
+         * </pre>
+         * @property gecko
+         * @type float
+         */
+        gecko:0,
+
+        /**
+         * AppleWebKit version.  KHTML browsers that are not WebKit browsers 
+         * will evaluate to 1, other browsers 0.  Example: 418.9.1
+         * <pre>
+         * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the 
+         *                                   latest available for Mac OSX 10.3.
+         * Safari 2.0.2:         416     <-- hasOwnProperty introduced
+         * Safari 2.0.4:         418     <-- preventDefault fixed
+         * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
+         *                                   different versions of webkit
+         * Safari 2.0.4 (419.3): 419     <-- Current Safari release
+         * Webkit 212 nightly:   522+    <-- Safari 3.0 (with native SVG) should
+         *                                   be higher than this
+         *                                   
+         * </pre>
+         * http://developer.apple.com/internet/safari/uamatrix.html
+         * @property webkit
+         * @type float
+         */
+        webkit:0,
+
+        /**
+         * The mobile property will be set to a string containing any relevant
+         * user agent information when a modern mobile browser is detected.
+         * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
+         * devices with the WebKit-based browser, and Opera Mini.  
+         * @property mobile 
+         * @type string
+         */
+        mobile: null 
+    };
+
+    var ua=navigator.userAgent, m;
+
+    // Modern KHTML browsers should qualify as Safari X-Grade
+    if ((/KHTML/).test(ua)) {
+        o.webkit=1;
+    }
+    // Modern WebKit browsers are at least X-Grade
+    m=ua.match(/AppleWebKit\/([^\s]*)/);
+    if (m&&m[1]) {
+        o.webkit=parseFloat(m[1]);
+
+        // Mobile browser check
+        if (/ Mobile\//.test(ua)) {
+            o.mobile = "Apple"; // iPhone or iPod Touch
+        } else {
+            m=ua.match(/NokiaN[^\/]*/);
+            if (m) {
+                o.mobile = m[0]; // Nokia N-series, ex: NokiaN95
+            }
+        }
+
+    }
+
+    if (!o.webkit) { // not webkit
+        // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
+        m=ua.match(/Opera[\s\/]([^\s]*)/);
+        if (m&&m[1]) {
+            o.opera=parseFloat(m[1]);
+            m=ua.match(/Opera Mini[^;]*/);
+            if (m) {
+                o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
+            }
+        } else { // not opera or webkit
+            m=ua.match(/MSIE\s([^;]*)/);
+            if (m&&m[1]) {
+                o.ie=parseFloat(m[1]);
+            } else { // not opera, webkit, or ie
+                m=ua.match(/Gecko\/([^\s]*)/);
+                if (m) {
+                    o.gecko=1; // Gecko detected, look for revision
+                    m=ua.match(/rv:([^\s\)]*)/);
+                    if (m&&m[1]) {
+                        o.gecko=parseFloat(m[1]);
+                    }
+                }
+            }
+        }
+    }
+    
+    return o;
+}();
+
+/*
+ * Initializes the global by creating the default namespaces and applying
+ * any new configuration information that is detected.  This is the setup
+ * for env.
+ * @method init
+ * @static
+ * @private
+ */
+(function() {
+    YAHOO.namespace("util", "widget", "example");
+    if ("undefined" !== typeof YAHOO_config) {
+        var l=YAHOO_config.listener,ls=YAHOO.env.listeners,unique=true,i;
+        if (l) {
+            // if YAHOO is loaded multiple times we need to check to see if
+            // this is a new config object.  If it is, add the new component
+            // load listener to the stack
+            for (i=0;i<ls.length;i=i+1) {
+                if (ls[i]==l) {
+                    unique=false;
+                    break;
+                }
+            }
+            if (unique) {
+                ls.push(l);
+            }
+        }
+    }
+})();
+/**
+ * Provides the language utilites and extensions used by the library
+ * @class YAHOO.lang
+ */
+YAHOO.lang = YAHOO.lang || {
+    /**
+     * Determines whether or not the provided object is an array.
+     * Testing typeof/instanceof/constructor of arrays across frame 
+     * boundaries isn't possible in Safari unless you have a reference
+     * to the other frame to test against its Array prototype.  To
+     * handle this case, we test well-known array properties instead.
+     * properties.
+     * @method isArray
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isArray: function(o) { 
+
+        if (o) {
+           var l = YAHOO.lang;
+           return l.isNumber(o.length) && l.isFunction(o.splice);
+        }
+        return false;
+    },
+
+    /**
+     * Determines whether or not the provided object is a boolean
+     * @method isBoolean
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isBoolean: function(o) {
+        return typeof o === 'boolean';
+    },
+    
+    /**
+     * Determines whether or not the provided object is a function
+     * @method isFunction
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isFunction: function(o) {
+        return typeof o === 'function';
+    },
+        
+    /**
+     * Determines whether or not the provided object is null
+     * @method isNull
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isNull: function(o) {
+        return o === null;
+    },
+        
+    /**
+     * Determines whether or not the provided object is a legal number
+     * @method isNumber
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isNumber: function(o) {
+        return typeof o === 'number' && isFinite(o);
+    },
+      
+    /**
+     * Determines whether or not the provided object is of type object
+     * or function
+     * @method isObject
+     * @param {any} o The object being testing
+     * @return Boolean
+     */  
+    isObject: function(o) {
+return (o && (typeof o === 'object' || YAHOO.lang.isFunction(o))) || false;
+    },
+        
+    /**
+     * Determines whether or not the provided object is a string
+     * @method isString
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isString: function(o) {
+        return typeof o === 'string';
+    },
+        
+    /**
+     * Determines whether or not the provided object is undefined
+     * @method isUndefined
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    isUndefined: function(o) {
+        return typeof o === 'undefined';
+    },
+    
+    /**
+     * Determines whether or not the property was added
+     * to the object instance.  Returns false if the property is not present
+     * in the object, or was inherited from the prototype.
+     * This abstraction is provided to enable hasOwnProperty for Safari 1.3.x.
+     * There is a discrepancy between YAHOO.lang.hasOwnProperty and
+     * Object.prototype.hasOwnProperty when the property is a primitive added to
+     * both the instance AND prototype with the same value:
+     * <pre>
+     * var A = function() {};
+     * A.prototype.foo = 'foo';
+     * var a = new A();
+     * a.foo = 'foo';
+     * alert(a.hasOwnProperty('foo')); // true
+     * alert(YAHOO.lang.hasOwnProperty(a, 'foo')); // false when using fallback
+     * </pre>
+     * @method hasOwnProperty
+     * @param {any} o The object being testing
+     * @return Boolean
+     */
+    hasOwnProperty: function(o, prop) {
+        if (Object.prototype.hasOwnProperty) {
+            return o.hasOwnProperty(prop);
+        }
+        
+        return !YAHOO.lang.isUndefined(o[prop]) && 
+                o.constructor.prototype[prop] !== o[prop];
+    },
+ 
+    /**
+     * IE will not enumerate native functions in a derived object even if the
+     * function was overridden.  This is a workaround for specific functions 
+     * we care about on the Object prototype. 
+     * @property _IEEnumFix
+     * @param {Function} r  the object to receive the augmentation
+     * @param {Function} s  the object that supplies the properties to augment
+     * @static
+     * @private
+     */
+    _IEEnumFix: function(r, s) {
+        if (YAHOO.env.ua.ie) {
+            var add=["toString", "valueOf"], i;
+            for (i=0;i<add.length;i=i+1) {
+                var fname=add[i],f=s[fname];
+                if (YAHOO.lang.isFunction(f) && f!=Object.prototype[fname]) {
+                    r[fname]=f;
+                }
+            }
+        }
+    },
+       
+    /**
+     * Utility to set up the prototype, constructor and superclass properties to
+     * support an inheritance strategy that can chain constructors and methods.
+     * Static members will not be inherited.
+     *
+     * @method extend
+     * @static
+     * @param {Function} subc   the object to modify
+     * @param {Function} superc the object to inherit
+     * @param {Object} overrides  additional properties/methods to add to the
+     *                              subclass prototype.  These will override the
+     *                              matching items obtained from the superclass 
+     *                              if present.
+     */
+    extend: function(subc, superc, overrides) {
+        if (!superc||!subc) {
+            throw new Error("YAHOO.lang.extend failed, please check that " +
+                            "all dependencies are included.");
+        }
+        var F = function() {};
+        F.prototype=superc.prototype;
+        subc.prototype=new F();
+        subc.prototype.constructor=subc;
+        subc.superclass=superc.prototype;
+        if (superc.prototype.constructor == Object.prototype.constructor) {
+            superc.prototype.constructor=superc;
+        }
+    
+        if (overrides) {
+            for (var i in overrides) {
+                subc.prototype[i]=overrides[i];
+            }
+
+            YAHOO.lang._IEEnumFix(subc.prototype, overrides);
+        }
+    },
+   
+    /**
+     * Applies all properties in the supplier to the receiver if the
+     * receiver does not have these properties yet.  Optionally, one or 
+     * more methods/properties can be specified (as additional 
+     * parameters).  This option will overwrite the property if receiver 
+     * has it already.  If true is passed as the third parameter, all 
+     * properties will be applied and _will_ overwrite properties in 
+     * the receiver.
+     *
+     * @method augmentObject
+     * @static
+     * @since 2.3.0
+     * @param {Function} r  the object to receive the augmentation
+     * @param {Function} s  the object that supplies the properties to augment
+     * @param {String*|boolean}  arguments zero or more properties methods 
+     *        to augment the receiver with.  If none specified, everything
+     *        in the supplier will be used unless it would
+     *        overwrite an existing property in the receiver. If true
+     *        is specified as the third parameter, all properties will
+     *        be applied and will overwrite an existing property in
+     *        the receiver
+     */
+    augmentObject: function(r, s) {
+        if (!s||!r) {
+            throw new Error("Absorb failed, verify dependencies.");
+        }
+        var a=arguments, i, p, override=a[2];
+        if (override && override!==true) { // only absorb the specified properties
+            for (i=2; i<a.length; i=i+1) {
+                r[a[i]] = s[a[i]];
+            }
+        } else { // take everything, overwriting only if the third parameter is true
+            for (p in s) { 
+                if (override || !r[p]) {
+                    r[p] = s[p];
+                }
+            }
+            
+            YAHOO.lang._IEEnumFix(r, s);
+        }
+    },
+ 
+    /**
+     * Same as YAHOO.lang.augmentObject, except it only applies prototype properties
+     * @see YAHOO.lang.augmentObject
+     * @method augmentProto
+     * @static
+     * @param {Function} r  the object to receive the augmentation
+     * @param {Function} s  the object that supplies the properties to augment
+     * @param {String*|boolean}  arguments zero or more properties methods 
+     *        to augment the receiver with.  If none specified, everything 
+     *        in the supplier will be used unless it would overwrite an existing 
+     *        property in the receiver.  if true is specified as the third 
+     *        parameter, all properties will be applied and will overwrite an 
+     *        existing property in the receiver
+     */
+    augmentProto: function(r, s) {
+        if (!s||!r) {
+            throw new Error("Augment failed, verify dependencies.");
+        }
+        //var a=[].concat(arguments);
+        var a=[r.prototype,s.prototype];
+        for (var i=2;i<arguments.length;i=i+1) {
+            a.push(arguments[i]);
+        }
+        YAHOO.lang.augmentObject.apply(this, a);
+    },
+
+      
+    /**
+     * Returns a simple string representation of the object or array.
+     * Other types of objects will be returned unprocessed.  Arrays
+     * are expected to be indexed.  Use object notation for
+     * associative arrays.
+     * @method dump
+     * @since 2.3.0
+     * @param o {Object} The object to dump
+     * @param d {int} How deep to recurse child objects, default 3
+     * @return {String} the dump result
+     */
+    dump: function(o, d) {
+        var l=YAHOO.lang,i,len,s=[],OBJ="{...}",FUN="f(){...}",
+            COMMA=', ', ARROW=' => ';
+
+        // Cast non-objects to string
+        // Skip dates because the std toString is what we want
+        // Skip HTMLElement-like objects because trying to dump 
+        // an element will cause an unhandled exception in FF 2.x
+        if (!l.isObject(o)) {
+            return o + "";
+        } else if (o instanceof Date || ("nodeType" in o && "tagName" in o)) {
+            return o;
+        } else if  (l.isFunction(o)) {
+            return FUN;
+        }
+
+        // dig into child objects the depth specifed. Default 3
+        d = (l.isNumber(d)) ? d : 3;
+
+        // arrays [1, 2, 3]
+        if (l.isArray(o)) {
+            s.push("[");
+            for (i=0,len=o.length;i<len;i=i+1) {
+                if (l.isObject(o[i])) {
+                    s.push((d > 0) ? l.dump(o[i], d-1) : OBJ);
+                } else {
+                    s.push(o[i]);
+                }
+                s.push(COMMA);
+            }
+            if (s.length > 1) {
+                s.pop();
+            }
+            s.push("]");
+        // objects {k1 => v1, k2 => v2}
+        } else {
+            s.push("{");
+            for (i in o) {
+                if (l.hasOwnProperty(o, i)) {
+                    s.push(i + ARROW);
+                    if (l.isObject(o[i])) {
+                        s.push((d > 0) ? l.dump(o[i], d-1) : OBJ);
+                    } else {
+                        s.push(o[i]);
+                    }
+                    s.push(COMMA);
+                }
+            }
+            if (s.length > 1) {
+                s.pop();
+            }
+            s.push("}");
+        }
+
+        return s.join("");
+    },
+
+    /**
+     * Does variable substitution on a string. It scans through the string 
+     * looking for expressions enclosed in { } braces. If an expression 
+     * is found, it is used a key on the object.  If there is a space in
+     * the key, the first word is used for the key and the rest is provided
+     * to an optional function to be used to programatically determine the
+     * value (the extra information might be used for this decision). If 
+     * the value for the key in the object, or what is returned from the
+     * function has a string value, number value, or object value, it is 
+     * substituted for the bracket expression and it repeats.  If this
+     * value is an object, it uses the Object's toString() if this has
+     * been overridden, otherwise it does a shallow dump of the key/value
+     * pairs.
+     * @method substitute
+     * @since 2.3.0
+     * @param s {String} The string that will be modified.
+     * @param o {Object} An object containing the replacement values
+     * @param f {Function} An optional function that can be used to
+     *                     process each match.  It receives the key,
+     *                     value, and any extra metadata included with
+     *                     the key inside of the braces.
+     * @return {String} the substituted string
+     */
+    substitute: function (s, o, f) {
+        var i, j, k, key, v, meta, l=YAHOO.lang, saved=[], token, 
+            DUMP='dump', SPACE=' ', LBRACE='{', RBRACE='}';
+
+
+        for (;;) {
+            i = s.lastIndexOf(LBRACE);
+            if (i < 0) {
+                break;
+            }
+            j = s.indexOf(RBRACE, i);
+            if (i + 1 >= j) {
+                break;
+            }
+
+            //Extract key and meta info 
+            token = s.substring(i + 1, j);
+            key = token;
+            meta = null;
+            k = key.indexOf(SPACE);
+            if (k > -1) {
+                meta = key.substring(k + 1);
+                key = key.substring(0, k);
+            }
+
+            // lookup the value
+            v = o[key];
+
+            // if a substitution function was provided, execute it
+            if (f) {
+                v = f(key, v, meta);
+            }
+
+            if (l.isObject(v)) {
+                if (l.isArray(v)) {
+                    v = l.dump(v, parseInt(meta, 10));
+                } else {
+                    meta = meta || "";
+
+                    // look for the keyword 'dump', if found force obj dump
+                    var dump = meta.indexOf(DUMP);
+                    if (dump > -1) {
+                        meta = meta.substring(4);
+                    }
+
+                    // use the toString if it is not the Object toString 
+                    // and the 'dump' meta info was not found
+                    if (v.toString===Object.prototype.toString||dump>-1) {
+                        v = l.dump(v, parseInt(meta, 10));
+                    } else {
+                        v = v.toString();
+                    }
+                }
+            } else if (!l.isString(v) && !l.isNumber(v)) {
+                // This {block} has no replace string. Save it for later.
+                v = "~-" + saved.length + "-~";
+                saved[saved.length] = token;
+
+                // break;
+            }
+
+            s = s.substring(0, i) + v + s.substring(j + 1);
+
+
+        }
+
+        // restore saved {block}s
+        for (i=saved.length-1; i>=0; i=i-1) {
+            s = s.replace(new RegExp("~-" + i + "-~"), "{"  + saved[i] + "}", "g");
+        }
+
+        return s;
+    },
+
+
+    /**
+     * Returns a string without any leading or trailing whitespace.  If 
+     * the input is not a string, the input will be returned untouched.
+     * @method trim
+     * @since 2.3.0
+     * @param s {string} the string to trim
+     * @return {string} the trimmed string
+     */
+    trim: function(s){
+        try {
+            return s.replace(/^\s+|\s+$/g, "");
+        } catch(e) {
+            return s;
+        }
+    },
+
+    /**
+     * Returns a new object containing all of the properties of
+     * all the supplied objects.  The properties from later objects
+     * will overwrite those in earlier objects.
+     * @method merge
+     * @since 2.3.0
+     * @param arguments {Object*} the objects to merge
+     * @return the new merged object
+     */
+    merge: function() {
+        var o={}, a=arguments;
+        for (var i=0, l=a.length; i<l; i=i+1) {
+            YAHOO.lang.augmentObject(o, a[i], true);
+        }
+        return o;
+    },
+
+    /**
+     * Executes the supplied function in the scope of the supplied 
+     * object 'when' milliseconds later.  Executes the function a 
+     * single time unless periodic is set to true.
+     * @method later
+     * @since 2.4.0
+     * @param when {int} the number of milliseconds to wait until the fn 
+     * is executed
+     * @param o the scope object
+     * @param fn {Function|String} the function to execute or the name of 
+     * the method in the 'o' object to execute
+     * @param data [Array] data that is provided to the function.  This accepts
+     * either a single item or an array.  If an array is provided, the
+     * function is executed with one parameter for each array item.  If
+     * you need to pass a single array parameter, it needs to be wrapped in
+     * an array [myarray]
+     * @param periodic {boolean} if true, executes continuously at supplied 
+     * interval until canceled
+     * @return a timer object. Call the cancel() method on this object to 
+     * stop the timer.
+     */
+    later: function(when, o, fn, data, periodic) {
+        when = when || 0; 
+        o = o || {};
+        var m=fn, d=data, f, r;
+
+        if (YAHOO.lang.isString(fn)) {
+            m = o[fn];
+        }
+
+        if (!m) {
+            throw new TypeError("method undefined");
+        }
+
+        if (!YAHOO.lang.isArray(d)) {
+            d = [data];
+        }
+
+        f = function() {
+            m.apply(o, d);
+        };
+
+        r = (periodic) ? setInterval(f, when) : setTimeout(f, when);
+
+        return {
+            interval: periodic,
+            cancel: function() {
+                if (this.interval) {
+                    clearInterval(r);
+                } else {
+                    clearTimeout(r);
+                }
+            }
+        };
+    },
+
+    /**
+     * A convenience method for detecting a legitimate non-null value.
+     * Returns false for null/undefined/NaN, true for other values, 
+     * including 0/false/''
+     * @method isValue
+     * @since 2.3.0
+     * @param o {any} the item to test
+     * @return {boolean} true if it is not null/undefined/NaN || false
+     */
+    isValue: function(o) {
+        // return (o || o === false || o === 0 || o === ''); // Infinity fails
+        var l = YAHOO.lang;
+return (l.isObject(o) || l.isString(o) || l.isNumber(o) || l.isBoolean(o));
+    }
+
+};
+
+/*
+ * An alias for <a href="YAHOO.lang.html">YAHOO.lang</a>
+ * @class YAHOO.util.Lang
+ */
+YAHOO.util.Lang = YAHOO.lang;
+ 
+/**
+ * Same as YAHOO.lang.augmentObject, except it only applies prototype 
+ * properties.  This is an alias for augmentProto.
+ * @see YAHOO.lang.augmentObject
+ * @method augment
+ * @static
+ * @param {Function} r  the object to receive the augmentation
+ * @param {Function} s  the object that supplies the properties to augment
+ * @param {String*|boolean}  arguments zero or more properties methods to 
+ *        augment the receiver with.  If none specified, everything
+ *        in the supplier will be used unless it would
+ *        overwrite an existing property in the receiver.  if true
+ *        is specified as the third parameter, all properties will
+ *        be applied and will overwrite an existing property in
+ *        the receiver
+ */
+YAHOO.lang.augment = YAHOO.lang.augmentProto;
+
+/**
+ * An alias for <a href="YAHOO.lang.html#augment">YAHOO.lang.augment</a>
+ * @for YAHOO
+ * @method augment
+ * @static
+ * @param {Function} r  the object to receive the augmentation
+ * @param {Function} s  the object that supplies the properties to augment
+ * @param {String*}  arguments zero or more properties methods to 
+ *        augment the receiver with.  If none specified, everything
+ *        in the supplier will be used unless it would
+ *        overwrite an existing property in the receiver
+ */
+YAHOO.augment = YAHOO.lang.augmentProto;
+       
+/**
+ * An alias for <a href="YAHOO.lang.html#extend">YAHOO.lang.extend</a>
+ * @method extend
+ * @static
+ * @param {Function} subc   the object to modify
+ * @param {Function} superc the object to inherit
+ * @param {Object} overrides  additional properties/methods to add to the
+ *        subclass prototype.  These will override the
+ *        matching items obtained from the superclass if present.
+ */
+YAHOO.extend = YAHOO.lang.extend;
+
+YAHOO.register("yahoo", YAHOO, {version: "2.4.1", build: "742"});
Index: trunk/wb/include/yui/License.txt
===================================================================
--- trunk/wb/include/yui/License.txt	(nonexistent)
+++ trunk/wb/include/yui/License.txt	(revision 601)
@@ -0,0 +1,22 @@
+The actual License of Yahoo YUI
+are here :
+http://developer.yahoo.com/yui/license.html
+
+Software License Agreement (BSD License)
+Copyright (c) 2008, Yahoo! Inc.
+All rights reserved.
+Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the
+following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of Yahoo! Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of Yahoo! Inc.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Sources of Intellectual Property Included in the YUI Library
+Where not otherwise indicated, all YUI content is authored by Yahoo! engineers and consists of Yahoo!-owned intellectual property. YUI is issued by Yahoo! under the BSD license above. In some specific instances, YUI will incorporate work done by developers outside of Yahoo! with their express permission. In these instances, copyright is retained by the author; however, in the context of the YUI library it is issued under the BSD license.
+
+Below is the explicit list of external (non-Yahoo) sources of intellectual property in YUI:
+
+Douglas Crockford's JSON parsing and stringifying methods: In the JSON Utility, Douglas Crockford's JSON parsing and stringifying methods are adapted from work published at JSON.org. The adapted work is in the public domain.
+Robert Penner's animation-easing algorithms: In the Animation Utility, YUI makes use of Robert Penner's algorithms for easing.
Index: trunk/wb/include/yui/event/event-debug.js
===================================================================
--- trunk/wb/include/yui/event/event-debug.js	(nonexistent)
+++ trunk/wb/include/yui/event/event-debug.js	(revision 601)
@@ -0,0 +1,2525 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+
+/**
+ * The CustomEvent class lets you define events for your application
+ * that can be subscribed to by one or more independent component.
+ *
+ * @param {String}  type The type of event, which is passed to the callback
+ *                  when the event fires
+ * @param {Object}  oScope The context the event will fire from.  "this" will
+ *                  refer to this object in the callback.  Default value: 
+ *                  the window object.  The listener can override this.
+ * @param {boolean} silent pass true to prevent the event from writing to
+ *                  the debugsystem
+ * @param {int}     signature the signature that the custom event subscriber
+ *                  will receive. YAHOO.util.CustomEvent.LIST or 
+ *                  YAHOO.util.CustomEvent.FLAT.  The default is
+ *                  YAHOO.util.CustomEvent.LIST.
+ * @namespace YAHOO.util
+ * @class CustomEvent
+ * @constructor
+ */
+YAHOO.util.CustomEvent = function(type, oScope, silent, signature) {
+
+    /**
+     * The type of event, returned to subscribers when the event fires
+     * @property type
+     * @type string
+     */
+    this.type = type;
+
+    /**
+     * The scope the the event will fire from by default.  Defaults to the window 
+     * obj
+     * @property scope
+     * @type object
+     */
+    this.scope = oScope || window;
+
+    /**
+     * By default all custom events are logged in the debug build, set silent
+     * to true to disable debug outpu for this event.
+     * @property silent
+     * @type boolean
+     */
+    this.silent = silent;
+
+    /**
+     * Custom events support two styles of arguments provided to the event
+     * subscribers.  
+     * <ul>
+     * <li>YAHOO.util.CustomEvent.LIST: 
+     *   <ul>
+     *   <li>param1: event name</li>
+     *   <li>param2: array of arguments sent to fire</li>
+     *   <li>param3: <optional> a custom object supplied by the subscriber</li>
+     *   </ul>
+     * </li>
+     * <li>YAHOO.util.CustomEvent.FLAT
+     *   <ul>
+     *   <li>param1: the first argument passed to fire.  If you need to
+     *           pass multiple parameters, use and array or object literal</li>
+     *   <li>param2: <optional> a custom object supplied by the subscriber</li>
+     *   </ul>
+     * </li>
+     * </ul>
+     *   @property signature
+     *   @type int
+     */
+    this.signature = signature || YAHOO.util.CustomEvent.LIST;
+
+    /**
+     * The subscribers to this event
+     * @property subscribers
+     * @type Subscriber[]
+     */
+    this.subscribers = [];
+
+    if (!this.silent) {
+        YAHOO.log( "Creating " + this, "info", "Event" );
+    }
+
+    var onsubscribeType = "_YUICEOnSubscribe";
+
+    // Only add subscribe events for events that are not generated by 
+    // CustomEvent
+    if (type !== onsubscribeType) {
+
+        /**
+         * Custom events provide a custom event that fires whenever there is
+         * a new subscriber to the event.  This provides an opportunity to
+         * handle the case where there is a non-repeating event that has
+         * already fired has a new subscriber.  
+         *
+         * @event subscribeEvent
+         * @type YAHOO.util.CustomEvent
+         * @param {Function} fn The function to execute
+         * @param {Object}   obj An object to be passed along when the event 
+         *                       fires
+         * @param {boolean|Object}  override If true, the obj passed in becomes 
+         *                                   the execution scope of the listener.
+         *                                   if an object, that object becomes the
+         *                                   the execution scope.
+         */
+        this.subscribeEvent = 
+                new YAHOO.util.CustomEvent(onsubscribeType, this, true);
+
+    } 
+
+
+    /**
+     * In order to make it possible to execute the rest of the subscriber
+     * stack when one thows an exception, the subscribers exceptions are
+     * caught.  The most recent exception is stored in this property
+     * @property lastError
+     * @type Error
+     */
+    this.lastError = null;
+};
+
+/**
+ * Subscriber listener sigature constant.  The LIST type returns three
+ * parameters: the event type, the array of args passed to fire, and
+ * the optional custom object
+ * @property YAHOO.util.CustomEvent.LIST
+ * @static
+ * @type int
+ */
+YAHOO.util.CustomEvent.LIST = 0;
+
+/**
+ * Subscriber listener sigature constant.  The FLAT type returns two
+ * parameters: the first argument passed to fire and the optional 
+ * custom object
+ * @property YAHOO.util.CustomEvent.FLAT
+ * @static
+ * @type int
+ */
+YAHOO.util.CustomEvent.FLAT = 1;
+
+YAHOO.util.CustomEvent.prototype = {
+
+    /**
+     * Subscribes the caller to this event
+     * @method subscribe
+     * @param {Function} fn        The function to execute
+     * @param {Object}   obj       An object to be passed along when the event 
+     *                             fires
+     * @param {boolean|Object}  override If true, the obj passed in becomes 
+     *                                   the execution scope of the listener.
+     *                                   if an object, that object becomes the
+     *                                   the execution scope.
+     */
+    subscribe: function(fn, obj, override) {
+
+        if (!fn) {
+throw new Error("Invalid callback for subscriber to '" + this.type + "'");
+        }
+
+        if (this.subscribeEvent) {
+            this.subscribeEvent.fire(fn, obj, override);
+        }
+
+        this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, override) );
+    },
+
+    /**
+     * Unsubscribes subscribers.
+     * @method unsubscribe
+     * @param {Function} fn  The subscribed function to remove, if not supplied
+     *                       all will be removed
+     * @param {Object}   obj  The custom object passed to subscribe.  This is
+     *                        optional, but if supplied will be used to
+     *                        disambiguate multiple listeners that are the same
+     *                        (e.g., you subscribe many object using a function
+     *                        that lives on the prototype)
+     * @return {boolean} True if the subscriber was found and detached.
+     */
+    unsubscribe: function(fn, obj) {
+
+        if (!fn) {
+            return this.unsubscribeAll();
+        }
+
+        var found = false;
+        for (var i=0, len=this.subscribers.length; i<len; ++i) {
+            var s = this.subscribers[i];
+            if (s && s.contains(fn, obj)) {
+                this._delete(i);
+                found = true;
+            }
+        }
+
+        return found;
+    },
+
+    /**
+     * Notifies the subscribers.  The callback functions will be executed
+     * from the scope specified when the event was created, and with the 
+     * following parameters:
+     *   <ul>
+     *   <li>The type of event</li>
+     *   <li>All of the arguments fire() was executed with as an array</li>
+     *   <li>The custom object (if any) that was passed into the subscribe() 
+     *       method</li>
+     *   </ul>
+     * @method fire 
+     * @param {Object*} arguments an arbitrary set of parameters to pass to 
+     *                            the handler.
+     * @return {boolean} false if one of the subscribers returned false, 
+     *                   true otherwise
+     */
+    fire: function() {
+        var len=this.subscribers.length;
+        if (!len && this.silent) {
+            return true;
+        }
+
+        var args=[], ret=true, i, rebuild=false;
+
+        for (i=0; i<arguments.length; ++i) {
+            args.push(arguments[i]);
+        }
+
+        if (!this.silent) {
+            YAHOO.log( "Firing "       + this  + ", " + 
+                       "args: "        + args  + ", " +
+                       "subscribers: " + len,                 
+                       "info", "Event"                  );
+        }
+
+        for (i=0; i<len; ++i) {
+            var s = this.subscribers[i];
+            if (!s) {
+                rebuild=true;
+            } else {
+                if (!this.silent) {
+                    YAHOO.log( this.type + "->" + (i+1) + ": " +  s, 
+                                "info", "Event" );
+                }
+
+                var scope = s.getScope(this.scope);
+
+                if (this.signature == YAHOO.util.CustomEvent.FLAT) {
+                    var param = null;
+                    if (args.length > 0) {
+                        param = args[0];
+                    }
+
+                    try {
+                        ret = s.fn.call(scope, param, s.obj);
+                    } catch(e) {
+                        this.lastError = e;
+                        YAHOO.log(this + " subscriber exception: " + e,
+                                  "error", "Event");
+                    }
+                } else {
+                    try {
+                        ret = s.fn.call(scope, this.type, args, s.obj);
+                    } catch(ex) {
+                        this.lastError = ex;
+                        YAHOO.log(this + " subscriber exception: " + ex,
+                                  "error", "Event");
+                    }
+                }
+                if (false === ret) {
+                    if (!this.silent) {
+                        YAHOO.log("Event cancelled, subscriber " + i + 
+                                  " of " + len, "info", "Event");
+                    }
+
+                    //break;
+                    return false;
+                }
+            }
+        }
+
+        if (rebuild) {
+            var newlist=[],subs=this.subscribers;
+            for (i=0,len=subs.length; i<len; i=i+1) {
+                newlist.push(subs[i]);
+            }
+
+            this.subscribers=newlist;
+        }
+
+        return true;
+    },
+
+    /**
+     * Removes all listeners
+     * @method unsubscribeAll
+     * @return {int} The number of listeners unsubscribed
+     */
+    unsubscribeAll: function() {
+        for (var i=0, len=this.subscribers.length; i<len; ++i) {
+            this._delete(len - 1 - i);
+        }
+
+        this.subscribers=[];
+
+        return i;
+    },
+
+    /**
+     * @method _delete
+     * @private
+     */
+    _delete: function(index) {
+        var s = this.subscribers[index];
+        if (s) {
+            delete s.fn;
+            delete s.obj;
+        }
+
+        this.subscribers[index]=null;
+    },
+
+    /**
+     * @method toString
+     */
+    toString: function() {
+         return "CustomEvent: " + "'" + this.type  + "', " + 
+             "scope: " + this.scope;
+
+    }
+};
+
+/////////////////////////////////////////////////////////////////////
+
+/**
+ * Stores the subscriber information to be used when the event fires.
+ * @param {Function} fn       The function to execute
+ * @param {Object}   obj      An object to be passed along when the event fires
+ * @param {boolean}  override If true, the obj passed in becomes the execution
+ *                            scope of the listener
+ * @class Subscriber
+ * @constructor
+ */
+YAHOO.util.Subscriber = function(fn, obj, override) {
+
+    /**
+     * The callback that will be execute when the event fires
+     * @property fn
+     * @type function
+     */
+    this.fn = fn;
+
+    /**
+     * An optional custom object that will passed to the callback when
+     * the event fires
+     * @property obj
+     * @type object
+     */
+    this.obj = YAHOO.lang.isUndefined(obj) ? null : obj;
+
+    /**
+     * The default execution scope for the event listener is defined when the
+     * event is created (usually the object which contains the event).
+     * By setting override to true, the execution scope becomes the custom
+     * object passed in by the subscriber.  If override is an object, that 
+     * object becomes the scope.
+     * @property override
+     * @type boolean|object
+     */
+    this.override = override;
+
+};
+
+/**
+ * Returns the execution scope for this listener.  If override was set to true
+ * the custom obj will be the scope.  If override is an object, that is the
+ * scope, otherwise the default scope will be used.
+ * @method getScope
+ * @param {Object} defaultScope the scope to use if this listener does not
+ *                              override it.
+ */
+YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) {
+    if (this.override) {
+        if (this.override === true) {
+            return this.obj;
+        } else {
+            return this.override;
+        }
+    }
+    return defaultScope;
+};
+
+/**
+ * Returns true if the fn and obj match this objects properties.
+ * Used by the unsubscribe method to match the right subscriber.
+ *
+ * @method contains
+ * @param {Function} fn the function to execute
+ * @param {Object} obj an object to be passed along when the event fires
+ * @return {boolean} true if the supplied arguments match this 
+ *                   subscriber's signature.
+ */
+YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
+    if (obj) {
+        return (this.fn == fn && this.obj == obj);
+    } else {
+        return (this.fn == fn);
+    }
+};
+
+/**
+ * @method toString
+ */
+YAHOO.util.Subscriber.prototype.toString = function() {
+    return "Subscriber { obj: " + this.obj  + 
+           ", override: " +  (this.override || "no") + " }";
+};
+
+/**
+ * The Event Utility provides utilities for managing DOM Events and tools
+ * for building event systems
+ *
+ * @module event
+ * @title Event Utility
+ * @namespace YAHOO.util
+ * @requires yahoo
+ */
+
+// The first instance of Event will win if it is loaded more than once.
+// @TODO this needs to be changed so that only the state data that needs to
+// be preserved is kept, while methods are overwritten/added as needed.
+// This means that the module pattern can't be used.
+if (!YAHOO.util.Event) {
+
+/**
+ * The event utility provides functions to add and remove event listeners,
+ * event cleansing.  It also tries to automatically remove listeners it
+ * registers during the unload event.
+ *
+ * @class Event
+ * @static
+ */
+    YAHOO.util.Event = function() {
+
+        /**
+         * True after the onload event has fired
+         * @property loadComplete
+         * @type boolean
+         * @static
+         * @private
+         */
+        var loadComplete =  false;
+
+        /**
+         * Cache of wrapped listeners
+         * @property listeners
+         * @type array
+         * @static
+         * @private
+         */
+        var listeners = [];
+
+        /**
+         * User-defined unload function that will be fired before all events
+         * are detached
+         * @property unloadListeners
+         * @type array
+         * @static
+         * @private
+         */
+        var unloadListeners = [];
+
+        /**
+         * Cache of DOM0 event handlers to work around issues with DOM2 events
+         * in Safari
+         * @property legacyEvents
+         * @static
+         * @private
+         */
+        var legacyEvents = [];
+
+        /**
+         * Listener stack for DOM0 events
+         * @property legacyHandlers
+         * @static
+         * @private
+         */
+        var legacyHandlers = [];
+
+        /**
+         * The number of times to poll after window.onload.  This number is
+         * increased if additional late-bound handlers are requested after
+         * the page load.
+         * @property retryCount
+         * @static
+         * @private
+         */
+        var retryCount = 0;
+
+        /**
+         * onAvailable listeners
+         * @property onAvailStack
+         * @static
+         * @private
+         */
+        var onAvailStack = [];
+
+        /**
+         * Lookup table for legacy events
+         * @property legacyMap
+         * @static
+         * @private
+         */
+        var legacyMap = [];
+
+        /**
+         * Counter for auto id generation
+         * @property counter
+         * @static
+         * @private
+         */
+        var counter = 0;
+        
+        /**
+         * Normalized keycodes for webkit/safari
+         * @property webkitKeymap
+         * @type {int: int}
+         * @private
+         * @static
+         * @final
+         */
+        var webkitKeymap = {
+            63232: 38, // up
+            63233: 40, // down
+            63234: 37, // left
+            63235: 39, // right
+            63276: 33, // page up
+            63277: 34, // page down
+            25: 9      // SHIFT-TAB (Safari provides a different key code in
+                       // this case, even though the shiftKey modifier is set)
+        };
+
+        return {
+
+            /**
+             * The number of times we should look for elements that are not
+             * in the DOM at the time the event is requested after the document
+             * has been loaded.  The default is 4000@amp;10 ms, so it will poll
+             * for 40 seconds or until all outstanding handlers are bound
+             * (whichever comes first).
+             * @property POLL_RETRYS
+             * @type int
+             * @static
+             * @final
+             */
+            POLL_RETRYS: 4000,
+
+            /**
+             * The poll interval in milliseconds
+             * @property POLL_INTERVAL
+             * @type int
+             * @static
+             * @final
+             */
+            POLL_INTERVAL: 10,
+
+            /**
+             * Element to bind, int constant
+             * @property EL
+             * @type int
+             * @static
+             * @final
+             */
+            EL: 0,
+
+            /**
+             * Type of event, int constant
+             * @property TYPE
+             * @type int
+             * @static
+             * @final
+             */
+            TYPE: 1,
+
+            /**
+             * Function to execute, int constant
+             * @property FN
+             * @type int
+             * @static
+             * @final
+             */
+            FN: 2,
+
+            /**
+             * Function wrapped for scope correction and cleanup, int constant
+             * @property WFN
+             * @type int
+             * @static
+             * @final
+             */
+            WFN: 3,
+
+            /**
+             * Object passed in by the user that will be returned as a 
+             * parameter to the callback, int constant.  Specific to
+             * unload listeners
+             * @property OBJ
+             * @type int
+             * @static
+             * @final
+             */
+            UNLOAD_OBJ: 3,
+
+            /**
+             * Adjusted scope, either the element we are registering the event
+             * on or the custom object passed in by the listener, int constant
+             * @property ADJ_SCOPE
+             * @type int
+             * @static
+             * @final
+             */
+            ADJ_SCOPE: 4,
+
+            /**
+             * The original obj passed into addListener
+             * @property OBJ
+             * @type int
+             * @static
+             * @final
+             */
+            OBJ: 5,
+
+            /**
+             * The original scope parameter passed into addListener
+             * @property OVERRIDE
+             * @type int
+             * @static
+             * @final
+             */
+            OVERRIDE: 6,
+
+            /**
+             * addListener/removeListener can throw errors in unexpected scenarios.
+             * These errors are suppressed, the method returns false, and this property
+             * is set
+             * @property lastError
+             * @static
+             * @type Error
+             */
+            lastError: null,
+
+            /**
+             * Safari detection
+             * @property isSafari
+             * @private
+             * @static
+             * @deprecated use YAHOO.env.ua.webkit
+             */
+            isSafari: YAHOO.env.ua.webkit,
+            
+            /**
+             * webkit version
+             * @property webkit
+             * @type string
+             * @private
+             * @static
+             * @deprecated use YAHOO.env.ua.webkit
+             */
+            webkit: YAHOO.env.ua.webkit,
+            
+            /**
+             * IE detection 
+             * @property isIE
+             * @private
+             * @static
+             * @deprecated use YAHOO.env.ua.ie
+             */
+            isIE: YAHOO.env.ua.ie,
+
+            /**
+             * poll handle
+             * @property _interval
+             * @static
+             * @private
+             */
+            _interval: null,
+
+            /**
+             * document readystate poll handle
+             * @property _dri
+             * @static
+             * @private
+             */
+             _dri: null,
+
+            /**
+             * True when the document is initially usable
+             * @property DOMReady
+             * @type boolean
+             * @static
+             */
+            DOMReady: false,
+
+            /**
+             * @method startInterval
+             * @static
+             * @private
+             */
+            startInterval: function() {
+                if (!this._interval) {
+                    var self = this;
+                    var callback = function() { self._tryPreloadAttach(); };
+                    this._interval = setInterval(callback, this.POLL_INTERVAL);
+                }
+            },
+
+            /**
+             * Executes the supplied callback when the item with the supplied
+             * id is found.  This is meant to be used to execute behavior as
+             * soon as possible as the page loads.  If you use this after the
+             * initial page load it will poll for a fixed time for the element.
+             * The number of times it will poll and the frequency are
+             * configurable.  By default it will poll for 10 seconds.
+             *
+             * <p>The callback is executed with a single parameter:
+             * the custom object parameter, if provided.</p>
+             *
+             * @method onAvailable
+             *
+             * @param {string||string[]}   p_id the id of the element, or an array
+             * of ids to look for.
+             * @param {function} p_fn what to execute when the element is found.
+             * @param {object}   p_obj an optional object to be passed back as
+             *                   a parameter to p_fn.
+             * @param {boolean|object}  p_override If set to true, p_fn will execute
+             *                   in the scope of p_obj, if set to an object it
+             *                   will execute in the scope of that object
+             * @param checkContent {boolean} check child node readiness (onContentReady)
+             * @static
+             */
+            onAvailable: function(p_id, p_fn, p_obj, p_override, checkContent) {
+
+                var a = (YAHOO.lang.isString(p_id)) ? [p_id] : p_id;
+
+                for (var i=0; i<a.length; i=i+1) {
+                    onAvailStack.push({id:         a[i], 
+                                       fn:         p_fn, 
+                                       obj:        p_obj, 
+                                       override:   p_override, 
+                                       checkReady: checkContent });
+                }
+                retryCount = this.POLL_RETRYS;
+                this.startInterval();
+            },
+
+            /**
+             * Works the same way as onAvailable, but additionally checks the
+             * state of sibling elements to determine if the content of the
+             * available element is safe to modify.
+             *
+             * <p>The callback is executed with a single parameter:
+             * the custom object parameter, if provided.</p>
+             *
+             * @method onContentReady
+             *
+             * @param {string}   p_id the id of the element to look for.
+             * @param {function} p_fn what to execute when the element is ready.
+             * @param {object}   p_obj an optional object to be passed back as
+             *                   a parameter to p_fn.
+             * @param {boolean|object}  p_override If set to true, p_fn will execute
+             *                   in the scope of p_obj.  If an object, p_fn will
+             *                   exectute in the scope of that object
+             *
+             * @static
+             */
+            onContentReady: function(p_id, p_fn, p_obj, p_override) {
+                this.onAvailable(p_id, p_fn, p_obj, p_override, true);
+            },
+
+            /**
+             * Executes the supplied callback when the DOM is first usable.  This
+             * will execute immediately if called after the DOMReady event has
+             * fired.   @todo the DOMContentReady event does not fire when the
+             * script is dynamically injected into the page.  This means the
+             * DOMReady custom event will never fire in FireFox or Opera when the
+             * library is injected.  It _will_ fire in Safari, and the IE 
+             * implementation would allow for us to fire it if the defered script
+             * is not available.  We want this to behave the same in all browsers.
+             * Is there a way to identify when the script has been injected 
+             * instead of included inline?  Is there a way to know whether the 
+             * window onload event has fired without having had a listener attached 
+             * to it when it did so?
+             *
+             * <p>The callback is a CustomEvent, so the signature is:</p>
+             * <p>type &lt;string&gt;, args &lt;array&gt;, customobject &lt;object&gt;</p>
+             * <p>For DOMReady events, there are no fire argments, so the
+             * signature is:</p>
+             * <p>"DOMReady", [], obj</p>
+             *
+             *
+             * @method onDOMReady
+             *
+             * @param {function} p_fn what to execute when the element is found.
+             * @param {object}   p_obj an optional object to be passed back as
+             *                   a parameter to p_fn.
+             * @param {boolean|object}  p_scope If set to true, p_fn will execute
+             *                   in the scope of p_obj, if set to an object it
+             *                   will execute in the scope of that object
+             *
+             * @static
+             */
+            onDOMReady: function(p_fn, p_obj, p_override) {
+                if (this.DOMReady) {
+                    setTimeout(function() {
+                        var s = window;
+                        if (p_override) {
+                            if (p_override === true) {
+                                s = p_obj;
+                            } else {
+                                s = p_override;
+                            }
+                        }
+                        p_fn.call(s, "DOMReady", [], p_obj);
+                    }, 0);
+                } else {
+                    this.DOMReadyEvent.subscribe(p_fn, p_obj, p_override);
+                }
+            },
+
+            /**
+             * Appends an event handler
+             *
+             * @method addListener
+             *
+             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
+             *  reference, or a collection of ids and/or elements to assign the 
+             *  listener to.
+             * @param {String}   sType     The type of event to append
+             * @param {Function} fn        The method the event invokes
+             * @param {Object}   obj    An arbitrary object that will be 
+             *                             passed as a parameter to the handler
+             * @param {Boolean|object}  override  If true, the obj passed in becomes
+             *                             the execution scope of the listener. If an
+             *                             object, this object becomes the execution
+             *                             scope.
+             * @return {Boolean} True if the action was successful or defered,
+             *                        false if one or more of the elements 
+             *                        could not have the listener attached,
+             *                        or if the operation throws an exception.
+             * @static
+             */
+            addListener: function(el, sType, fn, obj, override) {
+
+                if (!fn || !fn.call) {
+// throw new TypeError(sType + " addListener call failed, callback undefined");
+YAHOO.log(sType + " addListener call failed, invalid callback", "error", "Event");
+                    return false;
+                }
+
+                // The el argument can be an array of elements or element ids.
+                if ( this._isValidCollection(el)) {
+                    var ok = true;
+                    for (var i=0,len=el.length; i<len; ++i) {
+                        ok = this.on(el[i], 
+                                       sType, 
+                                       fn, 
+                                       obj, 
+                                       override) && ok;
+                    }
+                    return ok;
+
+                } else if (YAHOO.lang.isString(el)) {
+                    var oEl = this.getEl(el);
+                    // If the el argument is a string, we assume it is 
+                    // actually the id of the element.  If the page is loaded
+                    // we convert el to the actual element, otherwise we 
+                    // defer attaching the event until onload event fires
+
+                    // check to see if we need to delay hooking up the event 
+                    // until after the page loads.
+                    if (oEl) {
+                        el = oEl;
+                    } else {
+                        // defer adding the event until the element is available
+                        this.onAvailable(el, function() {
+                           YAHOO.util.Event.on(el, sType, fn, obj, override);
+                        });
+
+                        return true;
+                    }
+                }
+
+                // Element should be an html element or an array if we get 
+                // here.
+                if (!el) {
+                    // this.logger.debug("unable to attach event " + sType);
+                    return false;
+                }
+
+                // we need to make sure we fire registered unload events 
+                // prior to automatically unhooking them.  So we hang on to 
+                // these instead of attaching them to the window and fire the
+                // handles explicitly during our one unload event.
+                if ("unload" == sType && obj !== this) {
+                    unloadListeners[unloadListeners.length] =
+                            [el, sType, fn, obj, override];
+                    return true;
+                }
+
+                // this.logger.debug("Adding handler: " + el + ", " + sType);
+
+                // if the user chooses to override the scope, we use the custom
+                // object passed in, otherwise the executing scope will be the
+                // HTML element that the event is registered on
+                var scope = el;
+                if (override) {
+                    if (override === true) {
+                        scope = obj;
+                    } else {
+                        scope = override;
+                    }
+                }
+
+                // wrap the function so we can return the obj object when
+                // the event fires;
+                var wrappedFn = function(e) {
+                        return fn.call(scope, YAHOO.util.Event.getEvent(e, el), 
+                                obj);
+                    };
+
+                var li = [el, sType, fn, wrappedFn, scope, obj, override];
+                var index = listeners.length;
+                // cache the listener so we can try to automatically unload
+                listeners[index] = li;
+
+                if (this.useLegacyEvent(el, sType)) {
+                    var legacyIndex = this.getLegacyIndex(el, sType);
+
+                    // Add a new dom0 wrapper if one is not detected for this
+                    // element
+                    if ( legacyIndex == -1 || 
+                                el != legacyEvents[legacyIndex][0] ) {
+
+                        legacyIndex = legacyEvents.length;
+                        legacyMap[el.id + sType] = legacyIndex;
+
+                        // cache the signature for the DOM0 event, and 
+                        // include the existing handler for the event, if any
+                        legacyEvents[legacyIndex] = 
+                            [el, sType, el["on" + sType]];
+                        legacyHandlers[legacyIndex] = [];
+
+                        el["on" + sType] = 
+                            function(e) {
+                                YAHOO.util.Event.fireLegacyEvent(
+                                    YAHOO.util.Event.getEvent(e), legacyIndex);
+                            };
+                    }
+
+                    // add a reference to the wrapped listener to our custom
+                    // stack of events
+                    //legacyHandlers[legacyIndex].push(index);
+                    legacyHandlers[legacyIndex].push(li);
+
+                } else {
+                    try {
+                        this._simpleAdd(el, sType, wrappedFn, false);
+                    } catch(ex) {
+                        // handle an error trying to attach an event.  If it fails
+                        // we need to clean up the cache
+                        this.lastError = ex;
+                        this.removeListener(el, sType, fn);
+                        return false;
+                    }
+                }
+
+                return true;
+                
+            },
+
+            /**
+             * When using legacy events, the handler is routed to this object
+             * so we can fire our custom listener stack.
+             * @method fireLegacyEvent
+             * @static
+             * @private
+             */
+            fireLegacyEvent: function(e, legacyIndex) {
+                // this.logger.debug("fireLegacyEvent " + legacyIndex);
+                var ok=true,le,lh,li,scope,ret;
+                
+                lh = legacyHandlers[legacyIndex];
+                for (var i=0,len=lh.length; i<len; ++i) {
+                    li = lh[i];
+                    if ( li && li[this.WFN] ) {
+                        scope = li[this.ADJ_SCOPE];
+                        ret = li[this.WFN].call(scope, e);
+                        ok = (ok && ret);
+                    }
+                }
+
+                // Fire the original handler if we replaced one.  We fire this
+                // after the other events to keep stopPropagation/preventDefault
+                // that happened in the DOM0 handler from touching our DOM2
+                // substitute
+                le = legacyEvents[legacyIndex];
+                if (le && le[2]) {
+                    le[2](e);
+                }
+                
+                return ok;
+            },
+
+            /**
+             * Returns the legacy event index that matches the supplied 
+             * signature
+             * @method getLegacyIndex
+             * @static
+             * @private
+             */
+            getLegacyIndex: function(el, sType) {
+                var key = this.generateId(el) + sType;
+                if (typeof legacyMap[key] == "undefined") { 
+                    return -1;
+                } else {
+                    return legacyMap[key];
+                }
+            },
+
+            /**
+             * Logic that determines when we should automatically use legacy
+             * events instead of DOM2 events.  Currently this is limited to old
+             * Safari browsers with a broken preventDefault
+             * @method useLegacyEvent
+             * @static
+             * @private
+             */
+            useLegacyEvent: function(el, sType) {
+                if (this.webkit && ("click"==sType || "dblclick"==sType)) {
+                    var v = parseInt(this.webkit, 10);
+                    if (!isNaN(v) && v<418) {
+                        return true;
+                    }
+                }
+                return false;
+            },
+                    
+            /**
+             * Removes an event listener
+             *
+             * @method removeListener
+             *
+             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
+             *  reference, or a collection of ids and/or elements to remove
+             *  the listener from.
+             * @param {String} sType the type of event to remove.
+             * @param {Function} fn the method the event invokes.  If fn is
+             *  undefined, then all event handlers for the type of event are 
+             *  removed.
+             * @return {boolean} true if the unbind was successful, false 
+             *  otherwise.
+             * @static
+             */
+            removeListener: function(el, sType, fn) {
+                var i, len, li;
+
+                // The el argument can be a string
+                if (typeof el == "string") {
+                    el = this.getEl(el);
+                // The el argument can be an array of elements or element ids.
+                } else if ( this._isValidCollection(el)) {
+                    var ok = true;
+                    for (i=0,len=el.length; i<len; ++i) {
+                        ok = ( this.removeListener(el[i], sType, fn) && ok );
+                    }
+                    return ok;
+                }
+
+                if (!fn || !fn.call) {
+                    // this.logger.debug("Error, function is not valid " + fn);
+                    //return false;
+                    return this.purgeElement(el, false, sType);
+                }
+
+                if ("unload" == sType) {
+
+                    for (i=0, len=unloadListeners.length; i<len; i++) {
+                        li = unloadListeners[i];
+                        if (li && 
+                            li[0] == el && 
+                            li[1] == sType && 
+                            li[2] == fn) {
+                                //unloadListeners.splice(i, 1);
+                                unloadListeners[i]=null;
+                                return true;
+                        }
+                    }
+
+                    return false;
+                }
+
+                var cacheItem = null;
+
+                // The index is a hidden parameter; needed to remove it from
+                // the method signature because it was tempting users to
+                // try and take advantage of it, which is not possible.
+                var index = arguments[3];
+  
+                if ("undefined" === typeof index) {
+                    index = this._getCacheIndex(el, sType, fn);
+                }
+
+                if (index >= 0) {
+                    cacheItem = listeners[index];
+                }
+
+                if (!el || !cacheItem) {
+                    // this.logger.debug("cached listener not found");
+                    return false;
+                }
+
+                // this.logger.debug("Removing handler: " + el + ", " + sType);
+
+                if (this.useLegacyEvent(el, sType)) {
+                    var legacyIndex = this.getLegacyIndex(el, sType);
+                    var llist = legacyHandlers[legacyIndex];
+                    if (llist) {
+                        for (i=0, len=llist.length; i<len; ++i) {
+                            li = llist[i];
+                            if (li && 
+                                li[this.EL] == el && 
+                                li[this.TYPE] == sType && 
+                                li[this.FN] == fn) {
+                                    //llist.splice(i, 1);
+                                    llist[i]=null;
+                                    break;
+                            }
+                        }
+                    }
+
+                } else {
+                    try {
+                        this._simpleRemove(el, sType, cacheItem[this.WFN], false);
+                    } catch(ex) {
+                        this.lastError = ex;
+                        return false;
+                    }
+                }
+
+                // removed the wrapped handler
+                delete listeners[index][this.WFN];
+                delete listeners[index][this.FN];
+                //listeners.splice(index, 1);
+                listeners[index]=null;
+
+                return true;
+
+            },
+
+            /**
+             * Returns the event's target element.  Safari sometimes provides
+             * a text node, and this is automatically resolved to the text
+             * node's parent so that it behaves like other browsers.
+             * @method getTarget
+             * @param {Event} ev the event
+             * @param {boolean} resolveTextNode when set to true the target's
+             *                  parent will be returned if the target is a 
+             *                  text node.  @deprecated, the text node is
+             *                  now resolved automatically
+             * @return {HTMLElement} the event's target
+             * @static
+             */
+            getTarget: function(ev, resolveTextNode) {
+                var t = ev.target || ev.srcElement;
+                return this.resolveTextNode(t);
+            },
+
+            /**
+             * In some cases, some browsers will return a text node inside
+             * the actual element that was targeted.  This normalizes the
+             * return value for getTarget and getRelatedTarget.
+             * @method resolveTextNode
+             * @param {HTMLElement} node node to resolve
+             * @return {HTMLElement} the normized node
+             * @static
+             */
+            resolveTextNode: function(node) {
+                if (node && 3 == node.nodeType) {
+                    return node.parentNode;
+                } else {
+                    return node;
+                }
+            },
+
+            /**
+             * Returns the event's pageX
+             * @method getPageX
+             * @param {Event} ev the event
+             * @return {int} the event's pageX
+             * @static
+             */
+            getPageX: function(ev) {
+                var x = ev.pageX;
+                if (!x && 0 !== x) {
+                    x = ev.clientX || 0;
+
+                    if ( this.isIE ) {
+                        x += this._getScrollLeft();
+                    }
+                }
+
+                return x;
+            },
+
+            /**
+             * Returns the event's pageY
+             * @method getPageY
+             * @param {Event} ev the event
+             * @return {int} the event's pageY
+             * @static
+             */
+            getPageY: function(ev) {
+                var y = ev.pageY;
+                if (!y && 0 !== y) {
+                    y = ev.clientY || 0;
+
+                    if ( this.isIE ) {
+                        y += this._getScrollTop();
+                    }
+                }
+
+
+                return y;
+            },
+
+            /**
+             * Returns the pageX and pageY properties as an indexed array.
+             * @method getXY
+             * @param {Event} ev the event
+             * @return {[x, y]} the pageX and pageY properties of the event
+             * @static
+             */
+            getXY: function(ev) {
+                return [this.getPageX(ev), this.getPageY(ev)];
+            },
+
+            /**
+             * Returns the event's related target 
+             * @method getRelatedTarget
+             * @param {Event} ev the event
+             * @return {HTMLElement} the event's relatedTarget
+             * @static
+             */
+            getRelatedTarget: function(ev) {
+                var t = ev.relatedTarget;
+                if (!t) {
+                    if (ev.type == "mouseout") {
+                        t = ev.toElement;
+                    } else if (ev.type == "mouseover") {
+                        t = ev.fromElement;
+                    }
+                }
+
+                return this.resolveTextNode(t);
+            },
+
+            /**
+             * Returns the time of the event.  If the time is not included, the
+             * event is modified using the current time.
+             * @method getTime
+             * @param {Event} ev the event
+             * @return {Date} the time of the event
+             * @static
+             */
+            getTime: function(ev) {
+                if (!ev.time) {
+                    var t = new Date().getTime();
+                    try {
+                        ev.time = t;
+                    } catch(ex) { 
+                        this.lastError = ex;
+                        return t;
+                    }
+                }
+
+                return ev.time;
+            },
+
+            /**
+             * Convenience method for stopPropagation + preventDefault
+             * @method stopEvent
+             * @param {Event} ev the event
+             * @static
+             */
+            stopEvent: function(ev) {
+                this.stopPropagation(ev);
+                this.preventDefault(ev);
+            },
+
+            /**
+             * Stops event propagation
+             * @method stopPropagation
+             * @param {Event} ev the event
+             * @static
+             */
+            stopPropagation: function(ev) {
+                if (ev.stopPropagation) {
+                    ev.stopPropagation();
+                } else {
+                    ev.cancelBubble = true;
+                }
+            },
+
+            /**
+             * Prevents the default behavior of the event
+             * @method preventDefault
+             * @param {Event} ev the event
+             * @static
+             */
+            preventDefault: function(ev) {
+                if (ev.preventDefault) {
+                    ev.preventDefault();
+                } else {
+                    ev.returnValue = false;
+                }
+            },
+             
+            /**
+             * Finds the event in the window object, the caller's arguments, or
+             * in the arguments of another method in the callstack.  This is
+             * executed automatically for events registered through the event
+             * manager, so the implementer should not normally need to execute
+             * this function at all.
+             * @method getEvent
+             * @param {Event} e the event parameter from the handler
+             * @param {HTMLElement} boundEl the element the listener is attached to
+             * @return {Event} the event 
+             * @static
+             */
+            getEvent: function(e, boundEl) {
+                var ev = e || window.event;
+
+                if (!ev) {
+                    var c = this.getEvent.caller;
+                    while (c) {
+                        ev = c.arguments[0];
+                        if (ev && Event == ev.constructor) {
+                            break;
+                        }
+                        c = c.caller;
+                    }
+                }
+
+                // IE events that target non-browser objects (e.g., VML
+                // canvas) will sometimes throw errors when you try to
+                // inspect the properties of the event target.  We try to
+                // detect this condition, and provide a dummy target (the bound
+                // element) to eliminate spurious errors.  
+
+                // the implementation caused unexpected results in some 
+                // implementations, so this has been rolled back for now
+                /* 
+                if (ev && this.isIE) {
+
+                    try {
+
+                        var el = ev.srcElement;
+
+                    } catch(ex) {
+
+                        YAHOO.log("Inspecting the target caused an error, " +
+                            "setting the target to the bound element.", "warn");
+                         
+                        ev.target = boundEl;
+                    }
+
+                }
+                */
+
+                return ev;
+            },
+
+            /**
+             * Returns the charcode for an event
+             * @method getCharCode
+             * @param {Event} ev the event
+             * @return {int} the event's charCode
+             * @static
+             */
+            getCharCode: function(ev) {
+                var code = ev.keyCode || ev.charCode || 0;
+
+                // webkit normalization
+                if (YAHOO.env.ua.webkit && (code in webkitKeymap)) {
+                    code = webkitKeymap[code];
+                }
+                return code;
+            },
+
+            /**
+             * Locating the saved event handler data by function ref
+             *
+             * @method _getCacheIndex
+             * @static
+             * @private
+             */
+            _getCacheIndex: function(el, sType, fn) {
+                for (var i=0,len=listeners.length; i<len; ++i) {
+                    var li = listeners[i];
+                    if ( li                 && 
+                         li[this.FN] == fn  && 
+                         li[this.EL] == el  && 
+                         li[this.TYPE] == sType ) {
+                        return i;
+                    }
+                }
+
+                return -1;
+            },
+
+            /**
+             * Generates an unique ID for the element if it does not already 
+             * have one.
+             * @method generateId
+             * @param el the element to create the id for
+             * @return {string} the resulting id of the element
+             * @static
+             */
+            generateId: function(el) {
+                var id = el.id;
+
+                if (!id) {
+                    id = "yuievtautoid-" + counter;
+                    ++counter;
+                    el.id = id;
+                }
+
+                return id;
+            },
+
+
+            /**
+             * We want to be able to use getElementsByTagName as a collection
+             * to attach a group of events to.  Unfortunately, different 
+             * browsers return different types of collections.  This function
+             * tests to determine if the object is array-like.  It will also 
+             * fail if the object is an array, but is empty.
+             * @method _isValidCollection
+             * @param o the object to test
+             * @return {boolean} true if the object is array-like and populated
+             * @static
+             * @private
+             */
+            _isValidCollection: function(o) {
+                try {
+                    return ( o                     && // o is something
+                             typeof o !== "string" && // o is not a string
+                             o.length              && // o is indexed
+                             !o.tagName            && // o is not an HTML element
+                             !o.alert              && // o is not a window
+                             typeof o[0] !== "undefined" );
+                } catch(ex) {
+                    YAHOO.log("_isValidCollection error, assuming that " +
+                " this is a cross frame problem and not a collection", "warn");
+                    return false;
+                }
+
+            },
+
+            /**
+             * @private
+             * @property elCache
+             * DOM element cache
+             * @static
+             * @deprecated Elements are not cached due to issues that arise when
+             * elements are removed and re-added
+             */
+            elCache: {},
+
+            /**
+             * We cache elements bound by id because when the unload event 
+             * fires, we can no longer use document.getElementById
+             * @method getEl
+             * @static
+             * @private
+             * @deprecated Elements are not cached any longer
+             */
+            getEl: function(id) {
+                return (typeof id === "string") ? document.getElementById(id) : id;
+            },
+
+            /**
+             * Clears the element cache
+             * @deprecated Elements are not cached any longer
+             * @method clearCache
+             * @static
+             * @private
+             */
+            clearCache: function() { },
+
+            /**
+             * Custom event the fires when the dom is initially usable
+             * @event DOMReadyEvent
+             */
+            DOMReadyEvent: new YAHOO.util.CustomEvent("DOMReady", this),
+
+            /**
+             * hook up any deferred listeners
+             * @method _load
+             * @static
+             * @private
+             */
+            _load: function(e) {
+
+                if (!loadComplete) {
+                    loadComplete = true;
+                    var EU = YAHOO.util.Event;
+
+                    // Just in case DOMReady did not go off for some reason
+                    EU._ready();
+
+                    // Available elements may not have been detected before the
+                    // window load event fires. Try to find them now so that the
+                    // the user is more likely to get the onAvailable notifications
+                    // before the window load notification
+                    EU._tryPreloadAttach();
+
+                    // Remove the listener to assist with the IE memory issue, but not
+                    // for other browsers because FF 1.0x does not like it.
+                    //if (this.isIE) {
+                        //EU._simpleRemove(window, "load", EU._load);
+                    //}
+                }
+            },
+
+            /**
+             * Fires the DOMReady event listeners the first time the document is
+             * usable.
+             * @method _ready
+             * @static
+             * @private
+             */
+            _ready: function(e) {
+                var EU = YAHOO.util.Event;
+                if (!EU.DOMReady) {
+                    EU.DOMReady=true;
+
+                    // Fire the content ready custom event
+                    EU.DOMReadyEvent.fire();
+
+                    // Remove the DOMContentLoaded (FF/Opera)
+                    EU._simpleRemove(document, "DOMContentLoaded", EU._ready);
+                }
+            },
+
+            /**
+             * Polling function that runs before the onload event fires, 
+             * attempting to attach to DOM Nodes as soon as they are 
+             * available
+             * @method _tryPreloadAttach
+             * @static
+             * @private
+             */
+            _tryPreloadAttach: function() {
+
+                if (this.locked) {
+                    return false;
+                }
+
+                if (this.isIE) {
+                    // Hold off if DOMReady has not fired and check current
+                    // readyState to protect against the IE operation aborted
+                    // issue.
+                    //if (!this.DOMReady || "complete" !== document.readyState) {
+                    if (!this.DOMReady) {
+                        this.startInterval();
+                        return false;
+                    }
+                }
+
+                this.locked = true;
+
+                // this.logger.debug("tryPreloadAttach");
+
+                // keep trying until after the page is loaded.  We need to 
+                // check the page load state prior to trying to bind the 
+                // elements so that we can be certain all elements have been 
+                // tested appropriately
+                var tryAgain = !loadComplete;
+                if (!tryAgain) {
+                    tryAgain = (retryCount > 0);
+                }
+
+                // onAvailable
+                var notAvail = [];
+
+                var executeItem = function (el, item) {
+                    var scope = el;
+                    if (item.override) {
+                        if (item.override === true) {
+                            scope = item.obj;
+                        } else {
+                            scope = item.override;
+                        }
+                    }
+                    item.fn.call(scope, item.obj);
+                };
+
+                var i,len,item,el;
+
+                // onAvailable
+                for (i=0,len=onAvailStack.length; i<len; ++i) {
+                    item = onAvailStack[i];
+                    if (item && !item.checkReady) {
+                        el = this.getEl(item.id);
+                        if (el) {
+                            executeItem(el, item);
+                            onAvailStack[i] = null;
+                        } else {
+                            notAvail.push(item);
+                        }
+                    }
+                }
+
+                // onContentReady
+                for (i=0,len=onAvailStack.length; i<len; ++i) {
+                    item = onAvailStack[i];
+                    if (item && item.checkReady) {
+                        el = this.getEl(item.id);
+
+                        if (el) {
+                            // The element is available, but not necessarily ready
+                            // @todo should we test parentNode.nextSibling?
+                            if (loadComplete || el.nextSibling) {
+                                executeItem(el, item);
+                                onAvailStack[i] = null;
+                            }
+                        } else {
+                            notAvail.push(item);
+                        }
+                    }
+                }
+
+                retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
+
+                if (tryAgain) {
+                    // we may need to strip the nulled out items here
+                    this.startInterval();
+                } else {
+                    clearInterval(this._interval);
+                    this._interval = null;
+                }
+
+                this.locked = false;
+
+                return true;
+
+            },
+
+            /**
+             * Removes all listeners attached to the given element via addListener.
+             * Optionally, the node's children can also be purged.
+             * Optionally, you can specify a specific type of event to remove.
+             * @method purgeElement
+             * @param {HTMLElement} el the element to purge
+             * @param {boolean} recurse recursively purge this element's children
+             * as well.  Use with caution.
+             * @param {string} sType optional type of listener to purge. If
+             * left out, all listeners will be removed
+             * @static
+             */
+            purgeElement: function(el, recurse, sType) {
+                var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
+                var elListeners = this.getListeners(oEl, sType), i, len;
+                if (elListeners) {
+                    for (i=0,len=elListeners.length; i<len ; ++i) {
+                        var l = elListeners[i];
+                        // can't use the index on the changing collection
+                        this.removeListener(oEl, l.type, l.fn, l.index);
+                        //this.removeListener(oEl, l.type, l.fn);
+                    }
+                }
+
+                if (recurse && oEl && oEl.childNodes) {
+                    for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
+                        this.purgeElement(oEl.childNodes[i], recurse, sType);
+                    }
+                }
+            },
+
+            /**
+             * Returns all listeners attached to the given element via addListener.
+             * Optionally, you can specify a specific type of event to return.
+             * @method getListeners
+             * @param el {HTMLElement|string} the element or element id to inspect 
+             * @param sType {string} optional type of listener to return. If
+             * left out, all listeners will be returned
+             * @return {Object} the listener. Contains the following fields:
+             * &nbsp;&nbsp;type:   (string)   the type of event
+             * &nbsp;&nbsp;fn:     (function) the callback supplied to addListener
+             * &nbsp;&nbsp;obj:    (object)   the custom object supplied to addListener
+             * &nbsp;&nbsp;adjust: (boolean|object)  whether or not to adjust the default scope
+             * &nbsp;&nbsp;scope: (boolean)  the derived scope based on the adjust parameter
+             * &nbsp;&nbsp;index:  (int)      its position in the Event util listener cache
+             * @static
+             */           
+            getListeners: function(el, sType) {
+                var results=[], searchLists;
+                if (!sType) {
+                    searchLists = [listeners, unloadListeners];
+                } else if (sType === "unload") {
+                    searchLists = [unloadListeners];
+                } else {
+                    searchLists = [listeners];
+                }
+
+                var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
+
+                for (var j=0;j<searchLists.length; j=j+1) {
+                    var searchList = searchLists[j];
+                    if (searchList && searchList.length > 0) {
+                        for (var i=0,len=searchList.length; i<len ; ++i) {
+                            var l = searchList[i];
+                            if ( l  && l[this.EL] === oEl && 
+                                    (!sType || sType === l[this.TYPE]) ) {
+                                results.push({
+                                    type:   l[this.TYPE],
+                                    fn:     l[this.FN],
+                                    obj:    l[this.OBJ],
+                                    adjust: l[this.OVERRIDE],
+                                    scope:  l[this.ADJ_SCOPE],
+                                    index:  i
+                                });
+                            }
+                        }
+                    }
+                }
+
+                return (results.length) ? results : null;
+            },
+
+            /**
+             * Removes all listeners registered by pe.event.  Called 
+             * automatically during the unload event.
+             * @method _unload
+             * @static
+             * @private
+             */
+            _unload: function(e) {
+
+                var EU = YAHOO.util.Event, i, j, l, len, index;
+
+                // execute and clear stored unload listeners
+                for (i=0,len=unloadListeners.length; i<len; ++i) {
+                    l = unloadListeners[i];
+                    if (l) {
+                        var scope = window;
+                        if (l[EU.ADJ_SCOPE]) {
+                            if (l[EU.ADJ_SCOPE] === true) {
+                                scope = l[EU.UNLOAD_OBJ];
+                            } else {
+                                scope = l[EU.ADJ_SCOPE];
+                            }
+                        }
+                        l[EU.FN].call(scope, EU.getEvent(e, l[EU.EL]), l[EU.UNLOAD_OBJ] );
+                        unloadListeners[i] = null;
+                        l=null;
+                        scope=null;
+                    }
+                }
+
+                unloadListeners = null;
+
+                // call clearAttributes or remove listeners to handle IE memory leaks
+                if (YAHOO.env.ua.ie && listeners && listeners.length > 0) {
+                    j = listeners.length;
+                    while (j) {
+                        index = j-1;
+                        l = listeners[index];
+                        if (l) {
+                            //try {
+                                //l[EU.EL].clearAttributes(); // errors on window objects
+                            //} catch(ex) {
+                            EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], index);
+                            //}
+                        } 
+                        j--;
+                    }
+                    l=null;
+                }
+
+                /*
+                // remove all listeners
+                if (listeners && listeners.length > 0) {
+                    j = listeners.length;
+                    while (j) {
+                        index = j-1;
+                        l = listeners[index];
+                        if (l) {
+                            EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], index);
+                        } 
+                        j = j - 1;
+                    }
+                    l=null;
+                }
+                */
+
+                /*
+                // kill legacy events
+                for (i=0,len=legacyEvents.length; i<len; ++i) {
+                    // dereference the element
+                    //delete legacyEvents[i][0];
+                    legacyEvents[i][0] = null;
+
+                    // delete the array item
+                    //delete legacyEvents[i];
+                    legacyEvents[i] = null;
+                }
+
+                */
+
+                legacyEvents = null;
+
+                EU._simpleRemove(window, "unload", EU._unload);
+
+            },
+
+            /**
+             * Returns scrollLeft
+             * @method _getScrollLeft
+             * @static
+             * @private
+             */
+            _getScrollLeft: function() {
+                return this._getScroll()[1];
+            },
+
+            /**
+             * Returns scrollTop
+             * @method _getScrollTop
+             * @static
+             * @private
+             */
+            _getScrollTop: function() {
+                return this._getScroll()[0];
+            },
+
+            /**
+             * Returns the scrollTop and scrollLeft.  Used to calculate the 
+             * pageX and pageY in Internet Explorer
+             * @method _getScroll
+             * @static
+             * @private
+             */
+            _getScroll: function() {
+                var dd = document.documentElement, db = document.body;
+                if (dd && (dd.scrollTop || dd.scrollLeft)) {
+                    return [dd.scrollTop, dd.scrollLeft];
+                } else if (db) {
+                    return [db.scrollTop, db.scrollLeft];
+                } else {
+                    return [0, 0];
+                }
+            },
+            
+            /**
+             * Used by old versions of CustomEvent, restored for backwards
+             * compatibility
+             * @method regCE
+             * @private
+             * @static
+             * @deprecated still here for backwards compatibility
+             */
+            regCE: function() {
+                // does nothing
+            },
+
+/*
+            testIEReady: function (){
+                var n = document.createElement('p'), ready = false;
+                try {
+                    // throws an error until the doc is ready
+                    n.doScroll('left'); 
+                    ready = true;
+                } catch(ex){ 
+                    // document is not ready
+                }
+
+                n = null;
+                return ready;
+            },
+*/
+
+            /**
+             * Adds a DOM event directly without the caching, cleanup, scope adj, etc
+             *
+             * @method _simpleAdd
+             * @param {HTMLElement} el      the element to bind the handler to
+             * @param {string}      sType   the type of event handler
+             * @param {function}    fn      the callback to invoke
+             * @param {boolen}      capture capture or bubble phase
+             * @static
+             * @private
+             */
+            _simpleAdd: function () {
+                if (window.addEventListener) {
+                    return function(el, sType, fn, capture) {
+                        el.addEventListener(sType, fn, (capture));
+                    };
+                } else if (window.attachEvent) {
+                    return function(el, sType, fn, capture) {
+                        el.attachEvent("on" + sType, fn);
+                    };
+                } else {
+                    return function(){};
+                }
+            }(),
+
+            /**
+             * Basic remove listener
+             *
+             * @method _simpleRemove
+             * @param {HTMLElement} el      the element to bind the handler to
+             * @param {string}      sType   the type of event handler
+             * @param {function}    fn      the callback to invoke
+             * @param {boolen}      capture capture or bubble phase
+             * @static
+             * @private
+             */
+            _simpleRemove: function() {
+                if (window.removeEventListener) {
+                    return function (el, sType, fn, capture) {
+                        el.removeEventListener(sType, fn, (capture));
+                    };
+                } else if (window.detachEvent) {
+                    return function (el, sType, fn) {
+                        el.detachEvent("on" + sType, fn);
+                    };
+                } else {
+                    return function(){};
+                }
+            }()
+        };
+
+    }();
+
+    (function() {
+        var EU = YAHOO.util.Event;
+
+        /**
+         * YAHOO.util.Event.on is an alias for addListener
+         * @method on
+         * @see addListener
+         * @static
+         */
+        EU.on = EU.addListener;
+
+        /////////////////////////////////////////////////////////////
+        // DOMReady
+        // based on work by: Dean Edwards/John Resig/Matthias Miller 
+
+        // Internet Explorer: use the readyState of a defered script.
+        // This isolates what appears to be a safe moment to manipulate
+        // the DOM prior to when the document's readyState suggests
+        // it is safe to do so.
+        if (EU.isIE) {
+
+            // Process onAvailable/onContentReady items when when the 
+            // DOM is ready.
+            YAHOO.util.Event.onDOMReady(
+                    YAHOO.util.Event._tryPreloadAttach,
+                    YAHOO.util.Event, true);
+
+            /*
+
+            //YAHOO.log("-" + document.readyState + "-");
+
+            var el, d=document, b=d.body;
+
+            // If the library is being injected after window.onload, it
+            // is not safe to document.write the script tag.  Detecting
+            // this state doesn't appear possible, so we expect a flag
+            // in YAHOO_config to be set if the library is being injected.
+            if (("undefined" !== typeof YAHOO_config) && YAHOO_config.injecting) {
+
+                el = document.createElement("script");
+                var p=d.getElementsByTagName("head")[0] || b;
+                p.insertBefore(el, p.firstChild);
+
+            } else {
+                //YAHOO.log("-dw-");
+    d.write('<scr'+'ipt id="_yui_eu_dr" defer="true" src="//:"><'+'/script>');
+                el=document.getElementById("_yui_eu_dr");
+            }
+            
+
+            if (el) {
+                el.onreadystatechange = function() {
+                    //YAHOO.log(";comp-" + this.readyState + ";");
+                    if ("complete" === this.readyState) {
+                        this.parentNode.removeChild(this);
+                        YAHOO.util.Event._ready();
+                    }
+                };
+            } else {
+                // The library was likely injected into the page
+                // rendering onDOMReady unreliable
+                // YAHOO.util.Event._ready();
+            }
+
+            el=null;
+
+            */
+
+/*
+            (function (){
+                var n = document.createElement('p');  
+                try {
+                    // throws an error if doc is not ready
+                    n.doScroll('left');
+                    n = null;
+                    YAHOO.util.Event._ready();
+                } catch (ex){
+                    n = null;
+setTimeout(arguments.callee, YAHOO.util.Event.POLL_INTERVAL);
+                }
+            })();
+*/
+
+            EU._dri = setInterval(function() {
+                var n = document.createElement('p');  
+                try {
+                    // throws an error if doc is not ready
+                    n.doScroll('left');
+                    clearInterval(EU._dri);
+                    EU._dri = null;
+                    EU._ready();
+                    n = null;
+                } catch (ex) { 
+                    n = null;
+                }
+            }, EU.POLL_INTERVAL); 
+
+        
+        // Safari: The document's readyState in Safari currently will
+        // change to loaded/complete before images are loaded.
+        //} else if (EU.webkit) {
+        } else if (EU.webkit) {
+
+            EU._dri = setInterval(function() {
+                var rs=document.readyState;
+                if ("loaded" == rs || "complete" == rs) {
+                    clearInterval(EU._dri);
+                    EU._dri = null;
+                    EU._ready();
+                }
+            }, EU.POLL_INTERVAL); 
+
+        // FireFox and Opera: These browsers provide a event for this
+        // moment.
+        } else {
+
+            // @todo will this fire when the library is injected?
+
+            EU._simpleAdd(document, "DOMContentLoaded", EU._ready);
+
+        }
+        /////////////////////////////////////////////////////////////
+
+
+        EU._simpleAdd(window, "load", EU._load);
+        EU._simpleAdd(window, "unload", EU._unload);
+        EU._tryPreloadAttach();
+    })();
+
+}
+/**
+ * EventProvider is designed to be used with YAHOO.augment to wrap 
+ * CustomEvents in an interface that allows events to be subscribed to 
+ * and fired by name.  This makes it possible for implementing code to
+ * subscribe to an event that either has not been created yet, or will
+ * not be created at all.
+ *
+ * @Class EventProvider
+ */
+YAHOO.util.EventProvider = function() { };
+
+YAHOO.util.EventProvider.prototype = {
+
+    /**
+     * Private storage of custom events
+     * @property __yui_events
+     * @type Object[]
+     * @private
+     */
+    __yui_events: null,
+
+    /**
+     * Private storage of custom event subscribers
+     * @property __yui_subscribers
+     * @type Object[]
+     * @private
+     */
+    __yui_subscribers: null,
+    
+    /**
+     * Subscribe to a CustomEvent by event type
+     *
+     * @method subscribe
+     * @param p_type     {string}   the type, or name of the event
+     * @param p_fn       {function} the function to exectute when the event fires
+     * @param p_obj      {Object}   An object to be passed along when the event 
+     *                              fires
+     * @param p_override {boolean}  If true, the obj passed in becomes the 
+     *                              execution scope of the listener
+     */
+    subscribe: function(p_type, p_fn, p_obj, p_override) {
+
+        this.__yui_events = this.__yui_events || {};
+        var ce = this.__yui_events[p_type];
+
+        if (ce) {
+            ce.subscribe(p_fn, p_obj, p_override);
+        } else {
+            this.__yui_subscribers = this.__yui_subscribers || {};
+            var subs = this.__yui_subscribers;
+            if (!subs[p_type]) {
+                subs[p_type] = [];
+            }
+            subs[p_type].push(
+                { fn: p_fn, obj: p_obj, override: p_override } );
+        }
+    },
+
+    /**
+     * Unsubscribes one or more listeners the from the specified event
+     * @method unsubscribe
+     * @param p_type {string}   The type, or name of the event.  If the type
+     *                          is not specified, it will attempt to remove
+     *                          the listener from all hosted events.
+     * @param p_fn   {Function} The subscribed function to unsubscribe, if not
+     *                          supplied, all subscribers will be removed.
+     * @param p_obj  {Object}   The custom object passed to subscribe.  This is
+     *                        optional, but if supplied will be used to
+     *                        disambiguate multiple listeners that are the same
+     *                        (e.g., you subscribe many object using a function
+     *                        that lives on the prototype)
+     * @return {boolean} true if the subscriber was found and detached.
+     */
+    unsubscribe: function(p_type, p_fn, p_obj) {
+        this.__yui_events = this.__yui_events || {};
+        var evts = this.__yui_events;
+        if (p_type) {
+            var ce = evts[p_type];
+            if (ce) {
+                return ce.unsubscribe(p_fn, p_obj);
+            }
+        } else {
+            var ret = true;
+            for (var i in evts) {
+                if (YAHOO.lang.hasOwnProperty(evts, i)) {
+                    ret = ret && evts[i].unsubscribe(p_fn, p_obj);
+                }
+            }
+            return ret;
+        }
+
+        return false;
+    },
+    
+    /**
+     * Removes all listeners from the specified event.  If the event type
+     * is not specified, all listeners from all hosted custom events will
+     * be removed.
+     * @method unsubscribeAll
+     * @param p_type {string}   The type, or name of the event
+     */
+    unsubscribeAll: function(p_type) {
+        return this.unsubscribe(p_type);
+    },
+
+    /**
+     * Creates a new custom event of the specified type.  If a custom event
+     * by that name already exists, it will not be re-created.  In either
+     * case the custom event is returned. 
+     *
+     * @method createEvent
+     *
+     * @param p_type {string} the type, or name of the event
+     * @param p_config {object} optional config params.  Valid properties are:
+     *
+     *  <ul>
+     *    <li>
+     *      scope: defines the default execution scope.  If not defined
+     *      the default scope will be this instance.
+     *    </li>
+     *    <li>
+     *      silent: if true, the custom event will not generate log messages.
+     *      This is false by default.
+     *    </li>
+     *    <li>
+     *      onSubscribeCallback: specifies a callback to execute when the
+     *      event has a new subscriber.  This will fire immediately for
+     *      each queued subscriber if any exist prior to the creation of
+     *      the event.
+     *    </li>
+     *  </ul>
+     *
+     *  @return {CustomEvent} the custom event
+     *
+     */
+    createEvent: function(p_type, p_config) {
+
+        this.__yui_events = this.__yui_events || {};
+        var opts = p_config || {};
+        var events = this.__yui_events;
+
+        if (events[p_type]) {
+YAHOO.log("EventProvider createEvent skipped: '"+p_type+"' already exists");
+        } else {
+
+            var scope  = opts.scope  || this;
+            var silent = (opts.silent);
+
+            var ce = new YAHOO.util.CustomEvent(p_type, scope, silent,
+                    YAHOO.util.CustomEvent.FLAT);
+            events[p_type] = ce;
+
+            if (opts.onSubscribeCallback) {
+                ce.subscribeEvent.subscribe(opts.onSubscribeCallback);
+            }
+
+            this.__yui_subscribers = this.__yui_subscribers || {};
+            var qs = this.__yui_subscribers[p_type];
+
+            if (qs) {
+                for (var i=0; i<qs.length; ++i) {
+                    ce.subscribe(qs[i].fn, qs[i].obj, qs[i].override);
+                }
+            }
+        }
+
+        return events[p_type];
+    },
+
+
+   /**
+     * Fire a custom event by name.  The callback functions will be executed
+     * from the scope specified when the event was created, and with the 
+     * following parameters:
+     *   <ul>
+     *   <li>The first argument fire() was executed with</li>
+     *   <li>The custom object (if any) that was passed into the subscribe() 
+     *       method</li>
+     *   </ul>
+     * If the custom event has not been explicitly created, it will be
+     * created now with the default config, scoped to the host object
+     * @method fireEvent
+     * @param p_type    {string}  the type, or name of the event
+     * @param arguments {Object*} an arbitrary set of parameters to pass to 
+     *                            the handler.
+     * @return {boolean} the return value from CustomEvent.fire
+     *                   
+     */
+    fireEvent: function(p_type, arg1, arg2, etc) {
+
+        this.__yui_events = this.__yui_events || {};
+        var ce = this.__yui_events[p_type];
+
+        if (!ce) {
+YAHOO.log(p_type + "event fired before it was created.");
+            return null;
+        }
+
+        var args = [];
+        for (var i=1; i<arguments.length; ++i) {
+            args.push(arguments[i]);
+        }
+        return ce.fire.apply(ce, args);
+    },
+
+    /**
+     * Returns true if the custom event of the provided type has been created
+     * with createEvent.
+     * @method hasEvent
+     * @param type {string} the type, or name of the event
+     */
+    hasEvent: function(type) {
+        if (this.__yui_events) {
+            if (this.__yui_events[type]) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+};
+
+/**
+* KeyListener is a utility that provides an easy interface for listening for
+* keydown/keyup events fired against DOM elements.
+* @namespace YAHOO.util
+* @class KeyListener
+* @constructor
+* @param {HTMLElement} attachTo The element or element ID to which the key 
+*                               event should be attached
+* @param {String}      attachTo The element or element ID to which the key
+*                               event should be attached
+* @param {Object}      keyData  The object literal representing the key(s) 
+*                               to detect. Possible attributes are 
+*                               shift(boolean), alt(boolean), ctrl(boolean) 
+*                               and keys(either an int or an array of ints 
+*                               representing keycodes).
+* @param {Function}    handler  The CustomEvent handler to fire when the 
+*                               key event is detected
+* @param {Object}      handler  An object literal representing the handler. 
+* @param {String}      event    Optional. The event (keydown or keyup) to 
+*                               listen for. Defaults automatically to keydown.
+*
+* @knownissue the "keypress" event is completely broken in Safari 2.x and below.
+*             the workaround is use "keydown" for key listening.  However, if
+*             it is desired to prevent the default behavior of the keystroke,
+*             that can only be done on the keypress event.  This makes key
+*             handling quite ugly.
+* @knownissue keydown is also broken in Safari 2.x and below for the ESC key.
+*             There currently is no workaround other than choosing another
+*             key to listen for.
+*/
+YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) {
+    if (!attachTo) {
+        YAHOO.log("No attachTo element specified", "error");
+    } else if (!keyData) {
+        YAHOO.log("No keyData specified", "error");
+    } else if (!handler) {
+        YAHOO.log("No handler specified", "error");
+    } 
+    
+    if (!event) {
+        event = YAHOO.util.KeyListener.KEYDOWN;
+    }
+
+    /**
+    * The CustomEvent fired internally when a key is pressed
+    * @event keyEvent
+    * @private
+    * @param {Object} keyData The object literal representing the key(s) to 
+    *                         detect. Possible attributes are shift(boolean), 
+    *                         alt(boolean), ctrl(boolean) and keys(either an 
+    *                         int or an array of ints representing keycodes).
+    */
+    var keyEvent = new YAHOO.util.CustomEvent("keyPressed");
+    
+    /**
+    * The CustomEvent fired when the KeyListener is enabled via the enable() 
+    * function
+    * @event enabledEvent
+    * @param {Object} keyData The object literal representing the key(s) to 
+    *                         detect. Possible attributes are shift(boolean), 
+    *                         alt(boolean), ctrl(boolean) and keys(either an 
+    *                         int or an array of ints representing keycodes).
+    */
+    this.enabledEvent = new YAHOO.util.CustomEvent("enabled");
+
+    /**
+    * The CustomEvent fired when the KeyListener is disabled via the 
+    * disable() function
+    * @event disabledEvent
+    * @param {Object} keyData The object literal representing the key(s) to 
+    *                         detect. Possible attributes are shift(boolean), 
+    *                         alt(boolean), ctrl(boolean) and keys(either an 
+    *                         int or an array of ints representing keycodes).
+    */
+    this.disabledEvent = new YAHOO.util.CustomEvent("disabled");
+
+    if (typeof attachTo == 'string') {
+        attachTo = document.getElementById(attachTo);
+    }
+
+    if (typeof handler == 'function') {
+        keyEvent.subscribe(handler);
+    } else {
+        keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope);
+    }
+
+    /**
+    * Handles the key event when a key is pressed.
+    * @method handleKeyPress
+    * @param {DOMEvent} e   The keypress DOM event
+    * @param {Object}   obj The DOM event scope object
+    * @private
+    */
+    function handleKeyPress(e, obj) {
+        if (! keyData.shift) {  
+            keyData.shift = false; 
+        }
+        if (! keyData.alt) {    
+            keyData.alt = false;
+        }
+        if (! keyData.ctrl) {
+            keyData.ctrl = false;
+        }
+
+        // check held down modifying keys first
+        if (e.shiftKey == keyData.shift && 
+            e.altKey   == keyData.alt &&
+            e.ctrlKey  == keyData.ctrl) { // if we pass this, all modifiers match
+            
+            var dataItem;
+
+            if (keyData.keys instanceof Array) {
+                for (var i=0;i<keyData.keys.length;i++) {
+                    dataItem = keyData.keys[i];
+
+                    if (dataItem == e.charCode ) {
+                        keyEvent.fire(e.charCode, e);
+                        break;
+                    } else if (dataItem == e.keyCode) {
+                        keyEvent.fire(e.keyCode, e);
+                        break;
+                    }
+                }
+            } else {
+                dataItem = keyData.keys;
+                if (dataItem == e.charCode ) {
+                    keyEvent.fire(e.charCode, e);
+                } else if (dataItem == e.keyCode) {
+                    keyEvent.fire(e.keyCode, e);
+                }
+            }
+        }
+    }
+
+    /**
+    * Enables the KeyListener by attaching the DOM event listeners to the 
+    * target DOM element
+    * @method enable
+    */
+    this.enable = function() {
+        if (! this.enabled) {
+            YAHOO.util.Event.addListener(attachTo, event, handleKeyPress);
+            this.enabledEvent.fire(keyData);
+        }
+        /**
+        * Boolean indicating the enabled/disabled state of the Tooltip
+        * @property enabled
+        * @type Boolean
+        */
+        this.enabled = true;
+    };
+
+    /**
+    * Disables the KeyListener by removing the DOM event listeners from the 
+    * target DOM element
+    * @method disable
+    */
+    this.disable = function() {
+        if (this.enabled) {
+            YAHOO.util.Event.removeListener(attachTo, event, handleKeyPress);
+            this.disabledEvent.fire(keyData);
+        }
+        this.enabled = false;
+    };
+
+    /**
+    * Returns a String representation of the object.
+    * @method toString
+    * @return {String}  The string representation of the KeyListener
+    */ 
+    this.toString = function() {
+        return "KeyListener [" + keyData.keys + "] " + attachTo.tagName + 
+                (attachTo.id ? "[" + attachTo.id + "]" : "");
+    };
+
+};
+
+/**
+* Constant representing the DOM "keydown" event.
+* @property YAHOO.util.KeyListener.KEYDOWN
+* @static
+* @final
+* @type String
+*/
+YAHOO.util.KeyListener.KEYDOWN = "keydown";
+
+/**
+* Constant representing the DOM "keyup" event.
+* @property YAHOO.util.KeyListener.KEYUP
+* @static
+* @final
+* @type String
+*/
+YAHOO.util.KeyListener.KEYUP = "keyup";
+
+/**
+ * keycode constants for a subset of the special keys
+ * @property KEY
+ * @static
+ * @final
+ */
+YAHOO.util.KeyListener.KEY = {
+    ALT          : 18,
+    BACK_SPACE   : 8,
+    CAPS_LOCK    : 20,
+    CONTROL      : 17,
+    DELETE       : 46,
+    DOWN         : 40,
+    END          : 35,
+    ENTER        : 13,
+    ESCAPE       : 27,
+    HOME         : 36,
+    LEFT         : 37,
+    META         : 224,
+    NUM_LOCK     : 144,
+    PAGE_DOWN    : 34,
+    PAGE_UP      : 33, 
+    PAUSE        : 19,
+    PRINTSCREEN  : 44,
+    RIGHT        : 39,
+    SCROLL_LOCK  : 145,
+    SHIFT        : 16,
+    SPACE        : 32,
+    TAB          : 9,
+    UP           : 38
+};
+YAHOO.register("event", YAHOO.util.Event, {version: "2.4.1", build: "742"});
Index: trunk/wb/include/yui/event/event.js
===================================================================
--- trunk/wb/include/yui/event/event.js	(nonexistent)
+++ trunk/wb/include/yui/event/event.js	(revision 601)
@@ -0,0 +1,2492 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+
+/**
+ * The CustomEvent class lets you define events for your application
+ * that can be subscribed to by one or more independent component.
+ *
+ * @param {String}  type The type of event, which is passed to the callback
+ *                  when the event fires
+ * @param {Object}  oScope The context the event will fire from.  "this" will
+ *                  refer to this object in the callback.  Default value: 
+ *                  the window object.  The listener can override this.
+ * @param {boolean} silent pass true to prevent the event from writing to
+ *                  the debugsystem
+ * @param {int}     signature the signature that the custom event subscriber
+ *                  will receive. YAHOO.util.CustomEvent.LIST or 
+ *                  YAHOO.util.CustomEvent.FLAT.  The default is
+ *                  YAHOO.util.CustomEvent.LIST.
+ * @namespace YAHOO.util
+ * @class CustomEvent
+ * @constructor
+ */
+YAHOO.util.CustomEvent = function(type, oScope, silent, signature) {
+
+    /**
+     * The type of event, returned to subscribers when the event fires
+     * @property type
+     * @type string
+     */
+    this.type = type;
+
+    /**
+     * The scope the the event will fire from by default.  Defaults to the window 
+     * obj
+     * @property scope
+     * @type object
+     */
+    this.scope = oScope || window;
+
+    /**
+     * By default all custom events are logged in the debug build, set silent
+     * to true to disable debug outpu for this event.
+     * @property silent
+     * @type boolean
+     */
+    this.silent = silent;
+
+    /**
+     * Custom events support two styles of arguments provided to the event
+     * subscribers.  
+     * <ul>
+     * <li>YAHOO.util.CustomEvent.LIST: 
+     *   <ul>
+     *   <li>param1: event name</li>
+     *   <li>param2: array of arguments sent to fire</li>
+     *   <li>param3: <optional> a custom object supplied by the subscriber</li>
+     *   </ul>
+     * </li>
+     * <li>YAHOO.util.CustomEvent.FLAT
+     *   <ul>
+     *   <li>param1: the first argument passed to fire.  If you need to
+     *           pass multiple parameters, use and array or object literal</li>
+     *   <li>param2: <optional> a custom object supplied by the subscriber</li>
+     *   </ul>
+     * </li>
+     * </ul>
+     *   @property signature
+     *   @type int
+     */
+    this.signature = signature || YAHOO.util.CustomEvent.LIST;
+
+    /**
+     * The subscribers to this event
+     * @property subscribers
+     * @type Subscriber[]
+     */
+    this.subscribers = [];
+
+    if (!this.silent) {
+    }
+
+    var onsubscribeType = "_YUICEOnSubscribe";
+
+    // Only add subscribe events for events that are not generated by 
+    // CustomEvent
+    if (type !== onsubscribeType) {
+
+        /**
+         * Custom events provide a custom event that fires whenever there is
+         * a new subscriber to the event.  This provides an opportunity to
+         * handle the case where there is a non-repeating event that has
+         * already fired has a new subscriber.  
+         *
+         * @event subscribeEvent
+         * @type YAHOO.util.CustomEvent
+         * @param {Function} fn The function to execute
+         * @param {Object}   obj An object to be passed along when the event 
+         *                       fires
+         * @param {boolean|Object}  override If true, the obj passed in becomes 
+         *                                   the execution scope of the listener.
+         *                                   if an object, that object becomes the
+         *                                   the execution scope.
+         */
+        this.subscribeEvent = 
+                new YAHOO.util.CustomEvent(onsubscribeType, this, true);
+
+    } 
+
+
+    /**
+     * In order to make it possible to execute the rest of the subscriber
+     * stack when one thows an exception, the subscribers exceptions are
+     * caught.  The most recent exception is stored in this property
+     * @property lastError
+     * @type Error
+     */
+    this.lastError = null;
+};
+
+/**
+ * Subscriber listener sigature constant.  The LIST type returns three
+ * parameters: the event type, the array of args passed to fire, and
+ * the optional custom object
+ * @property YAHOO.util.CustomEvent.LIST
+ * @static
+ * @type int
+ */
+YAHOO.util.CustomEvent.LIST = 0;
+
+/**
+ * Subscriber listener sigature constant.  The FLAT type returns two
+ * parameters: the first argument passed to fire and the optional 
+ * custom object
+ * @property YAHOO.util.CustomEvent.FLAT
+ * @static
+ * @type int
+ */
+YAHOO.util.CustomEvent.FLAT = 1;
+
+YAHOO.util.CustomEvent.prototype = {
+
+    /**
+     * Subscribes the caller to this event
+     * @method subscribe
+     * @param {Function} fn        The function to execute
+     * @param {Object}   obj       An object to be passed along when the event 
+     *                             fires
+     * @param {boolean|Object}  override If true, the obj passed in becomes 
+     *                                   the execution scope of the listener.
+     *                                   if an object, that object becomes the
+     *                                   the execution scope.
+     */
+    subscribe: function(fn, obj, override) {
+
+        if (!fn) {
+throw new Error("Invalid callback for subscriber to '" + this.type + "'");
+        }
+
+        if (this.subscribeEvent) {
+            this.subscribeEvent.fire(fn, obj, override);
+        }
+
+        this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, override) );
+    },
+
+    /**
+     * Unsubscribes subscribers.
+     * @method unsubscribe
+     * @param {Function} fn  The subscribed function to remove, if not supplied
+     *                       all will be removed
+     * @param {Object}   obj  The custom object passed to subscribe.  This is
+     *                        optional, but if supplied will be used to
+     *                        disambiguate multiple listeners that are the same
+     *                        (e.g., you subscribe many object using a function
+     *                        that lives on the prototype)
+     * @return {boolean} True if the subscriber was found and detached.
+     */
+    unsubscribe: function(fn, obj) {
+
+        if (!fn) {
+            return this.unsubscribeAll();
+        }
+
+        var found = false;
+        for (var i=0, len=this.subscribers.length; i<len; ++i) {
+            var s = this.subscribers[i];
+            if (s && s.contains(fn, obj)) {
+                this._delete(i);
+                found = true;
+            }
+        }
+
+        return found;
+    },
+
+    /**
+     * Notifies the subscribers.  The callback functions will be executed
+     * from the scope specified when the event was created, and with the 
+     * following parameters:
+     *   <ul>
+     *   <li>The type of event</li>
+     *   <li>All of the arguments fire() was executed with as an array</li>
+     *   <li>The custom object (if any) that was passed into the subscribe() 
+     *       method</li>
+     *   </ul>
+     * @method fire 
+     * @param {Object*} arguments an arbitrary set of parameters to pass to 
+     *                            the handler.
+     * @return {boolean} false if one of the subscribers returned false, 
+     *                   true otherwise
+     */
+    fire: function() {
+        var len=this.subscribers.length;
+        if (!len && this.silent) {
+            return true;
+        }
+
+        var args=[], ret=true, i, rebuild=false;
+
+        for (i=0; i<arguments.length; ++i) {
+            args.push(arguments[i]);
+        }
+
+        if (!this.silent) {
+        }
+
+        for (i=0; i<len; ++i) {
+            var s = this.subscribers[i];
+            if (!s) {
+                rebuild=true;
+            } else {
+                if (!this.silent) {
+                }
+
+                var scope = s.getScope(this.scope);
+
+                if (this.signature == YAHOO.util.CustomEvent.FLAT) {
+                    var param = null;
+                    if (args.length > 0) {
+                        param = args[0];
+                    }
+
+                    try {
+                        ret = s.fn.call(scope, param, s.obj);
+                    } catch(e) {
+                        this.lastError = e;
+                    }
+                } else {
+                    try {
+                        ret = s.fn.call(scope, this.type, args, s.obj);
+                    } catch(ex) {
+                        this.lastError = ex;
+                    }
+                }
+                if (false === ret) {
+                    if (!this.silent) {
+                    }
+
+                    //break;
+                    return false;
+                }
+            }
+        }
+
+        if (rebuild) {
+            var newlist=[],subs=this.subscribers;
+            for (i=0,len=subs.length; i<len; i=i+1) {
+                newlist.push(subs[i]);
+            }
+
+            this.subscribers=newlist;
+        }
+
+        return true;
+    },
+
+    /**
+     * Removes all listeners
+     * @method unsubscribeAll
+     * @return {int} The number of listeners unsubscribed
+     */
+    unsubscribeAll: function() {
+        for (var i=0, len=this.subscribers.length; i<len; ++i) {
+            this._delete(len - 1 - i);
+        }
+
+        this.subscribers=[];
+
+        return i;
+    },
+
+    /**
+     * @method _delete
+     * @private
+     */
+    _delete: function(index) {
+        var s = this.subscribers[index];
+        if (s) {
+            delete s.fn;
+            delete s.obj;
+        }
+
+        this.subscribers[index]=null;
+    },
+
+    /**
+     * @method toString
+     */
+    toString: function() {
+         return "CustomEvent: " + "'" + this.type  + "', " + 
+             "scope: " + this.scope;
+
+    }
+};
+
+/////////////////////////////////////////////////////////////////////
+
+/**
+ * Stores the subscriber information to be used when the event fires.
+ * @param {Function} fn       The function to execute
+ * @param {Object}   obj      An object to be passed along when the event fires
+ * @param {boolean}  override If true, the obj passed in becomes the execution
+ *                            scope of the listener
+ * @class Subscriber
+ * @constructor
+ */
+YAHOO.util.Subscriber = function(fn, obj, override) {
+
+    /**
+     * The callback that will be execute when the event fires
+     * @property fn
+     * @type function
+     */
+    this.fn = fn;
+
+    /**
+     * An optional custom object that will passed to the callback when
+     * the event fires
+     * @property obj
+     * @type object
+     */
+    this.obj = YAHOO.lang.isUndefined(obj) ? null : obj;
+
+    /**
+     * The default execution scope for the event listener is defined when the
+     * event is created (usually the object which contains the event).
+     * By setting override to true, the execution scope becomes the custom
+     * object passed in by the subscriber.  If override is an object, that 
+     * object becomes the scope.
+     * @property override
+     * @type boolean|object
+     */
+    this.override = override;
+
+};
+
+/**
+ * Returns the execution scope for this listener.  If override was set to true
+ * the custom obj will be the scope.  If override is an object, that is the
+ * scope, otherwise the default scope will be used.
+ * @method getScope
+ * @param {Object} defaultScope the scope to use if this listener does not
+ *                              override it.
+ */
+YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) {
+    if (this.override) {
+        if (this.override === true) {
+            return this.obj;
+        } else {
+            return this.override;
+        }
+    }
+    return defaultScope;
+};
+
+/**
+ * Returns true if the fn and obj match this objects properties.
+ * Used by the unsubscribe method to match the right subscriber.
+ *
+ * @method contains
+ * @param {Function} fn the function to execute
+ * @param {Object} obj an object to be passed along when the event fires
+ * @return {boolean} true if the supplied arguments match this 
+ *                   subscriber's signature.
+ */
+YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
+    if (obj) {
+        return (this.fn == fn && this.obj == obj);
+    } else {
+        return (this.fn == fn);
+    }
+};
+
+/**
+ * @method toString
+ */
+YAHOO.util.Subscriber.prototype.toString = function() {
+    return "Subscriber { obj: " + this.obj  + 
+           ", override: " +  (this.override || "no") + " }";
+};
+
+/**
+ * The Event Utility provides utilities for managing DOM Events and tools
+ * for building event systems
+ *
+ * @module event
+ * @title Event Utility
+ * @namespace YAHOO.util
+ * @requires yahoo
+ */
+
+// The first instance of Event will win if it is loaded more than once.
+// @TODO this needs to be changed so that only the state data that needs to
+// be preserved is kept, while methods are overwritten/added as needed.
+// This means that the module pattern can't be used.
+if (!YAHOO.util.Event) {
+
+/**
+ * The event utility provides functions to add and remove event listeners,
+ * event cleansing.  It also tries to automatically remove listeners it
+ * registers during the unload event.
+ *
+ * @class Event
+ * @static
+ */
+    YAHOO.util.Event = function() {
+
+        /**
+         * True after the onload event has fired
+         * @property loadComplete
+         * @type boolean
+         * @static
+         * @private
+         */
+        var loadComplete =  false;
+
+        /**
+         * Cache of wrapped listeners
+         * @property listeners
+         * @type array
+         * @static
+         * @private
+         */
+        var listeners = [];
+
+        /**
+         * User-defined unload function that will be fired before all events
+         * are detached
+         * @property unloadListeners
+         * @type array
+         * @static
+         * @private
+         */
+        var unloadListeners = [];
+
+        /**
+         * Cache of DOM0 event handlers to work around issues with DOM2 events
+         * in Safari
+         * @property legacyEvents
+         * @static
+         * @private
+         */
+        var legacyEvents = [];
+
+        /**
+         * Listener stack for DOM0 events
+         * @property legacyHandlers
+         * @static
+         * @private
+         */
+        var legacyHandlers = [];
+
+        /**
+         * The number of times to poll after window.onload.  This number is
+         * increased if additional late-bound handlers are requested after
+         * the page load.
+         * @property retryCount
+         * @static
+         * @private
+         */
+        var retryCount = 0;
+
+        /**
+         * onAvailable listeners
+         * @property onAvailStack
+         * @static
+         * @private
+         */
+        var onAvailStack = [];
+
+        /**
+         * Lookup table for legacy events
+         * @property legacyMap
+         * @static
+         * @private
+         */
+        var legacyMap = [];
+
+        /**
+         * Counter for auto id generation
+         * @property counter
+         * @static
+         * @private
+         */
+        var counter = 0;
+        
+        /**
+         * Normalized keycodes for webkit/safari
+         * @property webkitKeymap
+         * @type {int: int}
+         * @private
+         * @static
+         * @final
+         */
+        var webkitKeymap = {
+            63232: 38, // up
+            63233: 40, // down
+            63234: 37, // left
+            63235: 39, // right
+            63276: 33, // page up
+            63277: 34, // page down
+            25: 9      // SHIFT-TAB (Safari provides a different key code in
+                       // this case, even though the shiftKey modifier is set)
+        };
+
+        return {
+
+            /**
+             * The number of times we should look for elements that are not
+             * in the DOM at the time the event is requested after the document
+             * has been loaded.  The default is 4000@amp;10 ms, so it will poll
+             * for 40 seconds or until all outstanding handlers are bound
+             * (whichever comes first).
+             * @property POLL_RETRYS
+             * @type int
+             * @static
+             * @final
+             */
+            POLL_RETRYS: 4000,
+
+            /**
+             * The poll interval in milliseconds
+             * @property POLL_INTERVAL
+             * @type int
+             * @static
+             * @final
+             */
+            POLL_INTERVAL: 10,
+
+            /**
+             * Element to bind, int constant
+             * @property EL
+             * @type int
+             * @static
+             * @final
+             */
+            EL: 0,
+
+            /**
+             * Type of event, int constant
+             * @property TYPE
+             * @type int
+             * @static
+             * @final
+             */
+            TYPE: 1,
+
+            /**
+             * Function to execute, int constant
+             * @property FN
+             * @type int
+             * @static
+             * @final
+             */
+            FN: 2,
+
+            /**
+             * Function wrapped for scope correction and cleanup, int constant
+             * @property WFN
+             * @type int
+             * @static
+             * @final
+             */
+            WFN: 3,
+
+            /**
+             * Object passed in by the user that will be returned as a 
+             * parameter to the callback, int constant.  Specific to
+             * unload listeners
+             * @property OBJ
+             * @type int
+             * @static
+             * @final
+             */
+            UNLOAD_OBJ: 3,
+
+            /**
+             * Adjusted scope, either the element we are registering the event
+             * on or the custom object passed in by the listener, int constant
+             * @property ADJ_SCOPE
+             * @type int
+             * @static
+             * @final
+             */
+            ADJ_SCOPE: 4,
+
+            /**
+             * The original obj passed into addListener
+             * @property OBJ
+             * @type int
+             * @static
+             * @final
+             */
+            OBJ: 5,
+
+            /**
+             * The original scope parameter passed into addListener
+             * @property OVERRIDE
+             * @type int
+             * @static
+             * @final
+             */
+            OVERRIDE: 6,
+
+            /**
+             * addListener/removeListener can throw errors in unexpected scenarios.
+             * These errors are suppressed, the method returns false, and this property
+             * is set
+             * @property lastError
+             * @static
+             * @type Error
+             */
+            lastError: null,
+
+            /**
+             * Safari detection
+             * @property isSafari
+             * @private
+             * @static
+             * @deprecated use YAHOO.env.ua.webkit
+             */
+            isSafari: YAHOO.env.ua.webkit,
+            
+            /**
+             * webkit version
+             * @property webkit
+             * @type string
+             * @private
+             * @static
+             * @deprecated use YAHOO.env.ua.webkit
+             */
+            webkit: YAHOO.env.ua.webkit,
+            
+            /**
+             * IE detection 
+             * @property isIE
+             * @private
+             * @static
+             * @deprecated use YAHOO.env.ua.ie
+             */
+            isIE: YAHOO.env.ua.ie,
+
+            /**
+             * poll handle
+             * @property _interval
+             * @static
+             * @private
+             */
+            _interval: null,
+
+            /**
+             * document readystate poll handle
+             * @property _dri
+             * @static
+             * @private
+             */
+             _dri: null,
+
+            /**
+             * True when the document is initially usable
+             * @property DOMReady
+             * @type boolean
+             * @static
+             */
+            DOMReady: false,
+
+            /**
+             * @method startInterval
+             * @static
+             * @private
+             */
+            startInterval: function() {
+                if (!this._interval) {
+                    var self = this;
+                    var callback = function() { self._tryPreloadAttach(); };
+                    this._interval = setInterval(callback, this.POLL_INTERVAL);
+                }
+            },
+
+            /**
+             * Executes the supplied callback when the item with the supplied
+             * id is found.  This is meant to be used to execute behavior as
+             * soon as possible as the page loads.  If you use this after the
+             * initial page load it will poll for a fixed time for the element.
+             * The number of times it will poll and the frequency are
+             * configurable.  By default it will poll for 10 seconds.
+             *
+             * <p>The callback is executed with a single parameter:
+             * the custom object parameter, if provided.</p>
+             *
+             * @method onAvailable
+             *
+             * @param {string||string[]}   p_id the id of the element, or an array
+             * of ids to look for.
+             * @param {function} p_fn what to execute when the element is found.
+             * @param {object}   p_obj an optional object to be passed back as
+             *                   a parameter to p_fn.
+             * @param {boolean|object}  p_override If set to true, p_fn will execute
+             *                   in the scope of p_obj, if set to an object it
+             *                   will execute in the scope of that object
+             * @param checkContent {boolean} check child node readiness (onContentReady)
+             * @static
+             */
+            onAvailable: function(p_id, p_fn, p_obj, p_override, checkContent) {
+
+                var a = (YAHOO.lang.isString(p_id)) ? [p_id] : p_id;
+
+                for (var i=0; i<a.length; i=i+1) {
+                    onAvailStack.push({id:         a[i], 
+                                       fn:         p_fn, 
+                                       obj:        p_obj, 
+                                       override:   p_override, 
+                                       checkReady: checkContent });
+                }
+                retryCount = this.POLL_RETRYS;
+                this.startInterval();
+            },
+
+            /**
+             * Works the same way as onAvailable, but additionally checks the
+             * state of sibling elements to determine if the content of the
+             * available element is safe to modify.
+             *
+             * <p>The callback is executed with a single parameter:
+             * the custom object parameter, if provided.</p>
+             *
+             * @method onContentReady
+             *
+             * @param {string}   p_id the id of the element to look for.
+             * @param {function} p_fn what to execute when the element is ready.
+             * @param {object}   p_obj an optional object to be passed back as
+             *                   a parameter to p_fn.
+             * @param {boolean|object}  p_override If set to true, p_fn will execute
+             *                   in the scope of p_obj.  If an object, p_fn will
+             *                   exectute in the scope of that object
+             *
+             * @static
+             */
+            onContentReady: function(p_id, p_fn, p_obj, p_override) {
+                this.onAvailable(p_id, p_fn, p_obj, p_override, true);
+            },
+
+            /**
+             * Executes the supplied callback when the DOM is first usable.  This
+             * will execute immediately if called after the DOMReady event has
+             * fired.   @todo the DOMContentReady event does not fire when the
+             * script is dynamically injected into the page.  This means the
+             * DOMReady custom event will never fire in FireFox or Opera when the
+             * library is injected.  It _will_ fire in Safari, and the IE 
+             * implementation would allow for us to fire it if the defered script
+             * is not available.  We want this to behave the same in all browsers.
+             * Is there a way to identify when the script has been injected 
+             * instead of included inline?  Is there a way to know whether the 
+             * window onload event has fired without having had a listener attached 
+             * to it when it did so?
+             *
+             * <p>The callback is a CustomEvent, so the signature is:</p>
+             * <p>type &lt;string&gt;, args &lt;array&gt;, customobject &lt;object&gt;</p>
+             * <p>For DOMReady events, there are no fire argments, so the
+             * signature is:</p>
+             * <p>"DOMReady", [], obj</p>
+             *
+             *
+             * @method onDOMReady
+             *
+             * @param {function} p_fn what to execute when the element is found.
+             * @param {object}   p_obj an optional object to be passed back as
+             *                   a parameter to p_fn.
+             * @param {boolean|object}  p_scope If set to true, p_fn will execute
+             *                   in the scope of p_obj, if set to an object it
+             *                   will execute in the scope of that object
+             *
+             * @static
+             */
+            onDOMReady: function(p_fn, p_obj, p_override) {
+                if (this.DOMReady) {
+                    setTimeout(function() {
+                        var s = window;
+                        if (p_override) {
+                            if (p_override === true) {
+                                s = p_obj;
+                            } else {
+                                s = p_override;
+                            }
+                        }
+                        p_fn.call(s, "DOMReady", [], p_obj);
+                    }, 0);
+                } else {
+                    this.DOMReadyEvent.subscribe(p_fn, p_obj, p_override);
+                }
+            },
+
+            /**
+             * Appends an event handler
+             *
+             * @method addListener
+             *
+             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
+             *  reference, or a collection of ids and/or elements to assign the 
+             *  listener to.
+             * @param {String}   sType     The type of event to append
+             * @param {Function} fn        The method the event invokes
+             * @param {Object}   obj    An arbitrary object that will be 
+             *                             passed as a parameter to the handler
+             * @param {Boolean|object}  override  If true, the obj passed in becomes
+             *                             the execution scope of the listener. If an
+             *                             object, this object becomes the execution
+             *                             scope.
+             * @return {Boolean} True if the action was successful or defered,
+             *                        false if one or more of the elements 
+             *                        could not have the listener attached,
+             *                        or if the operation throws an exception.
+             * @static
+             */
+            addListener: function(el, sType, fn, obj, override) {
+
+                if (!fn || !fn.call) {
+// throw new TypeError(sType + " addListener call failed, callback undefined");
+                    return false;
+                }
+
+                // The el argument can be an array of elements or element ids.
+                if ( this._isValidCollection(el)) {
+                    var ok = true;
+                    for (var i=0,len=el.length; i<len; ++i) {
+                        ok = this.on(el[i], 
+                                       sType, 
+                                       fn, 
+                                       obj, 
+                                       override) && ok;
+                    }
+                    return ok;
+
+                } else if (YAHOO.lang.isString(el)) {
+                    var oEl = this.getEl(el);
+                    // If the el argument is a string, we assume it is 
+                    // actually the id of the element.  If the page is loaded
+                    // we convert el to the actual element, otherwise we 
+                    // defer attaching the event until onload event fires
+
+                    // check to see if we need to delay hooking up the event 
+                    // until after the page loads.
+                    if (oEl) {
+                        el = oEl;
+                    } else {
+                        // defer adding the event until the element is available
+                        this.onAvailable(el, function() {
+                           YAHOO.util.Event.on(el, sType, fn, obj, override);
+                        });
+
+                        return true;
+                    }
+                }
+
+                // Element should be an html element or an array if we get 
+                // here.
+                if (!el) {
+                    return false;
+                }
+
+                // we need to make sure we fire registered unload events 
+                // prior to automatically unhooking them.  So we hang on to 
+                // these instead of attaching them to the window and fire the
+                // handles explicitly during our one unload event.
+                if ("unload" == sType && obj !== this) {
+                    unloadListeners[unloadListeners.length] =
+                            [el, sType, fn, obj, override];
+                    return true;
+                }
+
+
+                // if the user chooses to override the scope, we use the custom
+                // object passed in, otherwise the executing scope will be the
+                // HTML element that the event is registered on
+                var scope = el;
+                if (override) {
+                    if (override === true) {
+                        scope = obj;
+                    } else {
+                        scope = override;
+                    }
+                }
+
+                // wrap the function so we can return the obj object when
+                // the event fires;
+                var wrappedFn = function(e) {
+                        return fn.call(scope, YAHOO.util.Event.getEvent(e, el), 
+                                obj);
+                    };
+
+                var li = [el, sType, fn, wrappedFn, scope, obj, override];
+                var index = listeners.length;
+                // cache the listener so we can try to automatically unload
+                listeners[index] = li;
+
+                if (this.useLegacyEvent(el, sType)) {
+                    var legacyIndex = this.getLegacyIndex(el, sType);
+
+                    // Add a new dom0 wrapper if one is not detected for this
+                    // element
+                    if ( legacyIndex == -1 || 
+                                el != legacyEvents[legacyIndex][0] ) {
+
+                        legacyIndex = legacyEvents.length;
+                        legacyMap[el.id + sType] = legacyIndex;
+
+                        // cache the signature for the DOM0 event, and 
+                        // include the existing handler for the event, if any
+                        legacyEvents[legacyIndex] = 
+                            [el, sType, el["on" + sType]];
+                        legacyHandlers[legacyIndex] = [];
+
+                        el["on" + sType] = 
+                            function(e) {
+                                YAHOO.util.Event.fireLegacyEvent(
+                                    YAHOO.util.Event.getEvent(e), legacyIndex);
+                            };
+                    }
+
+                    // add a reference to the wrapped listener to our custom
+                    // stack of events
+                    //legacyHandlers[legacyIndex].push(index);
+                    legacyHandlers[legacyIndex].push(li);
+
+                } else {
+                    try {
+                        this._simpleAdd(el, sType, wrappedFn, false);
+                    } catch(ex) {
+                        // handle an error trying to attach an event.  If it fails
+                        // we need to clean up the cache
+                        this.lastError = ex;
+                        this.removeListener(el, sType, fn);
+                        return false;
+                    }
+                }
+
+                return true;
+                
+            },
+
+            /**
+             * When using legacy events, the handler is routed to this object
+             * so we can fire our custom listener stack.
+             * @method fireLegacyEvent
+             * @static
+             * @private
+             */
+            fireLegacyEvent: function(e, legacyIndex) {
+                var ok=true,le,lh,li,scope,ret;
+                
+                lh = legacyHandlers[legacyIndex];
+                for (var i=0,len=lh.length; i<len; ++i) {
+                    li = lh[i];
+                    if ( li && li[this.WFN] ) {
+                        scope = li[this.ADJ_SCOPE];
+                        ret = li[this.WFN].call(scope, e);
+                        ok = (ok && ret);
+                    }
+                }
+
+                // Fire the original handler if we replaced one.  We fire this
+                // after the other events to keep stopPropagation/preventDefault
+                // that happened in the DOM0 handler from touching our DOM2
+                // substitute
+                le = legacyEvents[legacyIndex];
+                if (le && le[2]) {
+                    le[2](e);
+                }
+                
+                return ok;
+            },
+
+            /**
+             * Returns the legacy event index that matches the supplied 
+             * signature
+             * @method getLegacyIndex
+             * @static
+             * @private
+             */
+            getLegacyIndex: function(el, sType) {
+                var key = this.generateId(el) + sType;
+                if (typeof legacyMap[key] == "undefined") { 
+                    return -1;
+                } else {
+                    return legacyMap[key];
+                }
+            },
+
+            /**
+             * Logic that determines when we should automatically use legacy
+             * events instead of DOM2 events.  Currently this is limited to old
+             * Safari browsers with a broken preventDefault
+             * @method useLegacyEvent
+             * @static
+             * @private
+             */
+            useLegacyEvent: function(el, sType) {
+                if (this.webkit && ("click"==sType || "dblclick"==sType)) {
+                    var v = parseInt(this.webkit, 10);
+                    if (!isNaN(v) && v<418) {
+                        return true;
+                    }
+                }
+                return false;
+            },
+                    
+            /**
+             * Removes an event listener
+             *
+             * @method removeListener
+             *
+             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
+             *  reference, or a collection of ids and/or elements to remove
+             *  the listener from.
+             * @param {String} sType the type of event to remove.
+             * @param {Function} fn the method the event invokes.  If fn is
+             *  undefined, then all event handlers for the type of event are 
+             *  removed.
+             * @return {boolean} true if the unbind was successful, false 
+             *  otherwise.
+             * @static
+             */
+            removeListener: function(el, sType, fn) {
+                var i, len, li;
+
+                // The el argument can be a string
+                if (typeof el == "string") {
+                    el = this.getEl(el);
+                // The el argument can be an array of elements or element ids.
+                } else if ( this._isValidCollection(el)) {
+                    var ok = true;
+                    for (i=0,len=el.length; i<len; ++i) {
+                        ok = ( this.removeListener(el[i], sType, fn) && ok );
+                    }
+                    return ok;
+                }
+
+                if (!fn || !fn.call) {
+                    //return false;
+                    return this.purgeElement(el, false, sType);
+                }
+
+                if ("unload" == sType) {
+
+                    for (i=0, len=unloadListeners.length; i<len; i++) {
+                        li = unloadListeners[i];
+                        if (li && 
+                            li[0] == el && 
+                            li[1] == sType && 
+                            li[2] == fn) {
+                                //unloadListeners.splice(i, 1);
+                                unloadListeners[i]=null;
+                                return true;
+                        }
+                    }
+
+                    return false;
+                }
+
+                var cacheItem = null;
+
+                // The index is a hidden parameter; needed to remove it from
+                // the method signature because it was tempting users to
+                // try and take advantage of it, which is not possible.
+                var index = arguments[3];
+  
+                if ("undefined" === typeof index) {
+                    index = this._getCacheIndex(el, sType, fn);
+                }
+
+                if (index >= 0) {
+                    cacheItem = listeners[index];
+                }
+
+                if (!el || !cacheItem) {
+                    return false;
+                }
+
+
+                if (this.useLegacyEvent(el, sType)) {
+                    var legacyIndex = this.getLegacyIndex(el, sType);
+                    var llist = legacyHandlers[legacyIndex];
+                    if (llist) {
+                        for (i=0, len=llist.length; i<len; ++i) {
+                            li = llist[i];
+                            if (li && 
+                                li[this.EL] == el && 
+                                li[this.TYPE] == sType && 
+                                li[this.FN] == fn) {
+                                    //llist.splice(i, 1);
+                                    llist[i]=null;
+                                    break;
+                            }
+                        }
+                    }
+
+                } else {
+                    try {
+                        this._simpleRemove(el, sType, cacheItem[this.WFN], false);
+                    } catch(ex) {
+                        this.lastError = ex;
+                        return false;
+                    }
+                }
+
+                // removed the wrapped handler
+                delete listeners[index][this.WFN];
+                delete listeners[index][this.FN];
+                //listeners.splice(index, 1);
+                listeners[index]=null;
+
+                return true;
+
+            },
+
+            /**
+             * Returns the event's target element.  Safari sometimes provides
+             * a text node, and this is automatically resolved to the text
+             * node's parent so that it behaves like other browsers.
+             * @method getTarget
+             * @param {Event} ev the event
+             * @param {boolean} resolveTextNode when set to true the target's
+             *                  parent will be returned if the target is a 
+             *                  text node.  @deprecated, the text node is
+             *                  now resolved automatically
+             * @return {HTMLElement} the event's target
+             * @static
+             */
+            getTarget: function(ev, resolveTextNode) {
+                var t = ev.target || ev.srcElement;
+                return this.resolveTextNode(t);
+            },
+
+            /**
+             * In some cases, some browsers will return a text node inside
+             * the actual element that was targeted.  This normalizes the
+             * return value for getTarget and getRelatedTarget.
+             * @method resolveTextNode
+             * @param {HTMLElement} node node to resolve
+             * @return {HTMLElement} the normized node
+             * @static
+             */
+            resolveTextNode: function(node) {
+                if (node && 3 == node.nodeType) {
+                    return node.parentNode;
+                } else {
+                    return node;
+                }
+            },
+
+            /**
+             * Returns the event's pageX
+             * @method getPageX
+             * @param {Event} ev the event
+             * @return {int} the event's pageX
+             * @static
+             */
+            getPageX: function(ev) {
+                var x = ev.pageX;
+                if (!x && 0 !== x) {
+                    x = ev.clientX || 0;
+
+                    if ( this.isIE ) {
+                        x += this._getScrollLeft();
+                    }
+                }
+
+                return x;
+            },
+
+            /**
+             * Returns the event's pageY
+             * @method getPageY
+             * @param {Event} ev the event
+             * @return {int} the event's pageY
+             * @static
+             */
+            getPageY: function(ev) {
+                var y = ev.pageY;
+                if (!y && 0 !== y) {
+                    y = ev.clientY || 0;
+
+                    if ( this.isIE ) {
+                        y += this._getScrollTop();
+                    }
+                }
+
+
+                return y;
+            },
+
+            /**
+             * Returns the pageX and pageY properties as an indexed array.
+             * @method getXY
+             * @param {Event} ev the event
+             * @return {[x, y]} the pageX and pageY properties of the event
+             * @static
+             */
+            getXY: function(ev) {
+                return [this.getPageX(ev), this.getPageY(ev)];
+            },
+
+            /**
+             * Returns the event's related target 
+             * @method getRelatedTarget
+             * @param {Event} ev the event
+             * @return {HTMLElement} the event's relatedTarget
+             * @static
+             */
+            getRelatedTarget: function(ev) {
+                var t = ev.relatedTarget;
+                if (!t) {
+                    if (ev.type == "mouseout") {
+                        t = ev.toElement;
+                    } else if (ev.type == "mouseover") {
+                        t = ev.fromElement;
+                    }
+                }
+
+                return this.resolveTextNode(t);
+            },
+
+            /**
+             * Returns the time of the event.  If the time is not included, the
+             * event is modified using the current time.
+             * @method getTime
+             * @param {Event} ev the event
+             * @return {Date} the time of the event
+             * @static
+             */
+            getTime: function(ev) {
+                if (!ev.time) {
+                    var t = new Date().getTime();
+                    try {
+                        ev.time = t;
+                    } catch(ex) { 
+                        this.lastError = ex;
+                        return t;
+                    }
+                }
+
+                return ev.time;
+            },
+
+            /**
+             * Convenience method for stopPropagation + preventDefault
+             * @method stopEvent
+             * @param {Event} ev the event
+             * @static
+             */
+            stopEvent: function(ev) {
+                this.stopPropagation(ev);
+                this.preventDefault(ev);
+            },
+
+            /**
+             * Stops event propagation
+             * @method stopPropagation
+             * @param {Event} ev the event
+             * @static
+             */
+            stopPropagation: function(ev) {
+                if (ev.stopPropagation) {
+                    ev.stopPropagation();
+                } else {
+                    ev.cancelBubble = true;
+                }
+            },
+
+            /**
+             * Prevents the default behavior of the event
+             * @method preventDefault
+             * @param {Event} ev the event
+             * @static
+             */
+            preventDefault: function(ev) {
+                if (ev.preventDefault) {
+                    ev.preventDefault();
+                } else {
+                    ev.returnValue = false;
+                }
+            },
+             
+            /**
+             * Finds the event in the window object, the caller's arguments, or
+             * in the arguments of another method in the callstack.  This is
+             * executed automatically for events registered through the event
+             * manager, so the implementer should not normally need to execute
+             * this function at all.
+             * @method getEvent
+             * @param {Event} e the event parameter from the handler
+             * @param {HTMLElement} boundEl the element the listener is attached to
+             * @return {Event} the event 
+             * @static
+             */
+            getEvent: function(e, boundEl) {
+                var ev = e || window.event;
+
+                if (!ev) {
+                    var c = this.getEvent.caller;
+                    while (c) {
+                        ev = c.arguments[0];
+                        if (ev && Event == ev.constructor) {
+                            break;
+                        }
+                        c = c.caller;
+                    }
+                }
+
+                // IE events that target non-browser objects (e.g., VML
+                // canvas) will sometimes throw errors when you try to
+                // inspect the properties of the event target.  We try to
+                // detect this condition, and provide a dummy target (the bound
+                // element) to eliminate spurious errors.  
+
+                // the implementation caused unexpected results in some 
+                // implementations, so this has been rolled back for now
+                /* 
+                if (ev && this.isIE) {
+
+                    try {
+
+                        var el = ev.srcElement;
+
+                    } catch(ex) {
+
+                         
+                        ev.target = boundEl;
+                    }
+
+                }
+                */
+
+                return ev;
+            },
+
+            /**
+             * Returns the charcode for an event
+             * @method getCharCode
+             * @param {Event} ev the event
+             * @return {int} the event's charCode
+             * @static
+             */
+            getCharCode: function(ev) {
+                var code = ev.keyCode || ev.charCode || 0;
+
+                // webkit normalization
+                if (YAHOO.env.ua.webkit && (code in webkitKeymap)) {
+                    code = webkitKeymap[code];
+                }
+                return code;
+            },
+
+            /**
+             * Locating the saved event handler data by function ref
+             *
+             * @method _getCacheIndex
+             * @static
+             * @private
+             */
+            _getCacheIndex: function(el, sType, fn) {
+                for (var i=0,len=listeners.length; i<len; ++i) {
+                    var li = listeners[i];
+                    if ( li                 && 
+                         li[this.FN] == fn  && 
+                         li[this.EL] == el  && 
+                         li[this.TYPE] == sType ) {
+                        return i;
+                    }
+                }
+
+                return -1;
+            },
+
+            /**
+             * Generates an unique ID for the element if it does not already 
+             * have one.
+             * @method generateId
+             * @param el the element to create the id for
+             * @return {string} the resulting id of the element
+             * @static
+             */
+            generateId: function(el) {
+                var id = el.id;
+
+                if (!id) {
+                    id = "yuievtautoid-" + counter;
+                    ++counter;
+                    el.id = id;
+                }
+
+                return id;
+            },
+
+
+            /**
+             * We want to be able to use getElementsByTagName as a collection
+             * to attach a group of events to.  Unfortunately, different 
+             * browsers return different types of collections.  This function
+             * tests to determine if the object is array-like.  It will also 
+             * fail if the object is an array, but is empty.
+             * @method _isValidCollection
+             * @param o the object to test
+             * @return {boolean} true if the object is array-like and populated
+             * @static
+             * @private
+             */
+            _isValidCollection: function(o) {
+                try {
+                    return ( o                     && // o is something
+                             typeof o !== "string" && // o is not a string
+                             o.length              && // o is indexed
+                             !o.tagName            && // o is not an HTML element
+                             !o.alert              && // o is not a window
+                             typeof o[0] !== "undefined" );
+                } catch(ex) {
+                    return false;
+                }
+
+            },
+
+            /**
+             * @private
+             * @property elCache
+             * DOM element cache
+             * @static
+             * @deprecated Elements are not cached due to issues that arise when
+             * elements are removed and re-added
+             */
+            elCache: {},
+
+            /**
+             * We cache elements bound by id because when the unload event 
+             * fires, we can no longer use document.getElementById
+             * @method getEl
+             * @static
+             * @private
+             * @deprecated Elements are not cached any longer
+             */
+            getEl: function(id) {
+                return (typeof id === "string") ? document.getElementById(id) : id;
+            },
+
+            /**
+             * Clears the element cache
+             * @deprecated Elements are not cached any longer
+             * @method clearCache
+             * @static
+             * @private
+             */
+            clearCache: function() { },
+
+            /**
+             * Custom event the fires when the dom is initially usable
+             * @event DOMReadyEvent
+             */
+            DOMReadyEvent: new YAHOO.util.CustomEvent("DOMReady", this),
+
+            /**
+             * hook up any deferred listeners
+             * @method _load
+             * @static
+             * @private
+             */
+            _load: function(e) {
+
+                if (!loadComplete) {
+                    loadComplete = true;
+                    var EU = YAHOO.util.Event;
+
+                    // Just in case DOMReady did not go off for some reason
+                    EU._ready();
+
+                    // Available elements may not have been detected before the
+                    // window load event fires. Try to find them now so that the
+                    // the user is more likely to get the onAvailable notifications
+                    // before the window load notification
+                    EU._tryPreloadAttach();
+
+                    // Remove the listener to assist with the IE memory issue, but not
+                    // for other browsers because FF 1.0x does not like it.
+                    //if (this.isIE) {
+                        //EU._simpleRemove(window, "load", EU._load);
+                    //}
+                }
+            },
+
+            /**
+             * Fires the DOMReady event listeners the first time the document is
+             * usable.
+             * @method _ready
+             * @static
+             * @private
+             */
+            _ready: function(e) {
+                var EU = YAHOO.util.Event;
+                if (!EU.DOMReady) {
+                    EU.DOMReady=true;
+
+                    // Fire the content ready custom event
+                    EU.DOMReadyEvent.fire();
+
+                    // Remove the DOMContentLoaded (FF/Opera)
+                    EU._simpleRemove(document, "DOMContentLoaded", EU._ready);
+                }
+            },
+
+            /**
+             * Polling function that runs before the onload event fires, 
+             * attempting to attach to DOM Nodes as soon as they are 
+             * available
+             * @method _tryPreloadAttach
+             * @static
+             * @private
+             */
+            _tryPreloadAttach: function() {
+
+                if (this.locked) {
+                    return false;
+                }
+
+                if (this.isIE) {
+                    // Hold off if DOMReady has not fired and check current
+                    // readyState to protect against the IE operation aborted
+                    // issue.
+                    //if (!this.DOMReady || "complete" !== document.readyState) {
+                    if (!this.DOMReady) {
+                        this.startInterval();
+                        return false;
+                    }
+                }
+
+                this.locked = true;
+
+
+                // keep trying until after the page is loaded.  We need to 
+                // check the page load state prior to trying to bind the 
+                // elements so that we can be certain all elements have been 
+                // tested appropriately
+                var tryAgain = !loadComplete;
+                if (!tryAgain) {
+                    tryAgain = (retryCount > 0);
+                }
+
+                // onAvailable
+                var notAvail = [];
+
+                var executeItem = function (el, item) {
+                    var scope = el;
+                    if (item.override) {
+                        if (item.override === true) {
+                            scope = item.obj;
+                        } else {
+                            scope = item.override;
+                        }
+                    }
+                    item.fn.call(scope, item.obj);
+                };
+
+                var i,len,item,el;
+
+                // onAvailable
+                for (i=0,len=onAvailStack.length; i<len; ++i) {
+                    item = onAvailStack[i];
+                    if (item && !item.checkReady) {
+                        el = this.getEl(item.id);
+                        if (el) {
+                            executeItem(el, item);
+                            onAvailStack[i] = null;
+                        } else {
+                            notAvail.push(item);
+                        }
+                    }
+                }
+
+                // onContentReady
+                for (i=0,len=onAvailStack.length; i<len; ++i) {
+                    item = onAvailStack[i];
+                    if (item && item.checkReady) {
+                        el = this.getEl(item.id);
+
+                        if (el) {
+                            // The element is available, but not necessarily ready
+                            // @todo should we test parentNode.nextSibling?
+                            if (loadComplete || el.nextSibling) {
+                                executeItem(el, item);
+                                onAvailStack[i] = null;
+                            }
+                        } else {
+                            notAvail.push(item);
+                        }
+                    }
+                }
+
+                retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
+
+                if (tryAgain) {
+                    // we may need to strip the nulled out items here
+                    this.startInterval();
+                } else {
+                    clearInterval(this._interval);
+                    this._interval = null;
+                }
+
+                this.locked = false;
+
+                return true;
+
+            },
+
+            /**
+             * Removes all listeners attached to the given element via addListener.
+             * Optionally, the node's children can also be purged.
+             * Optionally, you can specify a specific type of event to remove.
+             * @method purgeElement
+             * @param {HTMLElement} el the element to purge
+             * @param {boolean} recurse recursively purge this element's children
+             * as well.  Use with caution.
+             * @param {string} sType optional type of listener to purge. If
+             * left out, all listeners will be removed
+             * @static
+             */
+            purgeElement: function(el, recurse, sType) {
+                var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
+                var elListeners = this.getListeners(oEl, sType), i, len;
+                if (elListeners) {
+                    for (i=0,len=elListeners.length; i<len ; ++i) {
+                        var l = elListeners[i];
+                        // can't use the index on the changing collection
+                        this.removeListener(oEl, l.type, l.fn, l.index);
+                        //this.removeListener(oEl, l.type, l.fn);
+                    }
+                }
+
+                if (recurse && oEl && oEl.childNodes) {
+                    for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
+                        this.purgeElement(oEl.childNodes[i], recurse, sType);
+                    }
+                }
+            },
+
+            /**
+             * Returns all listeners attached to the given element via addListener.
+             * Optionally, you can specify a specific type of event to return.
+             * @method getListeners
+             * @param el {HTMLElement|string} the element or element id to inspect 
+             * @param sType {string} optional type of listener to return. If
+             * left out, all listeners will be returned
+             * @return {Object} the listener. Contains the following fields:
+             * &nbsp;&nbsp;type:   (string)   the type of event
+             * &nbsp;&nbsp;fn:     (function) the callback supplied to addListener
+             * &nbsp;&nbsp;obj:    (object)   the custom object supplied to addListener
+             * &nbsp;&nbsp;adjust: (boolean|object)  whether or not to adjust the default scope
+             * &nbsp;&nbsp;scope: (boolean)  the derived scope based on the adjust parameter
+             * &nbsp;&nbsp;index:  (int)      its position in the Event util listener cache
+             * @static
+             */           
+            getListeners: function(el, sType) {
+                var results=[], searchLists;
+                if (!sType) {
+                    searchLists = [listeners, unloadListeners];
+                } else if (sType === "unload") {
+                    searchLists = [unloadListeners];
+                } else {
+                    searchLists = [listeners];
+                }
+
+                var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
+
+                for (var j=0;j<searchLists.length; j=j+1) {
+                    var searchList = searchLists[j];
+                    if (searchList && searchList.length > 0) {
+                        for (var i=0,len=searchList.length; i<len ; ++i) {
+                            var l = searchList[i];
+                            if ( l  && l[this.EL] === oEl && 
+                                    (!sType || sType === l[this.TYPE]) ) {
+                                results.push({
+                                    type:   l[this.TYPE],
+                                    fn:     l[this.FN],
+                                    obj:    l[this.OBJ],
+                                    adjust: l[this.OVERRIDE],
+                                    scope:  l[this.ADJ_SCOPE],
+                                    index:  i
+                                });
+                            }
+                        }
+                    }
+                }
+
+                return (results.length) ? results : null;
+            },
+
+            /**
+             * Removes all listeners registered by pe.event.  Called 
+             * automatically during the unload event.
+             * @method _unload
+             * @static
+             * @private
+             */
+            _unload: function(e) {
+
+                var EU = YAHOO.util.Event, i, j, l, len, index;
+
+                // execute and clear stored unload listeners
+                for (i=0,len=unloadListeners.length; i<len; ++i) {
+                    l = unloadListeners[i];
+                    if (l) {
+                        var scope = window;
+                        if (l[EU.ADJ_SCOPE]) {
+                            if (l[EU.ADJ_SCOPE] === true) {
+                                scope = l[EU.UNLOAD_OBJ];
+                            } else {
+                                scope = l[EU.ADJ_SCOPE];
+                            }
+                        }
+                        l[EU.FN].call(scope, EU.getEvent(e, l[EU.EL]), l[EU.UNLOAD_OBJ] );
+                        unloadListeners[i] = null;
+                        l=null;
+                        scope=null;
+                    }
+                }
+
+                unloadListeners = null;
+
+                // call clearAttributes or remove listeners to handle IE memory leaks
+                if (YAHOO.env.ua.ie && listeners && listeners.length > 0) {
+                    j = listeners.length;
+                    while (j) {
+                        index = j-1;
+                        l = listeners[index];
+                        if (l) {
+                            //try {
+                                //l[EU.EL].clearAttributes(); // errors on window objects
+                            //} catch(ex) {
+                            EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], index);
+                            //}
+                        } 
+                        j--;
+                    }
+                    l=null;
+                }
+
+                /*
+                // remove all listeners
+                if (listeners && listeners.length > 0) {
+                    j = listeners.length;
+                    while (j) {
+                        index = j-1;
+                        l = listeners[index];
+                        if (l) {
+                            EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], index);
+                        } 
+                        j = j - 1;
+                    }
+                    l=null;
+                }
+                */
+
+                /*
+                // kill legacy events
+                for (i=0,len=legacyEvents.length; i<len; ++i) {
+                    // dereference the element
+                    //delete legacyEvents[i][0];
+                    legacyEvents[i][0] = null;
+
+                    // delete the array item
+                    //delete legacyEvents[i];
+                    legacyEvents[i] = null;
+                }
+
+                */
+
+                legacyEvents = null;
+
+                EU._simpleRemove(window, "unload", EU._unload);
+
+            },
+
+            /**
+             * Returns scrollLeft
+             * @method _getScrollLeft
+             * @static
+             * @private
+             */
+            _getScrollLeft: function() {
+                return this._getScroll()[1];
+            },
+
+            /**
+             * Returns scrollTop
+             * @method _getScrollTop
+             * @static
+             * @private
+             */
+            _getScrollTop: function() {
+                return this._getScroll()[0];
+            },
+
+            /**
+             * Returns the scrollTop and scrollLeft.  Used to calculate the 
+             * pageX and pageY in Internet Explorer
+             * @method _getScroll
+             * @static
+             * @private
+             */
+            _getScroll: function() {
+                var dd = document.documentElement, db = document.body;
+                if (dd && (dd.scrollTop || dd.scrollLeft)) {
+                    return [dd.scrollTop, dd.scrollLeft];
+                } else if (db) {
+                    return [db.scrollTop, db.scrollLeft];
+                } else {
+                    return [0, 0];
+                }
+            },
+            
+            /**
+             * Used by old versions of CustomEvent, restored for backwards
+             * compatibility
+             * @method regCE
+             * @private
+             * @static
+             * @deprecated still here for backwards compatibility
+             */
+            regCE: function() {
+                // does nothing
+            },
+
+/*
+            testIEReady: function (){
+                var n = document.createElement('p'), ready = false;
+                try {
+                    // throws an error until the doc is ready
+                    n.doScroll('left'); 
+                    ready = true;
+                } catch(ex){ 
+                    // document is not ready
+                }
+
+                n = null;
+                return ready;
+            },
+*/
+
+            /**
+             * Adds a DOM event directly without the caching, cleanup, scope adj, etc
+             *
+             * @method _simpleAdd
+             * @param {HTMLElement} el      the element to bind the handler to
+             * @param {string}      sType   the type of event handler
+             * @param {function}    fn      the callback to invoke
+             * @param {boolen}      capture capture or bubble phase
+             * @static
+             * @private
+             */
+            _simpleAdd: function () {
+                if (window.addEventListener) {
+                    return function(el, sType, fn, capture) {
+                        el.addEventListener(sType, fn, (capture));
+                    };
+                } else if (window.attachEvent) {
+                    return function(el, sType, fn, capture) {
+                        el.attachEvent("on" + sType, fn);
+                    };
+                } else {
+                    return function(){};
+                }
+            }(),
+
+            /**
+             * Basic remove listener
+             *
+             * @method _simpleRemove
+             * @param {HTMLElement} el      the element to bind the handler to
+             * @param {string}      sType   the type of event handler
+             * @param {function}    fn      the callback to invoke
+             * @param {boolen}      capture capture or bubble phase
+             * @static
+             * @private
+             */
+            _simpleRemove: function() {
+                if (window.removeEventListener) {
+                    return function (el, sType, fn, capture) {
+                        el.removeEventListener(sType, fn, (capture));
+                    };
+                } else if (window.detachEvent) {
+                    return function (el, sType, fn) {
+                        el.detachEvent("on" + sType, fn);
+                    };
+                } else {
+                    return function(){};
+                }
+            }()
+        };
+
+    }();
+
+    (function() {
+        var EU = YAHOO.util.Event;
+
+        /**
+         * YAHOO.util.Event.on is an alias for addListener
+         * @method on
+         * @see addListener
+         * @static
+         */
+        EU.on = EU.addListener;
+
+        /////////////////////////////////////////////////////////////
+        // DOMReady
+        // based on work by: Dean Edwards/John Resig/Matthias Miller 
+
+        // Internet Explorer: use the readyState of a defered script.
+        // This isolates what appears to be a safe moment to manipulate
+        // the DOM prior to when the document's readyState suggests
+        // it is safe to do so.
+        if (EU.isIE) {
+
+            // Process onAvailable/onContentReady items when when the 
+            // DOM is ready.
+            YAHOO.util.Event.onDOMReady(
+                    YAHOO.util.Event._tryPreloadAttach,
+                    YAHOO.util.Event, true);
+
+            /*
+
+
+            var el, d=document, b=d.body;
+
+            // If the library is being injected after window.onload, it
+            // is not safe to document.write the script tag.  Detecting
+            // this state doesn't appear possible, so we expect a flag
+            // in YAHOO_config to be set if the library is being injected.
+            if (("undefined" !== typeof YAHOO_config) && YAHOO_config.injecting) {
+
+                el = document.createElement("script");
+                var p=d.getElementsByTagName("head")[0] || b;
+                p.insertBefore(el, p.firstChild);
+
+            } else {
+    d.write('<scr'+'ipt id="_yui_eu_dr" defer="true" src="//:"><'+'/script>');
+                el=document.getElementById("_yui_eu_dr");
+            }
+            
+
+            if (el) {
+                el.onreadystatechange = function() {
+                    if ("complete" === this.readyState) {
+                        this.parentNode.removeChild(this);
+                        YAHOO.util.Event._ready();
+                    }
+                };
+            } else {
+                // The library was likely injected into the page
+                // rendering onDOMReady unreliable
+                // YAHOO.util.Event._ready();
+            }
+
+            el=null;
+
+            */
+
+/*
+            (function (){
+                var n = document.createElement('p');  
+                try {
+                    // throws an error if doc is not ready
+                    n.doScroll('left');
+                    n = null;
+                    YAHOO.util.Event._ready();
+                } catch (ex){
+                    n = null;
+setTimeout(arguments.callee, YAHOO.util.Event.POLL_INTERVAL);
+                }
+            })();
+*/
+
+            EU._dri = setInterval(function() {
+                var n = document.createElement('p');  
+                try {
+                    // throws an error if doc is not ready
+                    n.doScroll('left');
+                    clearInterval(EU._dri);
+                    EU._dri = null;
+                    EU._ready();
+                    n = null;
+                } catch (ex) { 
+                    n = null;
+                }
+            }, EU.POLL_INTERVAL); 
+
+        
+        // Safari: The document's readyState in Safari currently will
+        // change to loaded/complete before images are loaded.
+        //} else if (EU.webkit) {
+        } else if (EU.webkit) {
+
+            EU._dri = setInterval(function() {
+                var rs=document.readyState;
+                if ("loaded" == rs || "complete" == rs) {
+                    clearInterval(EU._dri);
+                    EU._dri = null;
+                    EU._ready();
+                }
+            }, EU.POLL_INTERVAL); 
+
+        // FireFox and Opera: These browsers provide a event for this
+        // moment.
+        } else {
+
+            // @todo will this fire when the library is injected?
+
+            EU._simpleAdd(document, "DOMContentLoaded", EU._ready);
+
+        }
+        /////////////////////////////////////////////////////////////
+
+
+        EU._simpleAdd(window, "load", EU._load);
+        EU._simpleAdd(window, "unload", EU._unload);
+        EU._tryPreloadAttach();
+    })();
+
+}
+/**
+ * EventProvider is designed to be used with YAHOO.augment to wrap 
+ * CustomEvents in an interface that allows events to be subscribed to 
+ * and fired by name.  This makes it possible for implementing code to
+ * subscribe to an event that either has not been created yet, or will
+ * not be created at all.
+ *
+ * @Class EventProvider
+ */
+YAHOO.util.EventProvider = function() { };
+
+YAHOO.util.EventProvider.prototype = {
+
+    /**
+     * Private storage of custom events
+     * @property __yui_events
+     * @type Object[]
+     * @private
+     */
+    __yui_events: null,
+
+    /**
+     * Private storage of custom event subscribers
+     * @property __yui_subscribers
+     * @type Object[]
+     * @private
+     */
+    __yui_subscribers: null,
+    
+    /**
+     * Subscribe to a CustomEvent by event type
+     *
+     * @method subscribe
+     * @param p_type     {string}   the type, or name of the event
+     * @param p_fn       {function} the function to exectute when the event fires
+     * @param p_obj      {Object}   An object to be passed along when the event 
+     *                              fires
+     * @param p_override {boolean}  If true, the obj passed in becomes the 
+     *                              execution scope of the listener
+     */
+    subscribe: function(p_type, p_fn, p_obj, p_override) {
+
+        this.__yui_events = this.__yui_events || {};
+        var ce = this.__yui_events[p_type];
+
+        if (ce) {
+            ce.subscribe(p_fn, p_obj, p_override);
+        } else {
+            this.__yui_subscribers = this.__yui_subscribers || {};
+            var subs = this.__yui_subscribers;
+            if (!subs[p_type]) {
+                subs[p_type] = [];
+            }
+            subs[p_type].push(
+                { fn: p_fn, obj: p_obj, override: p_override } );
+        }
+    },
+
+    /**
+     * Unsubscribes one or more listeners the from the specified event
+     * @method unsubscribe
+     * @param p_type {string}   The type, or name of the event.  If the type
+     *                          is not specified, it will attempt to remove
+     *                          the listener from all hosted events.
+     * @param p_fn   {Function} The subscribed function to unsubscribe, if not
+     *                          supplied, all subscribers will be removed.
+     * @param p_obj  {Object}   The custom object passed to subscribe.  This is
+     *                        optional, but if supplied will be used to
+     *                        disambiguate multiple listeners that are the same
+     *                        (e.g., you subscribe many object using a function
+     *                        that lives on the prototype)
+     * @return {boolean} true if the subscriber was found and detached.
+     */
+    unsubscribe: function(p_type, p_fn, p_obj) {
+        this.__yui_events = this.__yui_events || {};
+        var evts = this.__yui_events;
+        if (p_type) {
+            var ce = evts[p_type];
+            if (ce) {
+                return ce.unsubscribe(p_fn, p_obj);
+            }
+        } else {
+            var ret = true;
+            for (var i in evts) {
+                if (YAHOO.lang.hasOwnProperty(evts, i)) {
+                    ret = ret && evts[i].unsubscribe(p_fn, p_obj);
+                }
+            }
+            return ret;
+        }
+
+        return false;
+    },
+    
+    /**
+     * Removes all listeners from the specified event.  If the event type
+     * is not specified, all listeners from all hosted custom events will
+     * be removed.
+     * @method unsubscribeAll
+     * @param p_type {string}   The type, or name of the event
+     */
+    unsubscribeAll: function(p_type) {
+        return this.unsubscribe(p_type);
+    },
+
+    /**
+     * Creates a new custom event of the specified type.  If a custom event
+     * by that name already exists, it will not be re-created.  In either
+     * case the custom event is returned. 
+     *
+     * @method createEvent
+     *
+     * @param p_type {string} the type, or name of the event
+     * @param p_config {object} optional config params.  Valid properties are:
+     *
+     *  <ul>
+     *    <li>
+     *      scope: defines the default execution scope.  If not defined
+     *      the default scope will be this instance.
+     *    </li>
+     *    <li>
+     *      silent: if true, the custom event will not generate log messages.
+     *      This is false by default.
+     *    </li>
+     *    <li>
+     *      onSubscribeCallback: specifies a callback to execute when the
+     *      event has a new subscriber.  This will fire immediately for
+     *      each queued subscriber if any exist prior to the creation of
+     *      the event.
+     *    </li>
+     *  </ul>
+     *
+     *  @return {CustomEvent} the custom event
+     *
+     */
+    createEvent: function(p_type, p_config) {
+
+        this.__yui_events = this.__yui_events || {};
+        var opts = p_config || {};
+        var events = this.__yui_events;
+
+        if (events[p_type]) {
+        } else {
+
+            var scope  = opts.scope  || this;
+            var silent = (opts.silent);
+
+            var ce = new YAHOO.util.CustomEvent(p_type, scope, silent,
+                    YAHOO.util.CustomEvent.FLAT);
+            events[p_type] = ce;
+
+            if (opts.onSubscribeCallback) {
+                ce.subscribeEvent.subscribe(opts.onSubscribeCallback);
+            }
+
+            this.__yui_subscribers = this.__yui_subscribers || {};
+            var qs = this.__yui_subscribers[p_type];
+
+            if (qs) {
+                for (var i=0; i<qs.length; ++i) {
+                    ce.subscribe(qs[i].fn, qs[i].obj, qs[i].override);
+                }
+            }
+        }
+
+        return events[p_type];
+    },
+
+
+   /**
+     * Fire a custom event by name.  The callback functions will be executed
+     * from the scope specified when the event was created, and with the 
+     * following parameters:
+     *   <ul>
+     *   <li>The first argument fire() was executed with</li>
+     *   <li>The custom object (if any) that was passed into the subscribe() 
+     *       method</li>
+     *   </ul>
+     * If the custom event has not been explicitly created, it will be
+     * created now with the default config, scoped to the host object
+     * @method fireEvent
+     * @param p_type    {string}  the type, or name of the event
+     * @param arguments {Object*} an arbitrary set of parameters to pass to 
+     *                            the handler.
+     * @return {boolean} the return value from CustomEvent.fire
+     *                   
+     */
+    fireEvent: function(p_type, arg1, arg2, etc) {
+
+        this.__yui_events = this.__yui_events || {};
+        var ce = this.__yui_events[p_type];
+
+        if (!ce) {
+            return null;
+        }
+
+        var args = [];
+        for (var i=1; i<arguments.length; ++i) {
+            args.push(arguments[i]);
+        }
+        return ce.fire.apply(ce, args);
+    },
+
+    /**
+     * Returns true if the custom event of the provided type has been created
+     * with createEvent.
+     * @method hasEvent
+     * @param type {string} the type, or name of the event
+     */
+    hasEvent: function(type) {
+        if (this.__yui_events) {
+            if (this.__yui_events[type]) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+};
+
+/**
+* KeyListener is a utility that provides an easy interface for listening for
+* keydown/keyup events fired against DOM elements.
+* @namespace YAHOO.util
+* @class KeyListener
+* @constructor
+* @param {HTMLElement} attachTo The element or element ID to which the key 
+*                               event should be attached
+* @param {String}      attachTo The element or element ID to which the key
+*                               event should be attached
+* @param {Object}      keyData  The object literal representing the key(s) 
+*                               to detect. Possible attributes are 
+*                               shift(boolean), alt(boolean), ctrl(boolean) 
+*                               and keys(either an int or an array of ints 
+*                               representing keycodes).
+* @param {Function}    handler  The CustomEvent handler to fire when the 
+*                               key event is detected
+* @param {Object}      handler  An object literal representing the handler. 
+* @param {String}      event    Optional. The event (keydown or keyup) to 
+*                               listen for. Defaults automatically to keydown.
+*
+* @knownissue the "keypress" event is completely broken in Safari 2.x and below.
+*             the workaround is use "keydown" for key listening.  However, if
+*             it is desired to prevent the default behavior of the keystroke,
+*             that can only be done on the keypress event.  This makes key
+*             handling quite ugly.
+* @knownissue keydown is also broken in Safari 2.x and below for the ESC key.
+*             There currently is no workaround other than choosing another
+*             key to listen for.
+*/
+YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) {
+    if (!attachTo) {
+    } else if (!keyData) {
+    } else if (!handler) {
+    } 
+    
+    if (!event) {
+        event = YAHOO.util.KeyListener.KEYDOWN;
+    }
+
+    /**
+    * The CustomEvent fired internally when a key is pressed
+    * @event keyEvent
+    * @private
+    * @param {Object} keyData The object literal representing the key(s) to 
+    *                         detect. Possible attributes are shift(boolean), 
+    *                         alt(boolean), ctrl(boolean) and keys(either an 
+    *                         int or an array of ints representing keycodes).
+    */
+    var keyEvent = new YAHOO.util.CustomEvent("keyPressed");
+    
+    /**
+    * The CustomEvent fired when the KeyListener is enabled via the enable() 
+    * function
+    * @event enabledEvent
+    * @param {Object} keyData The object literal representing the key(s) to 
+    *                         detect. Possible attributes are shift(boolean), 
+    *                         alt(boolean), ctrl(boolean) and keys(either an 
+    *                         int or an array of ints representing keycodes).
+    */
+    this.enabledEvent = new YAHOO.util.CustomEvent("enabled");
+
+    /**
+    * The CustomEvent fired when the KeyListener is disabled via the 
+    * disable() function
+    * @event disabledEvent
+    * @param {Object} keyData The object literal representing the key(s) to 
+    *                         detect. Possible attributes are shift(boolean), 
+    *                         alt(boolean), ctrl(boolean) and keys(either an 
+    *                         int or an array of ints representing keycodes).
+    */
+    this.disabledEvent = new YAHOO.util.CustomEvent("disabled");
+
+    if (typeof attachTo == 'string') {
+        attachTo = document.getElementById(attachTo);
+    }
+
+    if (typeof handler == 'function') {
+        keyEvent.subscribe(handler);
+    } else {
+        keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope);
+    }
+
+    /**
+    * Handles the key event when a key is pressed.
+    * @method handleKeyPress
+    * @param {DOMEvent} e   The keypress DOM event
+    * @param {Object}   obj The DOM event scope object
+    * @private
+    */
+    function handleKeyPress(e, obj) {
+        if (! keyData.shift) {  
+            keyData.shift = false; 
+        }
+        if (! keyData.alt) {    
+            keyData.alt = false;
+        }
+        if (! keyData.ctrl) {
+            keyData.ctrl = false;
+        }
+
+        // check held down modifying keys first
+        if (e.shiftKey == keyData.shift && 
+            e.altKey   == keyData.alt &&
+            e.ctrlKey  == keyData.ctrl) { // if we pass this, all modifiers match
+            
+            var dataItem;
+
+            if (keyData.keys instanceof Array) {
+                for (var i=0;i<keyData.keys.length;i++) {
+                    dataItem = keyData.keys[i];
+
+                    if (dataItem == e.charCode ) {
+                        keyEvent.fire(e.charCode, e);
+                        break;
+                    } else if (dataItem == e.keyCode) {
+                        keyEvent.fire(e.keyCode, e);
+                        break;
+                    }
+                }
+            } else {
+                dataItem = keyData.keys;
+                if (dataItem == e.charCode ) {
+                    keyEvent.fire(e.charCode, e);
+                } else if (dataItem == e.keyCode) {
+                    keyEvent.fire(e.keyCode, e);
+                }
+            }
+        }
+    }
+
+    /**
+    * Enables the KeyListener by attaching the DOM event listeners to the 
+    * target DOM element
+    * @method enable
+    */
+    this.enable = function() {
+        if (! this.enabled) {
+            YAHOO.util.Event.addListener(attachTo, event, handleKeyPress);
+            this.enabledEvent.fire(keyData);
+        }
+        /**
+        * Boolean indicating the enabled/disabled state of the Tooltip
+        * @property enabled
+        * @type Boolean
+        */
+        this.enabled = true;
+    };
+
+    /**
+    * Disables the KeyListener by removing the DOM event listeners from the 
+    * target DOM element
+    * @method disable
+    */
+    this.disable = function() {
+        if (this.enabled) {
+            YAHOO.util.Event.removeListener(attachTo, event, handleKeyPress);
+            this.disabledEvent.fire(keyData);
+        }
+        this.enabled = false;
+    };
+
+    /**
+    * Returns a String representation of the object.
+    * @method toString
+    * @return {String}  The string representation of the KeyListener
+    */ 
+    this.toString = function() {
+        return "KeyListener [" + keyData.keys + "] " + attachTo.tagName + 
+                (attachTo.id ? "[" + attachTo.id + "]" : "");
+    };
+
+};
+
+/**
+* Constant representing the DOM "keydown" event.
+* @property YAHOO.util.KeyListener.KEYDOWN
+* @static
+* @final
+* @type String
+*/
+YAHOO.util.KeyListener.KEYDOWN = "keydown";
+
+/**
+* Constant representing the DOM "keyup" event.
+* @property YAHOO.util.KeyListener.KEYUP
+* @static
+* @final
+* @type String
+*/
+YAHOO.util.KeyListener.KEYUP = "keyup";
+
+/**
+ * keycode constants for a subset of the special keys
+ * @property KEY
+ * @static
+ * @final
+ */
+YAHOO.util.KeyListener.KEY = {
+    ALT          : 18,
+    BACK_SPACE   : 8,
+    CAPS_LOCK    : 20,
+    CONTROL      : 17,
+    DELETE       : 46,
+    DOWN         : 40,
+    END          : 35,
+    ENTER        : 13,
+    ESCAPE       : 27,
+    HOME         : 36,
+    LEFT         : 37,
+    META         : 224,
+    NUM_LOCK     : 144,
+    PAGE_DOWN    : 34,
+    PAGE_UP      : 33, 
+    PAUSE        : 19,
+    PRINTSCREEN  : 44,
+    RIGHT        : 39,
+    SCROLL_LOCK  : 145,
+    SHIFT        : 16,
+    SPACE        : 32,
+    TAB          : 9,
+    UP           : 38
+};
+YAHOO.register("event", YAHOO.util.Event, {version: "2.4.1", build: "742"});
Index: trunk/wb/include/yui/event/README
===================================================================
--- trunk/wb/include/yui/event/README	(nonexistent)
+++ trunk/wb/include/yui/event/README	(revision 601)
@@ -0,0 +1,228 @@
+
+YUI Library - Event - Release Notes
+
+2.4.1
+  * Reverted clearAttributes() change to fix IE memory leak on iframes/windows
+
+2.4.0
+  * getListeners/purgeElement accepts either an element reference or an element id
+  * onAvailable/onContentReady accepts a single id or an array of ids
+  * No longer removing listeners during the unload event for non-IE browsers
+  * IE unload strategy for cross page memory leaks changed from removing each listener
+    to calling clearAttributes()
+  * the DOMReady property is now public
+  * Changed IE onDOMReady approach from the defered script node hack to the doScroll('left') hack
+  * getCharChode now Remaps SHIFT-TAB key code (25) to TAB (9) in Safari
+  * getCharCode provides the proper key codes for page up and page down in Safari
+  * Restored object check to _isValidCollection.
+  * In KeyListener, added keycode constants for some of the special keys.
+  * Rolled back the change added for hacking around IE errors from bubbled 
+    events that originally targeted an ActiveX control.
+
+2.3.1
+  * CustomEvent fire() now catches exceptions thrown by the subscribers so
+    that the rest of the subscribers execute.  The exception is logged and
+    stored in the "lastError" property.
+  * Performance tweak: _isValidCollection fails strings first.
+  * onAvailable/onContentReady will work if an element ref rather than an
+    id is provided.
+  * getListeners: fixed the values for the obj and adjust properties.
+    Added scope property.
+  * Added IE hack for when the event target is something that can't be
+    inspected (like a VML canvas).  In this case the target is set to
+    the element the listener is bound to.
+
+2.3.0
+  * addListener now produces a logger warning if the supplied callback is 
+    undefined.
+  * Reduced the likelihood that window onload listeners fire before
+    onAvailable listeners when the two happen near the same moment.
+  * Fixed an IE error that could occur when the library was injected into 
+    the page after window onload.
+  * The onDOMReady listener will be executed immediately if the
+    DOMReady event had already fired prior to the call.
+  * Unsubscribing custom events within a handler for the event now works 
+    properly.
+  * EventProvider unsubscribeAll will unsubscribe all listeners from
+    all hosted custom events if the type is not specified.
+  * Event.getKeyCode now checks keyCode before charCode for Safari normalization.  
+    Safari arrow key codes are normalized as well.
+  * Broke up the </script> in Event to allow it to be included inline
+
+2.2.2
+  * No change
+
+2.2.1
+  * onAvailable listeners are processed before onContentReady listeners so
+    they fire in the logical order when an element is found to be available 
+    and ready in the same pass.
+  * Added onDOMReady for receiving notification when the DOM is first usable.
+  * In Internet Explorer, onAvailable/onContentReady checks now start when
+    DOMReady fires to reduce the possibility of receiving an "operation
+    aborted" errors when inserting DOM nodes during in the onAvailable/
+    onContentReady listener execution.
+  * CustomEvent subscribers supplying undefined callbacks will be notified
+    when subscribing rather than having an ambiguous error thrown when the
+    event is fired.
+  * Fixed missing html tags in the examples.
+  * POLL_INTERVAL is 10 ms.
+  * YAHOO.util.CustomEvent is now declared before YAHOO.util.Event.
+
+2.2.0
+
+  * YAHOO.util.KeyListener was moved from the container package into event.
+  
+  * The Safari bug that made it so preventDefault would not work with click
+    and double-click listeners on anchor tags was resolved in version 2.0.4.
+    The workaround (using DOM0 events for these instead of the normal DOM2
+    events) is now only used for releases prior to 2.0.4.
+    
+  * getListeners/purgeElement now work correctly with unload listeners
+
+  * When legacyEvents are used (Safari<2.0.4 click events), if there was an
+    existing DOM0 event that the utility replaced, it is executed along with
+    the listeners that were added with the utility.
+
+  * Restored Event.regCE for compatibility with a beta release of the library
+    that in use in an external API.
+
+  * unsubscribe in CustomEvent and EventProvider will remove all listeners
+    if the listener is omitted.
+
+  * Added unsubscribeAll to EventProvider
+
+  * Added Event.lastError which stores the most recent error object if
+    a browser-specific add/remove listener call throws an exception.
+
+0.12.2
+
+  * Fixed a bug introduced in 0.12.1 release caused nested onAvailable
+    calls to fail.
+
+0.12.1
+
+  * If an error is thrown during the browser-specific add/remove lister call,
+    addListener/removeListener will catch the error and return false.
+
+  * onAvailable array items are nulled out instead of deleted when completed to
+    get around an Opera issue introduced in a recent version of the browser.
+
+0.12.0
+
+   * If the function argument is not provided to Event.removeListener, all
+     all listeners for the specified event type on the element will be removed.
+
+   * CustomEvent now has an optional parameter that defines the signature of
+     the listeners for this event.  Two signatures are supported:
+
+       YAHOO.util.CustomEvent.LIST:
+         param1: event name
+         param2: array of arguments provided to fire()
+         param3: <optional> the custom object supplied to subscribe()
+
+       YAHOO.util.CustomEvent.FLAT:
+         param1: the first argument provided to fire()
+         param2: <optional> the custom object supplied to subscribe()
+
+     The new flat signature makes it possible to provide a better API 
+     when using custom events, and it makes it possible to transparently 
+     wrap DOM events.
+   
+   * The parameters for overriding scope in both Event.addListener, and
+     CustomEvent.subscribe have been augmented.  In addition to the 
+     previous behavior where a true value would make the previous parameter
+     the execution scope, an object can be supplied instead.  If an object
+     is provided, that object becomes the scope obj.  This makes it possible 
+     to pass a both a custom object and adjust the scope to a different object.
+
+   * Added EventProvider, which is a wrapper for CustomEvent that makes it
+     possible to subscribe to events by name, whether or not the event has
+     been created.  This class was designed to be used with YAHOO.augment.
+     EventProvider custom events are created with the new FLAT listener
+     signature.
+
+   * CustomEvent subscribers can return false to stop the propagation of
+     the event.
+
+   * CustomEvents now have an onSubscribe custom event that can used to the
+     case where a subscriber subscribes to an one-time event that has already
+     happened.  Also provides a way for the implementer to defer initialization
+     logic until after the first subscription.
+
+   * Event.getCharCode now always returns keyCode if charCode is not available.
+
+   * Added Event.onContentReady, which is similar to onAvailable, but it also
+     checks simblings to try to determine when the element's children are
+     available.
+
+0.11.4
+
+   * Fixed a memory leak in IE6 that occurred when the utility was hosted in 
+     an iframe.
+
+   * Fixed an issue with Safari click listeners when listeners were removed.
+
+0.11.3
+
+   * The listener cache is now pruned when events are removed.  This fixes
+     a performance issue when adding many listeners, removing them, and
+     adding them again repeatedly.
+
+   * Safari click listeners will work correctly if a bound element is removed
+     from the DOM and a new element with the same ID is added.
+
+   * Removed the code that automatically unsubscribed custom event listeners.
+
+0.11.0
+
+   * Added Event.purgeElement which will remove all listeners added via 
+     addListener from the supplied element.  If an optional "type" parameter
+     is supplied, only events of that type will be removed.  Optionally, the
+     purge can be performed recursively on the element's children as well.
+
+   * Added Event.getListeners which will return all listeners attached to 
+     a given element.. either all listeners or listeners of a specific type.
+
+   * getTarget now automatically resolves text nodes.  The optional parameter
+     for this feature is now deprecated.
+
+   * getRelatedTarget now resolves text nodes for the browsers that return the
+     text node rather than its host HTML element.
+
+   * CustomEvent now logs the custom event activity if the logger widget is available
+
+0.10.0
+
+   * Added Safari dblclick to the list of legacy events.
+
+   * When multiple identical event handlers are defined, multiple calls
+     to removeListener can now remove all of them.
+
+   * removeListener works properly for unload events
+
+   * Legacy event bookkeeping is more efficient, improving the performance for
+     adding Safari click events.
+
+   * _unload() is more efficient, improving the page transition experience in 
+     Safari in particular.
+
+   * addListener, removeListener now return false if the function argument is
+     not valid.
+
+   * Fixed an operator precedence issue in getCharCode.
+
+   * Added Event.getXY, which returns [Event.getPageX(e), Event.getPageY(e)]
+
+   * Added Event.onAvailable, which will execute the callback when the element
+     with the supplied id is found.  Currently searches periodically until the
+     window load event or for up to 10 seconds after the onAvailable method 
+     was executed.
+
+   * The lazy listener attachment process now will poll beyond the window load
+     event in order to better handle the case when a listener is defined
+     late in the page but before the element is in the dom.
+
+   * Fixed browser detection for Opera installations reporting as IE.
+
+   * It is now possible to remove and re-add legacy events (Safari click event).
+
Index: trunk/wb/include/yui/event/event-min.js
===================================================================
--- trunk/wb/include/yui/event/event-min.js	(nonexistent)
+++ trunk/wb/include/yui/event/event-min.js	(revision 601)
@@ -0,0 +1,9 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+YAHOO.util.CustomEvent=function(D,B,C,A){this.type=D;this.scope=B||window;this.silent=C;this.signature=A||YAHOO.util.CustomEvent.LIST;this.subscribers=[];if(!this.silent){}var E="_YUICEOnSubscribe";if(D!==E){this.subscribeEvent=new YAHOO.util.CustomEvent(E,this,true);}this.lastError=null;};YAHOO.util.CustomEvent.LIST=0;YAHOO.util.CustomEvent.FLAT=1;YAHOO.util.CustomEvent.prototype={subscribe:function(B,C,A){if(!B){throw new Error("Invalid callback for subscriber to '"+this.type+"'");}if(this.subscribeEvent){this.subscribeEvent.fire(B,C,A);}this.subscribers.push(new YAHOO.util.Subscriber(B,C,A));},unsubscribe:function(D,F){if(!D){return this.unsubscribeAll();}var E=false;for(var B=0,A=this.subscribers.length;B<A;++B){var C=this.subscribers[B];if(C&&C.contains(D,F)){this._delete(B);E=true;}}return E;},fire:function(){var D=this.subscribers.length;if(!D&&this.silent){return true;}var H=[],F=true,C,I=false;for(C=0;C<arguments.length;++C){H.push(arguments[C]);}if(!this.silent){}for(C=0;C<D;++C){var L=this.subscribers[C];if(!L){I=true;}else{if(!this.silent){}var K=L.getScope(this.scope);if(this.signature==YAHOO.util.CustomEvent.FLAT){var A=null;if(H.length>0){A=H[0];}try{F=L.fn.call(K,A,L.obj);}catch(E){this.lastError=E;}}else{try{F=L.fn.call(K,this.type,H,L.obj);}catch(G){this.lastError=G;}}if(false===F){if(!this.silent){}return false;}}}if(I){var J=[],B=this.subscribers;for(C=0,D=B.length;C<D;C=C+1){J.push(B[C]);}this.subscribers=J;}return true;},unsubscribeAll:function(){for(var B=0,A=this.subscribers.length;B<A;++B){this._delete(A-1-B);}this.subscribers=[];return B;},_delete:function(A){var B=this.subscribers[A];if(B){delete B.fn;delete B.obj;}this.subscribers[A]=null;},toString:function(){return"CustomEvent: '"+this.type+"', scope: "+this.scope;}};YAHOO.util.Subscriber=function(B,C,A){this.fn=B;this.obj=YAHOO.lang.isUndefined(C)?null:C;this.override=A;};YAHOO.util.Subscriber.prototype.getScope=function(A){if(this.override){if(this.override===true){return this.obj;}else{return this.override;}}return A;};YAHOO.util.Subscriber.prototype.contains=function(A,B){if(B){return(this.fn==A&&this.obj==B);}else{return(this.fn==A);}};YAHOO.util.Subscriber.prototype.toString=function(){return"Subscriber { obj: "+this.obj+", override: "+(this.override||"no")+" }";};if(!YAHOO.util.Event){YAHOO.util.Event=function(){var H=false;var I=[];var J=[];var G=[];var E=[];var C=0;var F=[];var B=[];var A=0;var D={63232:38,63233:40,63234:37,63235:39,63276:33,63277:34,25:9};return{POLL_RETRYS:4000,POLL_INTERVAL:10,EL:0,TYPE:1,FN:2,WFN:3,UNLOAD_OBJ:3,ADJ_SCOPE:4,OBJ:5,OVERRIDE:6,lastError:null,isSafari:YAHOO.env.ua.webkit,webkit:YAHOO.env.ua.webkit,isIE:YAHOO.env.ua.ie,_interval:null,_dri:null,DOMReady:false,startInterval:function(){if(!this._interval){var K=this;var L=function(){K._tryPreloadAttach();};this._interval=setInterval(L,this.POLL_INTERVAL);}},onAvailable:function(P,M,Q,O,N){var K=(YAHOO.lang.isString(P))?[P]:P;for(var L=0;L<K.length;L=L+1){F.push({id:K[L],fn:M,obj:Q,override:O,checkReady:N});}C=this.POLL_RETRYS;this.startInterval();},onContentReady:function(M,K,N,L){this.onAvailable(M,K,N,L,true);},onDOMReady:function(K,M,L){if(this.DOMReady){setTimeout(function(){var N=window;if(L){if(L===true){N=M;}else{N=L;}}K.call(N,"DOMReady",[],M);},0);}else{this.DOMReadyEvent.subscribe(K,M,L);}},addListener:function(M,K,V,Q,L){if(!V||!V.call){return false;}if(this._isValidCollection(M)){var W=true;for(var R=0,T=M.length;R<T;++R){W=this.on(M[R],K,V,Q,L)&&W;}return W;}else{if(YAHOO.lang.isString(M)){var P=this.getEl(M);if(P){M=P;}else{this.onAvailable(M,function(){YAHOO.util.Event.on(M,K,V,Q,L);});return true;}}}if(!M){return false;}if("unload"==K&&Q!==this){J[J.length]=[M,K,V,Q,L];return true;}var Y=M;if(L){if(L===true){Y=Q;}else{Y=L;}}var N=function(Z){return V.call(Y,YAHOO.util.Event.getEvent(Z,M),Q);};var X=[M,K,V,N,Y,Q,L];var S=I.length;I[S]=X;if(this.useLegacyEvent(M,K)){var O=this.getLegacyIndex(M,K);if(O==-1||M!=G[O][0]){O=G.length;B[M.id+K]=O;G[O]=[M,K,M["on"+K]];E[O]=[];M["on"+K]=function(Z){YAHOO.util.Event.fireLegacyEvent(YAHOO.util.Event.getEvent(Z),O);};}E[O].push(X);}else{try{this._simpleAdd(M,K,N,false);}catch(U){this.lastError=U;this.removeListener(M,K,V);return false;}}return true;},fireLegacyEvent:function(O,M){var Q=true,K,S,R,T,P;S=E[M];for(var L=0,N=S.length;L<N;++L){R=S[L];if(R&&R[this.WFN]){T=R[this.ADJ_SCOPE];P=R[this.WFN].call(T,O);Q=(Q&&P);}}K=G[M];if(K&&K[2]){K[2](O);}return Q;},getLegacyIndex:function(L,M){var K=this.generateId(L)+M;if(typeof B[K]=="undefined"){return -1;}else{return B[K];}},useLegacyEvent:function(L,M){if(this.webkit&&("click"==M||"dblclick"==M)){var K=parseInt(this.webkit,10);if(!isNaN(K)&&K<418){return true;}}return false;},removeListener:function(L,K,T){var O,R,V;if(typeof L=="string"){L=this.getEl(L);}else{if(this._isValidCollection(L)){var U=true;for(O=0,R=L.length;O<R;++O){U=(this.removeListener(L[O],K,T)&&U);}return U;}}if(!T||!T.call){return this.purgeElement(L,false,K);}if("unload"==K){for(O=0,R=J.length;O<R;O++){V=J[O];if(V&&V[0]==L&&V[1]==K&&V[2]==T){J[O]=null;return true;}}return false;}var P=null;var Q=arguments[3];if("undefined"===typeof Q){Q=this._getCacheIndex(L,K,T);}if(Q>=0){P=I[Q];}if(!L||!P){return false;}if(this.useLegacyEvent(L,K)){var N=this.getLegacyIndex(L,K);var M=E[N];if(M){for(O=0,R=M.length;O<R;++O){V=M[O];if(V&&V[this.EL]==L&&V[this.TYPE]==K&&V[this.FN]==T){M[O]=null;break;}}}}else{try{this._simpleRemove(L,K,P[this.WFN],false);}catch(S){this.lastError=S;return false;}}delete I[Q][this.WFN];delete I[Q][this.FN];I[Q]=null;return true;},getTarget:function(M,L){var K=M.target||M.srcElement;return this.resolveTextNode(K);},resolveTextNode:function(K){if(K&&3==K.nodeType){return K.parentNode;}else{return K;}},getPageX:function(L){var K=L.pageX;if(!K&&0!==K){K=L.clientX||0;if(this.isIE){K+=this._getScrollLeft();}}return K;},getPageY:function(K){var L=K.pageY;if(!L&&0!==L){L=K.clientY||0;if(this.isIE){L+=this._getScrollTop();}}return L;},getXY:function(K){return[this.getPageX(K),this.getPageY(K)];
+},getRelatedTarget:function(L){var K=L.relatedTarget;if(!K){if(L.type=="mouseout"){K=L.toElement;}else{if(L.type=="mouseover"){K=L.fromElement;}}}return this.resolveTextNode(K);},getTime:function(M){if(!M.time){var L=new Date().getTime();try{M.time=L;}catch(K){this.lastError=K;return L;}}return M.time;},stopEvent:function(K){this.stopPropagation(K);this.preventDefault(K);},stopPropagation:function(K){if(K.stopPropagation){K.stopPropagation();}else{K.cancelBubble=true;}},preventDefault:function(K){if(K.preventDefault){K.preventDefault();}else{K.returnValue=false;}},getEvent:function(M,K){var L=M||window.event;if(!L){var N=this.getEvent.caller;while(N){L=N.arguments[0];if(L&&Event==L.constructor){break;}N=N.caller;}}return L;},getCharCode:function(L){var K=L.keyCode||L.charCode||0;if(YAHOO.env.ua.webkit&&(K in D)){K=D[K];}return K;},_getCacheIndex:function(O,P,N){for(var M=0,L=I.length;M<L;++M){var K=I[M];if(K&&K[this.FN]==N&&K[this.EL]==O&&K[this.TYPE]==P){return M;}}return -1;},generateId:function(K){var L=K.id;if(!L){L="yuievtautoid-"+A;++A;K.id=L;}return L;},_isValidCollection:function(L){try{return(L&&typeof L!=="string"&&L.length&&!L.tagName&&!L.alert&&typeof L[0]!=="undefined");}catch(K){return false;}},elCache:{},getEl:function(K){return(typeof K==="string")?document.getElementById(K):K;},clearCache:function(){},DOMReadyEvent:new YAHOO.util.CustomEvent("DOMReady",this),_load:function(L){if(!H){H=true;var K=YAHOO.util.Event;K._ready();K._tryPreloadAttach();}},_ready:function(L){var K=YAHOO.util.Event;if(!K.DOMReady){K.DOMReady=true;K.DOMReadyEvent.fire();K._simpleRemove(document,"DOMContentLoaded",K._ready);}},_tryPreloadAttach:function(){if(this.locked){return false;}if(this.isIE){if(!this.DOMReady){this.startInterval();return false;}}this.locked=true;var P=!H;if(!P){P=(C>0);}var O=[];var Q=function(S,T){var R=S;if(T.override){if(T.override===true){R=T.obj;}else{R=T.override;}}T.fn.call(R,T.obj);};var L,K,N,M;for(L=0,K=F.length;L<K;++L){N=F[L];if(N&&!N.checkReady){M=this.getEl(N.id);if(M){Q(M,N);F[L]=null;}else{O.push(N);}}}for(L=0,K=F.length;L<K;++L){N=F[L];if(N&&N.checkReady){M=this.getEl(N.id);if(M){if(H||M.nextSibling){Q(M,N);F[L]=null;}}else{O.push(N);}}}C=(O.length===0)?0:C-1;if(P){this.startInterval();}else{clearInterval(this._interval);this._interval=null;}this.locked=false;return true;},purgeElement:function(O,P,R){var M=(YAHOO.lang.isString(O))?this.getEl(O):O;var Q=this.getListeners(M,R),N,K;if(Q){for(N=0,K=Q.length;N<K;++N){var L=Q[N];this.removeListener(M,L.type,L.fn,L.index);}}if(P&&M&&M.childNodes){for(N=0,K=M.childNodes.length;N<K;++N){this.purgeElement(M.childNodes[N],P,R);}}},getListeners:function(M,K){var P=[],L;if(!K){L=[I,J];}else{if(K==="unload"){L=[J];}else{L=[I];}}var R=(YAHOO.lang.isString(M))?this.getEl(M):M;for(var O=0;O<L.length;O=O+1){var T=L[O];if(T&&T.length>0){for(var Q=0,S=T.length;Q<S;++Q){var N=T[Q];if(N&&N[this.EL]===R&&(!K||K===N[this.TYPE])){P.push({type:N[this.TYPE],fn:N[this.FN],obj:N[this.OBJ],adjust:N[this.OVERRIDE],scope:N[this.ADJ_SCOPE],index:Q});}}}}return(P.length)?P:null;},_unload:function(R){var Q=YAHOO.util.Event,O,N,L,K,M;for(O=0,K=J.length;O<K;++O){L=J[O];if(L){var P=window;if(L[Q.ADJ_SCOPE]){if(L[Q.ADJ_SCOPE]===true){P=L[Q.UNLOAD_OBJ];}else{P=L[Q.ADJ_SCOPE];}}L[Q.FN].call(P,Q.getEvent(R,L[Q.EL]),L[Q.UNLOAD_OBJ]);J[O]=null;L=null;P=null;}}J=null;if(YAHOO.env.ua.ie&&I&&I.length>0){N=I.length;while(N){M=N-1;L=I[M];if(L){Q.removeListener(L[Q.EL],L[Q.TYPE],L[Q.FN],M);}N--;}L=null;}G=null;Q._simpleRemove(window,"unload",Q._unload);},_getScrollLeft:function(){return this._getScroll()[1];},_getScrollTop:function(){return this._getScroll()[0];},_getScroll:function(){var K=document.documentElement,L=document.body;if(K&&(K.scrollTop||K.scrollLeft)){return[K.scrollTop,K.scrollLeft];}else{if(L){return[L.scrollTop,L.scrollLeft];}else{return[0,0];}}},regCE:function(){},_simpleAdd:function(){if(window.addEventListener){return function(M,N,L,K){M.addEventListener(N,L,(K));};}else{if(window.attachEvent){return function(M,N,L,K){M.attachEvent("on"+N,L);};}else{return function(){};}}}(),_simpleRemove:function(){if(window.removeEventListener){return function(M,N,L,K){M.removeEventListener(N,L,(K));};}else{if(window.detachEvent){return function(L,M,K){L.detachEvent("on"+M,K);};}else{return function(){};}}}()};}();(function(){var A=YAHOO.util.Event;A.on=A.addListener;if(A.isIE){YAHOO.util.Event.onDOMReady(YAHOO.util.Event._tryPreloadAttach,YAHOO.util.Event,true);A._dri=setInterval(function(){var C=document.createElement("p");try{C.doScroll("left");clearInterval(A._dri);A._dri=null;A._ready();C=null;}catch(B){C=null;}},A.POLL_INTERVAL);}else{if(A.webkit){A._dri=setInterval(function(){var B=document.readyState;if("loaded"==B||"complete"==B){clearInterval(A._dri);A._dri=null;A._ready();}},A.POLL_INTERVAL);}else{A._simpleAdd(document,"DOMContentLoaded",A._ready);}}A._simpleAdd(window,"load",A._load);A._simpleAdd(window,"unload",A._unload);A._tryPreloadAttach();})();}YAHOO.util.EventProvider=function(){};YAHOO.util.EventProvider.prototype={__yui_events:null,__yui_subscribers:null,subscribe:function(A,C,F,E){this.__yui_events=this.__yui_events||{};var D=this.__yui_events[A];if(D){D.subscribe(C,F,E);}else{this.__yui_subscribers=this.__yui_subscribers||{};var B=this.__yui_subscribers;if(!B[A]){B[A]=[];}B[A].push({fn:C,obj:F,override:E});}},unsubscribe:function(C,E,G){this.__yui_events=this.__yui_events||{};var A=this.__yui_events;if(C){var F=A[C];if(F){return F.unsubscribe(E,G);}}else{var B=true;for(var D in A){if(YAHOO.lang.hasOwnProperty(A,D)){B=B&&A[D].unsubscribe(E,G);}}return B;}return false;},unsubscribeAll:function(A){return this.unsubscribe(A);},createEvent:function(G,D){this.__yui_events=this.__yui_events||{};var A=D||{};var I=this.__yui_events;if(I[G]){}else{var H=A.scope||this;var E=(A.silent);var B=new YAHOO.util.CustomEvent(G,H,E,YAHOO.util.CustomEvent.FLAT);I[G]=B;if(A.onSubscribeCallback){B.subscribeEvent.subscribe(A.onSubscribeCallback);}this.__yui_subscribers=this.__yui_subscribers||{};
+var F=this.__yui_subscribers[G];if(F){for(var C=0;C<F.length;++C){B.subscribe(F[C].fn,F[C].obj,F[C].override);}}}return I[G];},fireEvent:function(E,D,A,C){this.__yui_events=this.__yui_events||{};var G=this.__yui_events[E];if(!G){return null;}var B=[];for(var F=1;F<arguments.length;++F){B.push(arguments[F]);}return G.fire.apply(G,B);},hasEvent:function(A){if(this.__yui_events){if(this.__yui_events[A]){return true;}}return false;}};YAHOO.util.KeyListener=function(A,F,B,C){if(!A){}else{if(!F){}else{if(!B){}}}if(!C){C=YAHOO.util.KeyListener.KEYDOWN;}var D=new YAHOO.util.CustomEvent("keyPressed");this.enabledEvent=new YAHOO.util.CustomEvent("enabled");this.disabledEvent=new YAHOO.util.CustomEvent("disabled");if(typeof A=="string"){A=document.getElementById(A);}if(typeof B=="function"){D.subscribe(B);}else{D.subscribe(B.fn,B.scope,B.correctScope);}function E(J,I){if(!F.shift){F.shift=false;}if(!F.alt){F.alt=false;}if(!F.ctrl){F.ctrl=false;}if(J.shiftKey==F.shift&&J.altKey==F.alt&&J.ctrlKey==F.ctrl){var G;if(F.keys instanceof Array){for(var H=0;H<F.keys.length;H++){G=F.keys[H];if(G==J.charCode){D.fire(J.charCode,J);break;}else{if(G==J.keyCode){D.fire(J.keyCode,J);break;}}}}else{G=F.keys;if(G==J.charCode){D.fire(J.charCode,J);}else{if(G==J.keyCode){D.fire(J.keyCode,J);}}}}}this.enable=function(){if(!this.enabled){YAHOO.util.Event.addListener(A,C,E);this.enabledEvent.fire(F);}this.enabled=true;};this.disable=function(){if(this.enabled){YAHOO.util.Event.removeListener(A,C,E);this.disabledEvent.fire(F);}this.enabled=false;};this.toString=function(){return"KeyListener ["+F.keys+"] "+A.tagName+(A.id?"["+A.id+"]":"");};};YAHOO.util.KeyListener.KEYDOWN="keydown";YAHOO.util.KeyListener.KEYUP="keyup";YAHOO.util.KeyListener.KEY={ALT:18,BACK_SPACE:8,CAPS_LOCK:20,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,META:224,NUM_LOCK:144,PAGE_DOWN:34,PAGE_UP:33,PAUSE:19,PRINTSCREEN:44,RIGHT:39,SCROLL_LOCK:145,SHIFT:16,SPACE:32,TAB:9,UP:38};YAHOO.register("event",YAHOO.util.Event,{version:"2.4.1",build:"742"});
\ No newline at end of file
Index: trunk/wb/include/yui/dragdrop/dragdrop-debug.js
===================================================================
--- trunk/wb/include/yui/dragdrop/dragdrop-debug.js	(nonexistent)
+++ trunk/wb/include/yui/dragdrop/dragdrop-debug.js	(revision 601)
@@ -0,0 +1,3255 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+/**
+ * The drag and drop utility provides a framework for building drag and drop
+ * applications.  In addition to enabling drag and drop for specific elements,
+ * the drag and drop elements are tracked by the manager class, and the
+ * interactions between the various elements are tracked during the drag and
+ * the implementing code is notified about these important moments.
+ * @module dragdrop
+ * @title Drag and Drop
+ * @requires yahoo,dom,event
+ * @namespace YAHOO.util
+ */
+
+// Only load the library once.  Rewriting the manager class would orphan 
+// existing drag and drop instances.
+if (!YAHOO.util.DragDropMgr) {
+
+/**
+ * DragDropMgr is a singleton that tracks the element interaction for 
+ * all DragDrop items in the window.  Generally, you will not call 
+ * this class directly, but it does have helper methods that could 
+ * be useful in your DragDrop implementations.
+ * @class DragDropMgr
+ * @static
+ */
+YAHOO.util.DragDropMgr = function() {
+
+    var Event = YAHOO.util.Event;
+
+    return {
+
+        /**
+         * Two dimensional Array of registered DragDrop objects.  The first 
+         * dimension is the DragDrop item group, the second the DragDrop 
+         * object.
+         * @property ids
+         * @type {string: string}
+         * @private
+         * @static
+         */
+        ids: {},
+
+        /**
+         * Array of element ids defined as drag handles.  Used to determine 
+         * if the element that generated the mousedown event is actually the 
+         * handle and not the html element itself.
+         * @property handleIds
+         * @type {string: string}
+         * @private
+         * @static
+         */
+        handleIds: {},
+
+        /**
+         * the DragDrop object that is currently being dragged
+         * @property dragCurrent
+         * @type DragDrop
+         * @private
+         * @static
+         **/
+        dragCurrent: null,
+
+        /**
+         * the DragDrop object(s) that are being hovered over
+         * @property dragOvers
+         * @type Array
+         * @private
+         * @static
+         */
+        dragOvers: {},
+
+        /**
+         * the X distance between the cursor and the object being dragged
+         * @property deltaX
+         * @type int
+         * @private
+         * @static
+         */
+        deltaX: 0,
+
+        /**
+         * the Y distance between the cursor and the object being dragged
+         * @property deltaY
+         * @type int
+         * @private
+         * @static
+         */
+        deltaY: 0,
+
+        /**
+         * Flag to determine if we should prevent the default behavior of the
+         * events we define. By default this is true, but this can be set to 
+         * false if you need the default behavior (not recommended)
+         * @property preventDefault
+         * @type boolean
+         * @static
+         */
+        preventDefault: true,
+
+        /**
+         * Flag to determine if we should stop the propagation of the events 
+         * we generate. This is true by default but you may want to set it to
+         * false if the html element contains other features that require the
+         * mouse click.
+         * @property stopPropagation
+         * @type boolean
+         * @static
+         */
+        stopPropagation: true,
+
+        /**
+         * Internal flag that is set to true when drag and drop has been
+         * initialized
+         * @property initialized
+         * @private
+         * @static
+         */
+        initialized: false,
+
+        /**
+         * All drag and drop can be disabled.
+         * @property locked
+         * @private
+         * @static
+         */
+        locked: false,
+
+        /**
+         * Provides additional information about the the current set of
+         * interactions.  Can be accessed from the event handlers. It
+         * contains the following properties:
+         *
+         *       out:       onDragOut interactions
+         *       enter:     onDragEnter interactions
+         *       over:      onDragOver interactions
+         *       drop:      onDragDrop interactions
+         *       point:     The location of the cursor
+         *       draggedRegion: The location of dragged element at the time
+         *                      of the interaction
+         *       sourceRegion: The location of the source elemtn at the time
+         *                     of the interaction
+         *       validDrop: boolean
+         * @property interactionInfo
+         * @type object
+         * @static
+         */
+        interactionInfo: null,
+
+        /**
+         * Called the first time an element is registered.
+         * @method init
+         * @private
+         * @static
+         */
+        init: function() {
+            this.initialized = true;
+        },
+
+        /**
+         * In point mode, drag and drop interaction is defined by the 
+         * location of the cursor during the drag/drop
+         * @property POINT
+         * @type int
+         * @static
+         * @final
+         */
+        POINT: 0,
+
+        /**
+         * In intersect mode, drag and drop interaction is defined by the 
+         * cursor position or the amount of overlap of two or more drag and 
+         * drop objects.
+         * @property INTERSECT
+         * @type int
+         * @static
+         * @final
+         */
+        INTERSECT: 1,
+
+        /**
+         * In intersect mode, drag and drop interaction is defined only by the 
+         * overlap of two or more drag and drop objects.
+         * @property STRICT_INTERSECT
+         * @type int
+         * @static
+         * @final
+         */
+        STRICT_INTERSECT: 2,
+
+        /**
+         * The current drag and drop mode.  Default: POINT
+         * @property mode
+         * @type int
+         * @static
+         */
+        mode: 0,
+
+        /**
+         * Runs method on all drag and drop objects
+         * @method _execOnAll
+         * @private
+         * @static
+         */
+        _execOnAll: function(sMethod, args) {
+            for (var i in this.ids) {
+                for (var j in this.ids[i]) {
+                    var oDD = this.ids[i][j];
+                    if (! this.isTypeOfDD(oDD)) {
+                        continue;
+                    }
+                    oDD[sMethod].apply(oDD, args);
+                }
+            }
+        },
+
+        /**
+         * Drag and drop initialization.  Sets up the global event handlers
+         * @method _onLoad
+         * @private
+         * @static
+         */
+        _onLoad: function() {
+
+            this.init();
+
+            YAHOO.log("DragDropMgr onload", "info", "DragDropMgr");
+
+            Event.on(document, "mouseup",   this.handleMouseUp, this, true);
+            Event.on(document, "mousemove", this.handleMouseMove, this, true);
+            Event.on(window,   "unload",    this._onUnload, this, true);
+            Event.on(window,   "resize",    this._onResize, this, true);
+            // Event.on(window,   "mouseout",    this._test);
+
+        },
+
+        /**
+         * Reset constraints on all drag and drop objs
+         * @method _onResize
+         * @private
+         * @static
+         */
+        _onResize: function(e) {
+            YAHOO.log("window resize", "info", "DragDropMgr");
+            this._execOnAll("resetConstraints", []);
+        },
+
+        /**
+         * Lock all drag and drop functionality
+         * @method lock
+         * @static
+         */
+        lock: function() { this.locked = true; },
+
+        /**
+         * Unlock all drag and drop functionality
+         * @method unlock
+         * @static
+         */
+        unlock: function() { this.locked = false; },
+
+        /**
+         * Is drag and drop locked?
+         * @method isLocked
+         * @return {boolean} True if drag and drop is locked, false otherwise.
+         * @static
+         */
+        isLocked: function() { return this.locked; },
+
+        /**
+         * Location cache that is set for all drag drop objects when a drag is
+         * initiated, cleared when the drag is finished.
+         * @property locationCache
+         * @private
+         * @static
+         */
+        locationCache: {},
+
+        /**
+         * Set useCache to false if you want to force object the lookup of each
+         * drag and drop linked element constantly during a drag.
+         * @property useCache
+         * @type boolean
+         * @static
+         */
+        useCache: true,
+
+        /**
+         * The number of pixels that the mouse needs to move after the 
+         * mousedown before the drag is initiated.  Default=3;
+         * @property clickPixelThresh
+         * @type int
+         * @static
+         */
+        clickPixelThresh: 3,
+
+        /**
+         * The number of milliseconds after the mousedown event to initiate the
+         * drag if we don't get a mouseup event. Default=1000
+         * @property clickTimeThresh
+         * @type int
+         * @static
+         */
+        clickTimeThresh: 1000,
+
+        /**
+         * Flag that indicates that either the drag pixel threshold or the 
+         * mousdown time threshold has been met
+         * @property dragThreshMet
+         * @type boolean
+         * @private
+         * @static
+         */
+        dragThreshMet: false,
+
+        /**
+         * Timeout used for the click time threshold
+         * @property clickTimeout
+         * @type Object
+         * @private
+         * @static
+         */
+        clickTimeout: null,
+
+        /**
+         * The X position of the mousedown event stored for later use when a 
+         * drag threshold is met.
+         * @property startX
+         * @type int
+         * @private
+         * @static
+         */
+        startX: 0,
+
+        /**
+         * The Y position of the mousedown event stored for later use when a 
+         * drag threshold is met.
+         * @property startY
+         * @type int
+         * @private
+         * @static
+         */
+        startY: 0,
+
+        /**
+         * Flag to determine if the drag event was fired from the click timeout and
+         * not the mouse move threshold.
+         * @property fromTimeout
+         * @type boolean
+         * @private
+         * @static
+         */
+        fromTimeout: false,
+
+        /**
+         * Each DragDrop instance must be registered with the DragDropMgr.  
+         * This is executed in DragDrop.init()
+         * @method regDragDrop
+         * @param {DragDrop} oDD the DragDrop object to register
+         * @param {String} sGroup the name of the group this element belongs to
+         * @static
+         */
+        regDragDrop: function(oDD, sGroup) {
+            if (!this.initialized) { this.init(); }
+            
+            if (!this.ids[sGroup]) {
+                this.ids[sGroup] = {};
+            }
+            this.ids[sGroup][oDD.id] = oDD;
+        },
+
+        /**
+         * Removes the supplied dd instance from the supplied group. Executed
+         * by DragDrop.removeFromGroup, so don't call this function directly.
+         * @method removeDDFromGroup
+         * @private
+         * @static
+         */
+        removeDDFromGroup: function(oDD, sGroup) {
+            if (!this.ids[sGroup]) {
+                this.ids[sGroup] = {};
+            }
+
+            var obj = this.ids[sGroup];
+            if (obj && obj[oDD.id]) {
+                delete obj[oDD.id];
+            }
+        },
+
+        /**
+         * Unregisters a drag and drop item.  This is executed in 
+         * DragDrop.unreg, use that method instead of calling this directly.
+         * @method _remove
+         * @private
+         * @static
+         */
+        _remove: function(oDD) {
+            for (var g in oDD.groups) {
+                if (g && this.ids[g][oDD.id]) {
+                    delete this.ids[g][oDD.id];
+                    //YAHOO.log("NEW LEN " + this.ids.length, "warn");
+                }
+            }
+            delete this.handleIds[oDD.id];
+        },
+
+        /**
+         * Each DragDrop handle element must be registered.  This is done
+         * automatically when executing DragDrop.setHandleElId()
+         * @method regHandle
+         * @param {String} sDDId the DragDrop id this element is a handle for
+         * @param {String} sHandleId the id of the element that is the drag 
+         * handle
+         * @static
+         */
+        regHandle: function(sDDId, sHandleId) {
+            if (!this.handleIds[sDDId]) {
+                this.handleIds[sDDId] = {};
+            }
+            this.handleIds[sDDId][sHandleId] = sHandleId;
+        },
+
+        /**
+         * Utility function to determine if a given element has been 
+         * registered as a drag drop item.
+         * @method isDragDrop
+         * @param {String} id the element id to check
+         * @return {boolean} true if this element is a DragDrop item, 
+         * false otherwise
+         * @static
+         */
+        isDragDrop: function(id) {
+            return ( this.getDDById(id) ) ? true : false;
+        },
+
+        /**
+         * Returns the drag and drop instances that are in all groups the
+         * passed in instance belongs to.
+         * @method getRelated
+         * @param {DragDrop} p_oDD the obj to get related data for
+         * @param {boolean} bTargetsOnly if true, only return targetable objs
+         * @return {DragDrop[]} the related instances
+         * @static
+         */
+        getRelated: function(p_oDD, bTargetsOnly) {
+            var oDDs = [];
+            for (var i in p_oDD.groups) {
+                for (var j in this.ids[i]) {
+                    var dd = this.ids[i][j];
+                    if (! this.isTypeOfDD(dd)) {
+                        continue;
+                    }
+                    if (!bTargetsOnly || dd.isTarget) {
+                        oDDs[oDDs.length] = dd;
+                    }
+                }
+            }
+
+            return oDDs;
+        },
+
+        /**
+         * Returns true if the specified dd target is a legal target for 
+         * the specifice drag obj
+         * @method isLegalTarget
+         * @param {DragDrop} the drag obj
+         * @param {DragDrop} the target
+         * @return {boolean} true if the target is a legal target for the 
+         * dd obj
+         * @static
+         */
+        isLegalTarget: function (oDD, oTargetDD) {
+            var targets = this.getRelated(oDD, true);
+            for (var i=0, len=targets.length;i<len;++i) {
+                if (targets[i].id == oTargetDD.id) {
+                    return true;
+                }
+            }
+
+            return false;
+        },
+
+        /**
+         * My goal is to be able to transparently determine if an object is
+         * typeof DragDrop, and the exact subclass of DragDrop.  typeof 
+         * returns "object", oDD.constructor.toString() always returns
+         * "DragDrop" and not the name of the subclass.  So for now it just
+         * evaluates a well-known variable in DragDrop.
+         * @method isTypeOfDD
+         * @param {Object} the object to evaluate
+         * @return {boolean} true if typeof oDD = DragDrop
+         * @static
+         */
+        isTypeOfDD: function (oDD) {
+            return (oDD && oDD.__ygDragDrop);
+        },
+
+        /**
+         * Utility function to determine if a given element has been 
+         * registered as a drag drop handle for the given Drag Drop object.
+         * @method isHandle
+         * @param {String} id the element id to check
+         * @return {boolean} true if this element is a DragDrop handle, false 
+         * otherwise
+         * @static
+         */
+        isHandle: function(sDDId, sHandleId) {
+            return ( this.handleIds[sDDId] && 
+                            this.handleIds[sDDId][sHandleId] );
+        },
+
+        /**
+         * Returns the DragDrop instance for a given id
+         * @method getDDById
+         * @param {String} id the id of the DragDrop object
+         * @return {DragDrop} the drag drop object, null if it is not found
+         * @static
+         */
+        getDDById: function(id) {
+            for (var i in this.ids) {
+                if (this.ids[i][id]) {
+                    return this.ids[i][id];
+                }
+            }
+            return null;
+        },
+
+        /**
+         * Fired after a registered DragDrop object gets the mousedown event.
+         * Sets up the events required to track the object being dragged
+         * @method handleMouseDown
+         * @param {Event} e the event
+         * @param oDD the DragDrop object being dragged
+         * @private
+         * @static
+         */
+        handleMouseDown: function(e, oDD) {
+
+            this.currentTarget = YAHOO.util.Event.getTarget(e);
+
+            this.dragCurrent = oDD;
+
+            var el = oDD.getEl();
+
+            // track start position
+            this.startX = YAHOO.util.Event.getPageX(e);
+            this.startY = YAHOO.util.Event.getPageY(e);
+
+            this.deltaX = this.startX - el.offsetLeft;
+            this.deltaY = this.startY - el.offsetTop;
+
+            this.dragThreshMet = false;
+
+            this.clickTimeout = setTimeout( 
+                    function() { 
+                        var DDM = YAHOO.util.DDM;
+                        DDM.startDrag(DDM.startX, DDM.startY);
+                        DDM.fromTimeout = true;
+                    }, 
+                    this.clickTimeThresh );
+        },
+
+        /**
+         * Fired when either the drag pixel threshol or the mousedown hold 
+         * time threshold has been met.
+         * @method startDrag
+         * @param x {int} the X position of the original mousedown
+         * @param y {int} the Y position of the original mousedown
+         * @static
+         */
+        startDrag: function(x, y) {
+            YAHOO.log("firing drag start events", "info", "DragDropMgr");
+            clearTimeout(this.clickTimeout);
+            var dc = this.dragCurrent;
+            if (dc) {
+                dc.b4StartDrag(x, y);
+            }
+            if (dc) {
+                dc.startDrag(x, y);
+            }
+            this.dragThreshMet = true;
+        },
+
+        /**
+         * Internal function to handle the mouseup event.  Will be invoked 
+         * from the context of the document.
+         * @method handleMouseUp
+         * @param {Event} e the event
+         * @private
+         * @static
+         */
+        handleMouseUp: function(e) {
+            if (this.dragCurrent) {
+                clearTimeout(this.clickTimeout);
+
+                if (this.dragThreshMet) {
+                    YAHOO.log("mouseup detected - completing drag", "info", "DragDropMgr");
+                    if (this.fromTimeout) {
+                        YAHOO.log('fromTimeout is true (mouse didn\'t move), call handleMouseDown so we can get the dragOver event', 'info', 'DragDropMgr');
+                        this.handleMouseMove(e);
+                    }
+                    this.fromTimeout = false;
+                    this.fireEvents(e, true);
+                } else {
+                    YAHOO.log("drag threshold not met", "info", "DragDropMgr");
+                }
+
+                this.stopDrag(e);
+
+                this.stopEvent(e);
+            }
+        },
+
+        /**
+         * Utility to stop event propagation and event default, if these 
+         * features are turned on.
+         * @method stopEvent
+         * @param {Event} e the event as returned by this.getEvent()
+         * @static
+         */
+        stopEvent: function(e) {
+            if (this.stopPropagation) {
+                YAHOO.util.Event.stopPropagation(e);
+            }
+
+            if (this.preventDefault) {
+                YAHOO.util.Event.preventDefault(e);
+            }
+        },
+
+        /** 
+         * Ends the current drag, cleans up the state, and fires the endDrag
+         * and mouseUp events.  Called internally when a mouseup is detected
+         * during the drag.  Can be fired manually during the drag by passing
+         * either another event (such as the mousemove event received in onDrag)
+         * or a fake event with pageX and pageY defined (so that endDrag and
+         * onMouseUp have usable position data.).  Alternatively, pass true
+         * for the silent parameter so that the endDrag and onMouseUp events
+         * are skipped (so no event data is needed.)
+         *
+         * @method stopDrag
+         * @param {Event} e the mouseup event, another event (or a fake event) 
+         *                  with pageX and pageY defined, or nothing if the 
+         *                  silent parameter is true
+         * @param {boolean} silent skips the enddrag and mouseup events if true
+         * @static
+         */
+        stopDrag: function(e, silent) {
+            // YAHOO.log("mouseup - removing event handlers");
+
+            // Fire the drag end event for the item that was dragged
+            if (this.dragCurrent && !silent) {
+                if (this.dragThreshMet) {
+                    YAHOO.log("firing endDrag events", "info", "DragDropMgr");
+                    this.dragCurrent.b4EndDrag(e);
+                    this.dragCurrent.endDrag(e);
+                }
+
+                YAHOO.log("firing dragdrop onMouseUp event", "info", "DragDropMgr");
+                this.dragCurrent.onMouseUp(e);
+            }
+
+            this.dragCurrent = null;
+            this.dragOvers = {};
+        },
+
+        /** 
+         * Internal function to handle the mousemove event.  Will be invoked 
+         * from the context of the html element.
+         *
+         * @TODO figure out what we can do about mouse events lost when the 
+         * user drags objects beyond the window boundary.  Currently we can 
+         * detect this in internet explorer by verifying that the mouse is 
+         * down during the mousemove event.  Firefox doesn't give us the 
+         * button state on the mousemove event.
+         * @method handleMouseMove
+         * @param {Event} e the event
+         * @private
+         * @static
+         */
+        handleMouseMove: function(e) {
+            //YAHOO.log("handlemousemove");
+            
+            var dc = this.dragCurrent;
+            if (dc) {
+                // YAHOO.log("no current drag obj");
+
+                // var button = e.which || e.button;
+                // YAHOO.log("which: " + e.which + ", button: "+ e.button);
+
+                // check for IE mouseup outside of page boundary
+                if (YAHOO.util.Event.isIE && !e.button) {
+                    YAHOO.log("button failure", "info", "DragDropMgr");
+                    this.stopEvent(e);
+                    return this.handleMouseUp(e);
+                }
+
+                if (!this.dragThreshMet) {
+                    var diffX = Math.abs(this.startX - YAHOO.util.Event.getPageX(e));
+                    var diffY = Math.abs(this.startY - YAHOO.util.Event.getPageY(e));
+                    // YAHOO.log("diffX: " + diffX + "diffY: " + diffY);
+                    if (diffX > this.clickPixelThresh || 
+                                diffY > this.clickPixelThresh) {
+                        YAHOO.log("pixel threshold met", "info", "DragDropMgr");
+                        this.startDrag(this.startX, this.startY);
+                    }
+                }
+
+                if (this.dragThreshMet) {
+                    dc.b4Drag(e);
+                    if (dc) {
+                        dc.onDrag(e);
+                    }
+                    if (dc) {
+                        this.fireEvents(e, false);
+                    }
+                }
+
+                this.stopEvent(e);
+            }
+        },
+        
+        /**
+         * Iterates over all of the DragDrop elements to find ones we are 
+         * hovering over or dropping on
+         * @method fireEvents
+         * @param {Event} e the event
+         * @param {boolean} isDrop is this a drop op or a mouseover op?
+         * @private
+         * @static
+         */
+        fireEvents: function(e, isDrop) {
+            var dc = this.dragCurrent;
+
+            // If the user did the mouse up outside of the window, we could 
+            // get here even though we have ended the drag.
+            // If the config option dragOnly is true, bail out and don't fire the events
+            if (!dc || dc.isLocked() || dc.dragOnly) {
+                return;
+            }
+
+            var x = YAHOO.util.Event.getPageX(e),
+                y = YAHOO.util.Event.getPageY(e),
+                pt = new YAHOO.util.Point(x,y),
+                pos = dc.getTargetCoord(pt.x, pt.y),
+                el = dc.getDragEl(),
+                curRegion = new YAHOO.util.Region( pos.y, 
+                                               pos.x + el.offsetWidth,
+                                               pos.y + el.offsetHeight, 
+                                               pos.x ),
+            
+                oldOvers = [], // cache the previous dragOver array
+                outEvts   = [],
+                overEvts  = [],
+                dropEvts  = [],
+                enterEvts = [],
+                inGroupsObj  = {},
+                inGroups  = [];
+
+
+            // Check to see if the object(s) we were hovering over is no longer 
+            // being hovered over so we can fire the onDragOut event
+            for (var i in this.dragOvers) {
+
+                var ddo = this.dragOvers[i];
+
+                if (! this.isTypeOfDD(ddo)) {
+                    continue;
+                }
+                if (! this.isOverTarget(pt, ddo, this.mode, curRegion)) {
+                    outEvts.push( ddo );
+                }
+
+                oldOvers[i] = true;
+                delete this.dragOvers[i];
+            }
+
+            for (var sGroup in dc.groups) {
+                // YAHOO.log("Processing group " + sGroup);
+                
+                if ("string" != typeof sGroup) {
+                    continue;
+                }
+
+                for (i in this.ids[sGroup]) {
+                    var oDD = this.ids[sGroup][i];
+                    if (! this.isTypeOfDD(oDD)) {
+                        continue;
+                    }
+
+                    if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
+                        if (this.isOverTarget(pt, oDD, this.mode, curRegion)) {
+                            inGroupsObj[sGroup] = true;
+                            // look for drop interactions
+                            if (isDrop) {
+                                dropEvts.push( oDD );
+                            // look for drag enter and drag over interactions
+                            } else {
+
+                                // initial drag over: dragEnter fires
+                                if (!oldOvers[oDD.id]) {
+                                    enterEvts.push( oDD );
+                                // subsequent drag overs: dragOver fires
+                                } else {
+                                    overEvts.push( oDD );
+                                }
+
+                                this.dragOvers[oDD.id] = oDD;
+                            }
+                        }
+                    }
+                }
+            }
+
+            this.interactionInfo = {
+                out:       outEvts,
+                enter:     enterEvts,
+                over:      overEvts,
+                drop:      dropEvts,
+                point:     pt,
+                draggedRegion:    curRegion,
+                sourceRegion: this.locationCache[dc.id],
+                validDrop: isDrop
+            };
+
+            
+            for (var inG in inGroupsObj) {
+                inGroups.push(inG);
+            }
+
+            // notify about a drop that did not find a target
+            if (isDrop && !dropEvts.length) {
+                YAHOO.log(dc.id + " dropped, but not on a target", "info", "DragDropMgr");
+                this.interactionInfo.validDrop = false;
+                dc.onInvalidDrop(e);
+            }
+
+
+            if (this.mode) {
+                if (outEvts.length) {
+                    YAHOO.log(dc.id+" onDragOut: " + outEvts, "info", "DragDropMgr");
+                    dc.b4DragOut(e, outEvts);
+                    if (dc) {
+                        dc.onDragOut(e, outEvts);
+                    }
+                }
+
+                if (enterEvts.length) {
+                    YAHOO.log(dc.id+" onDragEnter: " + enterEvts + " (group: " + inGroups + ")", "info", "DragDropMgr");
+                    if (dc) {
+                        dc.onDragEnter(e, enterEvts, inGroups);
+                    }
+                }
+
+                if (overEvts.length) {
+                    YAHOO.log(dc.id+" onDragOver: " + overEvts + " (group: " + inGroups + ")", "info", "DragDropMgr");
+                    if (dc) {
+                        dc.b4DragOver(e, overEvts, inGroups);
+                    }
+
+                    if (dc) {
+                        dc.onDragOver(e, overEvts, inGroups);
+                    }
+                }
+
+                if (dropEvts.length) {
+                    YAHOO.log(dc.id+" onDragDrop: " + dropEvts + " (group: " + inGroups + ")", "info", "DragDropMgr");
+                    if (dc) {
+                        dc.b4DragDrop(e, dropEvts, inGroups);
+                    }
+                    if (dc) {
+                        dc.onDragDrop(e, dropEvts, inGroups);
+                    }
+                }
+
+            } else {
+                // fire dragout events
+                var len = 0;
+                for (i=0, len=outEvts.length; i<len; ++i) {
+                    YAHOO.log(dc.id+" onDragOut: " + outEvts[i].id, "info", "DragDropMgr");
+                    if (dc) {
+                        dc.b4DragOut(e, outEvts[i].id, inGroups[0]);
+                    }
+                    if (dc) {
+                        dc.onDragOut(e, outEvts[i].id, inGroups[0]);
+                    }
+                }
+                 
+                // fire enter events
+                for (i=0,len=enterEvts.length; i<len; ++i) {
+                    YAHOO.log(dc.id + " onDragEnter " + enterEvts[i].id + " (group: " + inGroups + ")", "info", "DragDropMgr");
+                    // dc.b4DragEnter(e, oDD.id);
+
+                    if (dc) {
+                        dc.onDragEnter(e, enterEvts[i].id, inGroups[0]);
+                    }
+                }
+         
+                // fire over events
+                for (i=0,len=overEvts.length; i<len; ++i) {
+                    YAHOO.log(dc.id + " onDragOver " + overEvts[i].id + " (group: " + inGroups + ")", "info", "DragDropMgr");
+                    if (dc) {
+                        dc.b4DragOver(e, overEvts[i].id, inGroups[0]);
+                    }
+                    if (dc) {
+                        dc.onDragOver(e, overEvts[i].id, inGroups[0]);
+                    }
+                }
+
+                // fire drop events
+                for (i=0, len=dropEvts.length; i<len; ++i) {
+                    YAHOO.log(dc.id + " dropped on " + dropEvts[i].id + " (group: " + inGroups + ")", "info", "DragDropMgr");
+                    if (dc) {
+                        dc.b4DragDrop(e, dropEvts[i].id, inGroups[0]);
+                    }
+                    if (dc) {
+                        dc.onDragDrop(e, dropEvts[i].id, inGroups[0]);
+                    }
+                }
+
+            }
+        },
+
+        /**
+         * Helper function for getting the best match from the list of drag 
+         * and drop objects returned by the drag and drop events when we are 
+         * in INTERSECT mode.  It returns either the first object that the 
+         * cursor is over, or the object that has the greatest overlap with 
+         * the dragged element.
+         * @method getBestMatch
+         * @param  {DragDrop[]} dds The array of drag and drop objects 
+         * targeted
+         * @return {DragDrop}       The best single match
+         * @static
+         */
+        getBestMatch: function(dds) {
+            var winner = null;
+
+            var len = dds.length;
+
+            if (len == 1) {
+                winner = dds[0];
+            } else {
+                // Loop through the targeted items
+                for (var i=0; i<len; ++i) {
+                    var dd = dds[i];
+                    // If the cursor is over the object, it wins.  If the 
+                    // cursor is over multiple matches, the first one we come
+                    // to wins.
+                    if (this.mode == this.INTERSECT && dd.cursorIsOver) {
+                        winner = dd;
+                        break;
+                    // Otherwise the object with the most overlap wins
+                    } else {
+                        if (!winner || !winner.overlap || (dd.overlap &&
+                            winner.overlap.getArea() < dd.overlap.getArea())) {
+                            winner = dd;
+                        }
+                    }
+                }
+            }
+
+            return winner;
+        },
+
+        /**
+         * Refreshes the cache of the top-left and bottom-right points of the 
+         * drag and drop objects in the specified group(s).  This is in the
+         * format that is stored in the drag and drop instance, so typical 
+         * usage is:
+         * <code>
+         * YAHOO.util.DragDropMgr.refreshCache(ddinstance.groups);
+         * </code>
+         * Alternatively:
+         * <code>
+         * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true});
+         * </code>
+         * @TODO this really should be an indexed array.  Alternatively this
+         * method could accept both.
+         * @method refreshCache
+         * @param {Object} groups an associative array of groups to refresh
+         * @static
+         */
+        refreshCache: function(groups) {
+            YAHOO.log("refreshing element location cache", "info", "DragDropMgr");
+
+            // refresh everything if group array is not provided
+            var g = groups || this.ids;
+
+            for (var sGroup in g) {
+                if ("string" != typeof sGroup) {
+                    continue;
+                }
+                for (var i in this.ids[sGroup]) {
+                    var oDD = this.ids[sGroup][i];
+
+                    if (this.isTypeOfDD(oDD)) {
+                        var loc = this.getLocation(oDD);
+                        if (loc) {
+                            this.locationCache[oDD.id] = loc;
+                        } else {
+                            delete this.locationCache[oDD.id];
+YAHOO.log("Could not get the loc for " + oDD.id, "warn", "DragDropMgr");
+                        }
+                    }
+                }
+            }
+        },
+
+        /**
+         * This checks to make sure an element exists and is in the DOM.  The
+         * main purpose is to handle cases where innerHTML is used to remove
+         * drag and drop objects from the DOM.  IE provides an 'unspecified
+         * error' when trying to access the offsetParent of such an element
+         * @method verifyEl
+         * @param {HTMLElement} el the element to check
+         * @return {boolean} true if the element looks usable
+         * @static
+         */
+        verifyEl: function(el) {
+            try {
+                if (el) {
+                    var parent = el.offsetParent;
+                    if (parent) {
+                        return true;
+                    }
+                }
+            } catch(e) {
+                YAHOO.log("detected problem with an element", "info", "DragDropMgr");
+            }
+
+            return false;
+        },
+        
+        /**
+         * Returns a Region object containing the drag and drop element's position
+         * and size, including the padding configured for it
+         * @method getLocation
+         * @param {DragDrop} oDD the drag and drop object to get the 
+         *                       location for
+         * @return {YAHOO.util.Region} a Region object representing the total area
+         *                             the element occupies, including any padding
+         *                             the instance is configured for.
+         * @static
+         */
+        getLocation: function(oDD) {
+            if (! this.isTypeOfDD(oDD)) {
+                YAHOO.log(oDD + " is not a DD obj", "info", "DragDropMgr");
+                return null;
+            }
+
+            var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
+
+            try {
+                pos= YAHOO.util.Dom.getXY(el);
+            } catch (e) { }
+
+            if (!pos) {
+                YAHOO.log("getXY failed", "info", "DragDropMgr");
+                return null;
+            }
+
+            x1 = pos[0];
+            x2 = x1 + el.offsetWidth;
+            y1 = pos[1];
+            y2 = y1 + el.offsetHeight;
+
+            t = y1 - oDD.padding[0];
+            r = x2 + oDD.padding[1];
+            b = y2 + oDD.padding[2];
+            l = x1 - oDD.padding[3];
+
+            return new YAHOO.util.Region( t, r, b, l );
+        },
+
+        /**
+         * Checks the cursor location to see if it over the target
+         * @method isOverTarget
+         * @param {YAHOO.util.Point} pt The point to evaluate
+         * @param {DragDrop} oTarget the DragDrop object we are inspecting
+         * @param {boolean} intersect true if we are in intersect mode
+         * @param {YAHOO.util.Region} pre-cached location of the dragged element
+         * @return {boolean} true if the mouse is over the target
+         * @private
+         * @static
+         */
+        isOverTarget: function(pt, oTarget, intersect, curRegion) {
+            // use cache if available
+            var loc = this.locationCache[oTarget.id];
+            if (!loc || !this.useCache) {
+                YAHOO.log("cache not populated", "info", "DragDropMgr");
+                loc = this.getLocation(oTarget);
+                this.locationCache[oTarget.id] = loc;
+
+                YAHOO.log("cache: " + loc, "info", "DragDropMgr");
+            }
+
+            if (!loc) {
+                YAHOO.log("could not get the location of the element", "info", "DragDropMgr");
+                return false;
+            }
+
+            //YAHOO.log("loc: " + loc + ", pt: " + pt);
+            oTarget.cursorIsOver = loc.contains( pt );
+
+            // DragDrop is using this as a sanity check for the initial mousedown
+            // in this case we are done.  In POINT mode, if the drag obj has no
+            // contraints, we are done. Otherwise we need to evaluate the 
+            // region the target as occupies to determine if the dragged element
+            // overlaps with it.
+            
+            var dc = this.dragCurrent;
+            if (!dc || (!intersect && !dc.constrainX && !dc.constrainY)) {
+
+                //if (oTarget.cursorIsOver) {
+                    //YAHOO.log("over " + oTarget + ", " + loc + ", " + pt, "warn");
+                //}
+                return oTarget.cursorIsOver;
+            }
+
+            oTarget.overlap = null;
+
+            // Get the current location of the drag element, this is the
+            // location of the mouse event less the delta that represents
+            // where the original mousedown happened on the element.  We
+            // need to consider constraints and ticks as well.
+
+            if (!curRegion) {
+                var pos = dc.getTargetCoord(pt.x, pt.y);
+                var el = dc.getDragEl();
+                curRegion = new YAHOO.util.Region( pos.y, 
+                                                   pos.x + el.offsetWidth,
+                                                   pos.y + el.offsetHeight, 
+                                                   pos.x );
+            }
+
+            var overlap = curRegion.intersect(loc);
+
+            if (overlap) {
+                oTarget.overlap = overlap;
+                return (intersect) ? true : oTarget.cursorIsOver;
+            } else {
+                return false;
+            }
+        },
+
+        /**
+         * unload event handler
+         * @method _onUnload
+         * @private
+         * @static
+         */
+        _onUnload: function(e, me) {
+            this.unregAll();
+        },
+
+        /**
+         * Cleans up the drag and drop events and objects.
+         * @method unregAll
+         * @private
+         * @static
+         */
+        unregAll: function() {
+            YAHOO.log("unregister all", "info", "DragDropMgr");
+
+            if (this.dragCurrent) {
+                this.stopDrag();
+                this.dragCurrent = null;
+            }
+
+            this._execOnAll("unreg", []);
+
+            //for (var i in this.elementCache) {
+                //delete this.elementCache[i];
+            //}
+            //this.elementCache = {};
+
+            this.ids = {};
+        },
+
+        /**
+         * A cache of DOM elements
+         * @property elementCache
+         * @private
+         * @static
+         * @deprecated elements are not cached now
+         */
+        elementCache: {},
+        
+        /**
+         * Get the wrapper for the DOM element specified
+         * @method getElWrapper
+         * @param {String} id the id of the element to get
+         * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
+         * @private
+         * @deprecated This wrapper isn't that useful
+         * @static
+         */
+        getElWrapper: function(id) {
+            var oWrapper = this.elementCache[id];
+            if (!oWrapper || !oWrapper.el) {
+                oWrapper = this.elementCache[id] = 
+                    new this.ElementWrapper(YAHOO.util.Dom.get(id));
+            }
+            return oWrapper;
+        },
+
+        /**
+         * Returns the actual DOM element
+         * @method getElement
+         * @param {String} id the id of the elment to get
+         * @return {Object} The element
+         * @deprecated use YAHOO.util.Dom.get instead
+         * @static
+         */
+        getElement: function(id) {
+            return YAHOO.util.Dom.get(id);
+        },
+        
+        /**
+         * Returns the style property for the DOM element (i.e., 
+         * document.getElById(id).style)
+         * @method getCss
+         * @param {String} id the id of the elment to get
+         * @return {Object} The style property of the element
+         * @deprecated use YAHOO.util.Dom instead
+         * @static
+         */
+        getCss: function(id) {
+            var el = YAHOO.util.Dom.get(id);
+            return (el) ? el.style : null;
+        },
+
+        /**
+         * Inner class for cached elements
+         * @class DragDropMgr.ElementWrapper
+         * @for DragDropMgr
+         * @private
+         * @deprecated
+         */
+        ElementWrapper: function(el) {
+                /**
+                 * The element
+                 * @property el
+                 */
+                this.el = el || null;
+                /**
+                 * The element id
+                 * @property id
+                 */
+                this.id = this.el && el.id;
+                /**
+                 * A reference to the style property
+                 * @property css
+                 */
+                this.css = this.el && el.style;
+            },
+
+        /**
+         * Returns the X position of an html element
+         * @method getPosX
+         * @param el the element for which to get the position
+         * @return {int} the X coordinate
+         * @for DragDropMgr
+         * @deprecated use YAHOO.util.Dom.getX instead
+         * @static
+         */
+        getPosX: function(el) {
+            return YAHOO.util.Dom.getX(el);
+        },
+
+        /**
+         * Returns the Y position of an html element
+         * @method getPosY
+         * @param el the element for which to get the position
+         * @return {int} the Y coordinate
+         * @deprecated use YAHOO.util.Dom.getY instead
+         * @static
+         */
+        getPosY: function(el) {
+            return YAHOO.util.Dom.getY(el); 
+        },
+
+        /**
+         * Swap two nodes.  In IE, we use the native method, for others we 
+         * emulate the IE behavior
+         * @method swapNode
+         * @param n1 the first node to swap
+         * @param n2 the other node to swap
+         * @static
+         */
+        swapNode: function(n1, n2) {
+            if (n1.swapNode) {
+                n1.swapNode(n2);
+            } else {
+                var p = n2.parentNode;
+                var s = n2.nextSibling;
+
+                if (s == n1) {
+                    p.insertBefore(n1, n2);
+                } else if (n2 == n1.nextSibling) {
+                    p.insertBefore(n2, n1);
+                } else {
+                    n1.parentNode.replaceChild(n2, n1);
+                    p.insertBefore(n1, s);
+                }
+            }
+        },
+
+        /**
+         * Returns the current scroll position
+         * @method getScroll
+         * @private
+         * @static
+         */
+        getScroll: function () {
+            var t, l, dde=document.documentElement, db=document.body;
+            if (dde && (dde.scrollTop || dde.scrollLeft)) {
+                t = dde.scrollTop;
+                l = dde.scrollLeft;
+            } else if (db) {
+                t = db.scrollTop;
+                l = db.scrollLeft;
+            } else {
+                YAHOO.log("could not get scroll property", "info", "DragDropMgr");
+            }
+            return { top: t, left: l };
+        },
+
+        /**
+         * Returns the specified element style property
+         * @method getStyle
+         * @param {HTMLElement} el          the element
+         * @param {string}      styleProp   the style property
+         * @return {string} The value of the style property
+         * @deprecated use YAHOO.util.Dom.getStyle
+         * @static
+         */
+        getStyle: function(el, styleProp) {
+            return YAHOO.util.Dom.getStyle(el, styleProp);
+        },
+
+        /**
+         * Gets the scrollTop
+         * @method getScrollTop
+         * @return {int} the document's scrollTop
+         * @static
+         */
+        getScrollTop: function () { return this.getScroll().top; },
+
+        /**
+         * Gets the scrollLeft
+         * @method getScrollLeft
+         * @return {int} the document's scrollTop
+         * @static
+         */
+        getScrollLeft: function () { return this.getScroll().left; },
+
+        /**
+         * Sets the x/y position of an element to the location of the
+         * target element.
+         * @method moveToEl
+         * @param {HTMLElement} moveEl      The element to move
+         * @param {HTMLElement} targetEl    The position reference element
+         * @static
+         */
+        moveToEl: function (moveEl, targetEl) {
+            var aCoord = YAHOO.util.Dom.getXY(targetEl);
+            YAHOO.log("moveToEl: " + aCoord, "info", "DragDropMgr");
+            YAHOO.util.Dom.setXY(moveEl, aCoord);
+        },
+
+        /**
+         * Gets the client height
+         * @method getClientHeight
+         * @return {int} client height in px
+         * @deprecated use YAHOO.util.Dom.getViewportHeight instead
+         * @static
+         */
+        getClientHeight: function() {
+            return YAHOO.util.Dom.getViewportHeight();
+        },
+
+        /**
+         * Gets the client width
+         * @method getClientWidth
+         * @return {int} client width in px
+         * @deprecated use YAHOO.util.Dom.getViewportWidth instead
+         * @static
+         */
+        getClientWidth: function() {
+            return YAHOO.util.Dom.getViewportWidth();
+        },
+
+        /**
+         * Numeric array sort function
+         * @method numericSort
+         * @static
+         */
+        numericSort: function(a, b) { return (a - b); },
+
+        /**
+         * Internal counter
+         * @property _timeoutCount
+         * @private
+         * @static
+         */
+        _timeoutCount: 0,
+
+        /**
+         * Trying to make the load order less important.  Without this we get
+         * an error if this file is loaded before the Event Utility.
+         * @method _addListeners
+         * @private
+         * @static
+         */
+        _addListeners: function() {
+            var DDM = YAHOO.util.DDM;
+            if ( YAHOO.util.Event && document ) {
+                DDM._onLoad();
+            } else {
+                if (DDM._timeoutCount > 2000) {
+                    YAHOO.log("DragDrop requires the Event Utility", "error", "DragDropMgr");
+                } else {
+                    setTimeout(DDM._addListeners, 10);
+                    if (document && document.body) {
+                        DDM._timeoutCount += 1;
+                    }
+                }
+            }
+        },
+
+        /**
+         * Recursively searches the immediate parent and all child nodes for 
+         * the handle element in order to determine wheter or not it was 
+         * clicked.
+         * @method handleWasClicked
+         * @param node the html element to inspect
+         * @static
+         */
+        handleWasClicked: function(node, id) {
+            if (this.isHandle(id, node.id)) {
+                YAHOO.log("clicked node is a handle", "info", "DragDropMgr");
+                return true;
+            } else {
+                // check to see if this is a text node child of the one we want
+                var p = node.parentNode;
+                // YAHOO.log("p: " + p);
+
+                while (p) {
+                    if (this.isHandle(id, p.id)) {
+                        return true;
+                    } else {
+                        YAHOO.log(p.id + " is not a handle", "info", "DragDropMgr");
+                        p = p.parentNode;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+    };
+
+}();
+
+// shorter alias, save a few bytes
+YAHOO.util.DDM = YAHOO.util.DragDropMgr;
+YAHOO.util.DDM._addListeners();
+
+}
+
+(function() {
+
+var Event=YAHOO.util.Event; 
+var Dom=YAHOO.util.Dom;
+
+/**
+ * Defines the interface and base operation of items that that can be 
+ * dragged or can be drop targets.  It was designed to be extended, overriding
+ * the event handlers for startDrag, onDrag, onDragOver, onDragOut.
+ * Up to three html elements can be associated with a DragDrop instance:
+ * <ul>
+ * <li>linked element: the element that is passed into the constructor.
+ * This is the element which defines the boundaries for interaction with 
+ * other DragDrop objects.</li>
+ * <li>handle element(s): The drag operation only occurs if the element that 
+ * was clicked matches a handle element.  By default this is the linked 
+ * element, but there are times that you will want only a portion of the 
+ * linked element to initiate the drag operation, and the setHandleElId() 
+ * method provides a way to define this.</li>
+ * <li>drag element: this represents an the element that would be moved along
+ * with the cursor during a drag operation.  By default, this is the linked
+ * element itself as in {@link YAHOO.util.DD}.  setDragElId() lets you define
+ * a separate element that would be moved, as in {@link YAHOO.util.DDProxy}
+ * </li>
+ * </ul>
+ * This class should not be instantiated until the onload event to ensure that
+ * the associated elements are available.
+ * The following would define a DragDrop obj that would interact with any 
+ * other DragDrop obj in the "group1" group:
+ * <pre>
+ *  dd = new YAHOO.util.DragDrop("div1", "group1");
+ * </pre>
+ * Since none of the event handlers have been implemented, nothing would 
+ * actually happen if you were to run the code above.  Normally you would 
+ * override this class or one of the default implementations, but you can 
+ * also override the methods you want on an instance of the class...
+ * <pre>
+ *  dd.onDragDrop = function(e, id) {
+ *  &nbsp;&nbsp;alert("dd was dropped on " + id);
+ *  }
+ * </pre>
+ * @namespace YAHOO.util
+ * @class DragDrop
+ * @constructor
+ * @param {String} id of the element that is linked to this instance
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ *                Valid properties for DragDrop: 
+ *                    padding, isTarget, maintainOffset, primaryButtonOnly,
+ */
+YAHOO.util.DragDrop = function(id, sGroup, config) {
+    if (id) {
+        this.init(id, sGroup, config); 
+    }
+};
+
+YAHOO.util.DragDrop.prototype = {
+
+    /**
+     * The id of the element associated with this object.  This is what we 
+     * refer to as the "linked element" because the size and position of 
+     * this element is used to determine when the drag and drop objects have 
+     * interacted.
+     * @property id
+     * @type String
+     */
+    id: null,
+
+    /**
+     * Configuration attributes passed into the constructor
+     * @property config
+     * @type object
+     */
+    config: null,
+
+    /**
+     * The id of the element that will be dragged.  By default this is same 
+     * as the linked element , but could be changed to another element. Ex: 
+     * YAHOO.util.DDProxy
+     * @property dragElId
+     * @type String
+     * @private
+     */
+    dragElId: null, 
+
+    /**
+     * the id of the element that initiates the drag operation.  By default 
+     * this is the linked element, but could be changed to be a child of this
+     * element.  This lets us do things like only starting the drag when the 
+     * header element within the linked html element is clicked.
+     * @property handleElId
+     * @type String
+     * @private
+     */
+    handleElId: null, 
+
+    /**
+     * An associative array of HTML tags that will be ignored if clicked.
+     * @property invalidHandleTypes
+     * @type {string: string}
+     */
+    invalidHandleTypes: null, 
+
+    /**
+     * An associative array of ids for elements that will be ignored if clicked
+     * @property invalidHandleIds
+     * @type {string: string}
+     */
+    invalidHandleIds: null, 
+
+    /**
+     * An indexted array of css class names for elements that will be ignored
+     * if clicked.
+     * @property invalidHandleClasses
+     * @type string[]
+     */
+    invalidHandleClasses: null, 
+
+    /**
+     * The linked element's absolute X position at the time the drag was 
+     * started
+     * @property startPageX
+     * @type int
+     * @private
+     */
+    startPageX: 0,
+
+    /**
+     * The linked element's absolute X position at the time the drag was 
+     * started
+     * @property startPageY
+     * @type int
+     * @private
+     */
+    startPageY: 0,
+
+    /**
+     * The group defines a logical collection of DragDrop objects that are 
+     * related.  Instances only get events when interacting with other 
+     * DragDrop object in the same group.  This lets us define multiple 
+     * groups using a single DragDrop subclass if we want.
+     * @property groups
+     * @type {string: string}
+     */
+    groups: null,
+
+    /**
+     * Individual drag/drop instances can be locked.  This will prevent 
+     * onmousedown start drag.
+     * @property locked
+     * @type boolean
+     * @private
+     */
+    locked: false,
+
+    /**
+     * Lock this instance
+     * @method lock
+     */
+    lock: function() { this.locked = true; },
+
+    /**
+     * Unlock this instace
+     * @method unlock
+     */
+    unlock: function() { this.locked = false; },
+
+    /**
+     * By default, all instances can be a drop target.  This can be disabled by
+     * setting isTarget to false.
+     * @property isTarget
+     * @type boolean
+     */
+    isTarget: true,
+
+    /**
+     * The padding configured for this drag and drop object for calculating
+     * the drop zone intersection with this object.
+     * @property padding
+     * @type int[]
+     */
+    padding: null,
+    /**
+     * If this flag is true, do not fire drop events. The element is a drag only element (for movement not dropping)
+     * @property dragOnly
+     * @type Boolean
+     */
+    dragOnly: false,
+
+    /**
+     * Cached reference to the linked element
+     * @property _domRef
+     * @private
+     */
+    _domRef: null,
+
+    /**
+     * Internal typeof flag
+     * @property __ygDragDrop
+     * @private
+     */
+    __ygDragDrop: true,
+
+    /**
+     * Set to true when horizontal contraints are applied
+     * @property constrainX
+     * @type boolean
+     * @private
+     */
+    constrainX: false,
+
+    /**
+     * Set to true when vertical contraints are applied
+     * @property constrainY
+     * @type boolean
+     * @private
+     */
+    constrainY: false,
+
+    /**
+     * The left constraint
+     * @property minX
+     * @type int
+     * @private
+     */
+    minX: 0,
+
+    /**
+     * The right constraint
+     * @property maxX
+     * @type int
+     * @private
+     */
+    maxX: 0,
+
+    /**
+     * The up constraint 
+     * @property minY
+     * @type int
+     * @type int
+     * @private
+     */
+    minY: 0,
+
+    /**
+     * The down constraint 
+     * @property maxY
+     * @type int
+     * @private
+     */
+    maxY: 0,
+
+    /**
+     * The difference between the click position and the source element's location
+     * @property deltaX
+     * @type int
+     * @private
+     */
+    deltaX: 0,
+
+    /**
+     * The difference between the click position and the source element's location
+     * @property deltaY
+     * @type int
+     * @private
+     */
+    deltaY: 0,
+
+    /**
+     * Maintain offsets when we resetconstraints.  Set to true when you want
+     * the position of the element relative to its parent to stay the same
+     * when the page changes
+     *
+     * @property maintainOffset
+     * @type boolean
+     */
+    maintainOffset: false,
+
+    /**
+     * Array of pixel locations the element will snap to if we specified a 
+     * horizontal graduation/interval.  This array is generated automatically
+     * when you define a tick interval.
+     * @property xTicks
+     * @type int[]
+     */
+    xTicks: null,
+
+    /**
+     * Array of pixel locations the element will snap to if we specified a 
+     * vertical graduation/interval.  This array is generated automatically 
+     * when you define a tick interval.
+     * @property yTicks
+     * @type int[]
+     */
+    yTicks: null,
+
+    /**
+     * By default the drag and drop instance will only respond to the primary
+     * button click (left button for a right-handed mouse).  Set to true to
+     * allow drag and drop to start with any mouse click that is propogated
+     * by the browser
+     * @property primaryButtonOnly
+     * @type boolean
+     */
+    primaryButtonOnly: true,
+
+    /**
+     * The availabe property is false until the linked dom element is accessible.
+     * @property available
+     * @type boolean
+     */
+    available: false,
+
+    /**
+     * By default, drags can only be initiated if the mousedown occurs in the
+     * region the linked element is.  This is done in part to work around a
+     * bug in some browsers that mis-report the mousedown if the previous
+     * mouseup happened outside of the window.  This property is set to true
+     * if outer handles are defined.
+     *
+     * @property hasOuterHandles
+     * @type boolean
+     * @default false
+     */
+    hasOuterHandles: false,
+
+    /**
+     * Property that is assigned to a drag and drop object when testing to
+     * see if it is being targeted by another dd object.  This property
+     * can be used in intersect mode to help determine the focus of
+     * the mouse interaction.  DDM.getBestMatch uses this property first to
+     * determine the closest match in INTERSECT mode when multiple targets
+     * are part of the same interaction.
+     * @property cursorIsOver
+     * @type boolean
+     */
+    cursorIsOver: false,
+
+    /**
+     * Property that is assigned to a drag and drop object when testing to
+     * see if it is being targeted by another dd object.  This is a region
+     * that represents the area the draggable element overlaps this target.
+     * DDM.getBestMatch uses this property to compare the size of the overlap
+     * to that of other targets in order to determine the closest match in
+     * INTERSECT mode when multiple targets are part of the same interaction.
+     * @property overlap 
+     * @type YAHOO.util.Region
+     */
+    overlap: null,
+
+    /**
+     * Code that executes immediately before the startDrag event
+     * @method b4StartDrag
+     * @private
+     */
+    b4StartDrag: function(x, y) { },
+
+    /**
+     * Abstract method called after a drag/drop object is clicked
+     * and the drag or mousedown time thresholds have beeen met.
+     * @method startDrag
+     * @param {int} X click location
+     * @param {int} Y click location
+     */
+    startDrag: function(x, y) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDrag event
+     * @method b4Drag
+     * @private
+     */
+    b4Drag: function(e) { },
+
+    /**
+     * Abstract method called during the onMouseMove event while dragging an 
+     * object.
+     * @method onDrag
+     * @param {Event} e the mousemove event
+     */
+    onDrag: function(e) { /* override this */ },
+
+    /**
+     * Abstract method called when this element fist begins hovering over 
+     * another DragDrop obj
+     * @method onDragEnter
+     * @param {Event} e the mousemove event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this is hovering over.  In INTERSECT mode, an array of one or more 
+     * dragdrop items being hovered over.
+     */
+    onDragEnter: function(e, id) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDragOver event
+     * @method b4DragOver
+     * @private
+     */
+    b4DragOver: function(e) { },
+
+    /**
+     * Abstract method called when this element is hovering over another 
+     * DragDrop obj
+     * @method onDragOver
+     * @param {Event} e the mousemove event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this is hovering over.  In INTERSECT mode, an array of dd items 
+     * being hovered over.
+     */
+    onDragOver: function(e, id) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDragOut event
+     * @method b4DragOut
+     * @private
+     */
+    b4DragOut: function(e) { },
+
+    /**
+     * Abstract method called when we are no longer hovering over an element
+     * @method onDragOut
+     * @param {Event} e the mousemove event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this was hovering over.  In INTERSECT mode, an array of dd items 
+     * that the mouse is no longer over.
+     */
+    onDragOut: function(e, id) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDragDrop event
+     * @method b4DragDrop
+     * @private
+     */
+    b4DragDrop: function(e) { },
+
+    /**
+     * Abstract method called when this item is dropped on another DragDrop 
+     * obj
+     * @method onDragDrop
+     * @param {Event} e the mouseup event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this was dropped on.  In INTERSECT mode, an array of dd items this 
+     * was dropped on.
+     */
+    onDragDrop: function(e, id) { /* override this */ },
+
+    /**
+     * Abstract method called when this item is dropped on an area with no
+     * drop target
+     * @method onInvalidDrop
+     * @param {Event} e the mouseup event
+     */
+    onInvalidDrop: function(e) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the endDrag event
+     * @method b4EndDrag
+     * @private
+     */
+    b4EndDrag: function(e) { },
+
+    /**
+     * Fired when we are done dragging the object
+     * @method endDrag
+     * @param {Event} e the mouseup event
+     */
+    endDrag: function(e) { /* override this */ },
+
+    /**
+     * Code executed immediately before the onMouseDown event
+     * @method b4MouseDown
+     * @param {Event} e the mousedown event
+     * @private
+     */
+    b4MouseDown: function(e) {  },
+
+    /**
+     * Event handler that fires when a drag/drop obj gets a mousedown
+     * @method onMouseDown
+     * @param {Event} e the mousedown event
+     */
+    onMouseDown: function(e) { /* override this */ },
+
+    /**
+     * Event handler that fires when a drag/drop obj gets a mouseup
+     * @method onMouseUp
+     * @param {Event} e the mouseup event
+     */
+    onMouseUp: function(e) { /* override this */ },
+   
+    /**
+     * Override the onAvailable method to do what is needed after the initial
+     * position was determined.
+     * @method onAvailable
+     */
+    onAvailable: function () { 
+        //this.logger.log("onAvailable (base)"); 
+    },
+
+    /**
+     * Returns a reference to the linked element
+     * @method getEl
+     * @return {HTMLElement} the html element 
+     */
+    getEl: function() { 
+        if (!this._domRef) {
+            this._domRef = Dom.get(this.id); 
+        }
+
+        return this._domRef;
+    },
+
+    /**
+     * Returns a reference to the actual element to drag.  By default this is
+     * the same as the html element, but it can be assigned to another 
+     * element. An example of this can be found in YAHOO.util.DDProxy
+     * @method getDragEl
+     * @return {HTMLElement} the html element 
+     */
+    getDragEl: function() {
+        return Dom.get(this.dragElId);
+    },
+
+    /**
+     * Sets up the DragDrop object.  Must be called in the constructor of any
+     * YAHOO.util.DragDrop subclass
+     * @method init
+     * @param id the id of the linked element
+     * @param {String} sGroup the group of related items
+     * @param {object} config configuration attributes
+     */
+    init: function(id, sGroup, config) {
+        this.initTarget(id, sGroup, config);
+        Event.on(this._domRef || this.id, "mousedown", 
+                        this.handleMouseDown, this, true);
+        // Event.on(this.id, "selectstart", Event.preventDefault);
+    },
+
+    /**
+     * Initializes Targeting functionality only... the object does not
+     * get a mousedown handler.
+     * @method initTarget
+     * @param id the id of the linked element
+     * @param {String} sGroup the group of related items
+     * @param {object} config configuration attributes
+     */
+    initTarget: function(id, sGroup, config) {
+
+        // configuration attributes 
+        this.config = config || {};
+
+        // create a local reference to the drag and drop manager
+        this.DDM = YAHOO.util.DDM;
+
+        // initialize the groups object
+        this.groups = {};
+
+        // assume that we have an element reference instead of an id if the
+        // parameter is not a string
+        if (typeof id !== "string") {
+            YAHOO.log("id is not a string, assuming it is an HTMLElement");
+            this._domRef = id;
+            id = Dom.generateId(id);
+        }
+
+        // set the id
+        this.id = id;
+
+        // add to an interaction group
+        this.addToGroup((sGroup) ? sGroup : "default");
+
+        // We don't want to register this as the handle with the manager
+        // so we just set the id rather than calling the setter.
+        this.handleElId = id;
+
+        Event.onAvailable(id, this.handleOnAvailable, this, true);
+
+        // create a logger instance
+        this.logger = (YAHOO.widget.LogWriter) ? 
+                new YAHOO.widget.LogWriter(this.toString()) : YAHOO;
+
+        // the linked element is the element that gets dragged by default
+        this.setDragElId(id); 
+
+        // by default, clicked anchors will not start drag operations. 
+        // @TODO what else should be here?  Probably form fields.
+        this.invalidHandleTypes = { A: "A" };
+        this.invalidHandleIds = {};
+        this.invalidHandleClasses = [];
+
+        this.applyConfig();
+    },
+
+    /**
+     * Applies the configuration parameters that were passed into the constructor.
+     * This is supposed to happen at each level through the inheritance chain.  So
+     * a DDProxy implentation will execute apply config on DDProxy, DD, and 
+     * DragDrop in order to get all of the parameters that are available in
+     * each object.
+     * @method applyConfig
+     */
+    applyConfig: function() {
+
+        // configurable properties: 
+        //    padding, isTarget, maintainOffset, primaryButtonOnly
+        this.padding           = this.config.padding || [0, 0, 0, 0];
+        this.isTarget          = (this.config.isTarget !== false);
+        this.maintainOffset    = (this.config.maintainOffset);
+        this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
+        this.dragOnly = ((this.config.dragOnly === true) ? true : false);
+    },
+
+    /**
+     * Executed when the linked element is available
+     * @method handleOnAvailable
+     * @private
+     */
+    handleOnAvailable: function() {
+        //this.logger.log("handleOnAvailable");
+        this.available = true;
+        this.resetConstraints();
+        this.onAvailable();
+    },
+
+     /**
+     * Configures the padding for the target zone in px.  Effectively expands
+     * (or reduces) the virtual object size for targeting calculations.  
+     * Supports css-style shorthand; if only one parameter is passed, all sides
+     * will have that padding, and if only two are passed, the top and bottom
+     * will have the first param, the left and right the second.
+     * @method setPadding
+     * @param {int} iTop    Top pad
+     * @param {int} iRight  Right pad
+     * @param {int} iBot    Bot pad
+     * @param {int} iLeft   Left pad
+     */
+    setPadding: function(iTop, iRight, iBot, iLeft) {
+        // this.padding = [iLeft, iRight, iTop, iBot];
+        if (!iRight && 0 !== iRight) {
+            this.padding = [iTop, iTop, iTop, iTop];
+        } else if (!iBot && 0 !== iBot) {
+            this.padding = [iTop, iRight, iTop, iRight];
+        } else {
+            this.padding = [iTop, iRight, iBot, iLeft];
+        }
+    },
+
+    /**
+     * Stores the initial placement of the linked element.
+     * @method setInitialPosition
+     * @param {int} diffX   the X offset, default 0
+     * @param {int} diffY   the Y offset, default 0
+     * @private
+     */
+    setInitPosition: function(diffX, diffY) {
+        var el = this.getEl();
+
+        if (!this.DDM.verifyEl(el)) {
+            this.logger.log(this.id + " element is broken");
+            return;
+        }
+
+        var dx = diffX || 0;
+        var dy = diffY || 0;
+
+        var p = Dom.getXY( el );
+
+        this.initPageX = p[0] - dx;
+        this.initPageY = p[1] - dy;
+
+        this.lastPageX = p[0];
+        this.lastPageY = p[1];
+
+        this.logger.log(this.id + " initial position: " + this.initPageX + 
+                ", " + this.initPageY);
+
+
+        this.setStartPosition(p);
+    },
+
+    /**
+     * Sets the start position of the element.  This is set when the obj
+     * is initialized, the reset when a drag is started.
+     * @method setStartPosition
+     * @param pos current position (from previous lookup)
+     * @private
+     */
+    setStartPosition: function(pos) {
+        var p = pos || Dom.getXY(this.getEl());
+
+        this.deltaSetXY = null;
+
+        this.startPageX = p[0];
+        this.startPageY = p[1];
+    },
+
+    /**
+     * Add this instance to a group of related drag/drop objects.  All 
+     * instances belong to at least one group, and can belong to as many 
+     * groups as needed.
+     * @method addToGroup
+     * @param sGroup {string} the name of the group
+     */
+    addToGroup: function(sGroup) {
+        this.groups[sGroup] = true;
+        this.DDM.regDragDrop(this, sGroup);
+    },
+
+    /**
+     * Remove's this instance from the supplied interaction group
+     * @method removeFromGroup
+     * @param {string}  sGroup  The group to drop
+     */
+    removeFromGroup: function(sGroup) {
+        this.logger.log("Removing from group: " + sGroup);
+        if (this.groups[sGroup]) {
+            delete this.groups[sGroup];
+        }
+
+        this.DDM.removeDDFromGroup(this, sGroup);
+    },
+
+    /**
+     * Allows you to specify that an element other than the linked element 
+     * will be moved with the cursor during a drag
+     * @method setDragElId
+     * @param id {string} the id of the element that will be used to initiate the drag
+     */
+    setDragElId: function(id) {
+        this.dragElId = id;
+    },
+
+    /**
+     * Allows you to specify a child of the linked element that should be 
+     * used to initiate the drag operation.  An example of this would be if 
+     * you have a content div with text and links.  Clicking anywhere in the 
+     * content area would normally start the drag operation.  Use this method
+     * to specify that an element inside of the content div is the element 
+     * that starts the drag operation.
+     * @method setHandleElId
+     * @param id {string} the id of the element that will be used to 
+     * initiate the drag.
+     */
+    setHandleElId: function(id) {
+        if (typeof id !== "string") {
+            YAHOO.log("id is not a string, assuming it is an HTMLElement");
+            id = Dom.generateId(id);
+        }
+        this.handleElId = id;
+        this.DDM.regHandle(this.id, id);
+    },
+
+    /**
+     * Allows you to set an element outside of the linked element as a drag 
+     * handle
+     * @method setOuterHandleElId
+     * @param id the id of the element that will be used to initiate the drag
+     */
+    setOuterHandleElId: function(id) {
+        if (typeof id !== "string") {
+            YAHOO.log("id is not a string, assuming it is an HTMLElement");
+            id = Dom.generateId(id);
+        }
+        this.logger.log("Adding outer handle event: " + id);
+        Event.on(id, "mousedown", 
+                this.handleMouseDown, this, true);
+        this.setHandleElId(id);
+
+        this.hasOuterHandles = true;
+    },
+
+    /**
+     * Remove all drag and drop hooks for this element
+     * @method unreg
+     */
+    unreg: function() {
+        this.logger.log("DragDrop obj cleanup " + this.id);
+        Event.removeListener(this.id, "mousedown", 
+                this.handleMouseDown);
+        this._domRef = null;
+        this.DDM._remove(this);
+    },
+
+    /**
+     * Returns true if this instance is locked, or the drag drop mgr is locked
+     * (meaning that all drag/drop is disabled on the page.)
+     * @method isLocked
+     * @return {boolean} true if this obj or all drag/drop is locked, else 
+     * false
+     */
+    isLocked: function() {
+        return (this.DDM.isLocked() || this.locked);
+    },
+
+    /**
+     * Fired when this object is clicked
+     * @method handleMouseDown
+     * @param {Event} e 
+     * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj)
+     * @private
+     */
+    handleMouseDown: function(e, oDD) {
+
+        var button = e.which || e.button;
+        this.logger.log("button: " + button);
+
+        if (this.primaryButtonOnly && button > 1) {
+            this.logger.log("Mousedown was not produced by the primary button");
+            return;
+        }
+
+        if (this.isLocked()) {
+            this.logger.log("Drag and drop is disabled, aborting");
+            return;
+        }
+
+        this.logger.log("mousedown " + this.id);
+
+        this.logger.log("firing onMouseDown events");
+
+        // firing the mousedown events prior to calculating positions
+        var b4Return = this.b4MouseDown(e);
+        var mDownReturn = this.onMouseDown(e);
+
+        if ((b4Return === false) || (mDownReturn === false)) {
+            this.logger.log('b4MouseDown or onMouseDown returned false, exiting drag');
+            return;
+        }
+
+        this.DDM.refreshCache(this.groups);
+        // var self = this;
+        // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0);
+
+        // Only process the event if we really clicked within the linked 
+        // element.  The reason we make this check is that in the case that 
+        // another element was moved between the clicked element and the 
+        // cursor in the time between the mousedown and mouseup events. When 
+        // this happens, the element gets the next mousedown event 
+        // regardless of where on the screen it happened.  
+        var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e));
+        if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
+                this.logger.log("Click was not over the element: " + this.id);
+        } else {
+            if (this.clickValidator(e)) {
+
+                this.logger.log("click was a valid handle");
+
+                // set the initial element position
+                this.setStartPosition();
+
+                // start tracking mousemove distance and mousedown time to
+                // determine when to start the actual drag
+                this.DDM.handleMouseDown(e, this);
+
+                // this mousedown is mine
+                this.DDM.stopEvent(e);
+            } else {
+
+this.logger.log("clickValidator returned false, drag not initiated");
+
+            }
+        }
+    },
+
+    /**
+     * @method clickValidator
+     * @description Method validates that the clicked element
+     * was indeed the handle or a valid child of the handle
+     * @param {Event} e 
+     * @private
+     */
+    clickValidator: function(e) {
+        var target = Event.getTarget(e);
+        return ( this.isValidHandleChild(target) &&
+                    (this.id == this.handleElId || 
+                        this.DDM.handleWasClicked(target, this.id)) );
+    },
+
+    /**
+     * Finds the location the element should be placed if we want to move
+     * it to where the mouse location less the click offset would place us.
+     * @method getTargetCoord
+     * @param {int} iPageX the X coordinate of the click
+     * @param {int} iPageY the Y coordinate of the click
+     * @return an object that contains the coordinates (Object.x and Object.y)
+     * @private
+     */
+    getTargetCoord: function(iPageX, iPageY) {
+
+        // this.logger.log("getTargetCoord: " + iPageX + ", " + iPageY);
+
+        var x = iPageX - this.deltaX;
+        var y = iPageY - this.deltaY;
+
+        if (this.constrainX) {
+            if (x < this.minX) { x = this.minX; }
+            if (x > this.maxX) { x = this.maxX; }
+        }
+
+        if (this.constrainY) {
+            if (y < this.minY) { y = this.minY; }
+            if (y > this.maxY) { y = this.maxY; }
+        }
+
+        x = this.getTick(x, this.xTicks);
+        y = this.getTick(y, this.yTicks);
+
+        // this.logger.log("getTargetCoord " + 
+                // " iPageX: " + iPageX +
+                // " iPageY: " + iPageY +
+                // " x: " + x + ", y: " + y);
+
+        return {x:x, y:y};
+    },
+
+    /**
+     * Allows you to specify a tag name that should not start a drag operation
+     * when clicked.  This is designed to facilitate embedding links within a
+     * drag handle that do something other than start the drag.
+     * @method addInvalidHandleType
+     * @param {string} tagName the type of element to exclude
+     */
+    addInvalidHandleType: function(tagName) {
+        var type = tagName.toUpperCase();
+        this.invalidHandleTypes[type] = type;
+    },
+
+    /**
+     * Lets you to specify an element id for a child of a drag handle
+     * that should not initiate a drag
+     * @method addInvalidHandleId
+     * @param {string} id the element id of the element you wish to ignore
+     */
+    addInvalidHandleId: function(id) {
+        if (typeof id !== "string") {
+            YAHOO.log("id is not a string, assuming it is an HTMLElement");
+            id = Dom.generateId(id);
+        }
+        this.invalidHandleIds[id] = id;
+    },
+
+
+    /**
+     * Lets you specify a css class of elements that will not initiate a drag
+     * @method addInvalidHandleClass
+     * @param {string} cssClass the class of the elements you wish to ignore
+     */
+    addInvalidHandleClass: function(cssClass) {
+        this.invalidHandleClasses.push(cssClass);
+    },
+
+    /**
+     * Unsets an excluded tag name set by addInvalidHandleType
+     * @method removeInvalidHandleType
+     * @param {string} tagName the type of element to unexclude
+     */
+    removeInvalidHandleType: function(tagName) {
+        var type = tagName.toUpperCase();
+        // this.invalidHandleTypes[type] = null;
+        delete this.invalidHandleTypes[type];
+    },
+    
+    /**
+     * Unsets an invalid handle id
+     * @method removeInvalidHandleId
+     * @param {string} id the id of the element to re-enable
+     */
+    removeInvalidHandleId: function(id) {
+        if (typeof id !== "string") {
+            YAHOO.log("id is not a string, assuming it is an HTMLElement");
+            id = Dom.generateId(id);
+        }
+        delete this.invalidHandleIds[id];
+    },
+
+    /**
+     * Unsets an invalid css class
+     * @method removeInvalidHandleClass
+     * @param {string} cssClass the class of the element(s) you wish to 
+     * re-enable
+     */
+    removeInvalidHandleClass: function(cssClass) {
+        for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
+            if (this.invalidHandleClasses[i] == cssClass) {
+                delete this.invalidHandleClasses[i];
+            }
+        }
+    },
+
+    /**
+     * Checks the tag exclusion list to see if this click should be ignored
+     * @method isValidHandleChild
+     * @param {HTMLElement} node the HTMLElement to evaluate
+     * @return {boolean} true if this is a valid tag type, false if not
+     */
+    isValidHandleChild: function(node) {
+
+        var valid = true;
+        // var n = (node.nodeName == "#text") ? node.parentNode : node;
+        var nodeName;
+        try {
+            nodeName = node.nodeName.toUpperCase();
+        } catch(e) {
+            nodeName = node.nodeName;
+        }
+        valid = valid && !this.invalidHandleTypes[nodeName];
+        valid = valid && !this.invalidHandleIds[node.id];
+
+        for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
+            valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
+        }
+
+        this.logger.log("Valid handle? ... " + valid);
+
+        return valid;
+
+    },
+
+    /**
+     * Create the array of horizontal tick marks if an interval was specified
+     * in setXConstraint().
+     * @method setXTicks
+     * @private
+     */
+    setXTicks: function(iStartX, iTickSize) {
+        this.xTicks = [];
+        this.xTickSize = iTickSize;
+        
+        var tickMap = {};
+
+        for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
+            if (!tickMap[i]) {
+                this.xTicks[this.xTicks.length] = i;
+                tickMap[i] = true;
+            }
+        }
+
+        for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
+            if (!tickMap[i]) {
+                this.xTicks[this.xTicks.length] = i;
+                tickMap[i] = true;
+            }
+        }
+
+        this.xTicks.sort(this.DDM.numericSort) ;
+        this.logger.log("xTicks: " + this.xTicks.join());
+    },
+
+    /**
+     * Create the array of vertical tick marks if an interval was specified in 
+     * setYConstraint().
+     * @method setYTicks
+     * @private
+     */
+    setYTicks: function(iStartY, iTickSize) {
+        // this.logger.log("setYTicks: " + iStartY + ", " + iTickSize
+               // + ", " + this.initPageY + ", " + this.minY + ", " + this.maxY );
+        this.yTicks = [];
+        this.yTickSize = iTickSize;
+
+        var tickMap = {};
+
+        for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
+            if (!tickMap[i]) {
+                this.yTicks[this.yTicks.length] = i;
+                tickMap[i] = true;
+            }
+        }
+
+        for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
+            if (!tickMap[i]) {
+                this.yTicks[this.yTicks.length] = i;
+                tickMap[i] = true;
+            }
+        }
+
+        this.yTicks.sort(this.DDM.numericSort) ;
+        this.logger.log("yTicks: " + this.yTicks.join());
+    },
+
+    /**
+     * By default, the element can be dragged any place on the screen.  Use 
+     * this method to limit the horizontal travel of the element.  Pass in 
+     * 0,0 for the parameters if you want to lock the drag to the y axis.
+     * @method setXConstraint
+     * @param {int} iLeft the number of pixels the element can move to the left
+     * @param {int} iRight the number of pixels the element can move to the 
+     * right
+     * @param {int} iTickSize optional parameter for specifying that the 
+     * element
+     * should move iTickSize pixels at a time.
+     */
+    setXConstraint: function(iLeft, iRight, iTickSize) {
+        this.leftConstraint = parseInt(iLeft, 10);
+        this.rightConstraint = parseInt(iRight, 10);
+
+        this.minX = this.initPageX - this.leftConstraint;
+        this.maxX = this.initPageX + this.rightConstraint;
+        if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
+
+        this.constrainX = true;
+        this.logger.log("initPageX:" + this.initPageX + " minX:" + this.minX + 
+                " maxX:" + this.maxX);
+    },
+
+    /**
+     * Clears any constraints applied to this instance.  Also clears ticks
+     * since they can't exist independent of a constraint at this time.
+     * @method clearConstraints
+     */
+    clearConstraints: function() {
+        this.logger.log("Clearing constraints");
+        this.constrainX = false;
+        this.constrainY = false;
+        this.clearTicks();
+    },
+
+    /**
+     * Clears any tick interval defined for this instance
+     * @method clearTicks
+     */
+    clearTicks: function() {
+        this.logger.log("Clearing ticks");
+        this.xTicks = null;
+        this.yTicks = null;
+        this.xTickSize = 0;
+        this.yTickSize = 0;
+    },
+
+    /**
+     * By default, the element can be dragged any place on the screen.  Set 
+     * this to limit the vertical travel of the element.  Pass in 0,0 for the
+     * parameters if you want to lock the drag to the x axis.
+     * @method setYConstraint
+     * @param {int} iUp the number of pixels the element can move up
+     * @param {int} iDown the number of pixels the element can move down
+     * @param {int} iTickSize optional parameter for specifying that the 
+     * element should move iTickSize pixels at a time.
+     */
+    setYConstraint: function(iUp, iDown, iTickSize) {
+        this.logger.log("setYConstraint: " + iUp + "," + iDown + "," + iTickSize);
+        this.topConstraint = parseInt(iUp, 10);
+        this.bottomConstraint = parseInt(iDown, 10);
+
+        this.minY = this.initPageY - this.topConstraint;
+        this.maxY = this.initPageY + this.bottomConstraint;
+        if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
+
+        this.constrainY = true;
+        
+        this.logger.log("initPageY:" + this.initPageY + " minY:" + this.minY + 
+                " maxY:" + this.maxY);
+    },
+
+    /**
+     * resetConstraints must be called if you manually reposition a dd element.
+     * @method resetConstraints
+     */
+    resetConstraints: function() {
+
+        //this.logger.log("resetConstraints");
+
+        // Maintain offsets if necessary
+        if (this.initPageX || this.initPageX === 0) {
+            //this.logger.log("init pagexy: " + this.initPageX + ", " + 
+                               //this.initPageY);
+            //this.logger.log("last pagexy: " + this.lastPageX + ", " + 
+                               //this.lastPageY);
+            // figure out how much this thing has moved
+            var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
+            var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
+
+            this.setInitPosition(dx, dy);
+
+        // This is the first time we have detected the element's position
+        } else {
+            this.setInitPosition();
+        }
+
+        if (this.constrainX) {
+            this.setXConstraint( this.leftConstraint, 
+                                 this.rightConstraint, 
+                                 this.xTickSize        );
+        }
+
+        if (this.constrainY) {
+            this.setYConstraint( this.topConstraint, 
+                                 this.bottomConstraint, 
+                                 this.yTickSize         );
+        }
+    },
+
+    /**
+     * Normally the drag element is moved pixel by pixel, but we can specify 
+     * that it move a number of pixels at a time.  This method resolves the 
+     * location when we have it set up like this.
+     * @method getTick
+     * @param {int} val where we want to place the object
+     * @param {int[]} tickArray sorted array of valid points
+     * @return {int} the closest tick
+     * @private
+     */
+    getTick: function(val, tickArray) {
+
+        if (!tickArray) {
+            // If tick interval is not defined, it is effectively 1 pixel, 
+            // so we return the value passed to us.
+            return val; 
+        } else if (tickArray[0] >= val) {
+            // The value is lower than the first tick, so we return the first
+            // tick.
+            return tickArray[0];
+        } else {
+            for (var i=0, len=tickArray.length; i<len; ++i) {
+                var next = i + 1;
+                if (tickArray[next] && tickArray[next] >= val) {
+                    var diff1 = val - tickArray[i];
+                    var diff2 = tickArray[next] - val;
+                    return (diff2 > diff1) ? tickArray[i] : tickArray[next];
+                }
+            }
+
+            // The value is larger than the last tick, so we return the last
+            // tick.
+            return tickArray[tickArray.length - 1];
+        }
+    },
+
+    /**
+     * toString method
+     * @method toString
+     * @return {string} string representation of the dd obj
+     */
+    toString: function() {
+        return ("DragDrop " + this.id);
+    }
+
+};
+
+})();
+/**
+ * A DragDrop implementation where the linked element follows the 
+ * mouse cursor during a drag.
+ * @class DD
+ * @extends YAHOO.util.DragDrop
+ * @constructor
+ * @param {String} id the id of the linked element 
+ * @param {String} sGroup the group of related DragDrop items
+ * @param {object} config an object containing configurable attributes
+ *                Valid properties for DD: 
+ *                    scroll
+ */
+YAHOO.util.DD = function(id, sGroup, config) {
+    if (id) {
+        this.init(id, sGroup, config);
+    }
+};
+
+YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, {
+
+    /**
+     * When set to true, the utility automatically tries to scroll the browser
+     * window wehn a drag and drop element is dragged near the viewport boundary.
+     * Defaults to true.
+     * @property scroll
+     * @type boolean
+     */
+    scroll: true, 
+
+    /**
+     * Sets the pointer offset to the distance between the linked element's top 
+     * left corner and the location the element was clicked
+     * @method autoOffset
+     * @param {int} iPageX the X coordinate of the click
+     * @param {int} iPageY the Y coordinate of the click
+     */
+    autoOffset: function(iPageX, iPageY) {
+        var x = iPageX - this.startPageX;
+        var y = iPageY - this.startPageY;
+        this.setDelta(x, y);
+        // this.logger.log("autoOffset el pos: " + aCoord + ", delta: " + x + "," + y);
+    },
+
+    /** 
+     * Sets the pointer offset.  You can call this directly to force the 
+     * offset to be in a particular location (e.g., pass in 0,0 to set it 
+     * to the center of the object, as done in YAHOO.widget.Slider)
+     * @method setDelta
+     * @param {int} iDeltaX the distance from the left
+     * @param {int} iDeltaY the distance from the top
+     */
+    setDelta: function(iDeltaX, iDeltaY) {
+        this.deltaX = iDeltaX;
+        this.deltaY = iDeltaY;
+        this.logger.log("deltaX:" + this.deltaX + ", deltaY:" + this.deltaY);
+    },
+
+    /**
+     * Sets the drag element to the location of the mousedown or click event, 
+     * maintaining the cursor location relative to the location on the element 
+     * that was clicked.  Override this if you want to place the element in a 
+     * location other than where the cursor is.
+     * @method setDragElPos
+     * @param {int} iPageX the X coordinate of the mousedown or drag event
+     * @param {int} iPageY the Y coordinate of the mousedown or drag event
+     */
+    setDragElPos: function(iPageX, iPageY) {
+        // the first time we do this, we are going to check to make sure
+        // the element has css positioning
+
+        var el = this.getDragEl();
+        this.alignElWithMouse(el, iPageX, iPageY);
+    },
+
+    /**
+     * Sets the element to the location of the mousedown or click event, 
+     * maintaining the cursor location relative to the location on the element 
+     * that was clicked.  Override this if you want to place the element in a 
+     * location other than where the cursor is.
+     * @method alignElWithMouse
+     * @param {HTMLElement} el the element to move
+     * @param {int} iPageX the X coordinate of the mousedown or drag event
+     * @param {int} iPageY the Y coordinate of the mousedown or drag event
+     */
+    alignElWithMouse: function(el, iPageX, iPageY) {
+        var oCoord = this.getTargetCoord(iPageX, iPageY);
+        // this.logger.log("****alignElWithMouse : " + el.id + ", " + aCoord + ", " + el.style.display);
+
+        if (!this.deltaSetXY) {
+            var aCoord = [oCoord.x, oCoord.y];
+            YAHOO.util.Dom.setXY(el, aCoord);
+            var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 );
+            var newTop  = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 );
+
+            this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
+        } else {
+            YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px");
+            YAHOO.util.Dom.setStyle(el, "top",  (oCoord.y + this.deltaSetXY[1]) + "px");
+            //el.style.left = (oCoord.x + this.deltaSetXY[0]) + "px";
+            //el.style.top = (oCoord.y + this.deltaSetXY[1]) + "px";
+        }
+        
+        this.cachePosition(oCoord.x, oCoord.y);
+        //DAV
+        var self = this;
+        setTimeout(function() {
+            self.autoScroll.call(self, oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
+        }, 0);
+    },
+
+    /**
+     * Saves the most recent position so that we can reset the constraints and
+     * tick marks on-demand.  We need to know this so that we can calculate the
+     * number of pixels the element is offset from its original position.
+     * @method cachePosition
+     * @param iPageX the current x position (optional, this just makes it so we
+     * don't have to look it up again)
+     * @param iPageY the current y position (optional, this just makes it so we
+     * don't have to look it up again)
+     */
+    cachePosition: function(iPageX, iPageY) {
+        if (iPageX) {
+            this.lastPageX = iPageX;
+            this.lastPageY = iPageY;
+        } else {
+            var aCoord = YAHOO.util.Dom.getXY(this.getEl());
+            this.lastPageX = aCoord[0];
+            this.lastPageY = aCoord[1];
+        }
+    },
+
+    /**
+     * Auto-scroll the window if the dragged object has been moved beyond the 
+     * visible window boundary.
+     * @method autoScroll
+     * @param {int} x the drag element's x position
+     * @param {int} y the drag element's y position
+     * @param {int} h the height of the drag element
+     * @param {int} w the width of the drag element
+     * @private
+     */
+    autoScroll: function(x, y, h, w) {
+
+        if (this.scroll) {
+            // The client height
+            var clientH = this.DDM.getClientHeight();
+
+            // The client width
+            var clientW = this.DDM.getClientWidth();
+
+            // The amt scrolled down
+            var st = this.DDM.getScrollTop();
+
+            // The amt scrolled right
+            var sl = this.DDM.getScrollLeft();
+
+            // Location of the bottom of the element
+            var bot = h + y;
+
+            // Location of the right of the element
+            var right = w + x;
+
+            // The distance from the cursor to the bottom of the visible area, 
+            // adjusted so that we don't scroll if the cursor is beyond the
+            // element drag constraints
+            var toBot = (clientH + st - y - this.deltaY);
+
+            // The distance from the cursor to the right of the visible area
+            var toRight = (clientW + sl - x - this.deltaX);
+
+            // this.logger.log( " x: " + x + " y: " + y + " h: " + h + 
+            // " clientH: " + clientH + " clientW: " + clientW + 
+            // " st: " + st + " sl: " + sl + " bot: " + bot + 
+            // " right: " + right + " toBot: " + toBot + " toRight: " + toRight);
+
+            // How close to the edge the cursor must be before we scroll
+            // var thresh = (document.all) ? 100 : 40;
+            var thresh = 40;
+
+            // How many pixels to scroll per autoscroll op.  This helps to reduce 
+            // clunky scrolling. IE is more sensitive about this ... it needs this 
+            // value to be higher.
+            var scrAmt = (document.all) ? 80 : 30;
+
+            // Scroll down if we are near the bottom of the visible page and the 
+            // obj extends below the crease
+            if ( bot > clientH && toBot < thresh ) { 
+                window.scrollTo(sl, st + scrAmt); 
+            }
+
+            // Scroll up if the window is scrolled down and the top of the object
+            // goes above the top border
+            if ( y < st && st > 0 && y - st < thresh ) { 
+                window.scrollTo(sl, st - scrAmt); 
+            }
+
+            // Scroll right if the obj is beyond the right border and the cursor is
+            // near the border.
+            if ( right > clientW && toRight < thresh ) { 
+                window.scrollTo(sl + scrAmt, st); 
+            }
+
+            // Scroll left if the window has been scrolled to the right and the obj
+            // extends past the left border
+            if ( x < sl && sl > 0 && x - sl < thresh ) { 
+                window.scrollTo(sl - scrAmt, st);
+            }
+        }
+    },
+
+    /*
+     * Sets up config options specific to this class. Overrides
+     * YAHOO.util.DragDrop, but all versions of this method through the 
+     * inheritance chain are called
+     */
+    applyConfig: function() {
+        YAHOO.util.DD.superclass.applyConfig.call(this);
+        this.scroll = (this.config.scroll !== false);
+    },
+
+    /*
+     * Event that fires prior to the onMouseDown event.  Overrides 
+     * YAHOO.util.DragDrop.
+     */
+    b4MouseDown: function(e) {
+        this.setStartPosition();
+        // this.resetConstraints();
+        this.autoOffset(YAHOO.util.Event.getPageX(e), 
+                            YAHOO.util.Event.getPageY(e));
+    },
+
+    /*
+     * Event that fires prior to the onDrag event.  Overrides 
+     * YAHOO.util.DragDrop.
+     */
+    b4Drag: function(e) {
+        this.setDragElPos(YAHOO.util.Event.getPageX(e), 
+                            YAHOO.util.Event.getPageY(e));
+    },
+
+    toString: function() {
+        return ("DD " + this.id);
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+    // Debugging ygDragDrop events that can be overridden
+    //////////////////////////////////////////////////////////////////////////
+    /*
+    startDrag: function(x, y) {
+        this.logger.log(this.id.toString()  + " startDrag");
+    },
+
+    onDrag: function(e) {
+        this.logger.log(this.id.toString() + " onDrag");
+    },
+
+    onDragEnter: function(e, id) {
+        this.logger.log(this.id.toString() + " onDragEnter: " + id);
+    },
+
+    onDragOver: function(e, id) {
+        this.logger.log(this.id.toString() + " onDragOver: " + id);
+    },
+
+    onDragOut: function(e, id) {
+        this.logger.log(this.id.toString() + " onDragOut: " + id);
+    },
+
+    onDragDrop: function(e, id) {
+        this.logger.log(this.id.toString() + " onDragDrop: " + id);
+    },
+
+    endDrag: function(e) {
+        this.logger.log(this.id.toString() + " endDrag");
+    }
+
+    */
+
+});
+/**
+ * A DragDrop implementation that inserts an empty, bordered div into
+ * the document that follows the cursor during drag operations.  At the time of
+ * the click, the frame div is resized to the dimensions of the linked html
+ * element, and moved to the exact location of the linked element.
+ *
+ * References to the "frame" element refer to the single proxy element that
+ * was created to be dragged in place of all DDProxy elements on the
+ * page.
+ *
+ * @class DDProxy
+ * @extends YAHOO.util.DD
+ * @constructor
+ * @param {String} id the id of the linked html element
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ *                Valid properties for DDProxy in addition to those in DragDrop: 
+ *                   resizeFrame, centerFrame, dragElId
+ */
+YAHOO.util.DDProxy = function(id, sGroup, config) {
+    if (id) {
+        this.init(id, sGroup, config);
+        this.initFrame(); 
+    }
+};
+
+/**
+ * The default drag frame div id
+ * @property YAHOO.util.DDProxy.dragElId
+ * @type String
+ * @static
+ */
+YAHOO.util.DDProxy.dragElId = "ygddfdiv";
+
+YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, {
+
+    /**
+     * By default we resize the drag frame to be the same size as the element
+     * we want to drag (this is to get the frame effect).  We can turn it off
+     * if we want a different behavior.
+     * @property resizeFrame
+     * @type boolean
+     */
+    resizeFrame: true,
+
+    /**
+     * By default the frame is positioned exactly where the drag element is, so
+     * we use the cursor offset provided by YAHOO.util.DD.  Another option that works only if
+     * you do not have constraints on the obj is to have the drag frame centered
+     * around the cursor.  Set centerFrame to true for this effect.
+     * @property centerFrame
+     * @type boolean
+     */
+    centerFrame: false,
+
+    /**
+     * Creates the proxy element if it does not yet exist
+     * @method createFrame
+     */
+    createFrame: function() {
+        var self=this, body=document.body;
+
+        if (!body || !body.firstChild) {
+            setTimeout( function() { self.createFrame(); }, 50 );
+            return;
+        }
+
+        var div=this.getDragEl(), Dom=YAHOO.util.Dom;
+
+        if (!div) {
+            div    = document.createElement("div");
+            div.id = this.dragElId;
+            var s  = div.style;
+
+            s.position   = "absolute";
+            s.visibility = "hidden";
+            s.cursor     = "move";
+            s.border     = "2px solid #aaa";
+            s.zIndex     = 999;
+            s.height     = "25px";
+            s.width      = "25px";
+
+            var _data = document.createElement('div');
+            Dom.setStyle(_data, 'height', '100%');
+            Dom.setStyle(_data, 'width', '100%');
+            /**
+            * If the proxy element has no background-color, then it is considered to the "transparent" by Internet Explorer.
+            * Since it is "transparent" then the events pass through it to the iframe below.
+            * So creating a "fake" div inside the proxy element and giving it a background-color, then setting it to an
+            * opacity of 0, it appears to not be there, however IE still thinks that it is so the events never pass through.
+            */
+            Dom.setStyle(_data, 'background-color', '#ccc');
+            Dom.setStyle(_data, 'opacity', '0');
+            div.appendChild(_data);
+
+            // appendChild can blow up IE if invoked prior to the window load event
+            // while rendering a table.  It is possible there are other scenarios 
+            // that would cause this to happen as well.
+            body.insertBefore(div, body.firstChild);
+        }
+    },
+
+    /**
+     * Initialization for the drag frame element.  Must be called in the
+     * constructor of all subclasses
+     * @method initFrame
+     */
+    initFrame: function() {
+        this.createFrame();
+    },
+
+    applyConfig: function() {
+        //this.logger.log("DDProxy applyConfig");
+        YAHOO.util.DDProxy.superclass.applyConfig.call(this);
+
+        this.resizeFrame = (this.config.resizeFrame !== false);
+        this.centerFrame = (this.config.centerFrame);
+        this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId);
+    },
+
+    /**
+     * Resizes the drag frame to the dimensions of the clicked object, positions 
+     * it over the object, and finally displays it
+     * @method showFrame
+     * @param {int} iPageX X click position
+     * @param {int} iPageY Y click position
+     * @private
+     */
+    showFrame: function(iPageX, iPageY) {
+        var el = this.getEl();
+        var dragEl = this.getDragEl();
+        var s = dragEl.style;
+
+        this._resizeProxy();
+
+        if (this.centerFrame) {
+            this.setDelta( Math.round(parseInt(s.width,  10)/2), 
+                           Math.round(parseInt(s.height, 10)/2) );
+        }
+
+        this.setDragElPos(iPageX, iPageY);
+
+        YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible"); 
+    },
+
+    /**
+     * The proxy is automatically resized to the dimensions of the linked
+     * element when a drag is initiated, unless resizeFrame is set to false
+     * @method _resizeProxy
+     * @private
+     */
+    _resizeProxy: function() {
+        if (this.resizeFrame) {
+            var DOM    = YAHOO.util.Dom;
+            var el     = this.getEl();
+            var dragEl = this.getDragEl();
+
+            var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth"    ), 10);
+            var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth"  ), 10);
+            var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10);
+            var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth"   ), 10);
+
+            if (isNaN(bt)) { bt = 0; }
+            if (isNaN(br)) { br = 0; }
+            if (isNaN(bb)) { bb = 0; }
+            if (isNaN(bl)) { bl = 0; }
+
+            this.logger.log("proxy size: " + bt + "  " + br + " " + bb + " " + bl);
+
+            var newWidth  = Math.max(0, el.offsetWidth  - br - bl);                                                                                           
+            var newHeight = Math.max(0, el.offsetHeight - bt - bb);
+
+            this.logger.log("Resizing proxy element");
+
+            DOM.setStyle( dragEl, "width",  newWidth  + "px" );
+            DOM.setStyle( dragEl, "height", newHeight + "px" );
+        }
+    },
+
+    // overrides YAHOO.util.DragDrop
+    b4MouseDown: function(e) {
+        this.setStartPosition();
+        var x = YAHOO.util.Event.getPageX(e);
+        var y = YAHOO.util.Event.getPageY(e);
+        this.autoOffset(x, y);
+
+        // This causes the autoscroll code to kick off, which means autoscroll can
+        // happen prior to the check for a valid drag handle.
+        // this.setDragElPos(x, y);
+    },
+
+    // overrides YAHOO.util.DragDrop
+    b4StartDrag: function(x, y) {
+        // show the drag frame
+        this.logger.log("start drag show frame, x: " + x + ", y: " + y);
+        this.showFrame(x, y);
+    },
+
+    // overrides YAHOO.util.DragDrop
+    b4EndDrag: function(e) {
+        this.logger.log(this.id + " b4EndDrag");
+        YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden"); 
+    },
+
+    // overrides YAHOO.util.DragDrop
+    // By default we try to move the element to the last location of the frame.  
+    // This is so that the default behavior mirrors that of YAHOO.util.DD.  
+    endDrag: function(e) {
+        var DOM = YAHOO.util.Dom;
+        this.logger.log(this.id + " endDrag");
+        var lel = this.getEl();
+        var del = this.getDragEl();
+
+        // Show the drag frame briefly so we can get its position
+        // del.style.visibility = "";
+        DOM.setStyle(del, "visibility", ""); 
+
+        // Hide the linked element before the move to get around a Safari 
+        // rendering bug.
+        //lel.style.visibility = "hidden";
+        DOM.setStyle(lel, "visibility", "hidden"); 
+        YAHOO.util.DDM.moveToEl(lel, del);
+        //del.style.visibility = "hidden";
+        DOM.setStyle(del, "visibility", "hidden"); 
+        //lel.style.visibility = "";
+        DOM.setStyle(lel, "visibility", ""); 
+    },
+
+    toString: function() {
+        return ("DDProxy " + this.id);
+    }
+
+});
+/**
+ * A DragDrop implementation that does not move, but can be a drop 
+ * target.  You would get the same result by simply omitting implementation 
+ * for the event callbacks, but this way we reduce the processing cost of the 
+ * event listener and the callbacks.
+ * @class DDTarget
+ * @extends YAHOO.util.DragDrop 
+ * @constructor
+ * @param {String} id the id of the element that is a drop target
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ *                 Valid properties for DDTarget in addition to those in 
+ *                 DragDrop: 
+ *                    none
+ */
+YAHOO.util.DDTarget = function(id, sGroup, config) {
+    if (id) {
+        this.initTarget(id, sGroup, config);
+    }
+};
+
+// YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
+YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, {
+    toString: function() {
+        return ("DDTarget " + this.id);
+    }
+});
+YAHOO.register("dragdrop", YAHOO.util.DragDropMgr, {version: "2.4.1", build: "742"});
Index: trunk/wb/include/yui/dragdrop/README
===================================================================
--- trunk/wb/include/yui/dragdrop/README	(nonexistent)
+++ trunk/wb/include/yui/dragdrop/README	(revision 601)
@@ -0,0 +1,162 @@
+Drag and Drop Release Notes
+
+2.4.1
+No change
+
+2.4.0
+  * Added configuration option called "dragOnly". If dragOnly is set to true,
+    all event in the fireEvents method will not fire. These events are:
+        onInvalidDrop
+        b4DragOut & onDragOut
+        onDragEnter
+        b4DragOver & onDragOver
+        b4DragDrop & onDragDrop
+    This config option should be used to drag elements that have no need for
+        drop interaction. They are elements that just need to move.
+
+2.3.1
+  * No change
+
+2.3.0
+  * YAHOO.util.DragDropMgr.stopDrag is now public, and can be used to cancel
+    a drag in progress.  An optional "silent" flag was added to skip the
+    onMouseUp and endDrag functions when needed (eliminating the need to
+    supply mouseup page coordinates to these functions).
+  * DDProxy: the position of the proxy is not set before the drag is confirmed,
+    preventing auto-scroll from distrupting the user experience.
+  * Modified the default proxy so that IE properly registers the proxy as
+    the event target during the drag.
+  * If a dd instance is created using a dom reference rather than an id, that
+    reference is stored and used throughout making it possible to control
+    instances outside the current window.
+  * The document mousemove listener no longer returns true.
+
+2.2.2
+  * No change
+
+2.2.1
+
+  * Added YAHOO.util.DragDropMgr.interactionInfo, which is
+    a repository of interaction information accumulated during
+    the current event loop result, and accessible from the
+    handlers for the events.
+  * The region for the dragged element is now cached while
+    processing the drag and drop events
+  * List example supports moving an item to an empty list
+  * Fixed missing html tags in the examples
+  * The debug version now works when included before the logger is included.
+
+2.2.0
+
+  * onMouseDown event is executed before element positions are calculated
+  * refreshCache refreshes everything if groups array is not provided
+  * setX/setYConstraint doesn't fail when presented ints cast as strings
+
+0.12.2
+
+  * No change
+
+0.12.1
+
+  * Added a STRICT_INTERSECT drag and drop interaction mode.  This alters the
+    behavior of DDM::getBestMatch.  INTERSECT mode first tests the cursor
+    location, and if it is over the target that target wins, otherwise it
+    tests the overlap of the elements.  STRICT_INTERSECT mode tests only 
+    the overlap, the largest overlap wins.
+
+  * getBestMatch will work for targeted elements that have no overlap.
+
+0.12.0
+
+   * The logic to determine if a drag should be initiated has been isolated
+     to the clickValidator method.  This method can be overridden to provide
+     custom valdiation logic.  For example, it is possible to specify hotspots
+     of any dimension or shape.  The provided example shows how to make only
+     a circular region in the middle of the element initiate a drag.
+
+   * Added a new drag and drop event: onInvalidDrop.  This is executed when
+     the dragged element in dropped in a location without a target.  Previously
+     this condition could only detected by implementing handlers for three 
+     other events.
+
+   * Now accepts an element reference in lieu of an id.  Ids will
+     be generated if the element does not have one.
+
+   * Fixed horizontal autoscroll when scrollTop is zero.
+
+   * Added hasOuterHandles property to bypass the isOverTarget check in the
+     mousedown validation routine.  Fixes setOuterHandleElId.
+
+0.11.4
+
+   * YAHOO.util.DragDropMgr.swapNode now handles adjacent nodes properly
+   
+   * Fixed missing variable declarations
+
+0.11.3
+ 
+   * Fixed a JavaScript error that would be generated when trying to implement
+     DDProxy using the default settings and a tiny element.
+
+   * Fixed an error that resulted when constraints were applied to DragDrop 
+     instances.
+
+0.11.2
+
+   * Drag and drop will no longer interfere with selecting text on elements
+     that are not involved in drag and drop.
+
+   * The shared drag and drop proxy element now resizes correctly when autoResize
+     is enabled.
+
+0.11.1
+
+   * Fixes an issue where the setXY cache could get out of sync if the element's
+     offsetParent is changed during onDragDrop.
+
+0.11.0
+
+   * The Dom.util.setXY calculation for the initial placement of the dragged
+     element is cached during the drag, enhancing the drag performance.
+
+   * DDProxy no longer enforces having a single proxy element for all instances.
+     dragElId can be set in the config object in the constructor.  If the
+     element already exists it will use that element, otherwise a new one will
+     be created with that id.
+
+   * DDProxy->borderWidth has been removed. The value is calculated on the fly
+     instead.
+
+   * Added DragDrop->clearTicks and DragDrop->clearConstraints
+
+   * All drag and drop constructors now have an additional, optional parameter
+     call "config".  It is an object that can contain properties for a
+     number of configuration settings.
+
+   * Drag and drop will not be disabled for elements that cannot have their
+     location determined.
+
+   * isLegalTarget won't return dd objects that are not targetable.
+
+   * Added DragDrop->removeFromGroup.
+
+   * Constraints are now applied properly when determining which drag and drop
+     events should fire.
+
+
+0.10.0
+
+   * Improved the performance when in intersect mode
+
+   * It was possible for the drag and drop initialization to be skipped
+     for very slow loading pages.  This was fixed.
+
+   * New methods to exclude regions within your drag and drop element: 
+     addInvalidHandleId(), addInvalidHandleClass()
+
+   * Added an onAvailable handler that is executed after the initial state is set.
+
+   * Drag and drop is more forgiving when the implementer attempts to create the
+     instance prior to the element being in the document, but after the window
+     load event has fired.
+
Index: trunk/wb/include/yui/dragdrop/dragdrop.js
===================================================================
--- trunk/wb/include/yui/dragdrop/dragdrop.js	(nonexistent)
+++ trunk/wb/include/yui/dragdrop/dragdrop.js	(revision 601)
@@ -0,0 +1,3145 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+/**
+ * The drag and drop utility provides a framework for building drag and drop
+ * applications.  In addition to enabling drag and drop for specific elements,
+ * the drag and drop elements are tracked by the manager class, and the
+ * interactions between the various elements are tracked during the drag and
+ * the implementing code is notified about these important moments.
+ * @module dragdrop
+ * @title Drag and Drop
+ * @requires yahoo,dom,event
+ * @namespace YAHOO.util
+ */
+
+// Only load the library once.  Rewriting the manager class would orphan 
+// existing drag and drop instances.
+if (!YAHOO.util.DragDropMgr) {
+
+/**
+ * DragDropMgr is a singleton that tracks the element interaction for 
+ * all DragDrop items in the window.  Generally, you will not call 
+ * this class directly, but it does have helper methods that could 
+ * be useful in your DragDrop implementations.
+ * @class DragDropMgr
+ * @static
+ */
+YAHOO.util.DragDropMgr = function() {
+
+    var Event = YAHOO.util.Event;
+
+    return {
+
+        /**
+         * Two dimensional Array of registered DragDrop objects.  The first 
+         * dimension is the DragDrop item group, the second the DragDrop 
+         * object.
+         * @property ids
+         * @type {string: string}
+         * @private
+         * @static
+         */
+        ids: {},
+
+        /**
+         * Array of element ids defined as drag handles.  Used to determine 
+         * if the element that generated the mousedown event is actually the 
+         * handle and not the html element itself.
+         * @property handleIds
+         * @type {string: string}
+         * @private
+         * @static
+         */
+        handleIds: {},
+
+        /**
+         * the DragDrop object that is currently being dragged
+         * @property dragCurrent
+         * @type DragDrop
+         * @private
+         * @static
+         **/
+        dragCurrent: null,
+
+        /**
+         * the DragDrop object(s) that are being hovered over
+         * @property dragOvers
+         * @type Array
+         * @private
+         * @static
+         */
+        dragOvers: {},
+
+        /**
+         * the X distance between the cursor and the object being dragged
+         * @property deltaX
+         * @type int
+         * @private
+         * @static
+         */
+        deltaX: 0,
+
+        /**
+         * the Y distance between the cursor and the object being dragged
+         * @property deltaY
+         * @type int
+         * @private
+         * @static
+         */
+        deltaY: 0,
+
+        /**
+         * Flag to determine if we should prevent the default behavior of the
+         * events we define. By default this is true, but this can be set to 
+         * false if you need the default behavior (not recommended)
+         * @property preventDefault
+         * @type boolean
+         * @static
+         */
+        preventDefault: true,
+
+        /**
+         * Flag to determine if we should stop the propagation of the events 
+         * we generate. This is true by default but you may want to set it to
+         * false if the html element contains other features that require the
+         * mouse click.
+         * @property stopPropagation
+         * @type boolean
+         * @static
+         */
+        stopPropagation: true,
+
+        /**
+         * Internal flag that is set to true when drag and drop has been
+         * initialized
+         * @property initialized
+         * @private
+         * @static
+         */
+        initialized: false,
+
+        /**
+         * All drag and drop can be disabled.
+         * @property locked
+         * @private
+         * @static
+         */
+        locked: false,
+
+        /**
+         * Provides additional information about the the current set of
+         * interactions.  Can be accessed from the event handlers. It
+         * contains the following properties:
+         *
+         *       out:       onDragOut interactions
+         *       enter:     onDragEnter interactions
+         *       over:      onDragOver interactions
+         *       drop:      onDragDrop interactions
+         *       point:     The location of the cursor
+         *       draggedRegion: The location of dragged element at the time
+         *                      of the interaction
+         *       sourceRegion: The location of the source elemtn at the time
+         *                     of the interaction
+         *       validDrop: boolean
+         * @property interactionInfo
+         * @type object
+         * @static
+         */
+        interactionInfo: null,
+
+        /**
+         * Called the first time an element is registered.
+         * @method init
+         * @private
+         * @static
+         */
+        init: function() {
+            this.initialized = true;
+        },
+
+        /**
+         * In point mode, drag and drop interaction is defined by the 
+         * location of the cursor during the drag/drop
+         * @property POINT
+         * @type int
+         * @static
+         * @final
+         */
+        POINT: 0,
+
+        /**
+         * In intersect mode, drag and drop interaction is defined by the 
+         * cursor position or the amount of overlap of two or more drag and 
+         * drop objects.
+         * @property INTERSECT
+         * @type int
+         * @static
+         * @final
+         */
+        INTERSECT: 1,
+
+        /**
+         * In intersect mode, drag and drop interaction is defined only by the 
+         * overlap of two or more drag and drop objects.
+         * @property STRICT_INTERSECT
+         * @type int
+         * @static
+         * @final
+         */
+        STRICT_INTERSECT: 2,
+
+        /**
+         * The current drag and drop mode.  Default: POINT
+         * @property mode
+         * @type int
+         * @static
+         */
+        mode: 0,
+
+        /**
+         * Runs method on all drag and drop objects
+         * @method _execOnAll
+         * @private
+         * @static
+         */
+        _execOnAll: function(sMethod, args) {
+            for (var i in this.ids) {
+                for (var j in this.ids[i]) {
+                    var oDD = this.ids[i][j];
+                    if (! this.isTypeOfDD(oDD)) {
+                        continue;
+                    }
+                    oDD[sMethod].apply(oDD, args);
+                }
+            }
+        },
+
+        /**
+         * Drag and drop initialization.  Sets up the global event handlers
+         * @method _onLoad
+         * @private
+         * @static
+         */
+        _onLoad: function() {
+
+            this.init();
+
+
+            Event.on(document, "mouseup",   this.handleMouseUp, this, true);
+            Event.on(document, "mousemove", this.handleMouseMove, this, true);
+            Event.on(window,   "unload",    this._onUnload, this, true);
+            Event.on(window,   "resize",    this._onResize, this, true);
+            // Event.on(window,   "mouseout",    this._test);
+
+        },
+
+        /**
+         * Reset constraints on all drag and drop objs
+         * @method _onResize
+         * @private
+         * @static
+         */
+        _onResize: function(e) {
+            this._execOnAll("resetConstraints", []);
+        },
+
+        /**
+         * Lock all drag and drop functionality
+         * @method lock
+         * @static
+         */
+        lock: function() { this.locked = true; },
+
+        /**
+         * Unlock all drag and drop functionality
+         * @method unlock
+         * @static
+         */
+        unlock: function() { this.locked = false; },
+
+        /**
+         * Is drag and drop locked?
+         * @method isLocked
+         * @return {boolean} True if drag and drop is locked, false otherwise.
+         * @static
+         */
+        isLocked: function() { return this.locked; },
+
+        /**
+         * Location cache that is set for all drag drop objects when a drag is
+         * initiated, cleared when the drag is finished.
+         * @property locationCache
+         * @private
+         * @static
+         */
+        locationCache: {},
+
+        /**
+         * Set useCache to false if you want to force object the lookup of each
+         * drag and drop linked element constantly during a drag.
+         * @property useCache
+         * @type boolean
+         * @static
+         */
+        useCache: true,
+
+        /**
+         * The number of pixels that the mouse needs to move after the 
+         * mousedown before the drag is initiated.  Default=3;
+         * @property clickPixelThresh
+         * @type int
+         * @static
+         */
+        clickPixelThresh: 3,
+
+        /**
+         * The number of milliseconds after the mousedown event to initiate the
+         * drag if we don't get a mouseup event. Default=1000
+         * @property clickTimeThresh
+         * @type int
+         * @static
+         */
+        clickTimeThresh: 1000,
+
+        /**
+         * Flag that indicates that either the drag pixel threshold or the 
+         * mousdown time threshold has been met
+         * @property dragThreshMet
+         * @type boolean
+         * @private
+         * @static
+         */
+        dragThreshMet: false,
+
+        /**
+         * Timeout used for the click time threshold
+         * @property clickTimeout
+         * @type Object
+         * @private
+         * @static
+         */
+        clickTimeout: null,
+
+        /**
+         * The X position of the mousedown event stored for later use when a 
+         * drag threshold is met.
+         * @property startX
+         * @type int
+         * @private
+         * @static
+         */
+        startX: 0,
+
+        /**
+         * The Y position of the mousedown event stored for later use when a 
+         * drag threshold is met.
+         * @property startY
+         * @type int
+         * @private
+         * @static
+         */
+        startY: 0,
+
+        /**
+         * Flag to determine if the drag event was fired from the click timeout and
+         * not the mouse move threshold.
+         * @property fromTimeout
+         * @type boolean
+         * @private
+         * @static
+         */
+        fromTimeout: false,
+
+        /**
+         * Each DragDrop instance must be registered with the DragDropMgr.  
+         * This is executed in DragDrop.init()
+         * @method regDragDrop
+         * @param {DragDrop} oDD the DragDrop object to register
+         * @param {String} sGroup the name of the group this element belongs to
+         * @static
+         */
+        regDragDrop: function(oDD, sGroup) {
+            if (!this.initialized) { this.init(); }
+            
+            if (!this.ids[sGroup]) {
+                this.ids[sGroup] = {};
+            }
+            this.ids[sGroup][oDD.id] = oDD;
+        },
+
+        /**
+         * Removes the supplied dd instance from the supplied group. Executed
+         * by DragDrop.removeFromGroup, so don't call this function directly.
+         * @method removeDDFromGroup
+         * @private
+         * @static
+         */
+        removeDDFromGroup: function(oDD, sGroup) {
+            if (!this.ids[sGroup]) {
+                this.ids[sGroup] = {};
+            }
+
+            var obj = this.ids[sGroup];
+            if (obj && obj[oDD.id]) {
+                delete obj[oDD.id];
+            }
+        },
+
+        /**
+         * Unregisters a drag and drop item.  This is executed in 
+         * DragDrop.unreg, use that method instead of calling this directly.
+         * @method _remove
+         * @private
+         * @static
+         */
+        _remove: function(oDD) {
+            for (var g in oDD.groups) {
+                if (g && this.ids[g][oDD.id]) {
+                    delete this.ids[g][oDD.id];
+                }
+            }
+            delete this.handleIds[oDD.id];
+        },
+
+        /**
+         * Each DragDrop handle element must be registered.  This is done
+         * automatically when executing DragDrop.setHandleElId()
+         * @method regHandle
+         * @param {String} sDDId the DragDrop id this element is a handle for
+         * @param {String} sHandleId the id of the element that is the drag 
+         * handle
+         * @static
+         */
+        regHandle: function(sDDId, sHandleId) {
+            if (!this.handleIds[sDDId]) {
+                this.handleIds[sDDId] = {};
+            }
+            this.handleIds[sDDId][sHandleId] = sHandleId;
+        },
+
+        /**
+         * Utility function to determine if a given element has been 
+         * registered as a drag drop item.
+         * @method isDragDrop
+         * @param {String} id the element id to check
+         * @return {boolean} true if this element is a DragDrop item, 
+         * false otherwise
+         * @static
+         */
+        isDragDrop: function(id) {
+            return ( this.getDDById(id) ) ? true : false;
+        },
+
+        /**
+         * Returns the drag and drop instances that are in all groups the
+         * passed in instance belongs to.
+         * @method getRelated
+         * @param {DragDrop} p_oDD the obj to get related data for
+         * @param {boolean} bTargetsOnly if true, only return targetable objs
+         * @return {DragDrop[]} the related instances
+         * @static
+         */
+        getRelated: function(p_oDD, bTargetsOnly) {
+            var oDDs = [];
+            for (var i in p_oDD.groups) {
+                for (var j in this.ids[i]) {
+                    var dd = this.ids[i][j];
+                    if (! this.isTypeOfDD(dd)) {
+                        continue;
+                    }
+                    if (!bTargetsOnly || dd.isTarget) {
+                        oDDs[oDDs.length] = dd;
+                    }
+                }
+            }
+
+            return oDDs;
+        },
+
+        /**
+         * Returns true if the specified dd target is a legal target for 
+         * the specifice drag obj
+         * @method isLegalTarget
+         * @param {DragDrop} the drag obj
+         * @param {DragDrop} the target
+         * @return {boolean} true if the target is a legal target for the 
+         * dd obj
+         * @static
+         */
+        isLegalTarget: function (oDD, oTargetDD) {
+            var targets = this.getRelated(oDD, true);
+            for (var i=0, len=targets.length;i<len;++i) {
+                if (targets[i].id == oTargetDD.id) {
+                    return true;
+                }
+            }
+
+            return false;
+        },
+
+        /**
+         * My goal is to be able to transparently determine if an object is
+         * typeof DragDrop, and the exact subclass of DragDrop.  typeof 
+         * returns "object", oDD.constructor.toString() always returns
+         * "DragDrop" and not the name of the subclass.  So for now it just
+         * evaluates a well-known variable in DragDrop.
+         * @method isTypeOfDD
+         * @param {Object} the object to evaluate
+         * @return {boolean} true if typeof oDD = DragDrop
+         * @static
+         */
+        isTypeOfDD: function (oDD) {
+            return (oDD && oDD.__ygDragDrop);
+        },
+
+        /**
+         * Utility function to determine if a given element has been 
+         * registered as a drag drop handle for the given Drag Drop object.
+         * @method isHandle
+         * @param {String} id the element id to check
+         * @return {boolean} true if this element is a DragDrop handle, false 
+         * otherwise
+         * @static
+         */
+        isHandle: function(sDDId, sHandleId) {
+            return ( this.handleIds[sDDId] && 
+                            this.handleIds[sDDId][sHandleId] );
+        },
+
+        /**
+         * Returns the DragDrop instance for a given id
+         * @method getDDById
+         * @param {String} id the id of the DragDrop object
+         * @return {DragDrop} the drag drop object, null if it is not found
+         * @static
+         */
+        getDDById: function(id) {
+            for (var i in this.ids) {
+                if (this.ids[i][id]) {
+                    return this.ids[i][id];
+                }
+            }
+            return null;
+        },
+
+        /**
+         * Fired after a registered DragDrop object gets the mousedown event.
+         * Sets up the events required to track the object being dragged
+         * @method handleMouseDown
+         * @param {Event} e the event
+         * @param oDD the DragDrop object being dragged
+         * @private
+         * @static
+         */
+        handleMouseDown: function(e, oDD) {
+
+            this.currentTarget = YAHOO.util.Event.getTarget(e);
+
+            this.dragCurrent = oDD;
+
+            var el = oDD.getEl();
+
+            // track start position
+            this.startX = YAHOO.util.Event.getPageX(e);
+            this.startY = YAHOO.util.Event.getPageY(e);
+
+            this.deltaX = this.startX - el.offsetLeft;
+            this.deltaY = this.startY - el.offsetTop;
+
+            this.dragThreshMet = false;
+
+            this.clickTimeout = setTimeout( 
+                    function() { 
+                        var DDM = YAHOO.util.DDM;
+                        DDM.startDrag(DDM.startX, DDM.startY);
+                        DDM.fromTimeout = true;
+                    }, 
+                    this.clickTimeThresh );
+        },
+
+        /**
+         * Fired when either the drag pixel threshol or the mousedown hold 
+         * time threshold has been met.
+         * @method startDrag
+         * @param x {int} the X position of the original mousedown
+         * @param y {int} the Y position of the original mousedown
+         * @static
+         */
+        startDrag: function(x, y) {
+            clearTimeout(this.clickTimeout);
+            var dc = this.dragCurrent;
+            if (dc) {
+                dc.b4StartDrag(x, y);
+            }
+            if (dc) {
+                dc.startDrag(x, y);
+            }
+            this.dragThreshMet = true;
+        },
+
+        /**
+         * Internal function to handle the mouseup event.  Will be invoked 
+         * from the context of the document.
+         * @method handleMouseUp
+         * @param {Event} e the event
+         * @private
+         * @static
+         */
+        handleMouseUp: function(e) {
+            if (this.dragCurrent) {
+                clearTimeout(this.clickTimeout);
+
+                if (this.dragThreshMet) {
+                    if (this.fromTimeout) {
+                        this.handleMouseMove(e);
+                    }
+                    this.fromTimeout = false;
+                    this.fireEvents(e, true);
+                } else {
+                }
+
+                this.stopDrag(e);
+
+                this.stopEvent(e);
+            }
+        },
+
+        /**
+         * Utility to stop event propagation and event default, if these 
+         * features are turned on.
+         * @method stopEvent
+         * @param {Event} e the event as returned by this.getEvent()
+         * @static
+         */
+        stopEvent: function(e) {
+            if (this.stopPropagation) {
+                YAHOO.util.Event.stopPropagation(e);
+            }
+
+            if (this.preventDefault) {
+                YAHOO.util.Event.preventDefault(e);
+            }
+        },
+
+        /** 
+         * Ends the current drag, cleans up the state, and fires the endDrag
+         * and mouseUp events.  Called internally when a mouseup is detected
+         * during the drag.  Can be fired manually during the drag by passing
+         * either another event (such as the mousemove event received in onDrag)
+         * or a fake event with pageX and pageY defined (so that endDrag and
+         * onMouseUp have usable position data.).  Alternatively, pass true
+         * for the silent parameter so that the endDrag and onMouseUp events
+         * are skipped (so no event data is needed.)
+         *
+         * @method stopDrag
+         * @param {Event} e the mouseup event, another event (or a fake event) 
+         *                  with pageX and pageY defined, or nothing if the 
+         *                  silent parameter is true
+         * @param {boolean} silent skips the enddrag and mouseup events if true
+         * @static
+         */
+        stopDrag: function(e, silent) {
+
+            // Fire the drag end event for the item that was dragged
+            if (this.dragCurrent && !silent) {
+                if (this.dragThreshMet) {
+                    this.dragCurrent.b4EndDrag(e);
+                    this.dragCurrent.endDrag(e);
+                }
+
+                this.dragCurrent.onMouseUp(e);
+            }
+
+            this.dragCurrent = null;
+            this.dragOvers = {};
+        },
+
+        /** 
+         * Internal function to handle the mousemove event.  Will be invoked 
+         * from the context of the html element.
+         *
+         * @TODO figure out what we can do about mouse events lost when the 
+         * user drags objects beyond the window boundary.  Currently we can 
+         * detect this in internet explorer by verifying that the mouse is 
+         * down during the mousemove event.  Firefox doesn't give us the 
+         * button state on the mousemove event.
+         * @method handleMouseMove
+         * @param {Event} e the event
+         * @private
+         * @static
+         */
+        handleMouseMove: function(e) {
+            
+            var dc = this.dragCurrent;
+            if (dc) {
+
+                // var button = e.which || e.button;
+
+                // check for IE mouseup outside of page boundary
+                if (YAHOO.util.Event.isIE && !e.button) {
+                    this.stopEvent(e);
+                    return this.handleMouseUp(e);
+                }
+
+                if (!this.dragThreshMet) {
+                    var diffX = Math.abs(this.startX - YAHOO.util.Event.getPageX(e));
+                    var diffY = Math.abs(this.startY - YAHOO.util.Event.getPageY(e));
+                    if (diffX > this.clickPixelThresh || 
+                                diffY > this.clickPixelThresh) {
+                        this.startDrag(this.startX, this.startY);
+                    }
+                }
+
+                if (this.dragThreshMet) {
+                    dc.b4Drag(e);
+                    if (dc) {
+                        dc.onDrag(e);
+                    }
+                    if (dc) {
+                        this.fireEvents(e, false);
+                    }
+                }
+
+                this.stopEvent(e);
+            }
+        },
+        
+        /**
+         * Iterates over all of the DragDrop elements to find ones we are 
+         * hovering over or dropping on
+         * @method fireEvents
+         * @param {Event} e the event
+         * @param {boolean} isDrop is this a drop op or a mouseover op?
+         * @private
+         * @static
+         */
+        fireEvents: function(e, isDrop) {
+            var dc = this.dragCurrent;
+
+            // If the user did the mouse up outside of the window, we could 
+            // get here even though we have ended the drag.
+            // If the config option dragOnly is true, bail out and don't fire the events
+            if (!dc || dc.isLocked() || dc.dragOnly) {
+                return;
+            }
+
+            var x = YAHOO.util.Event.getPageX(e),
+                y = YAHOO.util.Event.getPageY(e),
+                pt = new YAHOO.util.Point(x,y),
+                pos = dc.getTargetCoord(pt.x, pt.y),
+                el = dc.getDragEl(),
+                curRegion = new YAHOO.util.Region( pos.y, 
+                                               pos.x + el.offsetWidth,
+                                               pos.y + el.offsetHeight, 
+                                               pos.x ),
+            
+                oldOvers = [], // cache the previous dragOver array
+                outEvts   = [],
+                overEvts  = [],
+                dropEvts  = [],
+                enterEvts = [],
+                inGroupsObj  = {},
+                inGroups  = [];
+
+
+            // Check to see if the object(s) we were hovering over is no longer 
+            // being hovered over so we can fire the onDragOut event
+            for (var i in this.dragOvers) {
+
+                var ddo = this.dragOvers[i];
+
+                if (! this.isTypeOfDD(ddo)) {
+                    continue;
+                }
+                if (! this.isOverTarget(pt, ddo, this.mode, curRegion)) {
+                    outEvts.push( ddo );
+                }
+
+                oldOvers[i] = true;
+                delete this.dragOvers[i];
+            }
+
+            for (var sGroup in dc.groups) {
+                
+                if ("string" != typeof sGroup) {
+                    continue;
+                }
+
+                for (i in this.ids[sGroup]) {
+                    var oDD = this.ids[sGroup][i];
+                    if (! this.isTypeOfDD(oDD)) {
+                        continue;
+                    }
+
+                    if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
+                        if (this.isOverTarget(pt, oDD, this.mode, curRegion)) {
+                            inGroupsObj[sGroup] = true;
+                            // look for drop interactions
+                            if (isDrop) {
+                                dropEvts.push( oDD );
+                            // look for drag enter and drag over interactions
+                            } else {
+
+                                // initial drag over: dragEnter fires
+                                if (!oldOvers[oDD.id]) {
+                                    enterEvts.push( oDD );
+                                // subsequent drag overs: dragOver fires
+                                } else {
+                                    overEvts.push( oDD );
+                                }
+
+                                this.dragOvers[oDD.id] = oDD;
+                            }
+                        }
+                    }
+                }
+            }
+
+            this.interactionInfo = {
+                out:       outEvts,
+                enter:     enterEvts,
+                over:      overEvts,
+                drop:      dropEvts,
+                point:     pt,
+                draggedRegion:    curRegion,
+                sourceRegion: this.locationCache[dc.id],
+                validDrop: isDrop
+            };
+
+            
+            for (var inG in inGroupsObj) {
+                inGroups.push(inG);
+            }
+
+            // notify about a drop that did not find a target
+            if (isDrop && !dropEvts.length) {
+                this.interactionInfo.validDrop = false;
+                dc.onInvalidDrop(e);
+            }
+
+
+            if (this.mode) {
+                if (outEvts.length) {
+                    dc.b4DragOut(e, outEvts);
+                    if (dc) {
+                        dc.onDragOut(e, outEvts);
+                    }
+                }
+
+                if (enterEvts.length) {
+                    if (dc) {
+                        dc.onDragEnter(e, enterEvts, inGroups);
+                    }
+                }
+
+                if (overEvts.length) {
+                    if (dc) {
+                        dc.b4DragOver(e, overEvts, inGroups);
+                    }
+
+                    if (dc) {
+                        dc.onDragOver(e, overEvts, inGroups);
+                    }
+                }
+
+                if (dropEvts.length) {
+                    if (dc) {
+                        dc.b4DragDrop(e, dropEvts, inGroups);
+                    }
+                    if (dc) {
+                        dc.onDragDrop(e, dropEvts, inGroups);
+                    }
+                }
+
+            } else {
+                // fire dragout events
+                var len = 0;
+                for (i=0, len=outEvts.length; i<len; ++i) {
+                    if (dc) {
+                        dc.b4DragOut(e, outEvts[i].id, inGroups[0]);
+                    }
+                    if (dc) {
+                        dc.onDragOut(e, outEvts[i].id, inGroups[0]);
+                    }
+                }
+                 
+                // fire enter events
+                for (i=0,len=enterEvts.length; i<len; ++i) {
+                    // dc.b4DragEnter(e, oDD.id);
+
+                    if (dc) {
+                        dc.onDragEnter(e, enterEvts[i].id, inGroups[0]);
+                    }
+                }
+         
+                // fire over events
+                for (i=0,len=overEvts.length; i<len; ++i) {
+                    if (dc) {
+                        dc.b4DragOver(e, overEvts[i].id, inGroups[0]);
+                    }
+                    if (dc) {
+                        dc.onDragOver(e, overEvts[i].id, inGroups[0]);
+                    }
+                }
+
+                // fire drop events
+                for (i=0, len=dropEvts.length; i<len; ++i) {
+                    if (dc) {
+                        dc.b4DragDrop(e, dropEvts[i].id, inGroups[0]);
+                    }
+                    if (dc) {
+                        dc.onDragDrop(e, dropEvts[i].id, inGroups[0]);
+                    }
+                }
+
+            }
+        },
+
+        /**
+         * Helper function for getting the best match from the list of drag 
+         * and drop objects returned by the drag and drop events when we are 
+         * in INTERSECT mode.  It returns either the first object that the 
+         * cursor is over, or the object that has the greatest overlap with 
+         * the dragged element.
+         * @method getBestMatch
+         * @param  {DragDrop[]} dds The array of drag and drop objects 
+         * targeted
+         * @return {DragDrop}       The best single match
+         * @static
+         */
+        getBestMatch: function(dds) {
+            var winner = null;
+
+            var len = dds.length;
+
+            if (len == 1) {
+                winner = dds[0];
+            } else {
+                // Loop through the targeted items
+                for (var i=0; i<len; ++i) {
+                    var dd = dds[i];
+                    // If the cursor is over the object, it wins.  If the 
+                    // cursor is over multiple matches, the first one we come
+                    // to wins.
+                    if (this.mode == this.INTERSECT && dd.cursorIsOver) {
+                        winner = dd;
+                        break;
+                    // Otherwise the object with the most overlap wins
+                    } else {
+                        if (!winner || !winner.overlap || (dd.overlap &&
+                            winner.overlap.getArea() < dd.overlap.getArea())) {
+                            winner = dd;
+                        }
+                    }
+                }
+            }
+
+            return winner;
+        },
+
+        /**
+         * Refreshes the cache of the top-left and bottom-right points of the 
+         * drag and drop objects in the specified group(s).  This is in the
+         * format that is stored in the drag and drop instance, so typical 
+         * usage is:
+         * <code>
+         * YAHOO.util.DragDropMgr.refreshCache(ddinstance.groups);
+         * </code>
+         * Alternatively:
+         * <code>
+         * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true});
+         * </code>
+         * @TODO this really should be an indexed array.  Alternatively this
+         * method could accept both.
+         * @method refreshCache
+         * @param {Object} groups an associative array of groups to refresh
+         * @static
+         */
+        refreshCache: function(groups) {
+
+            // refresh everything if group array is not provided
+            var g = groups || this.ids;
+
+            for (var sGroup in g) {
+                if ("string" != typeof sGroup) {
+                    continue;
+                }
+                for (var i in this.ids[sGroup]) {
+                    var oDD = this.ids[sGroup][i];
+
+                    if (this.isTypeOfDD(oDD)) {
+                        var loc = this.getLocation(oDD);
+                        if (loc) {
+                            this.locationCache[oDD.id] = loc;
+                        } else {
+                            delete this.locationCache[oDD.id];
+                        }
+                    }
+                }
+            }
+        },
+
+        /**
+         * This checks to make sure an element exists and is in the DOM.  The
+         * main purpose is to handle cases where innerHTML is used to remove
+         * drag and drop objects from the DOM.  IE provides an 'unspecified
+         * error' when trying to access the offsetParent of such an element
+         * @method verifyEl
+         * @param {HTMLElement} el the element to check
+         * @return {boolean} true if the element looks usable
+         * @static
+         */
+        verifyEl: function(el) {
+            try {
+                if (el) {
+                    var parent = el.offsetParent;
+                    if (parent) {
+                        return true;
+                    }
+                }
+            } catch(e) {
+            }
+
+            return false;
+        },
+        
+        /**
+         * Returns a Region object containing the drag and drop element's position
+         * and size, including the padding configured for it
+         * @method getLocation
+         * @param {DragDrop} oDD the drag and drop object to get the 
+         *                       location for
+         * @return {YAHOO.util.Region} a Region object representing the total area
+         *                             the element occupies, including any padding
+         *                             the instance is configured for.
+         * @static
+         */
+        getLocation: function(oDD) {
+            if (! this.isTypeOfDD(oDD)) {
+                return null;
+            }
+
+            var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
+
+            try {
+                pos= YAHOO.util.Dom.getXY(el);
+            } catch (e) { }
+
+            if (!pos) {
+                return null;
+            }
+
+            x1 = pos[0];
+            x2 = x1 + el.offsetWidth;
+            y1 = pos[1];
+            y2 = y1 + el.offsetHeight;
+
+            t = y1 - oDD.padding[0];
+            r = x2 + oDD.padding[1];
+            b = y2 + oDD.padding[2];
+            l = x1 - oDD.padding[3];
+
+            return new YAHOO.util.Region( t, r, b, l );
+        },
+
+        /**
+         * Checks the cursor location to see if it over the target
+         * @method isOverTarget
+         * @param {YAHOO.util.Point} pt The point to evaluate
+         * @param {DragDrop} oTarget the DragDrop object we are inspecting
+         * @param {boolean} intersect true if we are in intersect mode
+         * @param {YAHOO.util.Region} pre-cached location of the dragged element
+         * @return {boolean} true if the mouse is over the target
+         * @private
+         * @static
+         */
+        isOverTarget: function(pt, oTarget, intersect, curRegion) {
+            // use cache if available
+            var loc = this.locationCache[oTarget.id];
+            if (!loc || !this.useCache) {
+                loc = this.getLocation(oTarget);
+                this.locationCache[oTarget.id] = loc;
+
+            }
+
+            if (!loc) {
+                return false;
+            }
+
+            oTarget.cursorIsOver = loc.contains( pt );
+
+            // DragDrop is using this as a sanity check for the initial mousedown
+            // in this case we are done.  In POINT mode, if the drag obj has no
+            // contraints, we are done. Otherwise we need to evaluate the 
+            // region the target as occupies to determine if the dragged element
+            // overlaps with it.
+            
+            var dc = this.dragCurrent;
+            if (!dc || (!intersect && !dc.constrainX && !dc.constrainY)) {
+
+                //if (oTarget.cursorIsOver) {
+                //}
+                return oTarget.cursorIsOver;
+            }
+
+            oTarget.overlap = null;
+
+            // Get the current location of the drag element, this is the
+            // location of the mouse event less the delta that represents
+            // where the original mousedown happened on the element.  We
+            // need to consider constraints and ticks as well.
+
+            if (!curRegion) {
+                var pos = dc.getTargetCoord(pt.x, pt.y);
+                var el = dc.getDragEl();
+                curRegion = new YAHOO.util.Region( pos.y, 
+                                                   pos.x + el.offsetWidth,
+                                                   pos.y + el.offsetHeight, 
+                                                   pos.x );
+            }
+
+            var overlap = curRegion.intersect(loc);
+
+            if (overlap) {
+                oTarget.overlap = overlap;
+                return (intersect) ? true : oTarget.cursorIsOver;
+            } else {
+                return false;
+            }
+        },
+
+        /**
+         * unload event handler
+         * @method _onUnload
+         * @private
+         * @static
+         */
+        _onUnload: function(e, me) {
+            this.unregAll();
+        },
+
+        /**
+         * Cleans up the drag and drop events and objects.
+         * @method unregAll
+         * @private
+         * @static
+         */
+        unregAll: function() {
+
+            if (this.dragCurrent) {
+                this.stopDrag();
+                this.dragCurrent = null;
+            }
+
+            this._execOnAll("unreg", []);
+
+            //for (var i in this.elementCache) {
+                //delete this.elementCache[i];
+            //}
+            //this.elementCache = {};
+
+            this.ids = {};
+        },
+
+        /**
+         * A cache of DOM elements
+         * @property elementCache
+         * @private
+         * @static
+         * @deprecated elements are not cached now
+         */
+        elementCache: {},
+        
+        /**
+         * Get the wrapper for the DOM element specified
+         * @method getElWrapper
+         * @param {String} id the id of the element to get
+         * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
+         * @private
+         * @deprecated This wrapper isn't that useful
+         * @static
+         */
+        getElWrapper: function(id) {
+            var oWrapper = this.elementCache[id];
+            if (!oWrapper || !oWrapper.el) {
+                oWrapper = this.elementCache[id] = 
+                    new this.ElementWrapper(YAHOO.util.Dom.get(id));
+            }
+            return oWrapper;
+        },
+
+        /**
+         * Returns the actual DOM element
+         * @method getElement
+         * @param {String} id the id of the elment to get
+         * @return {Object} The element
+         * @deprecated use YAHOO.util.Dom.get instead
+         * @static
+         */
+        getElement: function(id) {
+            return YAHOO.util.Dom.get(id);
+        },
+        
+        /**
+         * Returns the style property for the DOM element (i.e., 
+         * document.getElById(id).style)
+         * @method getCss
+         * @param {String} id the id of the elment to get
+         * @return {Object} The style property of the element
+         * @deprecated use YAHOO.util.Dom instead
+         * @static
+         */
+        getCss: function(id) {
+            var el = YAHOO.util.Dom.get(id);
+            return (el) ? el.style : null;
+        },
+
+        /**
+         * Inner class for cached elements
+         * @class DragDropMgr.ElementWrapper
+         * @for DragDropMgr
+         * @private
+         * @deprecated
+         */
+        ElementWrapper: function(el) {
+                /**
+                 * The element
+                 * @property el
+                 */
+                this.el = el || null;
+                /**
+                 * The element id
+                 * @property id
+                 */
+                this.id = this.el && el.id;
+                /**
+                 * A reference to the style property
+                 * @property css
+                 */
+                this.css = this.el && el.style;
+            },
+
+        /**
+         * Returns the X position of an html element
+         * @method getPosX
+         * @param el the element for which to get the position
+         * @return {int} the X coordinate
+         * @for DragDropMgr
+         * @deprecated use YAHOO.util.Dom.getX instead
+         * @static
+         */
+        getPosX: function(el) {
+            return YAHOO.util.Dom.getX(el);
+        },
+
+        /**
+         * Returns the Y position of an html element
+         * @method getPosY
+         * @param el the element for which to get the position
+         * @return {int} the Y coordinate
+         * @deprecated use YAHOO.util.Dom.getY instead
+         * @static
+         */
+        getPosY: function(el) {
+            return YAHOO.util.Dom.getY(el); 
+        },
+
+        /**
+         * Swap two nodes.  In IE, we use the native method, for others we 
+         * emulate the IE behavior
+         * @method swapNode
+         * @param n1 the first node to swap
+         * @param n2 the other node to swap
+         * @static
+         */
+        swapNode: function(n1, n2) {
+            if (n1.swapNode) {
+                n1.swapNode(n2);
+            } else {
+                var p = n2.parentNode;
+                var s = n2.nextSibling;
+
+                if (s == n1) {
+                    p.insertBefore(n1, n2);
+                } else if (n2 == n1.nextSibling) {
+                    p.insertBefore(n2, n1);
+                } else {
+                    n1.parentNode.replaceChild(n2, n1);
+                    p.insertBefore(n1, s);
+                }
+            }
+        },
+
+        /**
+         * Returns the current scroll position
+         * @method getScroll
+         * @private
+         * @static
+         */
+        getScroll: function () {
+            var t, l, dde=document.documentElement, db=document.body;
+            if (dde && (dde.scrollTop || dde.scrollLeft)) {
+                t = dde.scrollTop;
+                l = dde.scrollLeft;
+            } else if (db) {
+                t = db.scrollTop;
+                l = db.scrollLeft;
+            } else {
+            }
+            return { top: t, left: l };
+        },
+
+        /**
+         * Returns the specified element style property
+         * @method getStyle
+         * @param {HTMLElement} el          the element
+         * @param {string}      styleProp   the style property
+         * @return {string} The value of the style property
+         * @deprecated use YAHOO.util.Dom.getStyle
+         * @static
+         */
+        getStyle: function(el, styleProp) {
+            return YAHOO.util.Dom.getStyle(el, styleProp);
+        },
+
+        /**
+         * Gets the scrollTop
+         * @method getScrollTop
+         * @return {int} the document's scrollTop
+         * @static
+         */
+        getScrollTop: function () { return this.getScroll().top; },
+
+        /**
+         * Gets the scrollLeft
+         * @method getScrollLeft
+         * @return {int} the document's scrollTop
+         * @static
+         */
+        getScrollLeft: function () { return this.getScroll().left; },
+
+        /**
+         * Sets the x/y position of an element to the location of the
+         * target element.
+         * @method moveToEl
+         * @param {HTMLElement} moveEl      The element to move
+         * @param {HTMLElement} targetEl    The position reference element
+         * @static
+         */
+        moveToEl: function (moveEl, targetEl) {
+            var aCoord = YAHOO.util.Dom.getXY(targetEl);
+            YAHOO.util.Dom.setXY(moveEl, aCoord);
+        },
+
+        /**
+         * Gets the client height
+         * @method getClientHeight
+         * @return {int} client height in px
+         * @deprecated use YAHOO.util.Dom.getViewportHeight instead
+         * @static
+         */
+        getClientHeight: function() {
+            return YAHOO.util.Dom.getViewportHeight();
+        },
+
+        /**
+         * Gets the client width
+         * @method getClientWidth
+         * @return {int} client width in px
+         * @deprecated use YAHOO.util.Dom.getViewportWidth instead
+         * @static
+         */
+        getClientWidth: function() {
+            return YAHOO.util.Dom.getViewportWidth();
+        },
+
+        /**
+         * Numeric array sort function
+         * @method numericSort
+         * @static
+         */
+        numericSort: function(a, b) { return (a - b); },
+
+        /**
+         * Internal counter
+         * @property _timeoutCount
+         * @private
+         * @static
+         */
+        _timeoutCount: 0,
+
+        /**
+         * Trying to make the load order less important.  Without this we get
+         * an error if this file is loaded before the Event Utility.
+         * @method _addListeners
+         * @private
+         * @static
+         */
+        _addListeners: function() {
+            var DDM = YAHOO.util.DDM;
+            if ( YAHOO.util.Event && document ) {
+                DDM._onLoad();
+            } else {
+                if (DDM._timeoutCount > 2000) {
+                } else {
+                    setTimeout(DDM._addListeners, 10);
+                    if (document && document.body) {
+                        DDM._timeoutCount += 1;
+                    }
+                }
+            }
+        },
+
+        /**
+         * Recursively searches the immediate parent and all child nodes for 
+         * the handle element in order to determine wheter or not it was 
+         * clicked.
+         * @method handleWasClicked
+         * @param node the html element to inspect
+         * @static
+         */
+        handleWasClicked: function(node, id) {
+            if (this.isHandle(id, node.id)) {
+                return true;
+            } else {
+                // check to see if this is a text node child of the one we want
+                var p = node.parentNode;
+
+                while (p) {
+                    if (this.isHandle(id, p.id)) {
+                        return true;
+                    } else {
+                        p = p.parentNode;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+    };
+
+}();
+
+// shorter alias, save a few bytes
+YAHOO.util.DDM = YAHOO.util.DragDropMgr;
+YAHOO.util.DDM._addListeners();
+
+}
+
+(function() {
+
+var Event=YAHOO.util.Event; 
+var Dom=YAHOO.util.Dom;
+
+/**
+ * Defines the interface and base operation of items that that can be 
+ * dragged or can be drop targets.  It was designed to be extended, overriding
+ * the event handlers for startDrag, onDrag, onDragOver, onDragOut.
+ * Up to three html elements can be associated with a DragDrop instance:
+ * <ul>
+ * <li>linked element: the element that is passed into the constructor.
+ * This is the element which defines the boundaries for interaction with 
+ * other DragDrop objects.</li>
+ * <li>handle element(s): The drag operation only occurs if the element that 
+ * was clicked matches a handle element.  By default this is the linked 
+ * element, but there are times that you will want only a portion of the 
+ * linked element to initiate the drag operation, and the setHandleElId() 
+ * method provides a way to define this.</li>
+ * <li>drag element: this represents an the element that would be moved along
+ * with the cursor during a drag operation.  By default, this is the linked
+ * element itself as in {@link YAHOO.util.DD}.  setDragElId() lets you define
+ * a separate element that would be moved, as in {@link YAHOO.util.DDProxy}
+ * </li>
+ * </ul>
+ * This class should not be instantiated until the onload event to ensure that
+ * the associated elements are available.
+ * The following would define a DragDrop obj that would interact with any 
+ * other DragDrop obj in the "group1" group:
+ * <pre>
+ *  dd = new YAHOO.util.DragDrop("div1", "group1");
+ * </pre>
+ * Since none of the event handlers have been implemented, nothing would 
+ * actually happen if you were to run the code above.  Normally you would 
+ * override this class or one of the default implementations, but you can 
+ * also override the methods you want on an instance of the class...
+ * <pre>
+ *  dd.onDragDrop = function(e, id) {
+ *  &nbsp;&nbsp;alert("dd was dropped on " + id);
+ *  }
+ * </pre>
+ * @namespace YAHOO.util
+ * @class DragDrop
+ * @constructor
+ * @param {String} id of the element that is linked to this instance
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ *                Valid properties for DragDrop: 
+ *                    padding, isTarget, maintainOffset, primaryButtonOnly,
+ */
+YAHOO.util.DragDrop = function(id, sGroup, config) {
+    if (id) {
+        this.init(id, sGroup, config); 
+    }
+};
+
+YAHOO.util.DragDrop.prototype = {
+
+    /**
+     * The id of the element associated with this object.  This is what we 
+     * refer to as the "linked element" because the size and position of 
+     * this element is used to determine when the drag and drop objects have 
+     * interacted.
+     * @property id
+     * @type String
+     */
+    id: null,
+
+    /**
+     * Configuration attributes passed into the constructor
+     * @property config
+     * @type object
+     */
+    config: null,
+
+    /**
+     * The id of the element that will be dragged.  By default this is same 
+     * as the linked element , but could be changed to another element. Ex: 
+     * YAHOO.util.DDProxy
+     * @property dragElId
+     * @type String
+     * @private
+     */
+    dragElId: null, 
+
+    /**
+     * the id of the element that initiates the drag operation.  By default 
+     * this is the linked element, but could be changed to be a child of this
+     * element.  This lets us do things like only starting the drag when the 
+     * header element within the linked html element is clicked.
+     * @property handleElId
+     * @type String
+     * @private
+     */
+    handleElId: null, 
+
+    /**
+     * An associative array of HTML tags that will be ignored if clicked.
+     * @property invalidHandleTypes
+     * @type {string: string}
+     */
+    invalidHandleTypes: null, 
+
+    /**
+     * An associative array of ids for elements that will be ignored if clicked
+     * @property invalidHandleIds
+     * @type {string: string}
+     */
+    invalidHandleIds: null, 
+
+    /**
+     * An indexted array of css class names for elements that will be ignored
+     * if clicked.
+     * @property invalidHandleClasses
+     * @type string[]
+     */
+    invalidHandleClasses: null, 
+
+    /**
+     * The linked element's absolute X position at the time the drag was 
+     * started
+     * @property startPageX
+     * @type int
+     * @private
+     */
+    startPageX: 0,
+
+    /**
+     * The linked element's absolute X position at the time the drag was 
+     * started
+     * @property startPageY
+     * @type int
+     * @private
+     */
+    startPageY: 0,
+
+    /**
+     * The group defines a logical collection of DragDrop objects that are 
+     * related.  Instances only get events when interacting with other 
+     * DragDrop object in the same group.  This lets us define multiple 
+     * groups using a single DragDrop subclass if we want.
+     * @property groups
+     * @type {string: string}
+     */
+    groups: null,
+
+    /**
+     * Individual drag/drop instances can be locked.  This will prevent 
+     * onmousedown start drag.
+     * @property locked
+     * @type boolean
+     * @private
+     */
+    locked: false,
+
+    /**
+     * Lock this instance
+     * @method lock
+     */
+    lock: function() { this.locked = true; },
+
+    /**
+     * Unlock this instace
+     * @method unlock
+     */
+    unlock: function() { this.locked = false; },
+
+    /**
+     * By default, all instances can be a drop target.  This can be disabled by
+     * setting isTarget to false.
+     * @property isTarget
+     * @type boolean
+     */
+    isTarget: true,
+
+    /**
+     * The padding configured for this drag and drop object for calculating
+     * the drop zone intersection with this object.
+     * @property padding
+     * @type int[]
+     */
+    padding: null,
+    /**
+     * If this flag is true, do not fire drop events. The element is a drag only element (for movement not dropping)
+     * @property dragOnly
+     * @type Boolean
+     */
+    dragOnly: false,
+
+    /**
+     * Cached reference to the linked element
+     * @property _domRef
+     * @private
+     */
+    _domRef: null,
+
+    /**
+     * Internal typeof flag
+     * @property __ygDragDrop
+     * @private
+     */
+    __ygDragDrop: true,
+
+    /**
+     * Set to true when horizontal contraints are applied
+     * @property constrainX
+     * @type boolean
+     * @private
+     */
+    constrainX: false,
+
+    /**
+     * Set to true when vertical contraints are applied
+     * @property constrainY
+     * @type boolean
+     * @private
+     */
+    constrainY: false,
+
+    /**
+     * The left constraint
+     * @property minX
+     * @type int
+     * @private
+     */
+    minX: 0,
+
+    /**
+     * The right constraint
+     * @property maxX
+     * @type int
+     * @private
+     */
+    maxX: 0,
+
+    /**
+     * The up constraint 
+     * @property minY
+     * @type int
+     * @type int
+     * @private
+     */
+    minY: 0,
+
+    /**
+     * The down constraint 
+     * @property maxY
+     * @type int
+     * @private
+     */
+    maxY: 0,
+
+    /**
+     * The difference between the click position and the source element's location
+     * @property deltaX
+     * @type int
+     * @private
+     */
+    deltaX: 0,
+
+    /**
+     * The difference between the click position and the source element's location
+     * @property deltaY
+     * @type int
+     * @private
+     */
+    deltaY: 0,
+
+    /**
+     * Maintain offsets when we resetconstraints.  Set to true when you want
+     * the position of the element relative to its parent to stay the same
+     * when the page changes
+     *
+     * @property maintainOffset
+     * @type boolean
+     */
+    maintainOffset: false,
+
+    /**
+     * Array of pixel locations the element will snap to if we specified a 
+     * horizontal graduation/interval.  This array is generated automatically
+     * when you define a tick interval.
+     * @property xTicks
+     * @type int[]
+     */
+    xTicks: null,
+
+    /**
+     * Array of pixel locations the element will snap to if we specified a 
+     * vertical graduation/interval.  This array is generated automatically 
+     * when you define a tick interval.
+     * @property yTicks
+     * @type int[]
+     */
+    yTicks: null,
+
+    /**
+     * By default the drag and drop instance will only respond to the primary
+     * button click (left button for a right-handed mouse).  Set to true to
+     * allow drag and drop to start with any mouse click that is propogated
+     * by the browser
+     * @property primaryButtonOnly
+     * @type boolean
+     */
+    primaryButtonOnly: true,
+
+    /**
+     * The availabe property is false until the linked dom element is accessible.
+     * @property available
+     * @type boolean
+     */
+    available: false,
+
+    /**
+     * By default, drags can only be initiated if the mousedown occurs in the
+     * region the linked element is.  This is done in part to work around a
+     * bug in some browsers that mis-report the mousedown if the previous
+     * mouseup happened outside of the window.  This property is set to true
+     * if outer handles are defined.
+     *
+     * @property hasOuterHandles
+     * @type boolean
+     * @default false
+     */
+    hasOuterHandles: false,
+
+    /**
+     * Property that is assigned to a drag and drop object when testing to
+     * see if it is being targeted by another dd object.  This property
+     * can be used in intersect mode to help determine the focus of
+     * the mouse interaction.  DDM.getBestMatch uses this property first to
+     * determine the closest match in INTERSECT mode when multiple targets
+     * are part of the same interaction.
+     * @property cursorIsOver
+     * @type boolean
+     */
+    cursorIsOver: false,
+
+    /**
+     * Property that is assigned to a drag and drop object when testing to
+     * see if it is being targeted by another dd object.  This is a region
+     * that represents the area the draggable element overlaps this target.
+     * DDM.getBestMatch uses this property to compare the size of the overlap
+     * to that of other targets in order to determine the closest match in
+     * INTERSECT mode when multiple targets are part of the same interaction.
+     * @property overlap 
+     * @type YAHOO.util.Region
+     */
+    overlap: null,
+
+    /**
+     * Code that executes immediately before the startDrag event
+     * @method b4StartDrag
+     * @private
+     */
+    b4StartDrag: function(x, y) { },
+
+    /**
+     * Abstract method called after a drag/drop object is clicked
+     * and the drag or mousedown time thresholds have beeen met.
+     * @method startDrag
+     * @param {int} X click location
+     * @param {int} Y click location
+     */
+    startDrag: function(x, y) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDrag event
+     * @method b4Drag
+     * @private
+     */
+    b4Drag: function(e) { },
+
+    /**
+     * Abstract method called during the onMouseMove event while dragging an 
+     * object.
+     * @method onDrag
+     * @param {Event} e the mousemove event
+     */
+    onDrag: function(e) { /* override this */ },
+
+    /**
+     * Abstract method called when this element fist begins hovering over 
+     * another DragDrop obj
+     * @method onDragEnter
+     * @param {Event} e the mousemove event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this is hovering over.  In INTERSECT mode, an array of one or more 
+     * dragdrop items being hovered over.
+     */
+    onDragEnter: function(e, id) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDragOver event
+     * @method b4DragOver
+     * @private
+     */
+    b4DragOver: function(e) { },
+
+    /**
+     * Abstract method called when this element is hovering over another 
+     * DragDrop obj
+     * @method onDragOver
+     * @param {Event} e the mousemove event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this is hovering over.  In INTERSECT mode, an array of dd items 
+     * being hovered over.
+     */
+    onDragOver: function(e, id) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDragOut event
+     * @method b4DragOut
+     * @private
+     */
+    b4DragOut: function(e) { },
+
+    /**
+     * Abstract method called when we are no longer hovering over an element
+     * @method onDragOut
+     * @param {Event} e the mousemove event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this was hovering over.  In INTERSECT mode, an array of dd items 
+     * that the mouse is no longer over.
+     */
+    onDragOut: function(e, id) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDragDrop event
+     * @method b4DragDrop
+     * @private
+     */
+    b4DragDrop: function(e) { },
+
+    /**
+     * Abstract method called when this item is dropped on another DragDrop 
+     * obj
+     * @method onDragDrop
+     * @param {Event} e the mouseup event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this was dropped on.  In INTERSECT mode, an array of dd items this 
+     * was dropped on.
+     */
+    onDragDrop: function(e, id) { /* override this */ },
+
+    /**
+     * Abstract method called when this item is dropped on an area with no
+     * drop target
+     * @method onInvalidDrop
+     * @param {Event} e the mouseup event
+     */
+    onInvalidDrop: function(e) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the endDrag event
+     * @method b4EndDrag
+     * @private
+     */
+    b4EndDrag: function(e) { },
+
+    /**
+     * Fired when we are done dragging the object
+     * @method endDrag
+     * @param {Event} e the mouseup event
+     */
+    endDrag: function(e) { /* override this */ },
+
+    /**
+     * Code executed immediately before the onMouseDown event
+     * @method b4MouseDown
+     * @param {Event} e the mousedown event
+     * @private
+     */
+    b4MouseDown: function(e) {  },
+
+    /**
+     * Event handler that fires when a drag/drop obj gets a mousedown
+     * @method onMouseDown
+     * @param {Event} e the mousedown event
+     */
+    onMouseDown: function(e) { /* override this */ },
+
+    /**
+     * Event handler that fires when a drag/drop obj gets a mouseup
+     * @method onMouseUp
+     * @param {Event} e the mouseup event
+     */
+    onMouseUp: function(e) { /* override this */ },
+   
+    /**
+     * Override the onAvailable method to do what is needed after the initial
+     * position was determined.
+     * @method onAvailable
+     */
+    onAvailable: function () { 
+    },
+
+    /**
+     * Returns a reference to the linked element
+     * @method getEl
+     * @return {HTMLElement} the html element 
+     */
+    getEl: function() { 
+        if (!this._domRef) {
+            this._domRef = Dom.get(this.id); 
+        }
+
+        return this._domRef;
+    },
+
+    /**
+     * Returns a reference to the actual element to drag.  By default this is
+     * the same as the html element, but it can be assigned to another 
+     * element. An example of this can be found in YAHOO.util.DDProxy
+     * @method getDragEl
+     * @return {HTMLElement} the html element 
+     */
+    getDragEl: function() {
+        return Dom.get(this.dragElId);
+    },
+
+    /**
+     * Sets up the DragDrop object.  Must be called in the constructor of any
+     * YAHOO.util.DragDrop subclass
+     * @method init
+     * @param id the id of the linked element
+     * @param {String} sGroup the group of related items
+     * @param {object} config configuration attributes
+     */
+    init: function(id, sGroup, config) {
+        this.initTarget(id, sGroup, config);
+        Event.on(this._domRef || this.id, "mousedown", 
+                        this.handleMouseDown, this, true);
+        // Event.on(this.id, "selectstart", Event.preventDefault);
+    },
+
+    /**
+     * Initializes Targeting functionality only... the object does not
+     * get a mousedown handler.
+     * @method initTarget
+     * @param id the id of the linked element
+     * @param {String} sGroup the group of related items
+     * @param {object} config configuration attributes
+     */
+    initTarget: function(id, sGroup, config) {
+
+        // configuration attributes 
+        this.config = config || {};
+
+        // create a local reference to the drag and drop manager
+        this.DDM = YAHOO.util.DDM;
+
+        // initialize the groups object
+        this.groups = {};
+
+        // assume that we have an element reference instead of an id if the
+        // parameter is not a string
+        if (typeof id !== "string") {
+            this._domRef = id;
+            id = Dom.generateId(id);
+        }
+
+        // set the id
+        this.id = id;
+
+        // add to an interaction group
+        this.addToGroup((sGroup) ? sGroup : "default");
+
+        // We don't want to register this as the handle with the manager
+        // so we just set the id rather than calling the setter.
+        this.handleElId = id;
+
+        Event.onAvailable(id, this.handleOnAvailable, this, true);
+
+
+        // the linked element is the element that gets dragged by default
+        this.setDragElId(id); 
+
+        // by default, clicked anchors will not start drag operations. 
+        // @TODO what else should be here?  Probably form fields.
+        this.invalidHandleTypes = { A: "A" };
+        this.invalidHandleIds = {};
+        this.invalidHandleClasses = [];
+
+        this.applyConfig();
+    },
+
+    /**
+     * Applies the configuration parameters that were passed into the constructor.
+     * This is supposed to happen at each level through the inheritance chain.  So
+     * a DDProxy implentation will execute apply config on DDProxy, DD, and 
+     * DragDrop in order to get all of the parameters that are available in
+     * each object.
+     * @method applyConfig
+     */
+    applyConfig: function() {
+
+        // configurable properties: 
+        //    padding, isTarget, maintainOffset, primaryButtonOnly
+        this.padding           = this.config.padding || [0, 0, 0, 0];
+        this.isTarget          = (this.config.isTarget !== false);
+        this.maintainOffset    = (this.config.maintainOffset);
+        this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
+        this.dragOnly = ((this.config.dragOnly === true) ? true : false);
+    },
+
+    /**
+     * Executed when the linked element is available
+     * @method handleOnAvailable
+     * @private
+     */
+    handleOnAvailable: function() {
+        this.available = true;
+        this.resetConstraints();
+        this.onAvailable();
+    },
+
+     /**
+     * Configures the padding for the target zone in px.  Effectively expands
+     * (or reduces) the virtual object size for targeting calculations.  
+     * Supports css-style shorthand; if only one parameter is passed, all sides
+     * will have that padding, and if only two are passed, the top and bottom
+     * will have the first param, the left and right the second.
+     * @method setPadding
+     * @param {int} iTop    Top pad
+     * @param {int} iRight  Right pad
+     * @param {int} iBot    Bot pad
+     * @param {int} iLeft   Left pad
+     */
+    setPadding: function(iTop, iRight, iBot, iLeft) {
+        // this.padding = [iLeft, iRight, iTop, iBot];
+        if (!iRight && 0 !== iRight) {
+            this.padding = [iTop, iTop, iTop, iTop];
+        } else if (!iBot && 0 !== iBot) {
+            this.padding = [iTop, iRight, iTop, iRight];
+        } else {
+            this.padding = [iTop, iRight, iBot, iLeft];
+        }
+    },
+
+    /**
+     * Stores the initial placement of the linked element.
+     * @method setInitialPosition
+     * @param {int} diffX   the X offset, default 0
+     * @param {int} diffY   the Y offset, default 0
+     * @private
+     */
+    setInitPosition: function(diffX, diffY) {
+        var el = this.getEl();
+
+        if (!this.DDM.verifyEl(el)) {
+            return;
+        }
+
+        var dx = diffX || 0;
+        var dy = diffY || 0;
+
+        var p = Dom.getXY( el );
+
+        this.initPageX = p[0] - dx;
+        this.initPageY = p[1] - dy;
+
+        this.lastPageX = p[0];
+        this.lastPageY = p[1];
+
+
+
+        this.setStartPosition(p);
+    },
+
+    /**
+     * Sets the start position of the element.  This is set when the obj
+     * is initialized, the reset when a drag is started.
+     * @method setStartPosition
+     * @param pos current position (from previous lookup)
+     * @private
+     */
+    setStartPosition: function(pos) {
+        var p = pos || Dom.getXY(this.getEl());
+
+        this.deltaSetXY = null;
+
+        this.startPageX = p[0];
+        this.startPageY = p[1];
+    },
+
+    /**
+     * Add this instance to a group of related drag/drop objects.  All 
+     * instances belong to at least one group, and can belong to as many 
+     * groups as needed.
+     * @method addToGroup
+     * @param sGroup {string} the name of the group
+     */
+    addToGroup: function(sGroup) {
+        this.groups[sGroup] = true;
+        this.DDM.regDragDrop(this, sGroup);
+    },
+
+    /**
+     * Remove's this instance from the supplied interaction group
+     * @method removeFromGroup
+     * @param {string}  sGroup  The group to drop
+     */
+    removeFromGroup: function(sGroup) {
+        if (this.groups[sGroup]) {
+            delete this.groups[sGroup];
+        }
+
+        this.DDM.removeDDFromGroup(this, sGroup);
+    },
+
+    /**
+     * Allows you to specify that an element other than the linked element 
+     * will be moved with the cursor during a drag
+     * @method setDragElId
+     * @param id {string} the id of the element that will be used to initiate the drag
+     */
+    setDragElId: function(id) {
+        this.dragElId = id;
+    },
+
+    /**
+     * Allows you to specify a child of the linked element that should be 
+     * used to initiate the drag operation.  An example of this would be if 
+     * you have a content div with text and links.  Clicking anywhere in the 
+     * content area would normally start the drag operation.  Use this method
+     * to specify that an element inside of the content div is the element 
+     * that starts the drag operation.
+     * @method setHandleElId
+     * @param id {string} the id of the element that will be used to 
+     * initiate the drag.
+     */
+    setHandleElId: function(id) {
+        if (typeof id !== "string") {
+            id = Dom.generateId(id);
+        }
+        this.handleElId = id;
+        this.DDM.regHandle(this.id, id);
+    },
+
+    /**
+     * Allows you to set an element outside of the linked element as a drag 
+     * handle
+     * @method setOuterHandleElId
+     * @param id the id of the element that will be used to initiate the drag
+     */
+    setOuterHandleElId: function(id) {
+        if (typeof id !== "string") {
+            id = Dom.generateId(id);
+        }
+        Event.on(id, "mousedown", 
+                this.handleMouseDown, this, true);
+        this.setHandleElId(id);
+
+        this.hasOuterHandles = true;
+    },
+
+    /**
+     * Remove all drag and drop hooks for this element
+     * @method unreg
+     */
+    unreg: function() {
+        Event.removeListener(this.id, "mousedown", 
+                this.handleMouseDown);
+        this._domRef = null;
+        this.DDM._remove(this);
+    },
+
+    /**
+     * Returns true if this instance is locked, or the drag drop mgr is locked
+     * (meaning that all drag/drop is disabled on the page.)
+     * @method isLocked
+     * @return {boolean} true if this obj or all drag/drop is locked, else 
+     * false
+     */
+    isLocked: function() {
+        return (this.DDM.isLocked() || this.locked);
+    },
+
+    /**
+     * Fired when this object is clicked
+     * @method handleMouseDown
+     * @param {Event} e 
+     * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj)
+     * @private
+     */
+    handleMouseDown: function(e, oDD) {
+
+        var button = e.which || e.button;
+
+        if (this.primaryButtonOnly && button > 1) {
+            return;
+        }
+
+        if (this.isLocked()) {
+            return;
+        }
+
+
+
+        // firing the mousedown events prior to calculating positions
+        var b4Return = this.b4MouseDown(e);
+        var mDownReturn = this.onMouseDown(e);
+
+        if ((b4Return === false) || (mDownReturn === false)) {
+            return;
+        }
+
+        this.DDM.refreshCache(this.groups);
+        // var self = this;
+        // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0);
+
+        // Only process the event if we really clicked within the linked 
+        // element.  The reason we make this check is that in the case that 
+        // another element was moved between the clicked element and the 
+        // cursor in the time between the mousedown and mouseup events. When 
+        // this happens, the element gets the next mousedown event 
+        // regardless of where on the screen it happened.  
+        var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e));
+        if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
+        } else {
+            if (this.clickValidator(e)) {
+
+
+                // set the initial element position
+                this.setStartPosition();
+
+                // start tracking mousemove distance and mousedown time to
+                // determine when to start the actual drag
+                this.DDM.handleMouseDown(e, this);
+
+                // this mousedown is mine
+                this.DDM.stopEvent(e);
+            } else {
+
+
+            }
+        }
+    },
+
+    /**
+     * @method clickValidator
+     * @description Method validates that the clicked element
+     * was indeed the handle or a valid child of the handle
+     * @param {Event} e 
+     * @private
+     */
+    clickValidator: function(e) {
+        var target = Event.getTarget(e);
+        return ( this.isValidHandleChild(target) &&
+                    (this.id == this.handleElId || 
+                        this.DDM.handleWasClicked(target, this.id)) );
+    },
+
+    /**
+     * Finds the location the element should be placed if we want to move
+     * it to where the mouse location less the click offset would place us.
+     * @method getTargetCoord
+     * @param {int} iPageX the X coordinate of the click
+     * @param {int} iPageY the Y coordinate of the click
+     * @return an object that contains the coordinates (Object.x and Object.y)
+     * @private
+     */
+    getTargetCoord: function(iPageX, iPageY) {
+
+
+        var x = iPageX - this.deltaX;
+        var y = iPageY - this.deltaY;
+
+        if (this.constrainX) {
+            if (x < this.minX) { x = this.minX; }
+            if (x > this.maxX) { x = this.maxX; }
+        }
+
+        if (this.constrainY) {
+            if (y < this.minY) { y = this.minY; }
+            if (y > this.maxY) { y = this.maxY; }
+        }
+
+        x = this.getTick(x, this.xTicks);
+        y = this.getTick(y, this.yTicks);
+
+
+        return {x:x, y:y};
+    },
+
+    /**
+     * Allows you to specify a tag name that should not start a drag operation
+     * when clicked.  This is designed to facilitate embedding links within a
+     * drag handle that do something other than start the drag.
+     * @method addInvalidHandleType
+     * @param {string} tagName the type of element to exclude
+     */
+    addInvalidHandleType: function(tagName) {
+        var type = tagName.toUpperCase();
+        this.invalidHandleTypes[type] = type;
+    },
+
+    /**
+     * Lets you to specify an element id for a child of a drag handle
+     * that should not initiate a drag
+     * @method addInvalidHandleId
+     * @param {string} id the element id of the element you wish to ignore
+     */
+    addInvalidHandleId: function(id) {
+        if (typeof id !== "string") {
+            id = Dom.generateId(id);
+        }
+        this.invalidHandleIds[id] = id;
+    },
+
+
+    /**
+     * Lets you specify a css class of elements that will not initiate a drag
+     * @method addInvalidHandleClass
+     * @param {string} cssClass the class of the elements you wish to ignore
+     */
+    addInvalidHandleClass: function(cssClass) {
+        this.invalidHandleClasses.push(cssClass);
+    },
+
+    /**
+     * Unsets an excluded tag name set by addInvalidHandleType
+     * @method removeInvalidHandleType
+     * @param {string} tagName the type of element to unexclude
+     */
+    removeInvalidHandleType: function(tagName) {
+        var type = tagName.toUpperCase();
+        // this.invalidHandleTypes[type] = null;
+        delete this.invalidHandleTypes[type];
+    },
+    
+    /**
+     * Unsets an invalid handle id
+     * @method removeInvalidHandleId
+     * @param {string} id the id of the element to re-enable
+     */
+    removeInvalidHandleId: function(id) {
+        if (typeof id !== "string") {
+            id = Dom.generateId(id);
+        }
+        delete this.invalidHandleIds[id];
+    },
+
+    /**
+     * Unsets an invalid css class
+     * @method removeInvalidHandleClass
+     * @param {string} cssClass the class of the element(s) you wish to 
+     * re-enable
+     */
+    removeInvalidHandleClass: function(cssClass) {
+        for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
+            if (this.invalidHandleClasses[i] == cssClass) {
+                delete this.invalidHandleClasses[i];
+            }
+        }
+    },
+
+    /**
+     * Checks the tag exclusion list to see if this click should be ignored
+     * @method isValidHandleChild
+     * @param {HTMLElement} node the HTMLElement to evaluate
+     * @return {boolean} true if this is a valid tag type, false if not
+     */
+    isValidHandleChild: function(node) {
+
+        var valid = true;
+        // var n = (node.nodeName == "#text") ? node.parentNode : node;
+        var nodeName;
+        try {
+            nodeName = node.nodeName.toUpperCase();
+        } catch(e) {
+            nodeName = node.nodeName;
+        }
+        valid = valid && !this.invalidHandleTypes[nodeName];
+        valid = valid && !this.invalidHandleIds[node.id];
+
+        for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
+            valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
+        }
+
+
+        return valid;
+
+    },
+
+    /**
+     * Create the array of horizontal tick marks if an interval was specified
+     * in setXConstraint().
+     * @method setXTicks
+     * @private
+     */
+    setXTicks: function(iStartX, iTickSize) {
+        this.xTicks = [];
+        this.xTickSize = iTickSize;
+        
+        var tickMap = {};
+
+        for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
+            if (!tickMap[i]) {
+                this.xTicks[this.xTicks.length] = i;
+                tickMap[i] = true;
+            }
+        }
+
+        for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
+            if (!tickMap[i]) {
+                this.xTicks[this.xTicks.length] = i;
+                tickMap[i] = true;
+            }
+        }
+
+        this.xTicks.sort(this.DDM.numericSort) ;
+    },
+
+    /**
+     * Create the array of vertical tick marks if an interval was specified in 
+     * setYConstraint().
+     * @method setYTicks
+     * @private
+     */
+    setYTicks: function(iStartY, iTickSize) {
+        this.yTicks = [];
+        this.yTickSize = iTickSize;
+
+        var tickMap = {};
+
+        for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
+            if (!tickMap[i]) {
+                this.yTicks[this.yTicks.length] = i;
+                tickMap[i] = true;
+            }
+        }
+
+        for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
+            if (!tickMap[i]) {
+                this.yTicks[this.yTicks.length] = i;
+                tickMap[i] = true;
+            }
+        }
+
+        this.yTicks.sort(this.DDM.numericSort) ;
+    },
+
+    /**
+     * By default, the element can be dragged any place on the screen.  Use 
+     * this method to limit the horizontal travel of the element.  Pass in 
+     * 0,0 for the parameters if you want to lock the drag to the y axis.
+     * @method setXConstraint
+     * @param {int} iLeft the number of pixels the element can move to the left
+     * @param {int} iRight the number of pixels the element can move to the 
+     * right
+     * @param {int} iTickSize optional parameter for specifying that the 
+     * element
+     * should move iTickSize pixels at a time.
+     */
+    setXConstraint: function(iLeft, iRight, iTickSize) {
+        this.leftConstraint = parseInt(iLeft, 10);
+        this.rightConstraint = parseInt(iRight, 10);
+
+        this.minX = this.initPageX - this.leftConstraint;
+        this.maxX = this.initPageX + this.rightConstraint;
+        if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
+
+        this.constrainX = true;
+    },
+
+    /**
+     * Clears any constraints applied to this instance.  Also clears ticks
+     * since they can't exist independent of a constraint at this time.
+     * @method clearConstraints
+     */
+    clearConstraints: function() {
+        this.constrainX = false;
+        this.constrainY = false;
+        this.clearTicks();
+    },
+
+    /**
+     * Clears any tick interval defined for this instance
+     * @method clearTicks
+     */
+    clearTicks: function() {
+        this.xTicks = null;
+        this.yTicks = null;
+        this.xTickSize = 0;
+        this.yTickSize = 0;
+    },
+
+    /**
+     * By default, the element can be dragged any place on the screen.  Set 
+     * this to limit the vertical travel of the element.  Pass in 0,0 for the
+     * parameters if you want to lock the drag to the x axis.
+     * @method setYConstraint
+     * @param {int} iUp the number of pixels the element can move up
+     * @param {int} iDown the number of pixels the element can move down
+     * @param {int} iTickSize optional parameter for specifying that the 
+     * element should move iTickSize pixels at a time.
+     */
+    setYConstraint: function(iUp, iDown, iTickSize) {
+        this.topConstraint = parseInt(iUp, 10);
+        this.bottomConstraint = parseInt(iDown, 10);
+
+        this.minY = this.initPageY - this.topConstraint;
+        this.maxY = this.initPageY + this.bottomConstraint;
+        if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
+
+        this.constrainY = true;
+        
+    },
+
+    /**
+     * resetConstraints must be called if you manually reposition a dd element.
+     * @method resetConstraints
+     */
+    resetConstraints: function() {
+
+
+        // Maintain offsets if necessary
+        if (this.initPageX || this.initPageX === 0) {
+            // figure out how much this thing has moved
+            var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
+            var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
+
+            this.setInitPosition(dx, dy);
+
+        // This is the first time we have detected the element's position
+        } else {
+            this.setInitPosition();
+        }
+
+        if (this.constrainX) {
+            this.setXConstraint( this.leftConstraint, 
+                                 this.rightConstraint, 
+                                 this.xTickSize        );
+        }
+
+        if (this.constrainY) {
+            this.setYConstraint( this.topConstraint, 
+                                 this.bottomConstraint, 
+                                 this.yTickSize         );
+        }
+    },
+
+    /**
+     * Normally the drag element is moved pixel by pixel, but we can specify 
+     * that it move a number of pixels at a time.  This method resolves the 
+     * location when we have it set up like this.
+     * @method getTick
+     * @param {int} val where we want to place the object
+     * @param {int[]} tickArray sorted array of valid points
+     * @return {int} the closest tick
+     * @private
+     */
+    getTick: function(val, tickArray) {
+
+        if (!tickArray) {
+            // If tick interval is not defined, it is effectively 1 pixel, 
+            // so we return the value passed to us.
+            return val; 
+        } else if (tickArray[0] >= val) {
+            // The value is lower than the first tick, so we return the first
+            // tick.
+            return tickArray[0];
+        } else {
+            for (var i=0, len=tickArray.length; i<len; ++i) {
+                var next = i + 1;
+                if (tickArray[next] && tickArray[next] >= val) {
+                    var diff1 = val - tickArray[i];
+                    var diff2 = tickArray[next] - val;
+                    return (diff2 > diff1) ? tickArray[i] : tickArray[next];
+                }
+            }
+
+            // The value is larger than the last tick, so we return the last
+            // tick.
+            return tickArray[tickArray.length - 1];
+        }
+    },
+
+    /**
+     * toString method
+     * @method toString
+     * @return {string} string representation of the dd obj
+     */
+    toString: function() {
+        return ("DragDrop " + this.id);
+    }
+
+};
+
+})();
+/**
+ * A DragDrop implementation where the linked element follows the 
+ * mouse cursor during a drag.
+ * @class DD
+ * @extends YAHOO.util.DragDrop
+ * @constructor
+ * @param {String} id the id of the linked element 
+ * @param {String} sGroup the group of related DragDrop items
+ * @param {object} config an object containing configurable attributes
+ *                Valid properties for DD: 
+ *                    scroll
+ */
+YAHOO.util.DD = function(id, sGroup, config) {
+    if (id) {
+        this.init(id, sGroup, config);
+    }
+};
+
+YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, {
+
+    /**
+     * When set to true, the utility automatically tries to scroll the browser
+     * window wehn a drag and drop element is dragged near the viewport boundary.
+     * Defaults to true.
+     * @property scroll
+     * @type boolean
+     */
+    scroll: true, 
+
+    /**
+     * Sets the pointer offset to the distance between the linked element's top 
+     * left corner and the location the element was clicked
+     * @method autoOffset
+     * @param {int} iPageX the X coordinate of the click
+     * @param {int} iPageY the Y coordinate of the click
+     */
+    autoOffset: function(iPageX, iPageY) {
+        var x = iPageX - this.startPageX;
+        var y = iPageY - this.startPageY;
+        this.setDelta(x, y);
+    },
+
+    /** 
+     * Sets the pointer offset.  You can call this directly to force the 
+     * offset to be in a particular location (e.g., pass in 0,0 to set it 
+     * to the center of the object, as done in YAHOO.widget.Slider)
+     * @method setDelta
+     * @param {int} iDeltaX the distance from the left
+     * @param {int} iDeltaY the distance from the top
+     */
+    setDelta: function(iDeltaX, iDeltaY) {
+        this.deltaX = iDeltaX;
+        this.deltaY = iDeltaY;
+    },
+
+    /**
+     * Sets the drag element to the location of the mousedown or click event, 
+     * maintaining the cursor location relative to the location on the element 
+     * that was clicked.  Override this if you want to place the element in a 
+     * location other than where the cursor is.
+     * @method setDragElPos
+     * @param {int} iPageX the X coordinate of the mousedown or drag event
+     * @param {int} iPageY the Y coordinate of the mousedown or drag event
+     */
+    setDragElPos: function(iPageX, iPageY) {
+        // the first time we do this, we are going to check to make sure
+        // the element has css positioning
+
+        var el = this.getDragEl();
+        this.alignElWithMouse(el, iPageX, iPageY);
+    },
+
+    /**
+     * Sets the element to the location of the mousedown or click event, 
+     * maintaining the cursor location relative to the location on the element 
+     * that was clicked.  Override this if you want to place the element in a 
+     * location other than where the cursor is.
+     * @method alignElWithMouse
+     * @param {HTMLElement} el the element to move
+     * @param {int} iPageX the X coordinate of the mousedown or drag event
+     * @param {int} iPageY the Y coordinate of the mousedown or drag event
+     */
+    alignElWithMouse: function(el, iPageX, iPageY) {
+        var oCoord = this.getTargetCoord(iPageX, iPageY);
+
+        if (!this.deltaSetXY) {
+            var aCoord = [oCoord.x, oCoord.y];
+            YAHOO.util.Dom.setXY(el, aCoord);
+            var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 );
+            var newTop  = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 );
+
+            this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
+        } else {
+            YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px");
+            YAHOO.util.Dom.setStyle(el, "top",  (oCoord.y + this.deltaSetXY[1]) + "px");
+            //el.style.left = (oCoord.x + this.deltaSetXY[0]) + "px";
+            //el.style.top = (oCoord.y + this.deltaSetXY[1]) + "px";
+        }
+        
+        this.cachePosition(oCoord.x, oCoord.y);
+        //DAV
+        var self = this;
+        setTimeout(function() {
+            self.autoScroll.call(self, oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
+        }, 0);
+    },
+
+    /**
+     * Saves the most recent position so that we can reset the constraints and
+     * tick marks on-demand.  We need to know this so that we can calculate the
+     * number of pixels the element is offset from its original position.
+     * @method cachePosition
+     * @param iPageX the current x position (optional, this just makes it so we
+     * don't have to look it up again)
+     * @param iPageY the current y position (optional, this just makes it so we
+     * don't have to look it up again)
+     */
+    cachePosition: function(iPageX, iPageY) {
+        if (iPageX) {
+            this.lastPageX = iPageX;
+            this.lastPageY = iPageY;
+        } else {
+            var aCoord = YAHOO.util.Dom.getXY(this.getEl());
+            this.lastPageX = aCoord[0];
+            this.lastPageY = aCoord[1];
+        }
+    },
+
+    /**
+     * Auto-scroll the window if the dragged object has been moved beyond the 
+     * visible window boundary.
+     * @method autoScroll
+     * @param {int} x the drag element's x position
+     * @param {int} y the drag element's y position
+     * @param {int} h the height of the drag element
+     * @param {int} w the width of the drag element
+     * @private
+     */
+    autoScroll: function(x, y, h, w) {
+
+        if (this.scroll) {
+            // The client height
+            var clientH = this.DDM.getClientHeight();
+
+            // The client width
+            var clientW = this.DDM.getClientWidth();
+
+            // The amt scrolled down
+            var st = this.DDM.getScrollTop();
+
+            // The amt scrolled right
+            var sl = this.DDM.getScrollLeft();
+
+            // Location of the bottom of the element
+            var bot = h + y;
+
+            // Location of the right of the element
+            var right = w + x;
+
+            // The distance from the cursor to the bottom of the visible area, 
+            // adjusted so that we don't scroll if the cursor is beyond the
+            // element drag constraints
+            var toBot = (clientH + st - y - this.deltaY);
+
+            // The distance from the cursor to the right of the visible area
+            var toRight = (clientW + sl - x - this.deltaX);
+
+
+            // How close to the edge the cursor must be before we scroll
+            // var thresh = (document.all) ? 100 : 40;
+            var thresh = 40;
+
+            // How many pixels to scroll per autoscroll op.  This helps to reduce 
+            // clunky scrolling. IE is more sensitive about this ... it needs this 
+            // value to be higher.
+            var scrAmt = (document.all) ? 80 : 30;
+
+            // Scroll down if we are near the bottom of the visible page and the 
+            // obj extends below the crease
+            if ( bot > clientH && toBot < thresh ) { 
+                window.scrollTo(sl, st + scrAmt); 
+            }
+
+            // Scroll up if the window is scrolled down and the top of the object
+            // goes above the top border
+            if ( y < st && st > 0 && y - st < thresh ) { 
+                window.scrollTo(sl, st - scrAmt); 
+            }
+
+            // Scroll right if the obj is beyond the right border and the cursor is
+            // near the border.
+            if ( right > clientW && toRight < thresh ) { 
+                window.scrollTo(sl + scrAmt, st); 
+            }
+
+            // Scroll left if the window has been scrolled to the right and the obj
+            // extends past the left border
+            if ( x < sl && sl > 0 && x - sl < thresh ) { 
+                window.scrollTo(sl - scrAmt, st);
+            }
+        }
+    },
+
+    /*
+     * Sets up config options specific to this class. Overrides
+     * YAHOO.util.DragDrop, but all versions of this method through the 
+     * inheritance chain are called
+     */
+    applyConfig: function() {
+        YAHOO.util.DD.superclass.applyConfig.call(this);
+        this.scroll = (this.config.scroll !== false);
+    },
+
+    /*
+     * Event that fires prior to the onMouseDown event.  Overrides 
+     * YAHOO.util.DragDrop.
+     */
+    b4MouseDown: function(e) {
+        this.setStartPosition();
+        // this.resetConstraints();
+        this.autoOffset(YAHOO.util.Event.getPageX(e), 
+                            YAHOO.util.Event.getPageY(e));
+    },
+
+    /*
+     * Event that fires prior to the onDrag event.  Overrides 
+     * YAHOO.util.DragDrop.
+     */
+    b4Drag: function(e) {
+        this.setDragElPos(YAHOO.util.Event.getPageX(e), 
+                            YAHOO.util.Event.getPageY(e));
+    },
+
+    toString: function() {
+        return ("DD " + this.id);
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+    // Debugging ygDragDrop events that can be overridden
+    //////////////////////////////////////////////////////////////////////////
+    /*
+    startDrag: function(x, y) {
+    },
+
+    onDrag: function(e) {
+    },
+
+    onDragEnter: function(e, id) {
+    },
+
+    onDragOver: function(e, id) {
+    },
+
+    onDragOut: function(e, id) {
+    },
+
+    onDragDrop: function(e, id) {
+    },
+
+    endDrag: function(e) {
+    }
+
+    */
+
+});
+/**
+ * A DragDrop implementation that inserts an empty, bordered div into
+ * the document that follows the cursor during drag operations.  At the time of
+ * the click, the frame div is resized to the dimensions of the linked html
+ * element, and moved to the exact location of the linked element.
+ *
+ * References to the "frame" element refer to the single proxy element that
+ * was created to be dragged in place of all DDProxy elements on the
+ * page.
+ *
+ * @class DDProxy
+ * @extends YAHOO.util.DD
+ * @constructor
+ * @param {String} id the id of the linked html element
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ *                Valid properties for DDProxy in addition to those in DragDrop: 
+ *                   resizeFrame, centerFrame, dragElId
+ */
+YAHOO.util.DDProxy = function(id, sGroup, config) {
+    if (id) {
+        this.init(id, sGroup, config);
+        this.initFrame(); 
+    }
+};
+
+/**
+ * The default drag frame div id
+ * @property YAHOO.util.DDProxy.dragElId
+ * @type String
+ * @static
+ */
+YAHOO.util.DDProxy.dragElId = "ygddfdiv";
+
+YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, {
+
+    /**
+     * By default we resize the drag frame to be the same size as the element
+     * we want to drag (this is to get the frame effect).  We can turn it off
+     * if we want a different behavior.
+     * @property resizeFrame
+     * @type boolean
+     */
+    resizeFrame: true,
+
+    /**
+     * By default the frame is positioned exactly where the drag element is, so
+     * we use the cursor offset provided by YAHOO.util.DD.  Another option that works only if
+     * you do not have constraints on the obj is to have the drag frame centered
+     * around the cursor.  Set centerFrame to true for this effect.
+     * @property centerFrame
+     * @type boolean
+     */
+    centerFrame: false,
+
+    /**
+     * Creates the proxy element if it does not yet exist
+     * @method createFrame
+     */
+    createFrame: function() {
+        var self=this, body=document.body;
+
+        if (!body || !body.firstChild) {
+            setTimeout( function() { self.createFrame(); }, 50 );
+            return;
+        }
+
+        var div=this.getDragEl(), Dom=YAHOO.util.Dom;
+
+        if (!div) {
+            div    = document.createElement("div");
+            div.id = this.dragElId;
+            var s  = div.style;
+
+            s.position   = "absolute";
+            s.visibility = "hidden";
+            s.cursor     = "move";
+            s.border     = "2px solid #aaa";
+            s.zIndex     = 999;
+            s.height     = "25px";
+            s.width      = "25px";
+
+            var _data = document.createElement('div');
+            Dom.setStyle(_data, 'height', '100%');
+            Dom.setStyle(_data, 'width', '100%');
+            /**
+            * If the proxy element has no background-color, then it is considered to the "transparent" by Internet Explorer.
+            * Since it is "transparent" then the events pass through it to the iframe below.
+            * So creating a "fake" div inside the proxy element and giving it a background-color, then setting it to an
+            * opacity of 0, it appears to not be there, however IE still thinks that it is so the events never pass through.
+            */
+            Dom.setStyle(_data, 'background-color', '#ccc');
+            Dom.setStyle(_data, 'opacity', '0');
+            div.appendChild(_data);
+
+            // appendChild can blow up IE if invoked prior to the window load event
+            // while rendering a table.  It is possible there are other scenarios 
+            // that would cause this to happen as well.
+            body.insertBefore(div, body.firstChild);
+        }
+    },
+
+    /**
+     * Initialization for the drag frame element.  Must be called in the
+     * constructor of all subclasses
+     * @method initFrame
+     */
+    initFrame: function() {
+        this.createFrame();
+    },
+
+    applyConfig: function() {
+        YAHOO.util.DDProxy.superclass.applyConfig.call(this);
+
+        this.resizeFrame = (this.config.resizeFrame !== false);
+        this.centerFrame = (this.config.centerFrame);
+        this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId);
+    },
+
+    /**
+     * Resizes the drag frame to the dimensions of the clicked object, positions 
+     * it over the object, and finally displays it
+     * @method showFrame
+     * @param {int} iPageX X click position
+     * @param {int} iPageY Y click position
+     * @private
+     */
+    showFrame: function(iPageX, iPageY) {
+        var el = this.getEl();
+        var dragEl = this.getDragEl();
+        var s = dragEl.style;
+
+        this._resizeProxy();
+
+        if (this.centerFrame) {
+            this.setDelta( Math.round(parseInt(s.width,  10)/2), 
+                           Math.round(parseInt(s.height, 10)/2) );
+        }
+
+        this.setDragElPos(iPageX, iPageY);
+
+        YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible"); 
+    },
+
+    /**
+     * The proxy is automatically resized to the dimensions of the linked
+     * element when a drag is initiated, unless resizeFrame is set to false
+     * @method _resizeProxy
+     * @private
+     */
+    _resizeProxy: function() {
+        if (this.resizeFrame) {
+            var DOM    = YAHOO.util.Dom;
+            var el     = this.getEl();
+            var dragEl = this.getDragEl();
+
+            var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth"    ), 10);
+            var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth"  ), 10);
+            var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10);
+            var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth"   ), 10);
+
+            if (isNaN(bt)) { bt = 0; }
+            if (isNaN(br)) { br = 0; }
+            if (isNaN(bb)) { bb = 0; }
+            if (isNaN(bl)) { bl = 0; }
+
+
+            var newWidth  = Math.max(0, el.offsetWidth  - br - bl);                                                                                           
+            var newHeight = Math.max(0, el.offsetHeight - bt - bb);
+
+
+            DOM.setStyle( dragEl, "width",  newWidth  + "px" );
+            DOM.setStyle( dragEl, "height", newHeight + "px" );
+        }
+    },
+
+    // overrides YAHOO.util.DragDrop
+    b4MouseDown: function(e) {
+        this.setStartPosition();
+        var x = YAHOO.util.Event.getPageX(e);
+        var y = YAHOO.util.Event.getPageY(e);
+        this.autoOffset(x, y);
+
+        // This causes the autoscroll code to kick off, which means autoscroll can
+        // happen prior to the check for a valid drag handle.
+        // this.setDragElPos(x, y);
+    },
+
+    // overrides YAHOO.util.DragDrop
+    b4StartDrag: function(x, y) {
+        // show the drag frame
+        this.showFrame(x, y);
+    },
+
+    // overrides YAHOO.util.DragDrop
+    b4EndDrag: function(e) {
+        YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden"); 
+    },
+
+    // overrides YAHOO.util.DragDrop
+    // By default we try to move the element to the last location of the frame.  
+    // This is so that the default behavior mirrors that of YAHOO.util.DD.  
+    endDrag: function(e) {
+        var DOM = YAHOO.util.Dom;
+        var lel = this.getEl();
+        var del = this.getDragEl();
+
+        // Show the drag frame briefly so we can get its position
+        // del.style.visibility = "";
+        DOM.setStyle(del, "visibility", ""); 
+
+        // Hide the linked element before the move to get around a Safari 
+        // rendering bug.
+        //lel.style.visibility = "hidden";
+        DOM.setStyle(lel, "visibility", "hidden"); 
+        YAHOO.util.DDM.moveToEl(lel, del);
+        //del.style.visibility = "hidden";
+        DOM.setStyle(del, "visibility", "hidden"); 
+        //lel.style.visibility = "";
+        DOM.setStyle(lel, "visibility", ""); 
+    },
+
+    toString: function() {
+        return ("DDProxy " + this.id);
+    }
+
+});
+/**
+ * A DragDrop implementation that does not move, but can be a drop 
+ * target.  You would get the same result by simply omitting implementation 
+ * for the event callbacks, but this way we reduce the processing cost of the 
+ * event listener and the callbacks.
+ * @class DDTarget
+ * @extends YAHOO.util.DragDrop 
+ * @constructor
+ * @param {String} id the id of the element that is a drop target
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ *                 Valid properties for DDTarget in addition to those in 
+ *                 DragDrop: 
+ *                    none
+ */
+YAHOO.util.DDTarget = function(id, sGroup, config) {
+    if (id) {
+        this.initTarget(id, sGroup, config);
+    }
+};
+
+// YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
+YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, {
+    toString: function() {
+        return ("DDTarget " + this.id);
+    }
+});
+YAHOO.register("dragdrop", YAHOO.util.DragDropMgr, {version: "2.4.1", build: "742"});
Index: trunk/wb/include/yui/dragdrop/dragdrop-min.js
===================================================================
--- trunk/wb/include/yui/dragdrop/dragdrop-min.js	(nonexistent)
+++ trunk/wb/include/yui/dragdrop/dragdrop-min.js	(revision 601)
@@ -0,0 +1,10 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.4.1
+*/
+if(!YAHOO.util.DragDropMgr){YAHOO.util.DragDropMgr=function(){var A=YAHOO.util.Event;return{ids:{},handleIds:{},dragCurrent:null,dragOvers:{},deltaX:0,deltaY:0,preventDefault:true,stopPropagation:true,initialized:false,locked:false,interactionInfo:null,init:function(){this.initialized=true;},POINT:0,INTERSECT:1,STRICT_INTERSECT:2,mode:0,_execOnAll:function(D,C){for(var E in this.ids){for(var B in this.ids[E]){var F=this.ids[E][B];if(!this.isTypeOfDD(F)){continue;}F[D].apply(F,C);}}},_onLoad:function(){this.init();A.on(document,"mouseup",this.handleMouseUp,this,true);A.on(document,"mousemove",this.handleMouseMove,this,true);A.on(window,"unload",this._onUnload,this,true);A.on(window,"resize",this._onResize,this,true);},_onResize:function(B){this._execOnAll("resetConstraints",[]);},lock:function(){this.locked=true;},unlock:function(){this.locked=false;},isLocked:function(){return this.locked;},locationCache:{},useCache:true,clickPixelThresh:3,clickTimeThresh:1000,dragThreshMet:false,clickTimeout:null,startX:0,startY:0,fromTimeout:false,regDragDrop:function(C,B){if(!this.initialized){this.init();}if(!this.ids[B]){this.ids[B]={};}this.ids[B][C.id]=C;},removeDDFromGroup:function(D,B){if(!this.ids[B]){this.ids[B]={};}var C=this.ids[B];if(C&&C[D.id]){delete C[D.id];}},_remove:function(C){for(var B in C.groups){if(B&&this.ids[B][C.id]){delete this.ids[B][C.id];}}delete this.handleIds[C.id];},regHandle:function(C,B){if(!this.handleIds[C]){this.handleIds[C]={};}this.handleIds[C][B]=B;},isDragDrop:function(B){return(this.getDDById(B))?true:false;},getRelated:function(G,C){var F=[];for(var E in G.groups){for(var D in this.ids[E]){var B=this.ids[E][D];if(!this.isTypeOfDD(B)){continue;}if(!C||B.isTarget){F[F.length]=B;}}}return F;},isLegalTarget:function(F,E){var C=this.getRelated(F,true);for(var D=0,B=C.length;D<B;++D){if(C[D].id==E.id){return true;}}return false;},isTypeOfDD:function(B){return(B&&B.__ygDragDrop);},isHandle:function(C,B){return(this.handleIds[C]&&this.handleIds[C][B]);},getDDById:function(C){for(var B in this.ids){if(this.ids[B][C]){return this.ids[B][C];}}return null;},handleMouseDown:function(D,C){this.currentTarget=YAHOO.util.Event.getTarget(D);this.dragCurrent=C;var B=C.getEl();this.startX=YAHOO.util.Event.getPageX(D);this.startY=YAHOO.util.Event.getPageY(D);this.deltaX=this.startX-B.offsetLeft;this.deltaY=this.startY-B.offsetTop;this.dragThreshMet=false;this.clickTimeout=setTimeout(function(){var E=YAHOO.util.DDM;E.startDrag(E.startX,E.startY);E.fromTimeout=true;},this.clickTimeThresh);},startDrag:function(B,D){clearTimeout(this.clickTimeout);var C=this.dragCurrent;if(C){C.b4StartDrag(B,D);}if(C){C.startDrag(B,D);}this.dragThreshMet=true;},handleMouseUp:function(B){if(this.dragCurrent){clearTimeout(this.clickTimeout);if(this.dragThreshMet){if(this.fromTimeout){this.handleMouseMove(B);}this.fromTimeout=false;this.fireEvents(B,true);}else{}this.stopDrag(B);this.stopEvent(B);}},stopEvent:function(B){if(this.stopPropagation){YAHOO.util.Event.stopPropagation(B);}if(this.preventDefault){YAHOO.util.Event.preventDefault(B);}},stopDrag:function(C,B){if(this.dragCurrent&&!B){if(this.dragThreshMet){this.dragCurrent.b4EndDrag(C);this.dragCurrent.endDrag(C);}this.dragCurrent.onMouseUp(C);}this.dragCurrent=null;this.dragOvers={};},handleMouseMove:function(E){var B=this.dragCurrent;if(B){if(YAHOO.util.Event.isIE&&!E.button){this.stopEvent(E);return this.handleMouseUp(E);}if(!this.dragThreshMet){var D=Math.abs(this.startX-YAHOO.util.Event.getPageX(E));var C=Math.abs(this.startY-YAHOO.util.Event.getPageY(E));if(D>this.clickPixelThresh||C>this.clickPixelThresh){this.startDrag(this.startX,this.startY);}}if(this.dragThreshMet){B.b4Drag(E);if(B){B.onDrag(E);}if(B){this.fireEvents(E,false);}}this.stopEvent(E);}},fireEvents:function(T,J){var V=this.dragCurrent;if(!V||V.isLocked()||V.dragOnly){return ;}var L=YAHOO.util.Event.getPageX(T),K=YAHOO.util.Event.getPageY(T),M=new YAHOO.util.Point(L,K),H=V.getTargetCoord(M.x,M.y),E=V.getDragEl(),S=new YAHOO.util.Region(H.y,H.x+E.offsetWidth,H.y+E.offsetHeight,H.x),G=[],I=[],D=[],U=[],R=[],C={},N=[];for(var P in this.dragOvers){var W=this.dragOvers[P];if(!this.isTypeOfDD(W)){continue;}if(!this.isOverTarget(M,W,this.mode,S)){I.push(W);}G[P]=true;delete this.dragOvers[P];}for(var O in V.groups){if("string"!=typeof O){continue;}for(P in this.ids[O]){var F=this.ids[O][P];if(!this.isTypeOfDD(F)){continue;}if(F.isTarget&&!F.isLocked()&&F!=V){if(this.isOverTarget(M,F,this.mode,S)){C[O]=true;if(J){U.push(F);}else{if(!G[F.id]){R.push(F);}else{D.push(F);}this.dragOvers[F.id]=F;}}}}}this.interactionInfo={out:I,enter:R,over:D,drop:U,point:M,draggedRegion:S,sourceRegion:this.locationCache[V.id],validDrop:J};for(var B in C){N.push(B);}if(J&&!U.length){this.interactionInfo.validDrop=false;V.onInvalidDrop(T);}if(this.mode){if(I.length){V.b4DragOut(T,I);if(V){V.onDragOut(T,I);}}if(R.length){if(V){V.onDragEnter(T,R,N);}}if(D.length){if(V){V.b4DragOver(T,D,N);}if(V){V.onDragOver(T,D,N);}}if(U.length){if(V){V.b4DragDrop(T,U,N);}if(V){V.onDragDrop(T,U,N);}}}else{var Q=0;for(P=0,Q=I.length;P<Q;++P){if(V){V.b4DragOut(T,I[P].id,N[0]);}if(V){V.onDragOut(T,I[P].id,N[0]);}}for(P=0,Q=R.length;P<Q;++P){if(V){V.onDragEnter(T,R[P].id,N[0]);}}for(P=0,Q=D.length;P<Q;++P){if(V){V.b4DragOver(T,D[P].id,N[0]);}if(V){V.onDragOver(T,D[P].id,N[0]);}}for(P=0,Q=U.length;P<Q;++P){if(V){V.b4DragDrop(T,U[P].id,N[0]);}if(V){V.onDragDrop(T,U[P].id,N[0]);}}}},getBestMatch:function(D){var F=null;var C=D.length;if(C==1){F=D[0];}else{for(var E=0;E<C;++E){var B=D[E];if(this.mode==this.INTERSECT&&B.cursorIsOver){F=B;break;}else{if(!F||!F.overlap||(B.overlap&&F.overlap.getArea()<B.overlap.getArea())){F=B;}}}}return F;},refreshCache:function(C){var E=C||this.ids;for(var B in E){if("string"!=typeof B){continue;}for(var D in this.ids[B]){var F=this.ids[B][D];if(this.isTypeOfDD(F)){var G=this.getLocation(F);if(G){this.locationCache[F.id]=G;}else{delete this.locationCache[F.id];}}}}},verifyEl:function(C){try{if(C){var B=C.offsetParent;
+if(B){return true;}}}catch(D){}return false;},getLocation:function(G){if(!this.isTypeOfDD(G)){return null;}var E=G.getEl(),J,D,C,L,K,M,B,I,F;try{J=YAHOO.util.Dom.getXY(E);}catch(H){}if(!J){return null;}D=J[0];C=D+E.offsetWidth;L=J[1];K=L+E.offsetHeight;M=L-G.padding[0];B=C+G.padding[1];I=K+G.padding[2];F=D-G.padding[3];return new YAHOO.util.Region(M,B,I,F);},isOverTarget:function(J,B,D,E){var F=this.locationCache[B.id];if(!F||!this.useCache){F=this.getLocation(B);this.locationCache[B.id]=F;}if(!F){return false;}B.cursorIsOver=F.contains(J);var I=this.dragCurrent;if(!I||(!D&&!I.constrainX&&!I.constrainY)){return B.cursorIsOver;}B.overlap=null;if(!E){var G=I.getTargetCoord(J.x,J.y);var C=I.getDragEl();E=new YAHOO.util.Region(G.y,G.x+C.offsetWidth,G.y+C.offsetHeight,G.x);}var H=E.intersect(F);if(H){B.overlap=H;return(D)?true:B.cursorIsOver;}else{return false;}},_onUnload:function(C,B){this.unregAll();},unregAll:function(){if(this.dragCurrent){this.stopDrag();this.dragCurrent=null;}this._execOnAll("unreg",[]);this.ids={};},elementCache:{},getElWrapper:function(C){var B=this.elementCache[C];if(!B||!B.el){B=this.elementCache[C]=new this.ElementWrapper(YAHOO.util.Dom.get(C));}return B;},getElement:function(B){return YAHOO.util.Dom.get(B);},getCss:function(C){var B=YAHOO.util.Dom.get(C);return(B)?B.style:null;},ElementWrapper:function(B){this.el=B||null;this.id=this.el&&B.id;this.css=this.el&&B.style;},getPosX:function(B){return YAHOO.util.Dom.getX(B);},getPosY:function(B){return YAHOO.util.Dom.getY(B);},swapNode:function(D,B){if(D.swapNode){D.swapNode(B);}else{var E=B.parentNode;var C=B.nextSibling;if(C==D){E.insertBefore(D,B);}else{if(B==D.nextSibling){E.insertBefore(B,D);}else{D.parentNode.replaceChild(B,D);E.insertBefore(D,C);}}}},getScroll:function(){var D,B,E=document.documentElement,C=document.body;if(E&&(E.scrollTop||E.scrollLeft)){D=E.scrollTop;B=E.scrollLeft;}else{if(C){D=C.scrollTop;B=C.scrollLeft;}else{}}return{top:D,left:B};},getStyle:function(C,B){return YAHOO.util.Dom.getStyle(C,B);},getScrollTop:function(){return this.getScroll().top;},getScrollLeft:function(){return this.getScroll().left;},moveToEl:function(B,D){var C=YAHOO.util.Dom.getXY(D);YAHOO.util.Dom.setXY(B,C);},getClientHeight:function(){return YAHOO.util.Dom.getViewportHeight();},getClientWidth:function(){return YAHOO.util.Dom.getViewportWidth();},numericSort:function(C,B){return(C-B);},_timeoutCount:0,_addListeners:function(){var B=YAHOO.util.DDM;if(YAHOO.util.Event&&document){B._onLoad();}else{if(B._timeoutCount>2000){}else{setTimeout(B._addListeners,10);if(document&&document.body){B._timeoutCount+=1;}}}},handleWasClicked:function(B,D){if(this.isHandle(D,B.id)){return true;}else{var C=B.parentNode;while(C){if(this.isHandle(D,C.id)){return true;}else{C=C.parentNode;}}}return false;}};}();YAHOO.util.DDM=YAHOO.util.DragDropMgr;YAHOO.util.DDM._addListeners();}(function(){var A=YAHOO.util.Event;var B=YAHOO.util.Dom;YAHOO.util.DragDrop=function(E,C,D){if(E){this.init(E,C,D);}};YAHOO.util.DragDrop.prototype={id:null,config:null,dragElId:null,handleElId:null,invalidHandleTypes:null,invalidHandleIds:null,invalidHandleClasses:null,startPageX:0,startPageY:0,groups:null,locked:false,lock:function(){this.locked=true;},unlock:function(){this.locked=false;},isTarget:true,padding:null,dragOnly:false,_domRef:null,__ygDragDrop:true,constrainX:false,constrainY:false,minX:0,maxX:0,minY:0,maxY:0,deltaX:0,deltaY:0,maintainOffset:false,xTicks:null,yTicks:null,primaryButtonOnly:true,available:false,hasOuterHandles:false,cursorIsOver:false,overlap:null,b4StartDrag:function(C,D){},startDrag:function(C,D){},b4Drag:function(C){},onDrag:function(C){},onDragEnter:function(C,D){},b4DragOver:function(C){},onDragOver:function(C,D){},b4DragOut:function(C){},onDragOut:function(C,D){},b4DragDrop:function(C){},onDragDrop:function(C,D){},onInvalidDrop:function(C){},b4EndDrag:function(C){},endDrag:function(C){},b4MouseDown:function(C){},onMouseDown:function(C){},onMouseUp:function(C){},onAvailable:function(){},getEl:function(){if(!this._domRef){this._domRef=B.get(this.id);}return this._domRef;},getDragEl:function(){return B.get(this.dragElId);},init:function(E,C,D){this.initTarget(E,C,D);A.on(this._domRef||this.id,"mousedown",this.handleMouseDown,this,true);},initTarget:function(E,C,D){this.config=D||{};this.DDM=YAHOO.util.DDM;this.groups={};if(typeof E!=="string"){this._domRef=E;E=B.generateId(E);}this.id=E;this.addToGroup((C)?C:"default");this.handleElId=E;A.onAvailable(E,this.handleOnAvailable,this,true);this.setDragElId(E);this.invalidHandleTypes={A:"A"};this.invalidHandleIds={};this.invalidHandleClasses=[];this.applyConfig();},applyConfig:function(){this.padding=this.config.padding||[0,0,0,0];this.isTarget=(this.config.isTarget!==false);this.maintainOffset=(this.config.maintainOffset);this.primaryButtonOnly=(this.config.primaryButtonOnly!==false);this.dragOnly=((this.config.dragOnly===true)?true:false);},handleOnAvailable:function(){this.available=true;this.resetConstraints();this.onAvailable();},setPadding:function(E,C,F,D){if(!C&&0!==C){this.padding=[E,E,E,E];}else{if(!F&&0!==F){this.padding=[E,C,E,C];}else{this.padding=[E,C,F,D];}}},setInitPosition:function(F,E){var G=this.getEl();if(!this.DDM.verifyEl(G)){return ;}var D=F||0;var C=E||0;var H=B.getXY(G);this.initPageX=H[0]-D;this.initPageY=H[1]-C;this.lastPageX=H[0];this.lastPageY=H[1];this.setStartPosition(H);},setStartPosition:function(D){var C=D||B.getXY(this.getEl());this.deltaSetXY=null;this.startPageX=C[0];this.startPageY=C[1];},addToGroup:function(C){this.groups[C]=true;this.DDM.regDragDrop(this,C);},removeFromGroup:function(C){if(this.groups[C]){delete this.groups[C];}this.DDM.removeDDFromGroup(this,C);},setDragElId:function(C){this.dragElId=C;},setHandleElId:function(C){if(typeof C!=="string"){C=B.generateId(C);}this.handleElId=C;this.DDM.regHandle(this.id,C);},setOuterHandleElId:function(C){if(typeof C!=="string"){C=B.generateId(C);}A.on(C,"mousedown",this.handleMouseDown,this,true);this.setHandleElId(C);
+this.hasOuterHandles=true;},unreg:function(){A.removeListener(this.id,"mousedown",this.handleMouseDown);this._domRef=null;this.DDM._remove(this);},isLocked:function(){return(this.DDM.isLocked()||this.locked);},handleMouseDown:function(H,G){var D=H.which||H.button;if(this.primaryButtonOnly&&D>1){return ;}if(this.isLocked()){return ;}var C=this.b4MouseDown(H);var E=this.onMouseDown(H);if((C===false)||(E===false)){return ;}this.DDM.refreshCache(this.groups);var F=new YAHOO.util.Point(A.getPageX(H),A.getPageY(H));if(!this.hasOuterHandles&&!this.DDM.isOverTarget(F,this)){}else{if(this.clickValidator(H)){this.setStartPosition();this.DDM.handleMouseDown(H,this);this.DDM.stopEvent(H);}else{}}},clickValidator:function(D){var C=A.getTarget(D);return(this.isValidHandleChild(C)&&(this.id==this.handleElId||this.DDM.handleWasClicked(C,this.id)));},getTargetCoord:function(E,D){var C=E-this.deltaX;var F=D-this.deltaY;if(this.constrainX){if(C<this.minX){C=this.minX;}if(C>this.maxX){C=this.maxX;}}if(this.constrainY){if(F<this.minY){F=this.minY;}if(F>this.maxY){F=this.maxY;}}C=this.getTick(C,this.xTicks);F=this.getTick(F,this.yTicks);return{x:C,y:F};},addInvalidHandleType:function(C){var D=C.toUpperCase();this.invalidHandleTypes[D]=D;},addInvalidHandleId:function(C){if(typeof C!=="string"){C=B.generateId(C);}this.invalidHandleIds[C]=C;},addInvalidHandleClass:function(C){this.invalidHandleClasses.push(C);},removeInvalidHandleType:function(C){var D=C.toUpperCase();delete this.invalidHandleTypes[D];},removeInvalidHandleId:function(C){if(typeof C!=="string"){C=B.generateId(C);}delete this.invalidHandleIds[C];},removeInvalidHandleClass:function(D){for(var E=0,C=this.invalidHandleClasses.length;E<C;++E){if(this.invalidHandleClasses[E]==D){delete this.invalidHandleClasses[E];}}},isValidHandleChild:function(F){var E=true;var H;try{H=F.nodeName.toUpperCase();}catch(G){H=F.nodeName;}E=E&&!this.invalidHandleTypes[H];E=E&&!this.invalidHandleIds[F.id];for(var D=0,C=this.invalidHandleClasses.length;E&&D<C;++D){E=!B.hasClass(F,this.invalidHandleClasses[D]);}return E;},setXTicks:function(F,C){this.xTicks=[];this.xTickSize=C;var E={};for(var D=this.initPageX;D>=this.minX;D=D-C){if(!E[D]){this.xTicks[this.xTicks.length]=D;E[D]=true;}}for(D=this.initPageX;D<=this.maxX;D=D+C){if(!E[D]){this.xTicks[this.xTicks.length]=D;E[D]=true;}}this.xTicks.sort(this.DDM.numericSort);},setYTicks:function(F,C){this.yTicks=[];this.yTickSize=C;var E={};for(var D=this.initPageY;D>=this.minY;D=D-C){if(!E[D]){this.yTicks[this.yTicks.length]=D;E[D]=true;}}for(D=this.initPageY;D<=this.maxY;D=D+C){if(!E[D]){this.yTicks[this.yTicks.length]=D;E[D]=true;}}this.yTicks.sort(this.DDM.numericSort);},setXConstraint:function(E,D,C){this.leftConstraint=parseInt(E,10);this.rightConstraint=parseInt(D,10);this.minX=this.initPageX-this.leftConstraint;this.maxX=this.initPageX+this.rightConstraint;if(C){this.setXTicks(this.initPageX,C);}this.constrainX=true;},clearConstraints:function(){this.constrainX=false;this.constrainY=false;this.clearTicks();},clearTicks:function(){this.xTicks=null;this.yTicks=null;this.xTickSize=0;this.yTickSize=0;},setYConstraint:function(C,E,D){this.topConstraint=parseInt(C,10);this.bottomConstraint=parseInt(E,10);this.minY=this.initPageY-this.topConstraint;this.maxY=this.initPageY+this.bottomConstraint;if(D){this.setYTicks(this.initPageY,D);}this.constrainY=true;},resetConstraints:function(){if(this.initPageX||this.initPageX===0){var D=(this.maintainOffset)?this.lastPageX-this.initPageX:0;var C=(this.maintainOffset)?this.lastPageY-this.initPageY:0;this.setInitPosition(D,C);}else{this.setInitPosition();}if(this.constrainX){this.setXConstraint(this.leftConstraint,this.rightConstraint,this.xTickSize);}if(this.constrainY){this.setYConstraint(this.topConstraint,this.bottomConstraint,this.yTickSize);}},getTick:function(I,F){if(!F){return I;}else{if(F[0]>=I){return F[0];}else{for(var D=0,C=F.length;D<C;++D){var E=D+1;if(F[E]&&F[E]>=I){var H=I-F[D];var G=F[E]-I;return(G>H)?F[D]:F[E];}}return F[F.length-1];}}},toString:function(){return("DragDrop "+this.id);}};})();YAHOO.util.DD=function(C,A,B){if(C){this.init(C,A,B);}};YAHOO.extend(YAHOO.util.DD,YAHOO.util.DragDrop,{scroll:true,autoOffset:function(C,B){var A=C-this.startPageX;var D=B-this.startPageY;this.setDelta(A,D);},setDelta:function(B,A){this.deltaX=B;this.deltaY=A;},setDragElPos:function(C,B){var A=this.getDragEl();this.alignElWithMouse(A,C,B);},alignElWithMouse:function(C,G,F){var E=this.getTargetCoord(G,F);if(!this.deltaSetXY){var H=[E.x,E.y];YAHOO.util.Dom.setXY(C,H);var D=parseInt(YAHOO.util.Dom.getStyle(C,"left"),10);var B=parseInt(YAHOO.util.Dom.getStyle(C,"top"),10);this.deltaSetXY=[D-E.x,B-E.y];}else{YAHOO.util.Dom.setStyle(C,"left",(E.x+this.deltaSetXY[0])+"px");YAHOO.util.Dom.setStyle(C,"top",(E.y+this.deltaSetXY[1])+"px");}this.cachePosition(E.x,E.y);var A=this;setTimeout(function(){A.autoScroll.call(A,E.x,E.y,C.offsetHeight,C.offsetWidth);},0);},cachePosition:function(B,A){if(B){this.lastPageX=B;this.lastPageY=A;}else{var C=YAHOO.util.Dom.getXY(this.getEl());this.lastPageX=C[0];this.lastPageY=C[1];}},autoScroll:function(J,I,E,K){if(this.scroll){var L=this.DDM.getClientHeight();var B=this.DDM.getClientWidth();var N=this.DDM.getScrollTop();var D=this.DDM.getScrollLeft();var H=E+I;var M=K+J;var G=(L+N-I-this.deltaY);var F=(B+D-J-this.deltaX);var C=40;var A=(document.all)?80:30;if(H>L&&G<C){window.scrollTo(D,N+A);}if(I<N&&N>0&&I-N<C){window.scrollTo(D,N-A);}if(M>B&&F<C){window.scrollTo(D+A,N);}if(J<D&&D>0&&J-D<C){window.scrollTo(D-A,N);}}},applyConfig:function(){YAHOO.util.DD.superclass.applyConfig.call(this);this.scroll=(this.config.scroll!==false);},b4MouseDown:function(A){this.setStartPosition();this.autoOffset(YAHOO.util.Event.getPageX(A),YAHOO.util.Event.getPageY(A));},b4Drag:function(A){this.setDragElPos(YAHOO.util.Event.getPageX(A),YAHOO.util.Event.getPageY(A));},toString:function(){return("DD "+this.id);}});YAHOO.util.DDProxy=function(C,A,B){if(C){this.init(C,A,B);this.initFrame();
+}};YAHOO.util.DDProxy.dragElId="ygddfdiv";YAHOO.extend(YAHOO.util.DDProxy,YAHOO.util.DD,{resizeFrame:true,centerFrame:false,createFrame:function(){var B=this,A=document.body;if(!A||!A.firstChild){setTimeout(function(){B.createFrame();},50);return ;}var F=this.getDragEl(),E=YAHOO.util.Dom;if(!F){F=document.createElement("div");F.id=this.dragElId;var D=F.style;D.position="absolute";D.visibility="hidden";D.cursor="move";D.border="2px solid #aaa";D.zIndex=999;D.height="25px";D.width="25px";var C=document.createElement("div");E.setStyle(C,"height","100%");E.setStyle(C,"width","100%");E.setStyle(C,"background-color","#ccc");E.setStyle(C,"opacity","0");F.appendChild(C);A.insertBefore(F,A.firstChild);}},initFrame:function(){this.createFrame();},applyConfig:function(){YAHOO.util.DDProxy.superclass.applyConfig.call(this);this.resizeFrame=(this.config.resizeFrame!==false);this.centerFrame=(this.config.centerFrame);this.setDragElId(this.config.dragElId||YAHOO.util.DDProxy.dragElId);},showFrame:function(E,D){var C=this.getEl();var A=this.getDragEl();var B=A.style;this._resizeProxy();if(this.centerFrame){this.setDelta(Math.round(parseInt(B.width,10)/2),Math.round(parseInt(B.height,10)/2));}this.setDragElPos(E,D);YAHOO.util.Dom.setStyle(A,"visibility","visible");},_resizeProxy:function(){if(this.resizeFrame){var H=YAHOO.util.Dom;var B=this.getEl();var C=this.getDragEl();var G=parseInt(H.getStyle(C,"borderTopWidth"),10);var I=parseInt(H.getStyle(C,"borderRightWidth"),10);var F=parseInt(H.getStyle(C,"borderBottomWidth"),10);var D=parseInt(H.getStyle(C,"borderLeftWidth"),10);if(isNaN(G)){G=0;}if(isNaN(I)){I=0;}if(isNaN(F)){F=0;}if(isNaN(D)){D=0;}var E=Math.max(0,B.offsetWidth-I-D);var A=Math.max(0,B.offsetHeight-G-F);H.setStyle(C,"width",E+"px");H.setStyle(C,"height",A+"px");}},b4MouseDown:function(B){this.setStartPosition();var A=YAHOO.util.Event.getPageX(B);var C=YAHOO.util.Event.getPageY(B);this.autoOffset(A,C);},b4StartDrag:function(A,B){this.showFrame(A,B);},b4EndDrag:function(A){YAHOO.util.Dom.setStyle(this.getDragEl(),"visibility","hidden");},endDrag:function(D){var C=YAHOO.util.Dom;var B=this.getEl();var A=this.getDragEl();C.setStyle(A,"visibility","");C.setStyle(B,"visibility","hidden");YAHOO.util.DDM.moveToEl(B,A);C.setStyle(A,"visibility","hidden");C.setStyle(B,"visibility","");},toString:function(){return("DDProxy "+this.id);}});YAHOO.util.DDTarget=function(C,A,B){if(C){this.initTarget(C,A,B);}};YAHOO.extend(YAHOO.util.DDTarget,YAHOO.util.DragDrop,{toString:function(){return("DDTarget "+this.id);}});YAHOO.register("dragdrop",YAHOO.util.DragDropMgr,{version:"2.4.1",build:"742"});
\ No newline at end of file
Index: trunk/wb/include/yui/README
===================================================================
--- trunk/wb/include/yui/README	(nonexistent)
+++ trunk/wb/include/yui/README	(revision 601)
@@ -0,0 +1,257 @@
+YUI Library Release Notes
+
+*** version 2.4.1 ***
+
+This release fixes a IE memory leak when attaching listeners
+to iframes/windows, as well as a few example updates.  Of the 
+YUI components, only event has changed since 2.4.0.
+
+*** version 2.4.0 ***
+
+This release introduces five new components into YUI:
+
+   * Selector
+   * Charts Widget
+   * Get Utility
+   * Profiler
+   * JSON Utility
+   
+These new components are introduced as "beta", with the exception of the Charts
+Widget which is released as an experimental component.  Please see the YUI FAQ
+on the definition of beta and experimental at:
+http://developer.yahoo.com/yui/articles/faq/#beta
+
+In addition to new components, you'll find updates and bug fixes throughout the
+library.
+
+*** version 2.3.1 ***
+
+This update contains general fixes to the YUI library.  Please see the README
+file for each component in the build directory for specific updates since
+version 2.3.0.
+
+*** version 2.3.0 ***
+
+This release introduces six new components into YUI:
+
+   * Base CSS
+   * Color Picker Control
+   * ImageLoader Utility
+   * Rich Text Editor
+   * YUI Loader Utility
+   * YUI Test Utility
+   
+These new components are introduced as "beta", with the exception of ImageLoader
+Utility which is released as an experimental component.  Please see the YUI 
+FAQ on the definition of beta and experimental at:
+http://developer.yahoo.com/yui/articles/faq/#beta
+
+In addition to new components, you'll find updates and bug fixes throughout the
+library.
+
+*** version 2.2.2 ***
+
+This update contains general fixes to the YUI library.  Please see the
+README file for each component in the build directory for specific updates since
+version 2.2.1.
+
+*** version 2.2.1 ***
+
+This update contains general fixes to the YUI library.  Please see the
+README file for each component in the build directory for specific updates since
+version 2.2.0.
+
+*** version 2.2.0 ***
+
+This release synchronizes the SourceForge version with Yahoo!'s version of YUI
+-- version number 2.2.0.  Please see the following blog article for information
+on this specific version increment at:
+http://www.yuiblog.com/blog/2007/02/20/yui-220-released
+
+The following new components are introduced with this release:
+
+   * Button
+   * DataTable
+   * Browser History Manager
+
+   The following components are now available as utilities:
+
+   * Element
+   * DataSource
+
+These new components are introduced as "beta", with the exception of Browser
+History Manager as an experimental component.  Please see the YUI FAQ on the
+definition of beta and experimental at:
+http://developer.yahoo.com/yui/articles/faq/#beta
+
+* CSS customizations *
+Calendar, Container, and Menu have been revised to allow image customizations
+through their respective CSS files.  Please refer to each component's README to
+see the full details on affected properties.
+
+Additional details for fixes and updates can be found in the README files for
+each updated component.  All YUI API cheatsheets have been updated for the
+current version and can be found at: yui/docs/assets/cheatsheets.zip.
+
+*** version 0.12.2 ***
+
+This update contains general fixes to the YUI library.  Please see the
+README file for each component in the build directory for specific updates since
+version 0.12.1.  As part of the 0.12.2 update, a number of corrections have been
+made to functional examples that were configured incorrectly in 0.12.1.
+
+*** version 0.12.1 ***
+
+This update contains general fixes to the YUI library.  Please see the
+README file for each component in the build directory for specific updates since
+version 0.12.0.
+
+Beginning with this release, all YUI minification will be performed with JSMin(
+http://www.crockford.com/javascript/jsmin.html).  Line breaks will be preserved.
+
+*** version 0.12.0 ***
+
+This version introduces the Tabview widget as well as general fixes to the YUI
+library.  Additional details can be found in the README files for each updated
+component.  All YUI API cheatsheets have been updated for the current version
+and can be found at: yui/docs/assets/cheatsheets.zip.
+
+* documentation *
+The YUI API documentation format is revised with this release.  This new
+format -- now a flat file structure under yui/docs/ -- implements the YUI
+AutoComplete widget on the main landing page to allow keyword searching of YUI.
+It also contains the full API documentation for all YUI components along with
+the following categories of data for each component(if applicable):
+
+* Inheritance hierarchy for extensions and subclasses.
+* YUI dependencies
+* Properties and methods
+* Events
+* Configuration attributes.
+
+* concatenated files *
+For convenience, three additional, minified variations of YUI are now available
+for your use:
+
+* reset-fonts-grids.css
+* yahoo-dom-event.js
+* utilities.js
+
+* API changes *
+There are interface changes to the Calendar widget.  Please see the 0.12.0
+Calendar upgrade guide at http:// developer.yahoo.com/yui/calendar/upgrade.html
+to see if this update affects your Calendar implementation and instructions on
+upgrading to version 0.12.0.
+
+
+*** version 0.11.4 ***
+
+This update contains some priority bug fixes to the following components:
+
+* Yahoo
+* Container
+* Drag and Drop
+* Event
+
+Additional details can be found in the README files for each updated component.
+
+*** version 0.11.3 ***
+
+This update contains general fixes to the YUI library.  Please see the
+README file for each component in the build directory for updates and fixes
+since version 0.11.2.
+
+*** version 0.11.2 ***
+
+This update contains a high-priority fix to Drag and Drop that prevents text
+selection.  Additional details can be found in the README file for:
+
+* Drag and Drop
+
+*** version 0.11.1 ***
+
+Release 0.11.1 addresses some high-priority bugs reported in the recently
+released version 0.11.0. Additional details can be found in the README files for
+each updated component:
+
+* Animation
+* Dom
+* Drag and Drop
+* Connection Manager
+* Container
+
+*** version 0.11.0 ***
+
+This version introduces the Logger widget.  The YUI cheatsheets for version
+0.11.0 and 0.10.0 are also included in this distribution under docs/assets.
+
+Please see the README file for each component in the build directory for
+updates and fixes since version 0.10.0.
+
+*** version 0.10.0 ***
+
+In this release, the directory structure has been modified to provide more
+component organization.  This is the new format:
+
+yui/
+  |
+  -- build/
+  | |
+  | -- dom/
+  | |
+  | | -- assets/
+  | |
+  | | -- dom.js
+  | |
+  -- docs/
+  | |
+  | -- dom/
+  | |
+  -- examples/
+  | |
+  | -- dom/
+  | |
+  | | -- img/
+
+Notice there are three immediate directories under the
+root YUI.  They are:
+
+*build/*
+This directory contains the source files for each YUI component, and each
+component is presented as a sub-directory under build/.  Notice that there are
+generally two source files: component.js and component-min.js.  The "component"
+source is the raw, commented build and "component-min" is the minified version
+of the same file with comments and whitespace removed.  With some components,
+there is an assets sub-directory.  CSS and image dependencies for each
+component are stored in its assets sub-directory.
+
+Note: If a component makes use of images, the image source path in the
+component's CSS file will point to Akamai.  The physical images are also
+available in the assets/ directory for local availability.  Please modify the
+CSS image references if you wish to use your own image assets.
+
+*docs/*
+This directory contains the jsdoc-generated API reference for every YUI
+component.  Each component is presented as a sub-directory under docs/.
+
+*examples/*
+This directory contains functional examples for each component in the YUI
+Library.  The examples are not part of the formal distribution; rather,
+they provide demonstrations of each component in use.  Each example uses YUI
+Library source files from the build/ directory, while all example-specific
+dependencies are stored in sub-directories (e.g. examples/dom/img).
+Some components, including the AutoComplete Control and Connection Manager,
+make use of PHP to implement certain examples.
+
+For example:
+In yui/examples/dom, the dom.js source file and all YUI .js dependencies
+are drawn from yui/build/ directories.  All other example-specific sources are
+stored as sub-directories: yui/examples/dom/css and yui/examples/dom/img.
+
+Each functional example is also available and functional on
+developer.yahoo.com/yui/.  They reproduced here as part of the distribution
+for your convenience.
+
+
+
+
Index: trunk/wb/framework/class.admin.php
===================================================================
--- trunk/wb/framework/class.admin.php	(revision 600)
+++ trunk/wb/framework/class.admin.php	(revision 601)
@@ -161,6 +161,10 @@
 	
 	// Print the admin footer
 	function print_footer() {
+		// include the required file for Javascript admin
+    	if(file_exists(WB_PATH.'/modules/jsadmin/jsadmin_backend_include.php')){
+      	@include(WB_PATH.'/modules/jsadmin/jsadmin_backend_include.php');
+    	}
 		$footer_template = new Template(ADMIN_PATH."/interface");
 		$footer_template->set_file('page', 'footer.html');
 		$footer_template->set_block('page', 'footer_block', 'header');
Index: trunk/wb/modules/jsadmin/uninstall.php
===================================================================
--- trunk/wb/modules/jsadmin/uninstall.php	(nonexistent)
+++ trunk/wb/modules/jsadmin/uninstall.php	(revision 601)
@@ -0,0 +1,45 @@
+<?php
+
+// $Id$
+
+// JsAdmin module for Website Baker
+// Copyright (C) 2006, Stepan Riha
+// www.nonplus.net
+
+// modified by Swen Uth for Website Baker 2.7
+
+/*
+
+ Website Baker Project <http://www.websitebaker.org/>
+ Copyright (C) 2004-2008, Ryan Djurovich
+
+ Website Baker is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Website Baker is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Website Baker; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+// prevent this file from being accessed directly
+if(!defined('WB_PATH')) { exit('Cannot access this file directly'); }
+
+// delete the rows from the addons table
+$table = TABLE_PREFIX .'addons';
+$database->query("DELETE FROM `".$table."` WHERE `".$table."`.`name` ='Javascript Admin' LIMIT 1 ");
+
+// delete the rows from the settings table
+$table = TABLE_PREFIX .'settings';
+$database->query("DELETE FROM `".$table."` WHERE `".$table."`.`name` ='mod_jsadmin_persist_order' LIMIT 1 ");
+$database->query("DELETE FROM `".$table."` WHERE `".$table."`.`name` ='mod_jsadmin_ajax_order_pages' LIMIT 1 ");
+$database->query("DELETE FROM `".$table."` WHERE `".$table."`.`name` ='mod_jsadmin_ajax_order_sections' LIMIT 1 ");
+
+?>
\ No newline at end of file
Index: trunk/wb/modules/jsadmin/info.php
===================================================================
--- trunk/wb/modules/jsadmin/info.php	(nonexistent)
+++ trunk/wb/modules/jsadmin/info.php	(revision 601)
@@ -0,0 +1,74 @@
+<?php
+
+// $Id$
+
+// JsAdmin module for Website Baker
+// Copyright (C) 2006, Stepan Riha
+// www.nonplus.net
+
+/*
+Javascript Admin for Website Baker
+
+Software License Agreement (BSD License)
+
+Copyright (c) 2006, Stepan Riha.
+All rights reserved.
+
+Portions of this software are Copyright Yahoo! Inc.
+
+Redistribution and use of this software in source and binary forms, with 
+or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of Stepan Riha may be used to endorse or
+  promote products derived from this software without specific prior
+  written permission of Stepan Riha.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ -----------------------------------------------------------------------------------------------------------
+  Javascript Admin module for Website Baker v2.7
+  Copyright (C) 2006, Stepan Riha
+  Adapted for Website Baker 2.7 by Swen Uth
+ -----------------------------------------------------------------------------------------------------------
+	v1.2 (swen, Swen Uth; Jan 24, 2008)
+		+ updated YUI framework to version 2.4.1
+   	+ adapted module to work with Website Baker 2.7
+   	+ added multilingual support and support of the module.css and module.js files
+		
+	v1.0.2 (nonplus, Stepan Riha; May 5, 2006
+		initial release by Stepan Riha (all rights reserved)
+ -----------------------------------------------------------------------------------------------------------
+*/
+
+$module_directory 		= 'jsadmin';
+$module_name 				= 'Javascript Admin';
+$module_function 			= 'tool';
+$module_version 			= '1.2';
+$module_platform 			= '2.7';
+$module_author 			= 'Stepan Riha, Swen Uth';
+$module_license 			= 'BSD License';
+$module_description 		= 'This module adds Javascript functionality to the Website Baker Admin to improve some of the UI interactions. Uses the YahooUI library.';
+
+?>
Index: trunk/wb/modules/jsadmin/tool.php
===================================================================
--- trunk/wb/modules/jsadmin/tool.php	(nonexistent)
+++ trunk/wb/modules/jsadmin/tool.php	(revision 601)
@@ -0,0 +1,121 @@
+<?php
+
+// $Id$
+
+// JsAdmin module for Website Baker
+// Copyright (C) 2006, Stepan Riha
+// www.nonplus.net
+
+// modified by Swen Uth for Website Baker 2.7
+
+/*
+
+ Website Baker Project <http://www.websitebaker.org/>
+ Copyright (C) 2004-2008, Ryan Djurovich
+
+ Website Baker is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Website Baker is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Website Baker; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+// direct access prevention
+defined('WB_PATH') OR die(header('Location: ../index.php'));
+
+// check if module language file exists for the language set by the user (e.g. DE, EN)
+if(!file_exists(WB_PATH .'/modules/jsadmin/languages/'.LANGUAGE .'.php')) {
+	// no module language file exists for the language set by the user, include default module language file EN.php
+	require_once(WB_PATH .'/modules/jsadmin/languages/EN.php');
+} else {
+	// a module language file exists for the language defined by the user, load it
+	require_once(WB_PATH .'/modules/jsadmin/languages/'.LANGUAGE .'.php');
+}
+
+// check if backend.css file needs to be included into the <body></body>
+if(!method_exists($admin, 'register_backend_modfiles') && file_exists(WB_PATH .'/modules/jsadmin/backend.css')) {
+	echo '<style type="text/css">';
+	include(WB_PATH .'/modules/jsadmin/backend.css');
+	echo "\n</style>\n";
+}
+
+require_once(WB_PATH.'/modules/jsadmin/jsadmin.php');
+
+// Check if user selected what add-ons to reload
+if(isset($_POST['submit']) AND $_POST['submit'] != '') {
+	// Include functions file
+	require_once(WB_PATH.'/framework/functions.php');
+	save_setting('mod_jsadmin_persist_order', isset($_POST['persist_order']));
+	save_setting('mod_jsadmin_ajax_order_pages', isset($_POST['ajax_order_pages']));
+	save_setting('mod_jsadmin_ajax_order_sections', isset($_POST['ajax_order_sections']));
+	echo '<div style="border: solid 2px #9c9; background: #ffd; padding: 0.5em; margin-top: 1em">'.$MESSAGE['SETTINGS']['SAVED'].'</div>';
+}
+
+// Display form
+$persist_order = get_setting('mod_jsadmin_persist_order', true) ? 'checked="1"' : '';
+$ajax_order_pages = get_setting('mod_jsadmin_ajax_order_pages', true) ? 'checked="1"' : '';
+$ajax_order_sections = get_setting('mod_jsadmin_ajax_order_sections', true) ? 'checked="1"' : '';
+?>
+<?php
+
+// THIS ROUTINE CHECKS THE EXISTING OFF ALL NEEDED YUI FILES
+  $YUI_ERROR=false; // ist there an Error
+  $YUI_PUT ='';   // String with javascipt includes
+  $YUI_PUT_MISSING_Files=''; // String with missing files
+  reset($js_yui_scripts);
+  foreach($js_yui_scripts as $script) {
+    $fcheck =fopen($script,"r");  // Check if File Exist , This is better the file_exists
+    if(!$fcheck){
+        $YUI_ERROR=true;
+        $YUI_PUT_MISSING_Files =$YUI_PUT_MISSING_Files."- ".$script."<br />";   // catch all missing files
+    }
+    fclose($fcheck);
+	}
+	if($YUI_ERROR)
+	{
+    ?><div id="jsadmin_install" style="border: solid 2px #c99; background: #ffd; padding: 0.5em; margin-top: 1em">
+
+     <?php echo $MOD_JSADMIN['TXT_ERROR_INSTALLINFO_B'].$YUI_PUT_MISSING_Files; ?>
+      </div>
+      <?php
+  }
+  else
+  {
+  ?>
+   <form id="jsadmin_form" style="margin-top: 1em; display: true;" action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="post">
+   <table cellpadding="4" cellspacing="0" border="0">
+   <tr>
+	     <td colspan="2"><?php echo $MOD_JSADMIN['TXT_HEADING_B']; ?>:</td>
+   </tr>
+   <tr>
+	     <td width="20"><input type="checkbox" name="persist_order" id="persist_order" value="true" <?php echo $persist_order; ?>/></td>
+	     <td><label for="persist_order"><?php echo $MOD_JSADMIN['TXT_PERSIST_ORDER_B']; ?></label></td>
+   </tr>
+   <tr>
+	     <td width="20"><input type="checkbox" name="ajax_order_pages" id="ajax_order_pages" value="true" <?php echo $ajax_order_pages; ?>/></td>
+	     <td><label for="ajax_order_pages"><?php echo $MOD_JSADMIN['TXT_AJAX_ORDER_PAGES_B']; ?></label></td>
+   </tr>
+   <tr>
+	     <td width="20"><input type="checkbox" name="ajax_order_sections" id="ajax_order_sections" value="true" <?php echo $ajax_order_sections; ?>/></td>
+	     <td><label for="ajax_order_sections"><?php echo $MOD_JSADMIN['TXT_AJAX_ORDER_SECTIONS_B']; ?></label></td>
+   </tr>
+   <tr>
+	     <td>&nbsp;</td>
+	     <td>
+		   <input type="submit" name="submit" value="<?php echo $TEXT['SAVE']; ?>" onClick="javascript: if(!confirm('<?php echo $TEXT['ARE_YOU_SURE']; ?>')) { return false; }" />
+	    </td>
+   </tr>
+   </table>
+   </form>
+ <?php
+ }
+?>
Index: trunk/wb/modules/jsadmin/images/busy.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: trunk/wb/modules/jsadmin/images/busy.gif
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: trunk/wb/modules/jsadmin/images/failure.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: trunk/wb/modules/jsadmin/images/failure.gif
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: trunk/wb/modules/jsadmin/images/success.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: trunk/wb/modules/jsadmin/images/success.gif
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: trunk/wb/modules/jsadmin/jsadmin_backend_include.php
===================================================================
--- trunk/wb/modules/jsadmin/jsadmin_backend_include.php	(nonexistent)
+++ trunk/wb/modules/jsadmin/jsadmin_backend_include.php	(revision 601)
@@ -0,0 +1,144 @@
+<?php
+
+// $Id$
+
+// JsAdmin module for Website Baker
+// Copyright (C) 2006, Stepan Riha
+// www.nonplus.net
+
+// modified by Swen Uth for Website Baker 2.7
+
+/*
+
+ Website Baker Project <http://www.websitebaker.org/>
+ Copyright (C) 2004-2008, Ryan Djurovich
+
+ Website Baker is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Website Baker is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Website Baker; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+// Direct access prevention
+defined('WB_PATH') OR die(header('Location: ../index.php'));
+
+$JSADMIN_PATH = WB_URL.'/modules/jsadmin';
+$YUI_PATH = WB_URL.'/include/yui';
+$script = $_SERVER['SCRIPT_NAME'];
+if(!$script)
+	$script = $PHP_SELF;
+
+if(strstr($script, "/admin/pages/index.php"))
+	$page_type = 'pages';
+elseif(strstr($script, "/admin/pages/sections.php"))
+	$page_type = 'sections';
+elseif(strstr($script, "/admin/settings/tool.php")
+	&& isset($_REQUEST["tool"]) && $_REQUEST["tool"] == 'jsadmin')
+	$page_type = 'config';
+else
+	$page_type = '';
+if($page_type) {
+	require_once(WB_PATH.'/modules/jsadmin/jsadmin.php');
+
+	// Default scripts
+	$js_buttonCell = 3;
+	$js_scripts = Array();
+	$js_scripts[] = 'jsadmin.js';
+
+	if($page_type == 'pages') {
+		if(!get_setting('mod_jsadmin_persist_order', '1')) {   //Maybe Bug settings to negativ for persist , by Swen Uth
+			$js_scripts[] = 'restore_pages.js';
+  		}
+		if(get_setting('mod_jsadmin_ajax_order_pages', '1')) {
+			$js_scripts[] = 'dragdrop.js';
+			$js_buttonCell= 6; // This ist the Cell where the Button "Up" is , by Swen Uth
+		}
+	} elseif($page_type == 'sections') {
+		if(get_setting('mod_jsadmin_ajax_order_sections', '1')) {
+			$js_scripts[] = 'dragdrop.js';
+			$js_buttonCell= 3; // This ist the Cell where the Button "Up" is , by Swen Uth
+		}
+	} elseif($page_type == 'config') {
+		$js_scripts[] = 'tool.js';
+	}
+?>
+<style type="text/css">
+/* Fix for funky spacing on page listing */
+.pages_list li {
+	margin-bottom: -2px;
+}
+
+body.jsadmin_busy td.content {
+	background: white url(<?php echo WB_URL ?>/modules/jsadmin/images/busy.gif) 4px 4px no-repeat;
+}
+
+body.jsadmin_success td.content {
+	background: white url(<?php echo WB_URL ?>/modules/jsadmin/images/success.gif) 4px 4px no-repeat;
+}
+
+body.jsadmin_failure td.content {
+	background: white url(<?php echo WB_URL ?>/modules/jsadmin/images/failure.gif) 4px 4px no-repeat;
+}
+
+.jsadmin_drag {
+	cursor: move;
+}
+
+.jsadmin_drag a, .jsadmin_drag input, .jsadmin_drag select {
+	cursor: pointer;
+	cursor: hand;
+}
+
+ul.jsadmin_drag_area {
+	border: solid 1px #99f;
+}
+
+</style>
+
+<script language="JavaScript">
+<!--
+var JsAdmin = { WB_URL : '<?php echo WB_URL ?>' };
+//-->
+</script>
+<?php
+ // For variable cell structure in the tables of admin content
+  echo "<script type='text/javascript'>buttonCell=".$js_buttonCell.";</script>\n";   // , by Swen  Uth
+
+ // Check and Load the needed YUI functions  //, all by Swen Uth
+  $YUI_ERROR=false; // ist there an Error
+  $YUI_PUT ='';   // String with javascipt includes
+  $YUI_PUT_MISSING_Files=''; // Strin with missing files
+  
+  foreach($js_yui_scripts as $script) {
+    $fcheck =fopen($script,"r");  // Check if File Exist , This is better the file_exists
+    if($fcheck){
+        $YUI_PUT=$YUI_PUT."<script type='text/javascript' src='".$script."'></script>\n"; // go and include
+    } else {
+        $YUI_ERROR=true;
+        $YUI_PUT_MISSING_Files=$YUI_PUT_MISSING_Files."- ".$script."\\n";   // catch all missing files
+    }
+    fclose($fcheck);
+	}
+	if(!$YUI_ERROR)
+	{
+    echo $YUI_PUT;  // no Error so go and include
+    // Load the needed functions
+	  foreach($js_scripts as $script) {
+		  echo "<script type='text/javascript' src='".$JSADMIN_PATH."/js/".$script."'></script>\n";
+	  }
+  } else  {
+      echo "<script type='text/javascript'>alert('YUI ERROR!! File not Found!! > \\n".$YUI_PUT_MISSING_Files." so look in the include folder or switch Javascript Admin off!');</script>\n"; //, by Swen Uth
+  }
+
+ }
+?>
Index: trunk/wb/modules/jsadmin/languages/EN.php
===================================================================
--- trunk/wb/modules/jsadmin/languages/EN.php	(nonexistent)
+++ trunk/wb/modules/jsadmin/languages/EN.php	(revision 601)
@@ -0,0 +1,36 @@
+<?php
+
+// $Id$
+
+/*
+
+ Website Baker Project <http://www.websitebaker.org/>
+ Copyright (C) 2004-2008, Ryan Djurovich
+
+ Website Baker is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Website Baker is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Website Baker; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ -----------------------------------------------------------------------------------------
+  ENGLISH LANGUAGE FILE FOR THE ADDON: JAVASCRIPT ADMIN
+ -----------------------------------------------------------------------------------------
+*/
+
+// Headings and text outputs
+$MOD_JSADMIN['TXT_HEADING_B'] 				= 'Please choose the Javascript functions you want to enable';
+$MOD_JSADMIN['TXT_PERSIST_ORDER_B'] 		= 'Remember expanded pages';
+$MOD_JSADMIN['TXT_AJAX_ORDER_PAGES_B'] 	= 'Reorder pages by the use of drag-and-drop';
+$MOD_JSADMIN['TXT_AJAX_ORDER_SECTIONS_B'] = 'Reorder sections by the use of drag-and-drop';
+$MOD_JSADMIN['TXT_ERROR_INSTALLINFO_B'] 	= '<h1>Error</h1><p>JavaScript Admin requires the YUI (Yahoo User Interface) framework.<br />The following files are required to get Javascript Admin work as expected:<br /><br />';
+
+?>
\ No newline at end of file
Index: trunk/wb/modules/jsadmin/languages/DE.php
===================================================================
--- trunk/wb/modules/jsadmin/languages/DE.php	(nonexistent)
+++ trunk/wb/modules/jsadmin/languages/DE.php	(revision 601)
@@ -0,0 +1,39 @@
+<?php
+
+// $Id$
+
+/*
+
+ Website Baker Project <http://www.websitebaker.org/>
+ Copyright (C) 2004-2008, Ryan Djurovich
+
+ Website Baker is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Website Baker is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Website Baker; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ -----------------------------------------------------------------------------------------
+  DEUTSCHE SPRACHDATEI FUER DAS MODUL: JAVASCRIPT ADMIN
+ -----------------------------------------------------------------------------------------
+*/
+
+// Deutsche Modulbeschreibung
+$module_description 	= 'Dieses Modul stellt Javascript Funktionen f&uuml;r die Website Baker Benutzeroberfl&auml;che zur Verf&uuml;gung. Verwendet das YahooUI Javascript Framework.';
+
+// Überschriften und Textausgaben
+$MOD_JSADMIN['TXT_HEADING_B'] 				= 'Bitte w&auml;hle die gew&uuml;nschten Javascript Funktionen aus';
+$MOD_JSADMIN['TXT_PERSIST_ORDER_B'] 		= 'Ge&ouml;ffneten Seitenbaum merken';
+$MOD_JSADMIN['TXT_AJAX_ORDER_PAGES_B'] 	= 'Sortierung von Seiten per "drag-and-drop" erlauben';
+$MOD_JSADMIN['TXT_AJAX_ORDER_SECTIONS_B'] = 'Sortierung von Abschnitten per "drag-and-drop" erlauben';
+$MOD_JSADMIN['TXT_ERROR_INSTALLINFO_B'] 	= '<h1>Fehler</h1><p>JavaScript Admin ben&ouml;tigt das YUI (Yahoo User Interface) Framework.<br />Folgende Dateien werden f&uuml;r das Modul ben&ouml;tigt:<br /><br />';
+
+?>
\ No newline at end of file
Index: trunk/wb/modules/jsadmin/README.txt
===================================================================
--- trunk/wb/modules/jsadmin/README.txt	(nonexistent)
+++ trunk/wb/modules/jsadmin/README.txt	(revision 601)
@@ -0,0 +1,65 @@
+Javscript Admin
+
+Release 1.2
+ On Sourcecode of
+(C) Copyright 2006 Stepan Riha (www.nonplus.net)
+* -----------------------------------------------------------------------------------------
+*	MODIFICATION HISTORY:
+*   by Swen Uth; 01/24/2008
+*   + Update to 1.2
+*   + Update to Website Baker (include/yui)-> YUI to Version 2.4.1
+*   + take the YUI folder to wb/include/
+*   + Website Baker 2.7 Kompatible
+*   + Multilingual
+*   + initial release of the English language file
+*   + initial release of the German language file
+*   + include the Option for backend.cs
+*   + include the option for backend.js
+*   + renamed footer.php to jsadmin_backend_include.php for a better understanding for integration and development
+*   + INCLUDE ERRORHANDLING for missing YUI - Files
+*   + the Array $js_yui_scripts are defined in the jsadmin.php for better development
+* -----------------------------------------------------------------------------------------
+*
+______________________________________________________________
+
+DESCRIPTION
+
+This tool module adds Javascript functionality to the Website Baker Admin to
+improve some of the UI interactions.
+
+Portions of this module use scripts from the Yahoo! UI Library
+You can find it in the WB/include folder
+
+	http://developer.yahoo.com/yui/
+	
+______________________________________________________________
+
+REQUIREMENTS
+
+This module is supported on Website Baker 2.7 and later.
+
+______________________________________________________________
+
+INSTALLATION
+
+The module includes the following files:
+
+	!!! footer.php is canceled -> renamed to jsadmin_backend_include.php
+	images/busy.gif
+	images/failure.gif
+	images/success.gif
+	index.php
+	info.php
+	js/dragdrop.js
+	js/jsadmin.js
+	js/restore_pages.js
+	js/tool.js
+ 	+ languages/EN.php
+	+ languages/DE.php
+	jsadmin.php
+	move_to.php
+	README.txt
+	tool.php
+
+______________________________________________________________
+
Index: trunk/wb/modules/jsadmin/jsadmin.php
===================================================================
--- trunk/wb/modules/jsadmin/jsadmin.php	(nonexistent)
+++ trunk/wb/modules/jsadmin/jsadmin.php	(revision 601)
@@ -0,0 +1,63 @@
+<?php
+
+// $Id$
+
+// JsAdmin module for Website Baker
+// Copyright (C) 2006, Stepan Riha
+// www.nonplus.net
+
+// modified by Swen Uth for Website Baker 2.7
+
+/*
+
+ Website Baker Project <http://www.websitebaker.org/>
+ Copyright (C) 2004-2008, Ryan Djurovich
+
+ Website Baker is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Website Baker is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Website Baker; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+function get_setting($name, $default = '') {
+	global $database;
+	$rs = $database->query("SELECT value FROM ".TABLE_PREFIX."settings WHERE name = '".$name."'");
+	if($row = $rs->fetchRow())
+		return $row['value'];
+	return
+		$default;
+}
+
+function save_setting($name, $value) {
+	global $database;
+
+	$prev_value = get_setting($name, false);
+
+	if($prev_value === false) {
+		$database->query("INSERT INTO ".TABLE_PREFIX."settings (name,value) VALUES ('$name','$value')");
+	} else {
+		$database->query("UPDATE ".TABLE_PREFIX."settings SET value = '$value' WHERE name = '$name'");
+	}
+}
+
+// the follwing variables to use and check existing the YUI
+$YUI_PATH = WB_URL.'/include/yui';
+$js_yui_min = "-min";  // option for smaller code so faster
+$js_yui_scripts = Array();
+$js_yui_scripts[] = $YUI_PATH.'/yahoo/yahoo'.$js_yui_min.'.js';
+$js_yui_scripts[] = $YUI_PATH.'/event/event'.$js_yui_min.'.js';
+$js_yui_scripts[] = $YUI_PATH.'/dom/dom'.$js_yui_min.'.js';
+$js_yui_scripts[] = $YUI_PATH.'/connection/connection'.$js_yui_min.'.js';
+$js_yui_scripts[] = $YUI_PATH.'/dragdrop/dragdrop'.$js_yui_min.'.js';
+
+?>
\ No newline at end of file
Index: trunk/wb/modules/jsadmin/install.php
===================================================================
--- trunk/wb/modules/jsadmin/install.php	(nonexistent)
+++ trunk/wb/modules/jsadmin/install.php	(revision 601)
@@ -0,0 +1,41 @@
+<?php
+
+// $Id$
+
+// JsAdmin module for Website Baker
+// Copyright (C) 2006, Stepan Riha
+// www.nonplus.net
+
+// modified by Swen Uth for Website Baker 2.7
+
+/*
+
+ Website Baker Project <http://www.websitebaker.org/>
+ Copyright (C) 2004-2008, Ryan Djurovich
+
+ Website Baker is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Website Baker is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Website Baker; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+// prevent this file from being accessed directly
+if(!defined('WB_PATH')) { exit('Cannot access this file directly'); }
+
+// add new rows to table "settings"
+global $database;
+$database->query("INSERT INTO ".TABLE_PREFIX."settings (name,value) VALUES ('mod_jsadmin_persist_order','0')");
+$database->query("INSERT INTO ".TABLE_PREFIX."settings (name,value) VALUES ('mod_jsadmin_ajax_order_pages','0')");
+$database->query("INSERT INTO ".TABLE_PREFIX."settings (name,value) VALUES ('mod_jsadmin_ajax_order_sections','0')");
+
+?>
\ No newline at end of file
Index: trunk/wb/modules/jsadmin/js/jsadmin.js
===================================================================
--- trunk/wb/modules/jsadmin/js/jsadmin.js	(nonexistent)
+++ trunk/wb/modules/jsadmin/js/jsadmin.js	(revision 601)
@@ -0,0 +1,162 @@
+// Copyright 2006 Stepan Riha
+// www.nonplus.net
+// $Id: jsadmin.js 2 2006-04-18 03:04:39Z stepan $
+
+// Initialize JsAdmin when page loads
+JsAdmin.loadHandler = function(ev, self) {
+	if(self.init_tool) {
+		self.init_tool();
+	}
+	if(self.restore_toggled) {
+		self.restore_toggled();
+		YAHOO.util.Event.addListener(window, 'unload', JsAdmin.unloadHandler, self, false);
+	}
+	if(self.init_drag_drop) {
+		self.init_drag_drop();
+	}
+};
+
+// Store JsAdmin cookies when page unloads
+JsAdmin.unloadHandler = function(ev, self) {
+	self.save_toggled();
+};
+
+JsAdmin.rowMouseOverHandler = function(ev, tr) {
+	YAHOO.util.Dom.setStyle(tr, 'background' ,'#fea');
+};
+
+JsAdmin.rowMouseOutHandler = function(ev, tr) {
+	YAHOO.util.Dom.setStyle(tr, 'background' ,'');
+};
+
+YAHOO.util.Event.addListener(window, 'load', JsAdmin.loadHandler, JsAdmin, false);
+
+JsAdmin.util = {
+	createCookie : function(name,value,days)
+	{
+		if (days)
+		{
+			var date = new Date();
+			date.setTime(date.getTime()+(days*24*60*60*1000));
+			var expires = "; expires="+date.toGMTString();
+		}
+		else var expires = "";
+		document.cookie = name+"="+value+expires+"; path=/";
+	},
+
+	readCookie : function(name)
+	{
+		var nameEQ = name + "=";
+		var ca = document.cookie.split(';');
+		for(var i=0;i < ca.length;i++)
+		{
+			var c = ca[i];
+			while (c.charAt(0)==' ') c = c.substring(1,c.length);
+			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
+		}
+		return null;
+	},
+
+	eraseCookie : function(name)
+	{
+		this.createCookie(name,"",-1);
+	},
+
+	next_id : 1,
+	getUniqueId : function () {
+		var id;
+		do {
+			id = 'jsadmin_id_' + this.next_id++;
+		} while(YAHOO.util.Dom.get(id));
+		return id;
+	},
+	
+	isNodeType : function(elt, type) {
+		if(elt) {
+			return elt.nodeName.toUpperCase() == type.toUpperCase();
+		}
+		return false;
+	},
+
+	getItemIndex : function (elt) {
+		var type = elt.nodeName;
+		var index = 0;
+		for(var sib = elt.previousSibling; sib; sib = sib.previousSibling) {
+			if(sib.nodeName == type) {
+				index++;
+			}
+		}
+		return index;
+	},
+	
+	getAncestorNode : function(elt, type) {
+		if(elt) {
+			if(type) {
+				type = type.toUpperCase();
+			} else {
+				type = elt.nodeName.toUpperCase();
+			}
+			elt = elt.parentNode;
+			while(elt && elt.nodeName.toUpperCase() != type) {
+				elt = elt.parentNode;
+			}
+		}
+		return elt;
+	},
+	
+	getNextSiblingNode : function (elt, type) {
+		if(elt) {
+			if(type) {
+				type = type.toUpperCase();
+			} else {
+				type = elt.nodeName.toUpperCase();
+			}
+			elt = elt.nextSibling;
+			while(elt && elt.nodeName.toUpperCase() != type) {
+				elt = elt.nextSibling;
+			}
+		}
+		return elt;
+	},
+
+	matchNextSibling : function (elt, re) {
+		if(elt) {
+			elt = elt.nextSibling;
+			while(elt && !elt.nodeName.match(re)) {
+				elt = elt.nextSibling;
+			}
+		}
+		return elt;
+	},
+
+	getPreviousSiblingNode : function (elt, type) {
+		if(elt) {
+			if(type) {
+				type = type.toUpperCase();
+			} else {
+				type = elt.nodeName.toUpperCase();
+			}
+			elt = elt.previousSibling;
+			while(elt && elt.nodeName.toUpperCase() != type) {
+				elt = elt.previousSibling;
+			}
+		}
+		return elt;
+	},
+
+	insertBefore : function(parent, newChild, beforeChild) {
+		if(beforeChild) {
+			parent.insertBefore(newChild, beforeChild);
+		} else {
+			parent.appendChild(newChild);
+		}
+	},
+
+	insertAfter : function(parent, newChild, afterChild) {
+		if(afterChild) {
+			this.insertBefore(parent, newChild, afterChild.nextSibling);
+		} else {
+			this.insertBefore(parent, newChild, parent.firstChild);
+		}
+	}
+};
Index: trunk/wb/modules/jsadmin/js/tool.js
===================================================================
--- trunk/wb/modules/jsadmin/js/tool.js	(nonexistent)
+++ trunk/wb/modules/jsadmin/js/tool.js	(revision 601)
@@ -0,0 +1,14 @@
+// Copyright 2006 Stepan Riha
+// www.nonplus.net
+// $Id: tool.js 2 2006-04-18 03:04:39Z stepan $
+
+JsAdmin.init_tool = function() {
+	var instruction = YAHOO.util.Dom.get('jsadmin_install');
+	if(instruction) {
+		instruction.style.display = 'none';
+	}
+	var form = YAHOO.util.Dom.get('jsadmin_form');
+	if(form) {
+		form.style.display = '';
+	}
+};
Index: trunk/wb/modules/jsadmin/js/dragdrop.js
===================================================================
--- trunk/wb/modules/jsadmin/js/dragdrop.js	(nonexistent)
+++ trunk/wb/modules/jsadmin/js/dragdrop.js	(revision 601)
@@ -0,0 +1,345 @@
+// Copyright 2006 Stepan Riha
+// www.nonplus.net
+// $Id: dragdrop.js 2 2006-04-18 03:04:39Z stepan $
+/**
+* -----------------------------------------------------------------------------------------
+*  MODIFICATON FOR THE JSADMIN MODULE
+* -----------------------------------------------------------------------------------------
+*	MODIFICATION HISTORY:
+*   Swen Uth; 01/24/2008
+*   +INCLUDE VARIABLE buttonCell FOR ADAPTATION TO LATER LAYOUTS
+*
+**/
+JsAdmin.DD = {};
+JsAdmin.movable_rows = {};
+	
+JsAdmin.init_drag_drop = function() {
+
+	// There seems to be many different ways the ordering is set up
+	//		pages/index.php has UL/LI containing tables with single row
+	//		pages/sections.php has a TABLE with many rows
+	//		pages/modify.php for manuals is completely weird...
+	// So we only want to deal with pages & sections...
+
+	var page_type = '';
+	var is_tree = false;
+	if(document.URL.indexOf("/admin/pages/index.php") > 0) {
+		page_type = 'pages';
+		is_tree = true;
+
+		// This page uses duplicate IDs and incorrectly nested lists:
+		// <ul id="p1">
+		//		<li id="p1"><table /></li>
+		//		<ul>... sub items ...</ul>
+		// </ul>
+		//
+		// We need to fix that to the following:
+		// <ul id="p1">
+		//		<li id="uniqueID"><table />
+		//		<ul>... sub items ...</ul>
+		//		</li>
+		// </ul>
+
+		// Stash all UL ids
+		var ids = {};
+		var lists = document.getElementsByTagName('ul');
+		for(var i = 0; i < lists.length; i++) {
+			if(lists[i].id) {
+				ids[lists[i].id] = true;
+			}
+		}
+
+		// Now fix all LIs
+		var items = document.getElementsByTagName('li');
+ 		for(var i = 0; i < items.length; i++) {
+			var item = items[i];
+
+			// Fix duplicate ID
+			if(ids[item.id]) {
+				item.id =  JsAdmin.util.getUniqueId();
+			}
+
+			// Fix UL parented by UL
+			var ul = JsAdmin.util.getNextSiblingNode(item, 'ul');
+			if(ul) {
+				var lis = ul.getElementsByTagName('li');
+ 				if(!lis || lis.length == 0) {
+					// Remove list without items
+					ul.parentNode.removeChild(ul);
+				} else {
+					// Make list child of list item
+					item.appendChild(ul);
+				}
+			}
+		}
+
+	} else if(document.URL.indexOf("/admin/pages/sections.php") > 0) {
+		page_type = 'sections';
+	} else {
+		// We don't do any other pages
+		return;
+	}
+
+	var links = document.getElementsByTagName('a');
+	var reImg = /(.*)move_(down|up)\.php(.*)/;
+
+	for(var i = 0; i < links.length; i++) {
+		var link = links[i];
+		var href = link.href || '';
+		var match = href.match(reImg);
+		if(!match) {
+			continue;
+		}
+		var url = match[1];
+		var op = match[2];
+		var params = match[3];
+		var tr = JsAdmin.util.getAncestorNode(link, 'tr');
+		var item = is_tree ? JsAdmin.util.getAncestorNode(tr, 'li') : tr;
+		if(!item) {
+			continue;
+		}
+
+		// Make sure we have a unique id
+		if(!item.id || YAHOO.util.Dom.get(item.id) != item) {
+			item.id = JsAdmin.util.getUniqueId();
+		}
+
+		if(is_tree) {
+			var parent = JsAdmin.util.getAncestorNode(item, 'ul');
+			new JsAdmin.DD.liDDSwap(item.id, (parent && parent.id) ? parent.id : 'top');
+		} else {
+			new JsAdmin.DD.trDDSwap(item.id);
+		}
+		item.className += " jsadmin_drag";
+
+		this.movable_rows[item.id] = { item: item, tr : tr, url : url, params : params };
+	}
+};
+
+//==========================================================================
+// Drag-drop utils
+//==========================================================================
+
+JsAdmin.DD.dragee = null;
+
+JsAdmin.DD.addMoveButton = function(tr, cell, op) {
+	if(op == 'down') {
+		cell++;
+	}
+	var item = JsAdmin.movable_rows[tr.id];
+	if(!JsAdmin.util.isNodeType(tr, 'tr')) {
+		var rows = tr.getElementsByTagName('tr');
+		tr = rows[0];
+	}
+	
+	var html = '<a href="' + item.url + 'move_' + op + '.php' + item.params
+				+ '"><img src="' + JsAdmin.WB_URL + '/admin/images/' + op
+				+ '_16.png" border="0" alt="' + op + '" /></a>';
+	tr.cells[cell].innerHTML = html;
+};
+
+JsAdmin.DD.deleteMoveButton = function(tr, cell, op) {
+	if(op == 'down') {
+		cell++;
+	}
+	if(!JsAdmin.util.isNodeType(tr, 'tr')) {
+		var rows = tr.getElementsByTagName('tr');
+		tr = rows[0];
+	}
+	
+	tr.cells[cell].innerHTML = "";
+};
+
+//==========================================================================
+// Drag-drop handling for table rows
+//==========================================================================
+
+JsAdmin.DD.trDDSwap = function(id, sGroup) {
+    this.init(id, sGroup);
+	this.addInvalidHandleType('a');
+	this.addInvalidHandleType('input');
+	this.addInvalidHandleType('select');
+    this.initFrame();
+	this.buttonCell = buttonCell;//, by Swen Uth
+	
+	// For Connection
+	this.scope = this;
+};
+
+JsAdmin.DD.trDDSwap.prototype = new YAHOO.util.DDProxy();
+
+JsAdmin.DD.trDDSwap.prototype.startDrag = function(x, y) {
+	if (JsAdmin.DD.dragee != this) {
+		this.rowIndex = this.getEl().rowIndex;
+		this.numRows = this.getEl().parentNode.rows.length;
+		this.opacity = YAHOO.util.Dom.getStyle(this.getEl(), "opacity");
+		this.background = YAHOO.util.Dom.getStyle(this.getEl(), "background");
+		YAHOO.util.Dom.setStyle(this.getEl(), "opacity", 0.5);
+		YAHOO.util.Dom.setStyle(this.getEl(), "background", "#dde");
+	}
+	JsAdmin.DD.dragee = this;
+};
+
+JsAdmin.DD.trDDSwap.prototype.onDragEnter = function(e, id) {
+  var elt = id ? YAHOO.util.Dom.get(id) : null;
+	var item = JsAdmin.movable_rows[this.getEl().id];
+	var rows = item.tr.parentNode.rows;
+	var wasFirst = item.tr.rowIndex == 1;
+	var wasLast = item.tr.rowIndex == this.numRows - 2;
+	if(elt.rowIndex < item.tr.rowIndex) {
+		elt.parentNode.insertBefore(item.tr, elt);
+	} else {
+		elt.parentNode.insertBefore(elt, item.tr);
+	}
+	// Fixup buttons
+	var isFirst = item.tr.rowIndex == 1;
+	var isLast = item.tr.rowIndex == this.numRows - 2;
+
+	if(wasFirst != isFirst) {
+		if(isFirst) {
+			JsAdmin.DD.deleteMoveButton(item.tr, this.buttonCell, 'up');
+			JsAdmin.DD.addMoveButton(JsAdmin.util.getNextSiblingNode(item.tr), this.buttonCell, 'up');
+		} else {
+			JsAdmin.DD.addMoveButton(item.tr, this.buttonCell, 'up');
+			JsAdmin.DD.deleteMoveButton(rows[1], this.buttonCell, 'up');
+		}
+	}
+	if(wasLast != isLast) {
+		if(isLast) {
+			JsAdmin.DD.deleteMoveButton(item.tr, this.buttonCell, 'down');
+			JsAdmin.DD.addMoveButton(JsAdmin.util.getPreviousSiblingNode(item.tr), this.buttonCell, 'down');
+		} else {
+			JsAdmin.DD.addMoveButton(item.tr, this.buttonCell, 'down');
+			JsAdmin.DD.deleteMoveButton(rows[rows.length-2], this.buttonCell, 'down');
+		}
+	}
+
+	this.DDM.refreshCache(this.groups);
+};
+
+JsAdmin.DD.trDDSwap.prototype.endDrag = function(e) {
+	YAHOO.util.Dom.setStyle(this.getEl(), "opacity", this.opacity);
+	YAHOO.util.Dom.setStyle(this.getEl(), "background", "white");
+	
+	JsAdmin.DD.dragee = null;
+
+	var newIndex = this.getEl().rowIndex;
+	if(newIndex != this.rowIndex) {
+		var url = JsAdmin.WB_URL + "/modules/jsadmin/move_to.php";
+		url += JsAdmin.movable_rows[this.getEl().id].params + "&position=" + newIndex;
+		document.body.className = String(document.body.className).replace(/(\s*)jsadmin_([a-z]+)/g, "$1") + " jsadmin_busy";
+		YAHOO.util.Connect.asyncRequest('GET', url, this, null);
+	}
+};
+
+JsAdmin.DD.trDDSwap.prototype.success = function(o) {
+	document.body.className = String(document.body.className).replace(/(\s*)jsadmin_([a-z]+)/g, "$1") + " jsadmin_success";
+};
+
+JsAdmin.DD.trDDSwap.prototype.failure = function(o) {
+	document.body.className = String(document.body.className).replace(/(\s*)jsadmin_([a-z]+)/, "$1") + " jsadmin_failure";
+};
+
+//==========================================================================
+// Drag-drop handling for list items
+//==========================================================================
+
+JsAdmin.DD.liDDSwap = function(id, sGroup) {
+    this.init(id, sGroup);
+	this.addInvalidHandleType('a');
+	this.addInvalidHandleType('input');
+	this.addInvalidHandleType('select');
+    this.initFrame();
+ this.buttonCell = buttonCell;//, by Swen Uth
+	this.counter = 0;
+};
+
+JsAdmin.DD.liDDSwap.prototype = new YAHOO.util.DDProxy();
+
+JsAdmin.DD.liDDSwap.prototype.startDrag = function(x, y) {
+	// On IE, startDrag is sometimes called twice
+	if(JsAdmin.DD.dragee && JsAdmin.DD.dragee != this) {
+		JsAdmin.DD.dragee.endDrag(null);
+	}
+	if(JsAdmin.DD.dragee != this) {
+		this.rowIndex = JsAdmin.util.getItemIndex(this.getEl());
+		this.opacity = YAHOO.util.Dom.getStyle(this.getEl(), "opacity");
+		this.background = YAHOO.util.Dom.getStyle(this.getEl(), "background");
+		YAHOO.util.Dom.setStyle(this.getEl(), "opacity", 0.5);
+
+		this.list = JsAdmin.util.getAncestorNode(this.getEl(), "ul");
+		this.list.className += " jsadmin_drag_area";
+	}
+	JsAdmin.DD.dragee = this;
+};
+
+JsAdmin.DD.liDDSwap.prototype.onDragEnter = function(e, id) {
+	// Swap with other element
+	var elt = id ? YAHOO.util.Dom.get(id) : null;
+	var item = JsAdmin.movable_rows[this.getEl().id];
+	var eltRowIndex = JsAdmin.util.getItemIndex(elt);
+	var rowIndex = JsAdmin.util.getItemIndex(this.getEl());
+	var wasFirst = !JsAdmin.util.getPreviousSiblingNode(this.getEl());
+	var wasLast = !JsAdmin.util.getNextSiblingNode(this.getEl());
+
+	if(eltRowIndex < rowIndex) {
+		elt.parentNode.insertBefore(this.getEl(), elt);
+	} else {
+		elt.parentNode.insertBefore(elt, this.getEl());
+	}
+	// Fixup buttons
+	var isFirst = !JsAdmin.util.getPreviousSiblingNode(this.getEl());
+	var isLast = !JsAdmin.util.getNextSiblingNode(this.getEl());
+
+	if(wasFirst != isFirst) {
+		if(isFirst) {
+			JsAdmin.DD.deleteMoveButton(item.tr, this.buttonCell, 'up');
+			JsAdmin.DD.addMoveButton(JsAdmin.util.getNextSiblingNode(item.item), this.buttonCell, 'up');
+		} else {
+			JsAdmin.DD.addMoveButton(item.item, this.buttonCell, 'up');
+			var first, prev = JsAdmin.util.getPreviousSiblingNode(item.item);
+			while(prev) {
+				first = prev;
+				prev = JsAdmin.util.getPreviousSiblingNode(prev);
+			}
+			JsAdmin.DD.deleteMoveButton(JsAdmin.movable_rows[first.id].tr, this.buttonCell, 'up');
+		}
+	}
+	if(wasLast != isLast) {
+		if(isLast) {
+			JsAdmin.DD.deleteMoveButton(item.tr, this.buttonCell, 'down');
+			JsAdmin.DD.addMoveButton(JsAdmin.util.getPreviousSiblingNode(item.item), this.buttonCell, 'down');
+		} else {
+			JsAdmin.DD.addMoveButton(item.item, this.buttonCell, 'down');
+			var last, next = JsAdmin.util.getNextSiblingNode(item.item);
+			while(next) {
+				last = next;
+				next = JsAdmin.util.getNextSiblingNode(next);
+			}
+			JsAdmin.DD.deleteMoveButton(JsAdmin.movable_rows[last.id].tr, this.buttonCell, 'down');
+		}
+	}
+
+	this.DDM.refreshCache(this.groups);
+};
+
+JsAdmin.DD.liDDSwap.prototype.endDrag = function(e) {
+	YAHOO.util.Dom.setStyle(this.getEl(), "opacity", this.opacity);
+	this.list.className = String(this.list.className).replace(/(\s*)jsadmin_([a-z]+)/g, "$1");
+	JsAdmin.DD.dragee = null;
+	var newIndex = JsAdmin.util.getItemIndex(this.getEl());
+	if(newIndex != this.rowIndex) {
+		var url = JsAdmin.WB_URL + "/modules/jsadmin/move_to.php";
+		url += JsAdmin.movable_rows[this.getEl().id].params + "&position=" + (newIndex+1);
+		document.body.className = String(document.body.className).replace(/(\s*)jsadmin_([a-z]+)/g, "$1") + " jsadmin_busy";
+		YAHOO.util.Connect.asyncRequest('GET', url, this, null);
+	}
+};
+
+JsAdmin.DD.liDDSwap.prototype.success = function(o) {
+	document.body.className = String(document.body.className).replace(/(\s*)jsadmin_([a-z]+)/g, "$1") + " jsadmin_success";
+};
+
+JsAdmin.DD.liDDSwap.prototype.failure = function(o) {
+	document.body.className = String(document.body.className).replace(/(\s*)jsadmin_([a-z]+)/, "$1") + " jsadmin_failure";
+};
Index: trunk/wb/modules/jsadmin/js/restore_pages.js
===================================================================
--- trunk/wb/modules/jsadmin/js/restore_pages.js	(nonexistent)
+++ trunk/wb/modules/jsadmin/js/restore_pages.js	(revision 601)
@@ -0,0 +1,78 @@
+// Copyright 2006 Stepan Riha
+// www.nonplus.net
+// $Id: restore_pages.js 2 2006-04-18 03:04:39Z stepan $
+
+// Array of ids that can be toggled using toggle_visibility()
+JsAdmin.toggled_ids = function() {
+	var links = document.getElementsByTagName('a');
+	var ids = [];
+	var reId = /toggle_visibility\s*\(\s*\'([^\']+)/;
+
+	for(var i = 0; i < links.length; i++) {
+		var href = links[i].href || '';
+		var match = href.match(reId);
+		if(!match) {
+			continue;
+		}
+		var id = match[1];
+		ids.push(id);
+	}
+
+	if(ids.length > 0) {
+		return ids;
+	} else {
+		return false;
+	}
+};
+
+// Expand sections stored in the wb_jsadmin_pages cookie
+JsAdmin.restore_toggled = function() {
+	var ids = this.toggled_ids();
+
+	if(!ids) {
+		return;
+	}
+
+	var saved = this.util.readCookie('wb_jsadmin_pages');
+	if(!saved) {
+		return;
+	}
+
+	var reNum = /(\d+)/;
+
+	saved = ',' + saved + ',';
+
+	for(var i = ids.length-1; i >= 0; i--) {
+		var id = ids[i];
+		if(saved.indexOf(',' + id + ',') >= 0) {
+			toggle_visibility(id);
+			var match = id.match(reNum);
+			if(match)
+				toggle_plus_minus(match[1]);
+		}
+	}
+};
+
+// Store expanded section ids in the wb_jsadmin_pages cookie
+JsAdmin.save_toggled = function() {
+
+	var ids = this.toggled_ids();
+
+	if(!ids) {
+		return;
+	}
+
+	var visible = [];
+	for(var i = 0; i < ids.length; i++) {
+		var id = ids[i];
+		var elt = YAHOO.util.Dom.get(id);
+		var display = elt.style.display;
+		if(display == 'block') {
+			visible.push(ids[i]);
+		}
+	}
+
+	var ids = visible.join(",");
+	this.util.createCookie('wb_jsadmin_pages', ids, 14);
+};
+
Index: trunk/wb/modules/jsadmin/index.php
===================================================================
--- trunk/wb/modules/jsadmin/index.php	(nonexistent)
+++ trunk/wb/modules/jsadmin/index.php	(revision 601)
@@ -0,0 +1,34 @@
+<?php
+
+// $Id$
+
+// JsAdmin module for Website Baker
+// Copyright (C) 2006, Stepan Riha
+// www.nonplus.net
+
+// modified by Swen Uth for Website Baker 2.7
+
+/*
+
+ Website Baker Project <http://www.websitebaker.org/>
+ Copyright (C) 2004-2008, Ryan Djurovich
+
+ Website Baker is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Website Baker is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Website Baker; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+header('Location: ../index.php');
+
+?>
\ No newline at end of file
Index: trunk/wb/modules/jsadmin/move_to.php
===================================================================
--- trunk/wb/modules/jsadmin/move_to.php	(nonexistent)
+++ trunk/wb/modules/jsadmin/move_to.php	(revision 601)
@@ -0,0 +1,93 @@
+<?php
+
+// $Id$
+
+// JsAdmin module for Website Baker
+// Copyright (C) 2006, Stepan Riha
+// www.nonplus.net
+
+// modified by Swen Uth for Website Baker 2.7
+
+/*
+
+ Website Baker Project <http://www.websitebaker.org/>
+ Copyright (C) 2004-2008, Ryan Djurovich
+
+ Website Baker is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Website Baker is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Website Baker; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+require('../../config.php');
+
+if(isset($_GET['page_id']) AND is_numeric($_GET['page_id']) AND is_numeric(@$_GET['position'])) {
+	$position = $_GET['position'];
+
+	// Get common fields
+	if(isset($_GET['section_id']) AND is_numeric($_GET['section_id'])) {
+		$page_id = $_GET['page_id'];
+		$id = $_GET['section_id'];
+		$id_field = 'section_id';
+		$common_field = 'page_id';
+		$table = TABLE_PREFIX.'sections';
+	} else {
+		$id = $_GET['page_id'];
+		$id_field = 'page_id';
+		$common_field = 'parent';
+		$table = TABLE_PREFIX.'pages';
+	}
+
+	// Get current index
+	$sql = <<<EOT
+SELECT $common_field, position FROM $table WHERE $id_field = $id
+EOT;
+	echo "$sql<br>";
+	$rs = $database->query($sql);
+	if($row = $rs->fetchRow()) {
+		$common_id = $row[$common_field];
+		$old_position = $row['position'];
+	}
+	echo "$old_position<br>";
+	if($old_position == $position)
+		return;
+	
+	// Build query to update affected rows
+	if($old_position < $position)
+		$sql = <<<EOT
+UPDATE $table SET position = position - 1
+	WHERE position > $old_position AND position <= $position
+		AND $common_field = $common_id
+EOT;
+	else
+		$sql = <<<EOT
+UPDATE $table SET position = position + 1
+	WHERE position >= $position AND position < $old_position
+		AND $common_field = $common_id
+EOT;
+	echo "<pre>$sql</pre>";
+	$database->query($sql);
+
+	// Build query to update specified row
+	$sql = <<<EOT
+UPDATE $table SET position = $position
+	WHERE $id_field = $id
+EOT;
+	echo "<pre>$sql</pre>";
+	$database->query($sql);
+} else {
+	die("Missing parameters");
+	header("Location: index.php");
+	exit(0);
+}
+?>
