[Checkins] SVN: Zelenium/branches/gotcha-selenium-0.8.3/selenium/ add Selenium 0.8.3

Godefroid Chapelle gotcha at bubblenet.be
Sat Dec 8 08:40:49 EST 2007


Log message for revision 82197:
  add Selenium 0.8.3

Changed:
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/MANIFEST.MF
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/org.openqa.selenium.core/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/org.openqa.selenium.core/selenium-core/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/org.openqa.selenium.core/selenium-core/pom.properties
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/org.openqa.selenium.core/selenium-core/pom.xml
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/VERSION.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/coding-conventions.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/Blank.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/InjectedRemoteRunner.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/RemoteRunner.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/SeleniumLog.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestPrompt.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner-splash.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner.hta
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/butmin.gif
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/butplus.gif
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/domviewer.css
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/domviewer.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/selenium-domviewer.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/all.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/continue.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/continue_disabled.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/pause.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/pause_disabled.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/selected.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/step.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/step_disabled.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/iedoc-core.xml
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/iedoc.xml
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/cssQuery-p.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-level2.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-level3.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-standard.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/prototype.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/builder.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/controls.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/dragdrop.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/effects.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/scriptaculous.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/slider.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/unittest.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/find_matching_child.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/htmlutils.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/injection.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/js2html.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-defs.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-exec.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-parse.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/se2html.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-api.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-browserbot.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-browserdetect.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-commandhandlers.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-executionloop.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-logging.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-remoterunner.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-testrunner.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-version.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/user-extensions.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/user-extensions.js.sample
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/xmlextras.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium-logo.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium-test.css
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium.css
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/dom.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/misc.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/xpath.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/doc.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/doc2html.xml
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/js.jar
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/index.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/install-readme.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/css/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/css/jsUnitStyle.css
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/css/readme
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/emptyPage.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitCore.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitMockTimeout.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTestManager.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTestSuite.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTracer.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitVersionCheck.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-errors.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-failures.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-runs.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-data.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-errors.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-frame.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-loader.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-progress.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-results.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-status.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/testContainer.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/testContainerController.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/xbDebug.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/changelog.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/css/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/css/jsUnitStyle.css
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/green.gif
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/logo_jsunit.gif
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/powerby-transparent.gif
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/red.gif
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/JDOM_license.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/Jetty_license.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/MPL-1.1.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/gpl-2.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/index.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/lgpl-2.1.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/mpl-tri-license-c.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/mpl-tri-license-html.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/readme.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/testRunner.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/version.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/readyState.xpi
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/reference.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/compiler.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/selenium-strands.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/simpletest.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/strands-ext.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/strands.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/test.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/DogFoodTestSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/ErrorCheckingTestSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/FailingTestSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/GoogleTestSearch.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/GoogleTestSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/PassingTestSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/ShortTestSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestAddLocationStrategy.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestAlerts.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestBasicAuth.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestBrowserVersion.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCheckUncheck.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClick.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClickBlankTarget.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClickJavascriptHref.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCommandError.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestComments.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestConfirmations.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCookie.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCssLocators.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCursorPosition.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestDojoDragAndDrop.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestDragAndDrop.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEditable.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementIndex.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementOrder.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementPresent.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestErrorChecking.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEval.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEvilClosingWindow.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFailingAssert.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFailingVerifications.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFocusOnBlur.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesClick.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesClickJavascriptHref.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesNested.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesOpen.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesSpecialTargets.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFunkEventHandling.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestGoBack.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestHighlight.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestHtmlSource.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestImplicitLocators.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-for-loops.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-functions.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-if-then-else.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJSSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJavaScriptAttributes.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJavascriptParameters.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestLocators.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestModalDialog.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestMultiSelect.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpen.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpenInTargetFrame.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpen_SSV_syntax.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPatternMatching.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPause.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPrompt.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestQuickOpen.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestRefresh.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelect.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectMultiLevelFrame.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectWindow.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectWindowTitle.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestStore.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSubmit.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSuite-UserExtensions.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestTextWhitespace.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestType.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestTypeRichText.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestUserExtensions.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestVerifications.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestVisibility.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWait.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitFor.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitForNot.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitInPopupWindow.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestXPathLocatorInXHtml.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestXPathLocators.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestBaseUrl.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestBreakPoint.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestFailures.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestPauseAndResume.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestRunFailedTests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestRunSuccessfulTests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/BaseUrl1TestSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/BaseUrl2TestSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/PauseTestSuite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestBaseUrl1.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestBaseUrl2.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestTimeout.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/html/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/html/banner.gif
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/html/test_timeout.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestButtonEvents.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestCheckboxEvents.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestFireEvents.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestKeyEvents.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestLinkEvents.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestMouseEvents.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestRadioEvents.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestSelectEvents.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestTextEvents.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/readme.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/filter-tests-for-browser.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/FastLoadingFrameWithSlowLoadingFrame.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/Frames.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/NestedFrames.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/NestedFrames2.slow.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/ajax_autocompleter2_test.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/builder.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/controls.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/dragdrop.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/effects.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/prototype.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/scriptaculous.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/slider.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/unittest.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/banner.gif
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/basicAuth/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/basicAuth/index.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/LICENSE
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/README
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/build.txt
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/dojo.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/dojo.js.uncompressed.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/iframe_history.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/AdapterRegistry.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/Deferred.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/DeferredList.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/a11y.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/animation.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/behavior.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/bootstrap1.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/bootstrap2.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/browser_debug.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/crypto.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/data.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/date.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/debug.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/DragAndDrop.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragAndDrop.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragCopy.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragManager.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragMove.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/Sortable.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/TreeDragAndDrop.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/TreeDragAndDropV3.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/__package__.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/docs.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dom.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/__package__.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/browser.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/common.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/topic.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/experimental.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/flash.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_adobesvg.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_browser.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_dashboard.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_jsc.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_rhino.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_spidermonkey.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_svg.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_wsh.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/__package__.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/color.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/common.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/display.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/iframe.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowB.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowBL.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowBR.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowL.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowR.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowT.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowTL.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowTR.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/layout.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/metrics.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/selection.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/shadow.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/style.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/util.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/iCalendar.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/io.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/json.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/__package__.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/array.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/assert.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/common.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/declare.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/extras.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/func.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/repr.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/Streamer.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/Timer.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/__package__.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/type.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/loader.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/loader_xd.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/math.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/ns.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/profile.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/regexp.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/storage.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/string.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/style.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/svg.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/validate.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_container.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragcopy.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragdropparent.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_draghandle.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_draghandle_2.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragmove.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragmoveparent.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_nested_drop_targets.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_simple.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame-left.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame-right.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame_to_be_loaded.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/path1/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/path1/cookie1.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/path2/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/path2/cookie2.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/show_message.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/addanevent.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/img/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/img/bg.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/img/left.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/img/right.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/knob.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/knob2.png
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider-setup.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider.css
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_async_event.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_bottom.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_check_uncheck.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_javascript_page.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_page1.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_page2.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_confirm.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_dummy_page.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_editable.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_element_order.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_element_present.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_fast_reloader.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_focus_on_blur.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_form_events.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_framed_page.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_funk_event_handling_shouldnt_go_here.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_funky_event_handling.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_html_source.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_i18n.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_incrementing_counter.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_javascript_attributes.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_just_text.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_locators.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_locators.xhtml
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_modal_dialog.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_modal_dialog_dialog.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_multi_level_frame.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_multiselect.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_open.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_open_in_target_frame.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_page.slow.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_prompt.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_reload_onchange_page.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_reloader.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_rewrite_doc_from_js.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_rich_text.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select_window.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select_window_popup.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_slow_reloader.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_store_value.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_submit.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_text_content.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_top.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_type_page1.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_type_page2.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_verifications.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_verify_alert.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_visibility.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/proxy_injection_meta_equiv_test.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/alert-handling-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/assert-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/browserbot-frame-finder-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/browserbot-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/command-factory-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/command-handler-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/confirm-handling-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/error-checking-command-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/event-bubble-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/htmlutil-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/optionlocator-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-accessor-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-action-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-attribute-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-locator-tests-include.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-locator-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-property-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pattern-matcher-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/remoterunner-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/selenium-api-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/selenium-parameter-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/string-override-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/stringOverride.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/suite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/testloop-handle-error-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/text-content-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/dummy-logging.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestcase-row-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestcase-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestframe-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestsuite-row-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestsuite-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/suite.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/jsmock/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/jsmock/mock-tests.html
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/jsmock/mock.js
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/tableparser/
  A   Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/tableparser/tableparser-tests.html

-=-
Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/MANIFEST.MF
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/MANIFEST.MF	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/MANIFEST.MF	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Archiver-Version: Plexus Archiver
+Created-By: Apache Maven
+Built-By: openqa
+Build-Jdk: 1.6.0
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/org.openqa.selenium.core/selenium-core/pom.properties
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/org.openqa.selenium.core/selenium-core/pom.properties	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/org.openqa.selenium.core/selenium-core/pom.properties	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,5 @@
+#Generated by Maven
+#Thu Sep 20 01:04:44 PDT 2007
+version=0.8.3
+groupId=org.openqa.selenium.core
+artifactId=selenium-core

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/org.openqa.selenium.core/selenium-core/pom.xml
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/org.openqa.selenium.core/selenium-core/pom.xml	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/META-INF/maven/org.openqa.selenium.core/selenium-core/pom.xml	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,356 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.openqa.selenium.core</groupId>
+    <artifactId>selenium-core</artifactId>
+    <version>0.8.3</version>
+    <name>Selenium Core</name>
+    <packaging>jar</packaging>
+    <url>http://www.openqa.org/selenium-core</url>
+    <repositories>
+        <repository>
+            <id>openqa</id>
+            <name>OpenQA Maven Repository</name>
+            <url>http://maven.openqa.org</url>
+        </repository>
+        <repository>
+            <id>apache.snapshots</id>
+            <name>Maven Snapshot Repository</name>
+            <url>http://people.apache.org/maven-snapshot-repository</url>
+            <snapshots>
+                <enabled>true</enabled>
+                <updatePolicy>daily</updatePolicy>
+            </snapshots>
+            <releases>
+                <enabled>false</enabled>
+                <updatePolicy>never</updatePolicy>
+            </releases>
+        </repository>
+    </repositories>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <version>1.2-SNAPSHOT</version>
+                <executions>
+                    <execution>
+                        <id>compile</id>
+                        <phase>compile</phase>
+                        <configuration>
+                            <tasks>
+                                <exec executable="svnversion" outputProperty="svn-revision" failOnError="true">
+                                    <arg value="."/>
+                                </exec>
+
+                                <copy todir="target/classes" overwrite="true">
+                                    <fileset dir="src/main/resources">
+                                        <include name="core/scripts/selenium-version.js"/>
+                                        <include name="VERSION.txt"/>
+                                    </fileset>
+                                    <filterset>
+                                        <filter token="VERSION" value="${project.version}"/>
+                                        <filter token="REVISION" value="${svn-revision}"/>
+                                    </filterset>
+                                </copy>
+                                
+                                <copy file="target/classes/core/TestRunner.html" tofile="target/classes/core/TestRunner.hta" />
+
+                                <mkdir dir="target/classes/core"/>
+                                <java jar="${maven.dependency.rhino.js.jar.path}" output="target/classes/core/iedoc.xml" failonerror="true" fork="true">
+                                    <arg file="src/main/resources/doctool/doc.js"/>
+                                    <arg file="src/main/resources/core/scripts/selenium-api.js"/>
+                                    <arg file="src/main/resources/core/scripts/selenium-remoterunner.js"/>
+                                </java>
+                                <!--<xmlvalidate file="target/classes/core/iedoc.xml" lenient="true"/>-->
+                                <java jar="${maven.dependency.rhino.js.jar.path}" output="target/classes/core/iedoc-core.xml" failonerror="true" fork="true">
+                                    <arg file="src/main/resources/doctool/doc.js"/>
+                                    <arg file="src/main/resources/core/scripts/selenium-api.js"/>
+                                    <arg file="src/main/resources/core/scripts/selenium-testrunner.js"/>
+                                </java>
+                                <!--<xmlvalidate file="target/classes/core/iedoc-core.xml" lenient="true"/>-->
+
+                                <xslt in="target/classes/core/iedoc-core.xml" out="target/classes/reference.html" style="src/main/resources/doctool/doc2html.xml"/>
+                            </tasks>
+                        </configuration>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>ant</groupId>
+                        <artifactId>ant-trax</artifactId>
+                        <version>1.6.5</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>ant</groupId>
+                        <artifactId>ant-nodeps</artifactId>
+                        <version>1.6.5</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>ant</groupId>
+                        <artifactId>ant-junit</artifactId>
+                        <version>1.6.5</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>junit</groupId>
+                        <artifactId>junit</artifactId>
+                        <version>3.8.1</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <artifactId>maven-clean-plugin</artifactId>
+                <configuration>
+                    <filesets>
+                        <fileset>
+                            <directory>.</directory>
+                            <includes>
+                                <include>results-*.html</include>
+                            </includes>
+                        </fileset>
+                    </filesets>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <profiles>
+        <profile>
+            <id>test</id>
+            <activation>
+                <property>
+                    <name>!notest</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-antrun-plugin</artifactId>
+                        <version>1.2-SNAPSHOT</version>
+                        <executions>
+                            <execution>
+                                <id>test</id>
+                                <phase>test</phase>
+                                <configuration>
+                                    <tasks>
+                                        <condition property="browserFileNames" value="${firefox}">
+                                            <available file="${firefox}"/>
+                                        </condition>
+                                        <condition property="browserFileNames" value="c:\program files\Mozilla Firefox\firefox.exe">
+                                            <os family="windows"/>
+                                        </condition>
+                                        <condition property="browserFileNames" value="/Applications/Firefox.app/Contents/MacOS/firefox-bin">
+                                            <os family="mac"/>
+                                        </condition>
+                                        <condition property="browserFileNames" value="/usr/bin/firefox">
+                                            <os family="unix"/>
+                                        </condition>
+                                        <property name="testRunnerLocation" location="src/main/resources/jsunit/testRunner.html"/>
+                                        <property name="testSuiteLocation" location="src/main/resources/unittest/browserbot/suite.html"/>
+                                        <property name="port" value="8281"/>
+                                        <property name="browserBotTestUrl"
+                                                  value="file://${testRunnerLocation}?testPage=${testSuiteLocation}&amp;autoRun=true&amp;submitresults=localhost:${port}/jsunit/acceptor"/>
+                                        <property name="resourceBase" value=""/>
+                                        <property name="logsDirectory" value="target/logs"/>
+
+                                        <mkdir dir="target/logs"/>
+                                        <junit showoutput="true" errorproperty="tests.failed" failureproperty="tests.failed">
+                                            <classpath>
+                                                <fileset dir="code/java/lib">
+                                                    <include name="*.jar"/>
+                                                </fileset>
+                                            </classpath>
+                                            <sysproperty key="browserFileNames" value="${browserFileNames}"/>
+                                            <sysproperty key="url" value="${browserBotTestUrl}"/>
+                                            <sysproperty key="port" value="${port}"/>
+                                            <sysproperty key="resourceBase" value="${resourceBase}"/>
+                                            <sysproperty key="logsDirectory" value="target/logs"/>
+                                            <test name="net.jsunit.StandaloneTest"/>
+                                        </junit>
+                                        <junitreport todir="target/logs">
+                                            <fileset dir="target/logs"/>
+                                            <report format="frames" todir="target/logs" />
+                                        </junitreport>
+                                        <fail if="tests.failed" message="JSUnit tests failed, see output for details: target/logs" />
+                                    </tasks>
+                                </configuration>
+                                <goals>
+                                    <goal>run</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <dependencies>
+                            <dependency>
+                                <groupId>ant</groupId>
+                                <artifactId>ant-trax</artifactId>
+                                <version>1.6.5</version>
+                            </dependency>
+                            <dependency>
+                                <groupId>ant</groupId>
+                                <artifactId>ant-junit</artifactId>
+                                <version>1.6.5</version>
+                            </dependency>
+                            <dependency>
+                                <groupId>junit</groupId>
+                                <artifactId>junit</artifactId>
+                                <version>3.8.1</version>
+                            </dependency>
+                        </dependencies>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        
+        <profile>
+            <id>integration-test-firefox</id>
+            <activation>
+                <property>
+                    <name>!notest</name>
+                </property>
+            </activation>
+            <properties>
+                <slowResources>false</slowResources>
+                <multiWindow>true</multiWindow>
+                <browser>*firefox</browser>
+                <seleneseSuite>TestSuite</seleneseSuite>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-antrun-plugin</artifactId>
+                        <version>1.2-SNAPSHOT</version>
+                        <executions>
+                            <execution>
+                                <id>integration-test</id>
+                                <phase>integration-test</phase>
+                                <configuration>
+                                    <tasks>
+                                        <taskdef resource="selenium-ant.properties">
+                                            <classpath refid="maven.dependency.classpath" />
+                                        </taskdef>
+                                        <selenese 
+                                            browser="${browser}"
+                                            suite="src/main/resources/tests/${seleneseSuite}.html"
+                                            port="4444"
+                                            slowResources="${slowResources}"
+                                            multiWindow="${multiWindow}"
+                                            startURL="http://localhost:4444" />
+                                    </tasks>
+                                </configuration>
+                                <goals>
+                                    <goal>run</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <dependencies>
+                            <dependency>
+                                <groupId>ant</groupId>
+                                <artifactId>ant-trax</artifactId>
+                                <version>1.6.5</version>
+                            </dependency>
+                            <dependency>
+                                <groupId>ant</groupId>
+                                <artifactId>ant-junit</artifactId>
+                                <version>1.6.5</version>
+                            </dependency>
+                            <dependency>
+                                <groupId>junit</groupId>
+                                <artifactId>junit</artifactId>
+                                <version>3.8.1</version>
+                            </dependency>
+                        </dependencies>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        
+        
+        
+    </profiles>
+    <pluginRepositories>
+        <pluginRepository>
+            <id>snapshots</id>
+            <name>Maven Plugin Snapshot Repository</name>
+            <url>http://people.apache.org/maven-snapshot-repository</url>
+            <layout>default</layout>
+            <snapshots>
+                <enabled>true</enabled>
+                <updatePolicy>daily</updatePolicy>
+            </snapshots>
+            <releases>
+                <enabled>false</enabled>
+                <updatePolicy>never</updatePolicy>
+            </releases>
+        </pluginRepository>
+    </pluginRepositories>
+    <distributionManagement>
+        <repository>
+            <id>openqa</id>
+            <name>OpenQA Maven Repository</name>
+            <url>scp://maven.openqa.org/home/maven/maven.openqa.org/htdocs</url>
+        </repository>
+    </distributionManagement>
+    <dependencies>
+        <!--
+        <dependency>
+            <groupId>jsunit</groupId>
+            <artifactId>jsunit</artifactId>
+            <version>2.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>jetty</groupId>
+            <artifactId>org.mortbay.jetty</artifactId>
+            <version>5.1.10</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.0.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>jdom</groupId>
+            <artifactId>jdom</artifactId>
+            <version>1.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>xerces</groupId>
+            <artifactId>xercesImpl</artifactId>
+            <version>2.8.0</version>
+            <scope>test</scope>
+        </dependency>
+        -->
+        <dependency>
+            <groupId>org.openqa.selenium.server</groupId>
+            <artifactId>selenium-server-coreless</artifactId>
+            <version>0.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>rhino</groupId>
+            <artifactId>js</artifactId>
+            <version>1.6R5</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+        </dependency>
+    </dependencies>
+</project>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/VERSION.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/VERSION.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/VERSION.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,2 @@
+selenium.core.version=0.8.3
+selenium.core.revision=1879
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/coding-conventions.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/coding-conventions.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/coding-conventions.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,54 @@
+            Coding standards for Selenium Core Javascript code
+            --------------------------------------------------
+
+  Here is a set of conventions agreed by the active Selenium Core
+  developers at ThoughtWorks.  Please stick to these guidelines when
+  working on the Selenium Core code-base.
+
+Whitespace: we use spaces, NOT TABS.  Indent in 4-space increments.
+
+Braces: we place open-braces on the same line as the associated keyword,
+  for example:
+
+        if (command.isBreakpoint) {
+            this.pause();
+        } else {
+            window.setTimeout(this.resume.bind(this), delay);
+        }
+
+Encapsulation: we prefer to encapsulate functions and variables inside
+  objects, where possible.
+
+Variable declarations: declare variables (using "var") ... even if they're
+  "global".
+
+Class definitions: we're shifting to "prototype.js" style for
+  definition of classes, e.g.
+
+        var MyClass = Class.create();
+        Object.extend(MyClass.prototype, {
+        
+            initialize: function() {
+                // ... constructor code ...
+            },
+        
+            doStuff: function() {
+                // ... method body ...
+            }
+        
+        });
+
+'Private' functions/properties: we simulate "private" properties by
+  prepended the name with an underscore ("_"), e.g.
+
+        _resumeAfterDelay : function() {
+            // ...etc...
+        },
+
+Element addressing: use "$(id)" rather than
+  "document.getElementById('id')".
+
+Timeout functions: pass function objects to setTimeout(), rather than
+  strings, e.g.
+
+        window.setTimeout(this.resume.bind(this), delay);

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/Blank.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/Blank.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/Blank.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,7 @@
+<html>
+    <head>
+        <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
+    </head>
+<body>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/InjectedRemoteRunner.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/InjectedRemoteRunner.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/InjectedRemoteRunner.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,8 @@
+<html>
+    <head>
+        <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
+    </head>
+<body>
+    <h3>selenium-rc initial page</h3>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/RemoteRunner.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/RemoteRunner.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/RemoteRunner.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,110 @@
+<html>
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<HTA:APPLICATION ID="SeleniumHTARunner" APPLICATIONNAME="Selenium" >
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Selenium Remote Control</title>
+<link rel="stylesheet" type="text/css" href="selenium.css" />
+<script type="text/javascript" src="scripts/xmlextras.js"></script>
+<script language="JavaScript" type="text/javascript" src="lib/prototype.js"></script>
+<script language="JavaScript" type="text/javascript" src="lib/cssQuery/cssQuery-p.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/selenium-browserbot.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/find_matching_child.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/selenium-api.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/selenium-commandhandlers.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/selenium-executionloop.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/selenium-remoterunner.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/selenium-logging.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/selenium-version.js"></script>
+<script language="JavaScript" type="text/javascript" src="xpath/misc.js"></script>
+<script language="JavaScript" type="text/javascript" src="xpath/dom.js"></script>
+<script language="JavaScript" type="text/javascript" src="xpath/xpath.js"></script>
+<script language="JavaScript" type="text/javascript" src="scripts/user-extensions.js"></script>
+<script language="JavaScript" type="text/javascript">
+    function openDomViewer() {
+        var autFrame = document.getElementById('selenium_myiframe');
+        var autFrameDocument = getIframeDocument(autFrame);
+        var domViewer = window.open(getDocumentBase(document) + 'domviewer/domviewer.html');
+        domViewer.rootDocument = autFrameDocument;
+        return false;
+    }
+
+    function cleanUp() {
+    	if (LOG != null) {
+		LOG.close();
+	}
+    }
+
+</script>
+</head>
+
+<body onLoad="setTimeout(function(){runSeleniumTest();},1000)" onUnload="cleanUp()">
+
+<table border="1" style="height: 100%;">
+  <tr>
+    <td width="50%" height="30%">
+      <table>
+      <tr>
+        <td>
+          <img src="selenium-logo.png">
+        </td>
+        <td>
+          <h1><a href="http://selenium.thoughtworks.com" >Selenium</a> Functional Testing for Web Apps</h1>
+          Open Source From <a href="http://www.thoughtworks.com">ThoughtWorks, Inc</a> and Friends
+          <form action="">
+          <br/>Slow Mode:<INPUT TYPE="CHECKBOX" NAME="FASTMODE" VALUE="YES" onmouseup="slowClicked()">
+
+          <iframe id="seleniumLoggingFrame" name="seleniumLoggingFrame" src="Blank.html" style="border: 0; height: 0; width: 0; "></iframe>
+          <fieldset>
+            <legend>Tools</legend>
+
+            <button type="button" id="domViewer1" onclick="openDomViewer();">
+              View DOM
+            </button>
+            <button type="button" onclick="LOG.show();">
+              Show Log
+            </button>
+          </fieldset>
+
+          </form>
+
+        </td>
+      </tr>
+      </table>
+      <form action="">
+        <label id="context" name="context"></label>
+      </form>
+    </td>
+    <td width="50%" height="30%">
+      <b>Last Four Test Commands:</b><br/>
+      <div id="commandList"></div>
+    </td>
+  </tr>
+    <tr>
+    <td colspan="2" height="70%">
+      <iframe name="selenium_myiframe" id="selenium_myiframe" src="Blank.html" height="100%" width="100%"></iframe>
+    </td>
+  </tr>
+</table>
+
+</body>
+</html>
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/SeleniumLog.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/SeleniumLog.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/SeleniumLog.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,109 @@
+<html>
+
+<head>
+<title>Selenium Log Console</title>
+<link id="cssLink" rel="stylesheet" href="selenium.css" />
+<script src="scripts/htmlutils.js"></script>
+<script language="JavaScript">
+
+var disabled = true;
+
+function logOnLoad() {
+    var urlConfig = new URLConfiguration();
+    urlConfig.queryString = window.location.search.substr(1);
+    var startingThreshold = urlConfig._getQueryParameter("startingThreshold");
+    setThresholdLevel(startingThreshold);
+    var buttons = document.getElementsByTagName("input");
+    for (var i = 0; i < buttons.length; i++) {
+        addChangeListener(buttons[i]);
+    }
+}
+
+function enableButtons() {
+    var buttons = document.getElementsByTagName("input");
+    for (var i = 0; i < buttons.length; i++) {
+        buttons[i].disabled = false;
+        disabled = false;
+    }
+}
+
+function callBack() {}
+
+function changeHandler() {
+    callBack(getThresholdLevel());
+}
+
+function addChangeListener(element) {
+    if (window.addEventListener && !window.opera)
+        element.addEventListener("click", changeHandler, true);
+    else if (window.attachEvent)
+        element.attachEvent("onclick", changeHandler);
+}
+
+var logLevels = {
+    debug: 0,
+    info: 1,
+    warn: 2,
+    error: 3
+};
+
+function getThresholdLevel() {
+    var buttons = document.getElementById('logLevelChooser').level;
+    for (var i = 0; i < buttons.length; i++) {
+        if (buttons[i].checked) {
+            return buttons[i].value;
+        }
+    }
+}
+
+function setThresholdLevel(logLevel) {
+    var buttons = document.getElementById('logLevelChooser').level;
+    for (var i = 0; i < buttons.length; i++) {
+        if (buttons[i].value==logLevel) {
+            buttons[i].checked = true;
+        }
+        else {
+            buttons[i].checked = false;
+        }
+    }
+}
+
+function append(message, logLevel) {
+    var logLevelThreshold = getThresholdLevel();
+    if (logLevels[logLevel] < logLevels[logLevelThreshold]) {
+        return;
+    }
+    var log = document.getElementById('log');
+    var newEntry = document.createElement('li');
+    newEntry.className = logLevel;
+    newEntry.appendChild(document.createTextNode(message));
+    log.appendChild(newEntry);
+    if (newEntry.scrollIntoView) {
+        newEntry.scrollIntoView();
+    }
+}
+
+</script>
+</head>
+<body id="logging-console" onload="logOnLoad();">
+
+
+
+<div id="banner">
+  <form id="logLevelChooser">
+      <input id="level-error" type="radio" name="level" disabled='true'
+             value="error" /><label for="level-error">Error</label>
+      <input id="level-warn" type="radio" name="level" disabled='true'
+             value="warn" /><label for="level-warn">Warn</label>
+      <input id="level-info" type="radio" name="level" disabled='true'
+             value="info" /><label for="level-info">Info</label>
+      <input id="level-debug" type="radio" name="level" checked="yes" disabled='true'
+             value="debug" /><label for="level-debug">Debug</label>
+  </form>
+  <h1>Selenium Log Console</h1>
+</div>
+
+<ul id="log"></ul>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestPrompt.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestPrompt.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestPrompt.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,145 @@
+<html>
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<head>
+    <meta content="text/html; charset=ISO-8859-1"
+          http-equiv="content-type">
+    <title>Select a Test Suite</title>
+    <script language="JavaScript" type="text/javascript" src="scripts/selenium-browserdetect.js"></script>
+    <script language="JavaScript" type="text/javascript" src="scripts/xmlextras.js"></script>
+    <script>
+
+        function load() {
+            if (browserVersion.isHTA) {
+                document.getElementById("save-div").style.display = "inline";
+            }
+            if (/thisIsSeleniumServer/.test(window.location.search)) {
+                document.getElementById("slowResources-div").style.display = "inline";
+                if (browserVersion.isHTA || browserVersion.isChrome) {
+                    document.getElementById("test").value = "http://localhost:4444/selenium-server/tests/TestSuite.html";
+                }
+            }
+        }
+
+        function autoCheck() {
+            var auto = document.getElementById("auto");
+            var autoDiv = document.getElementById("auto-div");
+            if (auto.checked) {
+                autoDiv.style.display = "inline";
+            } else {
+                autoDiv.style.display = "none";
+            }
+        }
+
+        function slowCheck() {
+            var slowResourcesCheckbox = document.getElementById("slowResources");
+            var slowResources = slowResourcesCheckbox.checked ? true : false;
+            var xhr = XmlHttp.create();
+            var driverUrl = "http://localhost:4444/selenium-server/driver/?cmd=slowResources&1=" + slowResources;
+            xhr.open("GET", driverUrl, true);
+            xhr.send(null);
+        }
+
+        function saveCheck() {
+            var results = document.getElementById("results");
+            var check = document.getElementById("save").checked;
+            if (check) {
+                results.firstChild.nodeValue = "Results file ";
+                document.getElementById("resultsUrl").value = "results.html";
+            } else {
+                results.firstChild.nodeValue = "Results URL ";
+                document.getElementById("resultsUrl").value = "../postResults";
+            }
+        }
+
+        function go() {
+            if (!browserVersion.isHTA) return true;
+            var inputs = document.getElementsByTagName("input");
+            var queryString = "";
+            for (var i = 0; i < inputs.length; i++) {
+                var elem = inputs[i];
+                var name = elem.name;
+                var value = elem.value;
+                if (elem.type == "checkbox") {
+                    value = elem.checked;
+                }
+                queryString += escape(name) + "=" + escape(value);
+                if (i < (inputs.length - 1)) {
+                    queryString += "&";
+                }
+            }
+
+            window.parent.selenium = null;
+            window.parent.htmlTestRunner.controlPanel.queryString = queryString;
+            window.parent.htmlTestRunner.loadSuiteFrame();
+            return false;
+        }
+    </script>
+</head>
+
+<body onload="load()" style="font-size: x-small">
+<form id="prompt" target="_top" method="GET" onsubmit="return go();" action="TestRunner.html">
+
+    <p>
+        Test Suite:
+        <input id="test" name="test" size="30" value="../tests/TestSuite.html"/>
+    </p>
+
+    <p align="center"><input type="submit" value="Go"/></p>
+
+    <fieldset>
+        <legend>Options</legend>
+
+        <p>
+            <input id="multiWindow" type="checkbox" name="multiWindow" onclick="autoCheck();"/> <label
+                for="multiWindow">AUT in separate window</label>
+
+        <p>
+
+        <div id="slowResources-div" style="display: none">
+            <p>
+                <input id="slowResources" type="checkbox" name="slowResources" onclick="slowCheck();" /> <label for="slowResources">Slow down web server</label>
+            </p>
+        </div>
+
+        <p>
+            <input id="auto" type="checkbox" name="auto" onclick="autoCheck();"/> <label for="auto">Run
+            automatically</label>
+        </p>
+
+        <div id="auto-div" style="display: none">
+            <p>
+                <input id="close" type="checkbox" name="close"/> <label for="close">Close afterwards </label>
+            </p>
+
+            <div id="save-div" style="display: none">
+                <br/><label for="save">Save to file </label><input id="save" type="checkbox" name="save"
+                                                                   onclick="saveCheck();"/>
+            </div>
+
+            <p id="results">
+                Results URL:
+                <input id="resultsUrl" name="resultsUrl" value="../postResults"/>
+            </p>
+
+        </div>
+    </fieldset>
+
+
+</form>
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner-splash.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner-splash.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner-splash.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,55 @@
+<!--
+Copyright 2005 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<html>
+<link rel="stylesheet" type="text/css" href="selenium.css" />
+<body>
+<table width="100%">
+
+<tr>
+  <th>&uarr;</th>
+  <th>&uarr;</th>
+  <th>&uarr;</th>
+</tr>
+<tr>
+  <th width="25%">Test Suite</th>
+  <th width="50%">Current Test</th>
+  <th width="25%">Control Panel</th>
+</tr>
+<tr><td>&nbsp;</td></tr>
+
+<tr>
+<td></td>
+<td class="selenium splash">
+
+<img src="selenium-logo.png" align="right">
+
+<h1>Selenium</h1>
+<h2>by <a href="http://www.thoughtworks.com">ThoughtWorks</a> and friends</h2>
+
+<p>
+For more information on Selenium, visit
+
+<pre>
+    <a href="http://selenium.openqa.org" target="_blank">http://selenium.openqa.org</a>
+</pre>
+
+</td>
+<tr>
+
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner.hta
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner.hta	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner.hta	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,176 @@
+<html>
+
+<head>
+    <HTA:APPLICATION ID="SeleniumHTARunner" APPLICATIONNAME="Selenium">
+        <!-- the previous line is only relevant if you rename this
+     file to "TestRunner.hta" -->
+
+        <!-- The copyright notice and other comments have been moved to after the HTA declaration,
+to work-around a bug in IE on Win2K whereby the HTA application doesn't function correctly -->
+        <!--
+        Copyright 2004 ThoughtWorks, Inc
+
+         Licensed under the Apache License, Version 2.0 (the "License");
+         you may not use this file except in compliance with the License.
+         You may obtain a copy of the License at
+
+             http://www.apache.org/licenses/LICENSE-2.0
+
+         Unless required by applicable law or agreed to in writing, software
+         distributed under the License is distributed on an "AS IS" BASIS,
+         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+         See the License for the specific language governing permissions and
+         limitations under the License.
+        -->
+        <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"/>
+
+        <title>Selenium Functional Test Runner</title>
+        <link rel="stylesheet" type="text/css" href="selenium.css"/>
+        <script type="text/javascript" src="scripts/narcissus-defs.js"></script>
+        <script type="text/javascript" src="scripts/narcissus-parse.js"></script>
+        <script type="text/javascript" src="scripts/narcissus-exec.js"></script>
+        <script type="text/javascript" src="scripts/xmlextras.js"></script>
+        <script language="JavaScript" type="text/javascript" src="lib/prototype.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/htmlutils.js"></script>
+        <script language="JavaScript" type="text/javascript" src="lib/scriptaculous/scriptaculous.js"></script>
+        <script language="JavaScript" type="text/javascript" src="lib/cssQuery/cssQuery-p.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-browserdetect.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-browserbot.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/find_matching_child.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-api.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-commandhandlers.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-executionloop.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-testrunner.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-logging.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-version.js"></script>
+        <script language="JavaScript" type="text/javascript" src="xpath/misc.js"></script>
+        <script language="JavaScript" type="text/javascript" src="xpath/dom.js"></script>
+        <script language="JavaScript" type="text/javascript" src="xpath/xpath.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/user-extensions.js"></script>
+        <script language="JavaScript" type="text/javascript">
+            function openDomViewer() {
+                var autFrame = document.getElementById('selenium_myiframe');
+                var autFrameDocument = new SeleniumFrame(autFrame).getDocument();
+                this.rootDocument = autFrameDocument;
+                var domViewer = window.open(getDocumentBase(document) + 'domviewer/domviewer.html');
+                return false;
+            }
+        </script>
+</head>
+
+<body onLoad="onSeleniumLoad();">
+<table class="layout">
+<form action="" name="controlPanel">
+
+<!-- Suite, Test, Control Panel -->
+
+<tr class="selenium">
+<td width="25%" height="30%">
+    <iframe name="testSuiteFrame" id="testSuiteFrame" src="./TestPrompt.html" application="yes"></iframe>
+</td>
+<td width="50%" height="30%">
+    <iframe name="testFrame" id="testFrame" application="yes"></iframe>
+</td>
+
+<td width="25%">
+    <table class="layout">
+        <tr class="selenium">
+            <th width="25%" height="1" class="header">
+                <h1><a href="http://selenium.thoughtworks.com" title="The Selenium Project">Selenium</a> TestRunner
+                </h1>
+            </th>
+        </tr>
+        <tr>
+            <td width="25%" height="30%" id="controlPanel">
+                <fieldset>
+                    <legend>Execute Tests</legend>
+
+                    <div id="imageButtonPanel">
+                        <button type="button" id="runSuite" onClick="htmlTestRunner.startTestSuite();"
+                                title="Run All tests" accesskey="a">
+                        </button>
+                        <button type="button" id="runSeleniumTest" onClick="htmlTestRunner.runSingleTest();"
+                                title="Run the Selected test" accesskey="r">
+                        </button>
+                        <button type="button" id="pauseTest" disabled="disabled"
+                                title="Pause/Continue" accesskey="p" class="cssPauseTest">
+                        </button>
+                        <button type="button" id="stepTest" disabled="disabled"
+                                title="Step" accesskey="s">
+                        </button>
+                    </div>
+
+                    <div style="float:left">Fast</div>
+                    <div style="float:right">Slow</div>
+                    <br/>
+                    <div id="speedSlider">
+                        <div id="speedTrack">&nbsp;</div>
+                        <div id="speedHandle">&nbsp;</div>
+                    </div>
+
+                    <div class="executionOptions">
+                        <input id="highlightOption" type="checkbox" name="highlightOption" value="0"/>
+                        <label for="highlightOption">Highlight elements</label>
+                    </div>
+
+                </fieldset>
+
+                <table id="stats" align="center">
+                    <tr>
+                        <td colspan="2" align="right">Elapsed:</td>
+                        <td id="elapsedTime" colspan="2">00.00</td>
+                    </tr>
+                    <tr>
+                        <th colspan="2">Tests</th>
+                        <th colspan="2">Commands</th>
+                    </tr>
+                    <tr>
+                        <td class="count" id="testRuns">0</td>
+                        <td>run</td>
+                        <td class="count" id="commandPasses">0</td>
+                        <td>passed</td>
+                    </tr>
+                    <tr>
+                        <td class="count" id="testFailures">0</td>
+                        <td>failed</td>
+                        <td class="count" id="commandFailures">0</td>
+                        <td>failed</td>
+                    </tr>
+                    <tr>
+                        <td colspan="2"></td>
+                        <td class="count" id="commandErrors">0</td>
+                        <td>incomplete</td>
+                    </tr>
+                </table>
+
+                <fieldset>
+                    <legend>Tools</legend>
+
+                    <button type="button" id="domViewer1" onClick="openDomViewer();">
+                        View DOM
+                    </button>
+                    <button type="button" onClick="LOG.show();">
+                        Show Log
+                    </button>
+
+                </fieldset>
+
+            </td>
+        </tr>
+    </table>
+</td>
+</tr>
+
+<!-- AUT -->
+
+<tr>
+    <td colspan="3" height="70%">
+        <iframe name="selenium_myiframe" id="selenium_myiframe" src="TestRunner-splash.html"></iframe>
+    </td>
+</tr>
+
+    </form>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/TestRunner.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,176 @@
+<html>
+
+<head>
+    <HTA:APPLICATION ID="SeleniumHTARunner" APPLICATIONNAME="Selenium">
+        <!-- the previous line is only relevant if you rename this
+     file to "TestRunner.hta" -->
+
+        <!-- The copyright notice and other comments have been moved to after the HTA declaration,
+to work-around a bug in IE on Win2K whereby the HTA application doesn't function correctly -->
+        <!--
+        Copyright 2004 ThoughtWorks, Inc
+
+         Licensed under the Apache License, Version 2.0 (the "License");
+         you may not use this file except in compliance with the License.
+         You may obtain a copy of the License at
+
+             http://www.apache.org/licenses/LICENSE-2.0
+
+         Unless required by applicable law or agreed to in writing, software
+         distributed under the License is distributed on an "AS IS" BASIS,
+         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+         See the License for the specific language governing permissions and
+         limitations under the License.
+        -->
+        <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"/>
+
+        <title>Selenium Functional Test Runner</title>
+        <link rel="stylesheet" type="text/css" href="selenium.css"/>
+        <script type="text/javascript" src="scripts/narcissus-defs.js"></script>
+        <script type="text/javascript" src="scripts/narcissus-parse.js"></script>
+        <script type="text/javascript" src="scripts/narcissus-exec.js"></script>
+        <script type="text/javascript" src="scripts/xmlextras.js"></script>
+        <script language="JavaScript" type="text/javascript" src="lib/prototype.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/htmlutils.js"></script>
+        <script language="JavaScript" type="text/javascript" src="lib/scriptaculous/scriptaculous.js"></script>
+        <script language="JavaScript" type="text/javascript" src="lib/cssQuery/cssQuery-p.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-browserdetect.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-browserbot.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/find_matching_child.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-api.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-commandhandlers.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-executionloop.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-testrunner.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-logging.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/selenium-version.js"></script>
+        <script language="JavaScript" type="text/javascript" src="xpath/misc.js"></script>
+        <script language="JavaScript" type="text/javascript" src="xpath/dom.js"></script>
+        <script language="JavaScript" type="text/javascript" src="xpath/xpath.js"></script>
+        <script language="JavaScript" type="text/javascript" src="scripts/user-extensions.js"></script>
+        <script language="JavaScript" type="text/javascript">
+            function openDomViewer() {
+                var autFrame = document.getElementById('selenium_myiframe');
+                var autFrameDocument = new SeleniumFrame(autFrame).getDocument();
+                this.rootDocument = autFrameDocument;
+                var domViewer = window.open(getDocumentBase(document) + 'domviewer/domviewer.html');
+                return false;
+            }
+        </script>
+</head>
+
+<body onLoad="onSeleniumLoad();">
+<table class="layout">
+<form action="" name="controlPanel">
+
+<!-- Suite, Test, Control Panel -->
+
+<tr class="selenium">
+<td width="25%" height="30%">
+    <iframe name="testSuiteFrame" id="testSuiteFrame" src="./TestPrompt.html" application="yes"></iframe>
+</td>
+<td width="50%" height="30%">
+    <iframe name="testFrame" id="testFrame" application="yes"></iframe>
+</td>
+
+<td width="25%">
+    <table class="layout">
+        <tr class="selenium">
+            <th width="25%" height="1" class="header">
+                <h1><a href="http://selenium.thoughtworks.com" title="The Selenium Project">Selenium</a> TestRunner
+                </h1>
+            </th>
+        </tr>
+        <tr>
+            <td width="25%" height="30%" id="controlPanel">
+                <fieldset>
+                    <legend>Execute Tests</legend>
+
+                    <div id="imageButtonPanel">
+                        <button type="button" id="runSuite" onClick="htmlTestRunner.startTestSuite();"
+                                title="Run All tests" accesskey="a">
+                        </button>
+                        <button type="button" id="runSeleniumTest" onClick="htmlTestRunner.runSingleTest();"
+                                title="Run the Selected test" accesskey="r">
+                        </button>
+                        <button type="button" id="pauseTest" disabled="disabled"
+                                title="Pause/Continue" accesskey="p" class="cssPauseTest">
+                        </button>
+                        <button type="button" id="stepTest" disabled="disabled"
+                                title="Step" accesskey="s">
+                        </button>
+                    </div>
+
+                    <div style="float:left">Fast</div>
+                    <div style="float:right">Slow</div>
+                    <br/>
+                    <div id="speedSlider">
+                        <div id="speedTrack">&nbsp;</div>
+                        <div id="speedHandle">&nbsp;</div>
+                    </div>
+
+                    <div class="executionOptions">
+                        <input id="highlightOption" type="checkbox" name="highlightOption" value="0"/>
+                        <label for="highlightOption">Highlight elements</label>
+                    </div>
+
+                </fieldset>
+
+                <table id="stats" align="center">
+                    <tr>
+                        <td colspan="2" align="right">Elapsed:</td>
+                        <td id="elapsedTime" colspan="2">00.00</td>
+                    </tr>
+                    <tr>
+                        <th colspan="2">Tests</th>
+                        <th colspan="2">Commands</th>
+                    </tr>
+                    <tr>
+                        <td class="count" id="testRuns">0</td>
+                        <td>run</td>
+                        <td class="count" id="commandPasses">0</td>
+                        <td>passed</td>
+                    </tr>
+                    <tr>
+                        <td class="count" id="testFailures">0</td>
+                        <td>failed</td>
+                        <td class="count" id="commandFailures">0</td>
+                        <td>failed</td>
+                    </tr>
+                    <tr>
+                        <td colspan="2"></td>
+                        <td class="count" id="commandErrors">0</td>
+                        <td>incomplete</td>
+                    </tr>
+                </table>
+
+                <fieldset>
+                    <legend>Tools</legend>
+
+                    <button type="button" id="domViewer1" onClick="openDomViewer();">
+                        View DOM
+                    </button>
+                    <button type="button" onClick="LOG.show();">
+                        Show Log
+                    </button>
+
+                </fieldset>
+
+            </td>
+        </tr>
+    </table>
+</td>
+</tr>
+
+<!-- AUT -->
+
+<tr>
+    <td colspan="3" height="70%">
+        <iframe name="selenium_myiframe" id="selenium_myiframe" src="TestRunner-splash.html"></iframe>
+    </td>
+</tr>
+
+    </form>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/butmin.gif
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/butmin.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/butplus.gif
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/butplus.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/domviewer.css
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/domviewer.css	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/domviewer.css	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,298 @@
+/******************************************************************************
+* Defines default styles for site pages.                                      *
+******************************************************************************/
+.hidden {
+	display: none;
+}
+
+img{
+    display: inline;
+	border: none;
+}
+
+.box{
+	background: #fcfcfc;
+    border: 1px solid #000;
+	border-color: blue;
+    color: #000000;
+	margin: 10px auto;
+    padding: 3px;
+	vertical-align: bottom;
+}
+a {
+  text-decoration: none;
+}
+
+body {
+  background-color: #ffffff;
+  color: #000000;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 10pt;
+}
+
+h2 {
+  font-size: 140%;
+}
+
+h3 {
+  font-size: 120%;
+}
+
+h4 {
+  font-size: 100%;
+}
+
+pre {
+  font-family: Courier New, Courier, monospace;
+  font-size: 80%;
+}
+
+td, th {
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 10pt;
+  text-align: left;
+  vertical-align: top;
+}
+
+th {
+  font-weight: bold;
+  vertical-align: bottom;
+}
+
+ul {
+  list-style-type: square;
+}
+
+#demoBox {
+  border-color: #000000;
+  border-style: solid;
+  border-width: 1px;
+  padding: 8px;
+  width: 24em;
+}
+
+.footer {
+  margin-bottom: 0px;
+  text-align: center;
+}
+
+/* Boxed table styles */
+
+table.boxed {
+  border-spacing: 2px;
+  empty-cells: hide;
+}
+
+td.boxed, th.boxed, th.boxedHeader {
+  background-color: #ffffff;
+  border-color: #000000;
+  border-style: solid;
+  border-width: 1px;
+  color: #000000;
+  padding: 2px;
+  padding-left: 8px;
+  padding-right: 8px;
+}
+
+th.boxed {
+  background-color: #c0c0c0;
+}
+
+th.boxedHeader {
+  background-color: #808080;
+  color: #ffffff;
+}
+
+a.object {
+  color: #0000ff;
+}
+
+li {
+  white-space: nowrap;
+}
+
+ul {
+  list-style-type: square;
+  margin-left: 0px;
+  padding-left: 1em;
+}
+
+.boxlevel1{
+	background: #FFD700;
+}
+
+.boxlevel2{
+	background: #D2691E;
+}
+
+.boxlevel3{
+	background: #DCDCDC;
+}
+
+.boxlevel4{
+	background: #F5F5F5;
+}
+
+.boxlevel5{
+	background: #BEBEBE;
+}
+
+.boxlevel6{
+	background: #D3D3D3;
+}
+
+.boxlevel7{
+	background: #A9A9A9;
+}
+
+.boxlevel8{
+	background: #191970;
+}
+
+.boxlevel9{
+	background: #000080;
+}
+
+.boxlevel10{
+	background: #6495ED;
+}
+
+.boxlevel11{
+	background: #483D8B;
+}
+
+.boxlevel12{
+	background: #6A5ACD;
+}
+
+.boxlevel13{
+	background: #7B68EE;
+}
+
+.boxlevel14{
+	background: #8470FF;
+}
+
+.boxlevel15{
+	background: #0000CD;
+}
+
+.boxlevel16{
+	background: #4169E1;
+}
+
+.boxlevel17{
+	background: #0000FF;
+}
+
+.boxlevel18{
+	background: #1E90FF;
+}
+
+.boxlevel19{
+	background: #00BFFF;
+}
+
+.boxlevel20{
+	background: #87CEEB;
+}
+
+.boxlevel21{
+	background: #B0C4DE;
+}
+
+.boxlevel22{
+	background: #ADD8E6;
+}
+
+.boxlevel23{
+	background: #00CED1;
+}
+
+.boxlevel24{
+	background: #48D1CC;
+}
+
+.boxlevel25{
+	background: #40E0D0;
+}
+
+.boxlevel26{
+	background: #008B8B;
+}
+
+.boxlevel27{
+	background: #00FFFF;
+}
+
+.boxlevel28{
+	background: #E0FFFF;
+}
+
+.boxlevel29{
+	background: #5F9EA0;
+}
+
+.boxlevel30{
+	background: #66CDAA;
+}
+
+.boxlevel31{
+	background: #7FFFD4;
+}
+
+.boxlevel32{
+	background: #006400;
+}
+
+.boxlevel33{
+	background: #556B2F;
+}
+
+.boxlevel34{
+	background: #8FBC8F;
+}
+
+.boxlevel35{
+	background: #2E8B57;
+}
+
+.boxlevel36{
+	background: #3CB371;
+}
+
+.boxlevel37{
+	background: #20B2AA;
+}
+
+.boxlevel38{
+	background: #00FF7F;
+}
+
+.boxlevel39{
+	background: #7CFC00;
+}
+
+.boxlevel40{
+	background: #90EE90;
+}
+
+.boxlevel41{
+	background: #00FF00;
+}
+
+.boxlevel41{
+	background: #7FFF00;
+}
+
+.boxlevel42{
+	background: #00FA9A;
+}
+
+.boxlevel43{
+	background: #ADFF2F;
+}
+
+.boxlevel44{
+	background: #32CD32;
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/domviewer.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/domviewer.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/domviewer.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,16 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+    <head>
+        <title>DOM Viewer</title>
+        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+        <link rel="stylesheet" type="text/css" href="domviewer.css"/>
+        <script type="text/javascript" src="selenium-domviewer.js"></script>
+    </head>
+	<body onload="loadDomViewer();">
+		<h3>DOM Viewer</h3>
+		<p> This page is generated using JavaScript. If you see this text, your 
+			browser doesn't support JavaScript.</p>
+	</body>
+	
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/selenium-domviewer.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/selenium-domviewer.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/domviewer/selenium-domviewer.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,205 @@
+var HIDDEN="hidden";
+var LEVEL = "level";
+var PLUS_SRC="butplus.gif";
+var MIN_SRC="butmin.gif";
+var newRoot;
+var maxColumns=1;
+
+function loadDomViewer() {
+    // See if the rootDocument variable has been set on this window.
+    var rootDocument = window.rootDocument;
+
+    // If not look to the opener for an explicity rootDocument variable, otherwise, use the opener document
+    if (!rootDocument && window.opener) {
+        rootDocument = window.opener.rootDocument || window.opener.document;
+    }
+
+    if (rootDocument) {
+        document.body.innerHTML = displayDOM(rootDocument);
+    }
+    else {
+        document.body.innerHTML = "<b>Must specify rootDocument for window. This can be done by setting the rootDocument variable on this window, or on the opener window for a popup window.</b>";
+    }
+}
+
+
+function displayDOM(root){
+    var str = "";
+    str+="<table>";
+    str += treeTraversal(root,0);
+    // to make table columns work well.
+    str += "<tr>";
+    for (var i=0; i < maxColumns; i++) {
+        str+= "<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>";
+    }
+    str += "</tr>";
+    str += "</table>";
+    return str;
+}
+
+function checkForChildren(element){
+    if(!element.hasChildNodes())
+        return false;
+    
+    var nodes = element.childNodes;
+    var size = nodes.length;
+    var count=0;
+    
+    for(var i=0; i< size; i++){
+        var node = nodes.item(i);
+        //if(node.toString()=="[object Text]"){
+        //this is equalent to the above
+        //but will work with more browsers
+        if(node.nodeType!=1){
+            count++;
+        }
+    }
+    
+    if(count == size)
+        return false;
+    else
+        return true;
+}
+
+function treeTraversal(root, level){
+    var str = "";
+    var nodes= null;
+    var size = null;
+    //it is supposed to show the last node, 
+    //but the last node is always nodeText type
+    //and we don't show it
+    if(!root.hasChildNodes())
+        return "";//displayNode(root,level,false);
+    
+    nodes = root.childNodes;
+    size = nodes.length;
+
+    for(var i=0; i< size; i++){
+        var element = nodes.item(i);
+        //if the node is textNode, don't display
+        if(element.nodeType==1){
+            str+= displayNode(element,level,checkForChildren(element));
+            str+=treeTraversal(element, level+1);	
+        }
+    }
+    return str;
+}
+
+function displayNode(element, level, isLink){
+    nodeContent = getNodeContent(element);
+    columns = Math.round((nodeContent.length / 12) + 0.5);
+    if (columns + level > maxColumns) {
+        maxColumns = columns + level;
+    }
+    var str ="<tr class='"+LEVEL+level+"'>";
+    for (var i=0; i < level; i++)
+        str+= "<td> </td>";
+    str+="<td colspan='"+ columns +"' class='box"+" boxlevel"+level+"' >";
+    if(isLink){
+        str+='<a onclick="hide(this);return false;" href="javascript:void();">';
+        str+='<img src="'+MIN_SRC+'" />';
+    }
+    str += nodeContent;
+    if(isLink)
+        str+="</a></td></tr>";
+    return str;
+}
+
+function getNodeContent(element) {
+
+    str = "";
+    id ="";
+    if (element.id != null && element.id != "") {
+        id = " ID(" + element.id +")";
+    }
+    name ="";
+    if (element.name != null && element.name != "") {
+        name = " NAME(" + element.name + ")";
+    }
+    value ="";
+    if (element.value != null && element.value != "") {
+        value = " VALUE(" + element.value + ")";
+    }
+    href ="";
+    if (element.href != null && element.href != "") {
+        href = " HREF(" + element.href + ")";
+    }
+    clazz = "";
+    if (element.className != null && element.className != "") {
+        clazz = " CLASS(" + element.className + ")";
+    }
+    src = "";
+    if (element.src != null && element.src != "") {
+        src = " SRC(" + element.src + ")";
+    }
+    alt = "";
+    if (element.alt != null && element.alt != "") {
+        alt = " ALT(" + element.alt + ")";
+    }
+    type = "";
+    if (element.type != null && element.type != "") {
+        type = " TYPE(" + element.type + ")";
+    }
+    text ="";
+    if (element.text != null && element.text != "" && element.text != "undefined") {
+        text = " #TEXT(" + trim(element.text) +")";
+    }
+    str+=" <b>"+ element.nodeName + id + alt + type + clazz + name + value + href + src + text + "</b>";
+    return str;
+
+}
+
+function trim(val) {
+    val2 = val.substring(0,40) + "                   ";
+    var spaceChr = String.fromCharCode(32);
+    var length = val2.length;
+    var retVal = "";
+    var ix = length -1;
+
+    while(ix > -1){
+        if(val2.charAt(ix) == spaceChr) {
+        } else {
+            retVal = val2.substring(0, ix +1);
+            break;
+        }
+        ix = ix-1;
+    }
+    if (val.length > 40) {
+        retVal += "...";
+    }
+    return retVal;
+}
+
+function hide(hlink){
+    var isHidden = false;
+    var image = hlink.firstChild;
+    if(image.src.toString().indexOf(MIN_SRC)!=-1){
+        image.src=PLUS_SRC;
+        isHidden=true;
+    }else{
+        image.src=MIN_SRC;
+    }
+    var rowObj= hlink.parentNode.parentNode;
+    var rowLevel = parseInt(rowObj.className.substring(LEVEL.length));
+	
+    var sibling = rowObj.nextSibling;
+    var siblingLevel = sibling.className.substring(LEVEL.length);
+    if(siblingLevel.indexOf(HIDDEN)!=-1){
+        siblingLevel = siblingLevel.substring(0,siblingLevel.length - HIDDEN.length-1);
+    }
+    siblingLevel=parseInt(siblingLevel);
+    while(sibling!=null && rowLevel<siblingLevel){
+        if(isHidden){
+            sibling.className += " "+ HIDDEN;
+        }else if(!isHidden && sibling.className.indexOf(HIDDEN)!=-1){
+            var str = sibling.className;
+            sibling.className=str.substring(0, str.length - HIDDEN.length-1);
+        }
+        sibling = sibling.nextSibling;
+        siblingLevel = parseInt(sibling.className.substring(LEVEL.length));
+    }
+}
+
+function LOG(message) {
+    window.opener.LOG.warn(message);
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/all.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/all.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/continue.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/continue.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/continue_disabled.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/continue_disabled.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/pause.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/pause.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/pause_disabled.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/pause_disabled.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/selected.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/selected.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/step.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/step.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/step_disabled.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/icons/step_disabled.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/iedoc-core.xml
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/iedoc-core.xml	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/iedoc-core.xml	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,1515 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<apidoc>
+
+<top>Defines an object that runs Selenium commands.
+
+<h3><a name="locators"></a>Element Locators</h3>
+<p>
+Element Locators tell Selenium which HTML element a command refers to.
+The format of a locator is:</p>
+<blockquote>
+<em>locatorType</em><strong>=</strong><em>argument</em>
+</blockquote>
+
+<p>
+We support the following strategies for locating elements:
+</p>
+
+<ul>
+<li><strong>identifier</strong>=<em>id</em>: 
+Select the element with the specified &#064;id attribute. If no match is
+found, select the first element whose &#064;name attribute is <em>id</em>.
+(This is normally the default; see below.)</li>
+<li><strong>id</strong>=<em>id</em>:
+Select the element with the specified &#064;id attribute.</li>
+
+<li><strong>name</strong>=<em>name</em>:
+Select the first element with the specified &#064;name attribute.
+<ul class="first last simple">
+<li>username</li>
+<li>name=username</li>
+</ul>
+
+<p>The name may optionally be followed by one or more <em>element-filters</em>, separated from the name by whitespace.  If the <em>filterType</em> is not specified, <strong>value</strong> is assumed.</p>
+
+<ul class="first last simple">
+<li>name=flavour value=chocolate</li>
+</ul>
+</li>
+<li><strong>dom</strong>=<em>javascriptExpression</em>: 
+
+Find an element by evaluating the specified string.  This allows you to traverse the HTML Document Object
+Model using JavaScript.  Note that you must not return a value in this string; simply make it the last expression in the block.
+<ul class="first last simple">
+<li>dom=document.forms['myForm'].myDropdown</li>
+<li>dom=document.images[56]</li>
+<li>dom=function foo() { return document.links[1]; }; foo();</li>
+</ul>
+
+</li>
+
+<li><strong>xpath</strong>=<em>xpathExpression</em>: 
+Locate an element using an XPath expression.
+<ul class="first last simple">
+<li>xpath=//img[&#064;alt='The image alt text']</li>
+<li>xpath=//table[&#064;id='table1']//tr[4]/td[2]</li>
+<li>xpath=//a[contains(&#064;href,'#id1')]</li>
+<li>xpath=//a[contains(&#064;href,'#id1')]/&#064;class</li>
+<li>xpath=(//table[&#064;class='stylee'])//th[text()='theHeaderText']/../td</li>
+<li>xpath=//input[&#064;name='name2' and &#064;value='yes']</li>
+<li>xpath=//*[text()="right"]</li>
+
+</ul>
+</li>
+<li><strong>link</strong>=<em>textPattern</em>:
+Select the link (anchor) element which contains text matching the
+specified <em>pattern</em>.
+<ul class="first last simple">
+<li>link=The link text</li>
+</ul>
+
+</li>
+
+<li><strong>css</strong>=<em>cssSelectorSyntax</em>:
+Select the element using css selectors. Please refer to <a href="http://www.w3.org/TR/REC-CSS2/selector.html">CSS2 selectors</a>, <a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">CSS3 selectors</a> for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package.
+<ul class="first last simple">
+<li>css=a[href="#id3"]</li>
+<li>css=span#firstChild + span</li>
+</ul>
+<p>Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after). </p>
+</li>
+</ul>
+
+<p>
+Without an explicit locator prefix, Selenium uses the following default
+strategies:
+</p>
+
+<ul class="simple">
+<li><strong>dom</strong>, for locators starting with &quot;document.&quot;</li>
+<li><strong>xpath</strong>, for locators starting with &quot;//&quot;</li>
+<li><strong>identifier</strong>, otherwise</li>
+</ul>
+
+<h3><a name="element-filters">Element Filters</a></h3>
+<blockquote>
+<p>Element filters can be used with a locator to refine a list of candidate elements.  They are currently used only in the 'name' element-locator.</p>
+<p>Filters look much like locators, ie.</p>
+<blockquote>
+<em>filterType</em><strong>=</strong><em>argument</em></blockquote>
+
+<p>Supported element-filters are:</p>
+<p><strong>value=</strong><em>valuePattern</em></p>
+<blockquote>
+Matches elements based on their values.  This is particularly useful for refining a list of similarly-named toggle-buttons.</blockquote>
+<p><strong>index=</strong><em>index</em></p>
+<blockquote>
+Selects a single element based on its position in the list (offset from zero).</blockquote>
+</blockquote>
+
+<h3><a name="patterns"></a>String-match Patterns</h3>
+
+<p>
+Various Pattern syntaxes are available for matching string values:
+</p>
+<ul>
+<li><strong>glob:</strong><em>pattern</em>:
+Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a
+kind of limited regular-expression syntax typically used in command-line
+shells. In a glob pattern, "*" represents any sequence of characters, and "?"
+represents any single character. Glob patterns match against the entire
+string.</li>
+<li><strong>regexp:</strong><em>regexp</em>:
+Match a string using a regular-expression. The full power of JavaScript
+regular-expressions is available.</li>
+<li><strong>exact:</strong><em>string</em>:
+
+Match a string exactly, verbatim, without any of that fancy wildcard
+stuff.</li>
+</ul>
+<p>
+If no pattern prefix is specified, Selenium assumes that it's a "glob"
+pattern.
+</p></top>
+
+<function name="click">
+
+<param name="locator">an element locator</param>
+
+<comment>Clicks on a link, button, checkbox or radio button. If the click action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.</comment>
+
+</function>
+
+<function name="doubleClick">
+
+<param name="locator">an element locator</param>
+
+<comment>Double clicks on a link, button, checkbox or radio button. If the double click action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.</comment>
+
+</function>
+
+<function name="clickAt">
+
+<param name="locator">an element locator</param>
+
+<param name="coordString">specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</param>
+
+<comment>Clicks on a link, button, checkbox or radio button. If the click action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.</comment>
+
+</function>
+
+<function name="doubleClickAt">
+
+<param name="locator">an element locator</param>
+
+<param name="coordString">specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</param>
+
+<comment>Doubleclicks on a link, button, checkbox or radio button. If the action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.</comment>
+
+</function>
+
+<function name="fireEvent">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="eventName">the event name, e.g. "focus" or "blur"</param>
+
+<comment>Explicitly simulate an event, to trigger the corresponding &quot;on<em>event</em>&quot;
+handler.</comment>
+
+</function>
+
+<function name="keyPress">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="keySequence">Either be a string("\" followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: "w", "\119".</param>
+
+<comment>Simulates a user pressing and releasing a key.</comment>
+
+</function>
+
+<function name="shiftKeyDown">
+
+<comment>Press the shift key and hold it down until doShiftUp() is called or a new page is loaded.</comment>
+
+</function>
+
+<function name="shiftKeyUp">
+
+<comment>Release the shift key.</comment>
+
+</function>
+
+<function name="metaKeyDown">
+
+<comment>Press the meta key and hold it down until doMetaUp() is called or a new page is loaded.</comment>
+
+</function>
+
+<function name="metaKeyUp">
+
+<comment>Release the meta key.</comment>
+
+</function>
+
+<function name="altKeyDown">
+
+<comment>Press the alt key and hold it down until doAltUp() is called or a new page is loaded.</comment>
+
+</function>
+
+<function name="altKeyUp">
+
+<comment>Release the alt key.</comment>
+
+</function>
+
+<function name="controlKeyDown">
+
+<comment>Press the control key and hold it down until doControlUp() is called or a new page is loaded.</comment>
+
+</function>
+
+<function name="controlKeyUp">
+
+<comment>Release the control key.</comment>
+
+</function>
+
+<function name="keyDown">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="keySequence">Either be a string("\" followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: "w", "\119".</param>
+
+<comment>Simulates a user pressing a key (without releasing it yet).</comment>
+
+</function>
+
+<function name="keyUp">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="keySequence">Either be a string("\" followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: "w", "\119".</param>
+
+<comment>Simulates a user releasing a key.</comment>
+
+</function>
+
+<function name="mouseOver">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Simulates a user hovering a mouse over the specified element.</comment>
+
+</function>
+
+<function name="mouseOut">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Simulates a user moving the mouse pointer away from the specified element.</comment>
+
+</function>
+
+<function name="mouseDown">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Simulates a user pressing the mouse button (without releasing it yet) on
+the specified element.</comment>
+
+</function>
+
+<function name="mouseDownAt">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="coordString">specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</param>
+
+<comment>Simulates a user pressing the mouse button (without releasing it yet) at
+the specified location.</comment>
+
+</function>
+
+<function name="mouseUp">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Simulates the event that occurs when the user releases the mouse button (i.e., stops
+holding the button down) on the specified element.</comment>
+
+</function>
+
+<function name="mouseUpAt">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="coordString">specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</param>
+
+<comment>Simulates the event that occurs when the user releases the mouse button (i.e., stops
+holding the button down) at the specified location.</comment>
+
+</function>
+
+<function name="mouseMove">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Simulates a user pressing the mouse button (without releasing it yet) on
+the specified element.</comment>
+
+</function>
+
+<function name="mouseMoveAt">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="coordString">specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</param>
+
+<comment>Simulates a user pressing the mouse button (without releasing it yet) on
+the specified element.</comment>
+
+</function>
+
+<function name="type">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="value">the value to type</param>
+
+<comment>Sets the value of an input field, as though you typed it in.
+
+<p>Can also be used to set the value of combo boxes, check boxes, etc. In these cases,
+value should be the value of the option selected, not the visible text.</p></comment>
+
+</function>
+
+<function name="typeKeys">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="value">the value to type</param>
+
+<comment>Simulates keystroke events on the specified element, as though you typed the value key-by-key.
+
+<p>This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string;
+this is useful for dynamic UI widgets (like auto-completing combo boxes) that require explicit key events.</p>
+
+<p>Unlike the simple "type" command, which forces the specified value into the page directly, this command
+may or may not have any visible effect, even in cases where typing keys would normally have a visible effect.
+For example, if you use "typeKeys" on a form element, you may or may not see the results of what you typed in
+the field.</p>
+<p>In some cases, you may need to use the simple "type" command to set the value of the field and then the "typeKeys" command to
+send the keystroke events corresponding to what you just typed.</p></comment>
+
+</function>
+
+<function name="setSpeed">
+
+<param name="value">the number of milliseconds to pause after operation</param>
+
+<comment>Set execution speed (i.e., set the millisecond length of a delay which will follow each selenium operation).  By default, there is no such delay, i.e.,
+the delay is 0 milliseconds.</comment>
+
+</function>
+
+<function name="getSpeed">
+
+<comment>Get execution speed (i.e., get the millisecond length of the delay following each selenium operation).  By default, there is no such delay, i.e.,
+the delay is 0 milliseconds.
+
+See also setSpeed.</comment>
+
+</function>
+
+<function name="check">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Check a toggle-button (checkbox/radio)</comment>
+
+</function>
+
+<function name="uncheck">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Uncheck a toggle-button (checkbox/radio)</comment>
+
+</function>
+
+<function name="select">
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<param name="optionLocator">an option locator (a label by default)</param>
+
+<comment>Select an option from a drop-down using an option locator.
+
+<p>
+Option locators provide different ways of specifying options of an HTML
+Select element (e.g. for selecting a specific option, or for asserting
+that the selected option satisfies a specification). There are several
+forms of Select Option Locator.
+</p>
+<ul>
+<li><strong>label</strong>=<em>labelPattern</em>:
+matches options based on their labels, i.e. the visible text. (This
+is the default.)
+<ul class="first last simple">
+<li>label=regexp:^[Oo]ther</li>
+</ul>
+</li>
+<li><strong>value</strong>=<em>valuePattern</em>:
+matches options based on their values.
+<ul class="first last simple">
+<li>value=other</li>
+</ul>
+
+
+</li>
+<li><strong>id</strong>=<em>id</em>:
+
+matches options based on their ids.
+<ul class="first last simple">
+<li>id=option1</li>
+</ul>
+</li>
+<li><strong>index</strong>=<em>index</em>:
+matches an option based on its index (offset from zero).
+<ul class="first last simple">
+
+<li>index=2</li>
+</ul>
+</li>
+</ul>
+<p>
+If no option locator prefix is provided, the default behaviour is to match on <strong>label</strong>.
+</p></comment>
+
+</function>
+
+<function name="addSelection">
+
+<param name="locator">an <a href="#locators">element locator</a> identifying a multi-select box</param>
+
+<param name="optionLocator">an option locator (a label by default)</param>
+
+<comment>Add a selection to the set of selected options in a multi-select element using an option locator.
+
+ at see #doSelect for details of option locators</comment>
+
+</function>
+
+<function name="removeSelection">
+
+<param name="locator">an <a href="#locators">element locator</a> identifying a multi-select box</param>
+
+<param name="optionLocator">an option locator (a label by default)</param>
+
+<comment>Remove a selection from the set of selected options in a multi-select element using an option locator.
+
+ at see #doSelect for details of option locators</comment>
+
+</function>
+
+<function name="removeAllSelections">
+
+<param name="locator">an <a href="#locators">element locator</a> identifying a multi-select box</param>
+
+<comment>Unselects all of the selected options in a multi-select element.</comment>
+
+</function>
+
+<function name="submit">
+
+<param name="formLocator">an <a href="#locators">element locator</a> for the form you want to submit</param>
+
+<comment>Submit the specified form. This is particularly useful for forms without
+submit buttons, e.g. single-input "Search" forms.</comment>
+
+</function>
+
+<function name="open">
+
+<param name="url">the URL to open; may be relative or absolute</param>
+
+<comment>Opens an URL in the test frame. This accepts both relative and absolute
+URLs.
+
+The &quot;open&quot; command waits for the page to load before proceeding,
+ie. the &quot;AndWait&quot; suffix is implicit.
+
+<em>Note</em>: The URL must be on the same domain as the runner HTML
+due to security restrictions in the browser (Same Origin Policy). If you
+need to open an URL on another domain, use the Selenium Server to start a
+new browser session on that domain.</comment>
+
+</function>
+
+<function name="openWindow">
+
+<param name="url">the URL to open, which can be blank</param>
+
+<param name="windowID">the JavaScript window ID of the window to select</param>
+
+<comment>Opens a popup window (if a window with that ID isn't already open).
+After opening the window, you'll need to select it using the selectWindow
+command.
+
+<p>This command can also be a useful workaround for bug SEL-339.  In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p></comment>
+
+</function>
+
+<function name="selectWindow">
+
+<param name="windowID">the JavaScript window ID of the window to select</param>
+
+<comment>Selects a popup window; once a popup window has been selected, all
+commands go to that window. To select the main window again, use null
+as the target.
+
+<p>Note that there is a big difference between a window's internal JavaScript "name" property
+and the "title" of a given window's document (which is normally what you actually see, as an end user,
+in the title bar of the window).  The "name" is normally invisible to the end-user; it's the second 
+parameter "windowName" passed to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag)
+(which selenium intercepts).</p>
+
+<p>Selenium has several strategies for finding the window object referred to by the "windowID" parameter.</p>
+
+<p>1.) if windowID is null, (or the string "null") then it is assumed the user is referring to the original window instantiated by the browser).</p>
+<p>2.) if the value of the "windowID" parameter is a JavaScript variable name in the current application window, then it is assumed
+that this variable contains the return value from a call to the JavaScript window.open() method.</p>
+<p>3.) Otherwise, selenium looks in a hash it maintains that maps string names to window "names".</p>
+<p>4.) If <i>that</i> fails, we'll try looping over all of the known windows to try to find the appropriate "title".
+Since "title" is not necessarily unique, this may have unexpected behavior.</p>
+
+<p>If you're having trouble figuring out what is the name of a window that you want to manipulate, look at the selenium log messages
+which identify the names of windows created via window.open (and therefore intercepted by selenium).  You will see messages
+like the following for each window as it is opened:</p>
+
+<p><code>debug: window.open call intercepted; window ID (which you can use with selectWindow()) is "myNewWindow"</code></p>
+
+<p>In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+(This is bug SEL-339.)  In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p></comment>
+
+</function>
+
+<function name="selectFrame">
+
+<param name="locator">an <a href="#locators">element locator</a> identifying a frame or iframe</param>
+
+<comment>Selects a frame within the current window.  (You may invoke this command
+multiple times to select nested frames.)  To select the parent frame, use
+"relative=parent" as a locator; to select the top frame, use "relative=top".
+You can also select a frame by its 0-based index number; select the first frame with
+"index=0", or the third frame with "index=2".
+
+<p>You may also use a DOM expression to identify the frame you want directly,
+like this: <code>dom=frames["main"].frames["subframe"]</code></p></comment>
+
+</function>
+
+<function name="getWhetherThisFrameMatchFrameExpression">
+
+<return type="boolean">true if the new frame is this code's window</return>
+
+<param name="currentFrameString">starting frame</param>
+
+<param name="target">new frame (which might be relative to the current one)</param>
+
+<comment>Determine whether current/locator identify the frame containing this running code.
+
+<p>This is useful in proxy injection mode, where this code runs in every
+browser frame and window, and sometimes the selenium server needs to identify
+the "current" frame.  In this case, when the test calls selectFrame, this
+routine is called for each frame to figure out which one has been selected.
+The selected frame will return true, while all others will return false.</p></comment>
+
+</function>
+
+<function name="getWhetherThisWindowMatchWindowExpression">
+
+<return type="boolean">true if the new window is this code's window</return>
+
+<param name="currentWindowString">starting window</param>
+
+<param name="target">new window (which might be relative to the current one, e.g., "_parent")</param>
+
+<comment>Determine whether currentWindowString plus target identify the window containing this running code.
+
+<p>This is useful in proxy injection mode, where this code runs in every
+browser frame and window, and sometimes the selenium server needs to identify
+the "current" window.  In this case, when the test calls selectWindow, this
+routine is called for each window to figure out which one has been selected.
+The selected window will return true, while all others will return false.</p></comment>
+
+</function>
+
+<function name="waitForPopUp">
+
+<param name="windowID">the JavaScript window ID of the window that will appear</param>
+
+<param name="timeout">a timeout in milliseconds, after which the action will return with an error</param>
+
+<comment>Waits for a popup window to appear and load up.</comment>
+
+</function>
+
+<function name="chooseCancelOnNextConfirmation">
+
+<comment>By default, Selenium's overridden window.confirm() function will
+return true, as if the user had manually clicked OK; after running
+this command, the next call to confirm() will return false, as if
+the user had clicked Cancel.  Selenium will then resume using the
+default behavior for future confirmations, automatically returning 
+true (OK) unless/until you explicitly call this command for each
+confirmation.</comment>
+
+</function>
+
+<function name="chooseOkOnNextConfirmation">
+
+<comment>Undo the effect of calling chooseCancelOnNextConfirmation.  Note
+that Selenium's overridden window.confirm() function will normally automatically
+return true, as if the user had manually clicked OK, so you shouldn't
+need to use this command unless for some reason you need to change
+your mind prior to the next confirmation.  After any confirmation, Selenium will resume using the
+default behavior for future confirmations, automatically returning 
+true (OK) unless/until you explicitly call chooseCancelOnNextConfirmation for each
+confirmation.</comment>
+
+</function>
+
+<function name="answerOnNextPrompt">
+
+<param name="answer">the answer to give in response to the prompt pop-up</param>
+
+<comment>Instructs Selenium to return the specified answer string in response to
+the next JavaScript prompt [window.prompt()].</comment>
+
+</function>
+
+<function name="goBack">
+
+<comment>Simulates the user clicking the "back" button on their browser.</comment>
+
+</function>
+
+<function name="refresh">
+
+<comment>Simulates the user clicking the "Refresh" button on their browser.</comment>
+
+</function>
+
+<function name="close">
+
+<comment>Simulates the user clicking the "close" button in the titlebar of a popup
+window or tab.</comment>
+
+</function>
+
+<function name="isAlertPresent">
+
+<return type="boolean">true if there is an alert</return>
+
+<comment>Has an alert occurred?
+
+<p>
+This function never throws an exception
+</p></comment>
+
+</function>
+
+<function name="isPromptPresent">
+
+<return type="boolean">true if there is a pending prompt</return>
+
+<comment>Has a prompt occurred?
+
+<p>
+This function never throws an exception
+</p></comment>
+
+</function>
+
+<function name="isConfirmationPresent">
+
+<return type="boolean">true if there is a pending confirmation</return>
+
+<comment>Has confirm() been called?
+
+<p>
+This function never throws an exception
+</p></comment>
+
+</function>
+
+<function name="getAlert">
+
+<return type="string">The message of the most recent JavaScript alert</return>
+
+<comment>Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts.
+
+<p>Getting an alert has the same effect as manually clicking OK. If an
+alert is generated but you do not get/verify it, the next Selenium action
+will fail.</p>
+
+<p>NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert
+dialog.</p>
+
+<p>NOTE: Selenium does NOT support JavaScript alerts that are generated in a
+page's onload() event handler. In this case a visible dialog WILL be
+generated and Selenium will hang until someone manually clicks OK.</p></comment>
+
+</function>
+
+<function name="getConfirmation">
+
+<return type="string">the message of the most recent JavaScript confirmation dialog</return>
+
+<comment>Retrieves the message of a JavaScript confirmation dialog generated during
+the previous action.
+
+<p>
+By default, the confirm function will return true, having the same effect
+as manually clicking OK. This can be changed by prior execution of the
+chooseCancelOnNextConfirmation command. If an confirmation is generated
+but you do not get/verify it, the next Selenium action will fail.
+</p>
+
+<p>
+NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible
+dialog.
+</p>
+
+<p>
+NOTE: Selenium does NOT support JavaScript confirmations that are
+generated in a page's onload() event handler. In this case a visible
+dialog WILL be generated and Selenium will hang until you manually click
+OK.
+</p></comment>
+
+</function>
+
+<function name="getPrompt">
+
+<return type="string">the message of the most recent JavaScript question prompt</return>
+
+<comment>Retrieves the message of a JavaScript question prompt dialog generated during
+the previous action.
+
+<p>Successful handling of the prompt requires prior execution of the
+answerOnNextPrompt command. If a prompt is generated but you
+do not get/verify it, the next Selenium action will fail.</p>
+
+<p>NOTE: under Selenium, JavaScript prompts will NOT pop up a visible
+dialog.</p>
+
+<p>NOTE: Selenium does NOT support JavaScript prompts that are generated in a
+page's onload() event handler. In this case a visible dialog WILL be
+generated and Selenium will hang until someone manually clicks OK.</p></comment>
+
+</function>
+
+<function name="getLocation">
+
+<return type="string">the absolute URL of the current page</return>
+
+<comment>Gets the absolute URL of the current page.</comment>
+
+</function>
+
+<function name="getTitle">
+
+<return type="string">the title of the current page</return>
+
+<comment>Gets the title of the current page.</comment>
+
+</function>
+
+<function name="getBodyText">
+
+<return type="string">the entire text of the page</return>
+
+<comment>Gets the entire text of the page.</comment>
+
+</function>
+
+<function name="getValue">
+
+<return type="string">the element value, or "on/off" for checkbox/radio elements</return>
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Gets the (whitespace-trimmed) value of an input field (or anything else with a value parameter).
+For checkbox/radio elements, the value will be "on" or "off" depending on
+whether the element is checked or not.</comment>
+
+</function>
+
+<function name="getText">
+
+<return type="string">the text of the element</return>
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Gets the text of an element. This works for any element that contains
+text. This command uses either the textContent (Mozilla-like browsers) or
+the innerText (IE-like browsers) of the element, which is the rendered
+text shown to the user.</comment>
+
+</function>
+
+<function name="highlight">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Briefly changes the backgroundColor of the specified element yellow.  Useful for debugging.</comment>
+
+</function>
+
+<function name="getEval">
+
+<return type="string">the results of evaluating the snippet</return>
+
+<param name="script">the JavaScript snippet to run</param>
+
+<comment>Gets the result of evaluating the specified JavaScript snippet.  The snippet may
+have multiple lines, but only the result of the last line will be returned.
+
+<p>Note that, by default, the snippet will run in the context of the "selenium"
+object itself, so <code>this</code> will refer to the Selenium object.  Use <code>window</code> to
+refer to the window of your application, e.g. <code>window.document.getElementById('foo')</code></p>
+
+<p>If you need to use
+a locator to refer to a single element in your application page, you can
+use <code>this.browserbot.findElement("id=foo")</code> where "id=foo" is your locator.</p></comment>
+
+</function>
+
+<function name="isChecked">
+
+<return type="boolean">true if the checkbox is checked, false otherwise</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to a checkbox or radio button</param>
+
+<comment>Gets whether a toggle-button (checkbox/radio) is checked.  Fails if the specified element doesn't exist or isn't a toggle-button.</comment>
+
+</function>
+
+<function name="getTable">
+
+<return type="string">the text from the specified cell</return>
+
+<param name="tableCellAddress">a cell address, e.g. "foo.1.4"</param>
+
+<comment>Gets the text from a cell of a table. The cellAddress syntax
+tableLocator.row.column, where row and column start at 0.</comment>
+
+</function>
+
+<function name="getSelectedLabels">
+
+<return type="string[]">an array of all selected option labels in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets all option labels (visible text) for selected options in the specified select or multi-select element.</comment>
+
+</function>
+
+<function name="getSelectedLabel">
+
+<return type="string">the selected option label in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets option label (visible text) for selected option in the specified select element.</comment>
+
+</function>
+
+<function name="getSelectedValues">
+
+<return type="string[]">an array of all selected option values in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets all option values (value attributes) for selected options in the specified select or multi-select element.</comment>
+
+</function>
+
+<function name="getSelectedValue">
+
+<return type="string">the selected option value in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets option value (value attribute) for selected option in the specified select element.</comment>
+
+</function>
+
+<function name="getSelectedIndexes">
+
+<return type="string[]">an array of all selected option indexes in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets all option indexes (option number, starting at 0) for selected options in the specified select or multi-select element.</comment>
+
+</function>
+
+<function name="getSelectedIndex">
+
+<return type="string">the selected option index in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets option index (option number, starting at 0) for selected option in the specified select element.</comment>
+
+</function>
+
+<function name="getSelectedIds">
+
+<return type="string[]">an array of all selected option IDs in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets all option element IDs for selected options in the specified select or multi-select element.</comment>
+
+</function>
+
+<function name="getSelectedId">
+
+<return type="string">the selected option ID in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets option element ID for selected option in the specified select element.</comment>
+
+</function>
+
+<function name="isSomethingSelected">
+
+<return type="boolean">true if some option has been selected, false otherwise</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Determines whether some option in a drop-down menu is selected.</comment>
+
+</function>
+
+<function name="getSelectOptions">
+
+<return type="string[]">an array of all option labels in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets all option labels in the specified select drop-down.</comment>
+
+</function>
+
+<function name="getAttribute">
+
+<return type="string">the value of the specified attribute</return>
+
+<param name="attributeLocator">an element locator followed by an &#064; sign and then the name of the attribute, e.g. "foo&#064;bar"</param>
+
+<comment>Gets the value of an element attribute.</comment>
+
+</function>
+
+<function name="isTextPresent">
+
+<return type="boolean">true if the pattern matches the text, false otherwise</return>
+
+<param name="pattern">a <a href="#patterns">pattern</a> to match with the text of the page</param>
+
+<comment>Verifies that the specified text pattern appears somewhere on the rendered page shown to the user.</comment>
+
+</function>
+
+<function name="isElementPresent">
+
+<return type="boolean">true if the element is present, false otherwise</return>
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Verifies that the specified element is somewhere on the page.</comment>
+
+</function>
+
+<function name="isVisible">
+
+<return type="boolean">true if the specified element is visible, false otherwise</return>
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Determines if the specified element is visible. An
+element can be rendered invisible by setting the CSS "visibility"
+property to "hidden", or the "display" property to "none", either for the
+element itself or one if its ancestors.  This method will fail if
+the element is not present.</comment>
+
+</function>
+
+<function name="isEditable">
+
+<return type="boolean">true if the input element is editable, false otherwise</return>
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Determines whether the specified input element is editable, ie hasn't been disabled.
+This method will fail if the specified element isn't an input element.</comment>
+
+</function>
+
+<function name="getAllButtons">
+
+<return type="string[]">the IDs of all buttons on the page</return>
+
+<comment>Returns the IDs of all buttons on the page.
+
+<p>If a given button has no ID, it will appear as "" in this array.</p></comment>
+
+</function>
+
+<function name="getAllLinks">
+
+<return type="string[]">the IDs of all links on the page</return>
+
+<comment>Returns the IDs of all links on the page.
+
+<p>If a given link has no ID, it will appear as "" in this array.</p></comment>
+
+</function>
+
+<function name="getAllFields">
+
+<return type="string[]">the IDs of all field on the page</return>
+
+<comment>Returns the IDs of all input fields on the page.
+
+<p>If a given field has no ID, it will appear as "" in this array.</p></comment>
+
+</function>
+
+<function name="getAttributeFromAllWindows">
+
+<return type="string[]">the set of values of this attribute from all known windows.</return>
+
+<param name="attributeName">name of an attribute on the windows</param>
+
+<comment>Returns every instance of some attribute from all known windows.</comment>
+
+</function>
+
+<function name="dragdrop">
+
+<param name="locator">an element locator</param>
+
+<param name="movementsString">offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"</param>
+
+<comment>deprecated - use dragAndDrop instead</comment>
+
+</function>
+
+<function name="setMouseSpeed">
+
+<param name="pixels">the number of pixels between "mousemove" events</param>
+
+<comment>Configure the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+<p>Setting this value to 0 means that we'll send a "mousemove" event to every single pixel
+in between the start location and the end location; that can be very slow, and may
+cause some browsers to force the JavaScript to timeout.</p>
+
+<p>If the mouse speed is greater than the distance between the two dragged objects, we'll
+just send one "mousemove" at the start location and then one final one at the end location.</p></comment>
+
+</function>
+
+<function name="getMouseSpeed">
+
+<return type="number">the number of pixels between "mousemove" events during dragAndDrop commands (default=10)</return>
+
+<comment>Returns the number of pixels between "mousemove" events during dragAndDrop commands (default=10).</comment>
+
+</function>
+
+<function name="dragAndDrop">
+
+<param name="locator">an element locator</param>
+
+<param name="movementsString">offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"</param>
+
+<comment>Drags an element a certain distance and then drops it</comment>
+
+</function>
+
+<function name="dragAndDropToObject">
+
+<param name="locatorOfObjectToBeDragged">an element to be dragged</param>
+
+<param name="locatorOfDragDestinationObject">an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged  is dropped</param>
+
+<comment>Drags an element and drops it on another element</comment>
+
+</function>
+
+<function name="windowFocus">
+
+<comment>Gives focus to the currently selected window</comment>
+
+</function>
+
+<function name="windowMaximize">
+
+<comment>Resize currently selected window to take up the entire screen</comment>
+
+</function>
+
+<function name="getAllWindowIds">
+
+<return type="string[]">the IDs of all windows that the browser knows about.</return>
+
+<comment>Returns the IDs of all windows that the browser knows about.</comment>
+
+</function>
+
+<function name="getAllWindowNames">
+
+<return type="string[]">the names of all windows that the browser knows about.</return>
+
+<comment>Returns the names of all windows that the browser knows about.</comment>
+
+</function>
+
+<function name="getAllWindowTitles">
+
+<return type="string[]">the titles of all windows that the browser knows about.</return>
+
+<comment>Returns the titles of all windows that the browser knows about.</comment>
+
+</function>
+
+<function name="getHtmlSource">
+
+<return type="string">the entire HTML source</return>
+
+<comment>Returns the entire HTML source between the opening and
+closing "html" tags.</comment>
+
+</function>
+
+<function name="setCursorPosition">
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an input element or textarea</param>
+
+<param name="position">the numerical position of the cursor in the field; position should be 0 to move the position to the beginning of the field.  You can also set the cursor to -1 to move it to the end of the field.</param>
+
+<comment>Moves the text cursor to the specified position in the given input element or textarea.
+This method will fail if the specified element isn't an input element or textarea.</comment>
+
+</function>
+
+<function name="getElementIndex">
+
+<return type="number">of relative index of the element to its parent (starting from 0)</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element</param>
+
+<comment>Get the relative index of an element to its parent (starting from 0). The comment node and empty text node
+will be ignored.</comment>
+
+</function>
+
+<function name="isOrdered">
+
+<return type="boolean">true if element1 is the previous sibling of element2, false otherwise</return>
+
+<param name="locator1">an <a href="#locators">element locator</a> pointing to the first element</param>
+
+<param name="locator2">an <a href="#locators">element locator</a> pointing to the second element</param>
+
+<comment>Check if these two elements have same parent and are ordered siblings in the DOM. Two same elements will
+not be considered ordered.</comment>
+
+</function>
+
+<function name="getElementPositionLeft">
+
+<return type="number">of pixels from the edge of the frame.</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element OR an element itself</param>
+
+<comment>Retrieves the horizontal position of an element</comment>
+
+</function>
+
+<function name="getElementPositionTop">
+
+<return type="number">of pixels from the edge of the frame.</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element OR an element itself</param>
+
+<comment>Retrieves the vertical position of an element</comment>
+
+</function>
+
+<function name="getElementWidth">
+
+<return type="number">width of an element in pixels</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element</param>
+
+<comment>Retrieves the width of an element</comment>
+
+</function>
+
+<function name="getElementHeight">
+
+<return type="number">height of an element in pixels</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element</param>
+
+<comment>Retrieves the height of an element</comment>
+
+</function>
+
+<function name="getCursorPosition">
+
+<return type="number">the numerical position of the cursor in the field</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an input element or textarea</param>
+
+<comment>Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers.
+
+<p>Specifically, if the cursor/selection has been cleared by JavaScript, this command will tend to
+return the position of the last location of the cursor, even though the cursor is now gone from the page.  This is filed as <a href="http://jira.openqa.org/browse/SEL-243">SEL-243</a>.</p>
+This method will fail if the specified element isn't an input element or textarea, or there is no cursor in the element.</comment>
+
+</function>
+
+<function name="getExpression">
+
+<return type="string">the value passed in</return>
+
+<param name="expression">the value to return</param>
+
+<comment>Returns the specified expression.
+
+<p>This is useful because of JavaScript preprocessing.
+It is used to generate commands like assertExpression and waitForExpression.</p></comment>
+
+</function>
+
+<function name="getXpathCount">
+
+<return type="number">the number of nodes that match the specified xpath</return>
+
+<param name="xpath">the xpath expression to evaluate. do NOT wrap this expression in a 'count()' function; we will do that for you.</param>
+
+<comment>Returns the number of nodes that match the specified xpath, eg. "//table" would give
+the number of tables.</comment>
+
+</function>
+
+<function name="assignId">
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element</param>
+
+<param name="identifier">a string to be used as the ID of the specified element</param>
+
+<comment>Temporarily sets the "id" attribute of the specified element, so you can locate it in the future
+using its ID rather than a slow/complicated XPath.  This ID will disappear once the page is
+reloaded.</comment>
+
+</function>
+
+<function name="allowNativeXpath">
+
+<param name="allow">boolean, true means we'll prefer to use native XPath; false means we'll only use JS XPath</param>
+
+<comment>Specifies whether Selenium should use the native in-browser implementation
+of XPath (if any native version is available); if you pass "false" to
+this function, we will always use our pure-JavaScript xpath library.
+Using the pure-JS xpath library can improve the consistency of xpath
+element locators between different browser vendors, but the pure-JS
+version is much slower than the native implementations.</comment>
+
+</function>
+
+<function name="waitForCondition">
+
+<param name="script">the JavaScript snippet to run</param>
+
+<param name="timeout">a timeout in milliseconds, after which this command will return with an error</param>
+
+<comment>Runs the specified JavaScript snippet repeatedly until it evaluates to "true".
+The snippet may have multiple lines, but only the result of the last line
+will be considered.
+
+<p>Note that, by default, the snippet will be run in the runner's test window, not in the window
+of your application.  To get the window of your application, you can use
+the JavaScript snippet <code>selenium.browserbot.getCurrentWindow()</code>, and then
+run your JavaScript in there</p></comment>
+
+</function>
+
+<function name="setTimeout">
+
+<param name="timeout">a timeout in milliseconds, after which the action will return with an error</param>
+
+<comment>Specifies the amount of time that Selenium will wait for actions to complete.
+
+<p>Actions that require waiting include "open" and the "waitFor*" actions.</p>
+The default timeout is 30 seconds.</comment>
+
+</function>
+
+<function name="waitForPageToLoad">
+
+<param name="timeout">a timeout in milliseconds, after which this command will return with an error</param>
+
+<comment>Waits for a new page to load.
+
+<p>You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc.
+(which are only available in the JS API).</p>
+
+<p>Selenium constantly keeps track of new pages loading, and sets a "newPageLoaded"
+flag when it first notices a page load.  Running any other Selenium command after
+turns the flag to false.  Hence, if you want to wait for a page to load, you must
+wait immediately after a Selenium command that caused a page-load.</p></comment>
+
+</function>
+
+<function name="waitForFrameToLoad">
+
+<param name="frameAddress">FrameAddress from the server side</param>
+
+<param name="timeout">a timeout in milliseconds, after which this command will return with an error</param>
+
+<comment>Waits for a new frame to load.
+
+<p>Selenium constantly keeps track of new pages and frames loading, 
+and sets a "newPageLoaded" flag when it first notices a page load.</p>
+
+See waitForPageToLoad for more information.</comment>
+
+</function>
+
+<function name="getCookie">
+
+<return type="string">all cookies of the current page under test</return>
+
+<comment>Return all cookies of the current page under test.</comment>
+
+</function>
+
+<function name="createCookie">
+
+<param name="nameValuePair">name and value of the cookie in a format "name=value"</param>
+
+<param name="optionsString">options for the cookie. Currently supported options include 'path' and 'max_age'.      the optionsString's format is "path=/path/, max_age=60". The order of options are irrelevant, the unit      of the value of 'max_age' is second.</param>
+
+<comment>Create a new cookie whose path and domain are same with those of current page
+under test, unless you specified a path for this cookie explicitly.</comment>
+
+</function>
+
+<function name="deleteCookie">
+
+<param name="name">the name of the cookie to be deleted</param>
+
+<param name="path">the path property of the cookie to be deleted</param>
+
+<comment>Delete a named cookie with specified path.</comment>
+
+</function>
+
+<function name="setBrowserLogLevel">
+
+<param name="logLevel">one of the following: "debug", "info", "warn", "error" or "off"</param>
+
+<comment>Sets the threshold for browser-side logging messages; log messages beneath this threshold will be discarded.
+Valid logLevel strings are: "debug", "info", "warn", "error" or "off".
+To see the browser logs, you need to
+either show the log window in GUI mode, or enable browser-side logging in Selenium RC.</comment>
+
+</function>
+
+<function name="runScript">
+
+<param name="script">the JavaScript snippet to run</param>
+
+<comment>Creates a new "script" tag in the body of the current test window, and 
+adds the specified text into the body of the command.  Scripts run in
+this way can often be debugged more easily than scripts executed using
+Selenium's "getEval" command.  Beware that JS exceptions thrown in these script
+tags aren't managed by Selenium, so you should probably wrap your script
+in try/catch blocks if there is any chance that the script will throw
+an exception.</comment>
+
+</function>
+
+<function name="addLocationStrategy">
+
+<param name="strategyName">the name of the strategy to define; this should use only   letters [a-zA-Z] with no spaces or other punctuation.</param>
+
+<param name="functionDefinition">a string defining the body of a function in JavaScript.   For example: <code>return inDocument.getElementById(locator);</code></param>
+
+<comment>Defines a new function for Selenium to locate elements on the page.
+For example,
+if you define the strategy "foo", and someone runs click("foo=blah"), we'll
+run your function, passing you the string "blah", and click on the element 
+that your function
+returns, or throw an "Element not found" error if your function returns null.
+
+We'll pass three arguments to your function:
+<ul>
+<li>locator: the string the user passed in</li>
+<li>inWindow: the currently selected window</li>
+<li>inDocument: the currently selected document</li>
+</ul>
+The function must return null if the element can't be found.</comment>
+
+</function>
+
+<function name="pause">
+
+<param name="waitTime">the amount of time to sleep (in milliseconds)</param>
+
+<comment>Wait for the specified amount of time (in milliseconds)</comment>
+
+</function>
+
+<function name="break">
+
+<comment>Halt the currently running test, and wait for the user to press the Continue button.
+This command is useful for debugging, but be careful when using it, because it will
+force automated tests to hang until a user intervenes manually.</comment>
+
+</function>
+
+<function name="store">
+
+<param name="expression">the value to store</param>
+
+<param name="variableName">the name of a <a href="#storedVars">variable</a> in which the result is to be stored.</param>
+
+<comment>This command is a synonym for storeExpression.</comment>
+
+</function>
+
+<function name="echo">
+
+<param name="message">the message to print</param>
+
+<comment>Prints the specified message into the third table cell in your Selenese tables.
+Useful for debugging.</comment>
+
+</function>
+
+<function name="assertSelected">
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<param name="optionLocator">an option locator, typically just an option label (e.g. "John Smith")</param>
+
+<comment>Verifies that the selected option of a drop-down satisfies the optionSpecifier.  <i>Note that this command is deprecated; you should use assertSelectedLabel, assertSelectedValue, assertSelectedIndex, or assertSelectedId instead.</i>
+
+<p>See the select command for more information about option locators.</p></comment>
+
+</function>
+
+<function name="assertFailureOnNext">
+
+<param name="message">The failure message we should expect.  This command will fail if the wrong failure message appears.</param>
+
+<comment>Tell Selenium to expect a failure on the next command execution.</comment>
+
+</function>
+
+<function name="assertErrorOnNext">
+
+<param name="message">The error message we should expect.  This command will fail if the wrong error message appears.</param>
+
+<comment>Tell Selenium to expect an error on the next command execution.</comment>
+
+</function>
+
+</apidoc>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/iedoc.xml
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/iedoc.xml	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/iedoc.xml	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,1469 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<apidoc>
+
+<top>Defines an object that runs Selenium commands.
+
+<h3><a name="locators"></a>Element Locators</h3>
+<p>
+Element Locators tell Selenium which HTML element a command refers to.
+The format of a locator is:</p>
+<blockquote>
+<em>locatorType</em><strong>=</strong><em>argument</em>
+</blockquote>
+
+<p>
+We support the following strategies for locating elements:
+</p>
+
+<ul>
+<li><strong>identifier</strong>=<em>id</em>: 
+Select the element with the specified &#064;id attribute. If no match is
+found, select the first element whose &#064;name attribute is <em>id</em>.
+(This is normally the default; see below.)</li>
+<li><strong>id</strong>=<em>id</em>:
+Select the element with the specified &#064;id attribute.</li>
+
+<li><strong>name</strong>=<em>name</em>:
+Select the first element with the specified &#064;name attribute.
+<ul class="first last simple">
+<li>username</li>
+<li>name=username</li>
+</ul>
+
+<p>The name may optionally be followed by one or more <em>element-filters</em>, separated from the name by whitespace.  If the <em>filterType</em> is not specified, <strong>value</strong> is assumed.</p>
+
+<ul class="first last simple">
+<li>name=flavour value=chocolate</li>
+</ul>
+</li>
+<li><strong>dom</strong>=<em>javascriptExpression</em>: 
+
+Find an element by evaluating the specified string.  This allows you to traverse the HTML Document Object
+Model using JavaScript.  Note that you must not return a value in this string; simply make it the last expression in the block.
+<ul class="first last simple">
+<li>dom=document.forms['myForm'].myDropdown</li>
+<li>dom=document.images[56]</li>
+<li>dom=function foo() { return document.links[1]; }; foo();</li>
+</ul>
+
+</li>
+
+<li><strong>xpath</strong>=<em>xpathExpression</em>: 
+Locate an element using an XPath expression.
+<ul class="first last simple">
+<li>xpath=//img[&#064;alt='The image alt text']</li>
+<li>xpath=//table[&#064;id='table1']//tr[4]/td[2]</li>
+<li>xpath=//a[contains(&#064;href,'#id1')]</li>
+<li>xpath=//a[contains(&#064;href,'#id1')]/&#064;class</li>
+<li>xpath=(//table[&#064;class='stylee'])//th[text()='theHeaderText']/../td</li>
+<li>xpath=//input[&#064;name='name2' and &#064;value='yes']</li>
+<li>xpath=//*[text()="right"]</li>
+
+</ul>
+</li>
+<li><strong>link</strong>=<em>textPattern</em>:
+Select the link (anchor) element which contains text matching the
+specified <em>pattern</em>.
+<ul class="first last simple">
+<li>link=The link text</li>
+</ul>
+
+</li>
+
+<li><strong>css</strong>=<em>cssSelectorSyntax</em>:
+Select the element using css selectors. Please refer to <a href="http://www.w3.org/TR/REC-CSS2/selector.html">CSS2 selectors</a>, <a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">CSS3 selectors</a> for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package.
+<ul class="first last simple">
+<li>css=a[href="#id3"]</li>
+<li>css=span#firstChild + span</li>
+</ul>
+<p>Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after). </p>
+</li>
+</ul>
+
+<p>
+Without an explicit locator prefix, Selenium uses the following default
+strategies:
+</p>
+
+<ul class="simple">
+<li><strong>dom</strong>, for locators starting with &quot;document.&quot;</li>
+<li><strong>xpath</strong>, for locators starting with &quot;//&quot;</li>
+<li><strong>identifier</strong>, otherwise</li>
+</ul>
+
+<h3><a name="element-filters">Element Filters</a></h3>
+<blockquote>
+<p>Element filters can be used with a locator to refine a list of candidate elements.  They are currently used only in the 'name' element-locator.</p>
+<p>Filters look much like locators, ie.</p>
+<blockquote>
+<em>filterType</em><strong>=</strong><em>argument</em></blockquote>
+
+<p>Supported element-filters are:</p>
+<p><strong>value=</strong><em>valuePattern</em></p>
+<blockquote>
+Matches elements based on their values.  This is particularly useful for refining a list of similarly-named toggle-buttons.</blockquote>
+<p><strong>index=</strong><em>index</em></p>
+<blockquote>
+Selects a single element based on its position in the list (offset from zero).</blockquote>
+</blockquote>
+
+<h3><a name="patterns"></a>String-match Patterns</h3>
+
+<p>
+Various Pattern syntaxes are available for matching string values:
+</p>
+<ul>
+<li><strong>glob:</strong><em>pattern</em>:
+Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a
+kind of limited regular-expression syntax typically used in command-line
+shells. In a glob pattern, "*" represents any sequence of characters, and "?"
+represents any single character. Glob patterns match against the entire
+string.</li>
+<li><strong>regexp:</strong><em>regexp</em>:
+Match a string using a regular-expression. The full power of JavaScript
+regular-expressions is available.</li>
+<li><strong>exact:</strong><em>string</em>:
+
+Match a string exactly, verbatim, without any of that fancy wildcard
+stuff.</li>
+</ul>
+<p>
+If no pattern prefix is specified, Selenium assumes that it's a "glob"
+pattern.
+</p></top>
+
+<function name="click">
+
+<param name="locator">an element locator</param>
+
+<comment>Clicks on a link, button, checkbox or radio button. If the click action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.</comment>
+
+</function>
+
+<function name="doubleClick">
+
+<param name="locator">an element locator</param>
+
+<comment>Double clicks on a link, button, checkbox or radio button. If the double click action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.</comment>
+
+</function>
+
+<function name="clickAt">
+
+<param name="locator">an element locator</param>
+
+<param name="coordString">specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</param>
+
+<comment>Clicks on a link, button, checkbox or radio button. If the click action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.</comment>
+
+</function>
+
+<function name="doubleClickAt">
+
+<param name="locator">an element locator</param>
+
+<param name="coordString">specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</param>
+
+<comment>Doubleclicks on a link, button, checkbox or radio button. If the action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.</comment>
+
+</function>
+
+<function name="fireEvent">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="eventName">the event name, e.g. "focus" or "blur"</param>
+
+<comment>Explicitly simulate an event, to trigger the corresponding &quot;on<em>event</em>&quot;
+handler.</comment>
+
+</function>
+
+<function name="keyPress">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="keySequence">Either be a string("\" followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: "w", "\119".</param>
+
+<comment>Simulates a user pressing and releasing a key.</comment>
+
+</function>
+
+<function name="shiftKeyDown">
+
+<comment>Press the shift key and hold it down until doShiftUp() is called or a new page is loaded.</comment>
+
+</function>
+
+<function name="shiftKeyUp">
+
+<comment>Release the shift key.</comment>
+
+</function>
+
+<function name="metaKeyDown">
+
+<comment>Press the meta key and hold it down until doMetaUp() is called or a new page is loaded.</comment>
+
+</function>
+
+<function name="metaKeyUp">
+
+<comment>Release the meta key.</comment>
+
+</function>
+
+<function name="altKeyDown">
+
+<comment>Press the alt key and hold it down until doAltUp() is called or a new page is loaded.</comment>
+
+</function>
+
+<function name="altKeyUp">
+
+<comment>Release the alt key.</comment>
+
+</function>
+
+<function name="controlKeyDown">
+
+<comment>Press the control key and hold it down until doControlUp() is called or a new page is loaded.</comment>
+
+</function>
+
+<function name="controlKeyUp">
+
+<comment>Release the control key.</comment>
+
+</function>
+
+<function name="keyDown">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="keySequence">Either be a string("\" followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: "w", "\119".</param>
+
+<comment>Simulates a user pressing a key (without releasing it yet).</comment>
+
+</function>
+
+<function name="keyUp">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="keySequence">Either be a string("\" followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: "w", "\119".</param>
+
+<comment>Simulates a user releasing a key.</comment>
+
+</function>
+
+<function name="mouseOver">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Simulates a user hovering a mouse over the specified element.</comment>
+
+</function>
+
+<function name="mouseOut">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Simulates a user moving the mouse pointer away from the specified element.</comment>
+
+</function>
+
+<function name="mouseDown">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Simulates a user pressing the mouse button (without releasing it yet) on
+the specified element.</comment>
+
+</function>
+
+<function name="mouseDownAt">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="coordString">specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</param>
+
+<comment>Simulates a user pressing the mouse button (without releasing it yet) at
+the specified location.</comment>
+
+</function>
+
+<function name="mouseUp">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Simulates the event that occurs when the user releases the mouse button (i.e., stops
+holding the button down) on the specified element.</comment>
+
+</function>
+
+<function name="mouseUpAt">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="coordString">specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</param>
+
+<comment>Simulates the event that occurs when the user releases the mouse button (i.e., stops
+holding the button down) at the specified location.</comment>
+
+</function>
+
+<function name="mouseMove">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Simulates a user pressing the mouse button (without releasing it yet) on
+the specified element.</comment>
+
+</function>
+
+<function name="mouseMoveAt">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="coordString">specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</param>
+
+<comment>Simulates a user pressing the mouse button (without releasing it yet) on
+the specified element.</comment>
+
+</function>
+
+<function name="type">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="value">the value to type</param>
+
+<comment>Sets the value of an input field, as though you typed it in.
+
+<p>Can also be used to set the value of combo boxes, check boxes, etc. In these cases,
+value should be the value of the option selected, not the visible text.</p></comment>
+
+</function>
+
+<function name="typeKeys">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<param name="value">the value to type</param>
+
+<comment>Simulates keystroke events on the specified element, as though you typed the value key-by-key.
+
+<p>This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string;
+this is useful for dynamic UI widgets (like auto-completing combo boxes) that require explicit key events.</p>
+
+<p>Unlike the simple "type" command, which forces the specified value into the page directly, this command
+may or may not have any visible effect, even in cases where typing keys would normally have a visible effect.
+For example, if you use "typeKeys" on a form element, you may or may not see the results of what you typed in
+the field.</p>
+<p>In some cases, you may need to use the simple "type" command to set the value of the field and then the "typeKeys" command to
+send the keystroke events corresponding to what you just typed.</p></comment>
+
+</function>
+
+<function name="setSpeed">
+
+<param name="value">the number of milliseconds to pause after operation</param>
+
+<comment>Set execution speed (i.e., set the millisecond length of a delay which will follow each selenium operation).  By default, there is no such delay, i.e.,
+the delay is 0 milliseconds.</comment>
+
+</function>
+
+<function name="getSpeed">
+
+<comment>Get execution speed (i.e., get the millisecond length of the delay following each selenium operation).  By default, there is no such delay, i.e.,
+the delay is 0 milliseconds.
+
+See also setSpeed.</comment>
+
+</function>
+
+<function name="check">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Check a toggle-button (checkbox/radio)</comment>
+
+</function>
+
+<function name="uncheck">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Uncheck a toggle-button (checkbox/radio)</comment>
+
+</function>
+
+<function name="select">
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<param name="optionLocator">an option locator (a label by default)</param>
+
+<comment>Select an option from a drop-down using an option locator.
+
+<p>
+Option locators provide different ways of specifying options of an HTML
+Select element (e.g. for selecting a specific option, or for asserting
+that the selected option satisfies a specification). There are several
+forms of Select Option Locator.
+</p>
+<ul>
+<li><strong>label</strong>=<em>labelPattern</em>:
+matches options based on their labels, i.e. the visible text. (This
+is the default.)
+<ul class="first last simple">
+<li>label=regexp:^[Oo]ther</li>
+</ul>
+</li>
+<li><strong>value</strong>=<em>valuePattern</em>:
+matches options based on their values.
+<ul class="first last simple">
+<li>value=other</li>
+</ul>
+
+
+</li>
+<li><strong>id</strong>=<em>id</em>:
+
+matches options based on their ids.
+<ul class="first last simple">
+<li>id=option1</li>
+</ul>
+</li>
+<li><strong>index</strong>=<em>index</em>:
+matches an option based on its index (offset from zero).
+<ul class="first last simple">
+
+<li>index=2</li>
+</ul>
+</li>
+</ul>
+<p>
+If no option locator prefix is provided, the default behaviour is to match on <strong>label</strong>.
+</p></comment>
+
+</function>
+
+<function name="addSelection">
+
+<param name="locator">an <a href="#locators">element locator</a> identifying a multi-select box</param>
+
+<param name="optionLocator">an option locator (a label by default)</param>
+
+<comment>Add a selection to the set of selected options in a multi-select element using an option locator.
+
+ at see #doSelect for details of option locators</comment>
+
+</function>
+
+<function name="removeSelection">
+
+<param name="locator">an <a href="#locators">element locator</a> identifying a multi-select box</param>
+
+<param name="optionLocator">an option locator (a label by default)</param>
+
+<comment>Remove a selection from the set of selected options in a multi-select element using an option locator.
+
+ at see #doSelect for details of option locators</comment>
+
+</function>
+
+<function name="removeAllSelections">
+
+<param name="locator">an <a href="#locators">element locator</a> identifying a multi-select box</param>
+
+<comment>Unselects all of the selected options in a multi-select element.</comment>
+
+</function>
+
+<function name="submit">
+
+<param name="formLocator">an <a href="#locators">element locator</a> for the form you want to submit</param>
+
+<comment>Submit the specified form. This is particularly useful for forms without
+submit buttons, e.g. single-input "Search" forms.</comment>
+
+</function>
+
+<function name="open">
+
+<param name="url">the URL to open; may be relative or absolute</param>
+
+<comment>Opens an URL in the test frame. This accepts both relative and absolute
+URLs.
+
+The &quot;open&quot; command waits for the page to load before proceeding,
+ie. the &quot;AndWait&quot; suffix is implicit.
+
+<em>Note</em>: The URL must be on the same domain as the runner HTML
+due to security restrictions in the browser (Same Origin Policy). If you
+need to open an URL on another domain, use the Selenium Server to start a
+new browser session on that domain.</comment>
+
+</function>
+
+<function name="openWindow">
+
+<param name="url">the URL to open, which can be blank</param>
+
+<param name="windowID">the JavaScript window ID of the window to select</param>
+
+<comment>Opens a popup window (if a window with that ID isn't already open).
+After opening the window, you'll need to select it using the selectWindow
+command.
+
+<p>This command can also be a useful workaround for bug SEL-339.  In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p></comment>
+
+</function>
+
+<function name="selectWindow">
+
+<param name="windowID">the JavaScript window ID of the window to select</param>
+
+<comment>Selects a popup window; once a popup window has been selected, all
+commands go to that window. To select the main window again, use null
+as the target.
+
+<p>Note that there is a big difference between a window's internal JavaScript "name" property
+and the "title" of a given window's document (which is normally what you actually see, as an end user,
+in the title bar of the window).  The "name" is normally invisible to the end-user; it's the second 
+parameter "windowName" passed to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag)
+(which selenium intercepts).</p>
+
+<p>Selenium has several strategies for finding the window object referred to by the "windowID" parameter.</p>
+
+<p>1.) if windowID is null, (or the string "null") then it is assumed the user is referring to the original window instantiated by the browser).</p>
+<p>2.) if the value of the "windowID" parameter is a JavaScript variable name in the current application window, then it is assumed
+that this variable contains the return value from a call to the JavaScript window.open() method.</p>
+<p>3.) Otherwise, selenium looks in a hash it maintains that maps string names to window "names".</p>
+<p>4.) If <i>that</i> fails, we'll try looping over all of the known windows to try to find the appropriate "title".
+Since "title" is not necessarily unique, this may have unexpected behavior.</p>
+
+<p>If you're having trouble figuring out what is the name of a window that you want to manipulate, look at the selenium log messages
+which identify the names of windows created via window.open (and therefore intercepted by selenium).  You will see messages
+like the following for each window as it is opened:</p>
+
+<p><code>debug: window.open call intercepted; window ID (which you can use with selectWindow()) is "myNewWindow"</code></p>
+
+<p>In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+(This is bug SEL-339.)  In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p></comment>
+
+</function>
+
+<function name="selectFrame">
+
+<param name="locator">an <a href="#locators">element locator</a> identifying a frame or iframe</param>
+
+<comment>Selects a frame within the current window.  (You may invoke this command
+multiple times to select nested frames.)  To select the parent frame, use
+"relative=parent" as a locator; to select the top frame, use "relative=top".
+You can also select a frame by its 0-based index number; select the first frame with
+"index=0", or the third frame with "index=2".
+
+<p>You may also use a DOM expression to identify the frame you want directly,
+like this: <code>dom=frames["main"].frames["subframe"]</code></p></comment>
+
+</function>
+
+<function name="getWhetherThisFrameMatchFrameExpression">
+
+<return type="boolean">true if the new frame is this code's window</return>
+
+<param name="currentFrameString">starting frame</param>
+
+<param name="target">new frame (which might be relative to the current one)</param>
+
+<comment>Determine whether current/locator identify the frame containing this running code.
+
+<p>This is useful in proxy injection mode, where this code runs in every
+browser frame and window, and sometimes the selenium server needs to identify
+the "current" frame.  In this case, when the test calls selectFrame, this
+routine is called for each frame to figure out which one has been selected.
+The selected frame will return true, while all others will return false.</p></comment>
+
+</function>
+
+<function name="getWhetherThisWindowMatchWindowExpression">
+
+<return type="boolean">true if the new window is this code's window</return>
+
+<param name="currentWindowString">starting window</param>
+
+<param name="target">new window (which might be relative to the current one, e.g., "_parent")</param>
+
+<comment>Determine whether currentWindowString plus target identify the window containing this running code.
+
+<p>This is useful in proxy injection mode, where this code runs in every
+browser frame and window, and sometimes the selenium server needs to identify
+the "current" window.  In this case, when the test calls selectWindow, this
+routine is called for each window to figure out which one has been selected.
+The selected window will return true, while all others will return false.</p></comment>
+
+</function>
+
+<function name="waitForPopUp">
+
+<param name="windowID">the JavaScript window ID of the window that will appear</param>
+
+<param name="timeout">a timeout in milliseconds, after which the action will return with an error</param>
+
+<comment>Waits for a popup window to appear and load up.</comment>
+
+</function>
+
+<function name="chooseCancelOnNextConfirmation">
+
+<comment>By default, Selenium's overridden window.confirm() function will
+return true, as if the user had manually clicked OK; after running
+this command, the next call to confirm() will return false, as if
+the user had clicked Cancel.  Selenium will then resume using the
+default behavior for future confirmations, automatically returning 
+true (OK) unless/until you explicitly call this command for each
+confirmation.</comment>
+
+</function>
+
+<function name="chooseOkOnNextConfirmation">
+
+<comment>Undo the effect of calling chooseCancelOnNextConfirmation.  Note
+that Selenium's overridden window.confirm() function will normally automatically
+return true, as if the user had manually clicked OK, so you shouldn't
+need to use this command unless for some reason you need to change
+your mind prior to the next confirmation.  After any confirmation, Selenium will resume using the
+default behavior for future confirmations, automatically returning 
+true (OK) unless/until you explicitly call chooseCancelOnNextConfirmation for each
+confirmation.</comment>
+
+</function>
+
+<function name="answerOnNextPrompt">
+
+<param name="answer">the answer to give in response to the prompt pop-up</param>
+
+<comment>Instructs Selenium to return the specified answer string in response to
+the next JavaScript prompt [window.prompt()].</comment>
+
+</function>
+
+<function name="goBack">
+
+<comment>Simulates the user clicking the "back" button on their browser.</comment>
+
+</function>
+
+<function name="refresh">
+
+<comment>Simulates the user clicking the "Refresh" button on their browser.</comment>
+
+</function>
+
+<function name="close">
+
+<comment>Simulates the user clicking the "close" button in the titlebar of a popup
+window or tab.</comment>
+
+</function>
+
+<function name="isAlertPresent">
+
+<return type="boolean">true if there is an alert</return>
+
+<comment>Has an alert occurred?
+
+<p>
+This function never throws an exception
+</p></comment>
+
+</function>
+
+<function name="isPromptPresent">
+
+<return type="boolean">true if there is a pending prompt</return>
+
+<comment>Has a prompt occurred?
+
+<p>
+This function never throws an exception
+</p></comment>
+
+</function>
+
+<function name="isConfirmationPresent">
+
+<return type="boolean">true if there is a pending confirmation</return>
+
+<comment>Has confirm() been called?
+
+<p>
+This function never throws an exception
+</p></comment>
+
+</function>
+
+<function name="getAlert">
+
+<return type="string">The message of the most recent JavaScript alert</return>
+
+<comment>Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts.
+
+<p>Getting an alert has the same effect as manually clicking OK. If an
+alert is generated but you do not get/verify it, the next Selenium action
+will fail.</p>
+
+<p>NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert
+dialog.</p>
+
+<p>NOTE: Selenium does NOT support JavaScript alerts that are generated in a
+page's onload() event handler. In this case a visible dialog WILL be
+generated and Selenium will hang until someone manually clicks OK.</p></comment>
+
+</function>
+
+<function name="getConfirmation">
+
+<return type="string">the message of the most recent JavaScript confirmation dialog</return>
+
+<comment>Retrieves the message of a JavaScript confirmation dialog generated during
+the previous action.
+
+<p>
+By default, the confirm function will return true, having the same effect
+as manually clicking OK. This can be changed by prior execution of the
+chooseCancelOnNextConfirmation command. If an confirmation is generated
+but you do not get/verify it, the next Selenium action will fail.
+</p>
+
+<p>
+NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible
+dialog.
+</p>
+
+<p>
+NOTE: Selenium does NOT support JavaScript confirmations that are
+generated in a page's onload() event handler. In this case a visible
+dialog WILL be generated and Selenium will hang until you manually click
+OK.
+</p></comment>
+
+</function>
+
+<function name="getPrompt">
+
+<return type="string">the message of the most recent JavaScript question prompt</return>
+
+<comment>Retrieves the message of a JavaScript question prompt dialog generated during
+the previous action.
+
+<p>Successful handling of the prompt requires prior execution of the
+answerOnNextPrompt command. If a prompt is generated but you
+do not get/verify it, the next Selenium action will fail.</p>
+
+<p>NOTE: under Selenium, JavaScript prompts will NOT pop up a visible
+dialog.</p>
+
+<p>NOTE: Selenium does NOT support JavaScript prompts that are generated in a
+page's onload() event handler. In this case a visible dialog WILL be
+generated and Selenium will hang until someone manually clicks OK.</p></comment>
+
+</function>
+
+<function name="getLocation">
+
+<return type="string">the absolute URL of the current page</return>
+
+<comment>Gets the absolute URL of the current page.</comment>
+
+</function>
+
+<function name="getTitle">
+
+<return type="string">the title of the current page</return>
+
+<comment>Gets the title of the current page.</comment>
+
+</function>
+
+<function name="getBodyText">
+
+<return type="string">the entire text of the page</return>
+
+<comment>Gets the entire text of the page.</comment>
+
+</function>
+
+<function name="getValue">
+
+<return type="string">the element value, or "on/off" for checkbox/radio elements</return>
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Gets the (whitespace-trimmed) value of an input field (or anything else with a value parameter).
+For checkbox/radio elements, the value will be "on" or "off" depending on
+whether the element is checked or not.</comment>
+
+</function>
+
+<function name="getText">
+
+<return type="string">the text of the element</return>
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Gets the text of an element. This works for any element that contains
+text. This command uses either the textContent (Mozilla-like browsers) or
+the innerText (IE-like browsers) of the element, which is the rendered
+text shown to the user.</comment>
+
+</function>
+
+<function name="highlight">
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Briefly changes the backgroundColor of the specified element yellow.  Useful for debugging.</comment>
+
+</function>
+
+<function name="getEval">
+
+<return type="string">the results of evaluating the snippet</return>
+
+<param name="script">the JavaScript snippet to run</param>
+
+<comment>Gets the result of evaluating the specified JavaScript snippet.  The snippet may
+have multiple lines, but only the result of the last line will be returned.
+
+<p>Note that, by default, the snippet will run in the context of the "selenium"
+object itself, so <code>this</code> will refer to the Selenium object.  Use <code>window</code> to
+refer to the window of your application, e.g. <code>window.document.getElementById('foo')</code></p>
+
+<p>If you need to use
+a locator to refer to a single element in your application page, you can
+use <code>this.browserbot.findElement("id=foo")</code> where "id=foo" is your locator.</p></comment>
+
+</function>
+
+<function name="isChecked">
+
+<return type="boolean">true if the checkbox is checked, false otherwise</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to a checkbox or radio button</param>
+
+<comment>Gets whether a toggle-button (checkbox/radio) is checked.  Fails if the specified element doesn't exist or isn't a toggle-button.</comment>
+
+</function>
+
+<function name="getTable">
+
+<return type="string">the text from the specified cell</return>
+
+<param name="tableCellAddress">a cell address, e.g. "foo.1.4"</param>
+
+<comment>Gets the text from a cell of a table. The cellAddress syntax
+tableLocator.row.column, where row and column start at 0.</comment>
+
+</function>
+
+<function name="getSelectedLabels">
+
+<return type="string[]">an array of all selected option labels in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets all option labels (visible text) for selected options in the specified select or multi-select element.</comment>
+
+</function>
+
+<function name="getSelectedLabel">
+
+<return type="string">the selected option label in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets option label (visible text) for selected option in the specified select element.</comment>
+
+</function>
+
+<function name="getSelectedValues">
+
+<return type="string[]">an array of all selected option values in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets all option values (value attributes) for selected options in the specified select or multi-select element.</comment>
+
+</function>
+
+<function name="getSelectedValue">
+
+<return type="string">the selected option value in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets option value (value attribute) for selected option in the specified select element.</comment>
+
+</function>
+
+<function name="getSelectedIndexes">
+
+<return type="string[]">an array of all selected option indexes in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets all option indexes (option number, starting at 0) for selected options in the specified select or multi-select element.</comment>
+
+</function>
+
+<function name="getSelectedIndex">
+
+<return type="string">the selected option index in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets option index (option number, starting at 0) for selected option in the specified select element.</comment>
+
+</function>
+
+<function name="getSelectedIds">
+
+<return type="string[]">an array of all selected option IDs in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets all option element IDs for selected options in the specified select or multi-select element.</comment>
+
+</function>
+
+<function name="getSelectedId">
+
+<return type="string">the selected option ID in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets option element ID for selected option in the specified select element.</comment>
+
+</function>
+
+<function name="isSomethingSelected">
+
+<return type="boolean">true if some option has been selected, false otherwise</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Determines whether some option in a drop-down menu is selected.</comment>
+
+</function>
+
+<function name="getSelectOptions">
+
+<return type="string[]">an array of all option labels in the specified select drop-down</return>
+
+<param name="selectLocator">an <a href="#locators">element locator</a> identifying a drop-down menu</param>
+
+<comment>Gets all option labels in the specified select drop-down.</comment>
+
+</function>
+
+<function name="getAttribute">
+
+<return type="string">the value of the specified attribute</return>
+
+<param name="attributeLocator">an element locator followed by an &#064; sign and then the name of the attribute, e.g. "foo&#064;bar"</param>
+
+<comment>Gets the value of an element attribute.</comment>
+
+</function>
+
+<function name="isTextPresent">
+
+<return type="boolean">true if the pattern matches the text, false otherwise</return>
+
+<param name="pattern">a <a href="#patterns">pattern</a> to match with the text of the page</param>
+
+<comment>Verifies that the specified text pattern appears somewhere on the rendered page shown to the user.</comment>
+
+</function>
+
+<function name="isElementPresent">
+
+<return type="boolean">true if the element is present, false otherwise</return>
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Verifies that the specified element is somewhere on the page.</comment>
+
+</function>
+
+<function name="isVisible">
+
+<return type="boolean">true if the specified element is visible, false otherwise</return>
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Determines if the specified element is visible. An
+element can be rendered invisible by setting the CSS "visibility"
+property to "hidden", or the "display" property to "none", either for the
+element itself or one if its ancestors.  This method will fail if
+the element is not present.</comment>
+
+</function>
+
+<function name="isEditable">
+
+<return type="boolean">true if the input element is editable, false otherwise</return>
+
+<param name="locator">an <a href="#locators">element locator</a></param>
+
+<comment>Determines whether the specified input element is editable, ie hasn't been disabled.
+This method will fail if the specified element isn't an input element.</comment>
+
+</function>
+
+<function name="getAllButtons">
+
+<return type="string[]">the IDs of all buttons on the page</return>
+
+<comment>Returns the IDs of all buttons on the page.
+
+<p>If a given button has no ID, it will appear as "" in this array.</p></comment>
+
+</function>
+
+<function name="getAllLinks">
+
+<return type="string[]">the IDs of all links on the page</return>
+
+<comment>Returns the IDs of all links on the page.
+
+<p>If a given link has no ID, it will appear as "" in this array.</p></comment>
+
+</function>
+
+<function name="getAllFields">
+
+<return type="string[]">the IDs of all field on the page</return>
+
+<comment>Returns the IDs of all input fields on the page.
+
+<p>If a given field has no ID, it will appear as "" in this array.</p></comment>
+
+</function>
+
+<function name="getAttributeFromAllWindows">
+
+<return type="string[]">the set of values of this attribute from all known windows.</return>
+
+<param name="attributeName">name of an attribute on the windows</param>
+
+<comment>Returns every instance of some attribute from all known windows.</comment>
+
+</function>
+
+<function name="dragdrop">
+
+<param name="locator">an element locator</param>
+
+<param name="movementsString">offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"</param>
+
+<comment>deprecated - use dragAndDrop instead</comment>
+
+</function>
+
+<function name="setMouseSpeed">
+
+<param name="pixels">the number of pixels between "mousemove" events</param>
+
+<comment>Configure the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+<p>Setting this value to 0 means that we'll send a "mousemove" event to every single pixel
+in between the start location and the end location; that can be very slow, and may
+cause some browsers to force the JavaScript to timeout.</p>
+
+<p>If the mouse speed is greater than the distance between the two dragged objects, we'll
+just send one "mousemove" at the start location and then one final one at the end location.</p></comment>
+
+</function>
+
+<function name="getMouseSpeed">
+
+<return type="number">the number of pixels between "mousemove" events during dragAndDrop commands (default=10)</return>
+
+<comment>Returns the number of pixels between "mousemove" events during dragAndDrop commands (default=10).</comment>
+
+</function>
+
+<function name="dragAndDrop">
+
+<param name="locator">an element locator</param>
+
+<param name="movementsString">offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"</param>
+
+<comment>Drags an element a certain distance and then drops it</comment>
+
+</function>
+
+<function name="dragAndDropToObject">
+
+<param name="locatorOfObjectToBeDragged">an element to be dragged</param>
+
+<param name="locatorOfDragDestinationObject">an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged  is dropped</param>
+
+<comment>Drags an element and drops it on another element</comment>
+
+</function>
+
+<function name="windowFocus">
+
+<comment>Gives focus to the currently selected window</comment>
+
+</function>
+
+<function name="windowMaximize">
+
+<comment>Resize currently selected window to take up the entire screen</comment>
+
+</function>
+
+<function name="getAllWindowIds">
+
+<return type="string[]">the IDs of all windows that the browser knows about.</return>
+
+<comment>Returns the IDs of all windows that the browser knows about.</comment>
+
+</function>
+
+<function name="getAllWindowNames">
+
+<return type="string[]">the names of all windows that the browser knows about.</return>
+
+<comment>Returns the names of all windows that the browser knows about.</comment>
+
+</function>
+
+<function name="getAllWindowTitles">
+
+<return type="string[]">the titles of all windows that the browser knows about.</return>
+
+<comment>Returns the titles of all windows that the browser knows about.</comment>
+
+</function>
+
+<function name="getHtmlSource">
+
+<return type="string">the entire HTML source</return>
+
+<comment>Returns the entire HTML source between the opening and
+closing "html" tags.</comment>
+
+</function>
+
+<function name="setCursorPosition">
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an input element or textarea</param>
+
+<param name="position">the numerical position of the cursor in the field; position should be 0 to move the position to the beginning of the field.  You can also set the cursor to -1 to move it to the end of the field.</param>
+
+<comment>Moves the text cursor to the specified position in the given input element or textarea.
+This method will fail if the specified element isn't an input element or textarea.</comment>
+
+</function>
+
+<function name="getElementIndex">
+
+<return type="number">of relative index of the element to its parent (starting from 0)</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element</param>
+
+<comment>Get the relative index of an element to its parent (starting from 0). The comment node and empty text node
+will be ignored.</comment>
+
+</function>
+
+<function name="isOrdered">
+
+<return type="boolean">true if element1 is the previous sibling of element2, false otherwise</return>
+
+<param name="locator1">an <a href="#locators">element locator</a> pointing to the first element</param>
+
+<param name="locator2">an <a href="#locators">element locator</a> pointing to the second element</param>
+
+<comment>Check if these two elements have same parent and are ordered siblings in the DOM. Two same elements will
+not be considered ordered.</comment>
+
+</function>
+
+<function name="getElementPositionLeft">
+
+<return type="number">of pixels from the edge of the frame.</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element OR an element itself</param>
+
+<comment>Retrieves the horizontal position of an element</comment>
+
+</function>
+
+<function name="getElementPositionTop">
+
+<return type="number">of pixels from the edge of the frame.</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element OR an element itself</param>
+
+<comment>Retrieves the vertical position of an element</comment>
+
+</function>
+
+<function name="getElementWidth">
+
+<return type="number">width of an element in pixels</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element</param>
+
+<comment>Retrieves the width of an element</comment>
+
+</function>
+
+<function name="getElementHeight">
+
+<return type="number">height of an element in pixels</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element</param>
+
+<comment>Retrieves the height of an element</comment>
+
+</function>
+
+<function name="getCursorPosition">
+
+<return type="number">the numerical position of the cursor in the field</return>
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an input element or textarea</param>
+
+<comment>Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers.
+
+<p>Specifically, if the cursor/selection has been cleared by JavaScript, this command will tend to
+return the position of the last location of the cursor, even though the cursor is now gone from the page.  This is filed as <a href="http://jira.openqa.org/browse/SEL-243">SEL-243</a>.</p>
+This method will fail if the specified element isn't an input element or textarea, or there is no cursor in the element.</comment>
+
+</function>
+
+<function name="getExpression">
+
+<return type="string">the value passed in</return>
+
+<param name="expression">the value to return</param>
+
+<comment>Returns the specified expression.
+
+<p>This is useful because of JavaScript preprocessing.
+It is used to generate commands like assertExpression and waitForExpression.</p></comment>
+
+</function>
+
+<function name="getXpathCount">
+
+<return type="number">the number of nodes that match the specified xpath</return>
+
+<param name="xpath">the xpath expression to evaluate. do NOT wrap this expression in a 'count()' function; we will do that for you.</param>
+
+<comment>Returns the number of nodes that match the specified xpath, eg. "//table" would give
+the number of tables.</comment>
+
+</function>
+
+<function name="assignId">
+
+<param name="locator">an <a href="#locators">element locator</a> pointing to an element</param>
+
+<param name="identifier">a string to be used as the ID of the specified element</param>
+
+<comment>Temporarily sets the "id" attribute of the specified element, so you can locate it in the future
+using its ID rather than a slow/complicated XPath.  This ID will disappear once the page is
+reloaded.</comment>
+
+</function>
+
+<function name="allowNativeXpath">
+
+<param name="allow">boolean, true means we'll prefer to use native XPath; false means we'll only use JS XPath</param>
+
+<comment>Specifies whether Selenium should use the native in-browser implementation
+of XPath (if any native version is available); if you pass "false" to
+this function, we will always use our pure-JavaScript xpath library.
+Using the pure-JS xpath library can improve the consistency of xpath
+element locators between different browser vendors, but the pure-JS
+version is much slower than the native implementations.</comment>
+
+</function>
+
+<function name="waitForCondition">
+
+<param name="script">the JavaScript snippet to run</param>
+
+<param name="timeout">a timeout in milliseconds, after which this command will return with an error</param>
+
+<comment>Runs the specified JavaScript snippet repeatedly until it evaluates to "true".
+The snippet may have multiple lines, but only the result of the last line
+will be considered.
+
+<p>Note that, by default, the snippet will be run in the runner's test window, not in the window
+of your application.  To get the window of your application, you can use
+the JavaScript snippet <code>selenium.browserbot.getCurrentWindow()</code>, and then
+run your JavaScript in there</p></comment>
+
+</function>
+
+<function name="setTimeout">
+
+<param name="timeout">a timeout in milliseconds, after which the action will return with an error</param>
+
+<comment>Specifies the amount of time that Selenium will wait for actions to complete.
+
+<p>Actions that require waiting include "open" and the "waitFor*" actions.</p>
+The default timeout is 30 seconds.</comment>
+
+</function>
+
+<function name="waitForPageToLoad">
+
+<param name="timeout">a timeout in milliseconds, after which this command will return with an error</param>
+
+<comment>Waits for a new page to load.
+
+<p>You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc.
+(which are only available in the JS API).</p>
+
+<p>Selenium constantly keeps track of new pages loading, and sets a "newPageLoaded"
+flag when it first notices a page load.  Running any other Selenium command after
+turns the flag to false.  Hence, if you want to wait for a page to load, you must
+wait immediately after a Selenium command that caused a page-load.</p></comment>
+
+</function>
+
+<function name="waitForFrameToLoad">
+
+<param name="frameAddress">FrameAddress from the server side</param>
+
+<param name="timeout">a timeout in milliseconds, after which this command will return with an error</param>
+
+<comment>Waits for a new frame to load.
+
+<p>Selenium constantly keeps track of new pages and frames loading, 
+and sets a "newPageLoaded" flag when it first notices a page load.</p>
+
+See waitForPageToLoad for more information.</comment>
+
+</function>
+
+<function name="getCookie">
+
+<return type="string">all cookies of the current page under test</return>
+
+<comment>Return all cookies of the current page under test.</comment>
+
+</function>
+
+<function name="createCookie">
+
+<param name="nameValuePair">name and value of the cookie in a format "name=value"</param>
+
+<param name="optionsString">options for the cookie. Currently supported options include 'path' and 'max_age'.      the optionsString's format is "path=/path/, max_age=60". The order of options are irrelevant, the unit      of the value of 'max_age' is second.</param>
+
+<comment>Create a new cookie whose path and domain are same with those of current page
+under test, unless you specified a path for this cookie explicitly.</comment>
+
+</function>
+
+<function name="deleteCookie">
+
+<param name="name">the name of the cookie to be deleted</param>
+
+<param name="path">the path property of the cookie to be deleted</param>
+
+<comment>Delete a named cookie with specified path.</comment>
+
+</function>
+
+<function name="setBrowserLogLevel">
+
+<param name="logLevel">one of the following: "debug", "info", "warn", "error" or "off"</param>
+
+<comment>Sets the threshold for browser-side logging messages; log messages beneath this threshold will be discarded.
+Valid logLevel strings are: "debug", "info", "warn", "error" or "off".
+To see the browser logs, you need to
+either show the log window in GUI mode, or enable browser-side logging in Selenium RC.</comment>
+
+</function>
+
+<function name="runScript">
+
+<param name="script">the JavaScript snippet to run</param>
+
+<comment>Creates a new "script" tag in the body of the current test window, and 
+adds the specified text into the body of the command.  Scripts run in
+this way can often be debugged more easily than scripts executed using
+Selenium's "getEval" command.  Beware that JS exceptions thrown in these script
+tags aren't managed by Selenium, so you should probably wrap your script
+in try/catch blocks if there is any chance that the script will throw
+an exception.</comment>
+
+</function>
+
+<function name="addLocationStrategy">
+
+<param name="strategyName">the name of the strategy to define; this should use only   letters [a-zA-Z] with no spaces or other punctuation.</param>
+
+<param name="functionDefinition">a string defining the body of a function in JavaScript.   For example: <code>return inDocument.getElementById(locator);</code></param>
+
+<comment>Defines a new function for Selenium to locate elements on the page.
+For example,
+if you define the strategy "foo", and someone runs click("foo=blah"), we'll
+run your function, passing you the string "blah", and click on the element 
+that your function
+returns, or throw an "Element not found" error if your function returns null.
+
+We'll pass three arguments to your function:
+<ul>
+<li>locator: the string the user passed in</li>
+<li>inWindow: the currently selected window</li>
+<li>inDocument: the currently selected document</li>
+</ul>
+The function must return null if the element can't be found.</comment>
+
+</function>
+
+<function name="setContext">
+
+<param name="context">the message to be sent to the browser</param>
+
+<comment>Writes a message to the status bar and adds a note to the browser-side
+log.</comment>
+
+</function>
+
+<function name="captureScreenshot">
+
+<param name="filename">the absolute path to the file to be written, e.g. "c:\blah\screenshot.png"</param>
+
+<comment>Captures a PNG screenshot to the specified file.</comment>
+
+</function>
+
+</apidoc>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/cssQuery-p.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/cssQuery-p.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/cssQuery-p.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,6 @@
+/*
+	cssQuery, version 2.0.2 (2005-08-19)
+	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7 x=6(){7 1D="2.0.2";7 C=/\\s*,\\s*/;7 x=6(s,A){33{7 m=[];7 u=1z.32.2c&&!A;7 b=(A)?(A.31==22)?A:[A]:[1g];7 1E=18(s).1l(C),i;9(i=0;i<1E.y;i++){s=1y(1E[i]);8(U&&s.Z(0,3).2b("")==" *#"){s=s.Z(2);A=24([],b,s[1])}1A A=b;7 j=0,t,f,a,c="";H(j<s.y){t=s[j++];f=s[j++];c+=t+f;a="";8(s[j]=="("){H(s[j++]!=")")a+=s[j];a=a.Z(0,-1);c+="("+a+")"}A=(u&&V[c])?V[c]:21(A,t,f,a);8(u)V[c]=A}m=m.30(A)}2a x.2d;5 m}2Z(e){x.2d=e;5[]}};x.1Z=6(){5"6 x() {\\n  [1D "+1D+"]\\n}"};7 V={};x.2c=L;x.2Y=6(s){8(s){s=1y(s).2b("");2a V[s]}1A V={}};7 29={};7 19=L;x.15=6(n,s){8(19)1i("s="+1U(s));29[n]=12 s()};x.2X=6(c){5 c?1i(c):o};7 D={};7 h={};7 q={P:/\\[([\\w-]+(\\|[\\w-]+)?)\\s*(\\W?=)?\\s*([^\\]]*)\\]/};7 T=[];D[" "]=6(r,f,t,n){7 e,i,j;9(i=0;i<f.y;i++){7 s=X(f[i],t,n);9(j=0;(e=s[j]);j++){8(M(e)&&14(e,n))r.z(e)}}};D["#"]=6(r,f,i){7 e,j;9(j=0;(e=f[j]);j++)8(e.B==i)r.z(e)};D["."]=6(r,f,c){c=12 1t("(^|\\\\s)"+c+"(\\\\s|$)");7 e,i;9(i=0;(e=f[i]);i++)8(c.l(e.1V))r.z(e)};D[":"]=6(r,f,p,a){7 t=h[p],e,i;8(t)9(i=0;(e=f[i]);i++)8(t(e,a))r.z(e)};h["2W"]=6(e){7 d=Q(e);8(d.1C)9(7 i=0;i<d.1C.y;i++){8(d.1C[i]==e)5 K}};h["2V"]=6(e){};7 M=6(e){5(e&&e.1c==1&&e.1f!="!")?e:23};7 16=6(e){H(e&&(e=e.2U)&&!M(e))28;5 e};7 G=6(e){H(e&&(e=e.2T)&&!M(e))28;5 e};7 1r=6(e){5 M(e.27)||G(e.27)};7 1P=6(e){5 M(e.26)||16(e.26)};7 1o=6(e){7 c=[];e=1r(e);H(e){c.z(e);e=G(e)}5 c};7 U=K;7 1h=6(e){7 d=Q(e);5(2S d.25=="2R")?/\\.1J$/i.l(d.2Q):2P(d.25=="2O 2N")};7 Q=6(e){5 e.2M||e.1g};7 X=6(e,t){5(t=="*"&&e.1B)?e.1B:e.X(t)};7 17=6(e,t,n){8(t=="*")5 M(e);8(!14(e,n))5 L;8(!1h(e))t=t.2L();5 e.1f==t};7 14=6(e,n){5!n||(n=="*")||(e.2K==n)};7 1e=6(e){5 e.1G};6 24(r,f,B){7 m,i,j;9(i=0;i<f.y;i++){8(m=f[i].1B.2J(B)){8(m.B==B)r.z(m);1A 8(m.y!=23){9(j=0;j<m.y;j++){8(m[j].B==B)r.z(m[j])}}}}5 r};8(![].z)22.2I.z=6(){9(7 i=0;i<1z.y;i++){o[o.y]=1z[i]}5 o.y};7 N=/\\|/;6 21(A,t,f,a){8(N.l(f)){f=f.1l(N);a=f[0];f=f[1]}7 r=[];8(D[t]){D[t](r,A,f,a)}5 r};7 S=/^[^\\s>+~]/;7 20=/[\\s#.:>+~()@]|[^\\s#.:>+~()@]+/g;6 1y(s){8(S.l(s))s=" "+s;5 s.P(20)||[]};7 W=/\\s*([\\s>+~(),]|^|$)\\s*/g;7 I=/([\\s>+~,]|[^(]\\+|^)([#.:@])/g;7 18=6(s){5 s.O(W,"$1").O(I,"$1*$2")};7 1u={1Z:6(){5"\'"},P:/^(\'[^\']*\')|("[^"]*")$/,l:6(s){5 o.P.l(s)},1S:6(s){5 o.l(s)?s:o+s+o},1Y:6(s){5 o.l(s)?s.Z(1,-1):s}};7 1s=6(t){5 1u.1Y(t)};7 E=/([\\/()[\\]?{}|*+-])/g;6 R(s){5 s.O(E,"\\\\$1")};x.15("1j-2H",6(){D[">"]=6(r,f,t,n){7 e,i,j;9(i=0;i<f.y;i++){7 s=1o(f[i]);9(j=0;(e=s[j]);j++)8(17(e,t,n))r.z(e)}};D["+"]=6(r,f,t,n){9(7 i=0;i<f.y;i++){7 e=G(f[i]);8(e&&17(e,t,n))r.z(e)}};D["@"]=6(r,f,a){7 t=T[a].l;7 e,i;9(i=0;(e=f[i]);i++)8(t(e))r.z(e)};h["2G-10"]=6(e){5!16(e)};h["1x"]=6(e,c){c=12 1t("^"+c,"i");H(e&&!e.13("1x"))e=e.1n;5 e&&c.l(e.13("1x"))};q.1X=/\\\\:/g;q.1w="@";q.J={};q.O=6(m,a,n,c,v){7 k=o.1w+m;8(!T[k]){a=o.1W(a,c||"",v||"");T[k]=a;T.z(a)}5 T[k].B};q.1Q=6(s){s=s.O(o.1X,"|");7 m;H(m=s.P(o.P)){7 r=o.O(m[0],m[1],m[2],m[3],m[4]);s=s.O(o.P,r)}5 s};q.1W=6(p,t,v){7 a={};a.B=o.1w+T.y;a.2F=p;t=o.J[t];t=t?t(o.13(p),1s(v)):L;a.l=12 2E("e","5 "+t);5 a};q.13=6(n){1d(n.2D()){F"B":5"e.B";F"2C":5"e.1V";F"9":5"e.2B";F"1T":8(U){5"1U((e.2A.P(/1T=\\\\1v?([^\\\\s\\\\1v]*)\\\\1v?/)||[])[1]||\'\')"}}5"e.13(\'"+n.O(N,":")+"\')"};q.J[""]=6(a){5 a};q.J["="]=6(a,v){5 a+"=="+1u.1S(v)};q.J["~="]=6(a,v){5"/(^| )"+R(v)+"( |$)/.l("+a+")"};q.J["|="]=6(a,v){5"/^"+R(v)+"(-|$)/.l("+a+")"};7 1R=18;18=6(s){5 1R(q.1Q(s))}});x.15("1j-2z",6(){D["~"]=6(r,f,t,n){7 e,i;9(i=0;(e=f[i]);i++){H(e=G(e)){8(17(e,t,n))r.z(e)}}};h["2y"]=6(e,t){t=12 1t(R(1s(t)));5 t.l(1e(e))};h["2x"]=6(e){5 e==Q(e).1H};h["2w"]=6(e){7 n,i;9(i=0;(n=e.1F[i]);i++){8(M(n)||n.1c==3)5 L}5 K};h["1N-10"]=6(e){5!G(e)};h["2v-10"]=6(e){e=e.1n;5 1r(e)==1P(e)};h["2u"]=6(e,s){7 n=x(s,Q(e));9(7 i=0;i<n.y;i++){8(n[i]==e)5 L}5 K};h["1O-10"]=6(e,a){5 1p(e,a,16)};h["1O-1N-10"]=6(e,a){5 1p(e,a,G)};h["2t"]=6(e){5 e.B==2s.2r.Z(1)};h["1M"]=6(e){5 e.1M};h["2q"]=6(e){5 e.1q===L};h["1q"]=6(e){5 e.1q};h["1L"]=6(e){5 e.1L};q.J["^="]=6(a,v){5"/^"+R(v)+"/.l("+a+")"};q.J["$="]=6(a,v){5"/"+R(v)+"$/.l("+a+")"};q.J["*="]=6(a,v){5"/"+R(v)+"/.l("+a+")"};6 1p(e,a,t){1d(a){F"n":5 K;F"2p":a="2n";1a;F"2o":a="2n+1"}7 1m=1o(e.1n);6 1k(i){7 i=(t==G)?1m.y-i:i-1;5 1m[i]==e};8(!Y(a))5 1k(a);a=a.1l("n");7 m=1K(a[0]);7 s=1K(a[1]);8((Y(m)||m==1)&&s==0)5 K;8(m==0&&!Y(s))5 1k(s);8(Y(s))s=0;7 c=1;H(e=t(e))c++;8(Y(m)||m==1)5(t==G)?(c<=s):(s>=c);5(c%m)==s}});x.15("1j-2m",6(){U=1i("L;/*@2l at 8(@\\2k)U=K at 2j@*/");8(!U){X=6(e,t,n){5 n?e.2i("*",t):e.X(t)};14=6(e,n){5!n||(n=="*")||(e.2h==n)};1h=1g.1I?6(e){5/1J/i.l(Q(e).1I)}:6(e){5 Q(e).1H.1f!="2g"};1e=6(e){5 e.2f||e.1G||1b(e)};6 1b(e){7 t="",n,i;9(i=0;(n=e.1F[i]);i++){1d(n.1c){F 11:F 1:t+=1b(n);1a;F 3:t+=n.2e;1a}}5 t}}});19=K;5 x}();',62,190,'|||||return|function|var|if|for||||||||pseudoClasses||||test|||this||AttributeSelector|||||||cssQuery|length|push|fr|id||selectors||case|nextElementSibling|while||tests|true|false|thisElement||replace|match|getDocument|regEscape||attributeSelectors|isMSIE|cache||getElementsByTagName|isNaN|slice|child||new|getAttribute|compareNamespace|addModule|previousElementSibling|compareTagName|parseSelector|loaded|break|_0|nodeType|switch|getTextContent|tagName|document|isXML|eval|css|_1|split|ch|parentNode|childElements|nthChild|disabled|firstElementChild|getText|RegExp|Quote|x22|PREFIX|lang|_2|arguments|else|all|links|version|se|childNodes|innerText|documentElement|contentType|xml|parseInt|indeterminate|checked|last|nth|lastElementChild|parse|_3|add|href|String|className|create|NS_IE|remove|toString|ST|select|Array|null|_4|mimeType|lastChild|firstChild|continue|modules|delete|join|caching|error|nodeValue|textContent|HTML|prefix|getElementsByTagNameNS|end|x5fwin32|cc_on|standard||odd|even|enabled|hash|location|target|not|only|empty|root|contains|level3|outerHTML|htmlFor|class|toLowerCase|Function|name|first|level2|prototype|item|scopeName|toUpperCase|ownerDocument|Document|XML|Boolean|URL|unknown|typeof|nextSibling|previousSibling|visited|link|valueOf|clearCache|catch|concat|constructor|callee|try'.split('|'),0,{}))

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-level2.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-level2.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-level2.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,142 @@
+/*
+	cssQuery, version 2.0.2 (2005-08-19)
+	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+cssQuery.addModule("css-level2", function() {
+
+// -----------------------------------------------------------------------
+// selectors
+// -----------------------------------------------------------------------
+
+// child selector
+selectors[">"] = function($results, $from, $tagName, $namespace) {
+	var $element, i, j;
+	for (i = 0; i < $from.length; i++) {
+		var $subset = childElements($from[i]);
+		for (j = 0; ($element = $subset[j]); j++)
+			if (compareTagName($element, $tagName, $namespace))
+				$results.push($element);
+	}
+};
+
+// sibling selector
+selectors["+"] = function($results, $from, $tagName, $namespace) {
+	for (var i = 0; i < $from.length; i++) {
+		var $element = nextElementSibling($from[i]);
+		if ($element && compareTagName($element, $tagName, $namespace))
+			$results.push($element);
+	}
+};
+
+// attribute selector
+selectors["@"] = function($results, $from, $attributeSelectorID) {
+	var $test = attributeSelectors[$attributeSelectorID].test;
+	var $element, i;
+	for (i = 0; ($element = $from[i]); i++)
+		if ($test($element)) $results.push($element);
+};
+
+// -----------------------------------------------------------------------
+// pseudo-classes
+// -----------------------------------------------------------------------
+
+pseudoClasses["first-child"] = function($element) {
+	return !previousElementSibling($element);
+};
+
+pseudoClasses["lang"] = function($element, $code) {
+	$code = new RegExp("^" + $code, "i");
+	while ($element && !$element.getAttribute("lang")) $element = $element.parentNode;
+	return $element && $code.test($element.getAttribute("lang"));
+};
+
+// -----------------------------------------------------------------------
+//  attribute selectors
+// -----------------------------------------------------------------------
+
+// constants
+AttributeSelector.NS_IE = /\\:/g;
+AttributeSelector.PREFIX = "@";
+// properties
+AttributeSelector.tests = {};
+// methods
+AttributeSelector.replace = function($match, $attribute, $namespace, $compare, $value) {
+	var $key = this.PREFIX + $match;
+	if (!attributeSelectors[$key]) {
+		$attribute = this.create($attribute, $compare || "", $value || "");
+		// store the selector
+		attributeSelectors[$key] = $attribute;
+		attributeSelectors.push($attribute);
+	}
+	return attributeSelectors[$key].id;
+};
+AttributeSelector.parse = function($selector) {
+	$selector = $selector.replace(this.NS_IE, "|");
+	var $match;
+	while ($match = $selector.match(this.match)) {
+		var $replace = this.replace($match[0], $match[1], $match[2], $match[3], $match[4]);
+		$selector = $selector.replace(this.match, $replace);
+	}
+	return $selector;
+};
+AttributeSelector.create = function($propertyName, $test, $value) {
+	var $attributeSelector = {};
+	$attributeSelector.id = this.PREFIX + attributeSelectors.length;
+	$attributeSelector.name = $propertyName;
+	$test = this.tests[$test];
+	$test = $test ? $test(this.getAttribute($propertyName), getText($value)) : false;
+	$attributeSelector.test = new Function("e", "return " + $test);
+	return $attributeSelector;
+};
+AttributeSelector.getAttribute = function($name) {
+	switch ($name.toLowerCase()) {
+		case "id":
+			return "e.id";
+		case "class":
+			return "e.className";
+		case "for":
+			return "e.htmlFor";
+		case "href":
+			if (isMSIE) {
+				// IE always returns the full path not the fragment in the href attribute
+				//  so we RegExp it out of outerHTML. Opera does the same thing but there
+				//  is no way to get the original attribute.
+				return "String((e.outerHTML.match(/href=\\x22?([^\\s\\x22]*)\\x22?/)||[])[1]||'')";
+			}
+	}
+	return "e.getAttribute('" + $name.replace($NAMESPACE, ":") + "')";
+};
+
+// -----------------------------------------------------------------------
+//  attribute selector tests
+// -----------------------------------------------------------------------
+
+AttributeSelector.tests[""] = function($attribute) {
+	return $attribute;
+};
+
+AttributeSelector.tests["="] = function($attribute, $value) {
+	return $attribute + "==" + Quote.add($value);
+};
+
+AttributeSelector.tests["~="] = function($attribute, $value) {
+	return "/(^| )" + regEscape($value) + "( |$)/.test(" + $attribute + ")";
+};
+
+AttributeSelector.tests["|="] = function($attribute, $value) {
+	return "/^" + regEscape($value) + "(-|$)/.test(" + $attribute + ")";
+};
+
+// -----------------------------------------------------------------------
+//  parsing
+// -----------------------------------------------------------------------
+
+// override parseSelector to parse out attribute selectors
+var _parseSelector = parseSelector;
+parseSelector = function($selector) {
+	return _parseSelector(AttributeSelector.parse($selector));
+};
+
+}); // addModule

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-level3.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-level3.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-level3.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,150 @@
+/*
+	cssQuery, version 2.0.2 (2005-08-19)
+	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+/* Thanks to Bill Edney */
+
+cssQuery.addModule("css-level3", function() {
+
+// -----------------------------------------------------------------------
+// selectors
+// -----------------------------------------------------------------------
+
+// indirect sibling selector
+selectors["~"] = function($results, $from, $tagName, $namespace) {
+	var $element, i;
+	for (i = 0; ($element = $from[i]); i++) {
+		while ($element = nextElementSibling($element)) {
+			if (compareTagName($element, $tagName, $namespace))
+				$results.push($element);
+		}
+	}
+};
+
+// -----------------------------------------------------------------------
+// pseudo-classes
+// -----------------------------------------------------------------------
+
+// I'm hoping these pseudo-classes are pretty readable. Let me know if
+//  any need explanation.
+
+pseudoClasses["contains"] = function($element, $text) {
+	$text = new RegExp(regEscape(getText($text)));
+	return $text.test(getTextContent($element));
+};
+
+pseudoClasses["root"] = function($element) {
+	return $element == getDocument($element).documentElement;
+};
+
+pseudoClasses["empty"] = function($element) {
+	var $node, i;
+	for (i = 0; ($node = $element.childNodes[i]); i++) {
+		if (thisElement($node) || $node.nodeType == 3) return false;
+	}
+	return true;
+};
+
+pseudoClasses["last-child"] = function($element) {
+	return !nextElementSibling($element);
+};
+
+pseudoClasses["only-child"] = function($element) {
+	$element = $element.parentNode;
+	return firstElementChild($element) == lastElementChild($element);
+};
+
+pseudoClasses["not"] = function($element, $selector) {
+	var $negated = cssQuery($selector, getDocument($element));
+	for (var i = 0; i < $negated.length; i++) {
+		if ($negated[i] == $element) return false;
+	}
+	return true;
+};
+
+pseudoClasses["nth-child"] = function($element, $arguments) {
+	return nthChild($element, $arguments, previousElementSibling);
+};
+
+pseudoClasses["nth-last-child"] = function($element, $arguments) {
+	return nthChild($element, $arguments, nextElementSibling);
+};
+
+pseudoClasses["target"] = function($element) {
+	return $element.id == location.hash.slice(1);
+};
+
+// UI element states
+
+pseudoClasses["checked"] = function($element) {
+	return $element.checked;
+};
+
+pseudoClasses["enabled"] = function($element) {
+	return $element.disabled === false;
+};
+
+pseudoClasses["disabled"] = function($element) {
+	return $element.disabled;
+};
+
+pseudoClasses["indeterminate"] = function($element) {
+	return $element.indeterminate;
+};
+
+// -----------------------------------------------------------------------
+//  attribute selector tests
+// -----------------------------------------------------------------------
+
+AttributeSelector.tests["^="] = function($attribute, $value) {
+	return "/^" + regEscape($value) + "/.test(" + $attribute + ")";
+};
+
+AttributeSelector.tests["$="] = function($attribute, $value) {
+	return "/" + regEscape($value) + "$/.test(" + $attribute + ")";
+};
+
+AttributeSelector.tests["*="] = function($attribute, $value) {
+	return "/" + regEscape($value) + "/.test(" + $attribute + ")";
+};
+
+// -----------------------------------------------------------------------
+//  nth child support (Bill Edney)
+// -----------------------------------------------------------------------
+
+function nthChild($element, $arguments, $traverse) {
+	switch ($arguments) {
+		case "n": return true;
+		case "even": $arguments = "2n"; break;
+		case "odd": $arguments = "2n+1";
+	}
+
+	var $$children = childElements($element.parentNode);
+	function _checkIndex($index) {
+		var $index = ($traverse == nextElementSibling) ? $$children.length - $index : $index - 1;
+		return $$children[$index] == $element;
+	};
+
+	//	it was just a number (no "n")
+	if (!isNaN($arguments)) return _checkIndex($arguments);
+
+	$arguments = $arguments.split("n");
+	var $multiplier = parseInt($arguments[0]);
+	var $step = parseInt($arguments[1]);
+
+	if ((isNaN($multiplier) || $multiplier == 1) && $step == 0) return true;
+	if ($multiplier == 0 && !isNaN($step)) return _checkIndex($step);
+	if (isNaN($step)) $step = 0;
+
+	var $count = 1;
+	while ($element = $traverse($element)) $count++;
+
+	if (isNaN($multiplier) || $multiplier == 1)
+		return ($traverse == nextElementSibling) ? ($count <= $step) : ($step >= $count);
+
+	return ($count % $multiplier) == $step;
+};
+
+}); // addModule

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-standard.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-standard.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery-standard.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,53 @@
+/*
+	cssQuery, version 2.0.2 (2005-08-19)
+	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+cssQuery.addModule("css-standard", function() { // override IE optimisation
+
+// cssQuery was originally written as the CSS engine for IE7. It is
+//  optimised (in terms of size not speed) for IE so this module is
+//  provided separately to provide cross-browser support.
+
+// -----------------------------------------------------------------------
+// browser compatibility
+// -----------------------------------------------------------------------
+
+// sniff for Win32 Explorer
+isMSIE = eval("false;/*@cc_on at if(@\x5fwin32)isMSIE=true at end@*/");
+
+if (!isMSIE) {
+	getElementsByTagName = function($element, $tagName, $namespace) {
+		return $namespace ? $element.getElementsByTagNameNS("*", $tagName) :
+			$element.getElementsByTagName($tagName);
+	};
+
+	compareNamespace = function($element, $namespace) {
+		return !$namespace || ($namespace == "*") || ($element.prefix == $namespace);
+	};
+
+	isXML = document.contentType ? function($element) {
+		return /xml/i.test(getDocument($element).contentType);
+	} : function($element) {
+		return getDocument($element).documentElement.tagName != "HTML";
+	};
+
+	getTextContent = function($element) {
+		// mozilla || opera || other
+		return $element.textContent || $element.innerText || _getTextContent($element);
+	};
+
+	function _getTextContent($element) {
+		var $textContent = "", $node, i;
+		for (i = 0; ($node = $element.childNodes[i]); i++) {
+			switch ($node.nodeType) {
+				case 11: // document fragment
+				case 1: $textContent += _getTextContent($node); break;
+				case 3: $textContent += $node.nodeValue; break;
+			}
+		}
+		return $textContent;
+	};
+}
+}); // addModule

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/cssQuery/src/cssQuery.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,356 @@
+/*
+	cssQuery, version 2.0.2 (2005-08-19)
+	Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+*/
+
+// the following functions allow querying of the DOM using CSS selectors
+var cssQuery = function() {
+var version = "2.0.2";
+
+// -----------------------------------------------------------------------
+// main query function
+// -----------------------------------------------------------------------
+
+var $COMMA = /\s*,\s*/;
+var cssQuery = function($selector, $$from) {
+try {
+	var $match = [];
+	var $useCache = arguments.callee.caching && !$$from;
+	var $base = ($$from) ? ($$from.constructor == Array) ? $$from : [$$from] : [document];
+	// process comma separated selectors
+	var $$selectors = parseSelector($selector).split($COMMA), i;
+	for (i = 0; i < $$selectors.length; i++) {
+		// convert the selector to a stream
+		$selector = _toStream($$selectors[i]);
+		// faster chop if it starts with id (MSIE only)
+		if (isMSIE && $selector.slice(0, 3).join("") == " *#") {
+			$selector = $selector.slice(2);
+			$$from = _msie_selectById([], $base, $selector[1]);
+		} else $$from = $base;
+		// process the stream
+		var j = 0, $token, $filter, $arguments, $cacheSelector = "";
+		while (j < $selector.length) {
+			$token = $selector[j++];
+			$filter = $selector[j++];
+			$cacheSelector += $token + $filter;
+			// some pseudo-classes allow arguments to be passed
+			//  e.g. nth-child(even)
+			$arguments = "";
+			if ($selector[j] == "(") {
+				while ($selector[j++] != ")" && j < $selector.length) {
+					$arguments += $selector[j];
+				}
+				$arguments = $arguments.slice(0, -1);
+				$cacheSelector += "(" + $arguments + ")";
+			}
+			// process a token/filter pair use cached results if possible
+			$$from = ($useCache && cache[$cacheSelector]) ?
+				cache[$cacheSelector] : select($$from, $token, $filter, $arguments);
+			if ($useCache) cache[$cacheSelector] = $$from;
+		}
+		$match = $match.concat($$from);
+	}
+	delete cssQuery.error;
+	return $match;
+} catch ($error) {
+	cssQuery.error = $error;
+	return [];
+}};
+
+// -----------------------------------------------------------------------
+// public interface
+// -----------------------------------------------------------------------
+
+cssQuery.toString = function() {
+	return "function cssQuery() {\n  [version " + version + "]\n}";
+};
+
+// caching
+var cache = {};
+cssQuery.caching = false;
+cssQuery.clearCache = function($selector) {
+	if ($selector) {
+		$selector = _toStream($selector).join("");
+		delete cache[$selector];
+	} else cache = {};
+};
+
+// allow extensions
+var modules = {};
+var loaded = false;
+cssQuery.addModule = function($name, $script) {
+	if (loaded) eval("$script=" + String($script));
+	modules[$name] = new $script();;
+};
+
+// hackery
+cssQuery.valueOf = function($code) {
+	return $code ? eval($code) : this;
+};
+
+// -----------------------------------------------------------------------
+// declarations
+// -----------------------------------------------------------------------
+
+var selectors = {};
+var pseudoClasses = {};
+// a safari bug means that these have to be declared here
+var AttributeSelector = {match: /\[([\w-]+(\|[\w-]+)?)\s*(\W?=)?\s*([^\]]*)\]/};
+var attributeSelectors = [];
+
+// -----------------------------------------------------------------------
+// selectors
+// -----------------------------------------------------------------------
+
+// descendant selector
+selectors[" "] = function($results, $from, $tagName, $namespace) {
+	// loop through current selection
+	var $element, i, j;
+	for (i = 0; i < $from.length; i++) {
+		// get descendants
+		var $subset = getElementsByTagName($from[i], $tagName, $namespace);
+		// loop through descendants and add to results selection
+		for (j = 0; ($element = $subset[j]); j++) {
+			if (thisElement($element) && compareNamespace($element, $namespace))
+				$results.push($element);
+		}
+	}
+};
+
+// ID selector
+selectors["#"] = function($results, $from, $id) {
+	// loop through current selection and check ID
+	var $element, j;
+	for (j = 0; ($element = $from[j]); j++) if ($element.id == $id) $results.push($element);
+};
+
+// class selector
+selectors["."] = function($results, $from, $className) {
+	// create a RegExp version of the class
+	$className = new RegExp("(^|\\s)" + $className + "(\\s|$)");
+	// loop through current selection and check class
+	var $element, i;
+	for (i = 0; ($element = $from[i]); i++)
+		if ($className.test($element.className)) $results.push($element);
+};
+
+// pseudo-class selector
+selectors[":"] = function($results, $from, $pseudoClass, $arguments) {
+	// retrieve the cssQuery pseudo-class function
+	var $test = pseudoClasses[$pseudoClass], $element, i;
+	// loop through current selection and apply pseudo-class filter
+	if ($test) for (i = 0; ($element = $from[i]); i++)
+		// if the cssQuery pseudo-class function returns "true" add the element
+		if ($test($element, $arguments)) $results.push($element);
+};
+
+// -----------------------------------------------------------------------
+// pseudo-classes
+// -----------------------------------------------------------------------
+
+pseudoClasses["link"] = function($element) {
+	var $document = getDocument($element);
+	if ($document.links) for (var i = 0; i < $document.links.length; i++) {
+		if ($document.links[i] == $element) return true;
+	}
+};
+
+pseudoClasses["visited"] = function($element) {
+	// can't do this without jiggery-pokery
+};
+
+// -----------------------------------------------------------------------
+// DOM traversal
+// -----------------------------------------------------------------------
+
+// IE5/6 includes comments (LOL) in it's elements collections.
+// so we have to check for this. the test is tagName != "!". LOL (again).
+var thisElement = function($element) {
+	return ($element && $element.nodeType == 1 && $element.tagName != "!") ? $element : null;
+};
+
+// return the previous element to the supplied element
+//  previousSibling is not good enough as it might return a text or comment node
+var previousElementSibling = function($element) {
+	while ($element && ($element = $element.previousSibling) && !thisElement($element)) continue;
+	return $element;
+};
+
+// return the next element to the supplied element
+var nextElementSibling = function($element) {
+	while ($element && ($element = $element.nextSibling) && !thisElement($element)) continue;
+	return $element;
+};
+
+// return the first child ELEMENT of an element
+//  NOT the first child node (though they may be the same thing)
+var firstElementChild = function($element) {
+	return thisElement($element.firstChild) || nextElementSibling($element.firstChild);
+};
+
+var lastElementChild = function($element) {
+	return thisElement($element.lastChild) || previousElementSibling($element.lastChild);
+};
+
+// return child elements of an element (not child nodes)
+var childElements = function($element) {
+	var $childElements = [];
+	$element = firstElementChild($element);
+	while ($element) {
+		$childElements.push($element);
+		$element = nextElementSibling($element);
+	}
+	return $childElements;
+};
+
+// -----------------------------------------------------------------------
+// browser compatibility
+// -----------------------------------------------------------------------
+
+// all of the functions in this section can be overwritten. the default
+//  configuration is for IE. The functions below reflect this. standard
+//  methods are included in a separate module. It would probably be better
+//  the other way round of course but this makes it easier to keep IE7 trim.
+
+var isMSIE = true;
+
+var isXML = function($element) {
+	var $document = getDocument($element);
+	return (typeof $document.mimeType == "unknown") ?
+		/\.xml$/i.test($document.URL) :
+		Boolean($document.mimeType == "XML Document");
+};
+
+// return the element's containing document
+var getDocument = function($element) {
+	return $element.ownerDocument || $element.document;
+};
+
+var getElementsByTagName = function($element, $tagName) {
+	return ($tagName == "*" && $element.all) ? $element.all : $element.getElementsByTagName($tagName);
+};
+
+var compareTagName = function($element, $tagName, $namespace) {
+	if ($tagName == "*") return thisElement($element);
+	if (!compareNamespace($element, $namespace)) return false;
+	if (!isXML($element)) $tagName = $tagName.toUpperCase();
+	return $element.tagName == $tagName;
+};
+
+var compareNamespace = function($element, $namespace) {
+	return !$namespace || ($namespace == "*") || ($element.scopeName == $namespace);
+};
+
+var getTextContent = function($element) {
+	return $element.innerText;
+};
+
+function _msie_selectById($results, $from, id) {
+	var $match, i, j;
+	for (i = 0; i < $from.length; i++) {
+		if ($match = $from[i].all.item(id)) {
+			if ($match.id == id) $results.push($match);
+			else if ($match.length != null) {
+				for (j = 0; j < $match.length; j++) {
+					if ($match[j].id == id) $results.push($match[j]);
+				}
+			}
+		}
+	}
+	return $results;
+};
+
+// for IE5.0
+if (![].push) Array.prototype.push = function() {
+	for (var i = 0; i < arguments.length; i++) {
+		this[this.length] = arguments[i];
+	}
+	return this.length;
+};
+
+// -----------------------------------------------------------------------
+// query support
+// -----------------------------------------------------------------------
+
+// select a set of matching elements.
+// "from" is an array of elements.
+// "token" is a character representing the type of filter
+//  e.g. ">" means child selector
+// "filter" represents the tag name, id or class name that is being selected
+// the function returns an array of matching elements
+var $NAMESPACE = /\|/;
+function select($$from, $token, $filter, $arguments) {
+	if ($NAMESPACE.test($filter)) {
+		$filter = $filter.split($NAMESPACE);
+		$arguments = $filter[0];
+		$filter = $filter[1];
+	}
+	var $results = [];
+	if (selectors[$token]) {
+		selectors[$token]($results, $$from, $filter, $arguments);
+	}
+	return $results;
+};
+
+// -----------------------------------------------------------------------
+// parsing
+// -----------------------------------------------------------------------
+
+// convert css selectors to a stream of tokens and filters
+//  it's not a real stream. it's just an array of strings.
+var $STANDARD_SELECT = /^[^\s>+~]/;
+var $$STREAM = /[\s#.:>+~()@]|[^\s#.:>+~()@]+/g;
+function _toStream($selector) {
+	if ($STANDARD_SELECT.test($selector)) $selector = " " + $selector;
+	return $selector.match($$STREAM) || [];
+};
+
+var $WHITESPACE = /\s*([\s>+~(),]|^|$)\s*/g;
+var $IMPLIED_ALL = /([\s>+~,]|[^(]\+|^)([#.:@])/g;
+var parseSelector = function($selector) {
+	return $selector
+	// trim whitespace
+	.replace($WHITESPACE, "$1")
+	// e.g. ".class1" --> "*.class1"
+	.replace($IMPLIED_ALL, "$1*$2");
+};
+
+var Quote = {
+	toString: function() {return "'"},
+	match: /^('[^']*')|("[^"]*")$/,
+	test: function($string) {
+		return this.match.test($string);
+	},
+	add: function($string) {
+		return this.test($string) ? $string : this + $string + this;
+	},
+	remove: function($string) {
+		return this.test($string) ? $string.slice(1, -1) : $string;
+	}
+};
+
+var getText = function($text) {
+	return Quote.remove($text);
+};
+
+var $ESCAPE = /([\/()[\]?{}|*+-])/g;
+function regEscape($string) {
+	return $string.replace($ESCAPE, "\\$1");
+};
+
+// -----------------------------------------------------------------------
+// modules
+// -----------------------------------------------------------------------
+
+// -------- >>      insert modules here for packaging       << -------- \\
+
+loaded = true;
+
+// -----------------------------------------------------------------------
+// return the query function
+// -----------------------------------------------------------------------
+
+return cssQuery;
+
+}(); // cssQuery

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/prototype.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/prototype.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/prototype.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,2006 @@
+/*  Prototype JavaScript framework, version 1.5.0_rc0
+ *  (c) 2005 Sam Stephenson <sam at conio.net>
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.5.0_rc0',
+  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+
+  emptyFunction: function() {},
+  K: function(x) {return x}
+}
+
+var Class = {
+  create: function() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+  }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+  for (var property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+}
+
+Object.inspect = function(object) {
+  try {
+    if (object == undefined) return 'undefined';
+    if (object == null) return 'null';
+    return object.inspect ? object.inspect() : object.toString();
+  } catch (e) {
+    if (e instanceof RangeError) return '...';
+    throw e;
+  }
+}
+
+Function.prototype.bind = function() {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function() {
+    return __method.apply(object, args.concat($A(arguments)));
+  }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this;
+  return function(event) {
+    return __method.call(object, event || window.event);
+  }
+}
+
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    var digits = this.toString(16);
+    if (this < 16) return '0' + digits;
+    return digits;
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  }
+});
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0; i < arguments.length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.callback();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+}
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += (replacement(match) || '').toString();
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = count === undefined ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return this;
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = truncation === undefined ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : this;
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var div = document.createElement('div');
+    var text = document.createTextNode(this);
+    div.appendChild(text);
+    return div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = document.createElement('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+  },
+
+  toQueryParams: function() {
+    var pairs = this.match(/^\??(.*)$/)[1].split('&');
+    return pairs.inject({}, function(params, pairString) {
+      var pair = pairString.split('=');
+      params[pair[0]] = pair[1];
+      return params;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  camelize: function() {
+    var oStringList = this.split('-');
+    if (oStringList.length == 1) return oStringList[0];
+
+    var camelizedString = this.indexOf('-') == 0
+      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+      : oStringList[0];
+
+    for (var i = 1, len = oStringList.length; i < len; i++) {
+      var s = oStringList[i];
+      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+    }
+
+    return camelizedString;
+  },
+
+  inspect: function() {
+    return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (typeof replacement == 'function') return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern  = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    return this.template.gsub(this.pattern, function(match) {
+      var before = match[1];
+      if (before == '\\') return match[2];
+      return before + (object[match[3]] || '').toString();
+    });
+  }
+}
+
+var $break    = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+  each: function(iterator) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        try {
+          iterator(value, index++);
+        } catch (e) {
+          if (e != $continue) throw e;
+        }
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+  },
+
+  all: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!(iterator || Prototype.K)(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      if (result = !!(iterator || Prototype.K)(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  detect: function (iterator) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(pattern, iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      var stringValue = value.toString();
+      if (stringValue.match(pattern))
+        results.push((iterator || Prototype.K)(value, index));
+    })
+    return results;
+  },
+
+  include: function(object) {
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inject: function(memo, iterator) {
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.collect(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator) {
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      ((iterator || Prototype.K)(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator) {
+    return this.collect(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.collect(Prototype.K);
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (typeof args.last() == 'function')
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+}
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0; i < iterable.length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+  Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0; i < this.length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != undefined || value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(value && value.constructor == Array ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  indexOf: function(object) {
+    for (var i = 0; i < this.length; i++)
+      if (this[i] == object) return i;
+    return -1;
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  }
+});
+var Hash = {
+  _each: function(iterator) {
+    for (var key in this) {
+      var value = this[key];
+      if (typeof value == 'function') continue;
+
+      var pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  },
+
+  keys: function() {
+    return this.pluck('key');
+  },
+
+  values: function() {
+    return this.pluck('value');
+  },
+
+  merge: function(hash) {
+    return $H(hash).inject($H(this), function(mergedHash, pair) {
+      mergedHash[pair.key] = pair.value;
+      return mergedHash;
+    });
+  },
+
+  toQueryString: function() {
+    return this.map(function(pair) {
+      return pair.map(encodeURIComponent).join('=');
+    }).join('&');
+  },
+
+  inspect: function() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  }
+}
+
+function $H(object) {
+  var hash = Object.extend({}, object || {});
+  Object.extend(hash, Enumerable);
+  Object.extend(hash, Hash);
+  return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    do {
+      iterator(value);
+      value = value.succ();
+    } while (this.include(value));
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+}
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responderToAdd) {
+    if (!this.include(responderToAdd))
+      this.responders.push(responderToAdd);
+  },
+
+  unregister: function(responderToRemove) {
+    this.responders = this.responders.without(responderToRemove);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (responder[callback] && typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate: function() {
+    Ajax.activeRequestCount++;
+  },
+
+  onComplete: function() {
+    Ajax.activeRequestCount--;
+  }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      parameters:   ''
+    }
+    Object.extend(this.options, options || {});
+  },
+
+  responseIsSuccess: function() {
+    return this.transport.status == undefined
+        || this.transport.status == 0
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  responseIsFailure: function() {
+    return !this.responseIsSuccess();
+  }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(url, options) {
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    var parameters = this.options.parameters || '';
+    if (parameters.length > 0) parameters += '&_=';
+
+    try {
+      this.url = url;
+      if (this.options.method == 'get' && parameters.length > 0)
+        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+      Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.options.method, this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) {
+        this.transport.onreadystatechange = this.onStateChange.bind(this);
+        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+      }
+
+      this.setRequestHeaders();
+
+      var body = this.options.postBody ? this.options.postBody : parameters;
+      this.transport.send(this.options.method == 'post' ? body : null);
+
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  setRequestHeaders: function() {
+    var requestHeaders =
+      ['X-Requested-With', 'XMLHttpRequest',
+       'X-Prototype-Version', Prototype.Version,
+       'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
+
+    if (this.options.method == 'post') {
+      requestHeaders.push('Content-type', this.options.contentType);
+
+      /* Force "Connection: close" for Mozilla browsers to work around
+       * a bug where XMLHttpReqeuest sends an incorrect Content-length
+       * header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType)
+        requestHeaders.push('Connection', 'close');
+    }
+
+    if (this.options.requestHeaders)
+      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+    for (var i = 0; i < requestHeaders.length; i += 2)
+      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState != 1)
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  header: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) {}
+  },
+
+  evalJSON: function() {
+    try {
+      return eval('(' + this.header('X-JSON') + ')');
+    } catch (e) {}
+  },
+
+  evalResponse: function() {
+    try {
+      return eval(this.transport.responseText);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  respondToReadyState: function(readyState) {
+    var event = Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (event == 'Complete') {
+      try {
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + event, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+    if (event == 'Complete')
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+  initialize: function(container, url, options) {
+    this.containers = {
+      success: container.success ? $(container.success) : $(container),
+      failure: container.failure ? $(container.failure) :
+        (container.success ? null : $(container))
+    }
+
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+
+    var onComplete = this.options.onComplete || Prototype.emptyFunction;
+    this.options.onComplete = (function(transport, object) {
+      this.updateContent();
+      onComplete(transport, object);
+    }).bind(this);
+
+    this.request(url);
+  },
+
+  updateContent: function() {
+    var receiver = this.responseIsSuccess() ?
+      this.containers.success : this.containers.failure;
+    var response = this.transport.responseText;
+
+    if (!this.options.evalScripts)
+      response = response.stripScripts();
+
+    if (receiver) {
+      if (this.options.insertion) {
+        new this.options.insertion(receiver, response);
+      } else {
+        Element.update(receiver, response);
+      }
+    }
+
+    if (this.responseIsSuccess()) {
+      if (this.onComplete)
+        setTimeout(this.onComplete.bind(this), 10);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(container, url, options) {
+    this.setOptions(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = {};
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(request) {
+    if (this.options.decay) {
+      this.decay = (request.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = request.responseText;
+    }
+    this.timer = setTimeout(this.onTimerEvent.bind(this),
+      this.decay * this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $() {
+  var results = [], element;
+  for (var i = 0; i < arguments.length; i++) {
+    element = arguments[i];
+    if (typeof element == 'string')
+      element = document.getElementById(element);
+    results.push(Element.extend(element));
+  }
+  return results.length < 2 ? results[0] : results;
+}
+
+document.getElementsByClassName = function(className, parentElement) {
+  var children = ($(parentElement) || document.body).getElementsByTagName('*');
+  return $A(children).inject([], function(elements, child) {
+    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      elements.push(Element.extend(child));
+    return elements;
+  });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element)
+  var Element = new Object();
+
+Element.extend = function(element) {
+  if (!element) return;
+  if (_nativeExtensions) return element;
+
+  if (!element._extended && element.tagName && element != window) {
+    var methods = Element.Methods, cache = Element.extend.cache;
+    for (property in methods) {
+      var value = methods[property];
+      if (typeof value == 'function')
+        element[property] = cache.findOrStore(value);
+    }
+  }
+
+  element._extended = true;
+  return element;
+}
+
+Element.extend.cache = {
+  findOrStore: function(value) {
+    return this[value] = this[value] || function() {
+      return value.apply(null, [this].concat($A(arguments)));
+    }
+  }
+}
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      Element[Element.visible(element) ? 'hide' : 'show'](element);
+    }
+  },
+
+  hide: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = 'none';
+    }
+  },
+
+  show: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = '';
+    }
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+  },
+
+  update: function(element, html) {
+    $(element).innerHTML = html.stripScripts();
+    setTimeout(function() {html.evalScripts()}, 10);
+  },
+
+  replace: function(element, html) {
+    element = $(element);
+    if (element.outerHTML) {
+      element.outerHTML = html.stripScripts();
+    } else {
+      var range = element.ownerDocument.createRange();
+      range.selectNodeContents(element);
+      element.parentNode.replaceChild(
+        range.createContextualFragment(html.stripScripts()), element);
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+  },
+
+  getHeight: function(element) {
+    element = $(element);
+    return element.offsetHeight;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).include(className);
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).add(className);
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).remove(className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    for (var i = 0; i < element.childNodes.length; i++) {
+      var node = element.childNodes[i];
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        Element.remove(node);
+    }
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.match(/^\s*$/);
+  },
+
+  childOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var x = element.x ? element.x : element.offsetLeft,
+        y = element.y ? element.y : element.offsetTop;
+    window.scrollTo(x, y);
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    var value = element.style[style.camelize()];
+    if (!value) {
+      if (document.defaultView && document.defaultView.getComputedStyle) {
+        var css = document.defaultView.getComputedStyle(element, null);
+        value = css ? css.getPropertyValue(style) : null;
+      } else if (element.currentStyle) {
+        value = element.currentStyle[style.camelize()];
+      }
+    }
+
+    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+    return value == 'auto' ? null : value;
+  },
+
+  setStyle: function(element, style) {
+    element = $(element);
+    for (var name in style)
+      element.style[name.camelize()] = style[name];
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    if (Element.getStyle(element, 'display') != 'none')
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = '';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = 'none';
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return;
+    element._overflow = element.style.overflow;
+    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+      element.style.overflow = 'hidden';
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return;
+    element.style.overflow = element._overflow;
+    element._overflow = undefined;
+  }
+}
+
+Object.extend(Element, Element.Methods);
+
+var _nativeExtensions = false;
+
+if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+  var HTMLElement = {}
+  HTMLElement.prototype = document.createElement('div').__proto__;
+}
+
+Element.addMethods = function(methods) {
+  Object.extend(Element.Methods, methods || {});
+
+  if(typeof HTMLElement != 'undefined') {
+    var methods = Element.Methods, cache = Element.extend.cache;
+    for (property in methods) {
+      var value = methods[property];
+      if (typeof value == 'function')
+        HTMLElement.prototype[property] = cache.findOrStore(value);
+    }
+    _nativeExtensions = true;
+  }
+}
+
+Element.addMethods();
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+  this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+  initialize: function(element, content) {
+    this.element = $(element);
+    this.content = content.stripScripts();
+
+    if (this.adjacency && this.element.insertAdjacentHTML) {
+      try {
+        this.element.insertAdjacentHTML(this.adjacency, this.content);
+      } catch (e) {
+        var tagName = this.element.tagName.toLowerCase();
+        if (tagName == 'tbody' || tagName == 'tr') {
+          this.insertContent(this.contentFromAnonymousTable());
+        } else {
+          throw e;
+        }
+      }
+    } else {
+      this.range = this.element.ownerDocument.createRange();
+      if (this.initializeRange) this.initializeRange();
+      this.insertContent([this.range.createContextualFragment(this.content)]);
+    }
+
+    setTimeout(function() {content.evalScripts()}, 10);
+  },
+
+  contentFromAnonymousTable: function() {
+    var div = document.createElement('div');
+    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+    return $A(div.childNodes[0].childNodes[0].childNodes);
+  }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+  initializeRange: function() {
+    this.range.setStartBefore(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment, this.element);
+    }).bind(this));
+  }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(true);
+  },
+
+  insertContent: function(fragments) {
+    fragments.reverse(false).each((function(fragment) {
+      this.element.insertBefore(fragment, this.element.firstChild);
+    }).bind(this));
+  }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.appendChild(fragment);
+    }).bind(this));
+  }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+  initializeRange: function() {
+    this.range.setStartAfter(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment,
+        this.element.nextSibling);
+    }).bind(this));
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set(this.toArray().concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set(this.select(function(className) {
+      return className != classNameToRemove;
+    }).join(' '));
+  },
+
+  toString: function() {
+    return this.toArray().join(' ');
+  }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Selector = Class.create();
+Selector.prototype = {
+  initialize: function(expression) {
+    this.params = {classNames: []};
+    this.expression = expression.toString().strip();
+    this.parseExpression();
+    this.compileMatcher();
+  },
+
+  parseExpression: function() {
+    function abort(message) { throw 'Parse error in selector: ' + message; }
+
+    if (this.expression == '')  abort('empty expression');
+
+    var params = this.params, expr = this.expression, match, modifier, clause, rest;
+    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
+      params.attributes = params.attributes || [];
+      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
+      expr = match[1];
+    }
+
+    if (expr == '*') return this.params.wildcard = true;
+
+    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
+      modifier = match[1], clause = match[2], rest = match[3];
+      switch (modifier) {
+        case '#':       params.id = clause; break;
+        case '.':       params.classNames.push(clause); break;
+        case '':
+        case undefined: params.tagName = clause.toUpperCase(); break;
+        default:        abort(expr.inspect());
+      }
+      expr = rest;
+    }
+
+    if (expr.length > 0) abort(expr.inspect());
+  },
+
+  buildMatchExpression: function() {
+    var params = this.params, conditions = [], clause;
+
+    if (params.wildcard)
+      conditions.push('true');
+    if (clause = params.id)
+      conditions.push('element.id == ' + clause.inspect());
+    if (clause = params.tagName)
+      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
+    if ((clause = params.classNames).length > 0)
+      for (var i = 0; i < clause.length; i++)
+        conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
+    if (clause = params.attributes) {
+      clause.each(function(attribute) {
+        var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
+        var splitValueBy = function(delimiter) {
+          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
+        }
+
+        switch (attribute.operator) {
+          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
+          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
+          case '|=':      conditions.push(
+                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
+                          ); break;
+          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
+          case '':
+          case undefined: conditions.push(value + ' != null'); break;
+          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
+        }
+      });
+    }
+
+    return conditions.join(' && ');
+  },
+
+  compileMatcher: function() {
+    this.match = new Function('element', 'if (!element.tagName) return false; \n' +
+    'return ' + this.buildMatchExpression());
+  },
+
+  findElements: function(scope) {
+    var element;
+
+    if (element = $(this.params.id))
+      if (this.match(element))
+        if (!scope || Element.childOf(element, scope))
+          return [element];
+
+    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
+
+    var results = [];
+    for (var i = 0; i < scope.length; i++)
+      if (this.match(element = scope[i]))
+        results.push(Element.extend(element));
+
+    return results;
+  },
+
+  toString: function() {
+    return this.expression;
+  }
+}
+
+function $$() {
+  return $A(arguments).map(function(expression) {
+    return expression.strip().split(/\s+/).inject([null], function(results, expr) {
+      var selector = new Selector(expr);
+      return results.map(selector.findElements.bind(selector)).flatten();
+    });
+  }).flatten();
+}
+var Field = {
+  clear: function() {
+    for (var i = 0; i < arguments.length; i++)
+      $(arguments[i]).value = '';
+  },
+
+  focus: function(element) {
+    $(element).focus();
+  },
+
+  present: function() {
+    for (var i = 0; i < arguments.length; i++)
+      if ($(arguments[i]).value == '') return false;
+    return true;
+  },
+
+  select: function(element) {
+    $(element).select();
+  },
+
+  activate: function(element) {
+    element = $(element);
+    element.focus();
+    if (element.select)
+      element.select();
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+  serialize: function(form) {
+    var elements = Form.getElements($(form));
+    var queryComponents = new Array();
+
+    for (var i = 0; i < elements.length; i++) {
+      var queryComponent = Form.Element.serialize(elements[i]);
+      if (queryComponent)
+        queryComponents.push(queryComponent);
+    }
+
+    return queryComponents.join('&');
+  },
+
+  getElements: function(form) {
+    form = $(form);
+    var elements = new Array();
+
+    for (var tagName in Form.Element.Serializers) {
+      var tagElements = form.getElementsByTagName(tagName);
+      for (var j = 0; j < tagElements.length; j++)
+        elements.push(tagElements[j]);
+    }
+    return elements;
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name)
+      return inputs;
+
+    var matchingInputs = new Array();
+    for (var i = 0; i < inputs.length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) ||
+          (name && input.name != name))
+        continue;
+      matchingInputs.push(input);
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.blur();
+      element.disabled = 'true';
+    }
+  },
+
+  enable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.disabled = '';
+    }
+  },
+
+  findFirstElement: function(form) {
+    return Form.getElements(form).find(function(element) {
+      return element.type != 'hidden' && !element.disabled &&
+        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    Field.activate(Form.findFirstElement(form));
+  },
+
+  reset: function(form) {
+    $(form).reset();
+  }
+}
+
+Form.Element = {
+  serialize: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+
+    if (parameter) {
+      var key = encodeURIComponent(parameter[0]);
+      if (key.length == 0) return;
+
+      if (parameter[1].constructor != Array)
+        parameter[1] = [parameter[1]];
+
+      return parameter[1].map(function(value) {
+        return key + '=' + encodeURIComponent(value);
+      }).join('&');
+    }
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+
+    if (parameter)
+      return parameter[1];
+  }
+}
+
+Form.Element.Serializers = {
+  input: function(element) {
+    switch (element.type.toLowerCase()) {
+      case 'submit':
+      case 'hidden':
+      case 'password':
+      case 'text':
+        return Form.Element.Serializers.textarea(element);
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element);
+    }
+    return false;
+  },
+
+  inputSelector: function(element) {
+    if (element.checked)
+      return [element.name, element.value];
+  },
+
+  textarea: function(element) {
+    return [element.name, element.value];
+  },
+
+  select: function(element) {
+    return Form.Element.Serializers[element.type == 'select-one' ?
+      'selectOne' : 'selectMany'](element);
+  },
+
+  selectOne: function(element) {
+    var value = '', opt, index = element.selectedIndex;
+    if (index >= 0) {
+      opt = element.options[index];
+      value = opt.value || opt.text;
+    }
+    return [element.name, value];
+  },
+
+  selectMany: function(element) {
+    var value = [];
+    for (var i = 0; i < element.length; i++) {
+      var opt = element.options[i];
+      if (opt.selected)
+        value.push(opt.value || opt.text);
+    }
+    return [element.name, value];
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+  initialize: function(element, frequency, callback) {
+    this.frequency = frequency;
+    this.element   = $(element);
+    this.callback  = callback;
+
+    this.lastValue = this.getValue();
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    var elements = Form.getElements(this.element);
+    for (var i = 0; i < elements.length; i++)
+      this.registerCallback(elements[i]);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        case 'password':
+        case 'text':
+        case 'textarea':
+        case 'select-one':
+        case 'select-multiple':
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) {
+  var Event = new Object();
+}
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+
+  element: function(event) {
+    return event.target || event.srcElement;
+  },
+
+  isLeftClick: function(event) {
+    return (((event.which) && (event.which == 1)) ||
+            ((event.button) && (event.button == 1)));
+  },
+
+  pointerX: function(event) {
+    return event.pageX || (event.clientX +
+      (document.documentElement.scrollLeft || document.body.scrollLeft));
+  },
+
+  pointerY: function(event) {
+    return event.pageY || (event.clientY +
+      (document.documentElement.scrollTop || document.body.scrollTop));
+  },
+
+  stop: function(event) {
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
+    } else {
+      event.returnValue = false;
+      event.cancelBubble = true;
+    }
+  },
+
+  // find the first node with the given tagName, starting from the
+  // node the event was triggered on; traverses the DOM upwards
+  findElement: function(event, tagName) {
+    var element = Event.element(event);
+    while (element.parentNode && (!element.tagName ||
+        (element.tagName.toUpperCase() != tagName.toUpperCase())))
+      element = element.parentNode;
+    return element;
+  },
+
+  observers: false,
+
+  _observeAndCache: function(element, name, observer, useCapture) {
+    if (!this.observers) this.observers = [];
+    if (element.addEventListener) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.addEventListener(name, observer, useCapture);
+    } else if (element.attachEvent) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.attachEvent('on' + name, observer);
+    }
+  },
+
+  unloadCache: function() {
+    if (!Event.observers) return;
+    for (var i = 0; i < Event.observers.length; i++) {
+      Event.stopObserving.apply(this, Event.observers[i]);
+      Event.observers[i][0] = null;
+    }
+    Event.observers = false;
+  },
+
+  observe: function(element, name, observer, useCapture) {
+    var element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.attachEvent))
+      name = 'keydown';
+
+    this._observeAndCache(element, name, observer, useCapture);
+  },
+
+  stopObserving: function(element, name, observer, useCapture) {
+    var element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.detachEvent))
+      name = 'keydown';
+
+    if (element.removeEventListener) {
+      element.removeEventListener(name, observer, useCapture);
+    } else if (element.detachEvent) {
+      element.detachEvent('on' + name, observer);
+    }
+  }
+});
+
+/* prevent memory leaks in IE */
+if (navigator.appVersion.match(/\bMSIE\b/))
+  Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  realOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  offsetParent: function(element) {
+    if (element.offsetParent) return element.offsetParent;
+    if (element == document.body) return element;
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return element;
+
+    return document.body;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = this.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = this.realOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = this.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  clone: function(source, target) {
+    source = $(source);
+    target = $(target);
+    target.style.position = 'absolute';
+    var offsets = this.cumulativeOffset(source);
+    target.style.top    = offsets[1] + 'px';
+    target.style.left   = offsets[0] + 'px';
+    target.style.width  = source.offsetWidth + 'px';
+    target.style.height = source.offsetHeight + 'px';
+  },
+
+  page: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent==document.body)
+        if (Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      valueT -= element.scrollTop  || 0;
+      valueL -= element.scrollLeft || 0;
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+  },
+
+  clone: function(source, target) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || {})
+
+    // find page position of source
+    source = $(source);
+    var p = Position.page(source);
+
+    // find coordinate system to use
+    target = $(target);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(target,'position') == 'absolute') {
+      parent = Position.offsetParent(target);
+      delta = Position.page(parent);
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
+    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.style.position == 'absolute') return;
+    Position.prepare();
+
+    var offsets = Position.positionedOffset(element);
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';;
+    element.style.left   = left + 'px';;
+    element.style.width  = width + 'px';;
+    element.style.height = height + 'px';;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.style.position == 'relative') return;
+    Position.prepare();
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+  }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned.  For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+  Position.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return [valueL, valueT];
+  }
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/builder.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/builder.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/builder.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,101 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// See scriptaculous.js for full license.
+
+var Builder = {
+  NODEMAP: {
+    AREA: 'map',
+    CAPTION: 'table',
+    COL: 'table',
+    COLGROUP: 'table',
+    LEGEND: 'fieldset',
+    OPTGROUP: 'select',
+    OPTION: 'select',
+    PARAM: 'object',
+    TBODY: 'table',
+    TD: 'table',
+    TFOOT: 'table',
+    TH: 'table',
+    THEAD: 'table',
+    TR: 'table'
+  },
+  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+  //       due to a Firefox bug
+  node: function(elementName) {
+    elementName = elementName.toUpperCase();
+    
+    // try innerHTML approach
+    var parentTag = this.NODEMAP[elementName] || 'div';
+    var parentElement = document.createElement(parentTag);
+    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+    } catch(e) {}
+    var element = parentElement.firstChild || null;
+      
+    // see if browser added wrapping tags
+    if(element && (element.tagName != elementName))
+      element = element.getElementsByTagName(elementName)[0];
+    
+    // fallback to createElement approach
+    if(!element) element = document.createElement(elementName);
+    
+    // abort if nothing could be created
+    if(!element) return;
+
+    // attributes (or text)
+    if(arguments[1])
+      if(this._isStringOrNumber(arguments[1]) ||
+        (arguments[1] instanceof Array)) {
+          this._children(element, arguments[1]);
+        } else {
+          var attrs = this._attributes(arguments[1]);
+          if(attrs.length) {
+            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+              parentElement.innerHTML = "<" +elementName + " " +
+                attrs + "></" + elementName + ">";
+            } catch(e) {}
+            element = parentElement.firstChild || null;
+            // workaround firefox 1.0.X bug
+            if(!element) {
+              element = document.createElement(elementName);
+              for(attr in arguments[1]) 
+                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+            }
+            if(element.tagName != elementName)
+              element = parentElement.getElementsByTagName(elementName)[0];
+            }
+        } 
+
+    // text, or array of children
+    if(arguments[2])
+      this._children(element, arguments[2]);
+
+     return element;
+  },
+  _text: function(text) {
+     return document.createTextNode(text);
+  },
+  _attributes: function(attributes) {
+    var attrs = [];
+    for(attribute in attributes)
+      attrs.push((attribute=='className' ? 'class' : attribute) +
+          '="' + attributes[attribute].toString().escapeHTML() + '"');
+    return attrs.join(" ");
+  },
+  _children: function(element, children) {
+    if(typeof children=='object') { // array can hold nodes and text
+      children.flatten().each( function(e) {
+        if(typeof e=='object')
+          element.appendChild(e)
+        else
+          if(Builder._isStringOrNumber(e))
+            element.appendChild(Builder._text(e));
+      });
+    } else
+      if(Builder._isStringOrNumber(children)) 
+         element.appendChild(Builder._text(children));
+  },
+  _isStringOrNumber: function(param) {
+    return(typeof param=='string' || typeof param=='number');
+  }
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/controls.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/controls.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/controls.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,815 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+// 
+// See scriptaculous.js for full license.
+
+// Autocompleter.Base handles all the autocompletion functionality 
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least, 
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method 
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most 
+// useful when one of the tokens is \n (a newline), as it 
+// allows smart autocompletion after linebreaks.
+
+var Autocompleter = {}
+Autocompleter.Base = function() {};
+Autocompleter.Base.prototype = {
+  baseInitialize: function(element, update, options) {
+    this.element     = $(element); 
+    this.update      = $(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+
+    if (this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || {};
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+    function(element, update){ 
+      if(!update.style.position || update.style.position=='absolute') {
+        update.style.position = 'absolute';
+        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
+      }
+      Effect.Appear(update,{duration:0.15});
+    };
+    this.options.onHide = this.options.onHide || 
+    function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if (typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && 
+      (navigator.appVersion.indexOf('MSIE')>0) &&
+      (navigator.userAgent.indexOf('Opera')<0) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+  
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix);
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+      }
+     else 
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
+         (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+        
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+  
+  markPrevious: function() {
+    if(this.index > 0) this.index--
+      else this.index = this.entryCount-1;
+  },
+  
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++
+      else this.index = 0;
+  },
+  
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    
+    var lastTokenPos = this.findLastToken();
+    if (lastTokenPos != -1) {
+      var newValue = this.element.value.substr(0, lastTokenPos + 1);
+      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value;
+    } else {
+      this.element.value = value;
+    }
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.firstChild);
+
+      if(this.update.firstChild && this.update.firstChild.childNodes) {
+        this.entryCount = 
+          this.update.firstChild.childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+
+      this.index = 0;
+      this.render();
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    if(this.getToken().length>=this.options.minChars) {
+      this.startIndicator();
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+
+  getToken: function() {
+    var tokenPos = this.findLastToken();
+    if (tokenPos != -1)
+      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+    else
+      var ret = this.element.value;
+
+    return /\n/.test(ret) ? '' : ret;
+  },
+
+  findLastToken: function() {
+    var lastTokenPos = -1;
+
+    for (var i=0; i<this.options.tokens.length; i++) {
+      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
+      if (thisTokenPos > lastTokenPos)
+        lastTokenPos = thisTokenPos;
+    }
+    return lastTokenPos;
+  }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || {});
+  }
+});
+
+// AJAX in-place editor
+//
+// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = $(element);
+
+    this.options = Object.extend({
+      okButton: true,
+      okText: "ok",
+      cancelLink: true,
+      cancelText: "cancel",
+      savingText: "Saving...",
+      clickToEditText: "Click to edit",
+      okText: "ok",
+      rows: 1,
+      onComplete: function(transport, element) {
+        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+      },
+      onFailure: function(transport) {
+        alert("Error communicating with the server: " + transport.responseText.stripTags());
+      },
+      callback: function(form) {
+        return Form.serialize(form);
+      },
+      handleLineBreaks: true,
+      loadingText: 'Loading...',
+      savingClassName: 'inplaceeditor-saving',
+      loadingClassName: 'inplaceeditor-loading',
+      formClassName: 'inplaceeditor-form',
+      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+      highlightendcolor: "#FFFFFF",
+      externalControl: null,
+      submitOnBlur: false,
+      ajaxOptions: {},
+      evalScripts: false
+    }, options || {});
+
+    if(!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + "-inplaceeditor";
+      if ($(this.options.formId)) {
+        // there's already a form with that name, don't specify an id
+        this.options.formId = null;
+      }
+    }
+    
+    if (this.options.externalControl) {
+      this.options.externalControl = $(this.options.externalControl);
+    }
+    
+    this.originalBackground = Element.getStyle(this.element, 'background-color');
+    if (!this.originalBackground) {
+      this.originalBackground = "transparent";
+    }
+    
+    this.element.title = this.options.clickToEditText;
+    
+    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+    Event.observe(this.element, 'click', this.onclickListener);
+    Event.observe(this.element, 'mouseover', this.mouseoverListener);
+    Event.observe(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.observe(this.options.externalControl, 'click', this.onclickListener);
+      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  },
+  enterEditMode: function(evt) {
+    if (this.saving) return;
+    if (this.editing) return;
+    this.editing = true;
+    this.onEnterEditMode();
+    if (this.options.externalControl) {
+      Element.hide(this.options.externalControl);
+    }
+    Element.hide(this.element);
+    this.createForm();
+    this.element.parentNode.insertBefore(this.form, this.element);
+    Field.scrollFreeActivate(this.editField);
+    // stop the event to avoid a page refresh in Safari
+    if (evt) {
+      Event.stop(evt);
+    }
+    return false;
+  },
+  createForm: function() {
+    this.form = document.createElement("form");
+    this.form.id = this.options.formId;
+    Element.addClassName(this.form, this.options.formClassName)
+    this.form.onsubmit = this.onSubmit.bind(this);
+
+    this.createEditField();
+
+    if (this.options.textarea) {
+      var br = document.createElement("br");
+      this.form.appendChild(br);
+    }
+
+    if (this.options.okButton) {
+      okButton = document.createElement("input");
+      okButton.type = "submit";
+      okButton.value = this.options.okText;
+      okButton.className = 'editor_ok_button';
+      this.form.appendChild(okButton);
+    }
+
+    if (this.options.cancelLink) {
+      cancelLink = document.createElement("a");
+      cancelLink.href = "#";
+      cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+      cancelLink.onclick = this.onclickCancel.bind(this);
+      cancelLink.className = 'editor_cancel';      
+      this.form.appendChild(cancelLink);
+    }
+  },
+  hasHTMLLineBreaks: function(string) {
+    if (!this.options.handleLineBreaks) return false;
+    return string.match(/<br/i) || string.match(/<p>/i);
+  },
+  convertHTMLLineBreaks: function(string) {
+    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
+  },
+  createEditField: function() {
+    var text;
+    if(this.options.loadTextURL) {
+      text = this.options.loadingText;
+    } else {
+      text = this.getText();
+    }
+
+    var obj = this;
+    
+    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
+      this.options.textarea = false;
+      var textField = document.createElement("input");
+      textField.obj = this;
+      textField.type = "text";
+      textField.name = "value";
+      textField.value = text;
+      textField.style.backgroundColor = this.options.highlightcolor;
+      textField.className = 'editor_field';
+      var size = this.options.size || this.options.cols || 0;
+      if (size != 0) textField.size = size;
+      if (this.options.submitOnBlur)
+        textField.onblur = this.onSubmit.bind(this);
+      this.editField = textField;
+    } else {
+      this.options.textarea = true;
+      var textArea = document.createElement("textarea");
+      textArea.obj = this;
+      textArea.name = "value";
+      textArea.value = this.convertHTMLLineBreaks(text);
+      textArea.rows = this.options.rows;
+      textArea.cols = this.options.cols || 40;
+      textArea.className = 'editor_field';      
+      if (this.options.submitOnBlur)
+        textArea.onblur = this.onSubmit.bind(this);
+      this.editField = textArea;
+    }
+    
+    if(this.options.loadTextURL) {
+      this.loadExternalText();
+    }
+    this.form.appendChild(this.editField);
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  loadExternalText: function() {
+    Element.addClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = true;
+    new Ajax.Request(
+      this.options.loadTextURL,
+      Object.extend({
+        asynchronous: true,
+        onComplete: this.onLoadedExternalText.bind(this)
+      }, this.options.ajaxOptions)
+    );
+  },
+  onLoadedExternalText: function(transport) {
+    Element.removeClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = false;
+    this.editField.value = transport.responseText.stripTags();
+  },
+  onclickCancel: function() {
+    this.onComplete();
+    this.leaveEditMode();
+    return false;
+  },
+  onFailure: function(transport) {
+    this.options.onFailure(transport);
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+      this.oldInnerHTML = null;
+    }
+    return false;
+  },
+  onSubmit: function() {
+    // onLoading resets these so we need to save them away for the Ajax call
+    var form = this.form;
+    var value = this.editField.value;
+    
+    // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
+    // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
+    // to be displayed indefinitely
+    this.onLoading();
+    
+    if (this.options.evalScripts) {
+      new Ajax.Request(
+        this.url, Object.extend({
+          parameters: this.options.callback(form, value),
+          onComplete: this.onComplete.bind(this),
+          onFailure: this.onFailure.bind(this),
+          asynchronous:true, 
+          evalScripts:true
+        }, this.options.ajaxOptions));
+    } else  {
+      new Ajax.Updater(
+        { success: this.element,
+          // don't update on failure (this could be an option)
+          failure: null }, 
+        this.url, Object.extend({
+          parameters: this.options.callback(form, value),
+          onComplete: this.onComplete.bind(this),
+          onFailure: this.onFailure.bind(this)
+        }, this.options.ajaxOptions));
+    }
+    // stop the event to avoid a page refresh in Safari
+    if (arguments.length > 1) {
+      Event.stop(arguments[0]);
+    }
+    return false;
+  },
+  onLoading: function() {
+    this.saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  showSaving: function() {
+    this.oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    Element.addClassName(this.element, this.options.savingClassName);
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+  },
+  removeForm: function() {
+    if(this.form) {
+      if (this.form.parentNode) Element.remove(this.form);
+      this.form = null;
+    }
+  },
+  enterHover: function() {
+    if (this.saving) return;
+    this.element.style.backgroundColor = this.options.highlightcolor;
+    if (this.effect) {
+      this.effect.cancel();
+    }
+    Element.addClassName(this.element, this.options.hoverClassName)
+  },
+  leaveHover: function() {
+    if (this.options.backgroundColor) {
+      this.element.style.backgroundColor = this.oldBackground;
+    }
+    Element.removeClassName(this.element, this.options.hoverClassName)
+    if (this.saving) return;
+    this.effect = new Effect.Highlight(this.element, {
+      startcolor: this.options.highlightcolor,
+      endcolor: this.options.highlightendcolor,
+      restorecolor: this.originalBackground
+    });
+  },
+  leaveEditMode: function() {
+    Element.removeClassName(this.element, this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+    if (this.options.externalControl) {
+      Element.show(this.options.externalControl);
+    }
+    this.editing = false;
+    this.saving = false;
+    this.oldInnerHTML = null;
+    this.onLeaveEditMode();
+  },
+  onComplete: function(transport) {
+    this.leaveEditMode();
+    this.options.onComplete.bind(this)(transport, this.element);
+  },
+  onEnterEditMode: function() {},
+  onLeaveEditMode: function() {},
+  dispose: function() {
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+    }
+    this.leaveEditMode();
+    Event.stopObserving(this.element, 'click', this.onclickListener);
+    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  }
+};
+
+Ajax.InPlaceCollectionEditor = Class.create();
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
+  createEditField: function() {
+    if (!this.cached_selectTag) {
+      var selectTag = document.createElement("select");
+      var collection = this.options.collection || [];
+      var optionTag;
+      collection.each(function(e,i) {
+        optionTag = document.createElement("option");
+        optionTag.value = (e instanceof Array) ? e[0] : e;
+        if(this.options.value==optionTag.value) optionTag.selected = true;
+        optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
+        selectTag.appendChild(optionTag);
+      }.bind(this));
+      this.cached_selectTag = selectTag;
+    }
+
+    this.editField = this.cached_selectTag;
+    if(this.options.loadTextURL) this.loadExternalText();
+    this.form.appendChild(this.editField);
+    this.options.callback = function(form, value) {
+      return "value=" + encodeURIComponent(value);
+    }
+  }
+});
+
+// Delayed observer, like Form.Element.Observer, 
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create();
+Form.Element.DelayedObserver.prototype = {
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element); 
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/dragdrop.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/dragdrop.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/dragdrop.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,915 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi at oriontransfer.co.nz)
+// 
+// See scriptaculous.js for full license.
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null,
+      tree:       false
+    }, arguments[1] || {});
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if((typeof containment == 'object') && 
+        (containment.constructor == Array)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+    
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+  
+  findDeepestChild: function(drops) {
+    deepest = drops[0];
+      
+    for (i = 1; i < drops.length; ++i)
+      if (Element.isParent(drops[i].element, deepest.element))
+        deepest = drops[i];
+    
+    return deepest;
+  },
+
+  isContained: function(element, drop) {
+    var containmentNode;
+    if(drop.tree) {
+      containmentNode = element.treeNode; 
+    } else {
+      containmentNode = element.parentNode;
+    }
+    return drop._containers.detect(function(c) { return containmentNode == c });
+  },
+  
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect( 
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    var affected = [];
+    
+    if(this.last_active) this.deactivate(this.last_active);
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop))
+        affected.push(drop);
+    });
+        
+    if(affected.length>0) {
+      drop = Droppables.findDeepestChild(affected);
+      Position.within(drop.element, point[0], point[1]);
+      if(drop.onHover)
+        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+      
+      Droppables.activate(drop);
+    }
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) 
+        this.last_active.onDrop(element, this.last_active.element, event);
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+}
+
+var Draggables = {
+  drags: [],
+  observers: [],
+  
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+      
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+  
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+  
+  activate: function(draggable) {
+    window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+    this.activeDraggable = draggable;
+  },
+  
+  deactivate: function() {
+    this.activeDraggable = null;
+  },
+  
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // the same coordinates, prevent needless redrawing (moz bug?)
+    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+    this._lastPointer = pointer;
+    this.activeDraggable.updateDrag(event, pointer);
+  },
+  
+  endDrag: function(event) {
+    if(!this.activeDraggable) return;
+    this._lastPointer = null;
+    this.activeDraggable.endDrag(event);
+    this.activeDraggable = null;
+  },
+  
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+  
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+  
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+  
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+  },
+  
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable.prototype = {
+  initialize: function(element) {
+    var options = Object.extend({
+      handle: false,
+      starteffect: function(element) {
+        element._opacity = Element.getOpacity(element); 
+        new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
+      },
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
+      },
+      endeffect: function(element) {
+        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity}); 
+      },
+      zindex: 1000,
+      revert: false,
+      scroll: false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      snap: false   // false, or xy or [x,y] or function(x,y){ return [x,y] }
+    }, arguments[1] || {});
+
+    this.element = $(element);
+    
+    if(options.handle && (typeof options.handle == 'string')) {
+      var h = Element.childrenWithClassName(this.element, options.handle, true);
+      if(h.length>0) this.handle = h[0];
+    }
+    if(!this.handle) this.handle = $(options.handle);
+    if(!this.handle) this.handle = this.element;
+    
+    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
+      options.scroll = $(options.scroll);
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.delta    = this.currentDelta();
+    this.options  = options;
+    this.dragging = false;   
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+    
+    Draggables.register(this);
+  },
+  
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+  
+  currentDelta: function() {
+    return([
+      parseInt(Element.getStyle(this.element,'left') || '0'),
+      parseInt(Element.getStyle(this.element,'top') || '0')]);
+  },
+  
+  initDrag: function(event) {
+    if(Event.isLeftClick(event)) {    
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if(src.tagName && (
+        src.tagName=='INPUT' ||
+        src.tagName=='SELECT' ||
+        src.tagName=='OPTION' ||
+        src.tagName=='BUTTON' ||
+        src.tagName=='TEXTAREA')) return;
+        
+      if(this.element._revert) {
+        this.element._revert.cancel();
+        this.element._revert = null;
+      }
+      
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var pos     = Position.cumulativeOffset(this.element);
+      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+      
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+  
+  startDrag: function(event) {
+    this.dragging = true;
+    
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+    
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+    
+    if(this.options.scroll) {
+      if (this.options.scroll == window) {
+        var where = this._getWindowScroll(this.options.scroll);
+        this.originalScrollLeft = where.left;
+        this.originalScrollTop = where.top;
+      } else {
+        this.originalScrollLeft = this.options.scroll.scrollLeft;
+        this.originalScrollTop = this.options.scroll.scrollTop;
+      }
+    }
+    
+    Draggables.notify('onStart', this, event);
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+  
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+    Position.prepare();
+    Droppables.show(pointer, this.element);
+    Draggables.notify('onDrag', this, event);
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+    
+    if(this.options.scroll) {
+      this.stopScrolling();
+      
+      var p;
+      if (this.options.scroll == window) {
+        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+      } else {
+        p = Position.page(this.options.scroll);
+        p[0] += this.options.scroll.scrollLeft;
+        p[1] += this.options.scroll.scrollTop;
+        p.push(p[0]+this.options.scroll.offsetWidth);
+        p.push(p[1]+this.options.scroll.offsetHeight);
+      }
+      var speed = [0,0];
+      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+      this.startScrolling(speed);
+    }
+    
+    // fix AppleWebKit rendering
+    if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+    
+    Event.stop(event);
+  },
+  
+  finishDrag: function(event, success) {
+    this.dragging = false;
+
+    if(this.options.ghosting) {
+      Position.relativize(this.element);
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    if(success) Droppables.fire(event, this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && typeof revert == 'function') revert = revert(this.element);
+    
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      this.options.reverteffect(this.element, 
+        d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+  
+  keyPress: function(event) {
+    if(event.keyCode!=Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+  
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.stopScrolling();
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+  
+  draw: function(point) {
+    var pos = Position.cumulativeOffset(this.element);
+    var d = this.currentDelta();
+    pos[0] -= d[0]; pos[1] -= d[1];
+    
+    if(this.options.scroll && (this.options.scroll != window)) {
+      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+    }
+    
+    var p = [0,1].map(function(i){ 
+      return (point[i]-pos[i]-this.offset[i]) 
+    }.bind(this));
+    
+    if(this.options.snap) {
+      if(typeof this.options.snap == 'function') {
+        p = this.options.snap(p[0],p[1],this);
+      } else {
+      if(this.options.snap instanceof Array) {
+        p = p.map( function(v, i) {
+          return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
+      } else {
+        p = p.map( function(v) {
+          return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
+      }
+    }}
+    
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = p[0] + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = p[1] + "px";
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  },
+  
+  stopScrolling: function() {
+    if(this.scrollInterval) {
+      clearInterval(this.scrollInterval);
+      this.scrollInterval = null;
+      Draggables._lastScrollPointer = null;
+    }
+  },
+  
+  startScrolling: function(speed) {
+    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+    this.lastScrolled = new Date();
+    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+  },
+  
+  scroll: function() {
+    var current = new Date();
+    var delta = current - this.lastScrolled;
+    this.lastScrolled = current;
+    if(this.options.scroll == window) {
+      with (this._getWindowScroll(this.options.scroll)) {
+        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+          var d = delta / 1000;
+          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+        }
+      }
+    } else {
+      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
+    }
+    
+    Position.prepare();
+    Droppables.show(Draggables._lastPointer, this.element);
+    Draggables.notify('onDrag', this);
+    Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+    Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+    Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+    if (Draggables._lastScrollPointer[0] < 0)
+      Draggables._lastScrollPointer[0] = 0;
+    if (Draggables._lastScrollPointer[1] < 0)
+      Draggables._lastScrollPointer[1] = 0;
+    this.draw(Draggables._lastScrollPointer);
+    
+    if(this.options.change) this.options.change(this);
+  },
+  
+  _getWindowScroll: function(w) {
+    var T, L, W, H;
+    with (w.document) {
+      if (w.document.documentElement && documentElement.scrollTop) {
+        T = documentElement.scrollTop;
+        L = documentElement.scrollLeft;
+      } else if (w.document.body) {
+        T = body.scrollTop;
+        L = body.scrollLeft;
+      }
+      if (w.innerWidth) {
+        W = w.innerWidth;
+        H = w.innerHeight;
+      } else if (w.document.documentElement && documentElement.clientWidth) {
+        W = documentElement.clientWidth;
+        H = documentElement.clientHeight;
+      } else {
+        W = body.offsetWidth;
+        H = body.offsetHeight
+      }
+    }
+    return { top: T, left: L, width: W, height: H };
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+}
+
+var Sortable = {
+  sortables: {},
+  
+  _findRootElement: function(element) {
+    while (element.tagName != "BODY") {  
+      if(element.id && Sortable.sortables[element.id]) return element;
+      element = element.parentNode;
+    }
+  },
+
+  options: function(element) {
+    element = Sortable._findRootElement($(element));
+    if(!element) return;
+    return Sortable.sortables[element.id];
+  },
+  
+  destroy: function(element){
+    var s = Sortable.options(element);
+    
+    if(s) {
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+      
+      delete Sortable.sortables[s.element.id];
+    }
+  },
+
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,
+      treeTag:     'ul',
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      hoverclass:  null,
+      ghosting:    false,
+      scroll:      false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      format:      /^[^_]*_(.*)$/,
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || {});
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      scroll:      options.scroll,
+      scrollSpeed: options.scrollSpeed,
+      scrollSensitivity: options.scrollSensitivity,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      tree:        options.tree,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover
+      //greedy:      !options.dropOnEmpty
+    }
+    
+    var options_for_tree = {
+      onHover:      Sortable.onEmptyHover,
+      overlap:      options.overlap,
+      containment:  options.containment,
+      hoverclass:   options.hoverclass
+    }
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // drop on empty handling
+    if(options.dropOnEmpty || options.tree) {
+      Droppables.add(element, options_for_tree);
+      options.droppables.push(element);
+    }
+
+    (this.findElements(element, options) || []).each( function(e) {
+      // handles are per-draggable
+      var handle = options.handle ? 
+        Element.childrenWithClassName(e, options.handle)[0] : e;    
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      if(options.tree) e.treeNode = element;
+      options.droppables.push(e);      
+    });
+    
+    if(options.tree) {
+      (Sortable.findTreeElements(element, options) || []).each( function(e) {
+        Droppables.add(e, options_for_tree);
+        e.treeNode = element;
+        options.droppables.push(e);
+      });
+    }
+
+    // keep reference
+    this.sortables[element.id] = options;
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.tag);
+  },
+  
+  findTreeElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.treeTag);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(Element.isParent(dropon, element)) return;
+
+    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+      return;
+    } else if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+  
+  onEmptyHover: function(element, dropon, overlap) {
+    var oldParentNode = element.parentNode;
+    var droponOptions = Sortable.options(dropon);
+        
+    if(!Element.isParent(dropon, element)) {
+      var index;
+      
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
+      var child = null;
+            
+      if(children) {
+        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+        
+        for (index = 0; index < children.length; index += 1) {
+          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+            offset -= Element.offsetSize (children[index], droponOptions.overlap);
+          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+            child = index + 1 < children.length ? children[index + 1] : null;
+            break;
+          } else {
+            child = children[index];
+            break;
+          }
+        }
+      }
+      
+      dropon.insertBefore(element, child);
+      
+      Sortable.options(oldParentNode).onChange(element);
+      droponOptions.onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Element.hide(Sortable._marker);
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      Sortable._marker = $('dropmarker') || document.createElement('DIV');
+      Element.hide(Sortable._marker);
+      Element.addClassName(Sortable._marker, 'dropmarker');
+      Sortable._marker.style.position = 'absolute';
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }    
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.style.left = offsets[0] + 'px';
+    Sortable._marker.style.top = offsets[1] + 'px';
+    
+    if(position=='after')
+      if(sortable.overlap == 'horizontal') 
+        Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+      else
+        Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+    
+    Element.show(Sortable._marker);
+  },
+  
+  _tree: function(element, options, parent) {
+    var children = Sortable.findElements(element, options) || [];
+  
+    for (var i = 0; i < children.length; ++i) {
+      var match = children[i].id.match(options.format);
+
+      if (!match) continue;
+      
+      var child = {
+        id: encodeURIComponent(match ? match[1] : null),
+        element: element,
+        parent: parent,
+        children: new Array,
+        position: parent.children.length,
+        container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
+      }
+      
+      /* Get the element containing the children and recurse over it */
+      if (child.container)
+        this._tree(child.container, options, child)
+      
+      parent.children.push (child);
+    }
+
+    return parent; 
+  },
+
+  /* Finds the first element of the given tag type within a parent element.
+    Used for finding the first LI[ST] within a L[IST]I[TEM].*/
+  _findChildrenElement: function (element, containerTag) {
+    if (element && element.hasChildNodes)
+      for (var i = 0; i < element.childNodes.length; ++i)
+        if (element.childNodes[i].tagName == containerTag)
+          return element.childNodes[i];
+  
+    return null;
+  },
+
+  tree: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag: sortableOptions.tag,
+      treeTag: sortableOptions.treeTag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format
+    }, arguments[1] || {});
+    
+    var root = {
+      id: null,
+      parent: null,
+      children: new Array,
+      container: element,
+      position: 0
+    }
+    
+    return Sortable._tree (element, options, root);
+  },
+
+  /* Construct a [i] index for a particular node */
+  _constructIndex: function(node) {
+    var index = '';
+    do {
+      if (node.id) index = '[' + node.position + ']' + index;
+    } while ((node = node.parent) != null);
+    return index;
+  },
+
+  sequence: function(element) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[1] || {});
+    
+    return $(this.findElements(element, options) || []).map( function(item) {
+      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+    });
+  },
+
+  setSequence: function(element, new_sequence) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[2] || {});
+    
+    var nodeMap = {};
+    this.findElements(element, options).each( function(n) {
+        if (n.id.match(options.format))
+            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+        n.parentNode.removeChild(n);
+    });
+   
+    new_sequence.each(function(ident) {
+      var n = nodeMap[ident];
+      if (n) {
+        n[1].appendChild(n[0]);
+        delete nodeMap[ident];
+      }
+    });
+  },
+  
+  serialize: function(element) {
+    element = $(element);
+    var options = Object.extend(Sortable.options(element), arguments[1] || {});
+    var name = encodeURIComponent(
+      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+    
+    if (options.tree) {
+      return Sortable.tree(element, arguments[1]).children.map( function (item) {
+        return [name + Sortable._constructIndex(item) + "=" + 
+                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+      }).flatten().join('&');
+    } else {
+      return Sortable.sequence(element, arguments[1]).map( function(item) {
+        return name + "[]=" + encodeURIComponent(item);
+      }).join('&');
+    }
+  }
+}
+
+/* Returns true if child is contained within element */
+Element.isParent = function(child, element) {
+  if (!child.parentNode || child == element) return false;
+
+  if (child.parentNode == element) return true;
+
+  return Element.isParent(child.parentNode, element);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {    
+  if(!element.hasChildNodes()) return null;
+  tagName = tagName.toUpperCase();
+  if(only) only = [only].flatten();
+  var elements = [];
+  $A(element.childNodes).each( function(e) {
+    if(e.tagName && e.tagName.toUpperCase()==tagName &&
+      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+        elements.push(e);
+    if(recursive) {
+      var grandchildren = Element.findChildren(e, only, recursive, tagName);
+      if(grandchildren) elements.push(grandchildren);
+    }
+  });
+
+  return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+  if (type == 'vertical' || type == 'height')
+    return element.offsetHeight;
+  else
+    return element.offsetWidth;
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/effects.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/effects.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/effects.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,958 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+//  Justin Palmer (http://encytemedia.com/)
+//  Mark Pilgrim (http://diveintomark.org/)
+//  Martin Bialasinki
+// 
+// See scriptaculous.js for full license.  
+
+// converts rgb() and #xxx to #xxxxxx format,  
+// returns self (or first argument) if not convertable  
+String.prototype.parseColor = function() {  
+  var color = '#';  
+  if(this.slice(0,4) == 'rgb(') {  
+    var cols = this.slice(4,this.length-1).split(',');  
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
+  } else {  
+    if(this.slice(0,1) == '#') {  
+      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
+      if(this.length==7) color = this.toLowerCase();  
+    }  
+  }  
+  return(color.length==7 ? color : (arguments[0] || this));  
+}
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+  }).flatten().join('');
+}
+
+Element.collectTextNodesIgnoreClass = function(element, className) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
+        Element.collectTextNodesIgnoreClass(node, className) : ''));
+  }).flatten().join('');
+}
+
+Element.setContentZoom = function(element, percent) {
+  element = $(element);  
+  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
+  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+}
+
+Element.getOpacity = function(element){  
+  var opacity;
+  if (opacity = Element.getStyle(element, 'opacity'))  
+    return parseFloat(opacity);  
+  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
+    if(opacity[1]) return parseFloat(opacity[1]) / 100;  
+  return 1.0;  
+}
+
+Element.setOpacity = function(element, value){  
+  element= $(element);  
+  if (value == 1){
+    Element.setStyle(element, { opacity: 
+      (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
+      0.999999 : null });
+    if(/MSIE/.test(navigator.userAgent))  
+      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
+  } else {  
+    if(value < 0.00001) value = 0;  
+    Element.setStyle(element, {opacity: value});
+    if(/MSIE/.test(navigator.userAgent))  
+     Element.setStyle(element, 
+       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
+                 'alpha(opacity='+value*100+')' });  
+  }
+}  
+ 
+Element.getInlineOpacity = function(element){  
+  return $(element).style.opacity || '';
+}  
+
+Element.childrenWithClassName = function(element, className, findFirst) {
+  var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
+  var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { 
+    return (c.className && c.className.match(classNameRegExp));
+  });
+  if(!results) results = [];
+  return results;
+}
+
+Element.forceRerendering = function(element) {
+  try {
+    element = $(element);
+    var n = document.createTextNode(' ');
+    element.appendChild(n);
+    element.removeChild(n);
+  } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Array.prototype.call = function() {
+  var args = arguments;
+  this.each(function(f){ f.apply(this, args) });
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+    element = $(element);
+    $A(element.childNodes).each( function(child) {
+      if(child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            Builder.node('span',{style: tagifyStyle},
+              character == ' ' ? String.fromCharCode(160) : character), 
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if(((typeof element == 'object') || 
+        (typeof element == 'function')) && 
+       (element.length))
+      elements = element;
+    else
+      elements = $(element).childNodes;
+      
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || {});
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  },
+  PAIRS: {
+    'slide':  ['SlideDown','SlideUp'],
+    'blind':  ['BlindDown','BlindUp'],
+    'appear': ['Appear','Fade']
+  },
+  toggle: function(element, effect) {
+    element = $(element);
+    effect = (effect || 'appear').toLowerCase();
+    var options = Object.extend({
+      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+    }, arguments[2] || {});
+    Effect[element.visible() ? 
+      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+  }
+};
+
+var Effect2 = Effect; // deprecated
+
+/* ------------- transitions ------------- */
+
+Effect.Transitions = {}
+
+Effect.Transitions.linear = function(pos) {
+  return pos;
+}
+Effect.Transitions.sinoidal = function(pos) {
+  return (-Math.cos(pos*Math.PI)/2) + 0.5;
+}
+Effect.Transitions.reverse  = function(pos) {
+  return 1-pos;
+}
+Effect.Transitions.flicker = function(pos) {
+  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+}
+Effect.Transitions.wobble = function(pos) {
+  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+}
+Effect.Transitions.pulse = function(pos) {
+  return (Math.floor(pos*10) % 2 == 0 ? 
+    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
+}
+Effect.Transitions.none = function(pos) {
+  return 0;
+}
+Effect.Transitions.full = function(pos) {
+  return 1;
+}
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create();
+Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
+  initialize: function() {
+    this.effects  = [];
+    this.interval = null;
+  },
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+    
+    var position = (typeof effect.options.queue == 'string') ? 
+      effect.options.queue : effect.options.queue.position;
+    
+    switch(position) {
+      case 'front':
+        // move unstarted effects after this effect  
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        break;
+      case 'end':
+        // start effect after last queued effect has finished
+        timestamp = this.effects.pluck('finishOn').max() || timestamp;
+        break;
+    }
+    
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+
+    if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+      this.effects.push(effect);
+    
+    if(!this.interval) 
+      this.interval = setInterval(this.loop.bind(this), 40);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if(this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    this.effects.invoke('loop', timePos);
+  }
+});
+
+Effect.Queues = {
+  instances: $H(),
+  get: function(queueName) {
+    if(typeof queueName != 'string') return queueName;
+    
+    if(!this.instances[queueName])
+      this.instances[queueName] = new Effect.ScopedQueue();
+      
+    return this.instances[queueName];
+  }
+}
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.DefaultOptions = {
+  transition: Effect.Transitions.sinoidal,
+  duration:   1.0,   // seconds
+  fps:        25.0,  // max. 25fps due to Effect.Queue implementation
+  sync:       false, // true for combining
+  from:       0.0,
+  to:         1.0,
+  delay:      0.0,
+  queue:      'parallel'
+}
+
+Effect.Base = function() {};
+Effect.Base.prototype = {
+  position: null,
+  start: function(options) {
+    this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn + (this.options.duration*1000);
+    this.event('beforeStart');
+    if(!this.options.sync)
+      Effect.Queues.get(typeof this.options.queue == 'string' ? 
+        'global' : this.options.queue.scope).add(this);
+  },
+  loop: function(timePos) {
+    if(timePos >= this.startOn) {
+      if(timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if(this.finish) this.finish(); 
+        this.event('afterFinish');
+        return;  
+      }
+      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
+      var frame = Math.round(pos * this.options.fps * this.options.duration);
+      if(frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  render: function(pos) {
+    if(this.state == 'idle') {
+      this.state = 'running';
+      this.event('beforeSetup');
+      if(this.setup) this.setup();
+      this.event('afterSetup');
+    }
+    if(this.state == 'running') {
+      if(this.options.transition) pos = this.options.transition(pos);
+      pos *= (this.options.to-this.options.from);
+      pos += this.options.from;
+      this.position = pos;
+      this.event('beforeUpdate');
+      if(this.update) this.update(pos);
+      this.event('afterUpdate');
+    }
+  },
+  cancel: function() {
+    if(!this.options.sync)
+      Effect.Queues.get(typeof this.options.queue == 'string' ? 
+        'global' : this.options.queue.scope).remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if(this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+}
+
+Effect.Parallel = Class.create();
+Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if(effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Opacity = Class.create();
+Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    // make this work on IE on elements without 'layout'
+    if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
+      this.element.setStyle({zoom: 1});
+    var options = Object.extend({
+      from: this.element.getOpacity() || 0.0,
+      to:   1.0
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  update: function(position) {
+    this.element.setOpacity(position);
+  }
+});
+
+Effect.Move = Class.create();
+Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    var options = Object.extend({
+      x:    0,
+      y:    0,
+      mode: 'relative'
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function() {
+    // Bug in Opera: Opera returns the "real" position of a static element or
+    // relative element that does not have top/left explicitly set.
+    // ==> Always set top and left for position relative elements in your stylesheets 
+    // (to 0 if you do not need them) 
+    this.element.makePositioned();
+    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
+    if(this.options.mode == 'absolute') {
+      // absolute movement, so we need to calc deltaX and deltaY
+      this.options.x = this.options.x - this.originalLeft;
+      this.options.y = this.options.y - this.originalTop;
+    }
+  },
+  update: function(position) {
+    this.element.setStyle({
+      left: this.options.x  * position + this.originalLeft + 'px',
+      top:  this.options.y  * position + this.originalTop  + 'px'
+    });
+  }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+  return new Effect.Move(element, 
+    Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
+};
+
+Effect.Scale = Class.create();
+Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
+  initialize: function(element, percent) {
+    this.element = $(element)
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || {});
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = this.element.getStyle('position');
+    
+    this.originalStyle = {};
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+      
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+    
+    var fontSize = this.element.getStyle('font-size') || '100%';
+    ['em','px','%'].each( function(fontSizeType) {
+      if(fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+    
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+    
+    this.dims = null;
+    if(this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if(/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if(!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if(this.options.scaleContent && this.fontSize)
+      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = {};
+    if(this.options.scaleX) d.width = width + 'px';
+    if(this.options.scaleY) d.height = height + 'px';
+    if(this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if(this.elementPositioning == 'absolute') {
+        if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if(this.options.scaleY) d.top = -topd + 'px';
+        if(this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    this.element.setStyle(d);
+  }
+});
+
+Effect.Highlight = Class.create();
+Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if(this.element.getStyle('display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = {
+      backgroundImage: this.element.getStyle('background-image') };
+    this.element.setStyle({backgroundImage: 'none'});
+    if(!this.options.endcolor)
+      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+    if(!this.options.restorecolor)
+      this.options.restorecolor = this.element.getStyle('background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    this.element.setStyle(Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = Class.create();
+Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    this.start(arguments[1] || {});
+  },
+  setup: function() {
+    Position.prepare();
+    var offsets = Position.cumulativeOffset(this.element);
+    if(this.options.offset) offsets[1] += this.options.offset;
+    var max = window.innerHeight ? 
+      window.height - window.innerHeight :
+      document.body.scrollHeight - 
+        (document.documentElement.clientHeight ? 
+          document.documentElement.clientHeight : document.body.clientHeight);
+    this.scrollStart = Position.deltaY;
+    this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
+  },
+  update: function(position) {
+    Position.prepare();
+    window.scrollTo(Position.deltaX, 
+      this.scrollStart + (position*this.delta));
+  }
+});
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  var options = Object.extend({
+  from: element.getOpacity() || 1.0,
+  to:   0.0,
+  afterFinishInternal: function(effect) { 
+    if(effect.options.to!=0) return;
+    effect.element.hide();
+    effect.element.setStyle({opacity: oldOpacity}); 
+  }}, arguments[1] || {});
+  return new Effect.Opacity(element,options);
+}
+
+Effect.Appear = function(element) {
+  element = $(element);
+  var options = Object.extend({
+  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+  to:   1.0,
+  // force Safari to render floated elements properly
+  afterFinishInternal: function(effect) {
+    effect.element.forceRerendering();
+  },
+  beforeSetup: function(effect) {
+    effect.element.setOpacity(effect.options.from);
+    effect.element.show(); 
+  }}, arguments[1] || {});
+  return new Effect.Opacity(element,options);
+}
+
+Effect.Puff = function(element) {
+  element = $(element);
+  var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
+  return new Effect.Parallel(
+   [ new Effect.Scale(element, 200, 
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
+     Object.extend({ duration: 1.0, 
+      beforeSetupInternal: function(effect) {
+        effect.effects[0].element.setStyle({position: 'absolute'}); },
+      afterFinishInternal: function(effect) {
+         effect.effects[0].element.hide();
+         effect.effects[0].element.setStyle(oldStyle); }
+     }, arguments[1] || {})
+   );
+}
+
+Effect.BlindUp = function(element) {
+  element = $(element);
+  element.makeClipping();
+  return new Effect.Scale(element, 0, 
+    Object.extend({ scaleContent: false, 
+      scaleX: false, 
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) {
+        effect.element.hide();
+        effect.element.undoClipping();
+      } 
+    }, arguments[1] || {})
+  );
+}
+
+Effect.BlindDown = function(element) {
+  element = $(element);
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, 
+    Object.extend({ scaleContent: false, 
+      scaleX: false,
+      scaleFrom: 0,
+      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+      restoreAfterFinish: true,
+      afterSetup: function(effect) {
+        effect.element.makeClipping();
+        effect.element.setStyle({height: '0px'});
+        effect.element.show(); 
+      },  
+      afterFinishInternal: function(effect) {
+        effect.element.undoClipping();
+      }
+    }, arguments[1] || {})
+  );
+}
+
+Effect.SwitchOff = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  return new Effect.Appear(element, { 
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, { 
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) { 
+          effect.element.makePositioned();
+          effect.element.makeClipping();
+        },
+        afterFinishInternal: function(effect) {
+          effect.element.hide();
+          effect.element.undoClipping();
+          effect.element.undoPositioned();
+          effect.element.setStyle({opacity: oldOpacity});
+        }
+      })
+    }
+  });
+}
+
+Effect.DropOut = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left'),
+    opacity: element.getInlineOpacity() };
+  return new Effect.Parallel(
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) {
+          effect.effects[0].element.makePositioned(); 
+        },
+        afterFinishInternal: function(effect) {
+          effect.effects[0].element.hide();
+          effect.effects[0].element.undoPositioned();
+          effect.effects[0].element.setStyle(oldStyle);
+        } 
+      }, arguments[1] || {}));
+}
+
+Effect.Shake = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left') };
+    return new Effect.Move(element, 
+      { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+        effect.element.undoPositioned();
+        effect.element.setStyle(oldStyle);
+  }}) }}) }}) }}) }}) }});
+}
+
+Effect.SlideDown = function(element) {
+  element = $(element);
+  element.cleanWhitespace();
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false, 
+    scaleFrom: window.opera ? 0 : 1,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.firstChild.makePositioned();
+      if(window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping();
+      effect.element.setStyle({height: '0px'});
+      effect.element.show(); },
+    afterUpdateInternal: function(effect) {
+      effect.element.firstChild.setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping(); 
+      // IE will crash if child is undoPositioned first
+      if(/MSIE/.test(navigator.userAgent)){
+        effect.element.undoPositioned();
+        effect.element.firstChild.undoPositioned();
+      }else{
+        effect.element.firstChild.undoPositioned();
+        effect.element.undoPositioned();
+      }
+      effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
+    }, arguments[1] || {})
+  );
+}
+  
+Effect.SlideUp = function(element) {
+  element = $(element);
+  element.cleanWhitespace();
+  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+  return new Effect.Scale(element, window.opera ? 0 : 1,
+   Object.extend({ scaleContent: false, 
+    scaleX: false, 
+    scaleMode: 'box',
+    scaleFrom: 100,
+    restoreAfterFinish: true,
+    beforeStartInternal: function(effect) {
+      effect.element.makePositioned();
+      effect.element.firstChild.makePositioned();
+      if(window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping();
+      effect.element.show(); },  
+    afterUpdateInternal: function(effect) {
+      effect.element.firstChild.setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
+    afterFinishInternal: function(effect) {
+      effect.element.hide();
+      effect.element.undoClipping();
+      effect.element.firstChild.undoPositioned();
+      effect.element.undoPositioned();
+      effect.element.setStyle({bottom: oldInnerBottom}); }
+   }, arguments[1] || {})
+  );
+}
+
+// Bug in opera makes the TD containing this element expand for a instance after finish 
+Effect.Squish = function(element) {
+  return new Effect.Scale(element, window.opera ? 1 : 0, 
+    { restoreAfterFinish: true,
+      beforeSetup: function(effect) {
+        effect.element.makeClipping(effect.element); },  
+      afterFinishInternal: function(effect) {
+        effect.element.hide(effect.element); 
+        effect.element.undoClipping(effect.element); }
+  });
+}
+
+Effect.Grow = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || {});
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();    
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Move(element, {
+    x: initialMoveX,
+    y: initialMoveY,
+    duration: 0.01, 
+    beforeSetup: function(effect) {
+      effect.element.hide();
+      effect.element.makeClipping();
+      effect.element.makePositioned();
+    },
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) {
+               effect.effects[0].element.setStyle({height: '0px'});
+               effect.effects[0].element.show(); 
+             },
+             afterFinishInternal: function(effect) {
+               effect.effects[0].element.undoClipping();
+               effect.effects[0].element.undoPositioned();
+               effect.effects[0].element.setStyle(oldStyle); 
+             }
+           }, options)
+      )
+    }
+  });
+}
+
+Effect.Shrink = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || {});
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':  
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+    ], Object.extend({            
+         beforeStartInternal: function(effect) {
+           effect.effects[0].element.makePositioned();
+           effect.effects[0].element.makeClipping(); },
+         afterFinishInternal: function(effect) {
+           effect.effects[0].element.hide();
+           effect.effects[0].element.undoClipping();
+           effect.effects[0].element.undoPositioned();
+           effect.effects[0].element.setStyle(oldStyle); }
+       }, options)
+  );
+}
+
+Effect.Pulsate = function(element) {
+  element = $(element);
+  var options    = arguments[1] || {};
+  var oldOpacity = element.getInlineOpacity();
+  var transition = options.transition || Effect.Transitions.sinoidal;
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+  reverser.bind(transition);
+  return new Effect.Opacity(element, 
+    Object.extend(Object.extend({  duration: 3.0, from: 0,
+      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+}
+
+Effect.Fold = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  Element.makeClipping(element);
+  return new Effect.Scale(element, 5, Object.extend({   
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, { 
+      scaleContent: false, 
+      scaleY: false,
+      afterFinishInternal: function(effect) {
+        effect.element.hide();
+        effect.element.undoClipping(); 
+        effect.element.setStyle(oldStyle);
+      } });
+  }}, arguments[1] || {}));
+};
+
+['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
+ 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( 
+  function(f) { Element.Methods[f] = Element[f]; }
+);
+
+Element.Methods.visualEffect = function(element, effect, options) {
+  s = effect.gsub(/_/, '-').camelize();
+  effect_class = s.charAt(0).toUpperCase() + s.substring(1);
+  new Effect[effect_class](element, options);
+  return $(element);
+};
+
+Element.addMethods();
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/scriptaculous.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/scriptaculous.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/scriptaculous.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,47 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var Scriptaculous = {
+  Version: '1.6.1',
+  require: function(libraryName) {
+    // inserting via DOM fails in Safari 2.0, so brute force approach
+    document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
+  },
+  load: function() {
+    if((typeof Prototype=='undefined') || 
+       (typeof Element == 'undefined') || 
+       (typeof Element.Methods=='undefined') ||
+       parseFloat(Prototype.Version.split(".")[0] + "." +
+                  Prototype.Version.split(".")[1]) < 1.5)
+       throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0");
+    
+    $A(document.getElementsByTagName("script")).findAll( function(s) {
+      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+    }).each( function(s) {
+      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+      var includes = s.src.match(/\?.*load=([a-z,]*)/);
+      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each(
+       function(include) { Scriptaculous.require(path+include+'.js') });
+    });
+  }
+}
+
+Scriptaculous.load();
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/slider.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/slider.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/slider.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,283 @@
+// Copyright (c) 2005 Marty Haught, Thomas Fuchs 
+//
+// See http://script.aculo.us for more info
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if(!Control) var Control = {};
+Control.Slider = Class.create();
+
+// options:
+//  axis: 'vertical', or 'horizontal' (default)
+//
+// callbacks:
+//  onChange(value)
+//  onSlide(value)
+Control.Slider.prototype = {
+  initialize: function(handle, track, options) {
+    var slider = this;
+    
+    if(handle instanceof Array) {
+      this.handles = handle.collect( function(e) { return $(e) });
+    } else {
+      this.handles = [$(handle)];
+    }
+    
+    this.track   = $(track);
+    this.options = options || {};
+
+    this.axis      = this.options.axis || 'horizontal';
+    this.increment = this.options.increment || 1;
+    this.step      = parseInt(this.options.step || '1');
+    this.range     = this.options.range || $R(0,1);
+    
+    this.value     = 0; // assure backwards compat
+    this.values    = this.handles.map( function() { return 0 });
+    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
+    this.options.startSpan = $(this.options.startSpan || null);
+    this.options.endSpan   = $(this.options.endSpan || null);
+
+    this.restricted = this.options.restricted || false;
+
+    this.maximum   = this.options.maximum || this.range.end;
+    this.minimum   = this.options.minimum || this.range.start;
+
+    // Will be used to align the handle onto the track, if necessary
+    this.alignX = parseInt(this.options.alignX || '0');
+    this.alignY = parseInt(this.options.alignY || '0');
+    
+    this.trackLength = this.maximumOffset() - this.minimumOffset();
+    this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth;
+
+    this.active   = false;
+    this.dragging = false;
+    this.disabled = false;
+
+    if(this.options.disabled) this.setDisabled();
+
+    // Allowed values array
+    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
+    if(this.allowedValues) {
+      this.minimum = this.allowedValues.min();
+      this.maximum = this.allowedValues.max();
+    }
+
+    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+    this.eventMouseMove = this.update.bindAsEventListener(this);
+
+    // Initialize handles in reverse (make sure first handle is active)
+    this.handles.each( function(h,i) {
+      i = slider.handles.length-1-i;
+      slider.setValue(parseFloat(
+        (slider.options.sliderValue instanceof Array ? 
+          slider.options.sliderValue[i] : slider.options.sliderValue) || 
+         slider.range.start), i);
+      Element.makePositioned(h); // fix IE
+      Event.observe(h, "mousedown", slider.eventMouseDown);
+    });
+    
+    Event.observe(this.track, "mousedown", this.eventMouseDown);
+    Event.observe(document, "mouseup", this.eventMouseUp);
+    Event.observe(document, "mousemove", this.eventMouseMove);
+    
+    this.initialized = true;
+  },
+  dispose: function() {
+    var slider = this;    
+    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
+    Event.stopObserving(document, "mouseup", this.eventMouseUp);
+    Event.stopObserving(document, "mousemove", this.eventMouseMove);
+    this.handles.each( function(h) {
+      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
+    });
+  },
+  setDisabled: function(){
+    this.disabled = true;
+  },
+  setEnabled: function(){
+    this.disabled = false;
+  },  
+  getNearestValue: function(value){
+    if(this.allowedValues){
+      if(value >= this.allowedValues.max()) return(this.allowedValues.max());
+      if(value <= this.allowedValues.min()) return(this.allowedValues.min());
+      
+      var offset = Math.abs(this.allowedValues[0] - value);
+      var newValue = this.allowedValues[0];
+      this.allowedValues.each( function(v) {
+        var currentOffset = Math.abs(v - value);
+        if(currentOffset <= offset){
+          newValue = v;
+          offset = currentOffset;
+        } 
+      });
+      return newValue;
+    }
+    if(value > this.range.end) return this.range.end;
+    if(value < this.range.start) return this.range.start;
+    return value;
+  },
+  setValue: function(sliderValue, handleIdx){
+    if(!this.active) {
+      this.activeHandle    = this.handles[handleIdx];
+      this.activeHandleIdx = handleIdx;
+      this.updateStyles();
+    }
+    handleIdx = handleIdx || this.activeHandleIdx || 0;
+    if(this.initialized && this.restricted) {
+      if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
+        sliderValue = this.values[handleIdx-1];
+      if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
+        sliderValue = this.values[handleIdx+1];
+    }
+    sliderValue = this.getNearestValue(sliderValue);
+    this.values[handleIdx] = sliderValue;
+    this.value = this.values[0]; // assure backwards compat
+    
+    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 
+      this.translateToPx(sliderValue);
+    
+    this.drawSpans();
+    if(!this.dragging || !this.event) this.updateFinished();
+  },
+  setValueBy: function(delta, handleIdx) {
+    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 
+      handleIdx || this.activeHandleIdx || 0);
+  },
+  translateToPx: function(value) {
+    return Math.round(
+      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 
+      (value - this.range.start)) + "px";
+  },
+  translateToValue: function(offset) {
+    return ((offset/(this.trackLength-this.handleLength) * 
+      (this.range.end-this.range.start)) + this.range.start);
+  },
+  getRange: function(range) {
+    var v = this.values.sortBy(Prototype.K); 
+    range = range || 0;
+    return $R(v[range],v[range+1]);
+  },
+  minimumOffset: function(){
+    return(this.isVertical() ? this.alignY : this.alignX);
+  },
+  maximumOffset: function(){
+    return(this.isVertical() ?
+      this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX);
+  },  
+  isVertical:  function(){
+    return (this.axis == 'vertical');
+  },
+  drawSpans: function() {
+    var slider = this;
+    if(this.spans)
+      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
+    if(this.options.startSpan)
+      this.setSpan(this.options.startSpan,
+        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
+    if(this.options.endSpan)
+      this.setSpan(this.options.endSpan, 
+        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
+  },
+  setSpan: function(span, range) {
+    if(this.isVertical()) {
+      span.style.top = this.translateToPx(range.start);
+      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
+    } else {
+      span.style.left = this.translateToPx(range.start);
+      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
+    }
+  },
+  updateStyles: function() {
+    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
+    Element.addClassName(this.activeHandle, 'selected');
+  },
+  startDrag: function(event) {
+    if(Event.isLeftClick(event)) {
+      if(!this.disabled){
+        this.active = true;
+        
+        var handle = Event.element(event);
+        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
+        if(handle==this.track) {
+          var offsets  = Position.cumulativeOffset(this.track); 
+          this.event = event;
+          this.setValue(this.translateToValue( 
+           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
+          ));
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        } else {
+          // find the handle (prevents issues with Safari)
+          while((this.handles.indexOf(handle) == -1) && handle.parentNode) 
+            handle = handle.parentNode;
+        
+          this.activeHandle    = handle;
+          this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+          this.updateStyles();
+        
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        }
+      }
+      Event.stop(event);
+    }
+  },
+  update: function(event) {
+   if(this.active) {
+      if(!this.dragging) this.dragging = true;
+      this.draw(event);
+      // fix AppleWebKit rendering
+      if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+      Event.stop(event);
+   }
+  },
+  draw: function(event) {
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    var offsets = Position.cumulativeOffset(this.track);
+    pointer[0] -= this.offsetX + offsets[0];
+    pointer[1] -= this.offsetY + offsets[1];
+    this.event = event;
+    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
+    if(this.initialized && this.options.onSlide)
+      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
+  },
+  endDrag: function(event) {
+    if(this.active && this.dragging) {
+      this.finishDrag(event, true);
+      Event.stop(event);
+    }
+    this.active = false;
+    this.dragging = false;
+  },  
+  finishDrag: function(event, success) {
+    this.active = false;
+    this.dragging = false;
+    this.updateFinished();
+  },
+  updateFinished: function() {
+    if(this.initialized && this.options.onChange) 
+      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
+    this.event = null;
+  }
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/unittest.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/unittest.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/lib/scriptaculous/unittest.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,383 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+//           (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+  var options = Object.extend({
+    pointerX: 0,
+    pointerY: 0,
+    buttons: 0
+  }, arguments[2] || {});
+  var oEvent = document.createEvent("MouseEvents");
+  oEvent.initMouseEvent(eventName, true, true, document.defaultView, 
+    options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, 
+    false, false, false, false, 0, $(element));
+  
+  if(this.mark) Element.remove(this.mark);
+  this.mark = document.createElement('div');
+  this.mark.appendChild(document.createTextNode(" "));
+  document.body.appendChild(this.mark);
+  this.mark.style.position = 'absolute';
+  this.mark.style.top = options.pointerY + "px";
+  this.mark.style.left = options.pointerX + "px";
+  this.mark.style.width = "5px";
+  this.mark.style.height = "5px;";
+  this.mark.style.borderTop = "1px solid red;"
+  this.mark.style.borderLeft = "1px solid red;"
+  
+  if(this.step)
+    alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+  
+  $(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+  var options = Object.extend({
+    ctrlKey: false,
+    altKey: false,
+    shiftKey: false,
+    metaKey: false,
+    keyCode: 0,
+    charCode: 0
+  }, arguments[2] || {});
+
+  var oEvent = document.createEvent("KeyEvents");
+  oEvent.initKeyEvent(eventName, true, true, window, 
+    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+    options.keyCode, options.charCode );
+  $(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+  for(var i=0; i<command.length; i++) {
+    Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+  }
+};
+
+var Test = {}
+Test.Unit = {};
+
+// security exception workaround
+Test.Unit.inspect = Object.inspect;
+
+Test.Unit.Logger = Class.create();
+Test.Unit.Logger.prototype = {
+  initialize: function(log) {
+    this.log = $(log);
+    if (this.log) {
+      this._createLogTable();
+    }
+  },
+  start: function(testName) {
+    if (!this.log) return;
+    this.testName = testName;
+    this.lastLogLine = document.createElement('tr');
+    this.statusCell = document.createElement('td');
+    this.nameCell = document.createElement('td');
+    this.nameCell.appendChild(document.createTextNode(testName));
+    this.messageCell = document.createElement('td');
+    this.lastLogLine.appendChild(this.statusCell);
+    this.lastLogLine.appendChild(this.nameCell);
+    this.lastLogLine.appendChild(this.messageCell);
+    this.loglines.appendChild(this.lastLogLine);
+  },
+  finish: function(status, summary) {
+    if (!this.log) return;
+    this.lastLogLine.className = status;
+    this.statusCell.innerHTML = status;
+    this.messageCell.innerHTML = this._toHTML(summary);
+  },
+  message: function(message) {
+    if (!this.log) return;
+    this.messageCell.innerHTML = this._toHTML(message);
+  },
+  summary: function(summary) {
+    if (!this.log) return;
+    this.logsummary.innerHTML = this._toHTML(summary);
+  },
+  _createLogTable: function() {
+    this.log.innerHTML =
+    '<div id="logsummary"></div>' +
+    '<table id="logtable">' +
+    '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+    '<tbody id="loglines"></tbody>' +
+    '</table>';
+    this.logsummary = $('logsummary')
+    this.loglines = $('loglines');
+  },
+  _toHTML: function(txt) {
+    return txt.escapeHTML().replace(/\n/g,"<br/>");
+  }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+  initialize: function(testcases) {
+    this.options = Object.extend({
+      testLog: 'testlog'
+    }, arguments[1] || {});
+    this.options.resultsURL = this.parseResultsURLQueryParameter();
+    if (this.options.testLog) {
+      this.options.testLog = $(this.options.testLog) || null;
+    }
+    if(this.options.tests) {
+      this.tests = [];
+      for(var i = 0; i < this.options.tests.length; i++) {
+        if(/^test/.test(this.options.tests[i])) {
+          this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+        }
+      }
+    } else {
+      if (this.options.test) {
+        this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+      } else {
+        this.tests = [];
+        for(var testcase in testcases) {
+          if(/^test/.test(testcase)) {
+            this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"]));
+          }
+        }
+      }
+    }
+    this.currentTest = 0;
+    this.logger = new Test.Unit.Logger(this.options.testLog);
+    setTimeout(this.runTests.bind(this), 1000);
+  },
+  parseResultsURLQueryParameter: function() {
+    return window.location.search.parseQuery()["resultsURL"];
+  },
+  // Returns:
+  //  "ERROR" if there was an error,
+  //  "FAILURE" if there was a failure, or
+  //  "SUCCESS" if there was neither
+  getResult: function() {
+    var hasFailure = false;
+    for(var i=0;i<this.tests.length;i++) {
+      if (this.tests[i].errors > 0) {
+        return "ERROR";
+      }
+      if (this.tests[i].failures > 0) {
+        hasFailure = true;
+      }
+    }
+    if (hasFailure) {
+      return "FAILURE";
+    } else {
+      return "SUCCESS";
+    }
+  },
+  postResults: function() {
+    if (this.options.resultsURL) {
+      new Ajax.Request(this.options.resultsURL, 
+        { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+    }
+  },
+  runTests: function() {
+    var test = this.tests[this.currentTest];
+    if (!test) {
+      // finished!
+      this.postResults();
+      this.logger.summary(this.summary());
+      return;
+    }
+    if(!test.isWaiting) {
+      this.logger.start(test.name);
+    }
+    test.run();
+    if(test.isWaiting) {
+      this.logger.message("Waiting for " + test.timeToWait + "ms");
+      setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+    } else {
+      this.logger.finish(test.status(), test.summary());
+      this.currentTest++;
+      // tail recursive, hopefully the browser will skip the stackframe
+      this.runTests();
+    }
+  },
+  summary: function() {
+    var assertions = 0;
+    var failures = 0;
+    var errors = 0;
+    var messages = [];
+    for(var i=0;i<this.tests.length;i++) {
+      assertions +=   this.tests[i].assertions;
+      failures   +=   this.tests[i].failures;
+      errors     +=   this.tests[i].errors;
+    }
+    return (
+      this.tests.length + " tests, " + 
+      assertions + " assertions, " + 
+      failures   + " failures, " +
+      errors     + " errors");
+  }
+}
+
+Test.Unit.Assertions = Class.create();
+Test.Unit.Assertions.prototype = {
+  initialize: function() {
+    this.assertions = 0;
+    this.failures   = 0;
+    this.errors     = 0;
+    this.messages   = [];
+  },
+  summary: function() {
+    return (
+      this.assertions + " assertions, " + 
+      this.failures   + " failures, " +
+      this.errors     + " errors" + "\n" +
+      this.messages.join("\n"));
+  },
+  pass: function() {
+    this.assertions++;
+  },
+  fail: function(message) {
+    this.failures++;
+    this.messages.push("Failure: " + message);
+  },
+  info: function(message) {
+    this.messages.push("Info: " + message);
+  },
+  error: function(error) {
+    this.errors++;
+    this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
+  },
+  status: function() {
+    if (this.failures > 0) return 'failed';
+    if (this.errors > 0) return 'error';
+    return 'passed';
+  },
+  assert: function(expression) {
+    var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+    try { expression ? this.pass() : 
+      this.fail(message); }
+    catch(e) { this.error(e); }
+  },
+  assertEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEqual";
+    try { (expected == actual) ? this.pass() :
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
+        '", actual "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertEnumEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEnumEqual";
+    try { $A(expected).length == $A(actual).length && 
+      expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
+        this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + 
+          ', actual ' + Test.Unit.inspect(actual)); }
+    catch(e) { this.error(e); }
+  },
+  assertNotEqual: function(expected, actual) {
+    var message = arguments[2] || "assertNotEqual";
+    try { (expected != actual) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertNull: function(obj) {
+    var message = arguments[1] || 'assertNull'
+    try { (obj==null) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertHidden: function(element) {
+    var message = arguments[1] || 'assertHidden';
+    this.assertEqual("none", element.style.display, message);
+  },
+  assertNotNull: function(object) {
+    var message = arguments[1] || 'assertNotNull';
+    this.assert(object != null, message);
+  },
+  assertInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertInstanceOf';
+    try { 
+      (actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was not an instance of the expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  assertNotInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertNotInstanceOf';
+    try { 
+      !(actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was an instance of the not expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  _isVisible: function(element) {
+    element = $(element);
+    if(!element.parentNode) return true;
+    this.assertNotNull(element);
+    if(element.style && Element.getStyle(element, 'display') == 'none')
+      return false;
+    
+    return this._isVisible(element.parentNode);
+  },
+  assertNotVisible: function(element) {
+    this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+  },
+  assertVisible: function(element) {
+    this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+  },
+  benchmark: function(operation, iterations) {
+    var startAt = new Date();
+    (iterations || 1).times(operation);
+    var timeTaken = ((new Date())-startAt);
+    this.info((arguments[2] || 'Operation') + ' finished ' + 
+       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+    return timeTaken;
+  }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+  initialize: function(name, test, setup, teardown) {
+    Test.Unit.Assertions.prototype.initialize.bind(this)();
+    this.name           = name;
+    this.test           = test || function() {};
+    this.setup          = setup || function() {};
+    this.teardown       = teardown || function() {};
+    this.isWaiting      = false;
+    this.timeToWait     = 1000;
+  },
+  wait: function(time, nextPart) {
+    this.isWaiting = true;
+    this.test = nextPart;
+    this.timeToWait = time;
+  },
+  run: function() {
+    try {
+      try {
+        if (!this.isWaiting) this.setup.bind(this)();
+        this.isWaiting = false;
+        this.test.bind(this)();
+      } finally {
+        if(!this.isWaiting) {
+          this.teardown.bind(this)();
+        }
+      }
+    }
+    catch(e) { this.error(e); }
+  }
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/find_matching_child.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/find_matching_child.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/find_matching_child.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+elementFindMatchingChildren = function(element, selector) {
+  var matches = [];
+
+  var childCount = element.childNodes.length;
+  for (var i=0; i<childCount; i++) {
+    var child = element.childNodes[i];
+    if (selector(child)) {
+      matches.push(child);
+    } else {
+      childMatches = elementFindMatchingChildren(child, selector);
+      matches.push(childMatches);
+    }
+  }
+
+  return matches.flatten();
+}
+
+ELEMENT_NODE_TYPE = 1;
+
+elementFindFirstMatchingChild = function(element, selector) {
+
+  var childCount = element.childNodes.length;
+  for (var i=0; i<childCount; i++) {
+    var child = element.childNodes[i];
+    if (child.nodeType == ELEMENT_NODE_TYPE) {
+      if (selector(child)) {
+        return child;
+      }
+      result = elementFindFirstMatchingChild(child, selector);
+      if (result) {
+        return result;
+      }
+    }
+  }
+  return null;
+}
+
+elementFindFirstMatchingParent = function(element, selector) {
+  var current = element.parentNode;
+  while (current != null) {
+    if (selector(current)) {
+      break;
+    }
+    current = current.parentNode;
+  }
+  return current;
+}
+
+elementFindMatchingChildById = function(element, id) {
+  return elementFindFirstMatchingChild(element, function(element){return element.id==id} );
+}
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/htmlutils.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/htmlutils.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/htmlutils.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,894 @@
+/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+// This script contains a badly-organised collection of miscellaneous
+// functions that really better homes.
+
+function classCreate() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+}
+
+function objectExtend(destination, source) {
+  for (var property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+}
+
+function sel$() {
+  var results = [], element;
+  for (var i = 0; i < arguments.length; i++) {
+    element = arguments[i];
+    if (typeof element == 'string')
+      element = document.getElementById(element);
+    results[results.length] = element;
+  }
+  return results.length < 2 ? results[0] : results;
+}
+
+function sel$A(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0; i < iterable.length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+function fnBind() {
+  var args = sel$A(arguments), __method = args.shift(), object = args.shift();
+  var retval = function() {
+    return __method.apply(object, args.concat(sel$A(arguments)));
+  }
+  retval.__method = __method;
+  return retval;
+}
+
+function fnBindAsEventListener(fn, object) {
+  var __method = fn;
+  return function(event) {
+    return __method.call(object, event || window.event);
+  }
+}
+
+function removeClassName(element, name) {
+    var re = new RegExp("\\b" + name + "\\b", "g");
+    element.className = element.className.replace(re, "");
+}
+
+function addClassName(element, name) {
+    element.className = element.className + ' ' + name;
+}
+
+function elementSetStyle(element, style) {
+    for (var name in style) {
+      var value = style[name];
+      if (value == null) value = "";
+      element.style[name] = value;
+    }
+}
+
+function elementGetStyle(element, style) {
+    var value = element.style[style];
+    if (!value) {
+      if (document.defaultView && document.defaultView.getComputedStyle) {
+        var css = document.defaultView.getComputedStyle(element, null);
+        value = css ? css.getPropertyValue(style) : null;
+      } else if (element.currentStyle) {
+        value = element.currentStyle[style];
+      }
+    }
+
+    /** DGF necessary? 
+    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+      if (Element.getStyle(element, 'position') == 'static') value = 'auto'; */
+
+    return value == 'auto' ? null : value;
+  }
+
+String.prototype.trim = function() {
+    var result = this.replace(/^\s+/g, "");
+    // strip leading
+    return result.replace(/\s+$/g, "");
+    // strip trailing
+};
+String.prototype.lcfirst = function() {
+    return this.charAt(0).toLowerCase() + this.substr(1);
+};
+String.prototype.ucfirst = function() {
+    return this.charAt(0).toUpperCase() + this.substr(1);
+};
+String.prototype.startsWith = function(str) {
+    return this.indexOf(str) == 0;
+};
+
+// Returns the text in this element
+function getText(element) {
+    var text = "";
+
+    var isRecentFirefox = (browserVersion.isFirefox && browserVersion.firefoxVersion >= "1.5");
+    if (isRecentFirefox || browserVersion.isKonqueror || browserVersion.isSafari || browserVersion.isOpera) {
+        text = getTextContent(element);
+    } else if (element.textContent) {
+        text = element.textContent;
+    } else if (element.innerText) {
+        text = element.innerText;
+    }
+
+    text = normalizeNewlines(text);
+    text = normalizeSpaces(text);
+
+    return text.trim();
+}
+
+function getTextContent(element, preformatted) {
+    if (element.nodeType == 3 /*Node.TEXT_NODE*/) {
+        var text = element.data;
+        if (!preformatted) {
+            text = text.replace(/\n|\r|\t/g, " ");
+        }
+        return text;
+    }
+    if (element.nodeType == 1 /*Node.ELEMENT_NODE*/) {
+        var childrenPreformatted = preformatted || (element.tagName == "PRE");
+        var text = "";
+        for (var i = 0; i < element.childNodes.length; i++) {
+            var child = element.childNodes.item(i);
+            text += getTextContent(child, childrenPreformatted);
+        }
+        // Handle block elements that introduce newlines
+        // -- From HTML spec:
+        //<!ENTITY % block
+        //     "P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT |
+        //      BLOCKQUOTE | F:wORM | HR | TABLE | FIELDSET | ADDRESS">
+        //
+        // TODO: should potentially introduce multiple newlines to separate blocks
+        if (element.tagName == "P" || element.tagName == "BR" || element.tagName == "HR" || element.tagName == "DIV") {
+            text += "\n";
+        }
+        return text;
+    }
+    return '';
+}
+
+/**
+ * Convert all newlines to \m
+ */
+function normalizeNewlines(text)
+{
+    return text.replace(/\r\n|\r/g, "\n");
+}
+
+/**
+ * Replace multiple sequential spaces with a single space, and then convert &nbsp; to space.
+ */
+function normalizeSpaces(text)
+{
+    // IE has already done this conversion, so doing it again will remove multiple nbsp
+    if (browserVersion.isIE)
+    {
+        return text;
+    }
+
+    // Replace multiple spaces with a single space
+    // TODO - this shouldn't occur inside PRE elements
+    text = text.replace(/\ +/g, " ");
+
+    // Replace &nbsp; with a space
+    var nbspPattern = new RegExp(String.fromCharCode(160), "g");
+    if (browserVersion.isSafari) {
+	return replaceAll(text, String.fromCharCode(160), " ");
+    } else {
+	return text.replace(nbspPattern, " ");
+    }
+}
+
+function replaceAll(text, oldText, newText) {
+    while (text.indexOf(oldText) != -1) {
+	text = text.replace(oldText, newText);
+    }
+    return text;
+}
+
+
+function xmlDecode(text) {
+    text = text.replace(/&quot;/g, '"');
+    text = text.replace(/&apos;/g, "'");
+    text = text.replace(/&lt;/g, "<");
+    text = text.replace(/&gt;/g, ">");
+    text = text.replace(/&amp;/g, "&");
+    return text;
+}
+
+// Sets the text in this element
+function setText(element, text) {
+    if (element.textContent != null) {
+        element.textContent = text;
+    } else if (element.innerText != null) {
+        element.innerText = text;
+    }
+}
+
+// Get the value of an <input> element
+function getInputValue(inputElement) {
+    if (inputElement.type) {
+        if (inputElement.type.toUpperCase() == 'CHECKBOX' ||
+            inputElement.type.toUpperCase() == 'RADIO')
+        {
+            return (inputElement.checked ? 'on' : 'off');
+        }
+    }
+    if (inputElement.value == null) {
+        throw new SeleniumError("This element has no value; is it really a form field?");
+    }
+    return inputElement.value;
+}
+
+/* Fire an event in a browser-compatible manner */
+function triggerEvent(element, eventType, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
+    canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
+    if (element.fireEvent) {
+        var evt = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);        
+        element.fireEvent('on' + eventType, evt);
+    }
+    else {
+        var evt = document.createEvent('HTMLEvents');
+        
+        try {
+            evt.shiftKey = shiftKeyDown;
+            evt.metaKey = metaKeyDown;
+            evt.altKey = altKeyDown;
+            evt.ctrlKey = controlKeyDown;
+        } catch (e) {
+            // On Firefox 1.0, you can only set these during initMouseEvent or initKeyEvent
+            // we'll have to ignore them here
+            LOG.exception(e);
+        }
+        
+        evt.initEvent(eventType, canBubble, true);
+        element.dispatchEvent(evt);
+    }
+}
+
+function getKeyCodeFromKeySequence(keySequence) {
+    var match = /^\\(\d{1,3})$/.exec(keySequence);
+    if (match != null) {
+        return match[1];
+    }
+    match = /^.$/.exec(keySequence);
+    if (match != null) {
+        return match[0].charCodeAt(0);
+    }
+    // this is for backward compatibility with existing tests
+    // 1 digit ascii codes will break however because they are used for the digit chars
+    match = /^\d{2,3}$/.exec(keySequence);
+    if (match != null) {
+        return match[0];
+    }
+    throw new SeleniumError("invalid keySequence");
+}
+
+function createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
+     var evt = element.ownerDocument.createEventObject();
+     evt.shiftKey = shiftKeyDown;
+     evt.metaKey = metaKeyDown;
+     evt.altKey = altKeyDown;
+     evt.ctrlKey = controlKeyDown;
+     return evt;
+}
+
+function triggerKeyEvent(element, eventType, keySequence, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
+    var keycode = getKeyCodeFromKeySequence(keySequence);
+    canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
+    if (element.fireEvent) {
+        var keyEvent = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
+        keyEvent.keyCode = keycode;
+        element.fireEvent('on' + eventType, keyEvent);
+    }
+    else {
+        var evt;
+        if (window.KeyEvent) {
+            evt = document.createEvent('KeyEvents');
+            evt.initKeyEvent(eventType, true, true, window, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown, keycode, keycode);
+        } else {
+            evt = document.createEvent('UIEvents');
+            
+            evt.shiftKey = shiftKeyDown;
+            evt.metaKey = metaKeyDown;
+            evt.altKey = altKeyDown;
+            evt.ctrlKey = controlKeyDown;
+
+            evt.initUIEvent(eventType, true, true, window, 1);
+            evt.keyCode = keycode;
+            evt.which = keycode;
+        }
+
+        element.dispatchEvent(evt);
+    }
+}
+
+function removeLoadListener(element, command) {
+    LOG.debug('Removing loadListenter for ' + element + ', ' + command);
+    if (window.removeEventListener)
+        element.removeEventListener("load", command, true);
+    else if (window.detachEvent)
+        element.detachEvent("onload", command);
+}
+
+function addLoadListener(element, command) {
+    LOG.debug('Adding loadListenter for ' + element + ', ' + command);
+    var augmentedCommand = function() {
+        command.call(this, element);
+    }
+    if (window.addEventListener && !browserVersion.isOpera)
+        element.addEventListener("load", augmentedCommand, true);
+    else if (window.attachEvent)
+        element.attachEvent("onload", augmentedCommand);
+}
+
+/**
+ * Override the broken getFunctionName() method from JsUnit
+ * This file must be loaded _after_ the jsunitCore.js
+ */
+function getFunctionName(aFunction) {
+    var regexpResult = aFunction.toString().match(/function (\w*)/);
+    if (regexpResult && regexpResult[1]) {
+        return regexpResult[1];
+    }
+    return 'anonymous';
+}
+
+function getDocumentBase(doc) {
+    var bases = document.getElementsByTagName("base");
+    if (bases && bases.length && bases[0].href) {
+        return bases[0].href;
+    }
+    return "";
+}
+
+function getTagName(element) {
+    var tagName;
+    if (element && element.tagName && element.tagName.toLowerCase) {
+        tagName = element.tagName.toLowerCase();
+    }
+    return tagName;
+}
+
+function selArrayToString(a) {
+    if (isArray(a)) {
+        // DGF copying the array, because the array-like object may be a non-modifiable nodelist
+        var retval = [];
+        for (var i = 0; i < a.length; i++) {
+            var item = a[i];
+            var replaced = new String(item).replace(/([,\\])/g, '\\$1');
+            retval[i] = replaced;
+        }
+        return retval;
+    }
+    return new String(a);
+}
+
+
+function isArray(x) {
+    return ((typeof x) == "object") && (x["length"] != null);
+}
+
+function absolutify(url, baseUrl) {
+    /** returns a relative url in its absolute form, given by baseUrl.
+    * 
+    * This function is a little odd, because it can take baseUrls that
+    * aren't necessarily directories.  It uses the same rules as the HTML 
+    * &lt;base&gt; tag; if the baseUrl doesn't end with "/", we'll assume
+    * that it points to a file, and strip the filename off to find its
+    * base directory.
+    *
+    * So absolutify("foo", "http://x/bar") will return "http://x/foo" (stripping off bar),
+    * whereas absolutify("foo", "http://x/bar/") will return "http://x/bar/foo" (preserving bar).
+    * Naturally absolutify("foo", "http://x") will return "http://x/foo", appropriately.
+    * 
+    * @param url the url to make absolute; if this url is already absolute, we'll just return that, unchanged
+    * @param baseUrl the baseUrl from which we'll absolutify, following the rules above.
+    * @return 'url' if it was already absolute, or the absolutized version of url if it was not absolute.
+    */
+    
+    // DGF isn't there some library we could use for this?
+        
+    if (/^\w+:/.test(url)) {
+        // it's already absolute
+        return url;
+    }
+    
+    var loc;
+    try {
+        loc = parseUrl(baseUrl);
+    } catch (e) {
+        // is it an absolute windows file path? let's play the hero in that case
+        if (/^\w:\\/.test(baseUrl)) {
+            baseUrl = "file:///" + baseUrl.replace(/\\/g, "/");
+            loc = parseUrl(baseUrl);
+        } else {
+            throw new SeleniumError("baseUrl wasn't absolute: " + baseUrl);
+        }
+    }
+    loc.search = null;
+    loc.hash = null;
+    
+    // if url begins with /, then that's the whole pathname
+    if (/^\//.test(url)) {
+        loc.pathname = url;
+        var result = reassembleLocation(loc);
+        return result;
+    }
+    
+    // if pathname is null, then we'll just append "/" + the url
+    if (!loc.pathname) {
+        loc.pathname = "/" + url;
+        var result = reassembleLocation(loc);
+        return result;
+    }
+    
+    // if pathname ends with /, just append url
+    if (/\/$/.test(loc.pathname)) {
+        loc.pathname += url;
+        var result = reassembleLocation(loc);
+        return result;
+    }
+    
+    // if we're here, then the baseUrl has a pathname, but it doesn't end with /
+    // in that case, we replace everything after the final / with the relative url
+    loc.pathname = loc.pathname.replace(/[^\/\\]+$/, url);
+    var result = reassembleLocation(loc);
+    return result;
+    
+}
+
+var URL_REGEX = /^((\w+):\/\/)(([^:]+):?([^@]+)?@)?([^\/\?:]*):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(.+)?/;
+
+function parseUrl(url) {
+    var fields = ['url', null, 'protocol', null, 'username', 'password', 'host', 'port', 'pathname', 'search', 'hash'];
+    var result = URL_REGEX.exec(url);
+    if (!result) {
+        throw new SeleniumError("Invalid URL: " + url);
+    }
+    var loc = new Object();
+    for (var i = 0; i < fields.length; i++) {
+        var field = fields[i];
+        if (field == null) {
+            continue;
+        }
+        loc[field] = result[i];
+    }
+    return loc;
+}
+
+function reassembleLocation(loc) {
+    if (!loc.protocol) {
+        throw new Error("Not a valid location object: " + o2s(loc));
+    }
+    var protocol = loc.protocol;
+    protocol = protocol.replace(/:$/, "");
+    var url = protocol + "://";
+    if (loc.username) {
+        url += loc.username;
+        if (loc.password) {
+            url += ":" + loc.password;
+        }
+        url += "@";
+    }
+    if (loc.host) {
+        url += loc.host;
+    }
+    
+    if (loc.port) {
+        url += ":" + loc.port;
+    }
+    
+    if (loc.pathname) {
+        url += loc.pathname;
+    }
+    
+    if (loc.search) {
+        url += "?" + loc.search;
+    }
+    if (loc.hash) {
+        var hash = loc.hash;
+        hash = loc.hash.replace(/^#/, "");
+        url += "#" + hash;
+    }
+    return url;
+}
+
+function canonicalize(url) {
+    var tempLink = window.document.createElement("link");
+    tempLink.href = url; // this will canonicalize the href on most browsers
+    var loc = parseUrl(tempLink.href)
+    if (!/\/\.\.\//.test(loc.pathname)) {
+    	return tempLink.href;
+    }
+  	// didn't work... let's try it the hard way
+  	var originalParts = loc.pathname.split("/");
+  	var newParts = [];
+  	newParts.push(originalParts.shift());
+  	for (var i = 0; i < originalParts.length; i++) {
+  		var part = originalParts[i];
+  		if (".." == part) {
+  			newParts.pop();
+  			continue;
+  		}
+  		newParts.push(part);
+  	}
+  	loc.pathname = newParts.join("/");
+    return reassembleLocation(loc);
+}
+
+function extractExceptionMessage(ex) {
+    if (ex == null) return "null exception";
+    if (ex.message != null) return ex.message;
+    if (ex.toString && ex.toString() != null) return ex.toString();
+}
+    
+
+function describe(object, delimiter) {
+    var props = new Array();
+    for (var prop in object) {
+        try {
+            props.push(prop + " -> " + object[prop]);
+        } catch (e) {
+            props.push(prop + " -> [htmlutils: ack! couldn't read this property! (Permission Denied?)]");
+        }
+    }
+    return props.join(delimiter || '\n');
+}
+
+var PatternMatcher = function(pattern) {
+    this.selectStrategy(pattern);
+};
+PatternMatcher.prototype = {
+
+    selectStrategy: function(pattern) {
+        this.pattern = pattern;
+        var strategyName = 'glob';
+        // by default
+        if (/^([a-z-]+):(.*)/.test(pattern)) {
+            var possibleNewStrategyName = RegExp.$1;
+            var possibleNewPattern = RegExp.$2;
+            if (PatternMatcher.strategies[possibleNewStrategyName]) {
+                strategyName = possibleNewStrategyName;
+                pattern = possibleNewPattern;
+            }
+        }
+        var matchStrategy = PatternMatcher.strategies[strategyName];
+        if (!matchStrategy) {
+            throw new SeleniumError("cannot find PatternMatcher.strategies." + strategyName);
+        }
+        this.strategy = matchStrategy;
+        this.matcher = new matchStrategy(pattern);
+    },
+
+    matches: function(actual) {
+        return this.matcher.matches(actual + '');
+        // Note: appending an empty string avoids a Konqueror bug
+    }
+
+};
+
+/**
+ * A "static" convenience method for easy matching
+ */
+PatternMatcher.matches = function(pattern, actual) {
+    return new PatternMatcher(pattern).matches(actual);
+};
+
+PatternMatcher.strategies = {
+
+/**
+ * Exact matching, e.g. "exact:***"
+ */
+    exact: function(expected) {
+        this.expected = expected;
+        this.matches = function(actual) {
+            return actual == this.expected;
+        };
+    },
+
+/**
+ * Match by regular expression, e.g. "regexp:^[0-9]+$"
+ */
+    regexp: function(regexpString) {
+        this.regexp = new RegExp(regexpString);
+        this.matches = function(actual) {
+            return this.regexp.test(actual);
+        };
+    },
+
+    regex: function(regexpString) {
+        this.regexp = new RegExp(regexpString);
+        this.matches = function(actual) {
+            return this.regexp.test(actual);
+        };
+    },
+
+/**
+ * "globContains" (aka "wildmat") patterns, e.g. "glob:one,two,*",
+ * but don't require a perfect match; instead succeed if actual
+ * contains something that matches globString.
+ * Making this distinction is motivated by a bug in IE6 which
+ * leads to the browser hanging if we implement *TextPresent tests
+ * by just matching against a regular expression beginning and
+ * ending with ".*".  The globcontains strategy allows us to satisfy
+ * the functional needs of the *TextPresent ops more efficiently
+ * and so avoid running into this IE6 freeze.
+ */
+    globContains: function(globString) {
+        this.regexp = new RegExp(PatternMatcher.regexpFromGlobContains(globString));
+        this.matches = function(actual) {
+            return this.regexp.test(actual);
+        };
+    },
+
+
+/**
+ * "glob" (aka "wildmat") patterns, e.g. "glob:one,two,*"
+ */
+    glob: function(globString) {
+        this.regexp = new RegExp(PatternMatcher.regexpFromGlob(globString));
+        this.matches = function(actual) {
+            return this.regexp.test(actual);
+        };
+    }
+
+};
+
+PatternMatcher.convertGlobMetaCharsToRegexpMetaChars = function(glob) {
+    var re = glob;
+    re = re.replace(/([.^$+(){}\[\]\\|])/g, "\\$1");
+    re = re.replace(/\?/g, "(.|[\r\n])");
+    re = re.replace(/\*/g, "(.|[\r\n])*");
+    return re;
+};
+
+PatternMatcher.regexpFromGlobContains = function(globContains) {
+    return PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(globContains);
+};
+
+PatternMatcher.regexpFromGlob = function(glob) {
+    return "^" + PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(glob) + "$";
+};
+
+var Assert = {
+
+    fail: function(message) {
+        throw new AssertionFailedError(message);
+    },
+
+/*
+* Assert.equals(comment?, expected, actual)
+*/
+    equals: function() {
+        var args = new AssertionArguments(arguments);
+        if (args.expected === args.actual) {
+            return;
+        }
+        Assert.fail(args.comment +
+                    "Expected '" + args.expected +
+                    "' but was '" + args.actual + "'");
+    },
+
+/*
+* Assert.matches(comment?, pattern, actual)
+*/
+    matches: function() {
+        var args = new AssertionArguments(arguments);
+        if (PatternMatcher.matches(args.expected, args.actual)) {
+            return;
+        }
+        Assert.fail(args.comment +
+                    "Actual value '" + args.actual +
+                    "' did not match '" + args.expected + "'");
+    },
+
+/*
+* Assert.notMtches(comment?, pattern, actual)
+*/
+    notMatches: function() {
+        var args = new AssertionArguments(arguments);
+        if (!PatternMatcher.matches(args.expected, args.actual)) {
+            return;
+        }
+        Assert.fail(args.comment +
+                    "Actual value '" + args.actual +
+                    "' did match '" + args.expected + "'");
+    }
+
+};
+
+// Preprocess the arguments to allow for an optional comment.
+function AssertionArguments(args) {
+    if (args.length == 2) {
+        this.comment = "";
+        this.expected = args[0];
+        this.actual = args[1];
+    } else {
+        this.comment = args[0] + "; ";
+        this.expected = args[1];
+        this.actual = args[2];
+    }
+}
+
+function AssertionFailedError(message) {
+    this.isAssertionFailedError = true;
+    this.isSeleniumError = true;
+    this.message = message;
+    this.failureMessage = message;
+}
+
+function SeleniumError(message) {
+    var error = new Error(message);
+    if (typeof(arguments.caller) != 'undefined') { // IE, not ECMA
+        var result = '';
+        for (var a = arguments.caller; a != null; a = a.caller) {
+            result += '> ' + a.callee.toString() + '\n';
+            if (a.caller == a) {
+                result += '*';
+                break;
+            }
+        }
+        error.stack = result;
+    }
+    error.isSeleniumError = true;
+    return error;
+}
+
+function highlight(element) {
+    var highLightColor = "yellow";
+    if (element.originalColor == undefined) { // avoid picking up highlight
+        element.originalColor = elementGetStyle(element, "background-color");
+    }
+    elementSetStyle(element, {"backgroundColor" : highLightColor});
+    window.setTimeout(function() {
+        try {
+            //if element is orphan, probably page of it has already gone, so ignore
+            if (!element.parentNode) {
+                return;
+            }
+            elementSetStyle(element, {"backgroundColor" : element.originalColor});
+        } catch (e) {} // DGF unhighlighting is very dangerous and low priority
+    }, 200);
+}
+
+
+
+// for use from vs.2003 debugger
+function o2s(obj) {
+    var s = "";
+    for (key in obj) {
+        var line = key + "->" + obj[key];
+        line.replace("\n", " ");
+        s += line + "\n";
+    }
+    return s;
+}
+
+var seenReadyStateWarning = false;
+
+function openSeparateApplicationWindow(url, suppressMozillaWarning) {
+    // resize the Selenium window itself
+    window.resizeTo(1200, 500);
+    window.moveTo(window.screenX, 0);
+
+    var appWindow = window.open(url + '?start=true', 'main');
+    if (appWindow == null) {
+        var errorMessage = "Couldn't open app window; is the pop-up blocker enabled?"
+        LOG.error(errorMessage);
+        throw new Error("Couldn't open app window; is the pop-up blocker enabled?");
+    }
+    try {
+        var windowHeight = 500;
+        if (window.outerHeight) {
+            windowHeight = window.outerHeight;
+        } else if (document.documentElement && document.documentElement.offsetHeight) {
+            windowHeight = document.documentElement.offsetHeight;
+        }
+
+        if (window.screenLeft && !window.screenX) window.screenX = window.screenLeft;
+        if (window.screenTop && !window.screenY) window.screenY = window.screenTop;
+
+        appWindow.resizeTo(1200, screen.availHeight - windowHeight - 60);
+        appWindow.moveTo(window.screenX, window.screenY + windowHeight + 25);
+    } catch (e) {
+        LOG.error("Couldn't resize app window");
+        LOG.exception(e);
+    }
+
+
+    if (!suppressMozillaWarning && window.document.readyState == null && !seenReadyStateWarning) {
+        alert("Beware!  Mozilla bug 300992 means that we can't always reliably detect when a new page has loaded.  Install the Selenium IDE extension or the readyState extension available from selenium.openqa.org to make page load detection more reliable.");
+        seenReadyStateWarning = true;
+    }
+
+    return appWindow;
+}
+
+var URLConfiguration = classCreate();
+objectExtend(URLConfiguration.prototype, {
+    initialize: function() {
+    },
+    _isQueryParameterTrue: function (name) {
+        var parameterValue = this._getQueryParameter(name);
+        if (parameterValue == null) return false;
+        if (parameterValue.toLowerCase() == "true") return true;
+        if (parameterValue.toLowerCase() == "on") return true;
+        return false;
+    },
+
+    _getQueryParameter: function(searchKey) {
+        var str = this.queryString
+        if (str == null) return null;
+        var clauses = str.split('&');
+        for (var i = 0; i < clauses.length; i++) {
+            var keyValuePair = clauses[i].split('=', 2);
+            var key = unescape(keyValuePair[0]);
+            if (key == searchKey) {
+                return unescape(keyValuePair[1]);
+            }
+        }
+        return null;
+    },
+
+    _extractArgs: function() {
+        var str = SeleniumHTARunner.commandLine;
+        if (str == null || str == "") return new Array();
+        var matches = str.match(/(?:\"([^\"]+)\"|(?!\"([^\"]+)\")(\S+))/g);
+        // We either want non quote stuff ([^"]+) surrounded by quotes
+        // or we want to look-ahead, see that the next character isn't
+        // a quoted argument, and then grab all the non-space stuff
+        // this will return for the line: "foo" bar
+        // the results "\"foo\"" and "bar"
+
+        // So, let's unquote the quoted arguments:
+        var args = new Array;
+        for (var i = 0; i < matches.length; i++) {
+            args[i] = matches[i];
+            args[i] = args[i].replace(/^"(.*)"$/, "$1");
+        }
+        return args;
+    },
+
+    isMultiWindowMode:function() {
+        return this._isQueryParameterTrue('multiWindow');
+    },
+    
+    getBaseUrl:function() {
+        return this._getQueryParameter('baseUrl');
+            
+    }
+});
+
+
+function safeScrollIntoView(element) {
+    if (element.scrollIntoView) {
+        element.scrollIntoView(false);
+        return;
+    }
+    // TODO: work out how to scroll browsers that don't support
+    // scrollIntoView (like Konqueror)
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/injection.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/injection.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/injection.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,72 @@
+<script language="JavaScript">
+    if (window["selenium_has_been_loaded_into_this_window"]==null)
+    {
+
+        __SELENIUM_JS__
+// Some background on the code below: broadly speaking, where we are relative to other windows
+// when running in proxy injection mode depends on whether we are in a frame set file or not.
+//
+// In regular HTML files, the selenium JavaScript is injected into an iframe called "selenium"
+// in order to reduce its impact on the JavaScript environment (through namespace pollution,
+// etc.).  So in regular HTML files, we need to look at the parent of the current window when we want
+// a handle to, e.g., the application window.
+//
+// In frame set files, we can't use an iframe, so we put the JavaScript in the head element and share
+// the window with the frame set.  So in this case, we need to look at the current window, not the
+// parent when looking for, e.g., the application window.  (TODO: Perhaps I should have just
+// assigned a regular frame for selenium?)
+//
+BrowserBot.prototype.getContentWindow = function() {
+    return window;
+};
+
+BrowserBot.prototype.getTargetWindow = function(windowName) {
+    return window;
+};
+
+BrowserBot.prototype.getCurrentWindow = function() {
+    return window;
+};
+
+LOG.openLogWindow = function(message, className) {
+	// disable for now
+};
+
+BrowserBot.prototype.relayToRC = function(name) {
+	var object = eval(name);
+        var s = 'state:' + serializeObject(name, object) + "\n";
+        sendToRC(s,"state=true");
+}
+
+function selenium_frameRunTest(oldOnLoadRoutine) {
+	if (oldOnLoadRoutine) {
+		eval(oldOnLoadRoutine);
+	}
+        runSeleniumTest();
+}
+
+function seleniumOnLoad() {
+    injectedSessionId = @SESSION_ID@;
+    window["selenium_has_been_loaded_into_this_window"] = true;
+    runSeleniumTest();
+}
+
+function seleniumOnUnload() {
+	sendToRC("Current window or frame is closed!", "closing=true");
+}
+
+if (window.addEventListener) {
+        window.addEventListener("load", seleniumOnLoad, false);	// firefox
+        window.addEventListener("unload", seleniumOnUnload, false);	// firefox
+} else if (window.attachEvent){
+    	window.attachEvent("onload", seleniumOnLoad);	// IE
+        window.attachEvent("onunload", seleniumOnUnload);	// IE
+}
+else {
+    	throw "causing a JavaScript error to tell the world that I did not arrange to be run on load";
+}
+
+injectedSessionId = @SESSION_ID@;
+proxyInjectionMode = true;
+}
+</script>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/js2html.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/js2html.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/js2html.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,70 @@
+/*
+
+This is an experiment in using the Narcissus JavaScript engine 
+to allow Selenium scripts to be written in plain JavaScript.
+
+The 'jsparse' function will compile each high level block into a Selenium table script.
+
+
+TODO: 
+1) Test! (More browsers, more sample scripts)
+2) Stepping and walking lower levels of the parse tree
+3) Calling Selenium commands directly from JavaScript
+4) Do we want comments to appear in the TestRunner?
+5) Fix context so variables don't have to be global
+   For now, variables defined with "var" won't be found
+   if used later on in a script.
+6) Fix formatting   
+*/
+
+
+function jsparse() {
+    var script = document.getElementById('sejs')
+    var fname = 'javascript script';
+    parse_result = parse(script.text, fname, 0);       
+
+    var x2 = new ExecutionContext(GLOBAL_CODE);
+    ExecutionContext.current = x2;
+
+
+    var new_test_source = '';    
+    var new_line        = '';
+    
+    for (i=0;i<parse_result.$length;i++){ 
+        var the_start = parse_result[i].start;
+        var the_end;
+        if ( i == (parse_result.$length-1)) {
+            the_end = parse_result.tokenizer.source.length;
+        } else {
+            the_end = parse_result[i+1].start;
+        }
+        
+        var script_fragment = parse_result.tokenizer.source.slice(the_start,the_end)
+        
+        new_line = '<tr><td style="display:none;" class="js">getEval</td>' +
+                   '<td style="display:none;">currentTest.doNextCommand()</td>' +
+                   '<td style="white-space: pre;">' + script_fragment + '</td>' + 
+                   '<td></td></tr>\n';
+        new_test_source += new_line;
+        //eval(script_fragment);
+        
+              
+    };
+    
+    
+    
+    execute(parse_result,x2)
+
+    // Create HTML Table        
+    body = document.body      
+    body.innerHTML += "<table class='selenium' id='se-js-table'>"+
+                      "<tbody>" +
+                      "<tr><td>// " + document.title + "</td></tr>" +
+                      new_test_source +
+                      "</tbody" +
+                      "</table>";          
+   
+    //body.innerHTML = "<pre>" + parse_result + "</pre>"
+}
+
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-defs.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-defs.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-defs.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,175 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Narcissus JavaScript engine.
+ *
+ * The Initial Developer of the Original Code is
+ * Brendan Eich <brendan at mozilla.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Narcissus - JS implemented in JS.
+ *
+ * Well-known constants and lookup tables.  Many consts are generated from the
+ * tokens table via eval to minimize redundancy, so consumers must be compiled
+ * separately to take advantage of the simple switch-case constant propagation
+ * done by SpiderMonkey.
+ */
+
+// jrh
+//module('JS.Defs');
+
+GLOBAL = this;
+
+var tokens = [
+    // End of source.
+    "END",
+
+    // Operators and punctuators.  Some pair-wise order matters, e.g. (+, -)
+    // and (UNARY_PLUS, UNARY_MINUS).
+    "\n", ";",
+    ",",
+    "=",
+    "?", ":", "CONDITIONAL",
+    "||",
+    "&&",
+    "|",
+    "^",
+    "&",
+    "==", "!=", "===", "!==",
+    "<", "<=", ">=", ">",
+    "<<", ">>", ">>>",
+    "+", "-",
+    "*", "/", "%",
+    "!", "~", "UNARY_PLUS", "UNARY_MINUS",
+    "++", "--",
+    ".",
+    "[", "]",
+    "{", "}",
+    "(", ")",
+
+    // Nonterminal tree node type codes.
+    "SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX",
+    "ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER",
+    "GROUP", "LIST",
+
+    // Terminals.
+    "IDENTIFIER", "NUMBER", "STRING", "REGEXP",
+
+    // Keywords.
+    "break",
+    "case", "catch", "const", "continue",
+    "debugger", "default", "delete", "do",
+    "else", "enum",
+    "false", "finally", "for", "function",
+    "if", "in", "instanceof",
+    "new", "null",
+    "return",
+    "switch",
+    "this", "throw", "true", "try", "typeof",
+    "var", "void",
+    "while", "with",
+    // Extensions
+    "require", "bless", "mixin", "import"
+];
+
+// Operator and punctuator mapping from token to tree node type name.
+// NB: superstring tokens (e.g., ++) must come before their substring token
+// counterparts (+ in the example), so that the opRegExp regular expression
+// synthesized from this list makes the longest possible match.
+var opTypeNames = {
+    '\n':   "NEWLINE",
+    ';':    "SEMICOLON",
+    ',':    "COMMA",
+    '?':    "HOOK",
+    ':':    "COLON",
+    '||':   "OR",
+    '&&':   "AND",
+    '|':    "BITWISE_OR",
+    '^':    "BITWISE_XOR",
+    '&':    "BITWISE_AND",
+    '===':  "STRICT_EQ",
+    '==':   "EQ",
+    '=':    "ASSIGN",
+    '!==':  "STRICT_NE",
+    '!=':   "NE",
+    '<<':   "LSH",
+    '<=':   "LE",
+    '<':    "LT",
+    '>>>':  "URSH",
+    '>>':   "RSH",
+    '>=':   "GE",
+    '>':    "GT",
+    '++':   "INCREMENT",
+    '--':   "DECREMENT",
+    '+':    "PLUS",
+    '-':    "MINUS",
+    '*':    "MUL",
+    '/':    "DIV",
+    '%':    "MOD",
+    '!':    "NOT",
+    '~':    "BITWISE_NOT",
+    '.':    "DOT",
+    '[':    "LEFT_BRACKET",
+    ']':    "RIGHT_BRACKET",
+    '{':    "LEFT_CURLY",
+    '}':    "RIGHT_CURLY",
+    '(':    "LEFT_PAREN",
+    ')':    "RIGHT_PAREN"
+};
+
+// Hash of keyword identifier to tokens index.  NB: we must null __proto__ to
+// avoid toString, etc. namespace pollution.
+var keywords = {__proto__: null};
+
+// Define const END, etc., based on the token names.  Also map name to index.
+var consts = " ";
+for (var i = 0, j = tokens.length; i < j; i++) {
+    if (i > 0)
+        consts += "; ";
+    var t = tokens[i];
+    if (/^[a-z]/.test(t)) {
+        consts += t.toUpperCase();
+        keywords[t] = i;
+    } else {
+        consts += (/^\W/.test(t) ? opTypeNames[t] : t);
+    }
+    consts += " = " + i;
+    tokens[t] = i;
+}
+eval(consts + ";");
+
+// Map assignment operators to their indexes in the tokens array.
+var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%'];
+
+for (i = 0, j = assignOps.length; i < j; i++) {
+    t = assignOps[i];
+    assignOps[t] = tokens[t];
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-exec.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-exec.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-exec.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,1054 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * vim: set ts=4 sw=4 et tw=80:
+ *
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Narcissus JavaScript engine.
+ *
+ * The Initial Developer of the Original Code is
+ * Brendan Eich <brendan at mozilla.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Narcissus - JS implemented in JS.
+ *
+ * Execution of parse trees.
+ *
+ * Standard classes except for eval, Function, Array, and String are borrowed
+ * from the host JS environment.  Function is metacircular.  Array and String
+ * are reflected via wrapping the corresponding native constructor and adding
+ * an extra level of prototype-based delegation.
+ */
+
+// jrh
+//module('JS.Exec');
+// end jrh
+
+GLOBAL_CODE = 0; EVAL_CODE = 1; FUNCTION_CODE = 2;
+
+function ExecutionContext(type) {
+    this.type = type;
+}
+
+// jrh
+var agenda = new Array();
+var skip_setup = 0;
+// end jrh
+
+var global = {
+    // Value properties.
+    NaN: NaN, Infinity: Infinity, undefined: undefined,
+    alert : function(msg) { alert(msg) },
+    confirm : function(msg) { return confirm(msg) },
+    document : document,
+    window : window,
+    // jrh
+    //debug: window.open('','debugwindow','width=600,height=400,scrollbars=yes,resizable=yes'),     
+    // end jrh
+    navigator : navigator,
+    XMLHttpRequest : function() { return new XMLHttpRequest() },
+    // Function properties.
+    eval: function(s) {
+        if (typeof s != "string") {
+            return s;
+        }
+
+        var x = ExecutionContext.current;
+        var x2 = new ExecutionContext(EVAL_CODE);
+        x2.thisObject = x.thisObject;
+        x2.caller = x.caller;
+        x2.callee = x.callee;
+        x2.scope = x.scope;
+        ExecutionContext.current = x2;
+        try {
+            execute(parse(s), x2);
+        } catch (e) {
+            x.result = x2.result;
+            throw e;
+        } finally {
+            ExecutionContext.current = x;
+        }
+        return x2.result;
+    },
+    parseInt: parseInt, parseFloat: parseFloat,
+    isNaN: isNaN, isFinite: isFinite,
+    decodeURI: decodeURI, encodeURI: encodeURI,
+    decodeURIComponent: decodeURIComponent,
+    encodeURIComponent: encodeURIComponent,
+
+    // Class constructors.  Where ECMA-262 requires C.length == 1, we declare
+    // a dummy formal parameter.
+    Object: Object,
+    Function: function(dummy) {
+        var p = "", b = "", n = arguments.length;
+        if (n) {
+            var m = n - 1;
+            if (m) {
+                p += arguments[0];
+                for (var k = 1; k < m; k++)
+                    p += "," + arguments[k];
+            }
+            b += arguments[m];
+        }
+
+        // XXX We want to pass a good file and line to the tokenizer.
+        // Note the anonymous name to maintain parity with Spidermonkey.
+        var t = new Tokenizer("anonymous(" + p + ") {" + b + "}");
+
+        // NB: Use the STATEMENT_FORM constant since we don't want to push this
+        // function onto the null compilation context.
+        var f = FunctionDefinition(t, null, false, STATEMENT_FORM);
+        var s = {object: global, parent: null};
+        return new FunctionObject(f, s);
+    },
+    Array: function(dummy) {
+        // Array when called as a function acts as a constructor.
+        return GLOBAL.Array.apply(this, arguments);
+    },
+    String: function(s) {
+        // Called as function or constructor: convert argument to string type.
+        s = arguments.length ? "" + s : "";
+        if (this instanceof String) {
+            // Called as constructor: save the argument as the string value
+            // of this String object and return this object.
+            this.value = s;
+            return this;
+        }
+        return s;
+    },
+    Boolean: Boolean, Number: Number, Date: Date, RegExp: RegExp,
+    Error: Error, EvalError: EvalError, RangeError: RangeError,
+    ReferenceError: ReferenceError, SyntaxError: SyntaxError,
+    TypeError: TypeError, URIError: URIError,
+
+    // Other properties.
+    Math: Math,
+
+    // Extensions to ECMA.
+    //snarf: snarf,
+    evaluate: evaluate,
+    load: function(s) {
+        if (typeof s != "string")
+            return s;
+        var req = new XMLHttpRequest();
+        req.open('GET', s, false);
+        req.send(null);
+
+        evaluate(req.responseText, s, 1)
+    },
+    print: print, version: null
+};
+
+// jrh
+//global.debug.document.body.innerHTML = ''
+// end jrh
+
+// Helper to avoid Object.prototype.hasOwnProperty polluting scope objects.
+function hasDirectProperty(o, p) {
+    return Object.prototype.hasOwnProperty.call(o, p);
+}
+
+// Reflect a host class into the target global environment by delegation.
+function reflectClass(name, proto) {
+    var gctor = global[name];
+    gctor.prototype = proto;
+    proto.constructor = gctor;
+    return proto;
+}
+
+// Reflect Array -- note that all Array methods are generic.
+reflectClass('Array', new Array);
+
+// Reflect String, overriding non-generic methods.
+var gSp = reflectClass('String', new String);
+gSp.toSource = function () { return this.value.toSource(); };
+gSp.toString = function () { return this.value; };
+gSp.valueOf  = function () { return this.value; };
+global.String.fromCharCode = String.fromCharCode;
+
+var XCp = ExecutionContext.prototype;
+ExecutionContext.current = XCp.caller = XCp.callee = null;
+XCp.scope = {object: global, parent: null};
+XCp.thisObject = global;
+XCp.result = undefined;
+XCp.target = null;
+XCp.ecmaStrictMode = false;
+
+function Reference(base, propertyName, node) {
+    this.base = base;
+    this.propertyName = propertyName;
+    this.node = node;
+}
+
+Reference.prototype.toString = function () { return this.node.getSource(); }
+
+function getValue(v) {
+    if (v instanceof Reference) {
+        if (!v.base) {
+            throw new ReferenceError(v.propertyName + " is not defined",
+                                     v.node.filename(), v.node.lineno);
+        }
+        return v.base[v.propertyName];
+    }
+    return v;
+}
+
+function putValue(v, w, vn) {
+    if (v instanceof Reference)
+        return (v.base || global)[v.propertyName] = w;
+    throw new ReferenceError("Invalid assignment left-hand side",
+                             vn.filename(), vn.lineno);
+}
+
+function isPrimitive(v) {
+    var t = typeof v;
+    return (t == "object") ? v === null : t != "function";
+}
+
+function isObject(v) {
+    var t = typeof v;
+    return (t == "object") ? v !== null : t == "function";
+}
+
+// If r instanceof Reference, v == getValue(r); else v === r.  If passed, rn
+// is the node whose execute result was r.
+function toObject(v, r, rn) {
+    switch (typeof v) {
+      case "boolean":
+        return new global.Boolean(v);
+      case "number":
+        return new global.Number(v);
+      case "string":
+        return new global.String(v);
+      case "function":
+        return v;
+      case "object":
+        if (v !== null)
+            return v;
+    }
+    var message = r + " (type " + (typeof v) + ") has no properties";
+    throw rn ? new TypeError(message, rn.filename(), rn.lineno)
+             : new TypeError(message);
+}
+
+function execute(n, x) {
+    if (!this.new_block)
+        new_block = new Array();
+    //alert (n)
+    var a, f, i, j, r, s, t, u, v;
+    switch (n.type) {
+      case FUNCTION:
+        if (n.functionForm != DECLARED_FORM) {
+            if (!n.name || n.functionForm == STATEMENT_FORM) {
+                v = new FunctionObject(n, x.scope);
+                if (n.functionForm == STATEMENT_FORM)
+                    x.scope.object[n.name] = v;
+            } else {
+                t = new Object;
+                x.scope = {object: t, parent: x.scope};
+                try {
+                    v = new FunctionObject(n, x.scope);
+                    t[n.name] = v;
+                } finally {
+                    x.scope = x.scope.parent;
+                }
+            }
+        }
+        break;
+
+      case SCRIPT:      
+        t = x.scope.object;
+        a = n.funDecls;
+        for (i = 0, j = a.length; i < j; i++) {
+            s = a[i].name;
+            f = new FunctionObject(a[i], x.scope);
+            t[s] = f;
+        }
+        a = n.varDecls;
+        for (i = 0, j = a.length; i < j; i++) {
+            u = a[i];
+            s = u.name;
+            if (u.readOnly && hasDirectProperty(t, s)) {
+                throw new TypeError("Redeclaration of const " + s,
+                                    u.filename(), u.lineno);
+            }
+            if (u.readOnly || !hasDirectProperty(t, s)) {
+                t[s] = null;
+            }
+        }
+        // FALL THROUGH
+
+      case BLOCK:        
+        for (i = 0, j = n.$length; i < j; i++)  {  
+            //jrh
+            //execute(n[i], x);      
+            //new_block.unshift([n[i], x]);            
+            new_block.push([n[i], x]);         
+        }
+        new_block.reverse();        
+        agenda = agenda.concat(new_block);   
+        //agenda = new_block.concat(agenda)
+        // end jrh
+        break;
+
+      case IF:
+        if (getValue(execute(n.condition, x)))
+            execute(n.thenPart, x);
+        else if (n.elsePart)
+            execute(n.elsePart, x);
+        break;
+
+      case SWITCH:
+        s = getValue(execute(n.discriminant, x));
+        a = n.cases;
+        var matchDefault = false;
+      switch_loop:
+        for (i = 0, j = a.length; ; i++) {
+            if (i == j) {
+                if (n.defaultIndex >= 0) {
+                    i = n.defaultIndex - 1; // no case matched, do default
+                    matchDefault = true;
+                    continue;
+                }
+                break;                      // no default, exit switch_loop
+            }
+            t = a[i];                       // next case (might be default!)
+            if (t.type == CASE) {
+                u = getValue(execute(t.caseLabel, x));
+            } else {
+                if (!matchDefault)          // not defaulting, skip for now
+                    continue;
+                u = s;                      // force match to do default
+            }
+            if (u === s) {
+                for (;;) {                  // this loop exits switch_loop
+                    if (t.statements.length) {
+                        try {
+                            execute(t.statements, x);
+                        } catch (e) {
+                            if (!(e == BREAK && x.target == n)) { throw e }
+                            break switch_loop;
+                        }
+                    }
+                    if (++i == j)
+                        break switch_loop;
+                    t = a[i];
+                }
+                // NOT REACHED
+            }
+        }
+        break;
+
+      case FOR:
+        // jrh
+        // added "skip_setup" so initialization doesn't get called
+        // on every call..
+        if (!skip_setup)
+            n.setup && getValue(execute(n.setup, x));
+        // FALL THROUGH
+      case WHILE:
+        // jrh       
+        //while (!n.condition || getValue(execute(n.condition, x))) {
+        if (!n.condition || getValue(execute(n.condition, x))) {
+            try {
+                // jrh 
+                //execute(n.body, x);
+                new_block.push([n.body, x]);
+                agenda.push([n.body, x])
+                //agenda.unshift([n.body, x])
+                // end jrh
+            } catch (e) {
+                if (e == BREAK && x.target == n) {
+                    break;
+                } else if (e == CONTINUE && x.target == n) {
+                    // jrh
+                    // 'continue' is invalid inside an 'if' clause
+                    // I don't know what commenting this out will break!
+                    //continue;
+                    // end jrh
+                    
+                } else {
+                    throw e;
+                }
+            }    
+            n.update && getValue(execute(n.update, x));
+            // jrh
+            new_block.unshift([n, x])
+            agenda.splice(agenda.length-1,0,[n, x])
+            //agenda.splice(1,0,[n, x])
+            skip_setup = 1
+            // end jrh
+        } else {
+            skip_setup = 0
+        }
+        
+        break;
+
+      case FOR_IN:
+        u = n.varDecl;
+        if (u)
+            execute(u, x);
+        r = n.iterator;
+        s = execute(n.object, x);
+        v = getValue(s);
+
+        // ECMA deviation to track extant browser JS implementation behavior.
+        t = (v == null && !x.ecmaStrictMode) ? v : toObject(v, s, n.object);
+        a = [];
+        for (i in t)
+            a.push(i);
+        for (i = 0, j = a.length; i < j; i++) {
+            putValue(execute(r, x), a[i], r);
+            try {
+                execute(n.body, x);
+            } catch (e) {
+                if (e == BREAK && x.target == n) {
+                    break;
+                } else if (e == CONTINUE && x.target == n) {
+                    continue;
+                } else {
+                    throw e;
+                }
+            }
+        }
+        break;
+
+      case DO:
+        do {
+            try {
+                execute(n.body, x);
+            } catch (e) {
+                if (e == BREAK && x.target == n) {
+                    break;
+                } else if (e == CONTINUE && x.target == n) {
+                    continue;
+                } else {
+                    throw e;
+                }
+            }
+        } while (getValue(execute(n.condition, x)));
+        break;
+
+      case BREAK:
+      case CONTINUE:
+        x.target = n.target;
+        throw n.type;
+
+      case TRY:
+        try {
+            execute(n.tryBlock, x);
+        } catch (e) {
+            if (!(e == THROW && (j = n.catchClauses.length))) {
+                throw e;
+            }
+            e = x.result;
+            x.result = undefined;
+            for (i = 0; ; i++) {
+                if (i == j) {
+                    x.result = e;
+                    throw THROW;
+                }
+                t = n.catchClauses[i];
+                x.scope = {object: {}, parent: x.scope};
+                x.scope.object[t.varName] = e;
+                try {
+                    if (t.guard && !getValue(execute(t.guard, x)))
+                        continue;
+                    execute(t.block, x);
+                    break;
+                } finally {
+                    x.scope = x.scope.parent;
+                }
+            }
+        } finally {
+            if (n.finallyBlock)
+                execute(n.finallyBlock, x);
+        }
+        break;
+
+      case THROW:
+        x.result = getValue(execute(n.exception, x));
+        throw THROW;
+
+      case RETURN:
+        x.result = getValue(execute(n.value, x));
+        throw RETURN;
+
+      case WITH:
+        r = execute(n.object, x);
+        t = toObject(getValue(r), r, n.object);
+        x.scope = {object: t, parent: x.scope};
+        try {
+            execute(n.body, x);
+        } finally {
+            x.scope = x.scope.parent;
+        }
+        break;
+
+      case VAR:
+      case CONST:
+        for (i = 0, j = n.$length; i < j; i++) {
+            u = n[i].initializer;
+            if (!u)
+                continue;
+            t = n[i].name;
+            for (s = x.scope; s; s = s.parent) {
+                if (hasDirectProperty(s.object, t))
+                    break;
+            }
+            u = getValue(execute(u, x));
+            if (n.type == CONST)
+                s.object[t] = u;
+            else
+                s.object[t] = u;
+        }
+        break;
+
+      case DEBUGGER:
+        throw "NYI: " + tokens[n.type];
+
+      case REQUIRE:
+        var req = new XMLHttpRequest();
+        req.open('GET', n.filename, 'false');
+
+      case SEMICOLON:
+        if (n.expression)
+            // print debugging statements
+                     
+            var the_start = n.start
+            var the_end = n.end
+            var the_statement = parse_result.tokenizer.source.slice(the_start,the_end)
+            //global.debug.document.body.innerHTML += ('<pre>&gt;&gt;&gt; <b>' + the_statement + '</b></pre>')
+            LOG.info('>>>' + the_statement)
+            x.result = getValue(execute(n.expression, x));   
+            //if (x.result)
+            //global.debug.document.body.innerHTML += ( '<pre>&gt;&gt;&gt; ' + x.result + '</pre>')
+            
+        break;
+
+      case LABEL:
+        try {
+            execute(n.statement, x);
+        } catch (e) {
+            if (!(e == BREAK && x.target == n)) { throw e }
+        }
+        break;
+
+      case COMMA:
+        for (i = 0, j = n.$length; i < j; i++)
+            v = getValue(execute(n[i], x));
+        break;
+
+      case ASSIGN:
+        r = execute(n[0], x);
+        t = n[0].assignOp;
+        if (t)
+            u = getValue(r);
+        v = getValue(execute(n[1], x));
+        if (t) {
+            switch (t) {
+              case BITWISE_OR:  v = u | v; break;
+              case BITWISE_XOR: v = u ^ v; break;
+              case BITWISE_AND: v = u & v; break;
+              case LSH:         v = u << v; break;
+              case RSH:         v = u >> v; break;
+              case URSH:        v = u >>> v; break;
+              case PLUS:        v = u + v; break;
+              case MINUS:       v = u - v; break;
+              case MUL:         v = u * v; break;
+              case DIV:         v = u / v; break;
+              case MOD:         v = u % v; break;
+            }
+        }
+        putValue(r, v, n[0]);
+        break;
+
+      case CONDITIONAL:
+        v = getValue(execute(n[0], x)) ? getValue(execute(n[1], x))
+                                       : getValue(execute(n[2], x));
+        break;
+
+      case OR:
+        v = getValue(execute(n[0], x)) || getValue(execute(n[1], x));
+        break;
+
+      case AND:
+        v = getValue(execute(n[0], x)) && getValue(execute(n[1], x));
+        break;
+
+      case BITWISE_OR:
+        v = getValue(execute(n[0], x)) | getValue(execute(n[1], x));
+        break;
+
+      case BITWISE_XOR:
+        v = getValue(execute(n[0], x)) ^ getValue(execute(n[1], x));
+        break;
+
+      case BITWISE_AND:
+        v = getValue(execute(n[0], x)) & getValue(execute(n[1], x));
+        break;
+
+      case EQ:
+        v = getValue(execute(n[0], x)) == getValue(execute(n[1], x));
+        break;
+
+      case NE:
+        v = getValue(execute(n[0], x)) != getValue(execute(n[1], x));
+        break;
+
+      case STRICT_EQ:
+        v = getValue(execute(n[0], x)) === getValue(execute(n[1], x));
+        break;
+
+      case STRICT_NE:
+        v = getValue(execute(n[0], x)) !== getValue(execute(n[1], x));
+        break;
+
+      case LT:
+        v = getValue(execute(n[0], x)) < getValue(execute(n[1], x));
+        break;
+
+      case LE:
+        v = getValue(execute(n[0], x)) <= getValue(execute(n[1], x));
+        break;
+
+      case GE:
+        v = getValue(execute(n[0], x)) >= getValue(execute(n[1], x));
+        break;
+
+      case GT:
+        v = getValue(execute(n[0], x)) > getValue(execute(n[1], x));
+        break;
+
+      case IN:
+        v = getValue(execute(n[0], x)) in getValue(execute(n[1], x));
+        break;
+
+      case INSTANCEOF:
+        t = getValue(execute(n[0], x));
+        u = getValue(execute(n[1], x));
+        if (isObject(u) && typeof u.__hasInstance__ == "function")
+            v = u.__hasInstance__(t);
+        else
+            v = t instanceof u;
+        break;
+
+      case LSH:
+        v = getValue(execute(n[0], x)) << getValue(execute(n[1], x));
+        break;
+
+      case RSH:
+        v = getValue(execute(n[0], x)) >> getValue(execute(n[1], x));
+        break;
+
+      case URSH:
+        v = getValue(execute(n[0], x)) >>> getValue(execute(n[1], x));
+        break;
+
+      case PLUS:
+        v = getValue(execute(n[0], x)) + getValue(execute(n[1], x));
+        break;
+
+      case MINUS:
+        v = getValue(execute(n[0], x)) - getValue(execute(n[1], x));
+        break;
+
+      case MUL:
+        v = getValue(execute(n[0], x)) * getValue(execute(n[1], x));
+        break;
+
+      case DIV:
+        v = getValue(execute(n[0], x)) / getValue(execute(n[1], x));
+        break;
+
+      case MOD:
+        v = getValue(execute(n[0], x)) % getValue(execute(n[1], x));
+        break;
+
+      case DELETE:
+        t = execute(n[0], x);
+        v = !(t instanceof Reference) || delete t.base[t.propertyName];
+        break;
+
+      case VOID:
+        getValue(execute(n[0], x));
+        break;
+
+      case TYPEOF:
+        t = execute(n[0], x);
+        if (t instanceof Reference)
+            t = t.base ? t.base[t.propertyName] : undefined;
+        v = typeof t;
+        break;
+
+      case NOT:
+        v = !getValue(execute(n[0], x));
+        break;
+
+      case BITWISE_NOT:
+        v = ~getValue(execute(n[0], x));
+        break;
+
+      case UNARY_PLUS:
+        v = +getValue(execute(n[0], x));
+        break;
+
+      case UNARY_MINUS:
+        v = -getValue(execute(n[0], x));
+        break;
+
+      case INCREMENT:
+      case DECREMENT:
+        t = execute(n[0], x);
+        u = Number(getValue(t));
+        if (n.postfix)
+            v = u;
+        putValue(t, (n.type == INCREMENT) ? ++u : --u, n[0]);
+        if (!n.postfix)
+            v = u;
+        break;
+
+      case DOT:
+        r = execute(n[0], x);
+        t = getValue(r);
+        u = n[1].value;
+        v = new Reference(toObject(t, r, n[0]), u, n);
+        break;
+
+      case INDEX:
+        r = execute(n[0], x);
+        t = getValue(r);
+        u = getValue(execute(n[1], x));
+        v = new Reference(toObject(t, r, n[0]), String(u), n);
+        break;
+
+      case LIST:
+        // Curse ECMA for specifying that arguments is not an Array object!
+        v = {};
+        for (i = 0, j = n.$length; i < j; i++) {
+            u = getValue(execute(n[i], x));
+            v[i] = u;
+        }
+        v.length = i;
+        break;
+
+      case CALL:
+        r = execute(n[0], x);
+        a = execute(n[1], x);
+        f = getValue(r);
+        if (isPrimitive(f) || typeof f.__call__ != "function") {
+            throw new TypeError(r + " is not callable",
+                                n[0].filename(), n[0].lineno);
+        }
+        t = (r instanceof Reference) ? r.base : null;
+        if (t instanceof Activation)
+            t = null;
+        v = f.__call__(t, a, x);
+        break;
+
+      case NEW:
+      case NEW_WITH_ARGS:
+        r = execute(n[0], x);
+        f = getValue(r);
+        if (n.type == NEW) {
+            a = {};
+            a.length = 0;
+        } else {
+            a = execute(n[1], x);
+        }
+        if (isPrimitive(f) || typeof f.__construct__ != "function") {
+            throw new TypeError(r + " is not a constructor",
+                                n[0].filename(), n[0].lineno);
+        }
+        v = f.__construct__(a, x);
+        break;
+
+      case ARRAY_INIT:
+        v = [];
+        for (i = 0, j = n.$length; i < j; i++) {
+            if (n[i])
+                v[i] = getValue(execute(n[i], x));
+        }
+        v.length = j;
+        break;
+
+      case OBJECT_INIT:
+        v = {};
+        for (i = 0, j = n.$length; i < j; i++) {
+            t = n[i];
+            if (t.type == PROPERTY_INIT) {
+                v[t[0].value] = getValue(execute(t[1], x));
+            } else {
+                f = new FunctionObject(t, x.scope);
+                /*
+                u = (t.type == GETTER) ? '__defineGetter__'
+                                       : '__defineSetter__';
+                v[u](t.name, thunk(f, x));
+                */
+            }
+        }
+        break;
+
+      case NULL:
+        v = null;
+        break;
+
+      case THIS:
+        v = x.thisObject;
+        break;
+
+      case TRUE:
+        v = true;
+        break;
+
+      case FALSE:
+        v = false;
+        break;
+
+      case IDENTIFIER:
+        for (s = x.scope; s; s = s.parent) {
+            if (n.value in s.object)
+                break;
+        }
+        v = new Reference(s && s.object, n.value, n);
+        break;
+
+      case NUMBER:
+      case STRING:
+      case REGEXP:
+        v = n.value;
+        break;
+
+      case GROUP:
+        v = execute(n[0], x);
+        break;
+
+      default:
+        throw "PANIC: unknown operation " + n.type + ": " + uneval(n);
+    }
+    return v;
+}
+
+function Activation(f, a) {
+    for (var i = 0, j = f.params.length; i < j; i++)
+        this[f.params[i]] = a[i];
+    this.arguments = a;
+}
+
+// Null Activation.prototype's proto slot so that Object.prototype.* does not
+// pollute the scope of heavyweight functions.  Also delete its 'constructor'
+// property so that it doesn't pollute function scopes.
+
+Activation.prototype.__proto__ = null;
+delete Activation.prototype.constructor;
+
+function FunctionObject(node, scope) {
+    this.node = node;
+    this.scope = scope;
+    this.length = node.params.length;
+    var proto = {};
+    this.prototype = proto;
+    proto.constructor = this;
+}
+
+var FOp = FunctionObject.prototype = {
+    // Internal methods.
+    __call__: function (t, a, x) {
+        var x2 = new ExecutionContext(FUNCTION_CODE);
+        x2.thisObject = t || global;
+        x2.caller = x;
+        x2.callee = this;
+        a.callee = this;
+        var f = this.node;
+        x2.scope = {object: new Activation(f, a), parent: this.scope};
+
+        ExecutionContext.current = x2;
+        try {
+            execute(f.body, x2);
+        } catch (e) {
+            if (!(e == RETURN)) { throw e } else if (e == RETURN) {
+                return x2.result;
+            }
+            if (e != THROW) { throw e }
+            x.result = x2.result;
+            throw THROW;
+        } finally {
+            ExecutionContext.current = x;
+        }
+        return undefined;
+    },
+
+    __construct__: function (a, x) {
+        var o = new Object;
+        var p = this.prototype;
+        if (isObject(p))
+            o.__proto__ = p;
+        // else o.__proto__ defaulted to Object.prototype
+
+        var v = this.__call__(o, a, x);
+        if (isObject(v))
+            return v;
+        return o;
+    },
+
+    __hasInstance__: function (v) {
+        if (isPrimitive(v))
+            return false;
+        var p = this.prototype;
+        if (isPrimitive(p)) {
+            throw new TypeError("'prototype' property is not an object",
+                                this.node.filename(), this.node.lineno);
+        }
+        var o;
+        while ((o = v.__proto__)) {
+            if (o == p)
+                return true;
+            v = o;
+        }
+        return false;
+    },
+
+    // Standard methods.
+    toString: function () {
+        return this.node.getSource();
+    },
+
+    apply: function (t, a) {
+        // Curse ECMA again!
+        if (typeof this.__call__ != "function") {
+            throw new TypeError("Function.prototype.apply called on" +
+                                " uncallable object");
+        }
+
+        if (t === undefined || t === null)
+            t = global;
+        else if (typeof t != "object")
+            t = toObject(t, t);
+
+        if (a === undefined || a === null) {
+            a = {};
+            a.length = 0;
+        } else if (a instanceof Array) {
+            var v = {};
+            for (var i = 0, j = a.length; i < j; i++)
+                v[i] = a[i];
+            v.length = i;
+            a = v;
+        } else if (!(a instanceof Object)) {
+            // XXX check for a non-arguments object
+            throw new TypeError("Second argument to Function.prototype.apply" +
+                                " must be an array or arguments object",
+                                this.node.filename(), this.node.lineno);
+        }
+
+        return this.__call__(t, a, ExecutionContext.current);
+    },
+
+    call: function (t) {
+        // Curse ECMA a third time!
+        var a = Array.prototype.splice.call(arguments, 1);
+        return this.apply(t, a);
+    }
+};
+
+// Connect Function.prototype and Function.prototype.constructor in global.
+reflectClass('Function', FOp);
+
+// Help native and host-scripted functions be like FunctionObjects.
+var Fp = Function.prototype;
+var REp = RegExp.prototype;
+
+if (!('__call__' in Fp)) {
+    Fp.__call__ = function (t, a, x) {
+        // Curse ECMA yet again!
+        a = Array.prototype.splice.call(a, 0, a.length);
+        return this.apply(t, a);
+    };
+
+    REp.__call__ = function (t, a, x) {
+        a = Array.prototype.splice.call(a, 0, a.length);
+        return this.exec.apply(this, a);
+    };
+
+    Fp.__construct__ = function (a, x) {
+        switch (a.length) {
+          case 0:
+            return new this();
+          case 1:
+            return new this(a[0]);
+          case 2:
+            return new this(a[0], a[1]);
+          case 3:
+            return new this(a[0], a[1], a[2]);
+          case 4:
+            return new this(a[0], a[1], a[2], a[3]);
+          case 5:
+            return new this(a[0], a[1], a[2], a[3], a[4]);
+          case 6:
+            return new this(a[0], a[1], a[2], a[3], a[4], a[5]);
+          case 7:
+            return new this(a[0], a[1], a[2], a[3], a[4], a[5], a[6]);
+        }
+        throw "PANIC: too many arguments to constructor";
+    }
+
+    // Since we use native functions such as Date along with host ones such
+    // as global.eval, we want both to be considered instances of the native
+    // Function constructor.
+    Fp.__hasInstance__ = function (v) {
+        return v instanceof Function || v instanceof global.Function;
+    };
+}
+
+function thunk(f, x) {
+    return function () { return f.__call__(this, arguments, x); };
+}
+
+function evaluate(s, f, l) {
+    if (typeof s != "string")
+        return s;
+
+    var x = ExecutionContext.current;
+    var x2 = new ExecutionContext(GLOBAL_CODE);
+    ExecutionContext.current = x2;
+    try {
+        execute(parse(s, f, l), x2);
+    } catch (e) {
+        if (e != THROW) { throw e }
+        if (x) {
+            x.result = x2.result;
+            throw(THROW);
+        }
+        throw x2.result;
+    } finally {
+        ExecutionContext.current = x;
+    }
+    return x2.result;
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-parse.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-parse.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/narcissus-parse.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,1003 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Narcissus JavaScript engine.
+ *
+ * The Initial Developer of the Original Code is
+ * Brendan Eich <brendan at mozilla.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Richard Hundt <www.plextk.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Narcissus - JS implemented in JS.
+ *
+ * Lexical scanner and parser.
+ */
+
+// jrh
+//module('JS.Parse');
+
+// Build a regexp that recognizes operators and punctuators (except newline).
+var opRegExp =
+/^;|^,|^\?|^:|^\|\||^\&\&|^\||^\^|^\&|^===|^==|^=|^!==|^!=|^<<|^<=|^<|^>>>|^>>|^>=|^>|^\+\+|^\-\-|^\+|^\-|^\*|^\/|^%|^!|^~|^\.|^\[|^\]|^\{|^\}|^\(|^\)/;
+
+// A regexp to match floating point literals (but not integer literals).
+var fpRegExp = /^\d+\.\d*(?:[eE][-+]?\d+)?|^\d+(?:\.\d*)?[eE][-+]?\d+|^\.\d+(?:[eE][-+]?\d+)?/;
+
+function Tokenizer(s, f, l) {
+    this.cursor = 0;
+    this.source = String(s);
+    this.tokens = [];
+    this.tokenIndex = 0;
+    this.lookahead = 0;
+    this.scanNewlines = false;
+    this.scanOperand = true;
+    this.filename = f || "";
+    this.lineno = l || 1;
+}
+
+Tokenizer.prototype = {
+    input : function() {
+        return this.source.substring(this.cursor);
+    },
+
+    done : function() {
+        return this.peek() == END;
+    },
+
+    token : function() {
+        return this.tokens[this.tokenIndex];
+    },
+
+    match: function (tt) {
+        return this.get() == tt || this.unget();
+    },
+
+    mustMatch: function (tt) {
+        if (!this.match(tt))
+            throw this.newSyntaxError("Missing " + this.tokens[tt].toLowerCase());
+        return this.token();
+    },
+
+    peek: function () {
+        var tt;
+        if (this.lookahead) {
+            tt = this.tokens[(this.tokenIndex + this.lookahead) & 3].type;
+        } else {
+            tt = this.get();
+            this.unget();
+        }
+        return tt;
+    },
+
+    peekOnSameLine: function () {
+        this.scanNewlines = true;
+        var tt = this.peek();
+        this.scanNewlines = false;
+        return tt;
+    },
+
+    get: function () {
+        var token;
+        while (this.lookahead) {
+            --this.lookahead;
+            this.tokenIndex = (this.tokenIndex + 1) & 3;
+            token = this.tokens[this.tokenIndex];
+            if (token.type != NEWLINE || this.scanNewlines)
+                return token.type;
+        }
+
+        for (;;) {
+            var input = this.input();
+            var rx = this.scanNewlines ? /^[ \t]+/ : /^\s+/;
+            var match = input.match(rx);
+            if (match) {
+                var spaces = match[0];
+                this.cursor += spaces.length;
+                var newlines = spaces.match(/\n/g);
+                if (newlines)
+                    this.lineno += newlines.length;
+                input = this.input();
+            }
+
+            if (!(match = input.match(/^\/(?:\*(?:.|\n)*?\*\/|\/.*)/)))
+                break;
+            var comment = match[0];
+            this.cursor += comment.length;
+            newlines = comment.match(/\n/g);
+            if (newlines)
+                this.lineno += newlines.length
+        }
+
+        this.tokenIndex = (this.tokenIndex + 1) & 3;
+        token = this.tokens[this.tokenIndex];
+        if (!token)
+            this.tokens[this.tokenIndex] = token = {};
+        if (!input)
+            return token.type = END;
+        if ((match = input.match(fpRegExp))) {
+            token.type = NUMBER;
+            token.value = parseFloat(match[0]);
+        } else if ((match = input.match(/^0[xX][\da-fA-F]+|^0[0-7]*|^\d+/))) {
+            token.type = NUMBER;
+            token.value = parseInt(match[0]);
+        } else if ((match = input.match(/^((\$\w*)|(\w+))/))) {
+            var id = match[0];
+            token.type = keywords[id] || IDENTIFIER;
+            token.value = id;
+        } else if ((match = input.match(/^"(?:\\.|[^"])*"|^'(?:[^']|\\.)*'/))) {
+            token.type = STRING;
+            token.value = eval(match[0]);
+        } else if (this.scanOperand &&
+                   (match = input.match(/^\/((?:\\.|[^\/])+)\/([gi]*)/))) {
+            token.type = REGEXP;
+            token.value = new RegExp(match[1], match[2]);
+        } else if ((match = input.match(opRegExp))) {
+            var op = match[0];
+            if (assignOps[op] && input[op.length] == '=') {
+                token.type = ASSIGN;
+                token.assignOp = GLOBAL[opTypeNames[op]];
+                match[0] += '=';
+            } else {
+                token.type = GLOBAL[opTypeNames[op]];
+                if (this.scanOperand &&
+                    (token.type == PLUS || token.type == MINUS)) {
+                    token.type += UNARY_PLUS - PLUS;
+                }
+                token.assignOp = null;
+            }
+            //debug('token.value => '+op+', token.type => '+token.type);
+            token.value = op;
+        } else {
+            throw this.newSyntaxError("Illegal token");
+        }
+
+        token.start = this.cursor;
+        this.cursor += match[0].length;
+        token.end = this.cursor;
+        token.lineno = this.lineno;
+        return token.type;
+    },
+
+    unget: function () {
+        if (++this.lookahead == 4) throw "PANIC: too much lookahead!";
+        this.tokenIndex = (this.tokenIndex - 1) & 3;
+    },
+
+    newSyntaxError: function (m) {
+        var e = new SyntaxError(m, this.filename, this.lineno);
+        e.source = this.source;
+        e.cursor = this.cursor;
+        return e;
+    }
+};
+
+function CompilerContext(inFunction) {
+    this.inFunction = inFunction;
+    this.stmtStack = [];
+    this.funDecls = [];
+    this.varDecls = [];
+}
+
+var CCp = CompilerContext.prototype;
+CCp.bracketLevel = CCp.curlyLevel = CCp.parenLevel = CCp.hookLevel = 0;
+CCp.ecmaStrictMode = CCp.inForLoopInit = false;
+
+function Script(t, x) {
+    var n = Statements(t, x);
+    n.type = SCRIPT;
+    n.funDecls = x.funDecls;
+    n.varDecls = x.varDecls;
+    return n;
+}
+
+// Node extends Array, which we extend slightly with a top-of-stack method.
+Array.prototype.top = function() {
+    return this.length && this[this.length-1]; 
+}
+
+function NarcNode(t, type) {
+    var token = t.token();
+    if (token) {
+        this.type = type || token.type;
+        this.value = token.value;
+        this.lineno = token.lineno;
+        this.start = token.start;
+        this.end = token.end;
+    } else {
+        this.type = type;
+        this.lineno = t.lineno;
+    }
+    this.tokenizer = t;
+    for (var i = 2; i < arguments.length; i++)
+        this.push(arguments[i]);
+}
+
+var Np = NarcNode.prototype = new Array();
+Np.constructor = NarcNode;
+Np.$length = 0;
+Np.toSource = Object.prototype.toSource;
+
+// Always use push to add operands to an expression, to update start and end.
+Np.push = function (kid) {
+    if (kid.start < this.start)
+        this.start = kid.start;
+    if (this.end < kid.end)
+        this.end = kid.end;
+    //debug('length before => '+this.$length);
+    this[this.$length] = kid;
+    this.$length++;
+    //debug('length after => '+this.$length);
+}
+
+NarcNode.indentLevel = 0;
+
+function tokenstr(tt) {
+    var t = tokens[tt];
+    return /^\W/.test(t) ? opTypeNames[t] : t.toUpperCase();
+}
+
+Np.toString = function () {
+    var a = [];
+    for (var i in this) {
+        if (this.hasOwnProperty(i) && i != 'type')
+            a.push({id: i, value: this[i]});
+    }
+    a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; });
+    INDENTATION = "    ";
+    var n = ++NarcNode.indentLevel;
+    var s = "{\n" + INDENTATION.repeat(n) + "type: " + tokenstr(this.type);
+    for (i = 0; i < a.length; i++)
+        s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value;
+    n = --NarcNode.indentLevel;
+    s += "\n" + INDENTATION.repeat(n) + "}";
+    return s;
+}
+
+Np.getSource = function () {
+    return this.tokenizer.source.slice(this.start, this.end);
+};
+
+Np.filename = function () { return this.tokenizer.filename; };
+
+String.prototype.repeat = function (n) {
+    var s = "", t = this + s;
+    while (--n >= 0)
+        s += t;
+    return s;
+}
+
+// Statement stack and nested statement handler.
+function nest(t, x, node, func, end) {
+    x.stmtStack.push(node);
+    var n = func(t, x);
+    x.stmtStack.pop();
+    end && t.mustMatch(end);
+    return n;
+}
+
+function Statements(t, x) {
+    var n = new NarcNode(t, BLOCK);
+    x.stmtStack.push(n);
+    while (!t.done() && t.peek() != RIGHT_CURLY)
+        n.push(Statement(t, x));
+    x.stmtStack.pop();
+    return n;
+}
+
+function Block(t, x) {
+    t.mustMatch(LEFT_CURLY);
+    var n = Statements(t, x);
+    t.mustMatch(RIGHT_CURLY);
+    return n;
+}
+
+DECLARED_FORM = 0; EXPRESSED_FORM = 1; STATEMENT_FORM = 2;
+
+function Statement(t, x) {
+    var i, label, n, n2, ss, tt = t.get();
+
+    // Cases for statements ending in a right curly return early, avoiding the
+    // common semicolon insertion magic after this switch.
+    switch (tt) {
+      case FUNCTION:
+        return FunctionDefinition(t, x, true,
+                                  (x.stmtStack.length > 1)
+                                  ? STATEMENT_FORM
+                                  : DECLARED_FORM);
+
+      case LEFT_CURLY:
+        n = Statements(t, x);
+        t.mustMatch(RIGHT_CURLY);
+        return n;
+
+      case IF:
+        n = new NarcNode(t);
+        n.condition = ParenExpression(t, x);
+        x.stmtStack.push(n);
+        n.thenPart = Statement(t, x);
+        n.elsePart = t.match(ELSE) ? Statement(t, x) : null;
+        x.stmtStack.pop();
+        return n;
+
+      case SWITCH:
+        n = new NarcNode(t);
+        t.mustMatch(LEFT_PAREN);
+        n.discriminant = Expression(t, x);
+        t.mustMatch(RIGHT_PAREN);
+        n.cases = [];
+        n.defaultIndex = -1;
+        x.stmtStack.push(n);
+        t.mustMatch(LEFT_CURLY);
+        while ((tt = t.get()) != RIGHT_CURLY) {
+            switch (tt) {
+              case DEFAULT:
+                if (n.defaultIndex >= 0)
+                    throw t.newSyntaxError("More than one switch default");
+                // FALL THROUGH
+              case CASE:
+                n2 = new NarcNode(t);
+                if (tt == DEFAULT)
+                    n.defaultIndex = n.cases.length;
+                else
+                    n2.caseLabel = Expression(t, x, COLON);
+                break;
+              default:
+                throw t.newSyntaxError("Invalid switch case");
+            }
+            t.mustMatch(COLON);
+            n2.statements = new NarcNode(t, BLOCK);
+            while ((tt=t.peek()) != CASE && tt != DEFAULT && tt != RIGHT_CURLY)
+                n2.statements.push(Statement(t, x));
+            n.cases.push(n2);
+        }
+        x.stmtStack.pop();
+        return n;
+
+      case FOR:
+        n = new NarcNode(t);
+        n.isLoop = true;
+        t.mustMatch(LEFT_PAREN);
+        if ((tt = t.peek()) != SEMICOLON) {
+            x.inForLoopInit = true;
+            if (tt == VAR || tt == CONST) {
+                t.get();
+                n2 = Variables(t, x);
+            } else {
+                n2 = Expression(t, x);
+            }
+            x.inForLoopInit = false;
+        }
+        if (n2 && t.match(IN)) {
+            n.type = FOR_IN;
+            if (n2.type == VAR) {
+                if (n2.$length != 1) {
+                    throw new SyntaxError("Invalid for..in left-hand side",
+                                          t.filename, n2.lineno);
+                }
+
+                // NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name.
+                n.iterator = n2[0];
+                n.varDecl = n2;
+            } else {
+                n.iterator = n2;
+                n.varDecl = null;
+            }
+            n.object = Expression(t, x);
+        } else {
+            n.setup = n2 || null;
+            t.mustMatch(SEMICOLON);
+            n.condition = (t.peek() == SEMICOLON) ? null : Expression(t, x);
+            t.mustMatch(SEMICOLON);
+            n.update = (t.peek() == RIGHT_PAREN) ? null : Expression(t, x);
+        }
+        t.mustMatch(RIGHT_PAREN);
+        n.body = nest(t, x, n, Statement);
+        return n;
+
+      case WHILE:
+        n = new NarcNode(t);
+        n.isLoop = true;
+        n.condition = ParenExpression(t, x);
+        n.body = nest(t, x, n, Statement);
+        return n;
+
+      case DO:
+        n = new NarcNode(t);
+        n.isLoop = true;
+        n.body = nest(t, x, n, Statement, WHILE);
+        n.condition = ParenExpression(t, x);
+        if (!x.ecmaStrictMode) {
+            // <script language="JavaScript"> (without version hints) may need
+            // automatic semicolon insertion without a newline after do-while.
+            // See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
+            t.match(SEMICOLON);
+            return n;
+        }
+        break;
+
+      case BREAK:
+      case CONTINUE:
+        n = new NarcNode(t);
+        if (t.peekOnSameLine() == IDENTIFIER) {
+            t.get();
+            n.label = t.token().value;
+        }
+        ss = x.stmtStack;
+        i = ss.length;
+        label = n.label;
+        if (label) {
+            do {
+                if (--i < 0)
+                    throw t.newSyntaxError("Label not found");
+            } while (ss[i].label != label);
+        } else {
+            do {
+                if (--i < 0) {
+                    throw t.newSyntaxError("Invalid " + ((tt == BREAK)
+                                                         ? "break"
+                                                         : "continue"));
+                }
+            } while (!ss[i].isLoop && (tt != BREAK || ss[i].type != SWITCH));
+        }
+        n.target = ss[i];
+        break;
+
+      case TRY:
+        n = new NarcNode(t);
+        n.tryBlock = Block(t, x);
+        n.catchClauses = [];
+        while (t.match(CATCH)) {
+            n2 = new NarcNode(t);
+            t.mustMatch(LEFT_PAREN);
+            n2.varName = t.mustMatch(IDENTIFIER).value;
+            if (t.match(IF)) {
+                if (x.ecmaStrictMode)
+                    throw t.newSyntaxError("Illegal catch guard");
+                if (n.catchClauses.length && !n.catchClauses.top().guard)
+                    throw t.newSyntaxError("Guarded catch after unguarded");
+                n2.guard = Expression(t, x);
+            } else {
+                n2.guard = null;
+            }
+            t.mustMatch(RIGHT_PAREN);
+            n2.block = Block(t, x);
+            n.catchClauses.push(n2);
+        }
+        if (t.match(FINALLY))
+            n.finallyBlock = Block(t, x);
+        if (!n.catchClauses.length && !n.finallyBlock)
+            throw t.newSyntaxError("Invalid try statement");
+        return n;
+
+      case CATCH:
+      case FINALLY:
+        throw t.newSyntaxError(tokens[tt] + " without preceding try");
+
+      case THROW:
+        n = new NarcNode(t);
+        n.exception = Expression(t, x);
+        break;
+
+      case RETURN:
+        if (!x.inFunction)
+            throw t.newSyntaxError("Invalid return");
+        n = new NarcNode(t);
+        tt = t.peekOnSameLine();
+        if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY)
+            n.value = Expression(t, x);
+        break;
+
+      case WITH:
+        n = new NarcNode(t);
+        n.object = ParenExpression(t, x);
+        n.body = nest(t, x, n, Statement);
+        return n;
+
+      case VAR:
+      case CONST:
+        n = Variables(t, x);
+        break;
+
+      case DEBUGGER:
+        n = new NarcNode(t);
+        break;
+
+      case REQUIRE:
+        n = new NarcNode(t);
+        n.classPath = ParenExpression(t, x);
+        break;
+
+      case NEWLINE:
+      case SEMICOLON:
+        n = new NarcNode(t, SEMICOLON);
+        n.expression = null;
+        return n;
+
+      default:
+        if (tt == IDENTIFIER && t.peek() == COLON) {
+            label = t.token().value;
+            ss = x.stmtStack;
+            for (i = ss.length-1; i >= 0; --i) {
+                if (ss[i].label == label)
+                    throw t.newSyntaxError("Duplicate label");
+            }
+            t.get();
+            n = new NarcNode(t, LABEL);
+            n.label = label;
+            n.statement = nest(t, x, n, Statement);
+            return n;
+        }
+
+        n = new NarcNode(t, SEMICOLON);
+        t.unget();
+        n.expression = Expression(t, x);
+        n.end = n.expression.end;
+        break;
+    }
+
+    if (t.lineno == t.token().lineno) {
+        tt = t.peekOnSameLine();
+        if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY)
+            throw t.newSyntaxError("Missing ; before statement");
+    }
+    t.match(SEMICOLON);
+    return n;
+}
+
+function FunctionDefinition(t, x, requireName, functionForm) {
+    var f = new NarcNode(t);
+    if (f.type != FUNCTION)
+        f.type = (f.value == "get") ? GETTER : SETTER;
+    if (t.match(IDENTIFIER)) {
+        f.name = t.token().value;
+    }
+    else if (requireName)
+        throw t.newSyntaxError("Missing function identifier");
+
+    t.mustMatch(LEFT_PAREN);
+    f.params = [];
+    var tt;
+    while ((tt = t.get()) != RIGHT_PAREN) {
+        if (tt != IDENTIFIER)
+            throw t.newSyntaxError("Missing formal parameter");
+        f.params.push(t.token().value);
+        if (t.peek() != RIGHT_PAREN)
+            t.mustMatch(COMMA);
+    }
+
+    t.mustMatch(LEFT_CURLY);
+    var x2 = new CompilerContext(true);
+    f.body = Script(t, x2);
+    t.mustMatch(RIGHT_CURLY);
+    f.end = t.token().end;
+
+    f.functionForm = functionForm;
+    if (functionForm == DECLARED_FORM) {
+        x.funDecls.push(f);
+    }
+
+    return f;
+}
+
+function Variables(t, x) {
+    var n = new NarcNode(t);
+    do {
+        t.mustMatch(IDENTIFIER);
+        var n2 = new NarcNode(t);
+        n2.name = n2.value;
+        if (t.match(ASSIGN)) {
+            if (t.token().assignOp)
+                throw t.newSyntaxError("Invalid variable initialization");
+            n2.initializer = Expression(t, x, COMMA);
+        }
+        n2.readOnly = (n.type == CONST);
+        n.push(n2);
+        x.varDecls.push(n2);
+    } while (t.match(COMMA));
+    return n;
+}
+
+function ParenExpression(t, x) {
+    t.mustMatch(LEFT_PAREN);
+    var n = Expression(t, x);
+    t.mustMatch(RIGHT_PAREN);
+    return n;
+}
+
+var opPrecedence = {
+    SEMICOLON: 0,
+    COMMA: 1,
+    ASSIGN: 2, HOOK: 2, COLON: 2, CONDITIONAL: 2,
+    // The above all have to have the same precedence, see bug 330975.
+    OR: 4,
+    AND: 5,
+    BITWISE_OR: 6,
+    BITWISE_XOR: 7,
+    BITWISE_AND: 8,
+    EQ: 9, NE: 9, STRICT_EQ: 9, STRICT_NE: 9,
+    LT: 10, LE: 10, GE: 10, GT: 10, IN: 10, INSTANCEOF: 10,
+    LSH: 11, RSH: 11, URSH: 11,
+    PLUS: 12, MINUS: 12,
+    MUL: 13, DIV: 13, MOD: 13,
+    DELETE: 14, VOID: 14, TYPEOF: 14, // PRE_INCREMENT: 14, PRE_DECREMENT: 14,
+    NOT: 14, BITWISE_NOT: 14, UNARY_PLUS: 14, UNARY_MINUS: 14,
+    INCREMENT: 15, DECREMENT: 15,     // postfix
+    NEW: 16,
+    DOT: 17
+};
+
+// Map operator type code to precedence.
+for (i in opPrecedence)
+    opPrecedence[GLOBAL[i]] = opPrecedence[i];
+
+var opArity = {
+    COMMA: -2,
+    ASSIGN: 2,
+    CONDITIONAL: 3,
+    OR: 2,
+    AND: 2,
+    BITWISE_OR: 2,
+    BITWISE_XOR: 2,
+    BITWISE_AND: 2,
+    EQ: 2, NE: 2, STRICT_EQ: 2, STRICT_NE: 2,
+    LT: 2, LE: 2, GE: 2, GT: 2, IN: 2, INSTANCEOF: 2,
+    LSH: 2, RSH: 2, URSH: 2,
+    PLUS: 2, MINUS: 2,
+    MUL: 2, DIV: 2, MOD: 2,
+    DELETE: 1, VOID: 1, TYPEOF: 1,  // PRE_INCREMENT: 1, PRE_DECREMENT: 1,
+    NOT: 1, BITWISE_NOT: 1, UNARY_PLUS: 1, UNARY_MINUS: 1,
+    INCREMENT: 1, DECREMENT: 1,     // postfix
+    NEW: 1, NEW_WITH_ARGS: 2, DOT: 2, INDEX: 2, CALL: 2,
+    ARRAY_INIT: 1, OBJECT_INIT: 1, GROUP: 1
+};
+
+// Map operator type code to arity.
+for (i in opArity)
+    opArity[GLOBAL[i]] = opArity[i];
+
+function Expression(t, x, stop) {
+    var n, id, tt, operators = [], operands = [];
+    var bl = x.bracketLevel, cl = x.curlyLevel, pl = x.parenLevel,
+        hl = x.hookLevel;
+
+    function reduce() {
+        //debug('OPERATORS => '+operators);
+        var n = operators.pop();
+        var op = n.type;
+        var arity = opArity[op];
+        if (arity == -2) {
+            // Flatten left-associative trees.
+            var left = operands.length >= 2 && operands[operands.length-2];
+            if (left.type == op) {
+                var right = operands.pop();
+                left.push(right);
+                return left;
+            }
+            arity = 2;
+        }
+
+        // Always use push to add operands to n, to update start and end.
+        var a = operands.splice(operands.length - arity, operands.length);
+        for (var i = 0; i < arity; i++) {
+            n.push(a[i]);
+        }
+
+        // Include closing bracket or postfix operator in [start,end).
+        if (n.end < t.token().end)
+            n.end = t.token().end;
+
+        operands.push(n);
+        return n;
+    }
+
+loop:
+    while ((tt = t.get()) != END) {
+        //debug('TT => '+tokens[tt]);
+        if (tt == stop &&
+            x.bracketLevel == bl && x.curlyLevel == cl && x.parenLevel == pl &&
+            x.hookLevel == hl) {
+            // Stop only if tt matches the optional stop parameter, and that
+            // token is not quoted by some kind of bracket.
+            break;
+        }
+        switch (tt) {
+          case SEMICOLON:
+            // NB: cannot be empty, Statement handled that.
+            break loop;
+
+          case ASSIGN:
+          case HOOK:
+          case COLON:
+            if (t.scanOperand)
+                break loop;
+            // Use >, not >=, for right-associative ASSIGN and HOOK/COLON.
+            while (operators.length && opPrecedence[operators.top().type] > opPrecedence[tt] ||
+                   (tt == COLON && operators.top().type == ASSIGN)) {
+                reduce();
+            }
+            if (tt == COLON) {
+                n = operators.top();
+                if (n.type != HOOK)
+                    throw t.newSyntaxError("Invalid label");
+                n.type = CONDITIONAL;
+                --x.hookLevel;
+            } else {
+                operators.push(new NarcNode(t));
+                if (tt == ASSIGN)
+                    operands.top().assignOp = t.token().assignOp;
+                else
+                    ++x.hookLevel;      // tt == HOOK
+            }
+            t.scanOperand = true;
+            break;
+
+          case IN:
+            // An in operator should not be parsed if we're parsing the head of
+            // a for (...) loop, unless it is in the then part of a conditional
+            // expression, or parenthesized somehow.
+            if (x.inForLoopInit && !x.hookLevel &&
+                !x.bracketLevel && !x.curlyLevel && !x.parenLevel) {
+                break loop;
+            }
+            // FALL THROUGH
+          case COMMA:
+            // Treat comma as left-associative so reduce can fold left-heavy
+            // COMMA trees into a single array.
+            // FALL THROUGH
+          case OR:
+          case AND:
+          case BITWISE_OR:
+          case BITWISE_XOR:
+          case BITWISE_AND:
+          case EQ: case NE: case STRICT_EQ: case STRICT_NE:
+          case LT: case LE: case GE: case GT:
+          case INSTANCEOF:
+          case LSH: case RSH: case URSH:
+          case PLUS: case MINUS:
+          case MUL: case DIV: case MOD:
+          case DOT:
+            if (t.scanOperand)
+                break loop;
+            while (operators.length && opPrecedence[operators.top().type] >= opPrecedence[tt])
+                reduce();
+            if (tt == DOT) {
+                t.mustMatch(IDENTIFIER);
+                operands.push(new NarcNode(t, DOT, operands.pop(), new NarcNode(t)));
+            } else {
+                operators.push(new NarcNode(t));
+                t.scanOperand = true;
+            }
+            break;
+
+          case DELETE: case VOID: case TYPEOF:
+          case NOT: case BITWISE_NOT: case UNARY_PLUS: case UNARY_MINUS:
+          case NEW:
+            if (!t.scanOperand)
+                break loop;
+            operators.push(new NarcNode(t));
+            break;
+
+          case INCREMENT: case DECREMENT:
+            if (t.scanOperand) {
+                operators.push(new NarcNode(t));  // prefix increment or decrement
+            } else {
+                // Use >, not >=, so postfix has higher precedence than prefix.
+                while (operators.length && opPrecedence[operators.top().type] > opPrecedence[tt])
+                    reduce();
+                n = new NarcNode(t, tt, operands.pop());
+                n.postfix = true;
+                operands.push(n);
+            }
+            break;
+
+          case FUNCTION:
+            if (!t.scanOperand)
+                break loop;
+            operands.push(FunctionDefinition(t, x, false, EXPRESSED_FORM));
+            t.scanOperand = false;
+            break;
+
+          case NULL: case THIS: case TRUE: case FALSE:
+          case IDENTIFIER: case NUMBER: case STRING: case REGEXP:
+            if (!t.scanOperand)
+                break loop;
+            operands.push(new NarcNode(t));
+            t.scanOperand = false;
+            break;
+
+          case LEFT_BRACKET:
+            if (t.scanOperand) {
+                // Array initialiser.  Parse using recursive descent, as the
+                // sub-grammar here is not an operator grammar.
+                n = new NarcNode(t, ARRAY_INIT);
+                while ((tt = t.peek()) != RIGHT_BRACKET) {
+                    if (tt == COMMA) {
+                        t.get();
+                        n.push(null);
+                        continue;
+                    }
+                    n.push(Expression(t, x, COMMA));
+                    if (!t.match(COMMA))
+                        break;
+                }
+                t.mustMatch(RIGHT_BRACKET);
+                operands.push(n);
+                t.scanOperand = false;
+            } else {
+                // Property indexing operator.
+                operators.push(new NarcNode(t, INDEX));
+                t.scanOperand = true;
+                ++x.bracketLevel;
+            }
+            break;
+
+          case RIGHT_BRACKET:
+            if (t.scanOperand || x.bracketLevel == bl)
+                break loop;
+            while (reduce().type != INDEX)
+                continue;
+            --x.bracketLevel;
+            break;
+
+          case LEFT_CURLY:
+            if (!t.scanOperand)
+                break loop;
+            // Object initialiser.  As for array initialisers (see above),
+            // parse using recursive descent.
+            ++x.curlyLevel;
+            n = new NarcNode(t, OBJECT_INIT);
+          object_init:
+            if (!t.match(RIGHT_CURLY)) {
+                do {
+                    tt = t.get();
+                    if ((t.token().value == "get" || t.token().value == "set") &&
+                        t.peek() == IDENTIFIER) {
+                        if (x.ecmaStrictMode)
+                            throw t.newSyntaxError("Illegal property accessor");
+                        n.push(FunctionDefinition(t, x, true, EXPRESSED_FORM));
+                    } else {
+                        switch (tt) {
+                          case IDENTIFIER:
+                          case NUMBER:
+                          case STRING:
+                            id = new NarcNode(t);
+                            break;
+                          case RIGHT_CURLY:
+                            if (x.ecmaStrictMode)
+                                throw t.newSyntaxError("Illegal trailing ,");
+                            break object_init;
+                          default:
+                            throw t.newSyntaxError("Invalid property name");
+                        }
+                        t.mustMatch(COLON);
+                        n.push(new NarcNode(t, PROPERTY_INIT, id,
+                                        Expression(t, x, COMMA)));
+                    }
+                } while (t.match(COMMA));
+                t.mustMatch(RIGHT_CURLY);
+            }
+            operands.push(n);
+            t.scanOperand = false;
+            --x.curlyLevel;
+            break;
+
+          case RIGHT_CURLY:
+            if (!t.scanOperand && x.curlyLevel != cl)
+                throw "PANIC: right curly botch";
+            break loop;
+
+          case LEFT_PAREN:
+            if (t.scanOperand) {
+                operators.push(new NarcNode(t, GROUP));
+            } else {
+                while (operators.length && opPrecedence[operators.top().type] > opPrecedence[NEW])
+                    reduce();
+
+                // Handle () now, to regularize the n-ary case for n > 0.
+                // We must set scanOperand in case there are arguments and
+                // the first one is a regexp or unary+/-.
+                n = operators.top();
+                t.scanOperand = true;
+                if (t.match(RIGHT_PAREN)) {
+                    if (n.type == NEW) {
+                        --operators.length;
+                        n.push(operands.pop());
+                    } else {
+                        n = new NarcNode(t, CALL, operands.pop(),
+                                     new NarcNode(t, LIST));
+                    }
+                    operands.push(n);
+                    t.scanOperand = false;
+                    break;
+                }
+                if (n.type == NEW)
+                    n.type = NEW_WITH_ARGS;
+                else
+                    operators.push(new NarcNode(t, CALL));
+            }
+            ++x.parenLevel;
+            break;
+
+          case RIGHT_PAREN:
+            if (t.scanOperand || x.parenLevel == pl)
+                break loop;
+            while ((tt = reduce().type) != GROUP && tt != CALL &&
+                   tt != NEW_WITH_ARGS) {
+                continue;
+            }
+            if (tt != GROUP) {
+                n = operands.top();
+                if (n[1].type != COMMA)
+                    n[1] = new NarcNode(t, LIST, n[1]);
+                else
+                    n[1].type = LIST;
+            }
+            --x.parenLevel;
+            break;
+
+          // Automatic semicolon insertion means we may scan across a newline
+          // and into the beginning of another statement.  If so, break out of
+          // the while loop and let the t.scanOperand logic handle errors.
+          default:
+            break loop;
+        }
+    }
+    if (x.hookLevel != hl)
+        throw t.newSyntaxError("Missing : after ?");
+    if (x.parenLevel != pl)
+        throw t.newSyntaxError("Missing ) in parenthetical");
+    if (x.bracketLevel != bl)
+        throw t.newSyntaxError("Missing ] in index expression");
+    if (t.scanOperand)
+        throw t.newSyntaxError("Missing operand");
+
+    // Resume default mode, scanning for operands, not operators.
+    t.scanOperand = true;
+    t.unget();
+
+    while (operators.length)
+        reduce();
+    return operands.pop();
+}
+
+function parse(s, f, l) {
+    var t = new Tokenizer(s, f, l);
+    var x = new CompilerContext(false);
+    var n = Script(t, x);
+    if (!t.done())
+        throw t.newSyntaxError("Syntax error");
+    return n;
+}
+
+debug = function(msg) {
+    document.body.appendChild(document.createTextNode(msg));
+    document.body.appendChild(document.createElement('br'));
+}
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/se2html.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/se2html.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/se2html.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,63 @@
+/*
+
+This is an experiment in creating a "selenese" parser that drastically
+cuts down on the line noise associated with writing tests in HTML.
+
+The 'parse' function will accept the follow sample commands.
+
+test-cases:
+    //comment
+    command "param"
+    command "param" // comment
+    command "param" "param2"
+    command "param" "param2" // this is a comment
+
+TODO: 
+1) Deal with multiline parameters
+2) Escape quotes properly
+3) Determine whether this should/will become the "preferred" syntax 
+   for delivered Selenium self-test scripts
+*/    
+
+
+function separse(doc) {
+    // Get object
+    script = doc.getElementById('testcase')
+    // Split into lines
+    lines = script.text.split('\n');
+
+
+    var command_pattern = / *(\w+) *"([^"]*)" *(?:"([^"]*)"){0,1}(?: *(\/\/ *.+))*/i;
+    var comment_pattern = /^ *(\/\/ *.+)/
+
+    // Regex each line into selenium command and convert into table row.
+    // eg. "<command> <quote> <quote> <comment>"
+    var new_test_source = '';
+    var new_line        = '';
+    for (var x=0; x < lines.length; x++) {
+        result = lines[x].match(command_pattern);
+        if (result != null) {
+            new_line = "<tr><td>" + (result[1] || '&nbsp;') + "</td>" +
+                           "<td>" + (result[2] || '&nbsp;') + "</td>" +
+                           "<td>" + (result[3] || '&nbsp;') + "</td>" +
+                           "<td>" + (result[4] || '&nbsp;') + "</td></tr>\n";
+            new_test_source += new_line;
+        }
+        result = lines[x].match(comment_pattern);
+        if (result != null) {
+            new_line = '<tr><td rowspan="1" colspan="4">' +
+                       (result[1] || '&nbsp;') +
+                       '</td></tr>';
+            new_test_source += new_line;
+        }
+    }
+
+    // Create HTML Table        
+    body = doc.body
+    body.innerHTML += "<table class='selenium' id='testtable'>"+
+                      new_test_source +
+                      "</table>";
+
+}
+
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-api.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-api.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-api.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,2409 @@
+/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+// TODO: stop navigating this.browserbot.document() ... it breaks encapsulation
+
+var storedVars = new Object();
+
+function Selenium(browserbot) {
+    /**
+     * Defines an object that runs Selenium commands.
+     *
+     * <h3><a name="locators"></a>Element Locators</h3>
+     * <p>
+     * Element Locators tell Selenium which HTML element a command refers to.
+     * The format of a locator is:</p>
+     * <blockquote>
+     * <em>locatorType</em><strong>=</strong><em>argument</em>
+     * </blockquote>
+     *
+     * <p>
+     * We support the following strategies for locating elements:
+     * </p>
+     * 
+     * <ul>
+     * <li><strong>identifier</strong>=<em>id</em>: 
+     * Select the element with the specified &#064;id attribute. If no match is
+     * found, select the first element whose &#064;name attribute is <em>id</em>.
+     * (This is normally the default; see below.)</li>
+     * <li><strong>id</strong>=<em>id</em>:
+     * Select the element with the specified &#064;id attribute.</li>
+     *
+     * <li><strong>name</strong>=<em>name</em>:
+     * Select the first element with the specified &#064;name attribute.
+     * <ul class="first last simple">
+     * <li>username</li>
+     * <li>name=username</li>
+     * </ul>
+     * 
+     * <p>The name may optionally be followed by one or more <em>element-filters</em>, separated from the name by whitespace.  If the <em>filterType</em> is not specified, <strong>value</strong> is assumed.</p>
+     *
+     * <ul class="first last simple">
+     * <li>name=flavour value=chocolate</li>
+     * </ul>
+     * </li>
+     * <li><strong>dom</strong>=<em>javascriptExpression</em>: 
+     *
+     * Find an element by evaluating the specified string.  This allows you to traverse the HTML Document Object
+     * Model using JavaScript.  Note that you must not return a value in this string; simply make it the last expression in the block.
+     * <ul class="first last simple">
+     * <li>dom=document.forms['myForm'].myDropdown</li>
+     * <li>dom=document.images[56]</li>
+     * <li>dom=function foo() { return document.links[1]; }; foo();</li>
+     * </ul>
+     *
+     * </li>
+     *
+     * <li><strong>xpath</strong>=<em>xpathExpression</em>: 
+     * Locate an element using an XPath expression.
+     * <ul class="first last simple">
+     * <li>xpath=//img[&#064;alt='The image alt text']</li>
+     * <li>xpath=//table[&#064;id='table1']//tr[4]/td[2]</li>
+     * <li>xpath=//a[contains(&#064;href,'#id1')]</li>
+     * <li>xpath=//a[contains(&#064;href,'#id1')]/&#064;class</li>
+     * <li>xpath=(//table[&#064;class='stylee'])//th[text()='theHeaderText']/../td</li>
+     * <li>xpath=//input[&#064;name='name2' and &#064;value='yes']</li>
+     * <li>xpath=//*[text()="right"]</li>
+     *
+     * </ul>
+     * </li>
+     * <li><strong>link</strong>=<em>textPattern</em>:
+     * Select the link (anchor) element which contains text matching the
+     * specified <em>pattern</em>.
+     * <ul class="first last simple">
+     * <li>link=The link text</li>
+     * </ul>
+     *
+     * </li>
+     *
+     * <li><strong>css</strong>=<em>cssSelectorSyntax</em>:
+     * Select the element using css selectors. Please refer to <a href="http://www.w3.org/TR/REC-CSS2/selector.html">CSS2 selectors</a>, <a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">CSS3 selectors</a> for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package.
+     * <ul class="first last simple">
+     * <li>css=a[href="#id3"]</li>
+     * <li>css=span#firstChild + span</li>
+     * </ul>
+     * <p>Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after). </p>
+     * </li>
+     * </ul>
+     * 
+     * <p>
+     * Without an explicit locator prefix, Selenium uses the following default
+     * strategies:
+     * </p>
+     *
+     * <ul class="simple">
+     * <li><strong>dom</strong>, for locators starting with &quot;document.&quot;</li>
+     * <li><strong>xpath</strong>, for locators starting with &quot;//&quot;</li>
+     * <li><strong>identifier</strong>, otherwise</li>
+     * </ul>
+     *
+     * <h3><a name="element-filters">Element Filters</a></h3>
+     * <blockquote>
+     * <p>Element filters can be used with a locator to refine a list of candidate elements.  They are currently used only in the 'name' element-locator.</p>
+     * <p>Filters look much like locators, ie.</p>
+     * <blockquote>
+     * <em>filterType</em><strong>=</strong><em>argument</em></blockquote>
+     *
+     * <p>Supported element-filters are:</p>
+     * <p><strong>value=</strong><em>valuePattern</em></p>
+     * <blockquote>
+     * Matches elements based on their values.  This is particularly useful for refining a list of similarly-named toggle-buttons.</blockquote>
+     * <p><strong>index=</strong><em>index</em></p>
+     * <blockquote>
+     * Selects a single element based on its position in the list (offset from zero).</blockquote>
+     * </blockquote>
+     *
+     * <h3><a name="patterns"></a>String-match Patterns</h3>
+     *
+     * <p>
+     * Various Pattern syntaxes are available for matching string values:
+     * </p>
+     * <ul>
+     * <li><strong>glob:</strong><em>pattern</em>:
+     * Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a
+     * kind of limited regular-expression syntax typically used in command-line
+     * shells. In a glob pattern, "*" represents any sequence of characters, and "?"
+     * represents any single character. Glob patterns match against the entire
+     * string.</li>
+     * <li><strong>regexp:</strong><em>regexp</em>:
+     * Match a string using a regular-expression. The full power of JavaScript
+     * regular-expressions is available.</li>
+     * <li><strong>exact:</strong><em>string</em>:
+     *
+     * Match a string exactly, verbatim, without any of that fancy wildcard
+     * stuff.</li>
+     * </ul>
+     * <p>
+     * If no pattern prefix is specified, Selenium assumes that it's a "glob"
+     * pattern.
+     * </p>
+     */
+    this.browserbot = browserbot;
+    this.optionLocatorFactory = new OptionLocatorFactory();
+    // DGF for backwards compatibility
+    this.page = function() {
+        return browserbot;
+    };
+    this.defaultTimeout = Selenium.DEFAULT_TIMEOUT;
+    this.mouseSpeed = 10;
+}
+
+Selenium.DEFAULT_TIMEOUT = 30 * 1000;
+Selenium.DEFAULT_MOUSE_SPEED = 10;
+
+Selenium.decorateFunctionWithTimeout = function(f, timeout) {
+    if (f == null) {
+        return null;
+    }
+    var timeoutValue = parseInt(timeout);
+    if (isNaN(timeoutValue)) {
+        throw new SeleniumError("Timeout is not a number: '" + timeout + "'");
+    }
+    var now = new Date().getTime();
+    var timeoutTime = now + timeoutValue;
+    return function() {
+        if (new Date().getTime() > timeoutTime) {
+            throw new SeleniumError("Timed out after " + timeoutValue + "ms");
+        }
+        return f();
+    };
+}
+
+Selenium.createForWindow = function(window, proxyInjectionMode) {
+    if (!window.location) {
+        throw "error: not a window!";
+    }
+    return new Selenium(BrowserBot.createForWindow(window, proxyInjectionMode));
+};
+
+Selenium.prototype.reset = function() {
+    this.defaultTimeout = Selenium.DEFAULT_TIMEOUT;
+    // todo: this.browserbot.reset()
+    this.browserbot.selectWindow("null");
+    this.browserbot.resetPopups();
+};
+
+Selenium.prototype.doClick = function(locator) {
+    /**
+   * Clicks on a link, button, checkbox or radio button. If the click action
+   * causes a new page to load (like a link usually does), call
+   * waitForPageToLoad.
+   *
+   * @param locator an element locator
+   *
+   */
+   var element = this.browserbot.findElement(locator);
+   this.browserbot.clickElement(element);
+};
+
+Selenium.prototype.doDoubleClick = function(locator) {
+    /**
+   * Double clicks on a link, button, checkbox or radio button. If the double click action
+   * causes a new page to load (like a link usually does), call
+   * waitForPageToLoad.
+   *
+   * @param locator an element locator
+   *
+   */
+   var element = this.browserbot.findElement(locator);
+   this.browserbot.doubleClickElement(element);
+};
+
+Selenium.prototype.doClickAt = function(locator, coordString) {
+    /**
+   * Clicks on a link, button, checkbox or radio button. If the click action
+   * causes a new page to load (like a link usually does), call
+   * waitForPageToLoad.
+   *
+   * @param locator an element locator
+   * @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
+   *      event relative to the element returned by the locator.
+   *
+   */
+    var element = this.browserbot.findElement(locator);
+    var clientXY = getClientXY(element, coordString)
+    this.browserbot.clickElement(element, clientXY[0], clientXY[1]);
+};
+
+Selenium.prototype.doDoubleClickAt = function(locator, coordString) {
+    /**
+   * Doubleclicks on a link, button, checkbox or radio button. If the action
+   * causes a new page to load (like a link usually does), call
+   * waitForPageToLoad.
+   *
+   * @param locator an element locator
+   * @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
+   *      event relative to the element returned by the locator.
+   *
+   */
+    var element = this.browserbot.findElement(locator);
+    var clientXY = getClientXY(element, coordString)
+    this.browserbot.doubleClickElement(element, clientXY[0], clientXY[1]);
+};
+
+Selenium.prototype.doFireEvent = function(locator, eventName) {
+    /**
+   * Explicitly simulate an event, to trigger the corresponding &quot;on<em>event</em>&quot;
+   * handler.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @param eventName the event name, e.g. "focus" or "blur"
+   */
+    var element = this.browserbot.findElement(locator);
+    triggerEvent(element, eventName, false);
+};
+
+Selenium.prototype.doKeyPress = function(locator, keySequence) {
+    /**
+   * Simulates a user pressing and releasing a key.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @param keySequence Either be a string("\" followed by the numeric keycode
+   *  of the key to be pressed, normally the ASCII value of that key), or a single
+   *  character. For example: "w", "\119".
+   */
+    var element = this.browserbot.findElement(locator);
+    triggerKeyEvent(element, 'keypress', keySequence, true, 
+        this.browserbot.controlKeyDown, 
+        this.browserbot.altKeyDown, 
+            this.browserbot.shiftKeyDown,
+            this.browserbot.metaKeyDown);
+};
+
+Selenium.prototype.doShiftKeyDown = function() {
+    /**
+   * Press the shift key and hold it down until doShiftUp() is called or a new page is loaded.
+   *
+   */
+   this.browserbot.shiftKeyDown = true;
+};
+
+Selenium.prototype.doShiftKeyUp = function() {
+    /**
+   * Release the shift key.
+   *
+   */
+   this.browserbot.shiftKeyDown = false;
+};
+
+Selenium.prototype.doMetaKeyDown = function() {
+    /**
+   * Press the meta key and hold it down until doMetaUp() is called or a new page is loaded.
+   *
+   */
+   this.browserbot.metaKeyDown = true;
+};
+
+Selenium.prototype.doMetaKeyUp = function() {
+    /**
+   * Release the meta key.
+   *
+   */
+   this.browserbot.metaKeyDown = false;
+};
+
+Selenium.prototype.doAltKeyDown = function() {
+    /**
+   * Press the alt key and hold it down until doAltUp() is called or a new page is loaded.
+   *
+   */
+   this.browserbot.altKeyDown = true;
+};
+
+Selenium.prototype.doAltKeyUp = function() {
+    /**
+   * Release the alt key.
+   *
+   */
+   this.browserbot.altKeyDown = false;
+};
+
+Selenium.prototype.doControlKeyDown = function() {
+    /**
+   * Press the control key and hold it down until doControlUp() is called or a new page is loaded.
+   *
+   */
+   this.browserbot.controlKeyDown = true;
+};
+
+Selenium.prototype.doControlKeyUp = function() {
+    /**
+   * Release the control key.
+   *
+   */
+   this.browserbot.controlKeyDown = false;
+};
+
+Selenium.prototype.doKeyDown = function(locator, keySequence) {
+    /**
+   * Simulates a user pressing a key (without releasing it yet).
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @param keySequence Either be a string("\" followed by the numeric keycode
+   *  of the key to be pressed, normally the ASCII value of that key), or a single
+   *  character. For example: "w", "\119".
+   */
+    var element = this.browserbot.findElement(locator);
+    triggerKeyEvent(element, 'keydown', keySequence, true,
+        this.browserbot.controlKeyDown, 
+            this.browserbot.altKeyDown, 
+            this.browserbot.shiftKeyDown, 
+            this.browserbot.metaKeyDown);
+};
+
+Selenium.prototype.doKeyUp = function(locator, keySequence) {
+    /**
+   * Simulates a user releasing a key.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @param keySequence Either be a string("\" followed by the numeric keycode
+   *  of the key to be pressed, normally the ASCII value of that key), or a single
+   *  character. For example: "w", "\119".
+   */
+    var element = this.browserbot.findElement(locator);
+    triggerKeyEvent(element, 'keyup', keySequence, true,
+        this.browserbot.controlKeyDown, 
+            this.browserbot.altKeyDown, 
+        this.browserbot.shiftKeyDown,
+        this.browserbot.metaKeyDown);
+};
+
+function getClientXY(element, coordString) {
+   // Parse coordString
+   var coords = null;
+   var x;
+   var y;
+   if (coordString) {
+      coords = coordString.split(/,/);
+      x = Number(coords[0]);
+      y = Number(coords[1]);
+   }
+   else {
+      x = y = 0;
+   }
+
+   // Get position of element,
+   // Return 2 item array with clientX and clientY
+   return [Selenium.prototype.getElementPositionLeft(element) + x, Selenium.prototype.getElementPositionTop(element) + y];
+}
+
+Selenium.prototype.doMouseOver = function(locator) {
+    /**
+   * Simulates a user hovering a mouse over the specified element.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   */
+    var element = this.browserbot.findElement(locator);
+    this.browserbot.triggerMouseEvent(element, 'mouseover', true);
+};
+
+Selenium.prototype.doMouseOut = function(locator) {
+   /**
+   * Simulates a user moving the mouse pointer away from the specified element.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   */
+    var element = this.browserbot.findElement(locator);
+    this.browserbot.triggerMouseEvent(element, 'mouseout', true);
+};
+
+Selenium.prototype.doMouseDown = function(locator) {
+    /**
+   * Simulates a user pressing the mouse button (without releasing it yet) on
+   * the specified element.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   */
+   var element = this.browserbot.findElement(locator);
+   this.browserbot.triggerMouseEvent(element, 'mousedown', true);
+};
+
+Selenium.prototype.doMouseDownAt = function(locator, coordString) {
+    /**
+   * Simulates a user pressing the mouse button (without releasing it yet) at
+   * the specified location.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
+   *      event relative to the element returned by the locator.
+   */
+    var element = this.browserbot.findElement(locator);
+    var clientXY = getClientXY(element, coordString)
+
+    this.browserbot.triggerMouseEvent(element, 'mousedown', true, clientXY[0], clientXY[1]);
+};
+
+Selenium.prototype.doMouseUp = function(locator) {
+    /**
+   * Simulates the event that occurs when the user releases the mouse button (i.e., stops
+   * holding the button down) on the specified element.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   */
+   var element = this.browserbot.findElement(locator);
+   this.browserbot.triggerMouseEvent(element, 'mouseup', true);
+};
+
+Selenium.prototype.doMouseUpAt = function(locator, coordString) {
+    /**
+   * Simulates the event that occurs when the user releases the mouse button (i.e., stops
+   * holding the button down) at the specified location.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
+   *      event relative to the element returned by the locator.
+   */
+    var element = this.browserbot.findElement(locator);
+    var clientXY = getClientXY(element, coordString)
+
+    this.browserbot.triggerMouseEvent(element, 'mouseup', true, clientXY[0], clientXY[1]);
+};
+
+Selenium.prototype.doMouseMove = function(locator) {
+    /**
+   * Simulates a user pressing the mouse button (without releasing it yet) on
+   * the specified element.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   */
+   var element = this.browserbot.findElement(locator);
+   this.browserbot.triggerMouseEvent(element, 'mousemove', true);
+};
+
+Selenium.prototype.doMouseMoveAt = function(locator, coordString) {
+    /**
+   * Simulates a user pressing the mouse button (without releasing it yet) on
+   * the specified element.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
+   *      event relative to the element returned by the locator.
+   */
+
+    var element = this.browserbot.findElement(locator);
+    var clientXY = getClientXY(element, coordString)
+
+    this.browserbot.triggerMouseEvent(element, 'mousemove', true, clientXY[0], clientXY[1]);
+};
+
+Selenium.prototype.doType = function(locator, value) {
+    /**
+   * Sets the value of an input field, as though you typed it in.
+   *
+   * <p>Can also be used to set the value of combo boxes, check boxes, etc. In these cases,
+   * value should be the value of the option selected, not the visible text.</p>
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @param value the value to type
+   */
+   if (this.browserbot.controlKeyDown || this.browserbot.altKeyDown || this.browserbot.metaKeyDown) {
+        throw new SeleniumError("type not supported immediately after call to controlKeyDown() or altKeyDown() or metaKeyDown()");
+    }
+        // TODO fail if it can't be typed into.
+    var element = this.browserbot.findElement(locator);
+    if (this.browserbot.shiftKeyDown) {
+        value = new String(value).toUpperCase();
+    }
+    this.browserbot.replaceText(element, value);
+};
+
+Selenium.prototype.doTypeKeys = function(locator, value) {
+    /**
+    * Simulates keystroke events on the specified element, as though you typed the value key-by-key.
+    *
+    * <p>This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string;
+    * this is useful for dynamic UI widgets (like auto-completing combo boxes) that require explicit key events.</p>
+    * 
+    * <p>Unlike the simple "type" command, which forces the specified value into the page directly, this command
+    * may or may not have any visible effect, even in cases where typing keys would normally have a visible effect.
+    * For example, if you use "typeKeys" on a form element, you may or may not see the results of what you typed in
+    * the field.</p>
+    * <p>In some cases, you may need to use the simple "type" command to set the value of the field and then the "typeKeys" command to
+    * send the keystroke events corresponding to what you just typed.</p>
+    *
+    * @param locator an <a href="#locators">element locator</a>
+    * @param value the value to type
+    */
+    var keys = new String(value).split("");
+    for (var i = 0; i < keys.length; i++) {
+        var c = keys[i];
+        this.doKeyDown(locator, c);
+        this.doKeyUp(locator, c);
+        this.doKeyPress(locator, c);
+    }
+};
+
+Selenium.prototype.doSetSpeed = function(value) {
+ /**
+ * Set execution speed (i.e., set the millisecond length of a delay which will follow each selenium operation).  By default, there is no such delay, i.e.,
+ * the delay is 0 milliseconds.
+   *
+   * @param value the number of milliseconds to pause after operation
+   */
+   throw new SeleniumError("this operation is only implemented in selenium-rc, and should never result in a request making it across the wire");
+};
+
+Selenium.prototype.doGetSpeed = function() {
+ /**
+ * Get execution speed (i.e., get the millisecond length of the delay following each selenium operation).  By default, there is no such delay, i.e.,
+ * the delay is 0 milliseconds.
+   *
+   * See also setSpeed.
+   */
+   throw new SeleniumError("this operation is only implemented in selenium-rc, and should never result in a request making it across the wire");
+};
+
+Selenium.prototype.findToggleButton = function(locator) {
+    var element = this.browserbot.findElement(locator);
+    if (element.checked == null) {
+        Assert.fail("Element " + locator + " is not a toggle-button.");
+    }
+    return element;
+}
+
+Selenium.prototype.doCheck = function(locator) {
+    /**
+   * Check a toggle-button (checkbox/radio)
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   */
+    this.findToggleButton(locator).checked = true;
+};
+
+Selenium.prototype.doUncheck = function(locator) {
+    /**
+   * Uncheck a toggle-button (checkbox/radio)
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   */
+    this.findToggleButton(locator).checked = false;
+};
+
+Selenium.prototype.doSelect = function(selectLocator, optionLocator) {
+    /**
+   * Select an option from a drop-down using an option locator.
+   *
+   * <p>
+   * Option locators provide different ways of specifying options of an HTML
+   * Select element (e.g. for selecting a specific option, or for asserting
+   * that the selected option satisfies a specification). There are several
+   * forms of Select Option Locator.
+   * </p>
+   * <ul>
+   * <li><strong>label</strong>=<em>labelPattern</em>:
+   * matches options based on their labels, i.e. the visible text. (This
+   * is the default.)
+   * <ul class="first last simple">
+   * <li>label=regexp:^[Oo]ther</li>
+   * </ul>
+   * </li>
+   * <li><strong>value</strong>=<em>valuePattern</em>:
+   * matches options based on their values.
+   * <ul class="first last simple">
+   * <li>value=other</li>
+   * </ul>
+   *
+   *
+   * </li>
+   * <li><strong>id</strong>=<em>id</em>:
+   *
+   * matches options based on their ids.
+   * <ul class="first last simple">
+   * <li>id=option1</li>
+   * </ul>
+   * </li>
+   * <li><strong>index</strong>=<em>index</em>:
+   * matches an option based on its index (offset from zero).
+   * <ul class="first last simple">
+   *
+   * <li>index=2</li>
+   * </ul>
+   * </li>
+   * </ul>
+   * <p>
+   * If no option locator prefix is provided, the default behaviour is to match on <strong>label</strong>.
+   * </p>
+   *
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @param optionLocator an option locator (a label by default)
+   */
+    var element = this.browserbot.findElement(selectLocator);
+    if (!("options" in element)) {
+        throw new SeleniumError("Specified element is not a Select (has no options)");
+    }
+    var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
+    var option = locator.findOption(element);
+    this.browserbot.selectOption(element, option);
+};
+
+
+
+Selenium.prototype.doAddSelection = function(locator, optionLocator) {
+    /**
+   * Add a selection to the set of selected options in a multi-select element using an option locator.
+   *
+   * @see #doSelect for details of option locators
+   *
+   * @param locator an <a href="#locators">element locator</a> identifying a multi-select box
+   * @param optionLocator an option locator (a label by default)
+   */
+    var element = this.browserbot.findElement(locator);
+    if (!("options" in element)) {
+        throw new SeleniumError("Specified element is not a Select (has no options)");
+    }
+    var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
+    var option = locator.findOption(element);
+    this.browserbot.addSelection(element, option);
+};
+
+Selenium.prototype.doRemoveSelection = function(locator, optionLocator) {
+    /**
+   * Remove a selection from the set of selected options in a multi-select element using an option locator.
+   *
+   * @see #doSelect for details of option locators
+   *
+   * @param locator an <a href="#locators">element locator</a> identifying a multi-select box
+   * @param optionLocator an option locator (a label by default)
+   */
+
+    var element = this.browserbot.findElement(locator);
+    if (!("options" in element)) {
+        throw new SeleniumError("Specified element is not a Select (has no options)");
+    }
+    var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
+    var option = locator.findOption(element);
+    this.browserbot.removeSelection(element, option);
+};
+
+Selenium.prototype.doRemoveAllSelections = function(locator) {
+    /**
+    * Unselects all of the selected options in a multi-select element.
+    *
+    * @param locator an <a href="#locators">element locator</a> identifying a multi-select box
+    */
+    var element = this.browserbot.findElement(locator);
+    if (!("options" in element)) {
+        throw new SeleniumError("Specified element is not a Select (has no options)");
+    }
+    for (var i = 0; i < element.options.length; i++) {
+        this.browserbot.removeSelection(element, element.options[i]);
+    }
+}
+
+Selenium.prototype.doSubmit = function(formLocator) {
+    /**
+   * Submit the specified form. This is particularly useful for forms without
+   * submit buttons, e.g. single-input "Search" forms.
+   *
+   * @param formLocator an <a href="#locators">element locator</a> for the form you want to submit
+   */
+    var form = this.browserbot.findElement(formLocator);
+    return this.browserbot.submit(form);
+
+};
+
+Selenium.prototype.makePageLoadCondition = function(timeout) {
+    if (timeout == null) {
+        timeout = this.defaultTimeout;
+    }
+    return Selenium.decorateFunctionWithTimeout(fnBind(this._isNewPageLoaded, this), timeout);
+};
+
+Selenium.prototype.doOpen = function(url) {
+    /**
+   * Opens an URL in the test frame. This accepts both relative and absolute
+   * URLs.
+   *
+   * The &quot;open&quot; command waits for the page to load before proceeding,
+   * ie. the &quot;AndWait&quot; suffix is implicit.
+   *
+   * <em>Note</em>: The URL must be on the same domain as the runner HTML
+   * due to security restrictions in the browser (Same Origin Policy). If you
+   * need to open an URL on another domain, use the Selenium Server to start a
+   * new browser session on that domain.
+   *
+   * @param url the URL to open; may be relative or absolute
+   */
+    this.browserbot.openLocation(url);
+    if (window["proxyInjectionMode"] == null || !window["proxyInjectionMode"]) {
+        return this.makePageLoadCondition();
+    } // in PI mode, just return "OK"; the server will waitForLoad
+};
+
+Selenium.prototype.doOpenWindow = function(url, windowID) {
+    /**
+   * Opens a popup window (if a window with that ID isn't already open).
+   * After opening the window, you'll need to select it using the selectWindow
+   * command.
+   * 
+   * <p>This command can also be a useful workaround for bug SEL-339.  In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+   * In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+   * an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p>
+   *
+   * @param url the URL to open, which can be blank 
+   * @param windowID the JavaScript window ID of the window to select
+   */
+   this.browserbot.openWindow(url, windowID);
+};
+
+Selenium.prototype.doSelectWindow = function(windowID) {
+    /**
+   * Selects a popup window; once a popup window has been selected, all
+   * commands go to that window. To select the main window again, use null
+   * as the target.
+   *
+   * <p>Note that there is a big difference between a window's internal JavaScript "name" property
+   * and the "title" of a given window's document (which is normally what you actually see, as an end user,
+   * in the title bar of the window).  The "name" is normally invisible to the end-user; it's the second 
+   * parameter "windowName" passed to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag)
+   * (which selenium intercepts).</p>
+   *
+   * <p>Selenium has several strategies for finding the window object referred to by the "windowID" parameter.</p>
+   * 
+   * <p>1.) if windowID is null, (or the string "null") then it is assumed the user is referring to the original window instantiated by the browser).</p>
+   * <p>2.) if the value of the "windowID" parameter is a JavaScript variable name in the current application window, then it is assumed
+   * that this variable contains the return value from a call to the JavaScript window.open() method.</p>
+   * <p>3.) Otherwise, selenium looks in a hash it maintains that maps string names to window "names".</p>
+   * <p>4.) If <i>that</i> fails, we'll try looping over all of the known windows to try to find the appropriate "title".
+   * Since "title" is not necessarily unique, this may have unexpected behavior.</p>
+   *
+   * <p>If you're having trouble figuring out what is the name of a window that you want to manipulate, look at the selenium log messages
+   * which identify the names of windows created via window.open (and therefore intercepted by selenium).  You will see messages
+   * like the following for each window as it is opened:</p>
+   * 
+   * <p><code>debug: window.open call intercepted; window ID (which you can use with selectWindow()) is "myNewWindow"</code></p>
+   *
+   * <p>In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+   * (This is bug SEL-339.)  In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+   * an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p>
+   * 
+   * @param windowID the JavaScript window ID of the window to select
+   */
+    this.browserbot.selectWindow(windowID);
+};
+
+Selenium.prototype.doSelectFrame = function(locator) {
+    /**
+    * Selects a frame within the current window.  (You may invoke this command
+    * multiple times to select nested frames.)  To select the parent frame, use
+    * "relative=parent" as a locator; to select the top frame, use "relative=top".
+    * You can also select a frame by its 0-based index number; select the first frame with
+    * "index=0", or the third frame with "index=2".
+    *
+    * <p>You may also use a DOM expression to identify the frame you want directly,
+    * like this: <code>dom=frames["main"].frames["subframe"]</code></p>
+    *
+    * @param locator an <a href="#locators">element locator</a> identifying a frame or iframe
+    */
+        this.browserbot.selectFrame(locator);
+};
+
+Selenium.prototype.getWhetherThisFrameMatchFrameExpression = function(currentFrameString, target) {
+    /**
+     * Determine whether current/locator identify the frame containing this running code.
+     *
+     * <p>This is useful in proxy injection mode, where this code runs in every
+     * browser frame and window, and sometimes the selenium server needs to identify
+     * the "current" frame.  In this case, when the test calls selectFrame, this
+     * routine is called for each frame to figure out which one has been selected.
+     * The selected frame will return true, while all others will return false.</p>
+     *
+     * @param currentFrameString starting frame
+     * @param target new frame (which might be relative to the current one)
+     * @return boolean true if the new frame is this code's window
+     */
+    return this.browserbot.doesThisFrameMatchFrameExpression(currentFrameString, target);
+};
+
+Selenium.prototype.getWhetherThisWindowMatchWindowExpression = function(currentWindowString, target) {
+    /**
+    * Determine whether currentWindowString plus target identify the window containing this running code.
+     *
+     * <p>This is useful in proxy injection mode, where this code runs in every
+     * browser frame and window, and sometimes the selenium server needs to identify
+     * the "current" window.  In this case, when the test calls selectWindow, this
+     * routine is called for each window to figure out which one has been selected.
+     * The selected window will return true, while all others will return false.</p>
+     *
+     * @param currentWindowString starting window
+     * @param target new window (which might be relative to the current one, e.g., "_parent")
+     * @return boolean true if the new window is this code's window
+     */
+     if (window.opener!=null && window.opener[target]!=null && window.opener[target]==window) {
+         return true;
+     }
+     return false;
+};
+
+Selenium.prototype.doWaitForPopUp = function(windowID, timeout) {
+    /**
+    * Waits for a popup window to appear and load up.
+    *
+    * @param windowID the JavaScript window ID of the window that will appear
+    * @param timeout a timeout in milliseconds, after which the action will return with an error
+    */
+    var popupLoadedPredicate = function () {
+        var targetWindow = selenium.browserbot.getWindowByName(windowID, true);
+        if (!targetWindow) return false;
+        if (!targetWindow.location) return false;
+        if ("about:blank" == targetWindow.location) return false;
+        if (browserVersion.isKonqueror) {
+            if ("/" == targetWindow.location.href) {
+                // apparently Konqueror uses this as the temporary location, instead of about:blank
+                return false;
+            }
+        }
+        if (browserVersion.isSafari) {
+            if(targetWindow.location.href == selenium.browserbot.buttonWindow.location.href) {
+                // Apparently Safari uses this as the temporary location, instead of about:blank
+                // what a world!
+                LOG.debug("DGF what a world!");
+                return false;
+            }
+        }
+        if (!targetWindow.document) return false;
+        if (!selenium.browserbot.getCurrentWindow().document.readyState) {
+            // This is Firefox, with no readyState extension
+            return true;
+        }
+        if ('complete' != targetWindow.document.readyState) return false;
+        return true;
+    };
+
+    return Selenium.decorateFunctionWithTimeout(popupLoadedPredicate, timeout);
+}
+
+Selenium.prototype.doWaitForPopUp.dontCheckAlertsAndConfirms = true;
+
+Selenium.prototype.doChooseCancelOnNextConfirmation = function() {
+    /**
+   * By default, Selenium's overridden window.confirm() function will
+   * return true, as if the user had manually clicked OK; after running
+   * this command, the next call to confirm() will return false, as if
+   * the user had clicked Cancel.  Selenium will then resume using the
+   * default behavior for future confirmations, automatically returning 
+   * true (OK) unless/until you explicitly call this command for each
+   * confirmation.
+   *
+   */
+    this.browserbot.cancelNextConfirmation(false);
+};
+
+Selenium.prototype.doChooseOkOnNextConfirmation = function() {
+    /**
+   * Undo the effect of calling chooseCancelOnNextConfirmation.  Note
+   * that Selenium's overridden window.confirm() function will normally automatically
+   * return true, as if the user had manually clicked OK, so you shouldn't
+   * need to use this command unless for some reason you need to change
+   * your mind prior to the next confirmation.  After any confirmation, Selenium will resume using the
+   * default behavior for future confirmations, automatically returning 
+   * true (OK) unless/until you explicitly call chooseCancelOnNextConfirmation for each
+   * confirmation.
+   *
+   */
+    this.browserbot.cancelNextConfirmation(true);
+};
+
+Selenium.prototype.doAnswerOnNextPrompt = function(answer) {
+    /**
+   * Instructs Selenium to return the specified answer string in response to
+   * the next JavaScript prompt [window.prompt()].
+   *
+   *
+   * @param answer the answer to give in response to the prompt pop-up
+   */
+    this.browserbot.setNextPromptResult(answer);
+};
+
+Selenium.prototype.doGoBack = function() {
+    /**
+     * Simulates the user clicking the "back" button on their browser.
+     *
+     */
+    this.browserbot.goBack();
+};
+
+Selenium.prototype.doRefresh = function() {
+    /**
+     * Simulates the user clicking the "Refresh" button on their browser.
+     *
+     */
+    this.browserbot.refresh();
+};
+
+Selenium.prototype.doClose = function() {
+    /**
+     * Simulates the user clicking the "close" button in the titlebar of a popup
+     * window or tab.
+     */
+    this.browserbot.close();
+};
+
+Selenium.prototype.ensureNoUnhandledPopups = function() {
+    if (this.browserbot.hasAlerts()) {
+        throw new SeleniumError("There was an unexpected Alert! [" + this.browserbot.getNextAlert() + "]");
+    }
+    if ( this.browserbot.hasConfirmations() ) {
+        throw new SeleniumError("There was an unexpected Confirmation! [" + this.browserbot.getNextConfirmation() + "]");
+    }
+};
+
+Selenium.prototype.isAlertPresent = function() {
+   /**
+   * Has an alert occurred?
+   *
+   * <p>
+   * This function never throws an exception
+   * </p>
+   * @return boolean true if there is an alert
+   */
+    return this.browserbot.hasAlerts();
+};
+
+Selenium.prototype.isPromptPresent = function() {
+   /**
+   * Has a prompt occurred?
+   *
+   * <p>
+   * This function never throws an exception
+   * </p>
+   * @return boolean true if there is a pending prompt
+   */
+    return this.browserbot.hasPrompts();
+};
+
+Selenium.prototype.isConfirmationPresent = function() {
+   /**
+   * Has confirm() been called?
+   *
+   * <p>
+   * This function never throws an exception
+   * </p>
+   * @return boolean true if there is a pending confirmation
+   */
+    return this.browserbot.hasConfirmations();
+};
+Selenium.prototype.getAlert = function() {
+    /**
+   * Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts.
+   *
+   * <p>Getting an alert has the same effect as manually clicking OK. If an
+   * alert is generated but you do not get/verify it, the next Selenium action
+   * will fail.</p>
+   *
+   * <p>NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert
+   * dialog.</p>
+   *
+   * <p>NOTE: Selenium does NOT support JavaScript alerts that are generated in a
+   * page's onload() event handler. In this case a visible dialog WILL be
+   * generated and Selenium will hang until someone manually clicks OK.</p>
+   * @return string The message of the most recent JavaScript alert
+   */
+    if (!this.browserbot.hasAlerts()) {
+        Assert.fail("There were no alerts");
+    }
+    return this.browserbot.getNextAlert();
+};
+Selenium.prototype.getAlert.dontCheckAlertsAndConfirms = true;
+
+Selenium.prototype.getConfirmation = function() {
+    /**
+   * Retrieves the message of a JavaScript confirmation dialog generated during
+   * the previous action.
+   *
+   * <p>
+   * By default, the confirm function will return true, having the same effect
+   * as manually clicking OK. This can be changed by prior execution of the
+   * chooseCancelOnNextConfirmation command. If an confirmation is generated
+   * but you do not get/verify it, the next Selenium action will fail.
+   * </p>
+   *
+   * <p>
+   * NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible
+   * dialog.
+   * </p>
+   *
+   * <p>
+   * NOTE: Selenium does NOT support JavaScript confirmations that are
+   * generated in a page's onload() event handler. In this case a visible
+   * dialog WILL be generated and Selenium will hang until you manually click
+   * OK.
+   * </p>
+   *
+   * @return string the message of the most recent JavaScript confirmation dialog
+   */
+    if (!this.browserbot.hasConfirmations()) {
+        Assert.fail("There were no confirmations");
+    }
+    return this.browserbot.getNextConfirmation();
+};
+Selenium.prototype.getConfirmation.dontCheckAlertsAndConfirms = true;
+
+Selenium.prototype.getPrompt = function() {
+    /**
+   * Retrieves the message of a JavaScript question prompt dialog generated during
+   * the previous action.
+   *
+   * <p>Successful handling of the prompt requires prior execution of the
+   * answerOnNextPrompt command. If a prompt is generated but you
+   * do not get/verify it, the next Selenium action will fail.</p>
+   *
+   * <p>NOTE: under Selenium, JavaScript prompts will NOT pop up a visible
+   * dialog.</p>
+   *
+   * <p>NOTE: Selenium does NOT support JavaScript prompts that are generated in a
+   * page's onload() event handler. In this case a visible dialog WILL be
+   * generated and Selenium will hang until someone manually clicks OK.</p>
+   * @return string the message of the most recent JavaScript question prompt
+   */
+    if (! this.browserbot.hasPrompts()) {
+        Assert.fail("There were no prompts");
+    }
+    return this.browserbot.getNextPrompt();
+};
+
+Selenium.prototype.getLocation = function() {
+    /** Gets the absolute URL of the current page.
+   *
+   * @return string the absolute URL of the current page
+   */
+    return this.browserbot.getCurrentWindow().location;
+};
+
+Selenium.prototype.getTitle = function() {
+    /** Gets the title of the current page.
+   *
+   * @return string the title of the current page
+   */
+    return this.browserbot.getTitle();
+};
+
+
+Selenium.prototype.getBodyText = function() {
+    /**
+     * Gets the entire text of the page.
+     * @return string the entire text of the page
+     */
+    return this.browserbot.bodyText();
+};
+
+
+Selenium.prototype.getValue = function(locator) {
+  /**
+   * Gets the (whitespace-trimmed) value of an input field (or anything else with a value parameter).
+   * For checkbox/radio elements, the value will be "on" or "off" depending on
+   * whether the element is checked or not.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @return string the element value, or "on/off" for checkbox/radio elements
+   */
+    var element = this.browserbot.findElement(locator)
+    return getInputValue(element).trim();
+}
+
+Selenium.prototype.getText = function(locator) {
+    /**
+   * Gets the text of an element. This works for any element that contains
+   * text. This command uses either the textContent (Mozilla-like browsers) or
+   * the innerText (IE-like browsers) of the element, which is the rendered
+   * text shown to the user.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @return string the text of the element
+   */
+    var element = this.browserbot.findElement(locator);
+    return getText(element).trim();
+};
+
+Selenium.prototype.doHighlight = function(locator) {
+    /**
+    * Briefly changes the backgroundColor of the specified element yellow.  Useful for debugging.
+    * 
+    * @param locator an <a href="#locators">element locator</a>
+    */
+    var element = this.browserbot.findElement(locator);
+    this.browserbot.highlight(element, true);
+};
+
+Selenium.prototype.getEval = function(script) {
+    /** Gets the result of evaluating the specified JavaScript snippet.  The snippet may
+   * have multiple lines, but only the result of the last line will be returned.
+   *
+   * <p>Note that, by default, the snippet will run in the context of the "selenium"
+   * object itself, so <code>this</code> will refer to the Selenium object.  Use <code>window</code> to
+   * refer to the window of your application, e.g. <code>window.document.getElementById('foo')</code></p>
+   *
+   * <p>If you need to use
+   * a locator to refer to a single element in your application page, you can
+   * use <code>this.browserbot.findElement("id=foo")</code> where "id=foo" is your locator.</p>
+   *
+   * @param script the JavaScript snippet to run
+   * @return string the results of evaluating the snippet
+   */
+    try {
+        var window = this.browserbot.getCurrentWindow();
+        var result = eval(script);
+        // Selenium RC doesn't allow returning null
+        if (null == result) return "null";
+        return result;
+    } catch (e) {
+        throw new SeleniumError("Threw an exception: " + e.message);
+    }
+};
+
+Selenium.prototype.isChecked = function(locator) {
+    /**
+   * Gets whether a toggle-button (checkbox/radio) is checked.  Fails if the specified element doesn't exist or isn't a toggle-button.
+   * @param locator an <a href="#locators">element locator</a> pointing to a checkbox or radio button
+   * @return boolean true if the checkbox is checked, false otherwise
+   */
+    var element = this.browserbot.findElement(locator);
+    if (element.checked == null) {
+        throw new SeleniumError("Element " + locator + " is not a toggle-button.");
+    }
+    return element.checked;
+};
+
+Selenium.prototype.getTable = function(tableCellAddress) {
+    /**
+   * Gets the text from a cell of a table. The cellAddress syntax
+   * tableLocator.row.column, where row and column start at 0.
+   *
+   * @param tableCellAddress a cell address, e.g. "foo.1.4"
+   * @return string the text from the specified cell
+   */
+    // This regular expression matches "tableName.row.column"
+    // For example, "mytable.3.4"
+    pattern = /(.*)\.(\d+)\.(\d+)/;
+
+    if(!pattern.test(tableCellAddress)) {
+        throw new SeleniumError("Invalid target format. Correct format is tableName.rowNum.columnNum");
+    }
+
+    pieces = tableCellAddress.match(pattern);
+
+    tableName = pieces[1];
+    row = pieces[2];
+    col = pieces[3];
+
+    var table = this.browserbot.findElement(tableName);
+    if (row > table.rows.length) {
+        Assert.fail("Cannot access row " + row + " - table has " + table.rows.length + " rows");
+    }
+    else if (col > table.rows[row].cells.length) {
+        Assert.fail("Cannot access column " + col + " - table row has " + table.rows[row].cells.length + " columns");
+    }
+    else {
+        actualContent = getText(table.rows[row].cells[col]);
+        return actualContent.trim();
+    }
+    return null;
+};
+
+Selenium.prototype.getSelectedLabels = function(selectLocator) {
+    /** Gets all option labels (visible text) for selected options in the specified select or multi-select element.
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @return string[] an array of all selected option labels in the specified select drop-down
+   */
+    return this.findSelectedOptionProperties(selectLocator, "text");
+}
+
+Selenium.prototype.getSelectedLabel = function(selectLocator) {
+    /** Gets option label (visible text) for selected option in the specified select element.
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @return string the selected option label in the specified select drop-down
+   */
+    return this.findSelectedOptionProperty(selectLocator, "text");
+}
+
+Selenium.prototype.getSelectedValues = function(selectLocator) {
+    /** Gets all option values (value attributes) for selected options in the specified select or multi-select element.
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @return string[] an array of all selected option values in the specified select drop-down
+   */
+    return this.findSelectedOptionProperties(selectLocator, "value");
+}
+
+Selenium.prototype.getSelectedValue = function(selectLocator) {
+    /** Gets option value (value attribute) for selected option in the specified select element.
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @return string the selected option value in the specified select drop-down
+   */
+    return this.findSelectedOptionProperty(selectLocator, "value");
+}
+
+Selenium.prototype.getSelectedIndexes = function(selectLocator) {
+    /** Gets all option indexes (option number, starting at 0) for selected options in the specified select or multi-select element.
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @return string[] an array of all selected option indexes in the specified select drop-down
+   */
+    return this.findSelectedOptionProperties(selectLocator, "index");
+}
+
+Selenium.prototype.getSelectedIndex = function(selectLocator) {
+    /** Gets option index (option number, starting at 0) for selected option in the specified select element.
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @return string the selected option index in the specified select drop-down
+   */
+    return this.findSelectedOptionProperty(selectLocator, "index");
+}
+
+Selenium.prototype.getSelectedIds = function(selectLocator) {
+    /** Gets all option element IDs for selected options in the specified select or multi-select element.
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @return string[] an array of all selected option IDs in the specified select drop-down
+   */
+    return this.findSelectedOptionProperties(selectLocator, "id");
+}
+
+Selenium.prototype.getSelectedId = function(selectLocator) {
+    /** Gets option element ID for selected option in the specified select element.
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @return string the selected option ID in the specified select drop-down
+   */
+    return this.findSelectedOptionProperty(selectLocator, "id");
+}
+
+Selenium.prototype.isSomethingSelected = function(selectLocator) {
+    /** Determines whether some option in a drop-down menu is selected.
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @return boolean true if some option has been selected, false otherwise
+   */
+    var element = this.browserbot.findElement(selectLocator);
+    if (!("options" in element)) {
+        throw new SeleniumError("Specified element is not a Select (has no options)");
+    }
+
+    var selectedOptions = [];
+
+    for (var i = 0; i < element.options.length; i++) {
+        if (element.options[i].selected)
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+Selenium.prototype.findSelectedOptionProperties = function(locator, property) {
+   var element = this.browserbot.findElement(locator);
+   if (!("options" in element)) {
+        throw new SeleniumError("Specified element is not a Select (has no options)");
+    }
+
+    var selectedOptions = [];
+
+    for (var i = 0; i < element.options.length; i++) {
+        if (element.options[i].selected)
+        {
+            var propVal = element.options[i][property];
+            selectedOptions.push(propVal);
+        }
+    }
+    if (selectedOptions.length == 0) Assert.fail("No option selected");
+    return selectedOptions;
+}
+
+Selenium.prototype.findSelectedOptionProperty = function(locator, property) {
+    var selectedOptions = this.findSelectedOptionProperties(locator, property);
+    if (selectedOptions.length > 1) {
+        Assert.fail("More than one selected option!");
+    }
+    return selectedOptions[0];
+}
+
+Selenium.prototype.getSelectOptions = function(selectLocator) {
+    /** Gets all option labels in the specified select drop-down.
+   *
+   * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+   * @return string[] an array of all option labels in the specified select drop-down
+   */
+    var element = this.browserbot.findElement(selectLocator);
+
+    var selectOptions = [];
+
+    for (var i = 0; i < element.options.length; i++) {
+        var option = element.options[i].text;
+        selectOptions.push(option);
+    }
+
+    return selectOptions;
+};
+
+
+Selenium.prototype.getAttribute = function(attributeLocator) {
+    /**
+   * Gets the value of an element attribute.
+   *
+   * @param attributeLocator an element locator followed by an &#064; sign and then the name of the attribute, e.g. "foo&#064;bar"
+   * @return string the value of the specified attribute
+   */
+   var result = this.browserbot.findAttribute(attributeLocator);
+   if (result == null) {
+           throw new SeleniumError("Could not find element attribute: " + attributeLocator);
+    }
+    return result;
+};
+
+Selenium.prototype.isTextPresent = function(pattern) {
+    /**
+   * Verifies that the specified text pattern appears somewhere on the rendered page shown to the user.
+   * @param pattern a <a href="#patterns">pattern</a> to match with the text of the page
+   * @return boolean true if the pattern matches the text, false otherwise
+   */
+    var allText = this.browserbot.bodyText();
+
+    var patternMatcher = new PatternMatcher(pattern);
+    if (patternMatcher.strategy == PatternMatcher.strategies.glob) {
+            if (pattern.indexOf("glob:")==0) {
+                    pattern = pattern.substring("glob:".length); // strip off "glob:"
+                }
+        patternMatcher.matcher = new PatternMatcher.strategies.globContains(pattern);
+    }
+    else if (patternMatcher.strategy == PatternMatcher.strategies.exact) {
+                pattern = pattern.substring("exact:".length); // strip off "exact:"
+        return allText.indexOf(pattern) != -1;
+    }
+    return patternMatcher.matches(allText);
+};
+
+Selenium.prototype.isElementPresent = function(locator) {
+    /**
+    * Verifies that the specified element is somewhere on the page.
+    * @param locator an <a href="#locators">element locator</a>
+    * @return boolean true if the element is present, false otherwise
+    */
+    var element = this.browserbot.findElementOrNull(locator);
+    if (element == null) {
+        return false;
+    }
+    return true;
+};
+
+Selenium.prototype.isVisible = function(locator) {
+    /**
+   * Determines if the specified element is visible. An
+   * element can be rendered invisible by setting the CSS "visibility"
+   * property to "hidden", or the "display" property to "none", either for the
+   * element itself or one if its ancestors.  This method will fail if
+   * the element is not present.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @return boolean true if the specified element is visible, false otherwise
+   */
+    var element;
+    element = this.browserbot.findElement(locator);
+    var visibility = this.findEffectiveStyleProperty(element, "visibility");
+    var _isDisplayed = this._isDisplayed(element);
+    return (visibility != "hidden" && _isDisplayed);
+};
+
+Selenium.prototype.findEffectiveStyleProperty = function(element, property) {
+    var effectiveStyle = this.findEffectiveStyle(element);
+    var propertyValue = effectiveStyle[property];
+    if (propertyValue == 'inherit' && element.parentNode.style) {
+        return this.findEffectiveStyleProperty(element.parentNode, property);
+    }
+    return propertyValue;
+};
+
+Selenium.prototype._isDisplayed = function(element) {
+    var display = this.findEffectiveStyleProperty(element, "display");
+    if (display == "none") return false;
+    if (element.parentNode.style) {
+        return this._isDisplayed(element.parentNode);
+    }
+    return true;
+};
+
+Selenium.prototype.findEffectiveStyle = function(element) {
+    if (element.style == undefined) {
+        return undefined; // not a styled element
+    }
+    if (window.getComputedStyle) {
+        // DOM-Level-2-CSS
+        return this.browserbot.getCurrentWindow().getComputedStyle(element, null);
+    }
+    if (element.currentStyle) {
+        // non-standard IE alternative
+        return element.currentStyle;
+        // TODO: this won't really work in a general sense, as
+        //   currentStyle is not identical to getComputedStyle()
+        //   ... but it's good enough for "visibility"
+    }
+    throw new SeleniumError("cannot determine effective stylesheet in this browser");
+};
+
+Selenium.prototype.isEditable = function(locator) {
+    /**
+   * Determines whether the specified input element is editable, ie hasn't been disabled.
+   * This method will fail if the specified element isn't an input element.
+   *
+   * @param locator an <a href="#locators">element locator</a>
+   * @return boolean true if the input element is editable, false otherwise
+   */
+    var element = this.browserbot.findElement(locator);
+    if (element.value == undefined) {
+        Assert.fail("Element " + locator + " is not an input.");
+    }
+    return !element.disabled;
+};
+
+Selenium.prototype.getAllButtons = function() {
+    /** Returns the IDs of all buttons on the page.
+   *
+   * <p>If a given button has no ID, it will appear as "" in this array.</p>
+   *
+   * @return string[] the IDs of all buttons on the page
+   */
+   return this.browserbot.getAllButtons();
+};
+
+Selenium.prototype.getAllLinks = function() {
+    /** Returns the IDs of all links on the page.
+   *
+   * <p>If a given link has no ID, it will appear as "" in this array.</p>
+   *
+   * @return string[] the IDs of all links on the page
+   */
+   return this.browserbot.getAllLinks();
+};
+
+Selenium.prototype.getAllFields = function() {
+    /** Returns the IDs of all input fields on the page.
+   *
+   * <p>If a given field has no ID, it will appear as "" in this array.</p>
+   *
+   * @return string[] the IDs of all field on the page
+   */
+   return this.browserbot.getAllFields();
+};
+
+Selenium.prototype.getAttributeFromAllWindows = function(attributeName) {
+    /** Returns every instance of some attribute from all known windows.
+    *
+    * @param attributeName name of an attribute on the windows
+    * @return string[] the set of values of this attribute from all known windows.
+    */
+    var attributes = new Array();
+    
+    var win = selenium.browserbot.topWindow;
+    
+    // DGF normally you should use []s instead of eval "win."+attributeName
+    // but in this case, attributeName may contain dots (e.g. document.title)
+    // in that case, we have no choice but to use eval...
+    attributes.push(eval("win."+attributeName));
+    for (var windowName in this.browserbot.openedWindows)
+    {
+        try {
+            win = selenium.browserbot.openedWindows[windowName];
+            attributes.push(eval("win."+attributeName));
+        } catch (e) {} // DGF If we miss one... meh. It's probably closed or inaccessible anyway.
+    }
+    return attributes;
+};
+
+Selenium.prototype.findWindow = function(soughtAfterWindowPropertyValue) {
+   var targetPropertyName = "name";
+   if (soughtAfterWindowPropertyValue.match("^title=")) {
+       targetPropertyName = "document.title";
+        soughtAfterWindowPropertyValue = soughtAfterWindowPropertyValue.replace(/^title=/, "");
+   }
+   else {
+       // matching "name":
+       // If we are not in proxy injection mode, then the top-level test window will be named selenium_myiframe.
+        // But as far as the interface goes, we are expected to match a blank string to this window, if
+        // we are searching with respect to the widow name.
+        // So make a special case so that this logic will work:
+        if (PatternMatcher.matches(soughtAfterWindowPropertyValue, "")) {
+           return this.browserbot.getCurrentWindow();
+        }
+   }
+
+   // DGF normally you should use []s instead of eval "win."+attributeName
+   // but in this case, attributeName may contain dots (e.g. document.title)
+   // in that case, we have no choice but to use eval...
+   if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval("this.browserbot.topWindow." + targetPropertyName))) {
+       return this.browserbot.topWindow;
+   }
+   for (windowName in selenium.browserbot.openedWindows) {
+       var openedWindow = selenium.browserbot.openedWindows[windowName];
+       if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval("openedWindow." + targetPropertyName))) {
+            return openedWindow;
+        }
+   }
+   throw new SeleniumError("could not find window with property " + targetPropertyName + " matching " + soughtAfterWindowPropertyValue);
+};
+
+Selenium.prototype.doDragdrop = function(locator, movementsString) {
+/** deprecated - use dragAndDrop instead
+   *
+   * @param locator an element locator
+   * @param movementsString offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"
+   */
+   this.doDragAndDrop(locator, movementsString);
+};
+
+Selenium.prototype.doSetMouseSpeed = function(pixels) {
+    /** Configure the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+    * <p>Setting this value to 0 means that we'll send a "mousemove" event to every single pixel
+    * in between the start location and the end location; that can be very slow, and may
+    * cause some browsers to force the JavaScript to timeout.</p>
+    * 
+    * <p>If the mouse speed is greater than the distance between the two dragged objects, we'll
+    * just send one "mousemove" at the start location and then one final one at the end location.</p>
+    * @param pixels the number of pixels between "mousemove" events
+    */
+    this.mouseSpeed = pixels;
+}
+ 
+Selenium.prototype.getMouseSpeed = function() {
+    /** Returns the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+    * 
+    * @return number the number of pixels between "mousemove" events during dragAndDrop commands (default=10)
+    */
+    this.mouseSpeed = pixels;
+}
+
+
+Selenium.prototype.doDragAndDrop = function(locator, movementsString) {
+    /** Drags an element a certain distance and then drops it
+    * @param locator an element locator
+    * @param movementsString offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"
+    */
+    var element = this.browserbot.findElement(locator);
+    var clientStartXY = getClientXY(element)
+    var clientStartX = clientStartXY[0];
+    var clientStartY = clientStartXY[1];
+    
+    var movements = movementsString.split(/,/);
+    var movementX = Number(movements[0]);
+    var movementY = Number(movements[1]);
+    
+    var clientFinishX = ((clientStartX + movementX) < 0) ? 0 : (clientStartX + movementX);
+    var clientFinishY = ((clientStartY + movementY) < 0) ? 0 : (clientStartY + movementY);
+    
+    var mouseSpeed = this.mouseSpeed;
+    var move = function(current, dest) {
+        if (current == dest) return current;
+        if (Math.abs(current - dest) < mouseSpeed) return dest;
+        return (current < dest) ? current + mouseSpeed : current - mouseSpeed;
+    }
+    
+    this.browserbot.triggerMouseEvent(element, 'mousedown', true, clientStartX, clientStartY);
+    this.browserbot.triggerMouseEvent(element, 'mousemove',   true, clientStartX, clientStartY);
+    var clientX = clientStartX;
+    var clientY = clientStartY;
+    
+    while ((clientX != clientFinishX) || (clientY != clientFinishY)) {
+        clientX = move(clientX, clientFinishX);
+        clientY = move(clientY, clientFinishY);
+        this.browserbot.triggerMouseEvent(element, 'mousemove', true, clientX, clientY);
+    }
+    
+    this.browserbot.triggerMouseEvent(element, 'mousemove',   true, clientFinishX, clientFinishY);
+    this.browserbot.triggerMouseEvent(element, 'mouseup',   true, clientFinishX, clientFinishY);
+};
+
+Selenium.prototype.doDragAndDropToObject = function(locatorOfObjectToBeDragged, locatorOfDragDestinationObject) {
+/** Drags an element and drops it on another element
+   *
+   * @param locatorOfObjectToBeDragged an element to be dragged
+   * @param locatorOfDragDestinationObject an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged  is dropped
+   */
+   var startX = this.getElementPositionLeft(locatorOfObjectToBeDragged);
+   var startY = this.getElementPositionTop(locatorOfObjectToBeDragged);
+   
+   var destinationLeftX = this.getElementPositionLeft(locatorOfDragDestinationObject);
+   var destinationTopY = this.getElementPositionTop(locatorOfDragDestinationObject);
+   var destinationWidth = this.getElementWidth(locatorOfDragDestinationObject);
+   var destinationHeight = this.getElementHeight(locatorOfDragDestinationObject);
+
+   var endX = Math.round(destinationLeftX + (destinationWidth / 2));
+   var endY = Math.round(destinationTopY + (destinationHeight / 2));
+   
+   var deltaX = endX - startX;
+   var deltaY = endY - startY;
+   
+   var movementsString = "" + deltaX + "," + deltaY;
+   
+   this.doDragAndDrop(locatorOfObjectToBeDragged, movementsString);
+};
+
+Selenium.prototype.doWindowFocus = function() {
+/** Gives focus to the currently selected window
+   *
+   */
+   this.browserbot.getCurrentWindow().focus();
+};
+
+
+Selenium.prototype.doWindowMaximize = function() {
+/** Resize currently selected window to take up the entire screen
+   *
+   */
+   var window = this.browserbot.getCurrentWindow();
+   if (window!=null && window.screen) {
+       window.moveTo(0,0);
+       window.resizeTo(screen.availWidth, screen.availHeight);
+   }
+};
+
+Selenium.prototype.getAllWindowIds = function() {
+  /** Returns the IDs of all windows that the browser knows about.
+   *
+   * @return string[] the IDs of all windows that the browser knows about.
+   */
+   return this.getAttributeFromAllWindows("id");
+};
+
+Selenium.prototype.getAllWindowNames = function() {
+  /** Returns the names of all windows that the browser knows about.
+   *
+   * @return string[] the names of all windows that the browser knows about.
+   */
+   return this.getAttributeFromAllWindows("name");
+};
+
+Selenium.prototype.getAllWindowTitles = function() {
+  /** Returns the titles of all windows that the browser knows about.
+   *
+   * @return string[] the titles of all windows that the browser knows about.
+   */
+   return this.getAttributeFromAllWindows("document.title");
+};
+
+Selenium.prototype.getHtmlSource = function() {
+    /** Returns the entire HTML source between the opening and
+   * closing "html" tags.
+   *
+   * @return string the entire HTML source
+   */
+    return this.browserbot.getDocument().getElementsByTagName("html")[0].innerHTML;
+};
+
+Selenium.prototype.doSetCursorPosition = function(locator, position) {
+    /**
+   * Moves the text cursor to the specified position in the given input element or textarea.
+   * This method will fail if the specified element isn't an input element or textarea.
+   *
+   * @param locator an <a href="#locators">element locator</a> pointing to an input element or textarea
+   * @param position the numerical position of the cursor in the field; position should be 0 to move the position to the beginning of the field.  You can also set the cursor to -1 to move it to the end of the field.
+   */
+   var element = this.browserbot.findElement(locator);
+    if (element.value == undefined) {
+        Assert.fail("Element " + locator + " is not an input.");
+    }
+    if (position == -1) {
+        position = element.value.length;
+    }
+
+   if( element.setSelectionRange && !browserVersion.isOpera) {
+       element.focus();
+        element.setSelectionRange(/*start*/position,/*end*/position);
+   }
+   else if( element.createTextRange ) {
+      triggerEvent(element, 'focus', false);
+      var range = element.createTextRange();
+      range.collapse(true);
+      range.moveEnd('character',position);
+      range.moveStart('character',position);
+      range.select();
+   }
+}
+
+Selenium.prototype.getElementIndex = function(locator) {
+    /**
+     * Get the relative index of an element to its parent (starting from 0). The comment node and empty text node
+     * will be ignored.
+     *
+     * @param locator an <a href="#locators">element locator</a> pointing to an element
+     * @return number of relative index of the element to its parent (starting from 0)
+     */
+    var element = this.browserbot.findElement(locator);
+    var previousSibling;
+    var index = 0;
+    while ((previousSibling = element.previousSibling) != null) {
+        if (!this._isCommentOrEmptyTextNode(previousSibling)) {
+            index++;
+        }
+        element = previousSibling;
+    }
+    return index;
+}
+
+Selenium.prototype.isOrdered = function(locator1, locator2) {
+    /**
+     * Check if these two elements have same parent and are ordered siblings in the DOM. Two same elements will
+     * not be considered ordered.
+     *
+     * @param locator1 an <a href="#locators">element locator</a> pointing to the first element
+     * @param locator2 an <a href="#locators">element locator</a> pointing to the second element
+     * @return boolean true if element1 is the previous sibling of element2, false otherwise
+     */
+    var element1 = this.browserbot.findElement(locator1);
+    var element2 = this.browserbot.findElement(locator2);
+    if (element1 === element2) return false;
+
+    var previousSibling;
+    while ((previousSibling = element2.previousSibling) != null) {
+        if (previousSibling === element1) {
+            return true;
+        }
+        element2 = previousSibling;
+    }
+    return false;
+}
+
+Selenium.prototype._isCommentOrEmptyTextNode = function(node) {
+    return node.nodeType == 8 || ((node.nodeType == 3) && !(/[^\t\n\r ]/.test(node.data)));
+}
+
+Selenium.prototype.getElementPositionLeft = function(locator) {
+   /**
+   * Retrieves the horizontal position of an element
+   *
+   * @param locator an <a href="#locators">element locator</a> pointing to an element OR an element itself
+   * @return number of pixels from the edge of the frame.
+   */
+       var element;
+        if ("string"==typeof locator) {
+            element = this.browserbot.findElement(locator);
+        }
+        else {
+            element = locator;
+        }
+    var x = element.offsetLeft;
+    var elementParent = element.offsetParent;
+
+    while (elementParent != null)
+    {
+        if(document.all)
+        {
+            if( (elementParent.tagName != "TABLE") && (elementParent.tagName != "BODY") )
+            {
+                x += elementParent.clientLeft;
+            }
+        }
+        else // Netscape/DOM
+        {
+            if(elementParent.tagName == "TABLE")
+            {
+                var parentBorder = parseInt(elementParent.border);
+                if(isNaN(parentBorder))
+                {
+                    var parentFrame = elementParent.getAttribute('frame');
+                    if(parentFrame != null)
+                    {
+                        x += 1;
+                    }
+                }
+                else if(parentBorder > 0)
+                {
+                    x += parentBorder;
+                }
+            }
+        }
+        x += elementParent.offsetLeft;
+        elementParent = elementParent.offsetParent;
+    }
+    return x;
+};
+
+Selenium.prototype.getElementPositionTop = function(locator) {
+   /**
+   * Retrieves the vertical position of an element
+   *
+   * @param locator an <a href="#locators">element locator</a> pointing to an element OR an element itself
+   * @return number of pixels from the edge of the frame.
+   */
+       var element;
+        if ("string"==typeof locator) {
+            element = this.browserbot.findElement(locator);
+        }
+        else {
+            element = locator;
+        }
+
+       var y = 0;
+
+       while (element != null)
+    {
+        if(document.all)
+        {
+            if( (element.tagName != "TABLE") && (element.tagName != "BODY") )
+            {
+            y += element.clientTop;
+            }
+        }
+        else // Netscape/DOM
+        {
+            if(element.tagName == "TABLE")
+            {
+            var parentBorder = parseInt(element.border);
+            if(isNaN(parentBorder))
+            {
+                var parentFrame = element.getAttribute('frame');
+                if(parentFrame != null)
+                {
+                    y += 1;
+                }
+            }
+            else if(parentBorder > 0)
+            {
+                y += parentBorder;
+            }
+            }
+        }
+        y += element.offsetTop;
+
+            // Netscape can get confused in some cases, such that the height of the parent is smaller
+            // than that of the element (which it shouldn't really be). If this is the case, we need to
+            // exclude this element, since it will result in too large a 'top' return value.
+            if (element.offsetParent && element.offsetParent.offsetHeight && element.offsetParent.offsetHeight < element.offsetHeight)
+            {
+                // skip the parent that's too small
+                element = element.offsetParent.offsetParent;
+            }
+            else
+            {
+            // Next up...
+            element = element.offsetParent;
+        }
+       }
+    return y;
+};
+
+Selenium.prototype.getElementWidth = function(locator) {
+   /**
+   * Retrieves the width of an element
+   *
+   * @param locator an <a href="#locators">element locator</a> pointing to an element
+   * @return number width of an element in pixels
+   */
+   var element = this.browserbot.findElement(locator);
+   return element.offsetWidth;
+};
+
+Selenium.prototype.getElementHeight = function(locator) {
+   /**
+   * Retrieves the height of an element
+   *
+   * @param locator an <a href="#locators">element locator</a> pointing to an element
+   * @return number height of an element in pixels
+   */
+   var element = this.browserbot.findElement(locator);
+   return element.offsetHeight;
+};
+
+Selenium.prototype.getCursorPosition = function(locator) {
+    /**
+   * Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers.
+   *
+   * <p>Specifically, if the cursor/selection has been cleared by JavaScript, this command will tend to
+   * return the position of the last location of the cursor, even though the cursor is now gone from the page.  This is filed as <a href="http://jira.openqa.org/browse/SEL-243">SEL-243</a>.</p>
+   * This method will fail if the specified element isn't an input element or textarea, or there is no cursor in the element.
+   *
+   * @param locator an <a href="#locators">element locator</a> pointing to an input element or textarea
+   * @return number the numerical position of the cursor in the field
+   */
+    var element = this.browserbot.findElement(locator);
+    var doc = this.browserbot.getDocument();
+    var win = this.browserbot.getCurrentWindow();
+    if( doc.selection && !browserVersion.isOpera){
+        try {
+            var selectRange = doc.selection.createRange().duplicate();
+            var elementRange = element.createTextRange();
+            selectRange.move("character",0);
+            elementRange.move("character",0);
+            var inRange1 = selectRange.inRange(elementRange);
+            var inRange2 = elementRange.inRange(selectRange);
+            elementRange.setEndPoint("EndToEnd", selectRange);
+        } catch (e) {
+            Assert.fail("There is no cursor on this page!");
+        }
+        var answer = String(elementRange.text).replace(/\r/g,"").length;
+        return answer;
+    } else {
+        if (typeof(element.selectionStart) != "undefined") {
+            if (win.getSelection && typeof(win.getSelection().rangeCount) != undefined && win.getSelection().rangeCount == 0) {
+                Assert.fail("There is no cursor on this page!");
+            }
+            return element.selectionStart;
+        }
+    }
+    throw new Error("Couldn't detect cursor position on this browser!");
+}
+
+
+Selenium.prototype.getExpression = function(expression) {
+    /**
+     * Returns the specified expression.
+     *
+     * <p>This is useful because of JavaScript preprocessing.
+     * It is used to generate commands like assertExpression and waitForExpression.</p>
+     *
+     * @param expression the value to return
+     * @return string the value passed in
+     */
+    return expression;
+}
+
+Selenium.prototype.getXpathCount = function(xpath) {
+    /**
+    * Returns the number of nodes that match the specified xpath, eg. "//table" would give
+    * the number of tables.
+    * 
+    * @param xpath the xpath expression to evaluate. do NOT wrap this expression in a 'count()' function; we will do that for you.
+    * @return number the number of nodes that match the specified xpath
+    */
+    var result = this.browserbot.evaluateXPathCount(xpath, this.browserbot.getDocument());
+    return result;
+}
+
+Selenium.prototype.doAssignId = function(locator, identifier) {
+    /**
+    * Temporarily sets the "id" attribute of the specified element, so you can locate it in the future
+    * using its ID rather than a slow/complicated XPath.  This ID will disappear once the page is
+    * reloaded.
+    * @param locator an <a href="#locators">element locator</a> pointing to an element
+    * @param identifier a string to be used as the ID of the specified element
+    */
+    var element = this.browserbot.findElement(locator);
+    element.id = identifier;
+}
+
+Selenium.prototype.doAllowNativeXpath = function(allow) {
+    /**
+    * Specifies whether Selenium should use the native in-browser implementation
+    * of XPath (if any native version is available); if you pass "false" to
+    * this function, we will always use our pure-JavaScript xpath library.
+    * Using the pure-JS xpath library can improve the consistency of xpath
+    * element locators between different browser vendors, but the pure-JS
+    * version is much slower than the native implementations.
+    * @param allow boolean, true means we'll prefer to use native XPath; false means we'll only use JS XPath
+    */
+    if ("false" == allow || "0" == allow) { // The strings "false" and "0" are true values in JS
+        allow = false;
+    }
+    this.browserbot.allowNativeXpath = allow;
+}
+
+Selenium.prototype.doWaitForCondition = function(script, timeout) {
+    /**
+   * Runs the specified JavaScript snippet repeatedly until it evaluates to "true".
+   * The snippet may have multiple lines, but only the result of the last line
+   * will be considered.
+   *
+   * <p>Note that, by default, the snippet will be run in the runner's test window, not in the window
+   * of your application.  To get the window of your application, you can use
+   * the JavaScript snippet <code>selenium.browserbot.getCurrentWindow()</code>, and then
+   * run your JavaScript in there</p>
+   * @param script the JavaScript snippet to run
+   * @param timeout a timeout in milliseconds, after which this command will return with an error
+   */
+   
+    return Selenium.decorateFunctionWithTimeout(function () {
+        var window = selenium.browserbot.getCurrentWindow();
+        return eval(script);
+    }, timeout);
+};
+
+Selenium.prototype.doWaitForCondition.dontCheckAlertsAndConfirms = true;
+
+Selenium.prototype.doSetTimeout = function(timeout) {
+    /**
+     * Specifies the amount of time that Selenium will wait for actions to complete.
+     *
+     * <p>Actions that require waiting include "open" and the "waitFor*" actions.</p>
+     * The default timeout is 30 seconds.
+     * @param timeout a timeout in milliseconds, after which the action will return with an error
+     */
+    if (!timeout) {
+        timeout = Selenium.DEFAULT_TIMEOUT;
+    }
+    this.defaultTimeout = timeout;
+}
+
+Selenium.prototype.doWaitForPageToLoad = function(timeout) {
+    /**
+     * Waits for a new page to load.
+     *
+     * <p>You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc.
+     * (which are only available in the JS API).</p>
+     *
+     * <p>Selenium constantly keeps track of new pages loading, and sets a "newPageLoaded"
+     * flag when it first notices a page load.  Running any other Selenium command after
+     * turns the flag to false.  Hence, if you want to wait for a page to load, you must
+     * wait immediately after a Selenium command that caused a page-load.</p>
+     * @param timeout a timeout in milliseconds, after which this command will return with an error
+     */
+    // in pi-mode, the test and the harness share the window; thus if we are executing this code, then we have loaded
+    if (window["proxyInjectionMode"] == null || !window["proxyInjectionMode"]) {
+        return this.makePageLoadCondition(timeout);
+    }
+};
+
+Selenium.prototype.doWaitForFrameToLoad = function(frameAddress, timeout) {
+    /**
+     * Waits for a new frame to load.
+     *
+     * <p>Selenium constantly keeps track of new pages and frames loading, 
+     * and sets a "newPageLoaded" flag when it first notices a page load.</p>
+     * 
+     * See waitForPageToLoad for more information.
+     * 
+     * @param frameAddress FrameAddress from the server side
+     * @param timeout a timeout in milliseconds, after which this command will return with an error
+     */
+    // in pi-mode, the test and the harness share the window; thus if we are executing this code, then we have loaded
+    if (window["proxyInjectionMode"] == null || !window["proxyInjectionMode"]) {
+        return this.makePageLoadCondition(timeout);
+    }
+};
+
+Selenium.prototype._isNewPageLoaded = function() {
+    return this.browserbot.isNewPageLoaded();
+};
+
+Selenium.prototype.doWaitForPageToLoad.dontCheckAlertsAndConfirms = true;
+
+/**
+ * Evaluate a parameter, performing JavaScript evaluation and variable substitution.
+ * If the string matches the pattern "javascript{ ... }", evaluate the string between the braces.
+ */
+Selenium.prototype.preprocessParameter = function(value) {
+    var match = value.match(/^javascript\{((.|\r?\n)+)\}$/);
+    if (match && match[1]) {
+        return eval(match[1]).toString();
+    }
+    return this.replaceVariables(value);
+};
+
+/*
+ * Search through str and replace all variable references ${varName} with their
+ * value in storedVars.
+ */
+Selenium.prototype.replaceVariables = function(str) {
+    var stringResult = str;
+
+    // Find all of the matching variable references
+    var match = stringResult.match(/\$\{\w+\}/g);
+    if (!match) {
+        return stringResult;
+    }
+
+    // For each match, lookup the variable value, and replace if found
+    for (var i = 0; match && i < match.length; i++) {
+        var variable = match[i]; // The replacement variable, with ${}
+        var name = variable.substring(2, variable.length - 1); // The replacement variable without ${}
+        var replacement = storedVars[name];
+        if (replacement != undefined) {
+            stringResult = stringResult.replace(variable, replacement);
+        }
+    }
+    return stringResult;
+};
+
+Selenium.prototype.getCookie = function() {
+    /**
+     * Return all cookies of the current page under test.
+     *
+     * @return string all cookies of the current page under test
+     */
+    var doc = this.browserbot.getDocument();
+    return doc.cookie;
+};
+
+Selenium.prototype.doCreateCookie = function(nameValuePair, optionsString) {
+    /**
+     * Create a new cookie whose path and domain are same with those of current page
+     * under test, unless you specified a path for this cookie explicitly.
+     *
+     * @param nameValuePair name and value of the cookie in a format "name=value"
+     * @param optionsString options for the cookie. Currently supported options include 'path' and 'max_age'.
+     *      the optionsString's format is "path=/path/, max_age=60". The order of options are irrelevant, the unit
+     *      of the value of 'max_age' is second.
+     */
+    var results = /[^\s=\[\]\(\),"\/\?@:;]+=[^\s=\[\]\(\),"\/\?@:;]*/.test(nameValuePair);
+    if (!results) {
+        throw new SeleniumError("Invalid parameter.");
+    }
+    var cookie = nameValuePair.trim();
+    results = /max_age=(\d+)/.exec(optionsString);
+    if (results) {
+        var expireDateInMilliseconds = (new Date()).getTime() + results[1] * 1000;
+        cookie += "; expires=" + new Date(expireDateInMilliseconds).toGMTString();
+    }
+    results = /path=([^\s,]+)[,]?/.exec(optionsString);
+    if (results) {
+        var path = results[1];
+        if (browserVersion.khtml) {
+            // Safari and conquerer don't like paths with / at the end
+            if ("/" != path) {
+                path = path.replace(/\/$/, "");
+            }
+        }
+        cookie += "; path=" + path;
+    }
+    LOG.debug("Setting cookie to: " + cookie);
+    this.browserbot.getDocument().cookie = cookie;
+}
+
+Selenium.prototype.doDeleteCookie = function(name,path) {
+    /**
+     * Delete a named cookie with specified path.
+     *
+     * @param name the name of the cookie to be deleted
+     * @param path the path property of the cookie to be deleted
+     */
+    // set the expire time of the cookie to be deleted to one minute before now.
+    path = path.trim();
+    if (browserVersion.khtml) {
+        // Safari and conquerer don't like paths with / at the end
+        if ("/" != path) {
+            path = path.replace(/\/$/, "");
+        }
+    }
+    var expireDateInMilliseconds = (new Date()).getTime() + (-1 * 1000);
+    var cookie = name.trim() + "=deleted; path=" + path + "; expires=" + new Date(expireDateInMilliseconds).toGMTString();
+    LOG.debug("Setting cookie to: " + cookie);
+    this.browserbot.getDocument().cookie = cookie;
+}
+
+Selenium.prototype.doSetBrowserLogLevel = function(logLevel) {
+    /**
+    * Sets the threshold for browser-side logging messages; log messages beneath this threshold will be discarded.
+    * Valid logLevel strings are: "debug", "info", "warn", "error" or "off".
+    * To see the browser logs, you need to
+    * either show the log window in GUI mode, or enable browser-side logging in Selenium RC.
+    *
+    * @param logLevel one of the following: "debug", "info", "warn", "error" or "off"
+    */
+    if (logLevel == null || logLevel == "") {
+        throw new SeleniumError("You must specify a log level");
+    }
+    logLevel = logLevel.toLowerCase();
+    if (LOG.logLevels[logLevel] == null) {
+        throw new SeleniumError("Invalid log level: " + logLevel);
+    }
+    LOG.setLogLevelThreshold(logLevel);
+}
+
+Selenium.prototype.doRunScript = function(script) {
+    /**
+    * Creates a new "script" tag in the body of the current test window, and 
+    * adds the specified text into the body of the command.  Scripts run in
+    * this way can often be debugged more easily than scripts executed using
+    * Selenium's "getEval" command.  Beware that JS exceptions thrown in these script
+    * tags aren't managed by Selenium, so you should probably wrap your script
+    * in try/catch blocks if there is any chance that the script will throw
+    * an exception.
+    * @param script the JavaScript snippet to run
+    */
+    var win = this.browserbot.getCurrentWindow();
+    var doc = win.document;
+    var scriptTag = doc.createElement("script");
+    scriptTag.type = "text/javascript"
+    scriptTag.text = script;
+    doc.body.appendChild(scriptTag);
+}
+
+Selenium.prototype.doAddLocationStrategy = function(strategyName, functionDefinition) {
+    /**
+    * Defines a new function for Selenium to locate elements on the page.
+    * For example,
+    * if you define the strategy "foo", and someone runs click("foo=blah"), we'll
+    * run your function, passing you the string "blah", and click on the element 
+    * that your function
+    * returns, or throw an "Element not found" error if your function returns null.
+    *
+    * We'll pass three arguments to your function:
+    * <ul>
+    * <li>locator: the string the user passed in</li>
+    * <li>inWindow: the currently selected window</li>
+    * <li>inDocument: the currently selected document</li>
+    * </ul>
+    * The function must return null if the element can't be found.
+    * 
+    * @param strategyName the name of the strategy to define; this should use only
+    *   letters [a-zA-Z] with no spaces or other punctuation.
+    * @param functionDefinition a string defining the body of a function in JavaScript.
+    *   For example: <code>return inDocument.getElementById(locator);</code>
+    */
+    if (!/^[a-zA-Z]+$/.test(strategyName)) {
+        throw new SeleniumError("Invalid strategy name: " + strategyName);
+    }
+    var strategyFunction;
+    try {
+        strategyFunction = new Function("locator", "inDocument", "inWindow", functionDefinition);
+    } catch (ex) {
+        throw new SeleniumError("Error evaluating function definition: " + extractExceptionMessage(ex));
+    }
+    var safeStrategyFunction = function() {
+        try {
+            return strategyFunction.apply(this, arguments);
+        } catch (ex) {
+            throw new SeleniumError("Error executing strategy function " + strategyName + ": " + extractExceptionMessage(ex));
+        }
+    }
+    this.browserbot.locationStrategies[strategyName] = safeStrategyFunction;
+}
+
+/**
+ *  Factory for creating "Option Locators".
+ *  An OptionLocator is an object for dealing with Select options (e.g. for
+ *  finding a specified option, or asserting that the selected option of 
+ *  Select element matches some condition.
+ *  The type of locator returned by the factory depends on the locator string:
+ *     label=<exp>  (OptionLocatorByLabel)
+ *     value=<exp>  (OptionLocatorByValue)
+ *     index=<exp>  (OptionLocatorByIndex)
+ *     id=<exp>     (OptionLocatorById)
+ *     <exp> (default is OptionLocatorByLabel).
+ */
+function OptionLocatorFactory() {
+}
+
+OptionLocatorFactory.prototype.fromLocatorString = function(locatorString) {
+    var locatorType = 'label';
+    var locatorValue = locatorString;
+    // If there is a locator prefix, use the specified strategy
+    var result = locatorString.match(/^([a-zA-Z]+)=(.*)/);
+    if (result) {
+        locatorType = result[1];
+        locatorValue = result[2];
+    }
+    if (this.optionLocators == undefined) {
+        this.registerOptionLocators();
+    }
+    if (this.optionLocators[locatorType]) {
+        return new this.optionLocators[locatorType](locatorValue);
+    }
+    throw new SeleniumError("Unkown option locator type: " + locatorType);
+};
+
+/**
+ * To allow for easy extension, all of the option locators are found by
+ * searching for all methods of OptionLocatorFactory.prototype that start
+ * with "OptionLocatorBy".
+ * TODO: Consider using the term "Option Specifier" instead of "Option Locator".
+ */
+OptionLocatorFactory.prototype.registerOptionLocators = function() {
+    this.optionLocators={};
+    for (var functionName in this) {
+      var result = /OptionLocatorBy([A-Z].+)$/.exec(functionName);
+      if (result != null) {
+          var locatorName = result[1].lcfirst();
+          this.optionLocators[locatorName] = this[functionName];
+      }
+    }
+};
+
+/**
+ *  OptionLocator for options identified by their labels.
+ */
+OptionLocatorFactory.prototype.OptionLocatorByLabel = function(label) {
+    this.label = label;
+    this.labelMatcher = new PatternMatcher(this.label);
+    this.findOption = function(element) {
+        for (var i = 0; i < element.options.length; i++) {
+            if (this.labelMatcher.matches(element.options[i].text)) {
+                return element.options[i];
+            }
+        }
+        throw new SeleniumError("Option with label '" + this.label + "' not found");
+    };
+
+    this.assertSelected = function(element) {
+        var selectedLabel = element.options[element.selectedIndex].text;
+        Assert.matches(this.label, selectedLabel)
+    };
+};
+
+/**
+ *  OptionLocator for options identified by their values.
+ */
+OptionLocatorFactory.prototype.OptionLocatorByValue = function(value) {
+    this.value = value;
+    this.valueMatcher = new PatternMatcher(this.value);
+    this.findOption = function(element) {
+        for (var i = 0; i < element.options.length; i++) {
+            if (this.valueMatcher.matches(element.options[i].value)) {
+                return element.options[i];
+            }
+        }
+        throw new SeleniumError("Option with value '" + this.value + "' not found");
+    };
+
+    this.assertSelected = function(element) {
+        var selectedValue = element.options[element.selectedIndex].value;
+        Assert.matches(this.value, selectedValue)
+    };
+};
+
+/**
+ *  OptionLocator for options identified by their index.
+ */
+OptionLocatorFactory.prototype.OptionLocatorByIndex = function(index) {
+    this.index = Number(index);
+    if (isNaN(this.index) || this.index < 0) {
+        throw new SeleniumError("Illegal Index: " + index);
+    }
+
+    this.findOption = function(element) {
+        if (element.options.length <= this.index) {
+            throw new SeleniumError("Index out of range.  Only " + element.options.length + " options available");
+        }
+        return element.options[this.index];
+    };
+
+    this.assertSelected = function(element) {
+        Assert.equals(this.index, element.selectedIndex);
+    };
+};
+
+/**
+ *  OptionLocator for options identified by their id.
+ */
+OptionLocatorFactory.prototype.OptionLocatorById = function(id) {
+    this.id = id;
+    this.idMatcher = new PatternMatcher(this.id);
+    this.findOption = function(element) {
+        for (var i = 0; i < element.options.length; i++) {
+            if (this.idMatcher.matches(element.options[i].id)) {
+                return element.options[i];
+            }
+        }
+        throw new SeleniumError("Option with id '" + this.id + "' not found");
+    };
+
+    this.assertSelected = function(element) {
+        var selectedId = element.options[element.selectedIndex].id;
+        Assert.matches(this.id, selectedId)
+    };
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-browserbot.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-browserbot.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-browserbot.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,2203 @@
+/*
+* Copyright 2004 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*
+*/
+
+/*
+* This script provides the Javascript API to drive the test application contained within
+* a Browser Window.
+* TODO:
+*    Add support for more events (keyboard and mouse)
+*    Allow to switch "user-entry" mode from mouse-based to keyboard-based, firing different
+*          events in different modes.
+*/
+
+// The window to which the commands will be sent.  For example, to click on a
+// popup window, first select that window, and then do a normal click command.
+var BrowserBot = function(topLevelApplicationWindow) {
+    this.topWindow = topLevelApplicationWindow;
+    this.topFrame = this.topWindow;
+    this.baseUrl=window.location.href;
+
+    // the buttonWindow is the Selenium window
+    // it contains the Run/Pause buttons... this should *not* be the AUT window
+    this.buttonWindow = window;
+    this.currentWindow = this.topWindow;
+    this.currentWindowName = null;
+    this.allowNativeXpath = true;
+
+    // We need to know this in advance, in case the frame closes unexpectedly
+    this.isSubFrameSelected = false;
+
+    this.altKeyDown = false;
+    this.controlKeyDown = false;
+    this.shiftKeyDown = false;
+    this.metaKeyDown = false;
+
+    this.modalDialogTest = null;
+    this.recordedAlerts = new Array();
+    this.recordedConfirmations = new Array();
+    this.recordedPrompts = new Array();
+    this.openedWindows = {};
+    this.nextConfirmResult = true;
+    this.nextPromptResult = '';
+    this.newPageLoaded = false;
+    this.pageLoadError = null;
+
+    this.shouldHighlightLocatedElement = false;
+
+    this.uniqueId = "seleniumMarker" + new Date().getTime();
+    this.pollingForLoad = new Object();
+    this.permDeniedCount = new Object();
+    this.windowPollers = new Array();
+    // DGF for backwards compatibility
+    this.browserbot = this;
+
+    var self = this;
+
+    objectExtend(this, PageBot.prototype);
+    this._registerAllLocatorFunctions();
+
+    this.recordPageLoad = function(elementOrWindow) {
+        LOG.debug("Page load detected");
+        try {
+            if (elementOrWindow.location && elementOrWindow.location.href) {
+                LOG.debug("Page load location=" + elementOrWindow.location.href);
+            } else if (elementOrWindow.contentWindow && elementOrWindow.contentWindow.location && elementOrWindow.contentWindow.location.href) {
+                LOG.debug("Page load location=" + elementOrWindow.contentWindow.location.href);
+            } else {
+                LOG.debug("Page load location unknown, current window location=" + this.getCurrentWindow(true).location);
+            }
+        } catch (e) {
+            LOG.error("Caught an exception attempting to log location; this should get noticed soon!");
+            LOG.exception(e);
+            self.pageLoadError = e;
+            return;
+        }
+        self.newPageLoaded = true;
+    };
+
+    this.isNewPageLoaded = function() {
+        if (this.pageLoadError) {
+            LOG.error("isNewPageLoaded found an old pageLoadError");
+            var e = this.pageLoadError;
+            this.pageLoadError = null;
+            throw e;
+        }
+        return self.newPageLoaded;
+    };
+
+};
+
+// DGF PageBot exists for backwards compatibility with old user-extensions
+var PageBot = function(){};
+
+BrowserBot.createForWindow = function(window, proxyInjectionMode) {
+    var browserbot;
+    LOG.debug('createForWindow');
+    LOG.debug("browserName: " + browserVersion.name);
+    LOG.debug("userAgent: " + navigator.userAgent);
+    if (browserVersion.isIE) {
+        browserbot = new IEBrowserBot(window);
+    }
+    else if (browserVersion.isKonqueror) {
+        browserbot = new KonquerorBrowserBot(window);
+    }
+    else if (browserVersion.isOpera) {
+        browserbot = new OperaBrowserBot(window);
+    }
+    else if (browserVersion.isSafari) {
+        browserbot = new SafariBrowserBot(window);
+    }
+    else {
+        // Use mozilla by default
+        browserbot = new MozillaBrowserBot(window);
+    }
+    // getCurrentWindow has the side effect of modifying it to handle page loads etc
+    browserbot.proxyInjectionMode = proxyInjectionMode;
+    browserbot.getCurrentWindow();    // for modifyWindow side effect.  This is not a transparent style
+    return browserbot;
+};
+
+// todo: rename?  This doesn't actually "do" anything.
+BrowserBot.prototype.doModalDialogTest = function(test) {
+    this.modalDialogTest = test;
+};
+
+BrowserBot.prototype.cancelNextConfirmation = function(result) {
+    this.nextConfirmResult = result;
+};
+
+BrowserBot.prototype.setNextPromptResult = function(result) {
+    this.nextPromptResult = result;
+};
+
+BrowserBot.prototype.hasAlerts = function() {
+    return (this.recordedAlerts.length > 0);
+};
+
+BrowserBot.prototype.relayBotToRC = function(s) {
+    // DGF need to do this funny trick to see if we're in PI mode, because
+    // "this" might be the window, rather than the browserbot (e.g. during window.alert) 
+    var piMode = this.proxyInjectionMode;
+    if (!piMode) {
+        if (typeof(selenium) != "undefined") {
+            piMode = selenium.browserbot && selenium.browserbot.proxyInjectionMode;
+        }
+    }
+    if (piMode) {
+        this.relayToRC("selenium." + s);
+    }
+};
+
+BrowserBot.prototype.relayToRC = function(name) {
+        var object = eval(name);
+        var s = 'state:' + serializeObject(name, object) + "\n";
+        sendToRC(s,"state=true");
+}
+
+BrowserBot.prototype.resetPopups = function() {
+    this.recordedAlerts = [];
+    this.recordedConfirmations = [];
+    this.recordedPrompts = [];
+}
+
+BrowserBot.prototype.getNextAlert = function() {
+    var t = this.recordedAlerts.shift();
+    this.relayBotToRC("browserbot.recordedAlerts");
+    return t;
+};
+
+BrowserBot.prototype.hasConfirmations = function() {
+    return (this.recordedConfirmations.length > 0);
+};
+
+BrowserBot.prototype.getNextConfirmation = function() {
+    var t = this.recordedConfirmations.shift();
+    this.relayBotToRC("browserbot.recordedConfirmations");
+    return t;
+};
+
+BrowserBot.prototype.hasPrompts = function() {
+    return (this.recordedPrompts.length > 0);
+};
+
+BrowserBot.prototype.getNextPrompt = function() {
+    var t = this.recordedPrompts.shift();
+    this.relayBotToRC("browserbot.recordedPrompts");
+    return t;
+};
+
+/* Fire a mouse event in a browser-compatible manner */
+
+BrowserBot.prototype.triggerMouseEvent = function(element, eventType, canBubble, clientX, clientY) {
+    clientX = clientX ? clientX : 0;
+    clientY = clientY ? clientY : 0;
+
+    LOG.debug("triggerMouseEvent assumes setting screenX and screenY to 0 is ok");
+    var screenX = 0;
+    var screenY = 0;
+
+    canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
+    if (element.fireEvent) {
+        var evt = createEventObject(element, this.controlKeyDown, this.altKeyDown, this.shiftKeyDown, this.metaKeyDown);
+        evt.detail = 0;
+        evt.button = 1;
+        evt.relatedTarget = null;
+        if (!screenX && !screenY && !clientX && !clientY && !this.controlKeyDown && !this.altKeyDown && !this.shiftKeyDown && !this.metaKeyDown) {
+            element.fireEvent('on' + eventType);
+        }
+        else {
+            evt.screenX = screenX;
+            evt.screenY = screenY;
+            evt.clientX = clientX;
+            evt.clientY = clientY;
+
+            // when we go this route, window.event is never set to contain the event we have just created.
+            // ideally we could just slide it in as follows in the try-block below, but this normally
+            // doesn't work.  This is why I try to avoid this code path, which is only required if we need to
+            // set attributes on the event (e.g., clientX).
+            try {
+                window.event = evt;
+            }
+            catch(e) {
+                // getting an "Object does not support this action or property" error.  Save the event away
+                // for future reference.
+                // TODO: is there a way to update window.event?
+
+                // work around for http://jira.openqa.org/browse/SEL-280 -- make the event available somewhere:
+                selenium.browserbot.getCurrentWindow().selenium_event = evt;
+            }
+            element.fireEvent('on' + eventType, evt);
+        }
+    }
+    else {
+        var evt = document.createEvent('MouseEvents');
+        if (evt.initMouseEvent)
+        {
+            //Safari
+            evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, screenX, screenY, clientX, clientY,
+                this.controlKeyDown, this.altKeyDown, this.shiftKeyDown, this.metaKeyDown, 0, null);
+        }
+        else {
+            LOG.warn("element doesn't have initMouseEvent; firing an event which should -- but doesn't -- have other mouse-event related attributes here, as well as controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown");
+            evt.initEvent(eventType, canBubble, true);
+
+            evt.shiftKey = this.shiftKeyDown;
+            evt.metaKey = this.metaKeyDown;
+            evt.altKey = this.altKeyDown;
+            evt.ctrlKey = this.controlKeyDown;
+
+        }
+        element.dispatchEvent(evt);
+    }
+}
+
+BrowserBot.prototype._windowClosed = function(win) {
+    var c = win.closed;
+    if (c == null) return true;
+    return c;
+};
+
+BrowserBot.prototype._modifyWindow = function(win) {
+    // In proxyInjectionMode, have to suppress LOG calls in _modifyWindow to avoid an infinite loop
+    if (this._windowClosed(win)) {
+        if (!this.proxyInjectionMode) {
+            LOG.error("modifyWindow: Window was closed!");
+        }
+        return null;
+    }
+    if (!this.proxyInjectionMode) {
+        LOG.debug('modifyWindow ' + this.uniqueId + ":" + win[this.uniqueId]);
+    }
+    if (!win[this.uniqueId]) {
+        win[this.uniqueId] = 1;
+        this.modifyWindowToRecordPopUpDialogs(win, this);
+    }
+    // In proxyInjection mode, we have our own mechanism for detecting page loads
+    if (!this.proxyInjectionMode) {
+        this.modifySeparateTestWindowToDetectPageLoads(win);
+    }
+    if (win.frames && win.frames.length && win.frames.length > 0) {
+        for (var i = 0; i < win.frames.length; i++) {
+            try {
+                this._modifyWindow(win.frames[i]);
+            } catch (e) {} // we're just trying to be opportunistic; don't worry if this doesn't work out
+        }
+    }
+    return win;
+};
+
+BrowserBot.prototype.selectWindow = function(target) {
+    // TODO implement a locator syntax here
+    if (target && target != "null") {
+        try {
+            this._selectWindowByName(target);
+        }
+        catch (e) {
+            this._selectWindowByTitle(target);
+        }
+    } else {
+        this._selectTopWindow();
+    }
+};
+
+BrowserBot.prototype._selectTopWindow = function() {
+    this.currentWindowName = null;
+    this.currentWindow = this.topWindow;
+    this.topFrame = this.topWindow;
+    this.isSubFrameSelected = false;
+}
+
+BrowserBot.prototype._selectWindowByName = function(target) {
+    this.currentWindow = this.getWindowByName(target, false);
+    this.topFrame = this.currentWindow;
+    this.currentWindowName = target;
+    this.isSubFrameSelected = false;
+}
+
+BrowserBot.prototype._selectWindowByTitle = function(target) {
+    var windowName = this.getWindowNameByTitle(target);
+    if (!windowName) {
+        this._selectTopWindow();
+    } else {
+        this._selectWindowByName(windowName);
+    }
+}
+
+BrowserBot.prototype.selectFrame = function(target) {
+    if (target.indexOf("index=") == 0) {
+        target = target.substr(6);
+        var frame = this.getCurrentWindow().frames[target];
+        if (frame == null) {
+            throw new SeleniumError("Not found: frames["+index+"]");
+        }
+        if (!frame.document) {
+            throw new SeleniumError("frames["+index+"] is not a frame");
+        }
+        this.currentWindow = frame;
+        this.isSubFrameSelected = true;
+    }
+    else if (target == "relative=up") {
+        this.currentWindow = this.getCurrentWindow().parent;
+        this.isSubFrameSelected = (this._getFrameElement(this.currentWindow) != null);
+    } else if (target == "relative=top") {
+        this.currentWindow = this.topFrame;
+        this.isSubFrameSelected = false;
+    } else {
+        var frame = this.findElement(target);
+        if (frame == null) {
+            throw new SeleniumError("Not found: " + target);
+        }
+        // now, did they give us a frame or a frame ELEMENT?
+        var match = false;
+        if (frame.contentWindow) {
+            // this must be a frame element
+            if (browserVersion.isHTA) {
+                // stupid HTA bug; can't get in the front door
+                target = frame.contentWindow.name;
+            } else {
+                this.currentWindow = frame.contentWindow;
+                this.isSubFrameSelected = true;
+                match = true;
+            }
+        } else if (frame.document && frame.location) {
+            // must be an actual window frame
+            this.currentWindow = frame;
+            this.isSubFrameSelected = true;
+            match = true;
+        }
+
+        if (!match) {
+            // neither, let's loop through the frame names
+            var win = this.getCurrentWindow();
+
+            if (win && win.frames && win.frames.length) {
+                for (var i = 0; i < win.frames.length; i++) {
+                    if (win.frames[i].name == target) {
+                        this.currentWindow = win.frames[i];
+                        this.isSubFrameSelected = true;
+                        match = true;
+                        break;
+                    }
+                }
+            }
+            if (!match) {
+                throw new SeleniumError("Not a frame: " + target);
+            }
+        }
+    }
+    // modifies the window
+    this.getCurrentWindow();
+};
+
+BrowserBot.prototype.doesThisFrameMatchFrameExpression = function(currentFrameString, target) {
+    var isDom = false;
+    if (target.indexOf("dom=") == 0) {
+        target = target.substr(4);
+        isDom = true;
+    } else if (target.indexOf("index=") == 0) {
+        target = "frames[" + target.substr(6) + "]";
+        isDom = true;
+    }
+    var t;
+    try {
+        eval("t=" + currentFrameString + "." + target);
+    } catch (e) {
+    }
+    var autWindow = this.browserbot.getCurrentWindow();
+    if (t != null) {
+        try {
+            if (t.window == autWindow) {
+                return true;
+            }
+            if (t.window.uniqueId == autWindow.uniqueId) {
+                return true;
+               }
+            return false;
+        } catch (permDenied) {
+            // DGF if the windows are incomparable, they're probably not the same...
+        }
+    }
+    if (isDom) {
+        return false;
+    }
+    var currentFrame;
+    eval("currentFrame=" + currentFrameString);
+    if (target == "relative=up") {
+        if (currentFrame.window.parent == autWindow) {
+            return true;
+        }
+        return false;
+    }
+    if (target == "relative=top") {
+        if (currentFrame.window.top == autWindow) {
+            return true;
+        }
+        return false;
+    }
+    if (currentFrame.window == autWindow.parent) {
+        if (autWindow.name == target) {
+            return true;
+        }
+        try {
+            var element = this.findElement(target, currentFrame.window);
+            if (element.contentWindow == autWindow) {
+                return true;
+            }
+        } catch (e) {}
+    }
+    return false;
+};
+
+BrowserBot.prototype.openLocation = function(target) {
+    // We're moving to a new page - clear the current one
+    var win = this.getCurrentWindow();
+    LOG.debug("openLocation newPageLoaded = false");
+    this.newPageLoaded = false;
+
+    this.setOpenLocation(win, target);
+};
+
+BrowserBot.prototype.openWindow = function(url, windowID) {
+    if (url != "") {
+        url = absolutify(url, this.baseUrl);
+    }
+    if (browserVersion.isHTA) {
+        // in HTA mode, calling .open on the window interprets the url relative to that window
+        // we need to absolute-ize the URL to make it consistent
+        var child = this.getCurrentWindow().open(url, windowID);
+        selenium.browserbot.openedWindows[windowID] = child;
+    } else {
+        this.getCurrentWindow().open(url, windowID);
+    }
+};
+
+BrowserBot.prototype.setIFrameLocation = function(iframe, location) {
+    iframe.src = location;
+};
+
+BrowserBot.prototype.setOpenLocation = function(win, loc) {
+    loc = absolutify(loc, this.baseUrl);
+    if (browserVersion.isHTA) {
+        var oldHref = win.location.href;
+        win.location.href = loc;
+        var marker = null;
+        try {
+            marker = this.isPollingForLoad(win);
+            if (marker && win.location[marker]) {
+                win.location[marker] = false;
+            }
+        } catch (e) {} // DGF don't know why, but this often fails
+    } else {
+        win.location.href = loc;
+    }
+};
+
+BrowserBot.prototype.getCurrentPage = function() {
+    return this;
+};
+
+BrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
+    var self = this;
+
+    windowToModify.alert = function(alert) {
+        browserBot.recordedAlerts.push(alert);
+        self.relayBotToRC.call(self, "browserbot.recordedAlerts");
+    };
+
+    windowToModify.confirm = function(message) {
+        browserBot.recordedConfirmations.push(message);
+        var result = browserBot.nextConfirmResult;
+        browserBot.nextConfirmResult = true;
+        self.relayBotToRC.call(self, "browserbot.recordedConfirmations");
+        return result;
+    };
+
+    windowToModify.prompt = function(message) {
+        browserBot.recordedPrompts.push(message);
+        var result = !browserBot.nextConfirmResult ? null : browserBot.nextPromptResult;
+        browserBot.nextConfirmResult = true;
+        browserBot.nextPromptResult = '';
+        self.relayBotToRC.call(self, "browserbot.recordedPrompts");
+        return result;
+    };
+
+    // Keep a reference to all popup windows by name
+    // note that in IE the "windowName" argument must be a valid javascript identifier, it seems.
+    var originalOpen = windowToModify.open;
+    var originalOpenReference;
+    if (browserVersion.isHTA) {
+        originalOpenReference = 'selenium_originalOpen' + new Date().getTime();
+        windowToModify[originalOpenReference] = windowToModify.open;
+    }
+
+    var isHTA = browserVersion.isHTA;
+
+    var newOpen = function(url, windowName, windowFeatures, replaceFlag) {
+        var myOriginalOpen = originalOpen;
+        if (isHTA) {
+            myOriginalOpen = this[originalOpenReference];
+        }
+        var openedWindow = myOriginalOpen(url, windowName, windowFeatures, replaceFlag);
+        LOG.debug("window.open call intercepted; window ID (which you can use with selectWindow()) is \"" +  windowName + "\"");
+        if (windowName!=null) {
+            openedWindow["seleniumWindowName"] = windowName;
+        }
+        selenium.browserbot.openedWindows[windowName] = openedWindow;
+        return openedWindow;
+    };
+
+    if (browserVersion.isHTA) {
+        originalOpenReference = 'selenium_originalOpen' + new Date().getTime();
+        newOpenReference = 'selenium_newOpen' + new Date().getTime();
+        var setOriginalRef = "this['" + originalOpenReference + "'] = this.open;";
+
+        if (windowToModify.eval) {
+            windowToModify.eval(setOriginalRef);
+            windowToModify.open = newOpen;
+        } else {
+            // DGF why can't I eval here?  Seems like I'm querying the window at a bad time, maybe?
+            setOriginalRef += "this.open = this['" + newOpenReference + "'];";
+            windowToModify[newOpenReference] = newOpen;
+            windowToModify.setTimeout(setOriginalRef, 0);
+        }
+    } else {
+        windowToModify.open = newOpen;
+    }
+};
+
+/**
+ * Call the supplied function when a the current page unloads and a new one loads.
+ * This is done by polling continuously until the document changes and is fully loaded.
+ */
+BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowObject) {
+    // Since the unload event doesn't fire in Safari 1.3, we start polling immediately
+    if (!windowObject) {
+        LOG.warn("modifySeparateTestWindowToDetectPageLoads: no windowObject!");
+        return;
+    }
+    if (this._windowClosed(windowObject)) {
+        LOG.info("modifySeparateTestWindowToDetectPageLoads: windowObject was closed");
+        return;
+    }
+    var oldMarker = this.isPollingForLoad(windowObject);
+    if (oldMarker) {
+        LOG.debug("modifySeparateTestWindowToDetectPageLoads: already polling this window: " + oldMarker);
+        return;
+    }
+
+    var marker = 'selenium' + new Date().getTime();
+    LOG.debug("Starting pollForLoad (" + marker + "): " + windowObject.location);
+    this.pollingForLoad[marker] = true;
+    // if this is a frame, add a load listener, otherwise, attach a poller
+    var frameElement = this._getFrameElement(windowObject);
+    // DGF HTA mode can't attach load listeners to subframes (yuk!)
+    var htaSubFrame = this._isHTASubFrame(windowObject);
+    if (frameElement && !htaSubFrame) {
+        LOG.debug("modifySeparateTestWindowToDetectPageLoads: this window is a frame; attaching a load listener");
+        addLoadListener(frameElement, this.recordPageLoad);
+        frameElement[marker] = true;
+        frameElement["frame"+this.uniqueId] = marker;
+	LOG.debug("dgf this.uniqueId="+this.uniqueId);
+	LOG.debug("dgf marker="+marker);
+	LOG.debug("dgf frameElement['frame'+this.uniqueId]="+frameElement['frame'+this.uniqueId]);
+frameElement[this.uniqueId] = marker;
+LOG.debug("dgf frameElement[this.uniqueId]="+frameElement[this.uniqueId]);
+    } else {
+        windowObject.location[marker] = true;
+        windowObject[this.uniqueId] = marker;
+        this.pollForLoad(this.recordPageLoad, windowObject, windowObject.document, windowObject.location, windowObject.location.href, marker);
+    }
+};
+
+BrowserBot.prototype._isHTASubFrame = function(win) {
+    if (!browserVersion.isHTA) return false;
+    // DGF this is wrong! what if "win" isn't the selected window?
+    return this.isSubFrameSelected;
+}
+
+BrowserBot.prototype._getFrameElement = function(win) {
+    var frameElement = null;
+    var caught;
+    try {
+        frameElement = win.frameElement;
+    } catch (e) {
+        caught = true;
+    }
+    if (caught) {
+        // on IE, checking frameElement in a pop-up results in a "No such interface supported" exception
+        // but it might have a frame element anyway!
+        var parentContainsIdenticallyNamedFrame = false;
+        try {
+            parentContainsIdenticallyNamedFrame = win.parent.frames[win.name];
+        } catch (e) {} // this may fail if access is denied to the parent; in that case, assume it's not a pop-up
+
+        if (parentContainsIdenticallyNamedFrame) {
+            // it can't be a coincidence that the parent has a frame with the same name as myself!
+            var result;
+            try {
+                result = parentContainsIdenticallyNamedFrame.frameElement;
+                if (result) {
+                    return result;
+                }
+            } catch (e) {} // it was worth a try! _getFrameElementsByName is often slow
+            result = this._getFrameElementByName(win.name, win.parent.document, win);
+            return result;
+        }
+    }
+    LOG.debug("_getFrameElement: frameElement="+frameElement); 
+    if (frameElement) {
+        LOG.debug("frameElement.name="+frameElement.name);
+    }
+    return frameElement;
+}
+
+BrowserBot.prototype._getFrameElementByName = function(name, doc, win) {
+    var frames;
+    var frame;
+    var i;
+    frames = doc.getElementsByTagName("iframe");
+    for (i = 0; i < frames.length; i++) {
+        frame = frames[i];        
+        if (frame.name === name) {
+            return frame;
+        }
+    }
+    frames = doc.getElementsByTagName("frame");
+    for (i = 0; i < frames.length; i++) {
+        frame = frames[i];        
+        if (frame.name === name) {
+            return frame;
+        }
+    }
+    // DGF weird; we only call this function when we know the doc contains the frame
+    LOG.warn("_getFrameElementByName couldn't find a frame or iframe; checking every element for the name " + name);
+    return BrowserBot.prototype.locateElementByName(win.name, win.parent.document);
+}
+    
+
+/**
+ * Set up a polling timer that will keep checking the readyState of the document until it's complete.
+ * Since we might call this before the original page is unloaded, we first check to see that the current location
+ * or href is different from the original one.
+ */
+BrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
+    LOG.debug("pollForLoad original (" + marker + "): " + originalHref);
+    try {
+        if (this._windowClosed(windowObject)) {
+            LOG.debug("pollForLoad WINDOW CLOSED (" + marker + ")");
+            delete this.pollingForLoad[marker];
+            return;
+        }
+
+        var isSamePage = this._isSamePage(windowObject, originalDocument, originalLocation, originalHref, marker);
+        var rs = this.getReadyState(windowObject, windowObject.document);
+
+        if (!isSamePage && rs == 'complete') {
+            var currentHref = windowObject.location.href;
+            LOG.debug("pollForLoad FINISHED (" + marker + "): " + rs + " (" + currentHref + ")");
+            delete this.pollingForLoad[marker];
+            this._modifyWindow(windowObject);
+            var newMarker = this.isPollingForLoad(windowObject);
+            if (!newMarker) {
+                LOG.debug("modifyWindow didn't start new poller: " + newMarker);
+                this.modifySeparateTestWindowToDetectPageLoads(windowObject);
+            }
+            newMarker = this.isPollingForLoad(windowObject);
+            var currentlySelectedWindow;
+            var currentlySelectedWindowMarker;
+            currentlySelectedWindow =this.getCurrentWindow(true);
+            currentlySelectedWindowMarker = currentlySelectedWindow[this.uniqueId];
+
+            LOG.debug("pollForLoad (" + marker + ") restarting " + newMarker);
+            if (/(TestRunner-splash|Blank)\.html\?start=true$/.test(currentHref)) {
+                LOG.debug("pollForLoad Oh, it's just the starting page.  Never mind!");
+            } else if (currentlySelectedWindowMarker == newMarker) {
+                loadFunction(currentlySelectedWindow);
+            } else {
+                LOG.debug("pollForLoad page load detected in non-current window; ignoring (currentlySelected="+currentlySelectedWindowMarker+", detection in "+newMarker+")");
+            }
+            return;
+        }
+        LOG.debug("pollForLoad continue (" + marker + "): " + currentHref);
+        this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+    } catch (e) {
+        LOG.debug("Exception during pollForLoad; this should get noticed soon (" + e.message + ")!");
+        //DGF this is supposed to get logged later; log it at debug just in case
+        //LOG.exception(e);
+        this.pageLoadError = e;
+    }
+};
+
+BrowserBot.prototype._isSamePage = function(windowObject, originalDocument, originalLocation, originalHref, marker) {
+    var currentDocument = windowObject.document;
+    var currentLocation = windowObject.location;
+    var currentHref = currentLocation.href
+
+    var sameDoc = this._isSameDocument(originalDocument, currentDocument);
+
+    var sameLoc = (originalLocation === currentLocation);
+
+    // hash marks don't meant the page has loaded, so we need to strip them off if they exist...
+    var currentHash = currentHref.indexOf('#');
+    if (currentHash > 0) {
+        currentHref = currentHref.substring(0, currentHash);
+    }
+    var originalHash = originalHref.indexOf('#');
+    if (originalHash > 0) {
+        originalHref = originalHref.substring(0, originalHash);
+    }
+    LOG.debug("_isSamePage: currentHref: " + currentHref);
+    LOG.debug("_isSamePage: originalHref: " + originalHref);
+
+    var sameHref = (originalHref === currentHref);
+    var markedLoc = currentLocation[marker];
+
+    if (browserVersion.isKonqueror || browserVersion.isSafari) {
+        // the mark disappears too early on these browsers
+        markedLoc = true;
+    }
+
+    // since this is some _very_ important logic, especially for PI and multiWindow mode, we should log all these out
+    LOG.debug("_isSamePage: sameDoc: " + sameDoc);
+    LOG.debug("_isSamePage: sameLoc: " + sameLoc);
+    LOG.debug("_isSamePage: sameHref: " + sameHref);
+    LOG.debug("_isSamePage: markedLoc: " + markedLoc);
+
+    return sameDoc && sameLoc && sameHref && markedLoc
+};
+
+BrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
+    return originalDocument === currentDocument;
+};
+
+
+BrowserBot.prototype.getReadyState = function(windowObject, currentDocument) {
+    var rs = currentDocument.readyState;
+    if (rs == null) {
+       if ((this.buttonWindow!=null && this.buttonWindow.document.readyState == null) // not proxy injection mode (and therefore buttonWindow isn't null)
+       || (top.document.readyState == null)) {                                               // proxy injection mode (and therefore everything's in the top window, but buttonWindow doesn't exist)
+            // uh oh!  we're probably on Firefox with no readyState extension installed!
+            // We'll have to just take a guess as to when the document is loaded; this guess
+            // will never be perfect. :-(
+            if (typeof currentDocument.getElementsByTagName != 'undefined'
+                    && typeof currentDocument.getElementById != 'undefined'
+                    && ( currentDocument.getElementsByTagName('body')[0] != null
+                    || currentDocument.body != null )) {
+                if (windowObject.frameElement && windowObject.location.href == "about:blank" && windowObject.frameElement.src != "about:blank") {
+                    LOG.info("getReadyState not loaded, frame location was about:blank, but frame src = " + windowObject.frameElement.src);
+                    return null;
+                }
+                LOG.debug("getReadyState = windowObject.frames.length = " + windowObject.frames.length);
+                for (var i = 0; i < windowObject.frames.length; i++) {
+                    LOG.debug("i = " + i);
+                    if (this.getReadyState(windowObject.frames[i], windowObject.frames[i].document) != 'complete') {
+                        LOG.debug("getReadyState aha! the nested frame " + windowObject.frames[i].name + " wasn't ready!");
+                        return null;
+                    }
+                }
+
+                rs = 'complete';
+            } else {
+                LOG.debug("pollForLoad readyState was null and DOM appeared to not be ready yet");
+            }
+        }
+    }
+    else if (rs == "loading" && browserVersion.isIE) {
+        LOG.debug("pageUnloading = true!!!!");
+        this.pageUnloading = true;
+    }
+    LOG.debug("getReadyState returning " + rs);
+    return rs;
+};
+
+/** This function isn't used normally, but was the way we used to schedule pollers:
+ asynchronously executed autonomous units.  This is deprecated, but remains here
+ for future reference.
+ */
+BrowserBot.prototype.XXXreschedulePoller = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
+    var self = this;
+    window.setTimeout(function() {
+        self.pollForLoad(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+    }, 500);
+};
+
+/** This function isn't used normally, but is useful for debugging asynchronous pollers
+ * To enable it, rename it to "reschedulePoller", so it will override the
+ * existing reschedulePoller function
+ */
+BrowserBot.prototype.XXXreschedulePoller = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
+    var doc = this.buttonWindow.document;
+    var button = doc.createElement("button");
+    var buttonName = doc.createTextNode(marker + " - " + windowObject.name);
+    button.appendChild(buttonName);
+    var tools = doc.getElementById("tools");
+    var self = this;
+    button.onclick = function() {
+        tools.removeChild(button);
+        self.pollForLoad(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+    };
+    tools.appendChild(button);
+    window.setTimeout(button.onclick, 500);
+};
+
+BrowserBot.prototype.reschedulePoller = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
+    var self = this;
+    var pollerFunction = function() {
+        self.pollForLoad(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+    };
+    this.windowPollers.push(pollerFunction);
+};
+
+BrowserBot.prototype.runScheduledPollers = function() {
+    LOG.debug("runScheduledPollers");
+    var oldPollers = this.windowPollers;
+    this.windowPollers = new Array();
+    for (var i = 0; i < oldPollers.length; i++) {
+        oldPollers[i].call();
+    }
+    LOG.debug("runScheduledPollers DONE");
+};
+
+BrowserBot.prototype.isPollingForLoad = function(win) {
+    var marker;
+    var frameElement = this._getFrameElement(win);
+    var htaSubFrame = this._isHTASubFrame(win);
+    if (frameElement && !htaSubFrame) {
+	marker = frameElement["frame"+this.uniqueId];
+    } else {
+        marker = win[this.uniqueId];
+    }
+    if (!marker) {
+        LOG.debug("isPollingForLoad false, missing uniqueId " + this.uniqueId + ": " + marker);
+        return false;
+    }
+    if (!this.pollingForLoad[marker]) {
+        LOG.debug("isPollingForLoad false, this.pollingForLoad[" + marker + "]: " + this.pollingForLoad[marker]);
+        return false;
+    }
+    return marker;
+};
+
+BrowserBot.prototype.getWindowByName = function(windowName, doNotModify) {
+    LOG.debug("getWindowByName(" + windowName + ")");
+    // First look in the map of opened windows
+    var targetWindow = this.openedWindows[windowName];
+    if (!targetWindow) {
+        targetWindow = this.topWindow[windowName];
+    }
+    if (!targetWindow && windowName == "_blank") {
+        for (var winName in this.openedWindows) {
+            // _blank can match selenium_blank*, if it looks like it's OK (valid href, not closed)
+            if (/^selenium_blank/.test(winName)) {
+                targetWindow = this.openedWindows[winName];
+                var ok;
+                try {
+                    if (!this._windowClosed(targetWindow)) {
+                        ok = targetWindow.location.href;
+                    }
+                } catch (e) {}
+                if (ok) break;
+            }
+        }
+    }
+    if (!targetWindow) {
+        throw new SeleniumError("Window does not exist");
+    }
+    if (browserVersion.isHTA) {
+        try {
+            targetWindow.location.href;
+        } catch (e) {
+            targetWindow = window.open("", targetWindow.name);
+            this.openedWindows[targetWindow.name] = targetWindow;
+        }
+    }
+    if (!doNotModify) {
+        this._modifyWindow(targetWindow);
+    }
+    return targetWindow;
+};
+
+/**
+ * Find a window name from the window title.
+ */
+BrowserBot.prototype.getWindowNameByTitle = function(windowTitle) {
+    LOG.debug("getWindowNameByTitle(" + windowTitle + ")");
+
+    // First look in the map of opened windows and iterate them
+    for (var windowName in this.openedWindows) {
+        var targetWindow = this.openedWindows[windowName];
+
+        // If the target window's title is our title
+        try {
+            // TODO implement Pattern Matching here
+            if (!this._windowClosed(targetWindow) &&
+                targetWindow.document.title == windowTitle) {
+                return windowName;
+            }
+        } catch (e) {
+            // You'll often get Permission Denied errors here in IE
+            // eh, if we can't read this window's title,
+            // it's probably not available to us right now anyway
+        }
+    }
+    
+    try {
+        if (this.topWindow.document.title == windowTitle) {
+            return "";
+        }
+    } catch (e) {} // IE Perm denied
+
+    throw new SeleniumError("Could not find window with title " + windowTitle);
+};
+
+BrowserBot.prototype.getCurrentWindow = function(doNotModify) {
+    if (this.proxyInjectionMode) {
+        return window;
+    }
+    var testWindow = this.currentWindow;
+    if (!doNotModify) {
+        this._modifyWindow(testWindow);
+        LOG.debug("getCurrentWindow newPageLoaded = false");
+        this.newPageLoaded = false;
+    }
+    testWindow = this._handleClosedSubFrame(testWindow, doNotModify);
+    return testWindow;
+};
+
+BrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
+    if (this.proxyInjectionMode) {
+        return testWindow;
+    }
+
+    if (this.isSubFrameSelected) {
+        var missing = true;
+        if (testWindow.parent && testWindow.parent.frames && testWindow.parent.frames.length) {
+            for (var i = 0; i < testWindow.parent.frames.length; i++) {
+                if (testWindow.parent.frames[i] == testWindow) {
+                    missing = false;
+                    break;
+                }
+            }
+        }
+        if (missing) {
+            LOG.warn("Current subframe appears to have closed; selecting top frame");
+            this.selectFrame("relative=top");
+            return this.getCurrentWindow(doNotModify);
+        }
+    } else if (this._windowClosed(testWindow)) {
+        var closedError = new SeleniumError("Current window or frame is closed!");
+        closedError.windowClosed = true;
+        throw closedError;
+    }
+    return testWindow;
+};
+
+BrowserBot.prototype.highlight = function (element, force) {
+    if (force || this.shouldHighlightLocatedElement) {
+        try {
+            highlight(element);
+        } catch (e) {} // DGF element highlighting is low-priority and possibly dangerous
+    }
+    return element;
+}
+
+BrowserBot.prototype.setShouldHighlightElement = function (shouldHighlight) {
+    this.shouldHighlightLocatedElement = shouldHighlight;
+}
+
+/*****************************************************************/
+/* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
+
+
+BrowserBot.prototype._registerAllLocatorFunctions = function() {
+    // TODO - don't do this in the constructor - only needed once ever
+    this.locationStrategies = {};
+    for (var functionName in this) {
+        var result = /^locateElementBy([A-Z].+)$/.exec(functionName);
+        if (result != null) {
+            var locatorFunction = this[functionName];
+            if (typeof(locatorFunction) != 'function') {
+                continue;
+            }
+            // Use a specified prefix in preference to one generated from
+            // the function name
+            var locatorPrefix = locatorFunction.prefix || result[1].toLowerCase();
+            this.locationStrategies[locatorPrefix] = locatorFunction;
+        }
+    }
+
+    /**
+     * Find a locator based on a prefix.
+     */
+    this.findElementBy = function(locatorType, locator, inDocument, inWindow) {
+        var locatorFunction = this.locationStrategies[locatorType];
+        if (! locatorFunction) {
+            throw new SeleniumError("Unrecognised locator type: '" + locatorType + "'");
+        }
+        return locatorFunction.call(this, locator, inDocument, inWindow);
+    };
+
+    /**
+     * The implicit locator, that is used when no prefix is supplied.
+     */
+    this.locationStrategies['implicit'] = function(locator, inDocument, inWindow) {
+        if (locator.startsWith('//')) {
+            return this.locateElementByXPath(locator, inDocument, inWindow);
+        }
+        if (locator.startsWith('document.')) {
+            return this.locateElementByDomTraversal(locator, inDocument, inWindow);
+        }
+        return this.locateElementByIdentifier(locator, inDocument, inWindow);
+    };
+}
+
+BrowserBot.prototype.getDocument = function() {
+    return this.getCurrentWindow().document;
+}
+
+BrowserBot.prototype.getTitle = function() {
+    var t = this.getDocument().title;
+    if (typeof(t) == "string") {
+        t = t.trim();
+    }
+    return t;
+}
+
+/*
+ * Finds an element recursively in frames and nested frames
+ * in the specified document, using various lookup protocols
+ */
+BrowserBot.prototype.findElementRecursive = function(locatorType, locatorString, inDocument, inWindow) {
+
+    var element = this.findElementBy(locatorType, locatorString, inDocument, inWindow);
+    if (element != null) {
+        return element;
+    }
+
+    for (var i = 0; i < inWindow.frames.length; i++) {
+        element = this.findElementRecursive(locatorType, locatorString, inWindow.frames[i].document, inWindow.frames[i]);
+
+        if (element != null) {
+            return element;
+        }
+    }
+};
+
+/*
+* Finds an element on the current page, using various lookup protocols
+*/
+BrowserBot.prototype.findElementOrNull = function(locator, win) {
+    var locatorType = 'implicit';
+    var locatorString = locator;
+
+    // If there is a locator prefix, use the specified strategy
+    var result = locator.match(/^([A-Za-z]+)=(.+)/);
+    if (result) {
+        locatorType = result[1].toLowerCase();
+        locatorString = result[2];
+    }
+
+    if (win == null) {
+        win = this.getCurrentWindow();
+    }
+    var element = this.findElementRecursive(locatorType, locatorString, win.document, win);
+
+    if (element != null) {
+        return this.browserbot.highlight(element);
+    }
+
+    // Element was not found by any locator function.
+    return null;
+};
+
+BrowserBot.prototype.findElement = function(locator, win) {
+    var element = this.findElementOrNull(locator, win);
+    if (element == null) throw new SeleniumError("Element " + locator + " not found");
+    return element;
+}
+
+/**
+ * In non-IE browsers, getElementById() does not search by name.  Instead, we
+ * we search separately by id and name.
+ */
+BrowserBot.prototype.locateElementByIdentifier = function(identifier, inDocument, inWindow) {
+    return BrowserBot.prototype.locateElementById(identifier, inDocument, inWindow)
+            || BrowserBot.prototype.locateElementByName(identifier, inDocument, inWindow)
+            || null;
+};
+
+/**
+ * Find the element with id - can't rely on getElementById, coz it returns by name as well in IE..
+ */
+BrowserBot.prototype.locateElementById = function(identifier, inDocument, inWindow) {
+    var element = inDocument.getElementById(identifier);
+    if (element && element.id === identifier) {
+        return element;
+    }
+    else {
+        return null;
+    }
+};
+
+/**
+ * Find an element by name, refined by (optional) element-filter
+ * expressions.
+ */
+BrowserBot.prototype.locateElementByName = function(locator, document, inWindow) {
+    var elements = document.getElementsByTagName("*");
+
+    var filters = locator.split(' ');
+    filters[0] = 'name=' + filters[0];
+
+    while (filters.length) {
+        var filter = filters.shift();
+        elements = this.selectElements(filter, elements, 'value');
+    }
+
+    if (elements.length > 0) {
+        return elements[0];
+    }
+    return null;
+};
+
+/**
+ * Finds an element using by evaluating the specfied string.
+ */
+BrowserBot.prototype.locateElementByDomTraversal = function(domTraversal, document, window) {
+
+    var browserbot = this.browserbot;
+    var element = null;
+    try {
+        element = eval(domTraversal);
+    } catch (e) {
+        return null;
+    }
+
+    if (!element) {
+        return null;
+    }
+
+    return element;
+};
+BrowserBot.prototype.locateElementByDomTraversal.prefix = "dom";
+
+/**
+ * Finds an element identified by the xpath expression. Expressions _must_
+ * begin with "//".
+ */
+BrowserBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
+    // Trim any trailing "/": not valid xpath, and remains from attribute
+    // locator.
+    if (xpath.charAt(xpath.length - 1) == '/') {
+        xpath = xpath.slice(0, -1);
+    }
+
+    // Handle //tag
+    var match = xpath.match(/^\/\/(\w+|\*)$/);
+    if (match) {
+        var elements = inDocument.getElementsByTagName(match[1].toUpperCase());
+        if (elements == null) return null;
+        return elements[0];
+    }
+
+    // Handle //tag[@attr='value']
+    var match = xpath.match(/^\/\/(\w+|\*)\[@(\w+)=('([^\']+)'|"([^\"]+)")\]$/);
+    if (match) {
+        // We don't return the value without checking if it is null first.
+        // This is beacuse in some rare cases, this shortcut actually WONT work
+        // but that the full XPath WILL. A known case, for example, is in IE
+        // when the attribute is onclick/onblur/onsubmit/etc. Due to a bug in IE
+        // this shortcut won't work because the actual function is returned
+        // by getAttribute() rather than the text of the attribute.
+        var val = this._findElementByTagNameAndAttributeValue(
+                inDocument,
+                match[1].toUpperCase(),
+                match[2].toLowerCase(),
+                match[3].slice(1, -1)
+                );
+        if (val) {
+            return val;
+        }
+    }
+
+    // Handle //tag[text()='value']
+    var match = xpath.match(/^\/\/(\w+|\*)\[text\(\)=('([^\']+)'|"([^\"]+)")\]$/);
+    if (match) {
+        return this._findElementByTagNameAndText(
+                inDocument,
+                match[1].toUpperCase(),
+                match[2].slice(1, -1)
+                );
+    }
+
+    return this._findElementUsingFullXPath(xpath, inDocument);
+};
+
+BrowserBot.prototype._findElementByTagNameAndAttributeValue = function(
+        inDocument, tagName, attributeName, attributeValue
+        ) {
+    if (browserVersion.isIE && attributeName == "class") {
+        attributeName = "className";
+    }
+    var elements = inDocument.getElementsByTagName(tagName);
+    for (var i = 0; i < elements.length; i++) {
+        var elementAttr = elements[i].getAttribute(attributeName);
+        if (elementAttr == attributeValue) {
+            return elements[i];
+        }
+    }
+    return null;
+};
+
+BrowserBot.prototype._findElementByTagNameAndText = function(
+        inDocument, tagName, text
+        ) {
+    var elements = inDocument.getElementsByTagName(tagName);
+    for (var i = 0; i < elements.length; i++) {
+        if (getText(elements[i]) == text) {
+            return elements[i];
+        }
+    }
+    return null;
+};
+
+BrowserBot.prototype._namespaceResolver = function(prefix) {
+    if (prefix == 'html' || prefix == 'xhtml' || prefix == 'x') {
+        return 'http://www.w3.org/1999/xhtml';
+    } else if (prefix == 'mathml') {
+        return 'http://www.w3.org/1998/Math/MathML';
+    } else {
+        throw new Error("Unknown namespace: " + prefix + ".");
+    }
+}
+
+BrowserBot.prototype._findElementUsingFullXPath = function(xpath, inDocument, inWindow) {
+    // HUGE hack - remove namespace from xpath for IE
+    if (browserVersion.isIE) {
+        xpath = xpath.replace(/x:/g, '')
+    }
+
+    // Use document.evaluate() if it's available
+    if (this.allowNativeXpath && inDocument.evaluate) {
+        return inDocument.evaluate(xpath, inDocument, this._namespaceResolver, 0, null).iterateNext();
+    }
+
+    // If not, fall back to slower JavaScript implementation
+    // DGF set xpathdebug = true (using getEval, if you like) to turn on JS XPath debugging
+    //xpathdebug = true;
+    var context = new ExprContext(inDocument);
+    var xpathObj = xpathParse(xpath);
+    var xpathResult = xpathObj.evaluate(context);
+    if (xpathResult && xpathResult.value) {
+        return xpathResult.value[0];
+    }
+    return null;
+
+};
+
+// DGF this may LOOK identical to _findElementUsingFullXPath, but 
+// fEUFX pops the first element off the resulting nodelist; this function
+// wraps the xpath in a count() operator and returns the numeric value directly
+BrowserBot.prototype.evaluateXPathCount = function(xpath, inDocument) {
+    // HUGE hack - remove namespace from xpath for IE
+    if (browserVersion.isIE) {
+        xpath = xpath.replace(/x:/g, '')
+    }
+    xpath = new String(xpath);
+    if (xpath.indexOf("xpath=") == 0) {
+        xpath = xpath.substring(6); 
+    }
+    if (xpath.indexOf("count(") == 0) {
+        // DGF we COULD just fix this up for the user, but we might get it wrong (parens?)
+        throw new SeleniumError("XPath count expressions must not be wrapped in count() function: " + xpath);
+    }
+    
+    xpath="count("+xpath+")";
+
+    // Use document.evaluate() if it's available
+    if (this.allowNativeXpath && inDocument.evaluate) {
+        var result = inDocument.evaluate(xpath, inDocument, this._namespaceResolver, XPathResult.NUMBER_TYPE, null);
+        return result.numberValue;
+    }
+
+    // If not, fall back to slower JavaScript implementation
+    // DGF set xpathdebug = true (using getEval, if you like) to turn on JS XPath debugging
+    //xpathdebug = true;
+    var context = new ExprContext(inDocument);
+    var xpathObj = xpathParse(xpath);
+    var xpathResult = xpathObj.evaluate(context);
+    if (xpathResult && xpathResult.value) {
+        return xpathResult.value;
+    }
+    return 0;
+};
+
+/**
+ * Finds a link element with text matching the expression supplied. Expressions must
+ * begin with "link:".
+ */
+BrowserBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWindow) {
+    var links = inDocument.getElementsByTagName('a');
+    for (var i = 0; i < links.length; i++) {
+        var element = links[i];
+        if (PatternMatcher.matches(linkText, getText(element))) {
+            return element;
+        }
+    }
+    return null;
+};
+BrowserBot.prototype.locateElementByLinkText.prefix = "link";
+
+/**
+ * Returns an attribute based on an attribute locator. This is made up of an element locator
+ * suffixed with @attribute-name.
+ */
+BrowserBot.prototype.findAttribute = function(locator) {
+    // Split into locator + attributeName
+    var attributePos = locator.lastIndexOf("@");
+    var elementLocator = locator.slice(0, attributePos);
+    var attributeName = locator.slice(attributePos + 1);
+
+    // Find the element.
+    var element = this.findElement(elementLocator);
+
+    // Handle missing "class" attribute in IE.
+    if (browserVersion.isIE && attributeName == "class") {
+        attributeName = "className";
+    }
+
+    // Get the attribute value.
+    var attributeValue = element.getAttribute(attributeName);
+
+    return attributeValue ? attributeValue.toString() : null;
+};
+
+/*
+* Select the specified option and trigger the relevant events of the element.
+*/
+BrowserBot.prototype.selectOption = function(element, optionToSelect) {
+    triggerEvent(element, 'focus', false);
+    var changed = false;
+    for (var i = 0; i < element.options.length; i++) {
+        var option = element.options[i];
+        if (option.selected && option != optionToSelect) {
+            option.selected = false;
+            changed = true;
+        }
+        else if (!option.selected && option == optionToSelect) {
+            option.selected = true;
+            changed = true;
+        }
+    }
+
+    if (changed) {
+        triggerEvent(element, 'change', true);
+    }
+};
+
+/*
+* Select the specified option and trigger the relevant events of the element.
+*/
+BrowserBot.prototype.addSelection = function(element, option) {
+    this.checkMultiselect(element);
+    triggerEvent(element, 'focus', false);
+    if (!option.selected) {
+        option.selected = true;
+        triggerEvent(element, 'change', true);
+    }
+};
+
+/*
+* Select the specified option and trigger the relevant events of the element.
+*/
+BrowserBot.prototype.removeSelection = function(element, option) {
+    this.checkMultiselect(element);
+    triggerEvent(element, 'focus', false);
+    if (option.selected) {
+        option.selected = false;
+        triggerEvent(element, 'change', true);
+    }
+};
+
+BrowserBot.prototype.checkMultiselect = function(element) {
+    if (!element.multiple)
+    {
+        throw new SeleniumError("Not a multi-select");
+    }
+
+};
+
+BrowserBot.prototype.replaceText = function(element, stringValue) {
+    triggerEvent(element, 'focus', false);
+    triggerEvent(element, 'select', true);
+    var maxLengthAttr = element.getAttribute("maxLength");
+    var actualValue = stringValue;
+    if (maxLengthAttr != null) {
+        var maxLength = parseInt(maxLengthAttr);
+        if (stringValue.length > maxLength) {
+            actualValue = stringValue.substr(0, maxLength);
+        }
+    }
+
+    if (getTagName(element) == "body") {
+        if (element.ownerDocument && element.ownerDocument.designMode) {
+            var designMode = new String(element.ownerDocument.designMode).toLowerCase();
+            if (designMode = "on") {
+                // this must be a rich text control!
+                element.innerHTML = actualValue;
+            }
+        }
+    } else {
+        element.value = actualValue;
+    }
+    // DGF this used to be skipped in chrome URLs, but no longer.  Is xpcnativewrappers to blame?
+    try {
+        triggerEvent(element, 'change', true);
+    } catch (e) {}
+};
+
+BrowserBot.prototype.submit = function(formElement) {
+    var actuallySubmit = true;
+    this._modifyElementTarget(formElement);
+    if (formElement.onsubmit) {
+        if (browserVersion.isHTA) {
+            // run the code in the correct window so alerts are handled correctly even in HTA mode
+            var win = this.browserbot.getCurrentWindow();
+            var now = new Date().getTime();
+            var marker = 'marker' + now;
+            win[marker] = formElement;
+            win.setTimeout("var actuallySubmit = "+marker+".onsubmit();" +
+                "if (actuallySubmit) { " +
+                    marker+".submit(); " +
+                    "if ("+marker+".target && !/^_/.test("+marker+".target)) {"+
+                        "window.open('', "+marker+".target);"+
+                    "}"+
+                "};"+
+                marker+"=null", 0);
+            // pause for up to 2s while this command runs
+            var terminationCondition = function () {
+                return !win[marker];
+            }
+            return Selenium.decorateFunctionWithTimeout(terminationCondition, 2000);
+        } else {
+            actuallySubmit = formElement.onsubmit();
+            if (actuallySubmit) {
+                formElement.submit();
+                if (formElement.target && !/^_/.test(formElement.target)) {
+                    this.browserbot.openWindow('', formElement.target);
+                }
+            }
+        }
+    } else {
+        formElement.submit();
+    }
+}
+
+BrowserBot.prototype.clickElement = function(element, clientX, clientY) {
+       this._fireEventOnElement("click", element, clientX, clientY);
+};
+
+BrowserBot.prototype.doubleClickElement = function(element, clientX, clientY) {
+       this._fireEventOnElement("dblclick", element, clientX, clientY);
+};
+
+BrowserBot.prototype._modifyElementTarget = function(element) {
+    if (element.target) {
+        if (element.target == "_blank" || /^selenium_blank/.test(element.target) ) {
+            var tagName = getTagName(element);
+            if (tagName == "a" || tagName == "form") {
+                var newTarget = "selenium_blank" + Math.round(100000 * Math.random());
+                LOG.warn("Link has target '_blank', which is not supported in Selenium!  Randomizing target to be: " + newTarget);
+                this.browserbot.openWindow('', newTarget);
+                element.target = newTarget;
+            }
+        }
+    }
+}
+
+
+BrowserBot.prototype._handleClickingImagesInsideLinks = function(targetWindow, element) {
+    var itrElement = element;
+    while (itrElement != null) {
+        if (itrElement.href) {
+            targetWindow.location.href = itrElement.href;
+            break;
+        }
+        itrElement = itrElement.parentNode;
+    }
+}
+
+BrowserBot.prototype._getTargetWindow = function(element) {
+    var targetWindow = element.ownerDocument.defaultView;
+    if (element.target) {
+        targetWindow = this._getFrameFromGlobal(element.target);
+    }
+    return targetWindow;
+}
+
+BrowserBot.prototype._getFrameFromGlobal = function(target) {
+
+    if (target == "_top") {
+        return this.topFrame;
+    } else if (target == "_parent") {
+        return this.getCurrentWindow().parent;
+    } else if (target == "_blank") {
+        // TODO should this set cleverer window defaults?
+        return this.getCurrentWindow().open('', '_blank');
+    }
+    var frameElement = this.findElementBy("implicit", target, this.topFrame.document, this.topFrame);
+    if (frameElement) {
+        return frameElement.contentWindow;
+    }
+    var win = this.getWindowByName(target);
+    if (win) return win;
+    return this.getCurrentWindow().open('', target);
+}
+
+
+BrowserBot.prototype.bodyText = function() {
+    if (!this.getDocument().body) {
+        throw new SeleniumError("Couldn't access document.body.  Is this HTML page fully loaded?");
+    }
+    return getText(this.getDocument().body);
+};
+
+BrowserBot.prototype.getAllButtons = function() {
+    var elements = this.getDocument().getElementsByTagName('input');
+    var result = [];
+
+    for (var i = 0; i < elements.length; i++) {
+        if (elements[i].type == 'button' || elements[i].type == 'submit' || elements[i].type == 'reset') {
+            result.push(elements[i].id);
+        }
+    }
+
+    return result;
+};
+
+
+BrowserBot.prototype.getAllFields = function() {
+    var elements = this.getDocument().getElementsByTagName('input');
+    var result = [];
+
+    for (var i = 0; i < elements.length; i++) {
+        if (elements[i].type == 'text') {
+            result.push(elements[i].id);
+        }
+    }
+
+    return result;
+};
+
+BrowserBot.prototype.getAllLinks = function() {
+    var elements = this.getDocument().getElementsByTagName('a');
+    var result = [];
+
+    for (var i = 0; i < elements.length; i++) {
+        result.push(elements[i].id);
+    }
+
+    return result;
+};
+
+function isDefined(value) {
+    return typeof(value) != undefined;
+}
+
+BrowserBot.prototype.goBack = function() {
+    this.getCurrentWindow().history.back();
+};
+
+BrowserBot.prototype.goForward = function() {
+    this.getCurrentWindow().history.forward();
+};
+
+BrowserBot.prototype.close = function() {
+    if (browserVersion.isChrome || browserVersion.isSafari || browserVersion.isOpera) {
+        this.getCurrentWindow().close();
+    } else {
+        this.getCurrentWindow().eval("window.close();");
+    }
+};
+
+BrowserBot.prototype.refresh = function() {
+    this.getCurrentWindow().location.reload(true);
+};
+
+/**
+ * Refine a list of elements using a filter.
+ */
+BrowserBot.prototype.selectElementsBy = function(filterType, filter, elements) {
+    var filterFunction = BrowserBot.filterFunctions[filterType];
+    if (! filterFunction) {
+        throw new SeleniumError("Unrecognised element-filter type: '" + filterType + "'");
+    }
+
+    return filterFunction(filter, elements);
+};
+
+BrowserBot.filterFunctions = {};
+
+BrowserBot.filterFunctions.name = function(name, elements) {
+    var selectedElements = [];
+    for (var i = 0; i < elements.length; i++) {
+        if (elements[i].name === name) {
+            selectedElements.push(elements[i]);
+        }
+    }
+    return selectedElements;
+};
+
+BrowserBot.filterFunctions.value = function(value, elements) {
+    var selectedElements = [];
+    for (var i = 0; i < elements.length; i++) {
+        if (elements[i].value === value) {
+            selectedElements.push(elements[i]);
+        }
+    }
+    return selectedElements;
+};
+
+BrowserBot.filterFunctions.index = function(index, elements) {
+    index = Number(index);
+    if (isNaN(index) || index < 0) {
+        throw new SeleniumError("Illegal Index: " + index);
+    }
+    if (elements.length <= index) {
+        throw new SeleniumError("Index out of range: " + index);
+    }
+    return [elements[index]];
+};
+
+BrowserBot.prototype.selectElements = function(filterExpr, elements, defaultFilterType) {
+
+    var filterType = (defaultFilterType || 'value');
+
+    // If there is a filter prefix, use the specified strategy
+    var result = filterExpr.match(/^([A-Za-z]+)=(.+)/);
+    if (result) {
+        filterType = result[1].toLowerCase();
+        filterExpr = result[2];
+    }
+
+    return this.selectElementsBy(filterType, filterExpr, elements);
+};
+
+/**
+ * Find an element by class
+ */
+BrowserBot.prototype.locateElementByClass = function(locator, document) {
+    return elementFindFirstMatchingChild(document,
+            function(element) {
+                return element.className == locator
+            }
+            );
+}
+
+/**
+ * Find an element by alt
+ */
+BrowserBot.prototype.locateElementByAlt = function(locator, document) {
+    return elementFindFirstMatchingChild(document,
+            function(element) {
+                return element.alt == locator
+            }
+            );
+}
+
+/**
+ * Find an element by css selector
+ */
+BrowserBot.prototype.locateElementByCss = function(locator, document) {
+    var elements = cssQuery(locator, document);
+    if (elements.length != 0)
+        return elements[0];
+    return null;
+}
+
+
+/*****************************************************************/
+/* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
+
+function MozillaBrowserBot(frame) {
+    BrowserBot.call(this, frame);
+}
+objectExtend(MozillaBrowserBot.prototype, BrowserBot.prototype);
+
+function KonquerorBrowserBot(frame) {
+    BrowserBot.call(this, frame);
+}
+objectExtend(KonquerorBrowserBot.prototype, BrowserBot.prototype);
+
+KonquerorBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
+    // Window doesn't fire onload event when setting src to the current value,
+    // so we set it to blank first.
+    iframe.src = "about:blank";
+    iframe.src = location;
+};
+
+KonquerorBrowserBot.prototype.setOpenLocation = function(win, loc) {
+    // Window doesn't fire onload event when setting src to the current value,
+    // so we just refresh in that case instead.
+    loc = absolutify(loc, this.baseUrl);
+    loc = canonicalize(loc);
+    var startUrl = win.location.href;
+    if ("about:blank" != win.location.href) {
+        var startLoc = parseUrl(win.location.href);
+        startLoc.hash = null;
+        var startUrl = reassembleLocation(startLoc);
+    }
+    LOG.debug("startUrl="+startUrl);
+    LOG.debug("win.location.href="+win.location.href);
+    LOG.debug("loc="+loc);
+    if (startUrl == loc) {
+        LOG.debug("opening exact same location");
+        this.refresh();
+    } else {
+        LOG.debug("locations differ");
+        win.location.href = loc;
+    }
+    // force the current polling thread to detect a page load
+    var marker = this.isPollingForLoad(win);
+    if (marker) {
+        delete win.location[marker];
+    }
+};
+
+KonquerorBrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
+    // under Konqueror, there may be this case:
+    // originalDocument and currentDocument are different objects
+    // while their location are same.
+    if (originalDocument) {
+        return originalDocument.location == currentDocument.location
+    } else {
+        return originalDocument === currentDocument;
+    }
+};
+
+function SafariBrowserBot(frame) {
+    BrowserBot.call(this, frame);
+}
+objectExtend(SafariBrowserBot.prototype, BrowserBot.prototype);
+
+SafariBrowserBot.prototype.setIFrameLocation = KonquerorBrowserBot.prototype.setIFrameLocation;
+SafariBrowserBot.prototype.setOpenLocation = KonquerorBrowserBot.prototype.setOpenLocation;
+
+
+function OperaBrowserBot(frame) {
+    BrowserBot.call(this, frame);
+}
+objectExtend(OperaBrowserBot.prototype, BrowserBot.prototype);
+OperaBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
+    if (iframe.src == location) {
+        iframe.src = location + '?reload';
+    } else {
+        iframe.src = location;
+    }
+}
+
+function IEBrowserBot(frame) {
+    BrowserBot.call(this, frame);
+}
+objectExtend(IEBrowserBot.prototype, BrowserBot.prototype);
+
+IEBrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
+    if (this.proxyInjectionMode) {
+        return testWindow;
+    }
+
+    try {
+        testWindow.location.href;
+        this.permDenied = 0;
+    } catch (e) {
+        this.permDenied++;
+    }
+    if (this._windowClosed(testWindow) || this.permDenied > 4) {
+        if (this.isSubFrameSelected) {
+            LOG.warn("Current subframe appears to have closed; selecting top frame");
+            this.selectFrame("relative=top");
+            return this.getCurrentWindow(doNotModify);
+        } else {
+            var closedError = new SeleniumError("Current window or frame is closed!");
+            closedError.windowClosed = true;
+            throw closedError;
+        }
+    }
+    return testWindow;
+};
+
+IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
+    BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
+
+    // we will call the previous version of this method from within our own interception
+    oldShowModalDialog = windowToModify.showModalDialog;
+
+    windowToModify.showModalDialog = function(url, args, features) {
+        // Get relative directory to where TestRunner.html lives
+        // A risky assumption is that the user's TestRunner is named TestRunner.html
+        var doc_location = document.location.toString();
+        var end_of_base_ref = doc_location.indexOf('TestRunner.html');
+        var base_ref = doc_location.substring(0, end_of_base_ref);
+        var runInterval = '';
+        
+        // Only set run interval if options is defined
+        if (typeof(window.runOptions) != undefined) {
+            runInterval = "&runInterval=" + runOptions.runInterval;
+        }
+            
+        var testRunnerURL = "TestRunner.html?auto=true&singletest=" 
+            + escape(browserBot.modalDialogTest)
+            + "&autoURL=" 
+            + escape(url) 
+            + runInterval;
+        var fullURL = base_ref + testRunnerURL;
+        browserBot.modalDialogTest = null;
+
+        // If using proxy injection mode
+        if (this.proxyInjectionMode) {
+            var sessionId = runOptions.getSessionId();
+            if (sessionId == undefined) {
+                sessionId = injectedSessionId;
+            }
+            if (sessionId != undefined) {
+                LOG.debug("Invoking showModalDialog and injecting URL " + fullURL);
+            }
+            fullURL = url;
+        }
+        var returnValue = oldShowModalDialog(fullURL, args, features);
+        return returnValue;
+    };
+};
+
+IEBrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowObject) {
+    this.pageUnloading = false;
+    var self = this;
+    var pageUnloadDetector = function() {
+        self.pageUnloading = true;
+    };
+    windowObject.attachEvent("onbeforeunload", pageUnloadDetector);
+    BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads.call(this, windowObject);
+};
+
+IEBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
+    LOG.debug("IEBrowserBot.pollForLoad: " + marker);
+    if (!this.permDeniedCount[marker]) this.permDeniedCount[marker] = 0;
+    BrowserBot.prototype.pollForLoad.call(this, loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+    if (this.pageLoadError) {
+        if (this.pageUnloading) {
+            var self = this;
+            LOG.debug("pollForLoad UNLOADING (" + marker + "): caught exception while firing events on unloading page: " + this.pageLoadError.message);
+            this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+            this.pageLoadError = null;
+            return;
+        } else if (((this.pageLoadError.message == "Permission denied") || (/^Access is denied/.test(this.pageLoadError.message)))
+                && this.permDeniedCount[marker]++ < 8) {
+            if (this.permDeniedCount[marker] > 4) {
+                var canAccessThisWindow;
+                var canAccessCurrentlySelectedWindow;
+                try {
+                    windowObject.location.href;
+                    canAccessThisWindow = true;
+                } catch (e) {}
+                try {
+                    this.getCurrentWindow(true).location.href;
+                    canAccessCurrentlySelectedWindow = true;
+                } catch (e) {}
+                if (canAccessCurrentlySelectedWindow & !canAccessThisWindow) {
+                    LOG.debug("pollForLoad (" + marker + ") ABORTING: " + this.pageLoadError.message + " (" + this.permDeniedCount[marker] + "), but the currently selected window is fine");
+                    // returning without rescheduling
+                    this.pageLoadError = null;
+                    return;
+                }
+            }
+
+            var self = this;
+            LOG.debug("pollForLoad (" + marker + "): " + this.pageLoadError.message + " (" + this.permDeniedCount[marker] + "), waiting to see if it goes away");
+            this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+            this.pageLoadError = null;
+            return;
+        }
+        //handy for debugging!
+        //throw this.pageLoadError;
+    }
+};
+
+IEBrowserBot.prototype._windowClosed = function(win) {
+    try {
+        var c = win.closed;
+        // frame windows claim to be non-closed when their parents are closed
+        // but you can't access their document objects in that case
+        if (!c) {
+            try {
+                win.document;
+            } catch (de) {
+                if (de.message == "Permission denied") {
+                    // the window is probably unloading, which means it's probably not closed yet
+                    return false;
+                }
+                else if (/^Access is denied/.test(de.message)) {
+                    // rare variation on "Permission denied"?
+                    LOG.debug("IEBrowserBot.windowClosed: got " + de.message + " (this.pageUnloading=" + this.pageUnloading + "); assuming window is unloading, probably not closed yet");
+                    return false;
+                } else {
+                    // this is probably one of those frame window situations
+                    LOG.debug("IEBrowserBot.windowClosed: couldn't read win.document, assume closed: " + de.message + " (this.pageUnloading=" + this.pageUnloading + ")");
+                    return true;
+                }
+            }
+        }
+        if (c == null) {
+            LOG.debug("IEBrowserBot.windowClosed: win.closed was null, assuming closed");
+            return true;
+        }
+        return c;
+    } catch (e) {
+        LOG.debug("IEBrowserBot._windowClosed: Got an exception trying to read win.closed; we'll have to take a guess!");
+
+        if (browserVersion.isHTA) {
+            if (e.message == "Permission denied") {
+                // the window is probably unloading, which means it's not closed yet
+                return false;
+            } else {
+                // there's a good chance that we've lost contact with the window object if it is closed
+                return true;
+            }
+        } else {
+            // the window is probably unloading, which means it's not closed yet
+            return false;
+        }
+    }
+};
+
+/**
+ * In IE, getElementById() also searches by name - this is an optimisation for IE.
+ */
+IEBrowserBot.prototype.locateElementByIdentifer = function(identifier, inDocument, inWindow) {
+    return inDocument.getElementById(identifier);
+};
+
+IEBrowserBot.prototype._findElementByTagNameAndAttributeValue = function(
+        inDocument, tagName, attributeName, attributeValue
+        ) {
+    if (attributeName == "class") {
+        attributeName = "className";
+    }
+    var elements = inDocument.getElementsByTagName(tagName);
+    for (var i = 0; i < elements.length; i++) {
+        var elementAttr = elements[i].getAttribute(attributeName);
+        if (elementAttr == attributeValue) {
+            return elements[i];
+        }
+        // DGF SEL-347, IE6 URL-escapes javascript href attribute
+        if (!elementAttr) continue;
+        elementAttr = unescape(new String(elementAttr));
+        if (elementAttr == attributeValue) {
+            return elements[i];
+        }
+    }
+    return null;
+};
+
+SafariBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
+    BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
+
+    var originalOpen = windowToModify.open;
+    /*
+     * Safari seems to be broken, so that when we manually trigger the onclick method
+     * of a button/href, any window.open calls aren't resolved relative to the app location.
+     * So here we replace the open() method with one that does resolve the url correctly.
+     */
+    windowToModify.open = function(url, windowName, windowFeatures, replaceFlag) {
+
+        if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("/")) {
+            return originalOpen(url, windowName, windowFeatures, replaceFlag);
+        }
+
+        // Reduce the current path to the directory
+        var currentPath = windowToModify.location.pathname || "/";
+        currentPath = currentPath.replace(/\/[^\/]*$/, "/");
+
+        // Remove any leading "./" from the new url.
+        url = url.replace(/^\.\//, "");
+
+        newUrl = currentPath + url;
+
+        var openedWindow = originalOpen(newUrl, windowName, windowFeatures, replaceFlag);
+        LOG.debug("window.open call intercepted; window ID (which you can use with selectWindow()) is \"" +  windowName + "\"");
+        if (windowName!=null) {
+            openedWindow["seleniumWindowName"] = windowName;
+        }
+        return openedWindow;
+    };
+};
+
+MozillaBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+    var win = this.getCurrentWindow();
+    triggerEvent(element, 'focus', false);
+
+    // Add an event listener that detects if the default action has been prevented.
+    // (This is caused by a javascript onclick handler returning false)
+    // we capture the whole event, rather than the getPreventDefault() state at the time,
+    // because we need to let the entire event bubbling and capturing to go through
+    // before making a decision on whether we should force the href
+    var savedEvent = null;
+
+    element.addEventListener(eventType, function(evt) {
+        savedEvent = evt;
+    }, false);
+
+    this._modifyElementTarget(element);
+
+    // Trigger the event.
+    this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+
+    if (this._windowClosed(win)) {
+        return;
+    }
+
+    // Perform the link action if preventDefault was set.
+    // In chrome URL, the link action is already executed by triggerMouseEvent.
+    if (!browserVersion.isChrome && savedEvent != null && !savedEvent.getPreventDefault()) {
+        var targetWindow = this.browserbot._getTargetWindow(element);
+        if (element.href) {
+            targetWindow.location.href = element.href;
+        } else {
+            this.browserbot._handleClickingImagesInsideLinks(targetWindow, element);
+        }
+    }
+
+};
+
+
+OperaBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+    var win = this.getCurrentWindow();
+    triggerEvent(element, 'focus', false);
+
+    this._modifyElementTarget(element);
+
+    // Trigger the click event.
+    this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+
+    if (this._windowClosed(win)) {
+        return;
+    }
+
+};
+
+
+KonquerorBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+    var win = this.getCurrentWindow();
+    triggerEvent(element, 'focus', false);
+
+    this._modifyElementTarget(element);
+
+    if (element[eventType]) {
+        element[eventType]();
+    }
+    else {
+        this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+    }
+
+    if (this._windowClosed(win)) {
+        return;
+    }
+
+};
+
+SafariBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+    triggerEvent(element, 'focus', false);
+    var wasChecked = element.checked;
+
+    this._modifyElementTarget(element);
+
+    // For form element it is simple.
+    if (element[eventType]) {
+        element[eventType]();
+    }
+    // For links and other elements, event emulation is required.
+    else {
+        var targetWindow = this.browserbot._getTargetWindow(element);
+        // todo: deal with anchors?
+        this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+
+    }
+
+};
+
+SafariBrowserBot.prototype.refresh = function() {
+    var win = this.getCurrentWindow();
+    if (win.location.hash) {
+        // DGF Safari refuses to refresh when there's a hash symbol in the URL
+        win.location.hash = "";
+        var actuallyReload = function() {
+            win.location.reload(true);
+        }
+        window.setTimeout(actuallyReload, 1);
+    } else {
+        win.location.reload(true);
+    }
+};
+
+IEBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+    var win = this.getCurrentWindow();
+    triggerEvent(element, 'focus', false);
+
+    var wasChecked = element.checked;
+
+    // Set a flag that records if the page will unload - this isn't always accurate, because
+    // <a href="javascript:alert('foo'):"> triggers the onbeforeunload event, even thought the page won't unload
+    var pageUnloading = false;
+    var pageUnloadDetector = function() {
+        pageUnloading = true;
+    };
+    win.attachEvent("onbeforeunload", pageUnloadDetector);
+    this._modifyElementTarget(element);
+    if (element[eventType]) {
+        element[eventType]();
+    }
+    else {
+        this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+    }
+
+
+    // If the page is going to unload - still attempt to fire any subsequent events.
+    // However, we can't guarantee that the page won't unload half way through, so we need to handle exceptions.
+    try {
+        win.detachEvent("onbeforeunload", pageUnloadDetector);
+
+        if (this._windowClosed(win)) {
+            return;
+        }
+
+        // Onchange event is not triggered automatically in IE.
+        if (isDefined(element.checked) && wasChecked != element.checked) {
+            triggerEvent(element, 'change', true);
+        }
+
+    }
+    catch (e) {
+        // If the page is unloading, we may get a "Permission denied" or "Unspecified error".
+        // Just ignore it, because the document may have unloaded.
+        if (pageUnloading) {
+            LOG.logHook = function() {
+            };
+            LOG.warn("Caught exception when firing events on unloading page: " + e.message);
+            return;
+        }
+        throw e;
+    }
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-browserdetect.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-browserdetect.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-browserdetect.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+// Although it's generally better web development practice not to use
+// browser-detection (feature detection is better), the subtle browser
+// differences that Selenium has to work around seem to make it
+// necessary. Maybe as we learn more about what we need, we can do this in
+// a more "feature-centric" rather than "browser-centric" way.
+
+var BrowserVersion = function() {
+    this.name = navigator.appName;
+
+    if (navigator.userAgent.indexOf('Mac OS X') != -1) {
+        this.isOSX = true;
+    }
+
+    if (navigator.userAgent.indexOf('Windows NT 6') != -1) {
+        this.isVista = true;
+    }
+
+    if (window.opera != null) {
+        this.browser = BrowserVersion.OPERA;
+        this.isOpera = true;
+        return;
+    }
+    
+    var _getQueryParameter = function(searchKey) {
+        var str = location.search.substr(1);
+        if (str == null) return null;
+        var clauses = str.split('&');
+        for (var i = 0; i < clauses.length; i++) {
+            var keyValuePair = clauses[i].split('=', 2);
+            var key = unescape(keyValuePair[0]);
+            if (key == searchKey) {
+                return unescape(keyValuePair[1]);
+            }
+        }
+        return null;
+    };
+    
+    var self = this;
+    
+    var checkChrome = function() {
+        var loc = window.document.location.href;
+        try {
+            loc = window.top.document.location.href;
+            if (/^chrome:\/\//.test(loc)) {
+                self.isChrome = true;
+            } else {
+                self.isChrome = false;
+            }
+        } catch (e) {
+            // can't see the top (that means we might be chrome, but it's impossible to be sure)
+            self.isChromeDetectable = "no, top location couldn't be read in this window";
+            if (_getQueryParameter('thisIsChrome')) {
+                self.isChrome = true;
+            } else {
+                self.isChrome = false;
+            }
+        }
+        
+        
+    }
+    
+    
+
+    if (this.name == "Microsoft Internet Explorer") {
+        this.browser = BrowserVersion.IE;
+        this.isIE = true;
+        try {
+            if (window.top.SeleniumHTARunner && window.top.document.location.pathname.match(/.hta$/i)) {
+                this.isHTA = true;
+            }
+        } catch (e) {
+            this.isHTADetectable = "no, top location couldn't be read in this window";
+            if (_getQueryParameter('thisIsHTA')) {
+                self.isHTA = true;
+            } else {
+                self.isHTA = false;
+            }
+        }
+        if ("0" == navigator.appMinorVersion) {
+            this.preSV1 = true;
+            if (navigator.appVersion.match(/MSIE 6.0/)) {
+            	this.appearsToBeBrokenInitialIE6 = true;
+            }
+        }
+        return;
+    }
+
+    if (navigator.userAgent.indexOf('Safari') != -1) {
+        this.browser = BrowserVersion.SAFARI;
+        this.isSafari = true;
+        this.khtml = true;
+        return;
+    }
+
+    if (navigator.userAgent.indexOf('Konqueror') != -1) {
+        this.browser = BrowserVersion.KONQUEROR;
+        this.isKonqueror = true;
+        this.khtml = true;
+        return;
+    }
+
+    if (navigator.userAgent.indexOf('Firefox') != -1) {
+        this.browser = BrowserVersion.FIREFOX;
+        this.isFirefox = true;
+        this.isGecko = true;
+        var result = /.*Firefox\/([\d\.]+).*/.exec(navigator.userAgent);
+        if (result) {
+            this.firefoxVersion = result[1];
+        }
+        checkChrome();
+        return;
+    }
+
+    if (navigator.userAgent.indexOf('Gecko') != -1) {
+        this.browser = BrowserVersion.MOZILLA;
+        this.isMozilla = true;
+        this.isGecko = true;
+        checkChrome();
+        return;
+    }
+
+    this.browser = BrowserVersion.UNKNOWN;
+}
+
+BrowserVersion.OPERA = "Opera";
+BrowserVersion.IE = "IE";
+BrowserVersion.KONQUEROR = "Konqueror";
+BrowserVersion.SAFARI = "Safari";
+BrowserVersion.FIREFOX = "Firefox";
+BrowserVersion.MOZILLA = "Mozilla";
+BrowserVersion.UNKNOWN = "Unknown";
+
+var browserVersion = new BrowserVersion();

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-commandhandlers.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-commandhandlers.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-commandhandlers.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,377 @@
+/*
+* Copyright 2004 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*/
+
+// A naming convention used in this file:
+//
+//
+//   - a "seleniumApi" is an instance of the Selenium object, defined in selenium-api.js.
+//
+//   - a "Method" is an unbound function whose target must be supplied when it's called, ie.
+//     it should be invoked using Function.call() or Function.apply()
+//
+//   - a "Block" is a function that has been bound to a target object, so can be called invoked directly
+//     (or with a null target)
+//
+//   - "CommandHandler" is effectively an abstract base for
+//     various handlers including ActionHandler, AccessorHandler and AssertHandler.
+//     Subclasses need to implement an execute(seleniumApi, command) function,
+//     where seleniumApi is the Selenium object, and command a SeleniumCommand object.
+//
+//   - Handlers will return a "result" object (ActionResult, AccessorResult, AssertResult).
+//     ActionResults may contain a .terminationCondition function which is run by 
+//     -executionloop.js after the command is run; we'll run it over and over again
+//     until it returns true or the .terminationCondition throws an exception.
+//     AccessorResults will contain the results of running getter (e.g. getTitle returns
+//     the title as a string).
+
+var CommandHandlerFactory = classCreate();
+objectExtend(CommandHandlerFactory.prototype, {
+
+    initialize: function() {
+        this.handlers = {};
+    },
+
+    registerAction: function(name, actionBlock, wait, dontCheckAlertsAndConfirms) {
+        this.handlers[name] = new ActionHandler(actionBlock, wait, dontCheckAlertsAndConfirms);
+    },
+
+    registerAccessor: function(name, accessBlock) {
+        this.handlers[name] = new AccessorHandler(accessBlock);
+    },
+
+    registerAssert: function(name, assertBlock, haltOnFailure) {
+        this.handlers[name] = new AssertHandler(assertBlock, haltOnFailure);
+    },
+
+    getCommandHandler: function(name) {
+        return this.handlers[name];
+    },
+
+    _registerAllAccessors: function(seleniumApi) {
+        // Methods of the form getFoo(target) result in commands:
+        // getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo
+        // storeFoo, waitForFoo, and waitForNotFoo.
+        for (var functionName in seleniumApi) {
+            var match = /^(get|is)([A-Z].+)$/.exec(functionName);
+            if (match) {
+                var accessMethod = seleniumApi[functionName];
+                var accessBlock = fnBind(accessMethod, seleniumApi);
+                var baseName = match[2];
+                var isBoolean = (match[1] == "is");
+                var requiresTarget = (accessMethod.length == 1);
+
+                this.registerAccessor(functionName, accessBlock);
+                this._registerStoreCommandForAccessor(baseName, accessBlock, requiresTarget);
+
+                var predicateBlock = this._predicateForAccessor(accessBlock, requiresTarget, isBoolean);
+                this._registerAssertionsForPredicate(baseName, predicateBlock);
+                this._registerWaitForCommandsForPredicate(seleniumApi, baseName, predicateBlock);
+            }
+        }
+    },
+
+    _registerAllActions: function(seleniumApi) {
+        for (var functionName in seleniumApi) {
+            var match = /^do([A-Z].+)$/.exec(functionName);
+            if (match) {
+                var actionName = match[1].lcfirst();
+                var actionMethod = seleniumApi[functionName];
+                var dontCheckPopups = actionMethod.dontCheckAlertsAndConfirms;
+                var actionBlock = fnBind(actionMethod, seleniumApi);
+                this.registerAction(actionName, actionBlock, false, dontCheckPopups);
+                this.registerAction(actionName + "AndWait", actionBlock, true, dontCheckPopups);
+            }
+        }
+    },
+
+    _registerAllAsserts: function(seleniumApi) {
+        for (var functionName in seleniumApi) {
+            var match = /^assert([A-Z].+)$/.exec(functionName);
+            if (match) {
+                var assertBlock = fnBind(seleniumApi[functionName], seleniumApi);
+
+                // Register the assert with the "assert" prefix, and halt on failure.
+                var assertName = functionName;
+                this.registerAssert(assertName, assertBlock, true);
+
+                // Register the assert with the "verify" prefix, and do not halt on failure.
+                var verifyName = "verify" + match[1];
+                this.registerAssert(verifyName, assertBlock, false);
+            }
+        }
+    },
+
+    registerAll: function(seleniumApi) {
+        this._registerAllAccessors(seleniumApi);
+        this._registerAllActions(seleniumApi);
+        this._registerAllAsserts(seleniumApi);
+    },
+
+    _predicateForAccessor: function(accessBlock, requiresTarget, isBoolean) {
+        if (isBoolean) {
+            return this._predicateForBooleanAccessor(accessBlock);
+        }
+        if (requiresTarget) {
+            return this._predicateForSingleArgAccessor(accessBlock);
+        }
+        return this._predicateForNoArgAccessor(accessBlock);
+    },
+
+    _predicateForSingleArgAccessor: function(accessBlock) {
+        // Given an accessor function getBlah(target),
+        // return a "predicate" equivalient to isBlah(target, value) that
+        // is true when the value returned by the accessor matches the specified value.
+        return function(target, value) {
+            var accessorResult = accessBlock(target);
+            accessorResult = selArrayToString(accessorResult);
+            if (PatternMatcher.matches(value, accessorResult)) {
+                return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'");
+            } else {
+                return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'");
+            }
+        };
+    },
+
+    _predicateForNoArgAccessor: function(accessBlock) {
+        // Given a (no-arg) accessor function getBlah(),
+        // return a "predicate" equivalient to isBlah(value) that
+        // is true when the value returned by the accessor matches the specified value.
+        return function(value) {
+            var accessorResult = accessBlock();
+            accessorResult = selArrayToString(accessorResult);
+            if (PatternMatcher.matches(value, accessorResult)) {
+                return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'");
+            } else {
+                return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'");
+            }
+        };
+    },
+
+    _predicateForBooleanAccessor: function(accessBlock) {
+        // Given a boolean accessor function isBlah(),
+        // return a "predicate" equivalient to isBlah() that
+        // returns an appropriate PredicateResult value.
+        return function() {
+            var accessorResult;
+            if (arguments.length > 2) throw new SeleniumError("Too many arguments! " + arguments.length);
+            if (arguments.length == 2) {
+                accessorResult = accessBlock(arguments[0], arguments[1]);
+            } else if (arguments.length == 1) {
+                accessorResult = accessBlock(arguments[0]);
+            } else {
+                accessorResult = accessBlock();
+            }
+            if (accessorResult) {
+                return new PredicateResult(true, "true");
+            } else {
+                return new PredicateResult(false, "false");
+            }
+        };
+    },
+
+    _invertPredicate: function(predicateBlock) {
+        // Given a predicate, return the negation of that predicate.
+        // Leaves the message unchanged.
+        // Used to create assertNot, verifyNot, and waitForNot commands.
+        return function(target, value) {
+            var result = predicateBlock(target, value);
+            result.isTrue = !result.isTrue;
+            return result;
+        };
+    },
+
+    createAssertionFromPredicate: function(predicateBlock) {
+        // Convert an isBlahBlah(target, value) function into an assertBlahBlah(target, value) function.
+        return function(target, value) {
+            var result = predicateBlock(target, value);
+            if (!result.isTrue) {
+                Assert.fail(result.message);
+            }
+        };
+    },
+
+    _invertPredicateName: function(baseName) {
+        var matchResult = /^(.*)Present$/.exec(baseName);
+        if (matchResult != null) {
+            return matchResult[1] + "NotPresent";
+        }
+        return "Not" + baseName;
+    },
+
+    _registerAssertionsForPredicate: function(baseName, predicateBlock) {
+        // Register an assertion, a verification, a negative assertion,
+        // and a negative verification based on the specified accessor.
+        var assertBlock = this.createAssertionFromPredicate(predicateBlock);
+        this.registerAssert("assert" + baseName, assertBlock, true);
+        this.registerAssert("verify" + baseName, assertBlock, false);
+
+        var invertedPredicateBlock = this._invertPredicate(predicateBlock);
+        var negativeassertBlock = this.createAssertionFromPredicate(invertedPredicateBlock);
+        this.registerAssert("assert" + this._invertPredicateName(baseName), negativeassertBlock, true);
+        this.registerAssert("verify" + this._invertPredicateName(baseName), negativeassertBlock, false);
+    },
+
+    _waitForActionForPredicate: function(predicateBlock) {
+        // Convert an isBlahBlah(target, value) function into a waitForBlahBlah(target, value) function.
+        return function(target, value) {
+            var terminationCondition = function () {
+                try {
+                    return predicateBlock(target, value).isTrue;
+                } catch (e) {
+                    // Treat exceptions as meaning the condition is not yet met.
+                    // Useful, for example, for waitForValue when the element has
+                    // not even been created yet.
+                    // TODO: possibly should rethrow some types of exception.
+                    return false;
+                }
+            };
+            return Selenium.decorateFunctionWithTimeout(terminationCondition, this.defaultTimeout);
+        };
+    },
+
+    _registerWaitForCommandsForPredicate: function(seleniumApi, baseName, predicateBlock) {
+        // Register a waitForBlahBlah and waitForNotBlahBlah based on the specified accessor.
+        var waitForActionMethod = this._waitForActionForPredicate(predicateBlock);
+        var waitForActionBlock = fnBind(waitForActionMethod, seleniumApi);
+        
+        var invertedPredicateBlock = this._invertPredicate(predicateBlock);
+        var waitForNotActionMethod = this._waitForActionForPredicate(invertedPredicateBlock);
+        var waitForNotActionBlock = fnBind(waitForNotActionMethod, seleniumApi);
+        
+        this.registerAction("waitFor" + baseName, waitForActionBlock, false, true);
+        this.registerAction("waitFor" + this._invertPredicateName(baseName), waitForNotActionBlock, false, true);
+        //TODO decide remove "waitForNot.*Present" action name or not
+        //for the back compatiblity issues we still make waitForNot.*Present availble
+        this.registerAction("waitForNot" + baseName, waitForNotActionBlock, false, true);
+    },
+
+    _registerStoreCommandForAccessor: function(baseName, accessBlock, requiresTarget) {
+        var action;
+        if (requiresTarget) {
+            action = function(target, varName) {
+                storedVars[varName] = accessBlock(target);
+            };
+        } else {
+            action = function(varName) {
+                storedVars[varName] = accessBlock();
+            };
+        }
+        this.registerAction("store" + baseName, action, false, true);
+    }
+
+});
+
+function PredicateResult(isTrue, message) {
+    this.isTrue = isTrue;
+    this.message = message;
+}
+
+// NOTE: The CommandHandler is effectively an abstract base for
+// various handlers including ActionHandler, AccessorHandler and AssertHandler.
+// Subclasses need to implement an execute(seleniumApi, command) function,
+// where seleniumApi is the Selenium object, and command a SeleniumCommand object.
+function CommandHandler(type, haltOnFailure) {
+    this.type = type;
+    this.haltOnFailure = haltOnFailure;
+}
+
+// An ActionHandler is a command handler that executes the sepcified action,
+// possibly checking for alerts and confirmations (if checkAlerts is set), and
+// possibly waiting for a page load if wait is set.
+function ActionHandler(actionBlock, wait, dontCheckAlerts) {
+    this.actionBlock = actionBlock;
+    CommandHandler.call(this, "action", true);
+    if (wait) {
+        this.wait = true;
+    }
+    // note that dontCheckAlerts could be undefined!!!
+    this.checkAlerts = (dontCheckAlerts) ? false : true;
+}
+ActionHandler.prototype = new CommandHandler;
+ActionHandler.prototype.execute = function(seleniumApi, command) {
+    if (this.checkAlerts && (null == /(Alert|Confirmation)(Not)?Present/.exec(command.command))) {
+        // todo: this conditional logic is ugly
+        seleniumApi.ensureNoUnhandledPopups();
+    }
+    var terminationCondition = this.actionBlock(command.target, command.value);
+    // If the handler didn't return a wait flag, check to see if the
+    // handler was registered with the wait flag.
+    if (terminationCondition == undefined && this.wait) {
+        terminationCondition = seleniumApi.makePageLoadCondition();
+    }
+    return new ActionResult(terminationCondition);
+};
+
+function ActionResult(terminationCondition) {
+    this.terminationCondition = terminationCondition;
+}
+
+function AccessorHandler(accessBlock) {
+    this.accessBlock = accessBlock;
+    CommandHandler.call(this, "accessor", true);
+}
+AccessorHandler.prototype = new CommandHandler;
+AccessorHandler.prototype.execute = function(seleniumApi, command) {
+    var returnValue = this.accessBlock(command.target, command.value);
+    return new AccessorResult(returnValue);
+};
+
+function AccessorResult(result) {
+    this.result = result;
+}
+
+/**
+ * Handler for assertions and verifications.
+ */
+function AssertHandler(assertBlock, haltOnFailure) {
+    this.assertBlock = assertBlock;
+    CommandHandler.call(this, "assert", haltOnFailure || false);
+}
+AssertHandler.prototype = new CommandHandler;
+AssertHandler.prototype.execute = function(seleniumApi, command) {
+    var result = new AssertResult();
+    try {
+        this.assertBlock(command.target, command.value);
+    } catch (e) {
+        // If this is not a AssertionFailedError, or we should haltOnFailure, rethrow.
+        if (!e.isAssertionFailedError) {
+            throw e;
+        }
+        if (this.haltOnFailure) {
+            var error = new SeleniumError(e.failureMessage);
+            throw error;
+        }
+        result.setFailed(e.failureMessage);
+    }
+    return result;
+};
+
+function AssertResult() {
+    this.passed = true;
+}
+AssertResult.prototype.setFailed = function(message) {
+    this.passed = null;
+    this.failed = true;
+    this.failureMessage = message;
+}
+
+function SeleniumCommand(command, target, value, isBreakpoint) {
+    this.command = command;
+    this.target = target;
+    this.value = value;
+    this.isBreakpoint = isBreakpoint;
+}
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-executionloop.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-executionloop.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-executionloop.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,175 @@
+/*
+* Copyright 2004 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*/
+
+function TestLoop(commandFactory) {
+    this.commandFactory = commandFactory;
+}
+
+TestLoop.prototype = {
+
+    start : function() {
+        selenium.reset();
+        LOG.debug("currentTest.start()");
+        this.continueTest();
+    },
+
+    continueTest : function() {
+        /**
+         * Select the next command and continue the test.
+         */
+        LOG.debug("currentTest.continueTest() - acquire the next command");
+        if (! this.aborted) {
+            this.currentCommand = this.nextCommand();
+        }
+        if (! this.requiresCallBack) {
+            this.continueTestAtCurrentCommand();
+        } // otherwise, just finish and let the callback invoke continueTestAtCurrentCommand()
+    },
+
+    continueTestAtCurrentCommand : function() {
+        LOG.debug("currentTest.continueTestAtCurrentCommand()");
+        if (this.currentCommand) {
+            // TODO: rename commandStarted to commandSelected, OR roll it into nextCommand
+            this.commandStarted(this.currentCommand);
+            this._resumeAfterDelay();
+        } else {
+            this._testComplete();
+        }
+    },
+
+    _resumeAfterDelay : function() {
+        /**
+         * Pause, then execute the current command.
+         */
+
+        // Get the command delay. If a pauseInterval is set, use it once
+        // and reset it.  Otherwise, use the defined command-interval.
+        var delay = this.pauseInterval || this.getCommandInterval();
+        this.pauseInterval = undefined;
+
+        if (this.currentCommand.isBreakpoint || delay < 0) {
+            // Pause: enable the "next/continue" button
+            this.pause();
+        } else {
+            window.setTimeout(fnBind(this.resume, this), delay);
+        }
+    },
+
+    resume: function() {
+        /**
+         * Select the next command and continue the test.
+         */
+        LOG.debug("currentTest.resume() - actually execute");
+        try {
+            selenium.browserbot.runScheduledPollers();
+            this._executeCurrentCommand();
+            this.continueTestWhenConditionIsTrue();
+        } catch (e) {
+            if (!this._handleCommandError(e)) {
+                this.testComplete();
+            } else {
+                this.continueTest();
+            }
+        }
+    },
+
+    _testComplete : function() {
+        selenium.ensureNoUnhandledPopups();
+        this.testComplete();
+    },
+
+    _executeCurrentCommand : function() {
+        /**
+         * Execute the current command.
+         *
+         * @return a function which will be used to determine when
+         * execution can continue, or null if we can continue immediately
+         */
+        var command = this.currentCommand;
+        LOG.info("Executing: |" + command.command + " | " + command.target + " | " + command.value + " |");
+
+        var handler = this.commandFactory.getCommandHandler(command.command);
+        if (handler == null) {
+            throw new SeleniumError("Unknown command: '" + command.command + "'");
+        }
+
+        command.target = selenium.preprocessParameter(command.target);
+        command.value = selenium.preprocessParameter(command.value);
+        LOG.debug("Command found, going to execute " + command.command);
+        this.result = handler.execute(selenium, command);
+        
+
+        this.waitForCondition = this.result.terminationCondition;
+
+    },
+
+    _handleCommandError : function(e) {
+        if (!e.isSeleniumError) {
+            LOG.exception(e);
+            var msg = "Selenium failure. Please report to the Selenium Users forum at http://forums.openqa.org, with error details from the log window.";
+            msg += "  The error message is: " + extractExceptionMessage(e);
+            return this.commandError(msg);
+        } else {
+            LOG.error(e.message);
+            return this.commandError(e.message);
+        }
+    },
+
+    continueTestWhenConditionIsTrue: function () {
+        /**
+         * Busy wait for waitForCondition() to become true, and then carry
+         * on with test.  Fail the current test if there's a timeout or an
+         * exception.
+         */
+        //LOG.debug("currentTest.continueTestWhenConditionIsTrue()");
+        selenium.browserbot.runScheduledPollers();
+        try {
+            if (this.waitForCondition == null) {
+                LOG.debug("null condition; let's continueTest()");
+                LOG.debug("Command complete");
+                this.commandComplete(this.result);
+                this.continueTest();
+            } else if (this.waitForCondition()) {
+                LOG.debug("condition satisfied; let's continueTest()");
+                this.waitForCondition = null;
+                LOG.debug("Command complete");
+                this.commandComplete(this.result);
+                this.continueTest();
+            } else {
+                //LOG.debug("waitForCondition was false; keep waiting!");
+                window.setTimeout(fnBind(this.continueTestWhenConditionIsTrue, this), 10);
+            }
+        } catch (e) {
+            this.result = {};
+            this.result.failed = true;
+            this.result.failureMessage = extractExceptionMessage(e);
+            this.commandComplete(this.result);
+            this.continueTest();
+        }
+    },
+
+    pause : function() {},
+    nextCommand : function() {},
+    commandStarted : function() {},
+    commandComplete : function() {},
+    commandError : function() {},
+    testComplete : function() {},
+
+    getCommandInterval : function() {
+        return 0;
+    }
+
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-logging.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-logging.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-logging.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+var Logger = function() {
+    this.logWindow = null;
+}
+Logger.prototype = {
+
+    logLevels: {
+        debug: 0,
+        info: 1,
+        warn: 2,
+        error: 3,
+        off: 999
+    },
+
+    pendingMessages: new Array(),
+    
+    threshold: "info",
+
+    setLogLevelThreshold: function(logLevel) {
+        this.threshold = logLevel;
+        var logWindow = this.getLogWindow()
+        if (logWindow && logWindow.setThresholdLevel) {
+            logWindow.setThresholdLevel(logLevel);
+        }
+        // NOTE: log messages will be discarded until the log window is
+        // fully loaded.
+    },
+
+    getLogWindow: function() {
+        if (this.logWindow && this.logWindow.closed) {
+            this.logWindow = null;
+        }
+        return this.logWindow;
+    },
+    
+    openLogWindow: function() {
+        this.logWindow = window.open(
+            getDocumentBase(document) + "SeleniumLog.html?startingThreshold="+this.threshold, "SeleniumLog",
+            "width=600,height=1000,bottom=0,right=0,status,scrollbars,resizable"
+        );
+        this.logWindow.moveTo(window.screenX + 1210, window.screenY + window.outerHeight - 1400);
+        if (browserVersion.appearsToBeBrokenInitialIE6) {
+	// I would really prefer for the message to immediately appear in the log window, the instant the user requests that the log window be 
+        	// visible.  But when I initially coded it this way, thou message simply didn't appear unless I stepped through the code with a debugger.  
+        	// So obviously there is some timing issue here which I don't have the patience to figure out.
+        	var pendingMessage = new LogMessage("warn", "You appear to be running an unpatched IE 6, which is not stable and can crash due to memory problems.  We recommend you run Windows update to install a more stable version of IE.");
+            this.pendingMessages.push(pendingMessage);
+        }
+        return this.logWindow;
+    },
+    
+    show: function() {
+        if (! this.getLogWindow()) {
+            this.openLogWindow();
+        }
+        setTimeout(function(){LOG.error("Log window displayed.  Logging events will now be recorded to this window.");}, 500);
+    },
+
+    logHook: function(logLevel, message) {
+    },
+
+    log: function(logLevel, message) {
+        if (this.logLevels[logLevel] < this.logLevels[this.threshold]) {
+            return;
+        }
+        this.logHook(logLevel, message);
+        var logWindow = this.getLogWindow();
+        if (logWindow) {
+            if (logWindow.append) {
+                if (logWindow.disabled) {
+                    logWindow.callBack = fnBind(this.setLogLevelThreshold, this);
+                    logWindow.enableButtons();
+                }
+                if (this.pendingMessages.length > 0) {
+                    logWindow.append("info: Appending missed logging messages", "info");
+                    while (this.pendingMessages.length > 0) {
+                        var msg = this.pendingMessages.shift();
+                        logWindow.append(msg.type + ": " + msg.msg, msg.type);
+                    }
+                    logWindow.append("info: Done appending missed logging messages", "info");
+                }
+                logWindow.append(logLevel + ": " + message, logLevel);
+            }
+        } else {
+            // TODO these logging messages are never flushed, which creates 
+            //   an enormous array of strings that never stops growing.
+            //   there should at least be a way to clear the messages!
+            this.pendingMessages.push(new LogMessage(logLevel, message));
+        }
+    },
+
+    close: function(message) {
+        if (this.logWindow != null) {
+            try {
+                this.logWindow.close();
+            } catch (e) {
+                // swallow exception
+                // the window is probably closed if we get an exception here
+            }
+            this.logWindow = null;
+        }
+    },
+
+    debug: function(message) {
+       this.log("debug", message);
+    },
+
+    info: function(message) {
+       this.log("info", message);
+    },
+
+    warn: function(message) {
+       this.log("warn", message);
+    },
+
+    error: function(message) {
+       this.log("error", message);
+    },
+
+    exception: function(exception) {
+        this.error("Unexpected Exception: " + extractExceptionMessage(exception));
+        this.error("Exception details: " + describe(exception, ', '));
+    }
+
+};
+
+var LOG = new Logger();
+
+var LogMessage = function(type, msg) {
+    this.type = type;
+    this.msg = msg;
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-remoterunner.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-remoterunner.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-remoterunner.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,571 @@
+/*
+* Copyright 2005 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*
+*/
+
+passColor = "#cfffcf";
+failColor = "#ffcfcf";
+errorColor = "#ffffff";
+workingColor = "#DEE7EC";
+doneColor = "#FFFFCC";
+
+var injectedSessionId;
+var cmd1 = document.createElement("div");
+var cmd2 = document.createElement("div");
+var cmd3 = document.createElement("div");
+var cmd4 = document.createElement("div");
+
+var postResult = "START";
+var debugMode = false;
+var relayToRC = null;
+var proxyInjectionMode = false;
+var uniqueId = 'sel_' + Math.round(100000 * Math.random());
+var seleniumSequenceNumber = 0;
+
+var RemoteRunnerOptions = classCreate();
+objectExtend(RemoteRunnerOptions.prototype, URLConfiguration.prototype);
+objectExtend(RemoteRunnerOptions.prototype, {
+    initialize: function() {
+        this._acquireQueryString();
+    },
+    isDebugMode: function() {
+        return this._isQueryParameterTrue("debugMode");
+    },
+
+    getContinue: function() {
+        return this._getQueryParameter("continue");
+    },
+
+    getDriverUrl: function() {
+        return this._getQueryParameter("driverUrl");
+    },
+
+    getSessionId: function() {
+        return this._getQueryParameter("sessionId");
+    },
+
+    _acquireQueryString: function () {
+        if (this.queryString) return;
+        if (browserVersion.isHTA) {
+            var args = this._extractArgs();
+            if (args.length < 2) return null;
+            this.queryString = args[1];
+        } else if (proxyInjectionMode) {
+            this.queryString = window.location.search.substr(1);
+        } else {
+            this.queryString = top.location.search.substr(1);
+        }
+    }
+
+});
+var runOptions;
+
+function runSeleniumTest() {
+    runOptions = new RemoteRunnerOptions();
+    var testAppWindow;
+
+    if (runOptions.isMultiWindowMode()) {
+        testAppWindow = openSeparateApplicationWindow('Blank.html', true);
+    } else if (sel$('selenium_myiframe') != null) {
+        var myiframe = sel$('selenium_myiframe');
+        if (myiframe) {
+            testAppWindow = myiframe.contentWindow;
+        }
+    }
+    else {
+        proxyInjectionMode = true;
+        testAppWindow = window;
+    }
+    selenium = Selenium.createForWindow(testAppWindow, proxyInjectionMode);
+    if (runOptions.getBaseUrl()) {
+        selenium.browserbot.baseUrl = runOptions.getBaseUrl();
+    }
+    if (!debugMode) {
+        debugMode = runOptions.isDebugMode();
+    }
+    if (proxyInjectionMode) {
+        LOG.logHook = logToRc;
+        selenium.browserbot._modifyWindow(testAppWindow);
+    }
+    else if (debugMode) {
+        LOG.logHook = logToRc;
+    }
+    window.selenium = selenium;
+
+    commandFactory = new CommandHandlerFactory();
+    commandFactory.registerAll(selenium);
+
+    currentTest = new RemoteRunner(commandFactory);
+
+    if (document.getElementById("commandList") != null) {
+        document.getElementById("commandList").appendChild(cmd4);
+        document.getElementById("commandList").appendChild(cmd3);
+        document.getElementById("commandList").appendChild(cmd2);
+        document.getElementById("commandList").appendChild(cmd1);
+    }
+
+    var doContinue = runOptions.getContinue();
+    if (doContinue != null) postResult = "OK";
+
+    currentTest.start();
+}
+
+function buildDriverUrl() {
+    var driverUrl = runOptions.getDriverUrl();
+    if (driverUrl != null) {
+        return driverUrl;
+    }
+    var s = window.location.href
+    var slashPairOffset = s.indexOf("//") + "//".length
+    var pathSlashOffset = s.substring(slashPairOffset).indexOf("/")
+    return s.substring(0, slashPairOffset + pathSlashOffset) + "/selenium-server/driver/";
+    //return "http://localhost" + uniqueId + "/selenium-server/driver/";
+}
+
+function logToRc(logLevel, message) {
+    if (debugMode) {
+        if (logLevel == null) {
+            logLevel = "debug";
+        }
+        sendToRCAndForget("logLevel=" + logLevel + ":" + message.replace(/[\n\r\015]/g, " ") + "\n", "logging=true");
+    }
+}
+
+function serializeString(name, s) {
+    return name + "=unescape(\"" + escape(s) + "\");";
+}
+
+function serializeObject(name, x)
+{
+    var s = '';
+
+    if (isArray(x))
+    {
+        s = name + "=new Array(); ";
+        var len = x["length"];
+        for (var j = 0; j < len; j++)
+        {
+            s += serializeString(name + "[" + j + "]", x[j]);
+        }
+    }
+    else if (typeof x == "string")
+    {
+        s = serializeString(name, x);
+    }
+    else
+    {
+        throw "unrecognized object not encoded: " + name + "(" + x + ")";
+    }
+    return s;
+}
+
+function relayBotToRC(s) {
+}
+
+// seems like no one uses this, but in fact it is called using eval from server-side PI mode code; however,
+// because multiple names can map to the same popup, assigning a single name confuses matters sometimes;
+// thus, I'm disabling this for now.  -Nelson 10/21/06
+function setSeleniumWindowName(seleniumWindowName) {
+//selenium.browserbot.getCurrentWindow()['seleniumWindowName'] = seleniumWindowName;
+}
+
+RemoteRunner = classCreate();
+objectExtend(RemoteRunner.prototype, new TestLoop());
+objectExtend(RemoteRunner.prototype, {
+    initialize : function(commandFactory) {
+        this.commandFactory = commandFactory;
+        this.requiresCallBack = true;
+        this.commandNode = null;
+        this.xmlHttpForCommandsAndResults = null;
+    },
+
+    nextCommand : function() {
+        var urlParms = "";
+        if (postResult == "START") {
+            urlParms += "seleniumStart=true";
+        }
+        this.xmlHttpForCommandsAndResults = XmlHttp.create();
+        sendToRC(postResult, urlParms, fnBind(this._HandleHttpResponse, this), this.xmlHttpForCommandsAndResults);
+    },
+
+    commandStarted : function(command) {
+        this.commandNode = document.createElement("div");
+        var cmdText = command.command + '(';
+        if (command.target != null && command.target != "") {
+            cmdText += command.target;
+            if (command.value != null && command.value != "") {
+                cmdText += ', ' + command.value;
+            }
+        }
+        cmdText += ")";
+        if (cmdText.length >40) {
+            cmdText = cmdText.substring(0,40);
+            cmdText += "...";
+        }
+        this.commandNode.appendChild(document.createTextNode(cmdText));
+        this.commandNode.style.backgroundColor = workingColor;
+        if (document.getElementById("commandList") != null) {
+            document.getElementById("commandList").removeChild(cmd1);
+            document.getElementById("commandList").removeChild(cmd2);
+            document.getElementById("commandList").removeChild(cmd3);
+            document.getElementById("commandList").removeChild(cmd4);
+            cmd4 = cmd3;
+            cmd3 = cmd2;
+            cmd2 = cmd1;
+            cmd1 = this.commandNode;
+            document.getElementById("commandList").appendChild(cmd4);
+            document.getElementById("commandList").appendChild(cmd3);
+            document.getElementById("commandList").appendChild(cmd2);
+            document.getElementById("commandList").appendChild(cmd1);
+        }
+    },
+
+    commandComplete : function(result) {
+
+        if (result.failed) {
+            if (postResult == "CONTINUATION") {
+                currentTest.aborted = true;
+            }
+            postResult = result.failureMessage;
+            this.commandNode.title = result.failureMessage;
+            this.commandNode.style.backgroundColor = failColor;
+        } else if (result.passed) {
+            postResult = "OK";
+            this.commandNode.style.backgroundColor = passColor;
+        } else {
+            if (result.result == null) {
+                postResult = "OK";
+            } else {
+                var actualResult = result.result;
+                actualResult = selArrayToString(actualResult);
+                postResult = "OK," + actualResult;
+            }
+            this.commandNode.style.backgroundColor = doneColor;
+        }
+    },
+
+    commandError : function(message) {
+        postResult = "ERROR: " + message;
+        this.commandNode.style.backgroundColor = errorColor;
+        this.commandNode.title = message;
+    },
+
+    testComplete : function() {
+        window.status = "Selenium Tests Complete, for this Test"
+        // Continue checking for new results
+        this.continueTest();
+        postResult = "START";
+    },
+
+    _HandleHttpResponse : function() {
+        // When request is completed
+        if (this.xmlHttpForCommandsAndResults.readyState == 4) {
+            // OK
+            if (this.xmlHttpForCommandsAndResults.status == 200) {
+            	if (this.xmlHttpForCommandsAndResults.responseText=="") {
+                    LOG.error("saw blank string xmlHttpForCommandsAndResults.responseText");
+                    return;
+                }
+                var command = this._extractCommand(this.xmlHttpForCommandsAndResults);
+                this.currentCommand = command;
+                this.continueTestAtCurrentCommand();
+            }
+            // Not OK 
+            else {
+                var s = 'xmlHttp returned: ' + this.xmlHttpForCommandsAndResults.status + ": " + this.xmlHttpForCommandsAndResults.statusText;
+                LOG.error(s);
+                this.currentCommand = null;
+                setTimeout(fnBind(this.continueTestAtCurrentCommand, this), 2000);
+            }
+
+        }
+    },
+
+    _extractCommand : function(xmlHttp) {
+        var command;
+        try {
+            var re = new RegExp("^(.*?)\n((.|[\r\n])*)");
+            if (re.exec(xmlHttp.responseText)) {
+                command = RegExp.$1;
+                var rest = RegExp.$2;
+                rest = rest.trim();
+                if (rest) {
+                    eval(rest);
+                }
+            }
+            else {
+                command = xmlHttp.responseText;
+            }
+        } catch (e) {
+            alert('could not get responseText: ' + e.message);
+        }
+        if (command.substr(0, '|testComplete'.length) == '|testComplete') {
+            return null;
+        }
+
+        return this._createCommandFromRequest(command);
+    },
+
+
+    _delay : function(millis) {
+        var startMillis = new Date();
+        while (true) {
+            milli = new Date();
+            if (milli - startMillis > millis) {
+                break;
+            }
+        }
+    },
+
+// Parses a URI query string into a SeleniumCommand object
+    _createCommandFromRequest : function(commandRequest) {
+        //decodeURIComponent doesn't strip plus signs
+        var processed = commandRequest.replace(/\+/g, "%20");
+        // strip trailing spaces
+        var processed = processed.replace(/\s+$/, "");
+        var vars = processed.split("&");
+        var cmdArgs = new Object();
+        for (var i = 0; i < vars.length; i++) {
+            var pair = vars[i].split("=");
+            cmdArgs[pair[0]] = pair[1];
+        }
+        var cmd = cmdArgs['cmd'];
+        var arg1 = cmdArgs['1'];
+        if (null == arg1) arg1 = "";
+        arg1 = decodeURIComponent(arg1);
+        var arg2 = cmdArgs['2'];
+        if (null == arg2) arg2 = "";
+        arg2 = decodeURIComponent(arg2);
+        if (cmd == null) {
+            throw new Error("Bad command request: " + commandRequest);
+        }
+        return new SeleniumCommand(cmd, arg1, arg2);
+    }
+
+})
+
+
+function sendToRC(dataToBePosted, urlParms, callback, xmlHttpObject, async) {
+    if (async == null) {
+        async = true;
+    }
+    if (xmlHttpObject == null) {
+        xmlHttpObject = XmlHttp.create();
+    }
+    var url = buildDriverUrl() + "?"
+    if (urlParms) {
+        url += urlParms;
+    }
+    url = addUrlParams(url);
+    url += "&sequenceNumber=" + seleniumSequenceNumber++;
+    
+    var wrappingCallback;
+    if (callback == null) {
+        callback = function() {};
+        wrappingCallback = callback;
+    } else {
+        wrappingCallback = function() {
+            if (xmlHttpObject.readyState == 4) {
+                if (xmlHttpObject.status == 200) {
+                    var retry = false;
+                    if (typeof currentTest != 'undefined') {
+                        var command = currentTest._extractCommand(xmlHttpObject);
+                            //console.log("*********** " + command.command + " | " + command.target + " | " + command.value);
+                        if (command.command == 'retryLast') {
+                            retry = true;
+                        }
+                    }
+                    if (retry) {
+                        setTimeout(fnBind(function() {
+                            sendToRC("RETRY", "retry=true", callback, xmlHttpObject, async);
+                        }, this), 1000);
+                    } else {
+                        callback();
+                    }
+                }
+            }
+        }
+    }
+    
+    var postedData = "postedData=" + encodeURIComponent(dataToBePosted);
+
+    //xmlHttpObject.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+    xmlHttpObject.open("POST", url, async);
+    xmlHttpObject.onreadystatechange = wrappingCallback;
+    xmlHttpObject.send(postedData);
+    return null;
+}
+
+function addUrlParams(url) {
+    return url + "&localFrameAddress=" + (proxyInjectionMode ? makeAddressToAUTFrame() : "top")
+    + getSeleniumWindowNameURLparameters()
+    + "&uniqueId=" + uniqueId
+    + buildDriverParams() + preventBrowserCaching()
+}
+
+function sendToRCAndForget(dataToBePosted, urlParams) {
+    var url;
+    if (!(browserVersion.isChrome || browserVersion.isHTA)) { 
+        // DGF we're behind a proxy, so we can send our logging message to literally any host, to avoid 2-connection limit
+        var protocol = "http:";
+        if (window.location.protocol == "https:") {
+            // DGF if we're in HTTPS, use another HTTPS url to avoid security warning
+            protocol = "https:";
+        }
+        // we don't choose a super large random value, but rather 1 - 16, because this matches with the pre-computed
+        // tunnels waiting on the Selenium Server side. This gives us higher throughput than the two-connection-per-host
+        // limitation, but doesn't require we generate an extremely large ammount of fake SSL certs either.
+        url = protocol + "//" + Math.floor(Math.random()* 16 + 1) + ".selenium.doesnotexist/selenium-server/driver/?" + urlParams;
+    } else {
+        url = buildDriverUrl() + "?" + urlParams;
+    }
+    url = addUrlParams(url);
+    
+    var method = "GET";
+    if (method == "POST") {
+        // DGF submit a request using an iframe; we can't see the response, but we don't need to
+        // TODO not using this mechanism because it screws up back-button
+        var loggingForm = document.createElement("form");
+        loggingForm.method = "POST";
+        loggingForm.action = url;
+        loggingForm.target = "seleniumLoggingFrame";
+        var postedDataInput = document.createElement("input");
+        postedDataInput.type = "hidden";
+        postedDataInput.name = "postedData";
+        postedDataInput.value = dataToBePosted;
+        loggingForm.appendChild(postedDataInput);
+        document.body.appendChild(loggingForm);
+        loggingForm.submit();
+        document.body.removeChild(loggingForm);
+    } else {
+        var postedData = "&postedData=" + encodeURIComponent(dataToBePosted);
+        var scriptTag = document.createElement("script");
+        scriptTag.src = url + postedData;
+        document.body.appendChild(scriptTag);
+        document.body.removeChild(scriptTag);
+    }
+}
+
+function buildDriverParams() {
+    var params = "";
+
+    var sessionId = runOptions.getSessionId();
+    if (sessionId == undefined) {
+        sessionId = injectedSessionId;
+    }
+    if (sessionId != undefined) {
+        params = params + "&sessionId=" + sessionId;
+    }
+    return params;
+}
+
+function preventBrowserCaching() {
+    var t = (new Date()).getTime();
+    return "&counterToMakeURsUniqueAndSoStopPageCachingInTheBrowser=" + t;
+}
+
+//
+// Return URL parameters pertaining to the name(s?) of the current window
+//
+// In selenium, the main (i.e., first) window's name is a blank string.
+//
+// Additional pop-ups are associated with either 1.) the name given by the 2nd parameter to window.open, or 2.) the name of a
+// property on the opening window which points at the window.
+//
+// An example of #2: if window X contains JavaScript as follows:
+//
+// 	var windowABC = window.open(...)
+//
+// Note that the example JavaScript above is equivalent to
+//
+// 	window["windowABC"] = window.open(...)
+//
+function getSeleniumWindowNameURLparameters() {
+    var w = (proxyInjectionMode ? selenium.browserbot.getCurrentWindow() : window).top;
+    var s = "&seleniumWindowName=";
+    if (w.opener == null) {
+        return s;
+    }
+    if (w["seleniumWindowName"] == null) {
+        if (w.name) {
+            w["seleniumWindowName"] = w.name;
+        } else {
+    	    w["seleniumWindowName"] = 'generatedSeleniumWindowName_' + Math.round(100000 * Math.random());
+    	}
+    }
+    s += w["seleniumWindowName"];
+    var windowOpener = w.opener;
+    for (key in windowOpener) {
+        var val = null;
+        try {
+    	    val = windowOpener[key];
+        }
+        catch(e) {
+        }
+        if (val==w) {
+	    s += "&jsWindowNameVar=" + key;			// found a js variable in the opener referring to this window
+        }
+    }
+    return s;
+}
+
+// construct a JavaScript expression which leads to my frame (i.e., the frame containing the window
+// in which this code is operating)
+function makeAddressToAUTFrame(w, frameNavigationalJSexpression)
+{
+    if (w == null)
+    {
+        w = top;
+        frameNavigationalJSexpression = "top";
+    }
+
+    if (w == selenium.browserbot.getCurrentWindow())
+    {
+        return frameNavigationalJSexpression;
+    }
+    for (var j = 0; j < w.frames.length; j++)
+    {
+        var t = makeAddressToAUTFrame(w.frames[j], frameNavigationalJSexpression + ".frames[" + j + "]");
+        if (t != null)
+        {
+            return t;
+        }
+    }
+    return null;
+}
+
+Selenium.prototype.doSetContext = function(context) {
+    /**
+   * Writes a message to the status bar and adds a note to the browser-side
+   * log.
+   *
+   * @param context
+   *            the message to be sent to the browser
+   */
+    //set the current test title
+    var ctx = document.getElementById("context");
+    if (ctx != null) {
+        ctx.innerHTML = context;
+    }
+};
+
+Selenium.prototype.doCaptureScreenshot = function(filename) {
+    /**
+    * Captures a PNG screenshot to the specified file.
+    *
+    * @param filename the absolute path to the file to be written, e.g. "c:\blah\screenshot.png"
+    */
+    // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us!
+};
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-testrunner.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-testrunner.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-testrunner.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,1333 @@
+/*
+* Copyright 2004 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*
+*/
+
+// An object representing the current test, used external
+var currentTest = null; // TODO: get rid of this global, which mirrors the htmlTestRunner.currentTest
+var selenium = null;
+
+var htmlTestRunner;
+var HtmlTestRunner = classCreate();
+objectExtend(HtmlTestRunner.prototype, {
+    initialize: function() {
+        this.metrics = new Metrics();
+        this.controlPanel = new HtmlTestRunnerControlPanel();
+        this.testFailed = false;
+        this.currentTest = null;
+        this.runAllTests = false;
+        this.appWindow = null;
+        // we use a timeout here to make sure the LOG has loaded first, so we can see _every_ error
+        setTimeout(fnBind(function() {
+            this.loadSuiteFrame();
+        }, this), 500);
+    },
+
+    getTestSuite: function() {
+        return suiteFrame.getCurrentTestSuite();
+    },
+
+    markFailed: function() {
+        this.testFailed = true;
+        this.getTestSuite().markFailed();
+    },
+
+    loadSuiteFrame: function() {
+        var logLevel = this.controlPanel.getDefaultLogLevel();
+        if (logLevel) {
+            LOG.setLogLevelThreshold(logLevel);
+        }
+        if (selenium == null) {
+            var appWindow = this._getApplicationWindow();
+            try { appWindow.location; }
+            catch (e) { 
+                // when reloading, we may be pointing at an old window (Perm Denied)
+                setTimeout(fnBind(function() {
+                    this.loadSuiteFrame();
+                }, this), 50);
+                return;
+            }
+            selenium = Selenium.createForWindow(appWindow);
+            this._registerCommandHandlers();
+        }
+        this.controlPanel.setHighlightOption();
+        var testSuiteName = this.controlPanel.getTestSuiteName();
+        var self = this;
+        if (testSuiteName) {
+            suiteFrame.load(testSuiteName, function() {setTimeout(fnBind(self._onloadTestSuite, self), 50)} );
+            selenium.browserbot.baseUrl = absolutify(testSuiteName, window.location.href);
+        }
+        // DGF or should we use the old default?
+        // selenium.browserbot.baseUrl = window.location.href;
+        if (this.controlPanel.getBaseUrl()) {
+            selenium.browserbot.baseUrl = this.controlPanel.getBaseUrl();
+        }
+    },
+
+    _getApplicationWindow: function () {
+        if (this.controlPanel.isMultiWindowMode()) {
+            return this._getSeparateApplicationWindow();
+        }
+        return sel$('selenium_myiframe').contentWindow;
+    },
+
+    _getSeparateApplicationWindow: function () {
+        if (this.appWindow == null) {
+            this.appWindow = openSeparateApplicationWindow('TestRunner-splash.html', this.controlPanel.isAutomatedRun());
+        }
+        return this.appWindow;
+    },
+
+    _onloadTestSuite:function () {
+        if (! this.getTestSuite().isAvailable()) {
+            return;
+        }
+        if (this.controlPanel.isAutomatedRun()) {
+            this.startTestSuite();
+        } else if (this.controlPanel.getAutoUrl()) {
+            //todo what is the autourl doing, left to check it out
+            addLoadListener(this._getApplicationWindow(), fnBind(this._startSingleTest, this));
+            this._getApplicationWindow().src = this.controlPanel.getAutoUrl();
+        } else {
+            var testCaseLoaded = fnBind(function(){this.testCaseLoaded=true;},this);
+            this.getTestSuite().getSuiteRows()[0].loadTestCase(testCaseLoaded);
+        }
+    },
+
+    _startSingleTest:function () {
+        removeLoadListener(getApplicationWindow(), fnBind(this._startSingleTest, this));
+        var singleTestName = this.controlPanel.getSingleTestName();
+        testFrame.load(singleTestName, fnBind(this.startTest, this));
+    },
+
+    _registerCommandHandlers: function () {
+        this.commandFactory = new CommandHandlerFactory();
+        this.commandFactory.registerAll(selenium);
+    },
+
+    startTestSuite: function() {
+        this.controlPanel.reset();
+        this.metrics.resetMetrics();
+        this.getTestSuite().reset();
+        this.runAllTests = true;
+        this.runNextTest();
+    },
+
+    runNextTest: function () {
+        this.getTestSuite().updateSuiteWithResultOfPreviousTest();
+        if (!this.runAllTests) {
+            return;
+        }
+        this.getTestSuite().runNextTestInSuite();
+    },
+
+    startTest: function () {
+        this.controlPanel.reset();
+        testFrame.scrollToTop();
+        //todo: move testFailed and storedVars to TestCase
+        this.testFailed = false;
+        storedVars = new Object();
+        this.currentTest = new HtmlRunnerTestLoop(testFrame.getCurrentTestCase(), this.metrics, this.commandFactory);
+        currentTest = this.currentTest;
+        this.currentTest.start();
+    },
+
+    runSingleTest:function() {
+        this.runAllTests = false;
+        this.metrics.resetMetrics();
+        this.startTest();
+    }
+});
+
+var runInterval = 0;
+
+/** SeleniumFrame encapsulates an iframe element */
+var SeleniumFrame = classCreate();
+objectExtend(SeleniumFrame.prototype, {
+
+    initialize : function(frame) {
+        this.frame = frame;
+        addLoadListener(this.frame, fnBind(this._handleLoad, this));
+    },
+
+    getDocument : function() {
+        return this.frame.contentWindow.document;
+    },
+
+    _handleLoad: function() {
+        this._attachStylesheet();
+        this._onLoad();
+        if (this.loadCallback) {
+            this.loadCallback();
+            this.loadCallback = null;
+        }
+    },
+
+    _attachStylesheet: function() {
+        var d = this.getDocument();
+        var head = d.getElementsByTagName('head').item(0);
+        var styleLink = d.createElement("link");
+        styleLink.rel = "stylesheet";
+        styleLink.type = "text/css";
+        if (browserVersion && browserVersion.isChrome) {
+            // DGF We have to play a clever trick to get the right absolute path.
+            // This trick works on most browsers, (not IE), but is only needed in
+            // chrome
+            var tempLink = window.document.createElement("link");
+            tempLink.href = "selenium-test.css"; // this will become an absolute href
+            styleLink.href = tempLink.href;
+        } else {
+            // this works in every browser (except Firefox in chrome mode)
+            var styleSheetPath = window.location.pathname.replace(/[^\/\\]+$/, "selenium-test.css");
+            if (browserVersion.isIE && window.location.protocol == "file:") {
+                styleSheetPath = "file:///" + styleSheetPath;
+            }
+            styleLink.href = styleSheetPath;
+        }
+        // DGF You're only going to see this log message if you set defaultLogLevel=debug
+        LOG.debug("styleLink.href="+styleLink.href);
+        head.appendChild(styleLink);
+    },
+
+    _onLoad: function() {
+    },
+
+    scrollToTop : function() {
+        this.frame.contentWindow.scrollTo(0, 0);
+    },
+
+    _setLocation: function(location) {
+        var isChrome = browserVersion.isChrome || false;
+        var isHTA = browserVersion.isHTA || false;
+        // DGF TODO multiWindow
+        location += (location.indexOf("?") == -1 ? "?" : "&");
+        location += "thisIsChrome=" + isChrome + "&thisIsHTA=" + isHTA; 
+        if (browserVersion.isSafari) {
+            // safari doesn't reload the page when the location equals to current location.
+            // hence, set the location to blank so that the page will reload automatically.
+            this.frame.src = "about:blank";
+            this.frame.src = location;
+        } else {
+            this.frame.contentWindow.location.replace(location);
+        }
+    },
+
+    load: function(/* url, [callback] */) {
+        if (arguments.length > 1) {
+            this.loadCallback = arguments[1];
+
+        }
+        this._setLocation(arguments[0]);
+    }
+
+});
+
+/** HtmlTestSuiteFrame - encapsulates the suite iframe element */
+var HtmlTestSuiteFrame = classCreate();
+objectExtend(HtmlTestSuiteFrame.prototype, SeleniumFrame.prototype);
+objectExtend(HtmlTestSuiteFrame.prototype, {
+
+    getCurrentTestSuite: function() {
+        if (!this.currentTestSuite) {
+            this.currentTestSuite = new HtmlTestSuite(this.getDocument());
+        }
+        return this.currentTestSuite;
+    }
+
+});
+
+/** HtmlTestFrame - encapsulates the test-case iframe element */
+var HtmlTestFrame = classCreate();
+objectExtend(HtmlTestFrame.prototype, SeleniumFrame.prototype);
+objectExtend(HtmlTestFrame.prototype, {
+
+    _onLoad: function() {
+        this.currentTestCase = new HtmlTestCase(this.getDocument(), htmlTestRunner.getTestSuite().getCurrentRow());
+    },
+
+    getCurrentTestCase: function() {
+        return this.currentTestCase;
+    }
+
+});
+
+function onSeleniumLoad() {
+    suiteFrame = new HtmlTestSuiteFrame(getSuiteFrame());
+    testFrame = new HtmlTestFrame(getTestFrame());
+    htmlTestRunner = new HtmlTestRunner();
+}
+
+var suiteFrame;
+var testFrame;
+
+function getSuiteFrame() {
+    var f = sel$('testSuiteFrame');
+    if (f == null) {
+        f = top;
+        // proxyInjection mode does not set selenium_myiframe
+    }
+    return f;
+}
+
+function getTestFrame() {
+    var f = sel$('testFrame');
+    if (f == null) {
+        f = top;
+        // proxyInjection mode does not set selenium_myiframe
+    }
+    return f;
+}
+
+var HtmlTestRunnerControlPanel = classCreate();
+objectExtend(HtmlTestRunnerControlPanel.prototype, URLConfiguration.prototype);
+objectExtend(HtmlTestRunnerControlPanel.prototype, {
+    initialize: function() {
+        this._acquireQueryString();
+
+        this.runInterval = 0;
+
+        this.highlightOption = sel$('highlightOption');
+        this.pauseButton = sel$('pauseTest');
+        this.stepButton = sel$('stepTest');
+
+        this.highlightOption.onclick = fnBindAsEventListener((function() {
+            this.setHighlightOption();
+        }), this);
+        this.pauseButton.onclick = fnBindAsEventListener(this.pauseCurrentTest, this);
+        this.stepButton.onclick = fnBindAsEventListener(this.stepCurrentTest, this);
+
+
+        this.speedController = new Control.Slider('speedHandle', 'speedTrack', {
+            range: $R(0, 1000),
+            onSlide: fnBindAsEventListener(this.setRunInterval, this),
+            onChange: fnBindAsEventListener(this.setRunInterval, this)
+        });
+
+        this._parseQueryParameter();
+    },
+
+    setHighlightOption: function () {
+        var isHighlight = this.highlightOption.checked;
+        selenium.browserbot.setShouldHighlightElement(isHighlight);
+    },
+
+    _parseQueryParameter: function() {
+        var tempRunInterval = this._getQueryParameter("runInterval");
+        if (tempRunInterval) {
+            this.setRunInterval(tempRunInterval);
+        }
+        this.highlightOption.checked = this._getQueryParameter("highlight");
+    },
+
+    setRunInterval: function(runInterval) {
+        this.runInterval = runInterval;
+    },
+
+    setToPauseAtNextCommand: function() {
+        this.runInterval = -1;
+    },
+
+    pauseCurrentTest: function () {
+        this.setToPauseAtNextCommand();
+        this._switchPauseButtonToContinue();
+    },
+
+    continueCurrentTest: function () {
+        this.reset();
+        currentTest.resume();
+    },
+
+    reset: function() {
+        this.runInterval = this.speedController.value;
+        this._switchContinueButtonToPause();
+    },
+
+    _switchContinueButtonToPause: function() {
+        this.pauseButton.className = "cssPauseTest";
+        this.pauseButton.onclick = fnBindAsEventListener(this.pauseCurrentTest, this);
+    },
+
+    _switchPauseButtonToContinue: function() {
+        sel$('stepTest').disabled = false;
+        this.pauseButton.className = "cssContinueTest";
+        this.pauseButton.onclick = fnBindAsEventListener(this.continueCurrentTest, this);
+    },
+
+    stepCurrentTest: function () {
+        this.setToPauseAtNextCommand();
+        currentTest.resume();
+    },
+
+    isAutomatedRun: function() {
+        return this._isQueryParameterTrue("auto");
+    },
+
+    shouldSaveResultsToFile: function() {
+        return this._isQueryParameterTrue("save");
+    },
+
+    closeAfterTests: function() {
+        return this._isQueryParameterTrue("close");
+    },
+
+    getTestSuiteName: function() {
+        return this._getQueryParameter("test");
+    },
+
+    getSingleTestName: function() {
+        return this._getQueryParameter("singletest");
+    },
+
+    getAutoUrl: function() {
+        return this._getQueryParameter("autoURL");
+    },
+    
+    getDefaultLogLevel: function() {
+        return this._getQueryParameter("defaultLogLevel");
+    },
+
+    getResultsUrl: function() {
+        return this._getQueryParameter("resultsUrl");
+    },
+
+    _acquireQueryString: function() {
+        if (this.queryString) return;
+        if (browserVersion.isHTA) {
+            var args = this._extractArgs();
+            if (args.length < 2) return null;
+            this.queryString = args[1];
+        } else {
+            this.queryString = location.search.substr(1);
+        }
+    }
+
+});
+
+var AbstractResultAwareRow = classCreate();
+objectExtend(AbstractResultAwareRow.prototype, {
+
+    initialize: function(trElement) {
+        this.trElement = trElement;
+    },
+
+    setStatus: function(status) {
+        this.unselect();
+        this.trElement.className = this.trElement.className.replace(/status_[a-z]+/, "");
+        if (status) {
+            addClassName(this.trElement, "status_" + status);
+        }
+    },
+
+    select: function() {
+        addClassName(this.trElement, "selected");
+        safeScrollIntoView(this.trElement);
+    },
+
+    unselect: function() {
+        removeClassName(this.trElement, "selected");
+    },
+
+    markPassed: function() {
+        this.setStatus("passed");
+    },
+
+    markDone: function() {
+        this.setStatus("done");
+    },
+
+    markFailed: function() {
+        this.setStatus("failed");
+    }
+
+});
+
+var TitleRow = classCreate();
+objectExtend(TitleRow.prototype, AbstractResultAwareRow.prototype);
+objectExtend(TitleRow.prototype, {
+
+    initialize: function(trElement) {
+        this.trElement = trElement;
+        trElement.className = "title";
+    }
+
+});
+
+var HtmlTestCaseRow = classCreate();
+objectExtend(HtmlTestCaseRow.prototype, AbstractResultAwareRow.prototype);
+objectExtend(HtmlTestCaseRow.prototype, {
+
+    getCommand: function () {
+        return new SeleniumCommand(getText(this.trElement.cells[0]),
+                getText(this.trElement.cells[1]),
+                getText(this.trElement.cells[2]),
+                this.isBreakpoint());
+    },
+
+    markFailed: function(errorMsg) {
+        AbstractResultAwareRow.prototype.markFailed.call(this, errorMsg);
+        this.setMessage(errorMsg);
+    },
+
+    setMessage: function(message) {
+        setText(this.trElement.cells[2], message);
+    },
+
+    reset: function() {
+        this.setStatus(null);
+        var thirdCell = this.trElement.cells[2];
+        if (thirdCell) {
+            if (thirdCell.originalHTML) {
+                thirdCell.innerHTML = thirdCell.originalHTML;
+            } else {
+                thirdCell.originalHTML = thirdCell.innerHTML;
+            }
+        }
+    },
+
+    onClick: function() {
+        if (this.trElement.isBreakpoint == undefined) {
+            this.trElement.isBreakpoint = true;
+            addClassName(this.trElement, "breakpoint");
+        } else {
+            this.trElement.isBreakpoint = undefined;
+            removeClassName(this.trElement, "breakpoint");
+        }
+    },
+
+    addBreakpointSupport: function() {
+        elementSetStyle(this.trElement, {"cursor" : "pointer"});
+        this.trElement.onclick = fnBindAsEventListener(function() {
+            this.onClick();
+        }, this);
+    },
+
+    isBreakpoint: function() {
+        if (this.trElement.isBreakpoint == undefined || this.trElement.isBreakpoint == null) {
+            return false
+        }
+        return this.trElement.isBreakpoint;
+    }
+});
+
+var HtmlTestSuiteRow = classCreate();
+objectExtend(HtmlTestSuiteRow.prototype, AbstractResultAwareRow.prototype);
+objectExtend(HtmlTestSuiteRow.prototype, {
+
+    initialize: function(trElement, testFrame, htmlTestSuite) {
+        this.trElement = trElement;
+        this.testFrame = testFrame;
+        this.htmlTestSuite = htmlTestSuite;
+        this.link = trElement.getElementsByTagName("a")[0];
+        this.link.onclick = fnBindAsEventListener(this._onClick, this);
+    },
+
+    reset: function() {
+        this.setStatus(null);
+    },
+
+    _onClick: function() {
+        this.loadTestCase(null);
+        return false;
+    },
+
+    loadTestCase: function(onloadFunction) {
+        this.htmlTestSuite.unselectCurrentRow();
+        this.select();
+        this.htmlTestSuite.currentRowInSuite = this.trElement.rowIndex - 1;
+        // If the row has a stored results table, use that
+        var resultsFromPreviousRun = this.trElement.cells[1];
+        if (resultsFromPreviousRun) {
+            // todo: delegate to TestFrame, e.g.
+            //   this.testFrame.restoreTestCase(resultsFromPreviousRun.innerHTML);
+            var testBody = this.testFrame.getDocument().body;
+            testBody.innerHTML = resultsFromPreviousRun.innerHTML;
+            this.testFrame._onLoad();
+            if (onloadFunction) {
+                onloadFunction();
+            }
+        } else {
+            this.testFrame.load(this.link.href, onloadFunction);
+        }
+    },
+
+    saveTestResults: function() {
+        // todo: GLOBAL ACCESS!
+        var resultHTML = this.testFrame.getDocument().body.innerHTML;
+        if (!resultHTML) return;
+
+        // todo: why create this div?
+        var divElement = this.trElement.ownerDocument.createElement("div");
+        divElement.innerHTML = resultHTML;
+
+        var hiddenCell = this.trElement.ownerDocument.createElement("td");
+        hiddenCell.appendChild(divElement);
+        hiddenCell.style.display = "none";
+
+        this.trElement.appendChild(hiddenCell);
+    }
+
+});
+
+var HtmlTestSuite = classCreate();
+objectExtend(HtmlTestSuite.prototype, {
+
+    initialize: function(suiteDocument) {
+        this.suiteDocument = suiteDocument;
+        this.suiteRows = this._collectSuiteRows();
+        this.titleRow = new TitleRow(this.getTestTable().rows[0]);
+        this.reset();
+    },
+
+    reset: function() {
+        this.failed = false;
+        this.currentRowInSuite = -1;
+        this.titleRow.setStatus(null);
+        for (var i = 0; i < this.suiteRows.length; i++) {
+            var row = this.suiteRows[i];
+            row.reset();
+        }
+    },
+
+    getSuiteRows: function() {
+        return this.suiteRows;
+    },
+
+    getTestTable: function() {
+        var tables = sel$A(this.suiteDocument.getElementsByTagName("table"));
+        return tables[0];
+    },
+
+    isAvailable: function() {
+        return this.getTestTable() != null;
+    },
+
+    _collectSuiteRows: function () {
+        var result = [];
+        var tables = sel$A(this.suiteDocument.getElementsByTagName("table"));
+        var testTable = tables[0];
+        for (rowNum = 1; rowNum < testTable.rows.length; rowNum++) {
+            var rowElement = testTable.rows[rowNum];
+            result.push(new HtmlTestSuiteRow(rowElement, testFrame, this));
+        }
+        
+        // process the unsuited rows as well
+        for (var tableNum = 1; tableNum < sel$A(this.suiteDocument.getElementsByTagName("table")).length; tableNum++) {
+            testTable = tables[tableNum];
+            for (rowNum = 1; rowNum < testTable.rows.length; rowNum++) {
+                var rowElement = testTable.rows[rowNum];
+                new HtmlTestSuiteRow(rowElement, testFrame, this);
+            }
+        }
+        return result;
+    },
+
+    getCurrentRow: function() {
+        if (this.currentRowInSuite == -1) {
+            return null;
+        }
+        return this.suiteRows[this.currentRowInSuite];
+    },
+
+    unselectCurrentRow: function() {
+        var currentRow = this.getCurrentRow()
+        if (currentRow) {
+            currentRow.unselect();
+        }
+    },
+
+    markFailed: function() {
+        this.failed = true;
+        this.titleRow.markFailed();
+    },
+
+    markDone: function() {
+        if (!this.failed) {
+            this.titleRow.markPassed();
+        }
+    },
+
+    _startCurrentTestCase: function() {
+        this.getCurrentRow().loadTestCase(fnBind(htmlTestRunner.startTest, htmlTestRunner));
+    },
+
+    _onTestSuiteComplete: function() {
+        this.markDone();
+        new TestResult(this.failed, this.getTestTable()).post();
+    },
+
+    updateSuiteWithResultOfPreviousTest: function() {
+        if (this.currentRowInSuite >= 0) {
+            this.getCurrentRow().saveTestResults();
+        }
+    },
+
+    runNextTestInSuite: function() {
+        this.currentRowInSuite++;
+
+        // If we are done with all of the tests, set the title bar as pass or fail
+        if (this.currentRowInSuite >= this.suiteRows.length) {
+            this._onTestSuiteComplete();
+        } else {
+            this._startCurrentTestCase();
+        }
+    }
+
+
+
+});
+
+var TestResult = classCreate();
+objectExtend(TestResult.prototype, {
+
+// Post the results to a servlet, CGI-script, etc.  The URL of the
+// results-handler defaults to "/postResults", but an alternative location
+// can be specified by providing a "resultsUrl" query parameter.
+//
+// Parameters passed to the results-handler are:
+//      result:         passed/failed depending on whether the suite passed or failed
+//      totalTime:      the total running time in seconds for the suite.
+//
+//      numTestPasses:  the total number of tests which passed.
+//      numTestFailures: the total number of tests which failed.
+//
+//      numCommandPasses: the total number of commands which passed.
+//      numCommandFailures: the total number of commands which failed.
+//      numCommandErrors: the total number of commands which errored.
+//
+//      suite:      the suite table, including the hidden column of test results
+//      testTable.1 to testTable.N: the individual test tables
+//
+    initialize: function (suiteFailed, suiteTable) {
+        this.controlPanel = htmlTestRunner.controlPanel;
+        this.metrics = htmlTestRunner.metrics;
+        this.suiteFailed = suiteFailed;
+        this.suiteTable = suiteTable;
+    },
+
+    post: function () {
+        if (!this.controlPanel.isAutomatedRun()) {
+            return;
+        }
+        var form = document.createElement("form");
+        document.body.appendChild(form);
+
+        form.id = "resultsForm";
+        form.method = "post";
+        form.target = "selenium_myiframe";
+
+        var resultsUrl = this.controlPanel.getResultsUrl();
+        if (!resultsUrl) {
+            resultsUrl = "./postResults";
+        }
+
+        var actionAndParameters = resultsUrl.split('?', 2);
+        form.action = actionAndParameters[0];
+        var resultsUrlQueryString = actionAndParameters[1];
+
+        form.createHiddenField = function(name, value) {
+            input = document.createElement("input");
+            input.type = "hidden";
+            input.name = name;
+            input.value = value;
+            this.appendChild(input);
+        };
+
+        if (resultsUrlQueryString) {
+            var clauses = resultsUrlQueryString.split('&');
+            for (var i = 0; i < clauses.length; i++) {
+                var keyValuePair = clauses[i].split('=', 2);
+                var key = unescape(keyValuePair[0]);
+                var value = unescape(keyValuePair[1]);
+                form.createHiddenField(key, value);
+            }
+        }
+
+        form.createHiddenField("selenium.version", Selenium.version);
+        form.createHiddenField("selenium.revision", Selenium.revision);
+
+        form.createHiddenField("result", this.suiteFailed ? "failed" : "passed");
+
+        form.createHiddenField("totalTime", Math.floor((this.metrics.currentTime - this.metrics.startTime) / 1000));
+        form.createHiddenField("numTestPasses", this.metrics.numTestPasses);
+        form.createHiddenField("numTestFailures", this.metrics.numTestFailures);
+        form.createHiddenField("numCommandPasses", this.metrics.numCommandPasses);
+        form.createHiddenField("numCommandFailures", this.metrics.numCommandFailures);
+        form.createHiddenField("numCommandErrors", this.metrics.numCommandErrors);
+
+        // Create an input for each test table.  The inputs are named
+        // testTable.1, testTable.2, etc.
+        for (rowNum = 1; rowNum < this.suiteTable.rows.length; rowNum++) {
+            // If there is a second column, then add a new input
+            if (this.suiteTable.rows[rowNum].cells.length > 1) {
+                var resultCell = this.suiteTable.rows[rowNum].cells[1];
+                form.createHiddenField("testTable." + rowNum, resultCell.innerHTML);
+                // remove the resultCell, so it's not included in the suite HTML
+                resultCell.parentNode.removeChild(resultCell);
+            }
+        }
+
+        form.createHiddenField("numTestTotal", rowNum-1);
+
+        // Add HTML for the suite itself
+        form.createHiddenField("suite", this.suiteTable.parentNode.innerHTML);
+
+        var logMessages = [];
+        while (LOG.pendingMessages.length > 0) {
+            var msg = LOG.pendingMessages.shift();
+            logMessages.push(msg.type);
+            logMessages.push(": ");
+            logMessages.push(msg.msg);
+            logMessages.push('\n');
+        }
+        var logOutput = logMessages.join("");
+        form.createHiddenField("log", logOutput);
+
+        if (this.controlPanel.shouldSaveResultsToFile()) {
+            this._saveToFile(resultsUrl, form);
+        } else {
+            form.submit();
+        }
+        document.body.removeChild(form);
+        if (this.controlPanel.closeAfterTests()) {
+            window.top.close();
+        }
+    },
+
+    _saveToFile: function (fileName, form) {
+        // This only works when run as an IE HTA
+        var inputs = new Object();
+        for (var i = 0; i < form.elements.length; i++) {
+            inputs[form.elements[i].name] = form.elements[i].value;
+        }
+        
+        var objFSO = new ActiveXObject("Scripting.FileSystemObject")
+        
+        // DGF get CSS
+        var styles = "";
+        try {
+            var styleSheetPath = window.location.pathname.replace(/[^\/\\]+$/, "selenium-test.css");
+            if (window.location.protocol == "file:") {
+                var stylesFile = objFSO.OpenTextFile(styleSheetPath, 1);
+                styles = stylesFile.ReadAll();
+            } else {
+                var xhr = XmlHttp.create();
+                xhr.open("GET", styleSheetPath, false);
+                xhr.send("");
+                styles = xhr.responseText;
+            }
+        } catch (e) {}
+        
+        var scriptFile = objFSO.CreateTextFile(fileName);
+        
+        
+        scriptFile.WriteLine("<html><head><title>Test suite results</title><style>");
+        scriptFile.WriteLine(styles);
+        scriptFile.WriteLine("</style>");
+        scriptFile.WriteLine("<body>\n<h1>Test suite results</h1>" +
+             "\n\n<table>\n<tr>\n<td>result:</td>\n<td>" + inputs["result"] + "</td>\n" +
+             "</tr>\n<tr>\n<td>totalTime:</td>\n<td>" + inputs["totalTime"] + "</td>\n</tr>\n" +
+             "<tr>\n<td>numTestTotal:</td>\n<td>" + inputs["numTestTotal"] + "</td>\n</tr>\n" +
+             "<tr>\n<td>numTestPasses:</td>\n<td>" + inputs["numTestPasses"] + "</td>\n</tr>\n" +
+             "<tr>\n<td>numTestFailures:</td>\n<td>" + inputs["numTestFailures"] + "</td>\n</tr>\n" +
+             "<tr>\n<td>numCommandPasses:</td>\n<td>" + inputs["numCommandPasses"] + "</td>\n</tr>\n" +
+             "<tr>\n<td>numCommandFailures:</td>\n<td>" + inputs["numCommandFailures"] + "</td>\n</tr>\n" +
+             "<tr>\n<td>numCommandErrors:</td>\n<td>" + inputs["numCommandErrors"] + "</td>\n</tr>\n" +
+             "<tr>\n<td>" + inputs["suite"] + "</td>\n<td>&nbsp;</td>\n</tr></table><table>");
+        var testNum = inputs["numTestTotal"];
+        
+        for (var rowNum = 1; rowNum <= testNum; rowNum++) {
+            scriptFile.WriteLine("<tr>\n<td>" + inputs["testTable." + rowNum] + "</td>\n<td>&nbsp;</td>\n</tr>");
+        }
+        scriptFile.WriteLine("</table><pre>");
+        var log = inputs["log"];
+        log=log.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;").replace(/"/gm,"&quot;").replace(/'/gm,"&apos;");
+        scriptFile.WriteLine(log);
+        scriptFile.WriteLine("</pre></body></html>");
+        scriptFile.Close();
+    }
+});
+
+/** HtmlTestCase encapsulates an HTML test document */
+var HtmlTestCase = classCreate();
+objectExtend(HtmlTestCase.prototype, {
+
+    initialize: function(testDocument, htmlTestSuiteRow) {
+        if (testDocument == null) {
+            throw "testDocument should not be null";
+        }
+        if (htmlTestSuiteRow == null) {
+            throw "htmlTestSuiteRow should not be null";
+        }
+        this.testDocument = testDocument;
+        this.htmlTestSuiteRow = htmlTestSuiteRow;
+        this.headerRow = new TitleRow(this.testDocument.getElementsByTagName("tr")[0]);
+        this.commandRows = this._collectCommandRows();
+        this.nextCommandRowIndex = 0;
+        this._addBreakpointSupport();
+    },
+
+    _collectCommandRows: function () {
+        var commandRows = [];
+        var tables = sel$A(this.testDocument.getElementsByTagName("table"));
+        var self = this;
+        for (var i = 0; i < tables.length; i++) {
+            var table = tables[i];
+            var tableRows = sel$A(table.rows);
+            for (var j = 0; j < tableRows.length; j++) {
+                var candidateRow = tableRows[j];
+                if (self.isCommandRow(candidateRow)) {
+                    commandRows.push(new HtmlTestCaseRow(candidateRow));
+                }
+            }
+        }
+        return commandRows;
+    },
+
+    isCommandRow:  function (row) {
+        return row.cells.length >= 3;
+    },
+
+    reset: function() {
+        /**
+         * reset the test to runnable state
+         */
+        this.nextCommandRowIndex = 0;
+
+        this.setStatus('');
+        for (var i = 0; i < this.commandRows.length; i++) {
+            var row = this.commandRows[i];
+            row.reset();
+        }
+
+        // remove any additional fake "error" row added to the end of the document
+        var errorElement = this.testDocument.getElementById('error');
+        if (errorElement) {
+            errorElement.parentNode.removeChild(errorElement);
+        }
+    },
+
+    getCommandRows: function () {
+        return this.commandRows;
+    },
+
+    setStatus: function(status) {
+        this.headerRow.setStatus(status);
+    },
+
+    markFailed: function() {
+        this.setStatus("failed");
+        this.htmlTestSuiteRow.markFailed();
+    },
+
+    markPassed: function() {
+        this.setStatus("passed");
+        this.htmlTestSuiteRow.markPassed();
+    },
+
+    addErrorMessage: function(errorMsg, currentRow) {
+        errorMsg = errorMsg.replace(/ /g, String.fromCharCode(160)).replace("\n", '\\n');
+        if (currentRow) {
+            currentRow.markFailed(errorMsg);
+        } else {
+            var errorElement = this.testDocument.createElement("p");
+            errorElement.id = "error";
+            setText(errorElement, errorMsg);
+            this.testDocument.body.appendChild(errorElement);
+            errorElement.className = "status_failed";
+        }
+    },
+
+    _addBreakpointSupport: function() {
+        for (var i = 0; i < this.commandRows.length; i++) {
+            var row = this.commandRows[i];
+            row.addBreakpointSupport();
+        }
+    },
+
+    hasMoreCommandRows: function() {
+        return this.nextCommandRowIndex < this.commandRows.length;
+    },
+
+    getNextCommandRow: function() {
+        if (this.hasMoreCommandRows()) {
+            return this.commandRows[this.nextCommandRowIndex++];
+        }
+        return null;
+    }
+
+});
+
+
+// TODO: split out an JavascriptTestCase class to handle the "sejs" stuff
+
+var get_new_rows = function() {
+    var row_array = new Array();
+    for (var i = 0; i < new_block.length; i++) {
+
+        var new_source = (new_block[i][0].tokenizer.source.slice(new_block[i][0].start,
+                new_block[i][0].end));
+
+        var row = '<td style="display:none;" class="js">getEval</td>' +
+                  '<td style="display:none;">currentTest.doNextCommand()</td>' +
+                  '<td style="white-space: pre;">' + new_source + '</td>' +
+                  '<td></td>'
+
+        row_array.push(row);
+    }
+    return row_array;
+};
+
+
+var Metrics = classCreate();
+objectExtend(Metrics.prototype, {
+    initialize: function() {
+        // The number of tests run
+        this.numTestPasses = 0;
+        // The number of tests that have failed
+        this.numTestFailures = 0;
+        // The number of commands which have passed
+        this.numCommandPasses = 0;
+        // The number of commands which have failed
+        this.numCommandFailures = 0;
+        // The number of commands which have caused errors (element not found)
+        this.numCommandErrors = 0;
+        // The time that the test was started.
+        this.startTime = null;
+        // The current time.
+        this.currentTime = null;
+    },
+
+    printMetrics: function() {
+        setText(sel$('commandPasses'), this.numCommandPasses);
+        setText(sel$('commandFailures'), this.numCommandFailures);
+        setText(sel$('commandErrors'), this.numCommandErrors);
+        setText(sel$('testRuns'), this.numTestPasses + this.numTestFailures);
+        setText(sel$('testFailures'), this.numTestFailures);
+
+        this.currentTime = new Date().getTime();
+
+        var timeDiff = this.currentTime - this.startTime;
+        var totalSecs = Math.floor(timeDiff / 1000);
+
+        var minutes = Math.floor(totalSecs / 60);
+        var seconds = totalSecs % 60;
+
+        setText(sel$('elapsedTime'), this._pad(minutes) + ":" + this._pad(seconds));
+    },
+
+// Puts a leading 0 on num if it is less than 10
+    _pad: function(num) {
+        return (num > 9) ? num : "0" + num;
+    },
+
+    resetMetrics: function() {
+        this.numTestPasses = 0;
+        this.numTestFailures = 0;
+        this.numCommandPasses = 0;
+        this.numCommandFailures = 0;
+        this.numCommandErrors = 0;
+        this.startTime = new Date().getTime();
+    }
+
+});
+
+var HtmlRunnerCommandFactory = classCreate();
+objectExtend(HtmlRunnerCommandFactory.prototype, {
+
+    initialize: function(seleniumCommandFactory, testLoop) {
+        this.seleniumCommandFactory = seleniumCommandFactory;
+        this.testLoop = testLoop;
+        this.handlers = {};
+        //todo: register commands
+    },
+
+    getCommandHandler: function(command) {
+        if (this.handlers[command]) {
+            return this.handlers[command];
+        }
+        return this.seleniumCommandFactory.getCommandHandler(command);
+    }
+
+});
+
+var HtmlRunnerTestLoop = classCreate();
+objectExtend(HtmlRunnerTestLoop.prototype, new TestLoop());
+objectExtend(HtmlRunnerTestLoop.prototype, {
+    initialize: function(htmlTestCase, metrics, seleniumCommandFactory) {
+
+        this.commandFactory = new HtmlRunnerCommandFactory(seleniumCommandFactory, this);
+        this.metrics = metrics;
+
+        this.htmlTestCase = htmlTestCase;
+        LOG.info("Starting test " + htmlTestCase.testDocument.location.pathname);
+
+        se = selenium;
+        global.se = selenium;
+
+        this.currentRow = null;
+        this.currentRowIndex = 0;
+
+        // used for selenium tests in javascript
+        this.currentItem = null;
+        this.commandAgenda = new Array();
+        this.expectedFailure = null;
+        this.expectedFailureType = null;
+
+        this.htmlTestCase.reset();
+
+        this.sejsElement = this.htmlTestCase.testDocument.getElementById('sejs');
+        if (this.sejsElement) {
+            var fname = 'Selenium JavaScript';
+            parse_result = parse(this.sejsElement.innerHTML, fname, 0);
+
+            var x2 = new ExecutionContext(GLOBAL_CODE);
+            ExecutionContext.current = x2;
+
+            execute(parse_result, x2)
+        }
+    },
+
+    _advanceToNextRow: function() {
+        if (this.htmlTestCase.hasMoreCommandRows()) {
+            this.currentRow = this.htmlTestCase.getNextCommandRow();
+            if (this.sejsElement) {
+                this.currentItem = agenda.pop();
+                this.currentRowIndex++;
+            }
+        } else {
+            this.currentRow = null;
+            this.currentItem = null;
+        }
+    },
+
+    nextCommand : function() {
+        this._advanceToNextRow();
+        if (this.currentRow == null) {
+            return null;
+        }
+        return this.currentRow.getCommand();
+    },
+
+    commandStarted : function() {
+        sel$('pauseTest').disabled = false;
+        this.currentRow.select();
+        this.metrics.printMetrics();
+    },
+
+    commandComplete : function(result) {
+        this._checkExpectedFailure(result);
+        if (result.failed) {
+            this.metrics.numCommandFailures += 1;
+            this._recordFailure(result.failureMessage);
+        } else if (result.passed) {
+            this.metrics.numCommandPasses += 1;
+            this.currentRow.markPassed();
+        } else {
+            this.currentRow.markDone();
+        }
+    },
+
+    _checkExpectedFailure : function(result) {
+        if (this.expectedFailure != null) {
+            if (this.expectedFailureJustSet) {
+                this.expectedFailureJustSet = false;
+                return;
+            }
+            if (!result.failed) {
+                result.passed = false;
+                result.failed = true;
+                result.failureMessage = "Expected " + this.expectedFailureType + " did not occur.";
+            } else {
+                if (PatternMatcher.matches(this.expectedFailure, result.failureMessage)) {
+                    var failureType = result.error ? "error" : "failure";
+                    if (failureType == this.expectedFailureType) {
+                        result.failed = false;
+                        result.passed = true;
+                    } else {
+                        result.failed = true;
+                        result.failureMessage = "Expected "+this.expectedFailureType+", but "+failureType+" occurred instead";
+                    }
+                } else {
+                    result.failed = true;
+                    result.failureMessage = "Expected " + this.expectedFailureType + " message '" + this.expectedFailure
+                                            + "' but was '" + result.failureMessage + "'";
+                }
+            }
+            this.expectedFailure = null;
+            this.expectedFailureType = null;
+        }
+    },
+
+    commandError : function(errorMessage) {
+        var tempResult = {};
+        tempResult.passed = false;
+        tempResult.failed = true;
+        tempResult.error = true;
+        tempResult.failureMessage = errorMessage;
+        this._checkExpectedFailure(tempResult);
+        if (tempResult.passed) {
+            this.currentRow.markDone();
+            return true;
+        }
+        errorMessage = tempResult.failureMessage;
+        this.metrics.numCommandErrors += 1;
+        this._recordFailure(errorMessage);
+    },
+
+    _recordFailure : function(errorMsg) {
+        LOG.warn("currentTest.recordFailure: " + errorMsg);
+        htmlTestRunner.markFailed();
+        this.htmlTestCase.addErrorMessage(errorMsg, this.currentRow);
+    },
+
+    testComplete : function() {
+        sel$('pauseTest').disabled = true;
+        sel$('stepTest').disabled = true;
+        if (htmlTestRunner.testFailed) {
+            this.htmlTestCase.markFailed();
+            this.metrics.numTestFailures += 1;
+        } else {
+            this.htmlTestCase.markPassed();
+            this.metrics.numTestPasses += 1;
+        }
+
+        this.metrics.printMetrics();
+
+        window.setTimeout(function() {
+            htmlTestRunner.runNextTest();
+        }, 1);
+    },
+
+    getCommandInterval : function() {
+        return htmlTestRunner.controlPanel.runInterval;
+    },
+
+    pause : function() {
+        htmlTestRunner.controlPanel.pauseCurrentTest();
+    },
+
+    doNextCommand: function() {
+        var _n = this.currentItem[0];
+        var _x = this.currentItem[1];
+
+        new_block = new Array()
+        execute(_n, _x);
+        if (new_block.length > 0) {
+            var the_table = this.htmlTestCase.testDocument.getElementById("se-js-table")
+            var loc = this.currentRowIndex
+            var new_rows = get_new_rows()
+
+            // make the new statements visible on screen...
+            for (var i = 0; i < new_rows.length; i++) {
+                the_table.insertRow(loc + 1);
+                the_table.rows[loc + 1].innerHTML = new_rows[i];
+                this.commandRows.unshift(the_table.rows[loc + 1])
+            }
+
+        }
+    }
+
+});
+
+Selenium.prototype.doPause = function(waitTime) {
+    /** Wait for the specified amount of time (in milliseconds)
+     * @param waitTime the amount of time to sleep (in milliseconds)
+     */
+    // todo: should not refer to currentTest directly
+    currentTest.pauseInterval = waitTime;
+};
+
+Selenium.prototype.doBreak = function() {
+    /** Halt the currently running test, and wait for the user to press the Continue button.
+     * This command is useful for debugging, but be careful when using it, because it will
+     * force automated tests to hang until a user intervenes manually.
+     */
+    // todo: should not refer to controlPanel directly
+    htmlTestRunner.controlPanel.setToPauseAtNextCommand();
+};
+
+Selenium.prototype.doStore = function(expression, variableName) {
+    /** This command is a synonym for storeExpression.
+     * @param expression the value to store
+     * @param variableName the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+     */
+    storedVars[variableName] = expression;
+}
+
+/*
+ * Click on the located element, and attach a callback to notify
+ * when the page is reloaded.
+ */
+// DGF TODO this code has been broken for some time... what is it trying to accomplish?
+Selenium.prototype.XXXdoModalDialogTest = function(returnValue) {
+    this.browserbot.doModalDialogTest(returnValue);
+};
+
+Selenium.prototype.doEcho = function(message) {
+    /** Prints the specified message into the third table cell in your Selenese tables.
+     * Useful for debugging.
+     * @param message the message to print
+     */
+    currentTest.currentRow.setMessage(message);
+}
+
+Selenium.prototype.assertSelected = function(selectLocator, optionLocator) {
+    /**
+     * Verifies that the selected option of a drop-down satisfies the optionSpecifier.  <i>Note that this command is deprecated; you should use assertSelectedLabel, assertSelectedValue, assertSelectedIndex, or assertSelectedId instead.</i>
+     *
+     * <p>See the select command for more information about option locators.</p>
+     *
+     * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
+     * @param optionLocator an option locator, typically just an option label (e.g. "John Smith")
+     */
+    var element = this.page().findElement(selectLocator);
+    var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
+    if (element.selectedIndex == -1)
+    {
+        Assert.fail("No option selected");
+    }
+    locator.assertSelected(element);
+};
+
+Selenium.prototype.assertFailureOnNext = function(message) {
+    /**
+     * Tell Selenium to expect a failure on the next command execution. 
+     * @param message The failure message we should expect.  This command will fail if the wrong failure message appears.
+     */
+    if (!message) {
+        throw new SeleniumError("Message must be provided");
+    }
+
+    currentTest.expectedFailure = message;
+    currentTest.expectedFailureType = "failure";
+    currentTest.expectedFailureJustSet = true;
+};
+
+Selenium.prototype.assertErrorOnNext = function(message) {
+    /**
+     * Tell Selenium to expect an error on the next command execution. 
+     * @param message The error message we should expect.  This command will fail if the wrong error message appears.
+     */
+     // This command temporarily installs a CommandFactory that generates
+     // CommandHandlers that expect an error.
+    if (!message) {
+        throw new SeleniumError("Message must be provided");
+    }
+
+    currentTest.expectedFailure = message;
+    currentTest.expectedFailureType = "error";
+    currentTest.expectedFailureJustSet = true;
+};
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-version.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-version.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/selenium-version.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,5 @@
+Selenium.version = "0.8.3";
+Selenium.revision = "1879";
+
+window.top.document.title += " v" + Selenium.version + " [" + Selenium.revision + "]";
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/user-extensions.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/user-extensions.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/user-extensions.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,3 @@
+// User extensions can be added here.
+//
+// Keep this file to avoid  mystifying "Invalid Character" error in IE

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/user-extensions.js.sample
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/user-extensions.js.sample	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/user-extensions.js.sample	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,75 @@
+/*
+ * By default, Selenium looks for a file called "user-extensions.js", and loads and javascript
+ * code found in that file. This file is a sample of what that file could look like.
+ *
+ * user-extensions.js provides a convenient location for adding extensions to Selenium, like
+ * new actions, checks and locator-strategies.
+ * By default, this file does not exist. Users can create this file and place their extension code
+ * in this common location, removing the need to modify the Selenium sources, and hopefully assisting
+ * with the upgrade process.
+ *
+ * You can find contributed extensions at http://wiki.openqa.org/display/SEL/Contributed%20User-Extensions
+ */
+
+// The following examples try to give an indication of how Selenium can be extended with javascript.
+
+// All do* methods on the Selenium prototype are added as actions.
+// Eg add a typeRepeated action to Selenium, which types the text twice into a text box.
+// The typeTwiceAndWait command will be available automatically
+Selenium.prototype.doTypeRepeated = function(locator, text) {
+    // All locator-strategies are automatically handled by "findElement"
+    var element = this.page().findElement(locator);
+
+    // Create the text to type
+    var valueToType = text + text;
+
+    // Replace the element text with the new text
+    this.page().replaceText(element, valueToType);
+};
+
+// All assert* methods on the Selenium prototype are added as checks.
+// Eg add a assertValueRepeated check, that makes sure that the element value
+// consists of the supplied text repeated.
+// The verify version will be available automatically.
+Selenium.prototype.assertValueRepeated = function(locator, text) {
+    // All locator-strategies are automatically handled by "findElement"
+    var element = this.page().findElement(locator);
+
+    // Create the text to verify
+    var expectedValue = text + text;
+
+    // Get the actual element value
+    var actualValue = element.value;
+
+    // Make sure the actual value matches the expected
+    Assert.matches(expectedValue, actualValue);
+};
+
+// All get* methods on the Selenium prototype result in
+// store, assert, assertNot, verify, verifyNot, waitFor, and waitForNot commands.
+// E.g. add a getTextLength method that returns the length of the text
+// of a specified element.
+// Will result in support for storeTextLength, assertTextLength, etc.
+Selenium.prototype.getTextLength = function(locator) {
+	return this.getText(locator).length;
+};
+
+// All locateElementBy* methods are added as locator-strategies.
+// Eg add a "valuerepeated=" locator, that finds the first element with the supplied value, repeated.
+// The "inDocument" is a the document you are searching.
+PageBot.prototype.locateElementByValueRepeated = function(text, inDocument) {
+    // Create the text to search for
+    var expectedValue = text + text;
+
+    // Loop through all elements, looking for ones that have a value === our expected value
+    var allElements = inDocument.getElementsByTagName("*");
+    for (var i = 0; i < allElements.length; i++) {
+        var testElement = allElements[i];
+        if (testElement.value && testElement.value === expectedValue) {
+            return testElement;
+        }
+    }
+    return null;
+};
+
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/xmlextras.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/xmlextras.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/scripts/xmlextras.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,153 @@
+// This is a third party JavaScript library from
+// http://webfx.eae.net/dhtml/xmlextras/xmlextras.html
+// i.e. This has not been written by ThoughtWorks.
+
+//<script>
+//////////////////
+// Helper Stuff //
+//////////////////
+
+// used to find the Automation server name
+function getDomDocumentPrefix() {
+	if (getDomDocumentPrefix.prefix)
+		return getDomDocumentPrefix.prefix;
+	
+	var prefixes = ["MSXML2", "Microsoft", "MSXML", "MSXML3"];
+	var o;
+	for (var i = 0; i < prefixes.length; i++) {
+		try {
+			// try to create the objects
+			o = new ActiveXObject(prefixes[i] + ".DomDocument");
+			return getDomDocumentPrefix.prefix = prefixes[i];
+		}
+		catch (ex) {};
+	}
+	
+	throw new Error("Could not find an installed XML parser");
+}
+
+function getXmlHttpPrefix() {
+	if (getXmlHttpPrefix.prefix)
+		return getXmlHttpPrefix.prefix;
+	
+	var prefixes = ["MSXML2", "Microsoft", "MSXML", "MSXML3"];
+	var o;
+	for (var i = 0; i < prefixes.length; i++) {
+		try {
+			// try to create the objects
+			o = new ActiveXObject(prefixes[i] + ".XmlHttp");
+			return getXmlHttpPrefix.prefix = prefixes[i];
+		}
+		catch (ex) {};
+	}
+	
+	throw new Error("Could not find an installed XML parser");
+}
+
+//////////////////////////
+// Start the Real stuff //
+//////////////////////////
+
+
+// XmlHttp factory
+function XmlHttp() {}
+
+XmlHttp.create = function () {
+	try {
+		if (window.XMLHttpRequest) {
+			var req = new XMLHttpRequest();
+			
+			// some versions of Moz do not support the readyState property
+			// and the onreadystate event so we patch it!
+			if (req.readyState == null) {
+				req.readyState = 1;
+				req.addEventListener("load", function () {
+					req.readyState = 4;
+					if (typeof req.onreadystatechange == "function")
+						req.onreadystatechange();
+				}, false);
+			}
+			
+			return req;
+		}
+		if (window.ActiveXObject) {
+			return new ActiveXObject(getXmlHttpPrefix() + ".XmlHttp");
+		}
+	}
+	catch (ex) {}
+	// fell through
+	throw new Error("Your browser does not support XmlHttp objects");
+};
+
+// XmlDocument factory
+function XmlDocument() {}
+
+XmlDocument.create = function () {
+	try {
+		// DOM2
+		if (document.implementation && document.implementation.createDocument) {
+			var doc = document.implementation.createDocument("", "", null);
+			
+			// some versions of Moz do not support the readyState property
+			// and the onreadystate event so we patch it!
+			if (doc.readyState == null) {
+				doc.readyState = 1;
+				doc.addEventListener("load", function () {
+					doc.readyState = 4;
+					if (typeof doc.onreadystatechange == "function")
+						doc.onreadystatechange();
+				}, false);
+			}
+			
+			return doc;
+		}
+		if (window.ActiveXObject)
+			return new ActiveXObject(getDomDocumentPrefix() + ".DomDocument");
+	}
+	catch (ex) {}
+	throw new Error("Your browser does not support XmlDocument objects");
+};
+
+// Create the loadXML method and xml getter for Mozilla
+if (window.DOMParser &&
+	window.XMLSerializer &&
+	window.Node && Node.prototype && Node.prototype.__defineGetter__) {
+
+	// XMLDocument did not extend the Document interface in some versions
+	// of Mozilla. Extend both!
+	//XMLDocument.prototype.loadXML = 
+	Document.prototype.loadXML = function (s) {
+		
+		// parse the string to a new doc	
+		var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
+		
+		// remove all initial children
+		while (this.hasChildNodes())
+			this.removeChild(this.lastChild);
+			
+		// insert and import nodes
+		for (var i = 0; i < doc2.childNodes.length; i++) {
+			this.appendChild(this.importNode(doc2.childNodes[i], true));
+		}
+	};
+	
+	
+	/*
+	 * xml getter
+	 *
+	 * This serializes the DOM tree to an XML String
+	 *
+	 * Usage: var sXml = oNode.xml
+	 *
+	 */
+	// XMLDocument did not extend the Document interface in some versions
+	// of Mozilla. Extend both!
+	/*
+	XMLDocument.prototype.__defineGetter__("xml", function () {
+		return (new XMLSerializer()).serializeToString(this);
+	});
+	*/
+	Document.prototype.__defineGetter__("xml", function () {
+		return (new XMLSerializer()).serializeToString(this);
+	});
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium-logo.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium-logo.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium-test.css
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium-test.css	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium-test.css	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,43 @@
+body, table {
+    font-family: Verdana, Arial, sans-serif;
+    font-size: 12;
+}
+
+table {
+    border-collapse: collapse;
+    border: 1px solid #ccc;
+}
+
+th, td {
+    padding-left: 0.3em;
+    padding-right: 0.3em;
+}
+
+a {
+    text-decoration: none;
+}
+
+.title {
+    font-style: italic;
+}
+
+.selected {
+    background-color: #ffffcc;
+}
+
+.status_done {
+    background-color: #eeffee;
+}
+
+.status_passed {
+    background-color: #ccffcc;
+}
+
+.status_failed {
+    background-color: #ffcccc;
+}
+
+.breakpoint {
+    background-color: #cccccc;
+    border: 1px solid black;
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium.css
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium.css	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/selenium.css	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2005 ThoughtWorks, Inc
+ * 
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *  
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*---( Layout )---*/
+
+* {
+    margin: 0px;
+    padding: 0px;
+}
+
+body {
+    overflow: auto;
+}
+
+td {
+    position: static;
+}
+
+tr {
+    vertical-align: top;
+}
+
+.layout {
+    width: 100%;
+    height: 100%;
+    border-collapse: collapse;
+}
+
+.layout td {
+    border: 0;
+}
+
+iframe {
+    border: 0px;
+    width: 100%;
+    height: 100%;
+    background: white;
+    overflow: auto;
+}
+
+/*---( Style )---*/
+
+body, html {
+    font-family: Verdana, Arial, sans-serif;
+}
+
+.selenium th, .selenium td {
+    border: 1px solid #999;
+}
+
+.header {
+    background: #ccc;
+    padding: 0;
+    font-size: 90%;
+}
+
+#controlPanel {
+    padding: 0.5ex;
+    background: #eee;
+    overflow: auto;
+    font-size: 75%;
+    text-align: center;
+}
+
+#controlPanel fieldset {
+    margin: 0.3ex;
+    padding: 0.3ex;
+}
+
+#controlPanel fieldset legend {
+    color: black;
+}
+
+#controlPanel button {
+    margin: 0.5ex;
+}
+
+#imageButtonPanel button {
+    width: 24px;
+    height: 20px;
+    background-color:white;
+    background-repeat: no-repeat;
+    background-position: center;
+    border-style: solid;
+    border-color: black;
+    border-width: 1px;
+}
+
+#controlPanel #runSuite {
+    width: 32px;
+    background-image: url("icons/all.png");
+}
+
+#controlPanel #runSeleniumTest {
+    width: 32px;
+    background-image: url("icons/selected.png");
+}
+
+.cssPauseTest {
+    background-image: url("icons/pause.png");
+}
+
+.cssPauseTest[disabled]  {
+    background-image: url("icons/pause_disabled.png");
+}
+
+.cssContinueTest {
+    background-image: url("icons/continue.png");
+}
+
+.cssContinueTest[disabled] {
+    background-image: url("icons/continue_disabled.png");
+}
+
+#controlPanel #stepTest {
+    background-image: url("icons/step.png");
+}
+
+#controlPanel #stepTest[disabled] {
+    background-image: url("icons/step_disabled.png");
+}
+
+#controlPanel table {
+    font-size: 100%;
+}
+
+#controlPanel th, #controlPanel td {
+    border: 0;
+}
+
+h1 {
+    margin: 0.2ex;
+    font-size: 130%;
+    font-weight: bold;
+}
+
+h2 {
+    margin: 0.2ex;
+    font-size: 80%;
+    font-weight: normal;
+}
+
+.selenium a {
+    color: black;
+    text-decoration: none;
+}
+
+.selenium a:hover {
+    text-decoration: underline;
+}
+
+button, label {
+    cursor: pointer;
+}
+
+#stats {
+    margin: 0.5em auto 0.5em auto;
+}
+
+#stats th, #stats td {
+    text-align: left;
+    padding-left: 2px;
+}
+
+#stats th {
+    text-decoration: underline;
+}
+
+#stats td.count {
+    font-weight: bold;
+    text-align: right;
+}
+
+#testRuns {
+    color: green;
+}
+
+#testFailures {
+    color: red;
+}
+
+#commandPasses {
+    color: green;
+}
+
+#commandFailures {
+    color: red;
+}
+
+#commandErrors {
+    color: #f90;
+}
+
+
+/*---( Logging Console )---*/
+
+#logging-console {
+    background: #fff;
+    font-size: 75%;
+}
+
+#logging-console #banner {
+    display: block;
+    width: 100%;
+    position: fixed;
+    top: 0;
+    background: #ddd;
+    border-bottom: 1px solid #666;
+}
+
+#logging-console #logLevelChooser {
+    float: right;
+    margin: 3px;
+}
+
+#logging-console ul {
+    list-style-type: none;
+    margin: 0px;
+    margin-top: 3em;
+    padding-left: 5px;
+}
+
+#logging-console li {
+    margin: 2px;
+    border-top: 1px solid #ccc;
+}
+
+#logging-console li.error {
+    font-weight: bold;
+    color: red;
+}
+
+#logging-console li.warn {
+    color: red;
+}
+
+#logging-console li.debug {
+    color: green;
+}
+
+div.executionOptions {
+    padding-left: 5em;
+}
+
+div.executionOptions label, div.executionOptions input {
+    display: block;
+    float: left;
+}
+
+div.executionOptions br {
+    clear: left;
+}
+
+#speedSlider {
+    text-align: left;
+    margin: 0px auto;
+    width: 260px;
+    line-height: 0px;
+    font-size: 0px;
+    padding: 0px;
+}
+
+#speedSlider #speedTrack {
+    background-color: #333;
+    width: 260px;
+    height: 2px;
+    line-height: 2px;
+    z-index: 1;
+    border: 1px solid;
+    border-color: #999 #ddd #ddd #999;
+    cursor: pointer;
+}
+
+#speedSlider #speedHandle {
+    width: 12px;
+    top: -8px;
+    background-color: #666;
+    position: relative;
+    margin: 0px;
+    height: 8px;
+    line-height: 8px;
+    z-index: 1;
+    border: 1px solid;
+    border-color: #999 #333 #333 #999;
+    cursor: pointer;
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/dom.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/dom.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/dom.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,428 @@
+// Copyright 2005 Google Inc.
+// All Rights Reserved
+//
+// An XML parse and a minimal DOM implementation that just supportes
+// the subset of the W3C DOM that is used in the XSLT implementation.
+//
+// References: 
+//
+// [DOM] W3C DOM Level 3 Core Specification
+//       <http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/>.
+//
+// 
+// Author: Steffen Meschkat <mesch at google.com>
+
+// NOTE: The split() method in IE omits empty result strings. This is
+// utterly annoying. So we don't use it here.
+
+// Resolve entities in XML text fragments. According to the DOM
+// specification, the DOM is supposed to resolve entity references at
+// the API level. I.e. no entity references are passed through the
+// API. See "Entities and the DOM core", p.12, DOM 2 Core
+// Spec. However, different browsers actually pass very different
+// values at the API.
+//
+function xmlResolveEntities(s) {
+
+  var parts = stringSplit(s, '&');
+
+  var ret = parts[0];
+  for (var i = 1; i < parts.length; ++i) {
+    var rp = stringSplit(parts[i], ';');
+    if (rp.length == 1) {
+      // no entity reference: just a & but no ;
+      ret += parts[i];
+      continue;
+    }
+    
+    var ch;
+    switch (rp[0]) {
+      case 'lt': 
+        ch = '<';
+        break;
+      case 'gt': 
+        ch = '>';
+        break;
+      case 'amp': 
+        ch = '&';
+        break;
+      case 'quot': 
+        ch = '"';
+        break;
+      case 'apos': 
+        ch = '\'';
+        break;
+      case 'nbsp': 
+        ch = String.fromCharCode(160);
+        break;
+      default:
+        // Cool trick: let the DOM do the entity decoding. We assign
+        // the entity text through non-W3C DOM properties and read it
+        // through the W3C DOM. W3C DOM access is specified to resolve
+        // entities. 
+        var span = window.document.createElement('span');
+        span.innerHTML = '&' + rp[0] + '; ';
+        ch = span.childNodes[0].nodeValue.charAt(0);
+    }
+    ret += ch + rp[1];
+  }
+
+  return ret;
+}
+
+
+// Parses the given XML string with our custom, JavaScript XML parser. Written
+// by Steffen Meschkat (mesch at google.com).
+function xmlParse(xml) {
+  Timer.start('xmlparse');
+  var regex_empty = /\/$/;
+
+  // See also <http://www.w3.org/TR/REC-xml/#sec-common-syn> for
+  // allowed chars in a tag and attribute name. TODO(mesch): the
+  // following is still not completely correct.
+
+  var regex_tagname = /^([\w:-]*)/;
+  var regex_attribute = /([\w:-]+)\s?=\s?('([^\']*)'|"([^\"]*)")/g;
+
+  var xmldoc = new XDocument();
+  var root = xmldoc;
+
+  // For the record: in Safari, we would create native DOM nodes, but
+  // in Opera that is not possible, because the DOM only allows HTML
+  // element nodes to be created, so we have to do our own DOM nodes.
+
+  // xmldoc = document.implementation.createDocument('','',null);
+  // root = xmldoc; // .createDocumentFragment();
+  // NOTE(mesch): using the DocumentFragment instead of the Document
+  // crashes my Safari 1.2.4 (v125.12).
+  var stack = [];
+
+  var parent = root;
+  stack.push(parent);
+
+  var x = stringSplit(xml, '<');
+  for (var i = 1; i < x.length; ++i) {
+    var xx = stringSplit(x[i], '>');
+    var tag = xx[0];
+    var text = xmlResolveEntities(xx[1] || '');
+
+    if (tag.charAt(0) == '/') {
+      stack.pop();
+      parent = stack[stack.length-1];
+
+    } else if (tag.charAt(0) == '?') {
+      // Ignore XML declaration and processing instructions
+    } else if (tag.charAt(0) == '!') {
+      // Ignore notation and comments
+    } else {
+      var empty = tag.match(regex_empty);
+      var tagname = regex_tagname.exec(tag)[1];
+      var node = xmldoc.createElement(tagname);
+
+      var att;
+      while (att = regex_attribute.exec(tag)) {
+        var val = xmlResolveEntities(att[3] || att[4] || '');
+        node.setAttribute(att[1], val);
+      }
+      
+      if (empty) {
+        parent.appendChild(node);
+      } else {
+        parent.appendChild(node);
+        parent = node;
+        stack.push(node);
+      }
+    }
+
+    if (text && parent != root) {
+      parent.appendChild(xmldoc.createTextNode(text));
+    }
+  }
+
+  Timer.end('xmlparse');
+  return root;
+}
+
+
+// Our W3C DOM Node implementation. Note we call it XNode because we
+// can't define the identifier Node. We do this mostly for Opera,
+// where we can't reuse the HTML DOM for parsing our own XML, and for
+// Safari, where it is too expensive to have the template processor
+// operate on native DOM nodes.
+function XNode(type, name, value, owner) {
+  this.attributes = [];
+  this.childNodes = [];
+
+  XNode.init.call(this, type, name, value, owner);
+}
+
+// Don't call as method, use apply() or call().
+XNode.init = function(type, name, value, owner) {
+  this.nodeType = type - 0;
+  this.nodeName = '' + name;
+  this.nodeValue = '' + value;
+  this.ownerDocument = owner;
+
+  this.firstChild = null;
+  this.lastChild = null;
+  this.nextSibling = null;
+  this.previousSibling = null;
+  this.parentNode = null;
+}
+
+XNode.unused_ = [];
+
+XNode.recycle = function(node) {
+  if (!node) {
+    return;
+  }
+
+  if (node.constructor == XDocument) {
+    XNode.recycle(node.documentElement);
+    return;
+  }
+
+  if (node.constructor != this) {
+    return;
+  }
+
+  XNode.unused_.push(node);
+  for (var a = 0; a < node.attributes.length; ++a) {
+    XNode.recycle(node.attributes[a]);
+  }
+  for (var c = 0; c < node.childNodes.length; ++c) {
+    XNode.recycle(node.childNodes[c]);
+  }
+  node.attributes.length = 0;
+  node.childNodes.length = 0;
+  XNode.init.call(node, 0, '', '', null);
+}
+
+XNode.create = function(type, name, value, owner) {
+  if (XNode.unused_.length > 0) {
+    var node = XNode.unused_.pop();
+    XNode.init.call(node, type, name, value, owner);
+    return node;
+  } else {
+    return new XNode(type, name, value, owner);
+  }
+}
+
+XNode.prototype.appendChild = function(node) {
+  // firstChild
+  if (this.childNodes.length == 0) {
+    this.firstChild = node;
+  }
+
+  // previousSibling
+  node.previousSibling = this.lastChild;
+
+  // nextSibling
+  node.nextSibling = null;
+  if (this.lastChild) {
+    this.lastChild.nextSibling = node;
+  }
+
+  // parentNode
+  node.parentNode = this;
+
+  // lastChild
+  this.lastChild = node;
+
+  // childNodes
+  this.childNodes.push(node);
+}
+
+
+XNode.prototype.replaceChild = function(newNode, oldNode) {
+  if (oldNode == newNode) {
+    return;
+  }
+
+  for (var i = 0; i < this.childNodes.length; ++i) {
+    if (this.childNodes[i] == oldNode) {
+      this.childNodes[i] = newNode;
+      
+      var p = oldNode.parentNode;
+      oldNode.parentNode = null;
+      newNode.parentNode = p;
+      
+      p = oldNode.previousSibling;
+      oldNode.previousSibling = null;
+      newNode.previousSibling = p;
+      if (newNode.previousSibling) {
+        newNode.previousSibling.nextSibling = newNode;
+      }
+      
+      p = oldNode.nextSibling;
+      oldNode.nextSibling = null;
+      newNode.nextSibling = p;
+      if (newNode.nextSibling) {
+        newNode.nextSibling.previousSibling = newNode;
+      }
+
+      if (this.firstChild == oldNode) {
+        this.firstChild = newNode;
+      }
+
+      if (this.lastChild == oldNode) {
+        this.lastChild = newNode;
+      }
+
+      break;
+    }
+  }
+}
+
+XNode.prototype.insertBefore = function(newNode, oldNode) {
+  if (oldNode == newNode) {
+    return;
+  }
+
+  if (oldNode.parentNode != this) {
+    return;
+  }
+
+  if (newNode.parentNode) {
+    newNode.parentNode.removeChild(newNode);
+  }
+
+  var newChildren = [];
+  for (var i = 0; i < this.childNodes.length; ++i) {
+    var c = this.childNodes[i];
+    if (c == oldNode) {
+      newChildren.push(newNode);
+
+      newNode.parentNode = this;
+
+      newNode.previousSibling = oldNode.previousSibling;
+      oldNode.previousSibling = newNode;
+      if (newNode.previousSibling) {
+        newNode.previousSibling.nextSibling = newNode;
+      }
+      
+      newNode.nextSibling = oldNode;
+
+      if (this.firstChild == oldNode) {
+        this.firstChild = newNode;
+      }
+    }
+    newChildren.push(c);
+  }
+  this.childNodes = newChildren;
+}
+
+XNode.prototype.removeChild = function(node) {
+  var newChildren = [];
+  for (var i = 0; i < this.childNodes.length; ++i) {
+    var c = this.childNodes[i];
+    if (c != node) {
+      newChildren.push(c);
+    } else {
+      if (c.previousSibling) {
+        c.previousSibling.nextSibling = c.nextSibling;
+      }
+      if (c.nextSibling) {
+        c.nextSibling.previousSibling = c.previousSibling;
+      }
+      if (this.firstChild == c) {
+        this.firstChild = c.nextSibling;
+      }
+      if (this.lastChild == c) {
+        this.lastChild = c.previousSibling;
+      }
+    }
+  }
+  this.childNodes = newChildren;
+}
+
+
+XNode.prototype.hasAttributes = function() {
+  return this.attributes.length > 0;
+}
+
+
+XNode.prototype.setAttribute = function(name, value) {
+  for (var i = 0; i < this.attributes.length; ++i) {
+    if (this.attributes[i].nodeName == name) {
+      this.attributes[i].nodeValue = '' + value;
+      return;
+    }
+  }
+  this.attributes.push(new XNode(DOM_ATTRIBUTE_NODE, name, value));
+}
+
+
+XNode.prototype.getAttribute = function(name) {
+  for (var i = 0; i < this.attributes.length; ++i) {
+    if (this.attributes[i].nodeName == name) {
+      return this.attributes[i].nodeValue;
+    }
+  }
+  return null;
+}
+
+XNode.prototype.removeAttribute = function(name) {
+  var a = [];
+  for (var i = 0; i < this.attributes.length; ++i) {
+    if (this.attributes[i].nodeName != name) {
+      a.push(this.attributes[i]);
+    }
+  }
+  this.attributes = a;
+}
+
+
+function XDocument() {
+  XNode.call(this, DOM_DOCUMENT_NODE, '#document', null, this);
+  this.documentElement = null;
+}
+
+XDocument.prototype = new XNode(DOM_DOCUMENT_NODE, '#document');
+
+XDocument.prototype.clear = function() {
+  XNode.recycle(this.documentElement);
+  this.documentElement = null;
+}
+
+XDocument.prototype.appendChild = function(node) {
+  XNode.prototype.appendChild.call(this, node);
+  this.documentElement = this.childNodes[0];
+}
+
+XDocument.prototype.createElement = function(name) {
+  return XNode.create(DOM_ELEMENT_NODE, name, null, this);
+}
+
+XDocument.prototype.createDocumentFragment = function() {
+  return XNode.create(DOM_DOCUMENT_FRAGMENT_NODE, '#document-fragment',
+                    null, this);
+}
+
+XDocument.prototype.createTextNode = function(value) {
+  return XNode.create(DOM_TEXT_NODE, '#text', value, this);
+}
+
+XDocument.prototype.createAttribute = function(name) {
+  return XNode.create(DOM_ATTRIBUTE_NODE, name, null, this);
+}
+
+XDocument.prototype.createComment = function(data) {
+  return XNode.create(DOM_COMMENT_NODE, '#comment', data, this);
+}
+
+XNode.prototype.getElementsByTagName = function(name, list) {
+  if (!list) {
+    list = [];
+  }
+
+  if (this.nodeName == name) {
+    list.push(this);
+  }
+
+  for (var i = 0; i < this.childNodes.length; ++i) {
+    this.childNodes[i].getElementsByTagName(name, list);
+  }
+
+  return list;
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/misc.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/misc.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/misc.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,252 @@
+// Copyright 2005 Google Inc.
+// All Rights Reserved
+//
+// Miscellania that support the ajaxslt implementation.
+//
+// Author: Steffen Meschkat <mesch at google.com>
+//
+
+function el(i) {
+  return document.getElementById(i);
+}
+
+function px(x) {
+  return x + 'px';
+}
+
+// Split a string s at all occurrences of character c. This is like
+// the split() method of the string object, but IE omits empty
+// strings, which violates the invariant (s.split(x).join(x) == s).
+function stringSplit(s, c) {
+  var a = s.indexOf(c);
+  if (a == -1) {
+    return [ s ];
+  }
+  
+  var parts = [];
+  parts.push(s.substr(0,a));
+  while (a != -1) {
+    var a1 = s.indexOf(c, a + 1);
+    if (a1 != -1) {
+      parts.push(s.substr(a + 1, a1 - a - 1));
+    } else {
+      parts.push(s.substr(a + 1));
+    } 
+    a = a1;
+  }
+
+  return parts;
+}
+
+// Returns the text value if a node; for nodes without children this
+// is the nodeValue, for nodes with children this is the concatenation
+// of the value of all children.
+function xmlValue(node) {
+  if (!node) {
+    return '';
+  }
+
+  var ret = '';
+  if (node.nodeType == DOM_TEXT_NODE ||
+      node.nodeType == DOM_CDATA_SECTION_NODE ||
+      node.nodeType == DOM_ATTRIBUTE_NODE) {
+    ret += node.nodeValue;
+
+  } else if (node.nodeType == DOM_ELEMENT_NODE ||
+             node.nodeType == DOM_DOCUMENT_NODE ||
+             node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
+    for (var i = 0; i < node.childNodes.length; ++i) {
+      ret += arguments.callee(node.childNodes[i]);
+    }
+  }
+  return ret;
+}
+
+// Returns the representation of a node as XML text.
+function xmlText(node) {
+  var ret = '';
+  if (node.nodeType == DOM_TEXT_NODE) {
+    ret += xmlEscapeText(node.nodeValue);
+    
+  } else if (node.nodeType == DOM_ELEMENT_NODE) {
+    ret += '<' + node.nodeName;
+    for (var i = 0; i < node.attributes.length; ++i) {
+      var a = node.attributes[i];
+      if (a && a.nodeName && a.nodeValue) {
+        ret += ' ' + a.nodeName;
+        ret += '="' + xmlEscapeAttr(a.nodeValue) + '"';
+      }
+    }
+
+    if (node.childNodes.length == 0) {
+      ret += '/>';
+
+    } else {
+      ret += '>';
+      for (var i = 0; i < node.childNodes.length; ++i) {
+        ret += arguments.callee(node.childNodes[i]);
+      }
+      ret += '</' + node.nodeName + '>';
+    }
+    
+  } else if (node.nodeType == DOM_DOCUMENT_NODE || 
+             node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
+    for (var i = 0; i < node.childNodes.length; ++i) {
+      ret += arguments.callee(node.childNodes[i]);
+    }
+  }
+  
+  return ret;
+}
+
+// Applies the given function to each element of the array.
+function mapExec(array, func) {
+  for (var i = 0; i < array.length; ++i) {
+    func(array[i]);
+  }
+}
+
+// Returns an array that contains the return value of the given
+// function applied to every element of the input array.
+function mapExpr(array, func) {
+  var ret = [];
+  for (var i = 0; i < array.length; ++i) {
+    ret.push(func(array[i]));
+  }
+  return ret;
+};
+
+// Reverses the given array in place.
+function reverseInplace(array) {
+  for (var i = 0; i < array.length / 2; ++i) {
+    var h = array[i];
+    var ii = array.length - i - 1;
+    array[i] = array[ii];
+    array[ii] = h;
+  }
+}
+
+// Shallow-copies an array.
+function copyArray(dst, src) { 
+  for (var i = 0; i < src.length; ++i) {
+    dst.push(src[i]);
+  }
+}
+
+function assert(b) {
+  if (!b) {
+    throw 'assertion failed';
+  }
+}
+
+// Based on
+// <http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247>
+var DOM_ELEMENT_NODE = 1;
+var DOM_ATTRIBUTE_NODE = 2;
+var DOM_TEXT_NODE = 3;
+var DOM_CDATA_SECTION_NODE = 4;
+var DOM_ENTITY_REFERENCE_NODE = 5;
+var DOM_ENTITY_NODE = 6;
+var DOM_PROCESSING_INSTRUCTION_NODE = 7;
+var DOM_COMMENT_NODE = 8;
+var DOM_DOCUMENT_NODE = 9;
+var DOM_DOCUMENT_TYPE_NODE = 10;
+var DOM_DOCUMENT_FRAGMENT_NODE = 11;
+var DOM_NOTATION_NODE = 12;
+
+
+var xpathdebug = false; // trace xpath parsing
+var xsltdebug = false; // trace xslt processing
+
+
+// Escape XML special markup chracters: tag delimiter < > and entity
+// reference start delimiter &. The escaped string can be used in XML
+// text portions (i.e. between tags).
+function xmlEscapeText(s) {
+  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+}
+
+// Escape XML special markup characters: tag delimiter < > entity
+// reference start delimiter & and quotes ". The escaped string can be
+// used in double quoted XML attribute value portions (i.e. in
+// attributes within start tags).
+function xmlEscapeAttr(s) {
+  return xmlEscapeText(s).replace(/\"/g, '&quot;');
+}
+
+// Escape markup in XML text, but don't touch entity references. The
+// escaped string can be used as XML text (i.e. between tags).
+function xmlEscapeTags(s) {
+  return s.replace(/</g, '&lt;').replace(/>/g, '&gt;');
+}
+
+// An implementation of the debug log. 
+
+var logging__ = false;
+
+function Log() {};
+
+Log.lines = [];
+
+Log.write = function(s) {
+    LOG.debug("xpath logging: " + s);
+};
+
+// Writes the given XML with every tag on a new line.
+Log.writeXML = function(xml) {
+  if (logging__) {
+    var s0 = xml.replace(/</g, '\n<');
+    var s1 = xmlEscapeText(s0);
+    var s2 = s1.replace(/\s*\n(\s|\n)*/g, '<br/>');
+    this.lines.push(s2);
+    this.show();
+  }
+}
+
+// Writes without any escaping
+Log.writeRaw = function(s) {
+  if (logging__) {
+    this.lines.push(s);
+    this.show();
+  }
+}
+
+Log.clear = function() {
+  if (logging__) {
+    var l = this.div();
+    l.innerHTML = '';
+    this.lines = [];
+  }
+}
+
+Log.show = function() {
+  var l = this.div();
+  l.innerHTML += this.lines.join('<br/>') + '<br/>';
+  this.lines = [];
+  l.scrollTop = l.scrollHeight;
+}
+
+Log.div = function() {
+  var l = document.getElementById('log');
+  if (!l) {
+    l = document.createElement('div');
+    l.id = 'log';
+    l.style.position = 'absolute';
+    l.style.right = '5px';
+    l.style.top = '5px';
+    l.style.width = '250px';
+    l.style.height = '150px';
+    l.style.overflow = 'auto';
+    l.style.backgroundColor = '#f0f0f0';
+    l.style.border = '1px solid gray';
+    l.style.fontSize = '10px';
+    l.style.padding = '5px';
+    document.body.appendChild(l);
+  }
+  return l;
+}
+
+
+function Timer() {}
+Timer.start = function() {}
+Timer.end = function() {}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/xpath.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/xpath.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/core/xpath/xpath.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,2223 @@
+// Copyright 2005 Google Inc.
+// All Rights Reserved
+//
+// An XPath parser and evaluator written in JavaScript. The
+// implementation is complete except for functions handling
+// namespaces.
+//
+// Reference: [XPATH] XPath Specification
+// <http://www.w3.org/TR/1999/REC-xpath-19991116>.
+//
+//
+// The API of the parser has several parts:
+//
+// 1. The parser function xpathParse() that takes a string and returns
+// an expession object.
+//
+// 2. The expression object that has an evaluate() method to evaluate the
+// XPath expression it represents. (It is actually a hierarchy of
+// objects that resembles the parse tree, but an application will call
+// evaluate() only on the top node of this hierarchy.)
+//
+// 3. The context object that is passed as an argument to the evaluate()
+// method, which represents the DOM context in which the expression is
+// evaluated.
+//
+// 4. The value object that is returned from evaluate() and represents
+// values of the different types that are defined by XPath (number,
+// string, boolean, and node-set), and allows to convert between them.
+//
+// These parts are near the top of the file, the functions and data
+// that are used internally follow after them.
+//
+//
+// TODO(mesch): add jsdoc comments. Use more coherent naming.
+//
+//
+// Author: Steffen Meschkat <mesch at google.com>
+
+
+// The entry point for the parser.
+//
+// @param expr a string that contains an XPath expression.
+// @return an expression object that can be evaluated with an
+// expression context.
+
+function xpathParse(expr) {
+    //xpathdebug = true;
+  if (xpathdebug) {
+    Log.write('XPath parse ' + expr);
+  }
+  xpathParseInit();
+
+  var cached = xpathCacheLookup(expr);
+  if (cached) {
+    if (xpathdebug) {
+      Log.write(' ... cached');
+    }
+    return cached;
+  }
+
+  // Optimize for a few common cases: simple attribute node tests
+  // (@id), simple element node tests (page), variable references
+  // ($address), numbers (4), multi-step path expressions where each
+  // step is a plain element node test
+  // (page/overlay/locations/location).
+  
+  if (expr.match(/^(\$|@)?\w+$/i)) {
+    var ret = makeSimpleExpr(expr);
+    xpathParseCache[expr] = ret;
+    if (xpathdebug) {
+      Log.write(' ... simple');
+    }
+    return ret;
+  }
+
+  if (expr.match(/^\w+(\/\w+)*$/i)) {
+    var ret = makeSimpleExpr2(expr);
+    xpathParseCache[expr] = ret;
+    if (xpathdebug) {
+      Log.write(' ... simple 2');
+    }
+    return ret;
+  }
+
+  var cachekey = expr; // expr is modified during parse
+  if (xpathdebug) {
+    Timer.start('XPath parse', cachekey);
+  }
+
+  var stack = [];
+  var ahead = null;
+  var previous = null;
+  var done = false;
+
+  var parse_count = 0;
+  var lexer_count = 0;
+  var reduce_count = 0;
+  
+  while (!done) {
+    parse_count++;
+    expr = expr.replace(/^\s*/, '');
+    previous = ahead;
+    ahead = null;
+
+    var rule = null;
+    var match = '';
+    for (var i = 0; i < xpathTokenRules.length; ++i) {
+      var result = xpathTokenRules[i].re.exec(expr);
+      lexer_count++;
+      if (result && result.length > 0 && result[0].length > match.length) {
+        rule = xpathTokenRules[i];
+        match = result[0];
+        break;
+      }
+    }
+
+    // Special case: allow operator keywords to be element and
+    // variable names.
+
+    // NOTE(mesch): The parser resolves conflicts by looking ahead,
+    // and this is the only case where we look back to
+    // disambiguate. So this is indeed something different, and
+    // looking back is usually done in the lexer (via states in the
+    // general case, called "start conditions" in flex(1)). Also,the
+    // conflict resolution in the parser is not as robust as it could
+    // be, so I'd like to keep as much off the parser as possible (all
+    // these precedence values should be computed from the grammar
+    // rules and possibly associativity declarations, as in bison(1),
+    // and not explicitly set.
+
+    if (rule &&
+        (rule == TOK_DIV || 
+         rule == TOK_MOD ||
+         rule == TOK_AND || 
+         rule == TOK_OR) &&
+        (!previous || 
+         previous.tag == TOK_AT || 
+         previous.tag == TOK_DSLASH || 
+         previous.tag == TOK_SLASH ||
+         previous.tag == TOK_AXIS || 
+         previous.tag == TOK_DOLLAR)) {
+      rule = TOK_QNAME;
+    }
+
+    if (rule) {
+      expr = expr.substr(match.length);
+      if (xpathdebug) {
+        Log.write('token: ' + match + ' -- ' + rule.label);
+      }
+      ahead = {
+        tag: rule,
+        match: match,
+        prec: rule.prec ?  rule.prec : 0, // || 0 is removed by the compiler
+        expr: makeTokenExpr(match)
+      };
+
+    } else {
+      if (xpathdebug) {
+        Log.write('DONE');
+      }
+      done = true;
+    }
+
+    while (xpathReduce(stack, ahead)) {
+      reduce_count++;
+      if (xpathdebug) {
+        Log.write('stack: ' + stackToString(stack));
+      }
+    }
+  }
+
+  if (xpathdebug) {
+    Log.write(stackToString(stack));
+  }
+
+  // DGF any valid XPath should "reduce" to a single Expr token
+  if (stack.length != 1) {
+    throw 'XPath parse error ' + cachekey + ':\n' + stackToString(stack);
+  }
+
+  var result = stack[0].expr;
+  xpathParseCache[cachekey] = result;
+
+  if (xpathdebug) {
+    Timer.end('XPath parse', cachekey);
+  }
+
+  if (xpathdebug) {
+    Log.write('XPath parse: ' + parse_count + ' / ' + 
+              lexer_count + ' / ' + reduce_count);
+  }
+
+  return result;
+}
+
+var xpathParseCache = {};
+
+function xpathCacheLookup(expr) {
+  return xpathParseCache[expr];
+}
+
+/*DGF xpathReduce is where the magic happens in this parser.
+Skim down to the bottom of this file to find the table of 
+grammatical rules and precedence numbers, "The productions of the grammar".
+
+The idea here
+is that we want to take a stack of tokens and apply
+grammatical rules to them, "reducing" them to higher-level
+tokens.  Ultimately, any valid XPath should reduce to exactly one
+"Expr" token.
+
+Reduce too early or too late and you'll have two tokens that can't reduce
+to single Expr.  For example, you may hastily reduce a qname that
+should name a function, incorrectly treating it as a tag name.
+Or you may reduce too late, accidentally reducing the last part of the
+XPath into a top-level "Expr" that won't reduce with earlier parts of
+the XPath.
+
+A "cand" is a grammatical rule candidate, with a given precedence
+number.  "ahead" is the upcoming token, which also has a precedence
+number.  If the token has a higher precedence number than
+the rule candidate, we'll "shift" the token onto the token stack,
+instead of immediately applying the rule candidate.
+
+Some tokens have left associativity, in which case we shift when they
+have LOWER precedence than the candidate.
+*/
+function xpathReduce(stack, ahead) {
+  var cand = null;
+
+  if (stack.length > 0) {
+    var top = stack[stack.length-1];
+    var ruleset = xpathRules[top.tag.key];
+
+    if (ruleset) {
+      for (var i = 0; i < ruleset.length; ++i) {
+        var rule = ruleset[i];
+        var match = xpathMatchStack(stack, rule[1]);
+        if (match.length) {
+          cand = {
+            tag: rule[0],
+            rule: rule,
+            match: match
+          };
+          cand.prec = xpathGrammarPrecedence(cand);
+          break;
+        }
+      }
+    }
+  }
+
+  var ret;
+  if (cand && (!ahead || cand.prec > ahead.prec || 
+               (ahead.tag.left && cand.prec >= ahead.prec))) {
+    for (var i = 0; i < cand.match.matchlength; ++i) {
+      stack.pop();
+    }
+
+    if (xpathdebug) {
+      Log.write('reduce ' + cand.tag.label + ' ' + cand.prec +
+                ' ahead ' + (ahead ? ahead.tag.label + ' ' + ahead.prec + 
+                             (ahead.tag.left ? ' left' : '')
+                             : ' none '));
+    }
+
+    var matchexpr = mapExpr(cand.match, function(m) { return m.expr; });
+    if (xpathdebug) {
+        Log.write('about to run ' + cand.rule[3].toString());
+    }
+    cand.expr = cand.rule[3].apply(null, matchexpr);
+
+    stack.push(cand);
+    ret = true;
+
+  } else {
+    if (ahead) {
+      if (xpathdebug) {
+        Log.write('shift ' + ahead.tag.label + ' ' + ahead.prec + 
+                  (ahead.tag.left ? ' left' : '') +
+                  ' over ' + (cand ? cand.tag.label + ' ' + 
+                              cand.prec : ' none'));
+      }
+      stack.push(ahead);
+    }
+    ret = false;
+  }
+  return ret;
+}
+
+function xpathMatchStack(stack, pattern) {
+
+  // NOTE(mesch): The stack matches for variable cardinality are
+  // greedy but don't do backtracking. This would be an issue only
+  // with rules of the form A* A, i.e. with an element with variable
+  // cardinality followed by the same element. Since that doesn't
+  // occur in the grammar at hand, all matches on the stack are
+  // unambiguous.
+
+  var S = stack.length;
+  var P = pattern.length;
+  var p, s;
+  var match = [];
+  match.matchlength = 0;
+  var ds = 0;
+  for (p = P - 1, s = S - 1; p >= 0 && s >= 0; --p, s -= ds) {
+    ds = 0;
+    var qmatch = [];
+    if (pattern[p] == Q_MM) {
+      p -= 1;
+      match.push(qmatch);
+      while (s - ds >= 0 && stack[s - ds].tag == pattern[p]) {
+        qmatch.push(stack[s - ds]);
+        ds += 1;
+        match.matchlength += 1;
+      }
+
+    } else if (pattern[p] == Q_01) {
+      p -= 1;
+      match.push(qmatch);
+      while (s - ds >= 0 && ds < 2 && stack[s - ds].tag == pattern[p]) {
+        qmatch.push(stack[s - ds]);
+        ds += 1;
+        match.matchlength += 1;
+      }
+
+    } else if (pattern[p] == Q_1M) {
+      p -= 1;
+      match.push(qmatch);
+      if (stack[s].tag == pattern[p]) {
+        while (s - ds >= 0 && stack[s - ds].tag == pattern[p]) {
+          qmatch.push(stack[s - ds]);
+          ds += 1;
+          match.matchlength += 1;
+        }
+      } else {
+        return [];
+      }
+
+    } else if (stack[s].tag == pattern[p]) {
+      match.push(stack[s]);
+      ds += 1;
+      match.matchlength += 1;
+
+    } else {
+      return [];
+    }
+
+    reverseInplace(qmatch);
+    qmatch.expr = mapExpr(qmatch, function(m) { return m.expr; });
+  }
+
+  reverseInplace(match);
+
+  if (p == -1) {
+    return match;
+
+  } else {
+    return [];
+  }
+}
+
+function xpathTokenPrecedence(tag) {
+  return tag.prec || 2;
+}
+
+function xpathGrammarPrecedence(frame) {
+  var ret = 0;
+
+  if (frame.rule) { /* normal reduce */
+    if (frame.rule.length >= 3 && frame.rule[2] >= 0) {
+      ret = frame.rule[2];
+
+    } else {
+      for (var i = 0; i < frame.rule[1].length; ++i) {
+        var p = xpathTokenPrecedence(frame.rule[1][i]);
+        ret = Math.max(ret, p);
+      }
+    }
+  } else if (frame.tag) { /* TOKEN match */
+    ret = xpathTokenPrecedence(frame.tag);
+
+  } else if (frame.length) { /* Q_ match */
+    for (var j = 0; j < frame.length; ++j) {
+      var p = xpathGrammarPrecedence(frame[j]);
+      ret = Math.max(ret, p);
+    }
+  }
+
+  return ret;
+}
+
+function stackToString(stack) {
+  var ret = '';
+  for (var i = 0; i < stack.length; ++i) {
+    if (ret) {
+      ret += '\n';
+    }
+    ret += stack[i].tag.label;
+  }
+  return ret;
+}
+
+
+// XPath expression evaluation context. An XPath context consists of a
+// DOM node, a list of DOM nodes that contains this node, a number
+// that represents the position of the single node in the list, and a
+// current set of variable bindings. (See XPath spec.)
+//
+// The interface of the expression context:
+//
+//   Constructor -- gets the node, its position, the node set it
+//   belongs to, and a parent context as arguments. The parent context
+//   is used to implement scoping rules for variables: if a variable
+//   is not found in the current context, it is looked for in the
+//   parent context, recursively. Except for node, all arguments have
+//   default values: default position is 0, default node set is the
+//   set that contains only the node, and the default parent is null.
+//
+//     Notice that position starts at 0 at the outside interface;
+//     inside XPath expressions this shows up as position()=1.
+//
+//   clone() -- creates a new context with the current context as
+//   parent. If passed as argument to clone(), the new context has a
+//   different node, position, or node set. What is not passed is
+//   inherited from the cloned context.
+//
+//   setVariable(name, expr) -- binds given XPath expression to the
+//   name.
+//
+//   getVariable(name) -- what the name says.
+//
+//   setNode(node, position) -- sets the context to the new node and
+//   its corresponding position. Needed to implement scoping rules for
+//   variables in XPath. (A variable is visible to all subsequent
+//   siblings, not only to its children.)
+
+function ExprContext(node, position, nodelist, parent) {
+  this.node = node;
+  this.position = position || 0;
+  this.nodelist = nodelist || [ node ];
+  this.variables = {};
+  this.parent = parent || null;
+  this.root = parent ? parent.root : node.ownerDocument;
+}
+
+ExprContext.prototype.clone = function(node, position, nodelist) {
+  return new
+  ExprContext(node || this.node,
+              typeof position != 'undefined' ? position : this.position,
+              nodelist || this.nodelist, this);
+};
+
+ExprContext.prototype.setVariable = function(name, value) {
+  this.variables[name] = value;
+};
+
+ExprContext.prototype.getVariable = function(name) {
+  if (typeof this.variables[name] != 'undefined') {
+    return this.variables[name];
+
+  } else if (this.parent) {
+    return this.parent.getVariable(name);
+
+  } else {
+    return null;
+  }
+}
+
+ExprContext.prototype.setNode = function(node, position) {
+  this.node = node;
+  this.position = position;
+}
+
+
+// XPath expression values. They are what XPath expressions evaluate
+// to. Strangely, the different value types are not specified in the
+// XPath syntax, but only in the semantics, so they don't show up as
+// nonterminals in the grammar. Yet, some expressions are required to
+// evaluate to particular types, and not every type can be coerced
+// into every other type. Although the types of XPath values are
+// similar to the types present in JavaScript, the type coercion rules
+// are a bit peculiar, so we explicitly model XPath types instead of
+// mapping them onto JavaScript types. (See XPath spec.)
+//
+// The four types are:
+//
+//   StringValue
+//
+//   NumberValue
+//
+//   BooleanValue
+//
+//   NodeSetValue
+//
+// The common interface of the value classes consists of methods that
+// implement the XPath type coercion rules:
+//
+//   stringValue() -- returns the value as a JavaScript String,
+//
+//   numberValue() -- returns the value as a JavaScript Number,
+//
+//   booleanValue() -- returns the value as a JavaScript Boolean,
+//
+//   nodeSetValue() -- returns the value as a JavaScript Array of DOM
+//   Node objects.
+//
+
+function StringValue(value) {
+  this.value = value;
+  this.type = 'string';
+}
+
+StringValue.prototype.stringValue = function() {
+  return this.value;
+}
+
+StringValue.prototype.booleanValue = function() {
+  return this.value.length > 0;
+}
+
+StringValue.prototype.numberValue = function() {
+  return this.value - 0;
+}
+
+StringValue.prototype.nodeSetValue = function() {
+  throw this + ' ' + Error().stack;
+}
+
+function BooleanValue(value) {
+  this.value = value;
+  this.type = 'boolean';
+}
+
+BooleanValue.prototype.stringValue = function() {
+  return '' + this.value;
+}
+
+BooleanValue.prototype.booleanValue = function() {
+  return this.value;
+}
+
+BooleanValue.prototype.numberValue = function() {
+  return this.value ? 1 : 0;
+}
+
+BooleanValue.prototype.nodeSetValue = function() {
+  throw this + ' ' + Error().stack;
+}
+
+function NumberValue(value) {
+  this.value = value;
+  this.type = 'number';
+}
+
+NumberValue.prototype.stringValue = function() {
+  return '' + this.value;
+}
+
+NumberValue.prototype.booleanValue = function() {
+  return !!this.value;
+}
+
+NumberValue.prototype.numberValue = function() {
+  return this.value - 0;
+}
+
+NumberValue.prototype.nodeSetValue = function() {
+  throw this + ' ' + Error().stack;
+}
+
+function NodeSetValue(value) {
+  this.value = value;
+  this.type = 'node-set';
+}
+
+NodeSetValue.prototype.stringValue = function() {
+  if (this.value.length == 0) {
+    return '';
+  } else {
+    return xmlValue(this.value[0]);
+  }
+}
+
+NodeSetValue.prototype.booleanValue = function() {
+  return this.value.length > 0;
+}
+
+NodeSetValue.prototype.numberValue = function() {
+  return this.stringValue() - 0;
+}
+
+NodeSetValue.prototype.nodeSetValue = function() {
+  return this.value;
+};
+
+// XPath expressions. They are used as nodes in the parse tree and
+// possess an evaluate() method to compute an XPath value given an XPath
+// context. Expressions are returned from the parser. Teh set of
+// expression classes closely mirrors the set of non terminal symbols
+// in the grammar. Every non trivial nonterminal symbol has a
+// corresponding expression class.
+//
+// The common expression interface consists of the following methods:
+//
+// evaluate(context) -- evaluates the expression, returns a value.
+//
+// toString() -- returns the XPath text representation of the
+// expression (defined in xsltdebug.js).
+//
+// parseTree(indent) -- returns a parse tree representation of the
+// expression (defined in xsltdebug.js).
+
+function TokenExpr(m) {
+  this.value = m;
+}
+
+TokenExpr.prototype.evaluate = function() {
+  return new StringValue(this.value);
+};
+
+function LocationExpr() {
+  this.absolute = false;
+  this.steps = [];
+}
+
+LocationExpr.prototype.appendStep = function(s) {
+  this.steps.push(s);
+}
+
+LocationExpr.prototype.prependStep = function(s) {
+  var steps0 = this.steps;
+  this.steps = [ s ];
+  for (var i = 0; i < steps0.length; ++i) {
+    this.steps.push(steps0[i]);
+  }
+};
+
+LocationExpr.prototype.evaluate = function(ctx) {
+  var start;
+  if (this.absolute) {
+    start = ctx.root;
+
+  } else {
+    start = ctx.node;
+  }
+
+  var nodes = [];
+  xPathStep(nodes, this.steps, 0, start, ctx);
+  return new NodeSetValue(nodes);
+};
+
+function xPathStep(nodes, steps, step, input, ctx) {
+  var s = steps[step];
+  var ctx2 = ctx.clone(input);
+  var nodelist = s.evaluate(ctx2).nodeSetValue();
+
+  for (var i = 0; i < nodelist.length; ++i) {
+    if (step == steps.length - 1) {
+      nodes.push(nodelist[i]);
+    } else {
+      xPathStep(nodes, steps, step + 1, nodelist[i], ctx);
+    }
+  }
+}
+
+function StepExpr(axis, nodetest, predicate) {
+  this.axis = axis;
+  this.nodetest = nodetest;
+  this.predicate = predicate || [];
+}
+
+StepExpr.prototype.appendPredicate = function(p) {
+  this.predicate.push(p);
+}
+
+StepExpr.prototype.evaluate = function(ctx) {
+  var input = ctx.node;
+  var nodelist = [];
+
+  // NOTE(mesch): When this was a switch() statement, it didn't work
+  // in Safari/2.0. Not sure why though; it resulted in the JavaScript
+  // console output "undefined" (without any line number or so).
+
+  if (this.axis ==  xpathAxis.ANCESTOR_OR_SELF) {
+    nodelist.push(input);
+    for (var n = input.parentNode; n; n = input.parentNode) {
+      nodelist.push(n);
+    }
+
+  } else if (this.axis == xpathAxis.ANCESTOR) {
+    for (var n = input.parentNode; n; n = input.parentNode) {
+      nodelist.push(n);
+    }
+
+  } else if (this.axis == xpathAxis.ATTRIBUTE) {
+    copyArray(nodelist, input.attributes);
+
+  } else if (this.axis == xpathAxis.CHILD) {
+    copyArray(nodelist, input.childNodes);
+
+  } else if (this.axis == xpathAxis.DESCENDANT_OR_SELF) {
+    nodelist.push(input);
+    xpathCollectDescendants(nodelist, input);
+
+  } else if (this.axis == xpathAxis.DESCENDANT) {
+    xpathCollectDescendants(nodelist, input);
+
+  } else if (this.axis == xpathAxis.FOLLOWING) {
+    for (var n = input.parentNode; n; n = n.parentNode) {
+      for (var nn = n.nextSibling; nn; nn = nn.nextSibling) {
+        nodelist.push(nn);
+        xpathCollectDescendants(nodelist, nn);
+      }
+    }
+
+  } else if (this.axis == xpathAxis.FOLLOWING_SIBLING) {
+    for (var n = input.nextSibling; n; n = input.nextSibling) {
+      nodelist.push(n);
+    }
+
+  } else if (this.axis == xpathAxis.NAMESPACE) {
+    alert('not implemented: axis namespace');
+
+  } else if (this.axis == xpathAxis.PARENT) {
+    if (input.parentNode) {
+      nodelist.push(input.parentNode);
+    }
+
+  } else if (this.axis == xpathAxis.PRECEDING) {
+    for (var n = input.parentNode; n; n = n.parentNode) {
+      for (var nn = n.previousSibling; nn; nn = nn.previousSibling) {
+        nodelist.push(nn);
+        xpathCollectDescendantsReverse(nodelist, nn);
+      }
+    }
+
+  } else if (this.axis == xpathAxis.PRECEDING_SIBLING) {
+    for (var n = input.previousSibling; n; n = input.previousSibling) {
+      nodelist.push(n);
+    }
+
+  } else if (this.axis == xpathAxis.SELF) {
+    nodelist.push(input);
+
+  } else {
+    throw 'ERROR -- NO SUCH AXIS: ' + this.axis;
+  }
+
+  // process node test
+  var nodelist0 = nodelist;
+  nodelist = [];
+  for (var i = 0; i < nodelist0.length; ++i) {
+    var n = nodelist0[i];
+    if (this.nodetest.evaluate(ctx.clone(n, i, nodelist0)).booleanValue()) {
+      nodelist.push(n);
+    }
+  }
+
+  // process predicates
+  for (var i = 0; i < this.predicate.length; ++i) {
+    var nodelist0 = nodelist;
+    nodelist = [];
+    for (var ii = 0; ii < nodelist0.length; ++ii) {
+      var n = nodelist0[ii];
+      if (this.predicate[i].evaluate(ctx.clone(n, ii, nodelist0)).booleanValue()) {
+        nodelist.push(n);
+      }
+    }
+  }
+
+  return new NodeSetValue(nodelist);
+};
+
+function NodeTestAny() {
+  this.value = new BooleanValue(true);
+}
+
+NodeTestAny.prototype.evaluate = function(ctx) {
+  return this.value;
+};
+
+function NodeTestElement() {}
+
+NodeTestElement.prototype.evaluate = function(ctx) {
+  return new BooleanValue(ctx.node.nodeType == DOM_ELEMENT_NODE);
+}
+
+function NodeTestText() {}
+
+NodeTestText.prototype.evaluate = function(ctx) {
+  return new BooleanValue(ctx.node.nodeType == DOM_TEXT_NODE);
+}
+
+function NodeTestComment() {}
+
+NodeTestComment.prototype.evaluate = function(ctx) {
+  return new BooleanValue(ctx.node.nodeType == DOM_COMMENT_NODE);
+}
+
+function NodeTestPI(target) {
+  this.target = target;
+}
+
+NodeTestPI.prototype.evaluate = function(ctx) {
+  return new
+  BooleanValue(ctx.node.nodeType == DOM_PROCESSING_INSTRUCTION_NODE &&
+               (!this.target || ctx.node.nodeName == this.target));
+}
+
+function NodeTestNC(nsprefix) {
+  this.regex = new RegExp("^" + nsprefix + ":");
+  this.nsprefix = nsprefix;
+}
+
+NodeTestNC.prototype.evaluate = function(ctx) {
+  var n = ctx.node;
+  return new BooleanValue(this.regex.match(n.nodeName));
+}
+
+function NodeTestName(name) {
+  this.name = name;
+}
+
+NodeTestName.prototype.evaluate = function(ctx) {
+  var n = ctx.node;
+  // NOTE (Patrick Lightbody): this change allows node selection to be case-insensitive
+  return new BooleanValue(n.nodeName.toUpperCase() == this.name.toUpperCase());
+}
+
+function PredicateExpr(expr) {
+  this.expr = expr;
+}
+
+PredicateExpr.prototype.evaluate = function(ctx) {
+  var v = this.expr.evaluate(ctx);
+  if (v.type == 'number') {
+    // NOTE(mesch): Internally, position is represented starting with
+    // 0, however in XPath position starts with 1. See functions
+    // position() and last().
+    return new BooleanValue(ctx.position == v.numberValue() - 1);
+  } else {
+    return new BooleanValue(v.booleanValue());
+  }
+};
+
+function FunctionCallExpr(name) {
+  this.name = name;
+  this.args = [];
+}
+
+FunctionCallExpr.prototype.appendArg = function(arg) {
+  this.args.push(arg);
+};
+
+FunctionCallExpr.prototype.evaluate = function(ctx) {
+  var fn = '' + this.name.value;
+  var f = this.xpathfunctions[fn];
+  if (f) {
+    return f.call(this, ctx);
+  } else {
+    Log.write('XPath NO SUCH FUNCTION ' + fn);
+    return new BooleanValue(false);
+  }
+};
+
+FunctionCallExpr.prototype.xpathfunctions = {
+  'last': function(ctx) {
+    assert(this.args.length == 0);
+    // NOTE(mesch): XPath position starts at 1.
+    return new NumberValue(ctx.nodelist.length);
+  },
+
+  'position': function(ctx) {
+    assert(this.args.length == 0);
+    // NOTE(mesch): XPath position starts at 1.
+    return new NumberValue(ctx.position + 1);
+  },
+
+  'count': function(ctx) {
+    assert(this.args.length == 1);
+    var v = this.args[0].evaluate(ctx);
+    return new NumberValue(v.nodeSetValue().length);
+  },
+
+  'id': function(ctx) {
+    assert(this.args.length == 1);
+    var e = this.args[0].evaluate(ctx);
+    var ret = [];
+    var ids;
+    if (e.type == 'node-set') {
+      ids = [];
+      for (var i = 0; i < e.length; ++i) {
+        var v = xmlValue(e[i]).split(/\s+/);
+        for (var ii = 0; ii < v.length; ++ii) {
+          ids.push(v[ii]);
+        }
+      }
+    } else {
+      ids = e.stringValue().split(/\s+/);
+    }
+    var contextNode = ctx.node;
+    var d;
+    if (contextNode.nodeName == "#document") {
+        d = contextNode;
+    } else {
+        d = contextNode.ownerDocument;
+    }
+    for (var i = 0; i < ids.length; ++i) {
+      var n = d.getElementById(ids[i]);
+      if (n) {
+        ret.push(n);
+      }
+    }
+    return new NodeSetValue(ret);
+  },
+
+  'local-name': function(ctx) {
+    alert('not implmented yet: XPath function local-name()');
+  },
+
+  'namespace-uri': function(ctx) {
+    alert('not implmented yet: XPath function namespace-uri()');
+  },
+
+  'name': function(ctx) {
+    assert(this.args.length == 1 || this.args.length == 0);
+    var n;
+    if (this.args.length == 0) {
+      n = [ ctx.node ];
+    } else {
+      n = this.args[0].evaluate(ctx).nodeSetValue();
+    }
+
+    if (n.length == 0) {
+      return new StringValue('');
+    } else {
+      return new StringValue(n[0].nodeName);
+    }
+  },
+
+  'string':  function(ctx) {
+    assert(this.args.length == 1 || this.args.length == 0);
+    if (this.args.length == 0) {
+      return new StringValue(new NodeSetValue([ ctx.node ]).stringValue());
+    } else {
+      return new StringValue(this.args[0].evaluate(ctx).stringValue());
+    }
+  },
+
+  'concat': function(ctx) {
+    var ret = '';
+    for (var i = 0; i < this.args.length; ++i) {
+      ret += this.args[i].evaluate(ctx).stringValue();
+    }
+    return new StringValue(ret);
+  },
+
+  'starts-with': function(ctx) {
+    assert(this.args.length == 2);
+    var s0 = this.args[0].evaluate(ctx).stringValue();
+    var s1 = this.args[1].evaluate(ctx).stringValue();
+    return new BooleanValue(s0.indexOf(s1) == 0);
+  },
+
+  'contains': function(ctx) {
+    assert(this.args.length == 2);
+    var s0 = this.args[0].evaluate(ctx).stringValue();
+    var s1 = this.args[1].evaluate(ctx).stringValue();
+    return new BooleanValue(s0.indexOf(s1) != -1);
+  },
+
+  'substring-before': function(ctx) {
+    assert(this.args.length == 2);
+    var s0 = this.args[0].evaluate(ctx).stringValue();
+    var s1 = this.args[1].evaluate(ctx).stringValue();
+    var i = s0.indexOf(s1);
+    var ret;
+    if (i == -1) {
+      ret = '';
+    } else {
+      ret = s0.substr(0,i);
+    }
+    return new StringValue(ret);
+  },
+
+  'substring-after': function(ctx) {
+    assert(this.args.length == 2);
+    var s0 = this.args[0].evaluate(ctx).stringValue();
+    var s1 = this.args[1].evaluate(ctx).stringValue();
+    var i = s0.indexOf(s1);
+    var ret;
+    if (i == -1) {
+      ret = '';
+    } else {
+      ret = s0.substr(i + s1.length);
+    }
+    return new StringValue(ret);
+  },
+
+  'substring': function(ctx) {
+    // NOTE: XPath defines the position of the first character in a
+    // string to be 1, in JavaScript this is 0 ([XPATH] Section 4.2).
+    assert(this.args.length == 2 || this.args.length == 3);
+    var s0 = this.args[0].evaluate(ctx).stringValue();
+    var s1 = this.args[1].evaluate(ctx).numberValue();
+    var ret;
+    if (this.args.length == 2) {
+      var i1 = Math.max(0, Math.round(s1) - 1);
+      ret = s0.substr(i1);
+
+    } else {
+      var s2 = this.args[2].evaluate(ctx).numberValue();
+      var i0 = Math.round(s1) - 1;
+      var i1 = Math.max(0, i0);
+      var i2 = Math.round(s2) - Math.max(0, -i0);
+      ret = s0.substr(i1, i2);
+    }
+    return new StringValue(ret);
+  },
+
+  'string-length': function(ctx) {
+    var s;
+    if (this.args.length > 0) {
+      s = this.args[0].evaluate(ctx).stringValue();
+    } else {
+      s = new NodeSetValue([ ctx.node ]).stringValue();
+    }
+    return new NumberValue(s.length);
+  },
+
+  'normalize-space': function(ctx) {
+    var s;
+    if (this.args.length > 0) {
+      s = this.args[0].evaluate(ctx).stringValue();
+    } else {
+      s = new NodeSetValue([ ctx.node ]).stringValue();
+    }
+    s = s.replace(/^\s*/,'').replace(/\s*$/,'').replace(/\s+/g, ' ');
+    return new StringValue(s);
+  },
+
+  'translate': function(ctx) {
+    assert(this.args.length == 3);
+    var s0 = this.args[0].evaluate(ctx).stringValue();
+    var s1 = this.args[1].evaluate(ctx).stringValue();
+    var s2 = this.args[2].evaluate(ctx).stringValue();
+
+    for (var i = 0; i < s1.length; ++i) {
+      s0 = s0.replace(new RegExp(s1.charAt(i), 'g'), s2.charAt(i));
+    }
+    return new StringValue(s0);
+  },
+
+  'boolean': function(ctx) {
+    assert(this.args.length == 1);
+    return new BooleanValue(this.args[0].evaluate(ctx).booleanValue());
+  },
+
+  'not': function(ctx) {
+    assert(this.args.length == 1);
+    var ret = !this.args[0].evaluate(ctx).booleanValue();
+    return new BooleanValue(ret);
+  },
+
+  'true': function(ctx) {
+    assert(this.args.length == 0);
+    return new BooleanValue(true);
+  },
+
+  'false': function(ctx) {
+    assert(this.args.length == 0);
+    return new BooleanValue(false);
+  },
+
+  'lang': function(ctx) {
+    assert(this.args.length == 1);
+    var lang = this.args[0].evaluate(ctx).stringValue();
+    var xmllang;
+    var n = ctx.node;
+    while (n && n != n.parentNode /* just in case ... */) {
+      xmllang = n.getAttribute('xml:lang');
+      if (xmllang) {
+        break;
+      }
+      n = n.parentNode;
+    }
+    if (!xmllang) {
+      return new BooleanValue(false);
+    } else {
+      var re = new RegExp('^' + lang + '$', 'i');
+      return new BooleanValue(xmllang.match(re) ||
+                              xmllang.replace(/_.*$/,'').match(re));
+    }
+  },
+
+  'number': function(ctx) {
+    assert(this.args.length == 1 || this.args.length == 0);
+
+    if (this.args.length == 1) {
+      return new NumberValue(this.args[0].evaluate(ctx).numberValue());
+    } else {
+      return new NumberValue(new NodeSetValue([ ctx.node ]).numberValue());
+    }
+  },
+
+  'sum': function(ctx) {
+    assert(this.args.length == 1);
+    var n = this.args[0].evaluate(ctx).nodeSetValue();
+    var sum = 0;
+    for (var i = 0; i < n.length; ++i) {
+      sum += xmlValue(n[i]) - 0;
+    }
+    return new NumberValue(sum);
+  },
+
+  'floor': function(ctx) {
+    assert(this.args.length == 1);
+    var num = this.args[0].evaluate(ctx).numberValue();
+    return new NumberValue(Math.floor(num));
+  },
+
+  'ceiling': function(ctx) {
+    assert(this.args.length == 1);
+    var num = this.args[0].evaluate(ctx).numberValue();
+    return new NumberValue(Math.ceil(num));
+  },
+
+  'round': function(ctx) {
+    assert(this.args.length == 1);
+    var num = this.args[0].evaluate(ctx).numberValue();
+    return new NumberValue(Math.round(num));
+  },
+
+  // TODO(mesch): The following functions are custom. There is a
+  // standard that defines how to add functions, which should be
+  // applied here.
+
+  'ext-join': function(ctx) {
+    assert(this.args.length == 2);
+    var nodes = this.args[0].evaluate(ctx).nodeSetValue();
+    var delim = this.args[1].evaluate(ctx).stringValue();
+    var ret = '';
+    for (var i = 0; i < nodes.length; ++i) {
+      if (ret) {
+        ret += delim;
+      }
+      ret += xmlValue(nodes[i]);
+    }
+    return new StringValue(ret);
+  },
+
+  // ext-if() evaluates and returns its second argument, if the
+  // boolean value of its first argument is true, otherwise it
+  // evaluates and returns its third argument.
+
+  'ext-if': function(ctx) {
+    assert(this.args.length == 3);
+    if (this.args[0].evaluate(ctx).booleanValue()) {
+      return this.args[1].evaluate(ctx);
+    } else {
+      return this.args[2].evaluate(ctx);
+    }
+  },
+
+  'ext-sprintf': function(ctx) {
+    assert(this.args.length >= 1);
+    var args = [];
+    for (var i = 0; i < this.args.length; ++i) {
+      args.push(this.args[i].evaluate(ctx).stringValue());
+    }
+    return new StringValue(sprintf.apply(null, args));
+  },
+
+  // ext-cardinal() evaluates its single argument as a number, and
+  // returns the current node that many times. It can be used in the
+  // select attribute to iterate over an integer range.
+  
+  'ext-cardinal': function(ctx) {
+    assert(this.args.length >= 1);
+    var c = this.args[0].evaluate(ctx).numberValue();
+    var ret = [];
+    for (var i = 0; i < c; ++i) {
+      ret.push(ctx.node);
+    }
+    return new NodeSetValue(ret);
+  }
+};
+
+function UnionExpr(expr1, expr2) {
+  this.expr1 = expr1;
+  this.expr2 = expr2;
+}
+
+UnionExpr.prototype.evaluate = function(ctx) {
+  var nodes1 = this.expr1.evaluate(ctx).nodeSetValue();
+  var nodes2 = this.expr2.evaluate(ctx).nodeSetValue();
+  var I1 = nodes1.length;
+  for (var i2 = 0; i2 < nodes2.length; ++i2) {
+    for (var i1 = 0; i1 < I1; ++i1) {
+      if (nodes1[i1] == nodes2[i2]) {
+        // break inner loop and continue outer loop, labels confuse
+        // the js compiler, so we don't use them here.
+        i1 = I1;
+      }
+    }
+    nodes1.push(nodes2[i2]);
+  }
+  return new NodeSetValue(nodes2);
+};
+
+function PathExpr(filter, rel) {
+  this.filter = filter;
+  this.rel = rel;
+}
+
+PathExpr.prototype.evaluate = function(ctx) {
+  var nodes = this.filter.evaluate(ctx).nodeSetValue();
+  var nodes1 = [];
+  for (var i = 0; i < nodes.length; ++i) {
+    var nodes0 = this.rel.evaluate(ctx.clone(nodes[i], i, nodes)).nodeSetValue();
+    for (var ii = 0; ii < nodes0.length; ++ii) {
+      nodes1.push(nodes0[ii]);
+    }
+  }
+  return new NodeSetValue(nodes1);
+};
+
+function FilterExpr(expr, predicate) {
+  this.expr = expr;
+  this.predicate = predicate;
+}
+
+FilterExpr.prototype.evaluate = function(ctx) {
+  var nodes = this.expr.evaluate(ctx).nodeSetValue();
+  for (var i = 0; i < this.predicate.length; ++i) {
+    var nodes0 = nodes;
+    nodes = [];
+    for (var j = 0; j < nodes0.length; ++j) {
+      var n = nodes0[j];
+      if (this.predicate[i].evaluate(ctx.clone(n, j, nodes0)).booleanValue()) {
+        nodes.push(n);
+      }
+    }
+  }
+
+  return new NodeSetValue(nodes);
+}
+
+function UnaryMinusExpr(expr) {
+  this.expr = expr;
+}
+
+UnaryMinusExpr.prototype.evaluate = function(ctx) {
+  return new NumberValue(-this.expr.evaluate(ctx).numberValue());
+};
+
+function BinaryExpr(expr1, op, expr2) {
+  this.expr1 = expr1;
+  this.expr2 = expr2;
+  this.op = op;
+}
+
+BinaryExpr.prototype.evaluate = function(ctx) {
+  var ret;
+  switch (this.op.value) {
+    case 'or':
+      ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() ||
+                             this.expr2.evaluate(ctx).booleanValue());
+      break;
+
+    case 'and':
+      ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() &&
+                             this.expr2.evaluate(ctx).booleanValue());
+      break;
+
+    case '+':
+      ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() +
+                            this.expr2.evaluate(ctx).numberValue());
+      break;
+
+    case '-':
+      ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() -
+                            this.expr2.evaluate(ctx).numberValue());
+      break;
+
+    case '*':
+      ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() *
+                            this.expr2.evaluate(ctx).numberValue());
+      break;
+
+    case 'mod':
+      ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() %
+                            this.expr2.evaluate(ctx).numberValue());
+      break;
+
+    case 'div':
+      ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() /
+                            this.expr2.evaluate(ctx).numberValue());
+      break;
+
+    case '=':
+      ret = this.compare(ctx, function(x1, x2) { return x1 == x2; });
+      break;
+
+    case '!=':
+      ret = this.compare(ctx, function(x1, x2) { return x1 != x2; });
+      break;
+
+    case '<':
+      ret = this.compare(ctx, function(x1, x2) { return x1 < x2; });
+      break;
+
+    case '<=':
+      ret = this.compare(ctx, function(x1, x2) { return x1 <= x2; });
+      break;
+
+    case '>':
+      ret = this.compare(ctx, function(x1, x2) { return x1 > x2; });
+      break;
+
+    case '>=':
+      ret = this.compare(ctx, function(x1, x2) { return x1 >= x2; });
+      break;
+
+    default:
+      alert('BinaryExpr.evaluate: ' + this.op.value);
+  }
+  return ret;
+};
+
+BinaryExpr.prototype.compare = function(ctx, cmp) {
+  var v1 = this.expr1.evaluate(ctx);
+  var v2 = this.expr2.evaluate(ctx);
+
+  var ret;
+  if (v1.type == 'node-set' && v2.type == 'node-set') {
+    var n1 = v1.nodeSetValue();
+    var n2 = v2.nodeSetValue();
+    ret = false;
+    for (var i1 = 0; i1 < n1.length; ++i1) {
+      for (var i2 = 0; i2 < n2.length; ++i2) {
+        if (cmp(xmlValue(n1[i1]), xmlValue(n2[i2]))) {
+          ret = true;
+          // Break outer loop. Labels confuse the jscompiler and we
+          // don't use them.
+          i2 = n2.length;
+          i1 = n1.length;
+        }
+      }
+    }
+
+  } else if (v1.type == 'node-set' || v2.type == 'node-set') {
+
+    if (v1.type == 'number') {
+      var s = v1.numberValue();
+      var n = v2.nodeSetValue();
+
+      ret = false;
+      for (var i = 0;  i < n.length; ++i) {
+        var nn = xmlValue(n[i]) - 0;
+        if (cmp(s, nn)) {
+          ret = true;
+          break;
+        }
+      }
+
+    } else if (v2.type == 'number') {
+      var n = v1.nodeSetValue();
+      var s = v2.numberValue();
+
+      ret = false;
+      for (var i = 0;  i < n.length; ++i) {
+        var nn = xmlValue(n[i]) - 0;
+        if (cmp(nn, s)) {
+          ret = true;
+          break;
+        }
+      }
+
+    } else if (v1.type == 'string') {
+      var s = v1.stringValue();
+      var n = v2.nodeSetValue();
+
+      ret = false;
+      for (var i = 0;  i < n.length; ++i) {
+        var nn = xmlValue(n[i]);
+        if (cmp(s, nn)) {
+          ret = true;
+          break;
+        }
+      }
+
+    } else if (v2.type == 'string') {
+      var n = v1.nodeSetValue();
+      var s = v2.stringValue();
+
+      ret = false;
+      for (var i = 0;  i < n.length; ++i) {
+        var nn = xmlValue(n[i]);
+        if (cmp(nn, s)) {
+          ret = true;
+          break;
+        }
+      }
+
+    } else {
+      ret = cmp(v1.booleanValue(), v2.booleanValue());
+    }
+
+  } else if (v1.type == 'boolean' || v2.type == 'boolean') {
+    ret = cmp(v1.booleanValue(), v2.booleanValue());
+
+  } else if (v1.type == 'number' || v2.type == 'number') {
+    ret = cmp(v1.numberValue(), v2.numberValue());
+
+  } else {
+    ret = cmp(v1.stringValue(), v2.stringValue());
+  }
+
+  return new BooleanValue(ret);
+}
+
+function LiteralExpr(value) {
+  this.value = value;
+}
+
+LiteralExpr.prototype.evaluate = function(ctx) {
+  return new StringValue(this.value);
+};
+
+function NumberExpr(value) {
+  this.value = value;
+}
+
+NumberExpr.prototype.evaluate = function(ctx) {
+  return new NumberValue(this.value);
+};
+
+function VariableExpr(name) {
+  this.name = name;
+}
+
+VariableExpr.prototype.evaluate = function(ctx) {
+  return ctx.getVariable(this.name);
+}
+
+// Factory functions for semantic values (i.e. Expressions) of the
+// productions in the grammar. When a production is matched to reduce
+// the current parse state stack, the function is called with the
+// semantic values of the matched elements as arguments, and returns
+// another semantic value. The semantic value is a node of the parse
+// tree, an expression object with an evaluate() method that evaluates the
+// expression in an actual context. These factory functions are used
+// in the specification of the grammar rules, below.
+
+function makeTokenExpr(m) {
+  return new TokenExpr(m);
+}
+
+function passExpr(e) {
+  return e;
+}
+
+function makeLocationExpr1(slash, rel) {
+  rel.absolute = true;
+  return rel;
+}
+
+function makeLocationExpr2(dslash, rel) {
+  rel.absolute = true;
+  rel.prependStep(makeAbbrevStep(dslash.value));
+  return rel;
+}
+
+function makeLocationExpr3(slash) {
+  var ret = new LocationExpr();
+  ret.appendStep(makeAbbrevStep('.'));
+  ret.absolute = true;
+  return ret;
+}
+
+function makeLocationExpr4(dslash) {
+  var ret = new LocationExpr();
+  ret.absolute = true;
+  ret.appendStep(makeAbbrevStep(dslash.value));
+  return ret;
+}
+
+function makeLocationExpr5(step) {
+  var ret = new LocationExpr();
+  ret.appendStep(step);
+  return ret;
+}
+
+function makeLocationExpr6(rel, slash, step) {
+  rel.appendStep(step);
+  return rel;
+}
+
+function makeLocationExpr7(rel, dslash, step) {
+  rel.appendStep(makeAbbrevStep(dslash.value));
+  rel.appendStep(step);
+  return rel;
+}
+
+function makeStepExpr1(dot) {
+  return makeAbbrevStep(dot.value);
+}
+
+function makeStepExpr2(ddot) {
+  return makeAbbrevStep(ddot.value);
+}
+
+function makeStepExpr3(axisname, axis, nodetest) {
+  return new StepExpr(axisname.value, nodetest);
+}
+
+function makeStepExpr4(at, nodetest) {
+  return new StepExpr('attribute', nodetest);
+}
+
+function makeStepExpr5(nodetest) {
+  return new StepExpr('child', nodetest);
+}
+
+function makeStepExpr6(step, predicate) {
+  step.appendPredicate(predicate);
+  return step;
+}
+
+function makeAbbrevStep(abbrev) {
+  switch (abbrev) {
+  case '//':
+    return new StepExpr('descendant-or-self', new NodeTestAny);
+
+  case '.':
+    return new StepExpr('self', new NodeTestAny);
+
+  case '..':
+    return new StepExpr('parent', new NodeTestAny);
+  }
+}
+
+function makeNodeTestExpr1(asterisk) {
+  return new NodeTestElement;
+}
+
+function makeNodeTestExpr2(ncname, colon, asterisk) {
+  return new NodeTestNC(ncname.value);
+}
+
+function makeNodeTestExpr3(qname) {
+  return new NodeTestName(qname.value);
+}
+
+function makeNodeTestExpr4(typeo, parenc) {
+  var type = typeo.value.replace(/\s*\($/, '');
+  switch(type) {
+  case 'node':
+    return new NodeTestAny;
+
+  case 'text':
+    return new NodeTestText;
+
+  case 'comment':
+    return new NodeTestComment;
+
+  case 'processing-instruction':
+    return new NodeTestPI;
+  }
+}
+
+function makeNodeTestExpr5(typeo, target, parenc) {
+  var type = typeo.replace(/\s*\($/, '');
+  if (type != 'processing-instruction') {
+    throw type + ' ' + Error().stack;
+  }
+  return new NodeTestPI(target.value);
+}
+
+function makePredicateExpr(pareno, expr, parenc) {
+  return new PredicateExpr(expr);
+}
+
+function makePrimaryExpr(pareno, expr, parenc) {
+  return expr;
+}
+
+function makeFunctionCallExpr1(name, pareno, parenc) {
+  return new FunctionCallExpr(name);
+}
+
+function makeFunctionCallExpr2(name, pareno, arg1, args, parenc) {
+  var ret = new FunctionCallExpr(name);
+  ret.appendArg(arg1);
+  for (var i = 0; i < args.length; ++i) {
+    ret.appendArg(args[i]);
+  }
+  return ret;
+}
+
+function makeArgumentExpr(comma, expr) {
+  return expr;
+}
+
+function makeUnionExpr(expr1, pipe, expr2) {
+  return new UnionExpr(expr1, expr2);
+}
+
+function makePathExpr1(filter, slash, rel) {
+  return new PathExpr(filter, rel);
+}
+
+function makePathExpr2(filter, dslash, rel) {
+  rel.prependStep(makeAbbrevStep(dslash.value));
+  return new PathExpr(filter, rel);
+}
+
+function makeFilterExpr(expr, predicates) {
+  if (predicates.length > 0) {
+    return new FilterExpr(expr, predicates);
+  } else {
+    return expr;
+  }
+}
+
+function makeUnaryMinusExpr(minus, expr) {
+  return new UnaryMinusExpr(expr);
+}
+
+function makeBinaryExpr(expr1, op, expr2) {
+  return new BinaryExpr(expr1, op, expr2);
+}
+
+function makeLiteralExpr(token) {
+  // remove quotes from the parsed value:
+  var value = token.value.substring(1, token.value.length - 1);
+  return new LiteralExpr(value);
+}
+
+function makeNumberExpr(token) {
+  return new NumberExpr(token.value);
+}
+
+function makeVariableReference(dollar, name) {
+  return new VariableExpr(name.value);
+}
+
+// Used before parsing for optimization of common simple cases. See
+// the begin of xpathParse() for which they are.
+function makeSimpleExpr(expr) {
+  if (expr.charAt(0) == '$') {
+    return new VariableExpr(expr.substr(1));
+  } else if (expr.charAt(0) == '@') {
+    var a = new NodeTestName(expr.substr(1));
+    var b = new StepExpr('attribute', a);
+    var c = new LocationExpr();
+    c.appendStep(b);
+    return c;
+  } else if (expr.match(/^[0-9]+$/)) {
+    return new NumberExpr(expr);
+  } else {
+    var a = new NodeTestName(expr);
+    var b = new StepExpr('child', a);
+    var c = new LocationExpr();
+    c.appendStep(b);
+    return c;
+  }
+}
+
+function makeSimpleExpr2(expr) {
+  var steps = expr.split('/');
+  var c = new LocationExpr();
+  for (var i in steps) {
+    var a = new NodeTestName(steps[i]);
+    var b = new StepExpr('child', a);
+    c.appendStep(b);
+  }
+  return c;
+}
+
+// The axes of XPath expressions.
+
+var xpathAxis = {
+  ANCESTOR_OR_SELF: 'ancestor-or-self',
+  ANCESTOR: 'ancestor',
+  ATTRIBUTE: 'attribute',
+  CHILD: 'child',
+  DESCENDANT_OR_SELF: 'descendant-or-self',
+  DESCENDANT: 'descendant',
+  FOLLOWING_SIBLING: 'following-sibling',
+  FOLLOWING: 'following',
+  NAMESPACE: 'namespace',
+  PARENT: 'parent',
+  PRECEDING_SIBLING: 'preceding-sibling',
+  PRECEDING: 'preceding',
+  SELF: 'self'
+};
+
+var xpathAxesRe = [
+    xpathAxis.ANCESTOR_OR_SELF,
+    xpathAxis.ANCESTOR,
+    xpathAxis.ATTRIBUTE,
+    xpathAxis.CHILD,
+    xpathAxis.DESCENDANT_OR_SELF,
+    xpathAxis.DESCENDANT,
+    xpathAxis.FOLLOWING_SIBLING,
+    xpathAxis.FOLLOWING,
+    xpathAxis.NAMESPACE,
+    xpathAxis.PARENT,
+    xpathAxis.PRECEDING_SIBLING,
+    xpathAxis.PRECEDING,
+    xpathAxis.SELF
+].join('|');
+
+
+// The tokens of the language. The label property is just used for
+// generating debug output. The prec property is the precedence used
+// for shift/reduce resolution. Default precedence is 0 as a lookahead
+// token and 2 on the stack. TODO(mesch): this is certainly not
+// necessary and too complicated. Simplify this!
+
+// NOTE: tabular formatting is the big exception, but here it should
+// be OK.
+
+var TOK_PIPE =   { label: "|",   prec:   17, re: new RegExp("^\\|") };
+var TOK_DSLASH = { label: "//",  prec:   19, re: new RegExp("^//")  };
+var TOK_SLASH =  { label: "/",   prec:   30, re: new RegExp("^/")   };
+var TOK_AXIS =   { label: "::",  prec:   20, re: new RegExp("^::")  };
+var TOK_COLON =  { label: ":",   prec: 1000, re: new RegExp("^:")  };
+var TOK_AXISNAME = { label: "[axis]", re: new RegExp('^(' + xpathAxesRe + ')') };
+var TOK_PARENO = { label: "(",   prec:   34, re: new RegExp("^\\(") };
+var TOK_PARENC = { label: ")",               re: new RegExp("^\\)") };
+var TOK_DDOT =   { label: "..",  prec:   34, re: new RegExp("^\\.\\.") };
+var TOK_DOT =    { label: ".",   prec:   34, re: new RegExp("^\\.") };
+var TOK_AT =     { label: "@",   prec:   34, re: new RegExp("^@")   };
+
+var TOK_COMMA =  { label: ",",               re: new RegExp("^,") };
+
+var TOK_OR =     { label: "or",  prec:   10, re: new RegExp("^or\\b") };
+var TOK_AND =    { label: "and", prec:   11, re: new RegExp("^and\\b") };
+var TOK_EQ =     { label: "=",   prec:   12, re: new RegExp("^=")   };
+var TOK_NEQ =    { label: "!=",  prec:   12, re: new RegExp("^!=")  };
+var TOK_GE =     { label: ">=",  prec:   13, re: new RegExp("^>=")  };
+var TOK_GT =     { label: ">",   prec:   13, re: new RegExp("^>")   };
+var TOK_LE =     { label: "<=",  prec:   13, re: new RegExp("^<=")  };
+var TOK_LT =     { label: "<",   prec:   13, re: new RegExp("^<")   };
+var TOK_PLUS =   { label: "+",   prec:   14, re: new RegExp("^\\+"), left: true };
+var TOK_MINUS =  { label: "-",   prec:   14, re: new RegExp("^\\-"), left: true };
+var TOK_DIV =    { label: "div", prec:   15, re: new RegExp("^div\\b"), left: true };
+var TOK_MOD =    { label: "mod", prec:   15, re: new RegExp("^mod\\b"), left: true };
+
+var TOK_BRACKO = { label: "[",   prec:   32, re: new RegExp("^\\[") };
+var TOK_BRACKC = { label: "]",               re: new RegExp("^\\]") };
+var TOK_DOLLAR = { label: "$",               re: new RegExp("^\\$") };
+
+var TOK_NCNAME = { label: "[ncname]", re: new RegExp('^[a-z][-\\w]*','i') };
+
+var TOK_ASTERISK = { label: "*", prec: 15, re: new RegExp("^\\*"), left: true };
+var TOK_LITERALQ = { label: "[litq]", prec: 20, re: new RegExp("^'[^\\']*'") };
+var TOK_LITERALQQ = {
+  label: "[litqq]",
+  prec: 20,
+  re: new RegExp('^"[^\\"]*"')
+};
+
+var TOK_NUMBER  = {
+  label: "[number]",
+  prec: 35,
+  re: new RegExp('^\\d+(\\.\\d*)?') };
+
+var TOK_QNAME = {
+  label: "[qname]",
+  re: new RegExp('^([a-z][-\\w]*:)?[a-z][-\\w]*','i')
+};
+
+var TOK_NODEO = {
+  label: "[nodetest-start]",
+  re: new RegExp('^(processing-instruction|comment|text|node)\\(')
+};
+
+// The table of the tokens of our grammar, used by the lexer: first
+// column the tag, second column a regexp to recognize it in the
+// input, third column the precedence of the token, fourth column a
+// factory function for the semantic value of the token.
+//
+// NOTE: order of this list is important, because the first match
+// counts. Cf. DDOT and DOT, and AXIS and COLON.
+
+var xpathTokenRules = [
+    TOK_DSLASH,
+    TOK_SLASH,
+    TOK_DDOT,
+    TOK_DOT,
+    TOK_AXIS,
+    TOK_COLON,
+    TOK_AXISNAME,
+    TOK_NODEO,
+    TOK_PARENO,
+    TOK_PARENC,
+    TOK_BRACKO,
+    TOK_BRACKC,
+    TOK_AT,
+    TOK_COMMA,
+    TOK_OR,
+    TOK_AND,
+    TOK_NEQ,
+    TOK_EQ,
+    TOK_GE,
+    TOK_GT,
+    TOK_LE,
+    TOK_LT,
+    TOK_PLUS,
+    TOK_MINUS,
+    TOK_ASTERISK,
+    TOK_PIPE,
+    TOK_MOD,
+    TOK_DIV,
+    TOK_LITERALQ,
+    TOK_LITERALQQ,
+    TOK_NUMBER,
+    TOK_QNAME,
+    TOK_NCNAME,
+    TOK_DOLLAR
+];
+
+// All the nonterminals of the grammar. The nonterminal objects are
+// identified by object identity; the labels are used in the debug
+// output only.
+var XPathLocationPath = { label: "LocationPath" };
+var XPathRelativeLocationPath = { label: "RelativeLocationPath" };
+var XPathAbsoluteLocationPath = { label: "AbsoluteLocationPath" };
+var XPathStep = { label: "Step" };
+var XPathNodeTest = { label: "NodeTest" };
+var XPathPredicate = { label: "Predicate" };
+var XPathLiteral = { label: "Literal" };
+var XPathExpr = { label: "Expr" };
+var XPathPrimaryExpr = { label: "PrimaryExpr" };
+var XPathVariableReference = { label: "Variablereference" };
+var XPathNumber = { label: "Number" };
+var XPathFunctionCall = { label: "FunctionCall" };
+var XPathArgumentRemainder = { label: "ArgumentRemainder" };
+var XPathPathExpr = { label: "PathExpr" };
+var XPathUnionExpr = { label: "UnionExpr" };
+var XPathFilterExpr = { label: "FilterExpr" };
+var XPathDigits = { label: "Digits" };
+
+var xpathNonTerminals = [
+    XPathLocationPath,
+    XPathRelativeLocationPath,
+    XPathAbsoluteLocationPath,
+    XPathStep,
+    XPathNodeTest,
+    XPathPredicate,
+    XPathLiteral,
+    XPathExpr,
+    XPathPrimaryExpr,
+    XPathVariableReference,
+    XPathNumber,
+    XPathFunctionCall,
+    XPathArgumentRemainder,
+    XPathPathExpr,
+    XPathUnionExpr,
+    XPathFilterExpr,
+    XPathDigits
+];
+
+// Quantifiers that are used in the productions of the grammar.
+var Q_01 = { label: "?" };
+var Q_MM = { label: "*" };
+var Q_1M = { label: "+" };
+
+// Tag for left associativity (right assoc is implied by undefined).
+var ASSOC_LEFT = true;
+
+// The productions of the grammar. Columns of the table:
+//
+// - target nonterminal,
+// - pattern,
+// - precedence,
+// - semantic value factory
+//
+// The semantic value factory is a function that receives parse tree
+// nodes from the stack frames of the matched symbols as arguments and
+// returns an a node of the parse tree. The node is stored in the top
+// stack frame along with the target object of the rule. The node in
+// the parse tree is an expression object that has an evaluate() method
+// and thus evaluates XPath expressions.
+//
+// The precedence is used to decide between reducing and shifting by
+// comparing the precendence of the rule that is candidate for
+// reducing with the precedence of the look ahead token. Precedence of
+// -1 means that the precedence of the tokens in the pattern is used
+// instead. TODO: It shouldn't be necessary to explicitly assign
+// precedences to rules.
+
+// DGF Where do these precedence rules come from?  I just tweaked some
+// of these numbers to fix bug SEL-486.
+
+var xpathGrammarRules =
+  [
+   [ XPathLocationPath, [ XPathRelativeLocationPath ], 18,
+     passExpr ],
+   [ XPathLocationPath, [ XPathAbsoluteLocationPath ], 18,
+     passExpr ],
+
+   [ XPathAbsoluteLocationPath, [ TOK_SLASH, XPathRelativeLocationPath ], 18, 
+     makeLocationExpr1 ],
+   [ XPathAbsoluteLocationPath, [ TOK_DSLASH, XPathRelativeLocationPath ], 18,
+     makeLocationExpr2 ],
+
+   [ XPathAbsoluteLocationPath, [ TOK_SLASH ], 0,
+     makeLocationExpr3 ],
+   [ XPathAbsoluteLocationPath, [ TOK_DSLASH ], 0,
+     makeLocationExpr4 ],
+
+   [ XPathRelativeLocationPath, [ XPathStep ], 31,
+     makeLocationExpr5 ],
+   [ XPathRelativeLocationPath,
+     [ XPathRelativeLocationPath, TOK_SLASH, XPathStep ], 31,
+     makeLocationExpr6 ],
+   [ XPathRelativeLocationPath,
+     [ XPathRelativeLocationPath, TOK_DSLASH, XPathStep ], 31,
+     makeLocationExpr7 ],
+
+   [ XPathStep, [ TOK_DOT ], 33,
+     makeStepExpr1 ],
+   [ XPathStep, [ TOK_DDOT ], 33,
+     makeStepExpr2 ],
+   [ XPathStep,
+     [ TOK_AXISNAME, TOK_AXIS, XPathNodeTest ], 33,
+     makeStepExpr3 ],
+   [ XPathStep, [ TOK_AT, XPathNodeTest ], 33,
+     makeStepExpr4 ],
+   [ XPathStep, [ XPathNodeTest ], 33,
+     makeStepExpr5 ],
+   [ XPathStep, [ XPathStep, XPathPredicate ], 33,
+     makeStepExpr6 ],
+
+   [ XPathNodeTest, [ TOK_ASTERISK ], 33,
+     makeNodeTestExpr1 ],
+   [ XPathNodeTest, [ TOK_NCNAME, TOK_COLON, TOK_ASTERISK ], 33,
+     makeNodeTestExpr2 ],
+   [ XPathNodeTest, [ TOK_QNAME ], 33,
+     makeNodeTestExpr3 ],
+   [ XPathNodeTest, [ TOK_NODEO, TOK_PARENC ], 33,
+     makeNodeTestExpr4 ],
+   [ XPathNodeTest, [ TOK_NODEO, XPathLiteral, TOK_PARENC ], 33,
+     makeNodeTestExpr5 ],
+
+   [ XPathPredicate, [ TOK_BRACKO, XPathExpr, TOK_BRACKC ], 33,
+     makePredicateExpr ],
+
+   [ XPathPrimaryExpr, [ XPathVariableReference ], 33,
+     passExpr ],
+   [ XPathPrimaryExpr, [ TOK_PARENO, XPathExpr, TOK_PARENC ], 33,
+     makePrimaryExpr ],
+   [ XPathPrimaryExpr, [ XPathLiteral ], 30,
+     passExpr ],
+   [ XPathPrimaryExpr, [ XPathNumber ], 30,
+     passExpr ],
+   [ XPathPrimaryExpr, [ XPathFunctionCall ], 31,
+     passExpr ],
+
+   [ XPathFunctionCall, [ TOK_QNAME, TOK_PARENO, TOK_PARENC ], -1,
+     makeFunctionCallExpr1 ],
+   [ XPathFunctionCall,
+     [ TOK_QNAME, TOK_PARENO, XPathExpr, XPathArgumentRemainder, Q_MM,
+       TOK_PARENC ], -1,
+     makeFunctionCallExpr2 ],
+   [ XPathArgumentRemainder, [ TOK_COMMA, XPathExpr ], -1,
+     makeArgumentExpr ],
+
+   [ XPathUnionExpr, [ XPathPathExpr ], 20,
+     passExpr ],
+   [ XPathUnionExpr, [ XPathUnionExpr, TOK_PIPE, XPathPathExpr ], 20,
+     makeUnionExpr ],
+
+   [ XPathPathExpr, [ XPathLocationPath ], 20, 
+     passExpr ], 
+   [ XPathPathExpr, [ XPathFilterExpr ], 19, 
+     passExpr ], 
+   [ XPathPathExpr, 
+     [ XPathFilterExpr, TOK_SLASH, XPathRelativeLocationPath ], 19,
+     makePathExpr1 ],
+   [ XPathPathExpr,
+     [ XPathFilterExpr, TOK_DSLASH, XPathRelativeLocationPath ], 19,
+     makePathExpr2 ],
+
+   [ XPathFilterExpr, [ XPathPrimaryExpr, XPathPredicate, Q_MM ], 31,
+     makeFilterExpr ], 
+
+   [ XPathExpr, [ XPathPrimaryExpr ], 16,
+     passExpr ],
+   [ XPathExpr, [ XPathUnionExpr ], 16,
+     passExpr ],
+
+   [ XPathExpr, [ TOK_MINUS, XPathExpr ], -1,
+     makeUnaryMinusExpr ],
+
+   [ XPathExpr, [ XPathExpr, TOK_OR, XPathExpr ], -1,
+     makeBinaryExpr ],
+   [ XPathExpr, [ XPathExpr, TOK_AND, XPathExpr ], -1,
+     makeBinaryExpr ],
+
+   [ XPathExpr, [ XPathExpr, TOK_EQ, XPathExpr ], -1,
+     makeBinaryExpr ],
+   [ XPathExpr, [ XPathExpr, TOK_NEQ, XPathExpr ], -1,
+     makeBinaryExpr ],
+
+   [ XPathExpr, [ XPathExpr, TOK_LT, XPathExpr ], -1,
+     makeBinaryExpr ],
+   [ XPathExpr, [ XPathExpr, TOK_LE, XPathExpr ], -1,
+     makeBinaryExpr ],
+   [ XPathExpr, [ XPathExpr, TOK_GT, XPathExpr ], -1,
+     makeBinaryExpr ],
+   [ XPathExpr, [ XPathExpr, TOK_GE, XPathExpr ], -1,
+     makeBinaryExpr ],
+
+   [ XPathExpr, [ XPathExpr, TOK_PLUS, XPathExpr ], -1,
+     makeBinaryExpr, ASSOC_LEFT ],
+   [ XPathExpr, [ XPathExpr, TOK_MINUS, XPathExpr ], -1,
+     makeBinaryExpr, ASSOC_LEFT ],
+
+   [ XPathExpr, [ XPathExpr, TOK_ASTERISK, XPathExpr ], -1,
+     makeBinaryExpr, ASSOC_LEFT ],
+   [ XPathExpr, [ XPathExpr, TOK_DIV, XPathExpr ], -1,
+     makeBinaryExpr, ASSOC_LEFT ],
+   [ XPathExpr, [ XPathExpr, TOK_MOD, XPathExpr ], -1,
+     makeBinaryExpr, ASSOC_LEFT ],
+
+   [ XPathLiteral, [ TOK_LITERALQ ], -1,
+     makeLiteralExpr ],
+   [ XPathLiteral, [ TOK_LITERALQQ ], -1,
+     makeLiteralExpr ],
+
+   [ XPathNumber, [ TOK_NUMBER ], -1,
+     makeNumberExpr ],
+
+   [ XPathVariableReference, [ TOK_DOLLAR, TOK_QNAME ], 200,
+     makeVariableReference ]
+   ];
+
+// That function computes some optimizations of the above data
+// structures and will be called right here. It merely takes the
+// counter variables out of the global scope.
+
+var xpathRules = [];
+
+function xpathParseInit() {
+  if (xpathRules.length) {
+    return;
+  }
+
+  // Some simple optimizations for the xpath expression parser: sort
+  // grammar rules descending by length, so that the longest match is
+  // first found.
+
+  xpathGrammarRules.sort(function(a,b) {
+    var la = a[1].length;
+    var lb = b[1].length;
+    if (la < lb) {
+      return 1;
+    } else if (la > lb) {
+      return -1;
+    } else {
+      return 0;
+    }
+  });
+
+  var k = 1;
+  for (var i = 0; i < xpathNonTerminals.length; ++i) {
+    xpathNonTerminals[i].key = k++;
+  }
+
+  for (i = 0; i < xpathTokenRules.length; ++i) {
+    xpathTokenRules[i].key = k++;
+  }
+
+  Log.write('XPath parse INIT: ' + k + ' rules');
+
+  // Another slight optimization: sort the rules into bins according
+  // to the last element (observing quantifiers), so we can restrict
+  // the match against the stack to the subest of rules that match the
+  // top of the stack.
+  //
+  // TODO(mesch): What we actually want is to compute states as in
+  // bison, so that we don't have to do any explicit and iterated
+  // match against the stack.
+
+  function push_(array, position, element) {
+    if (!array[position]) {
+      array[position] = [];
+    }
+    array[position].push(element);
+  }
+
+  for (i = 0; i < xpathGrammarRules.length; ++i) {
+    var rule = xpathGrammarRules[i];
+    var pattern = rule[1];
+
+    for (var j = pattern.length - 1; j >= 0; --j) {
+      if (pattern[j] == Q_1M) {
+        push_(xpathRules, pattern[j-1].key, rule);
+        break;
+        
+      } else if (pattern[j] == Q_MM || pattern[j] == Q_01) {
+        push_(xpathRules, pattern[j-1].key, rule);
+        --j;
+
+      } else {
+        push_(xpathRules, pattern[j].key, rule);
+        break;
+      }
+    }
+  }
+
+  Log.write('XPath parse INIT: ' + xpathRules.length + ' rule bins');
+  
+  var sum = 0;
+  mapExec(xpathRules, function(i) {
+    if (i) {
+      sum += i.length;
+    }
+  });
+  
+  Log.write('XPath parse INIT: ' + (sum / xpathRules.length) + ' average bin size');
+}
+
+// Local utility functions that are used by the lexer or parser.
+
+function xpathCollectDescendants(nodelist, node) {
+  for (var n = node.firstChild; n; n = n.nextSibling) {
+    nodelist.push(n);
+    arguments.callee(nodelist, n);
+  }
+}
+
+function xpathCollectDescendantsReverse(nodelist, node) {
+  for (var n = node.lastChild; n; n = n.previousSibling) {
+    nodelist.push(n);
+    arguments.callee(nodelist, n);
+  }
+}
+
+
+// The entry point for the library: match an expression against a DOM
+// node. Returns an XPath value.
+function xpathDomEval(expr, node) {
+  var expr1 = xpathParse(expr);
+  var ret = expr1.evaluate(new ExprContext(node));
+  return ret;
+}
+
+// Utility function to sort a list of nodes. Used by xsltSort() and
+// nxslSelect().
+function xpathSort(input, sort) {
+  if (sort.length == 0) {
+    return;
+  }
+
+  var sortlist = [];
+
+  for (var i = 0; i < input.nodelist.length; ++i) {
+    var node = input.nodelist[i];
+    var sortitem = { node: node, key: [] };
+    var context = input.clone(node, 0, [ node ]);
+    
+    for (var j = 0; j < sort.length; ++j) {
+      var s = sort[j];
+      var value = s.expr.evaluate(context);
+
+      var evalue;
+      if (s.type == 'text') {
+        evalue = value.stringValue();
+      } else if (s.type == 'number') {
+        evalue = value.numberValue();
+      }
+      sortitem.key.push({ value: evalue, order: s.order });
+    }
+
+    // Make the sort stable by adding a lowest priority sort by
+    // id. This is very convenient and furthermore required by the
+    // spec ([XSLT] - Section 10 Sorting).
+    sortitem.key.push({ value: i, order: 'ascending' });
+
+    sortlist.push(sortitem);
+  }
+
+  sortlist.sort(xpathSortByKey);
+
+  var nodes = [];
+  for (var i = 0; i < sortlist.length; ++i) {
+    nodes.push(sortlist[i].node);
+  }
+  input.nodelist = nodes;
+  input.setNode(nodes[0], 0);
+}
+
+
+// Sorts by all order criteria defined. According to the JavaScript
+// spec ([ECMA] Section 11.8.5), the compare operators compare strings
+// as strings and numbers as numbers.
+//
+// NOTE: In browsers which do not follow the spec, this breaks only in
+// the case that numbers should be sorted as strings, which is very
+// uncommon.
+
+function xpathSortByKey(v1, v2) {
+  // NOTE: Sort key vectors of different length never occur in
+  // xsltSort.
+
+  for (var i = 0; i < v1.key.length; ++i) {
+    var o = v1.key[i].order == 'descending' ? -1 : 1;
+    if (v1.key[i].value > v2.key[i].value) {
+      return +1 * o;
+    } else if (v1.key[i].value < v2.key[i].value) {
+      return -1 * o;
+    }
+  }
+
+  return 0;
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/doc.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/doc.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/doc.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,131 @@
+function stripDoPrefix(name) {
+    if ("do" == name.substring(0, 2)) {
+        var firstChar = name.substring(2, 3).toLowerCase();
+        return firstChar + name.substring(3);
+    }
+    return name;
+}
+
+function extractArgList(source) {
+    var argString = source.match(/function\s*\(([^)]*)\)/)[1];
+    argString = argString.replace(/\s/, "");
+    var args = argString.split(/,/);
+    // Split will return a single empty string if there are no args
+    if ("" == args[0]) {
+        args.shift();
+    }
+    return args;
+}
+
+function extractInitialComment(source) {
+    var commentMatcher = source.match(/function\s*\w*\s*\([^)]*\)\s*{\s*\/\*\*((?:.|[\r\n])*?)\*\//);
+    if (commentMatcher == null) return "";
+    if (commentMatcher.length < 2) {
+        return "";
+    }
+    var comment = commentMatcher[1];
+    comment = comment.replace(/\n\s*\* ?/g, "\n");
+    comment = comment.replace(/^[\s\r\n]*/, "");
+    comment = comment.replace(/[\s\r\n]*$/, "");
+    return comment;
+}
+
+function handleTags(name, args, comment) {
+    var argMap = new Object();
+    for (var i = 0; i < args.length; i++) {
+        argMap[args[i]] = "";
+    }
+    var tagStart = comment.search(/@(param|return)/);
+    if (tagStart == -1) {
+        if ("do" != name.substring(0, 2)) {
+            throw "Command " + name + " doesn't start with 'do', or else you forget to specify a @return tag";
+        }
+        comment = comment.replace(/^[\s\r\n]*/, "");
+        comment = comment.replace(/[\s\r\n]*$/, "");
+        print("<comment>" + comment + "</comment>\n");
+        return;
+    }
+    tagString = comment.substring(tagStart);
+    comment = comment.substring(0, tagStart - 1);
+    tagString = tagString.replace(/\n/g, " ");
+    var tags = tagString.match(/(@[^@]*)/g);
+    for (var i = 0; i < tags.length; i++) {
+        var tag = tags[i];
+        var paramMatch = tag.match(/^@param\s+(\S+)\s+(.*)/);
+        if (paramMatch) {
+            var arg = paramMatch[1];
+            var argDesc = paramMatch[2];
+            if (argMap[arg] == null) {
+                throw ("Comment error: " + name + " @param " + arg + " does not match any argument");
+            }
+            argDesc = argDesc.replace(/^\s+/, "");
+            argDesc = argDesc.replace(/\s+$/, "");
+            argMap[arg] = argDesc;
+        }
+        var returnMatch = tag.match(/^@return\s+(\S+)\s+(.*)/);
+        if (returnMatch) {
+            var returnType = returnMatch[1];
+            if (!returnType.match(/^(string|number|boolean)(\[\])?$/)) {
+                throw ("Comment error: " + name + " @return type " + returnType + " is invalid; must be one of: string, number, boolean, string[], number[], boolean[]");
+            }
+            var returnDesc = returnMatch[2];
+            if (returnDesc == null) {
+                throw ("Comment error: " + name + " @return does not have a description");
+            }
+            returnDesc = returnDesc.replace(/^\s+/, "");
+            returnDesc = returnDesc.replace(/\s+$/, "");
+            print("<return type=\"" + returnType + "\">" + returnDesc + "</return>\n");
+        }
+    }
+    
+    for (var i = 0; i < args.length; i++) {
+        if ("" == argMap[args[i]]) throw ("Comment error: " + name + " param " + args[i] + " has no description");
+        print("<param name=\"" + args[i] + "\">" + argMap[args[i]] + "</param>\n");
+    }
+    comment = comment.replace(/^[\s\r\n]*/, "");
+    comment = comment.replace(/[\s\r\n]*$/, "");
+    print("<comment>" + comment + "</comment>\n");
+}
+
+function getFileContentOfSeleniumAPIFile(filename) {
+    importPackage(java.io);
+
+    var apiJsFile = new BufferedReader(new FileReader(filename));
+    var content;
+    var line;
+    while ((line = apiJsFile.readLine()) != null) {
+        content += line + '\n';
+    }
+    apiJsFile.close();
+    return content;
+}
+
+var content = getFileContentOfSeleniumAPIFile(arguments[0]);
+if (arguments[1]) {
+    content += getFileContentOfSeleniumAPIFile(arguments[1]);
+}
+
+print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+print("<apidoc>\n");
+
+print("<top>" + extractInitialComment(content) + "</top>\n");
+
+var commandPattern = /Selenium.prototype.((do|get|assert|is)\w*)\s*=\s*function\s*\([^)]*\)\s*{\s*(\/\*\*((?:.|[\r\n])*?)\*\/|[^\/])/ig;
+var result;
+while ((result = commandPattern.exec(content)) != null) {
+    var name = result[1];
+    var source = result[0];
+    print("<function name=\"" + stripDoPrefix(name) + "\">\n");
+    var args = extractArgList(source);
+    var comment = extractInitialComment(source);
+    if (comment == null || "" == comment) {
+        throw ("Comment for " + name + " was blank!");
+    }
+    handleTags(name, args, comment);
+    print("</function>\n");
+}
+
+print("</apidoc>");
+
+
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/doc2html.xml
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/doc2html.xml	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/doc2html.xml	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,639 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE xslt [
+  <!--Used to control code intenting -->
+  <!ENTITY nbsp "&#xa0;">
+]>
+<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:strip-space elements="*"/><xsl:output method="html"/>
+
+<!-- TOP LEVEL -->
+<xsl:template match="/">
+    <html><head><title>Selenium Reference</title></head><body>
+	<xsl:call-template name="header" />
+	<xsl:apply-templates select="//top" />
+	<h2>Selenium Actions</h2>
+	<dl>
+		<xsl:apply-templates select="//function[not(return) and not(starts-with(@name, 'assert'))]" mode="action">
+		    <xsl:sort select="@name" />
+		</xsl:apply-templates>
+	</dl>
+	<h2>Selenium Accessors</h2>
+	<dl>
+		<xsl:apply-templates select="//function[starts-with(@name, 'assert')]" mode="assertion">
+		    <xsl:sort select="@name" />
+		</xsl:apply-templates>
+		<xsl:apply-templates select="//function[return]" mode="accessor">
+		    <xsl:sort select="@name" />
+		</xsl:apply-templates>
+	</dl>
+	<xsl:call-template name="footer" />
+	</body></html>
+</xsl:template>
+
+<xsl:template name="header">
+	<h1>Selenium Reference</h1>
+	<h2>Concepts</h2>
+	<p>A <strong>command</strong> is what tells Selenium what to do. Selenium commands come in three 'flavors': <strong>Actions</strong>, <strong>Accessors</strong> and <strong>Assertions</strong>.
+	Each command call is one line in the test table of the form:</p>
+	<blockquote>
+	<table border="1" class="table">
+	<colgroup>
+	<col width="39%" />
+	<col width="33%" />
+	<col width="28%" />
+	</colgroup>
+	<tbody valign="top">
+	<tr><td>command</td>
+	<td>target</td>
+	<td>value</td>
+	</tr>
+	</tbody>
+	</table>
+	</blockquote>
+	<p><strong>Actions</strong> are commands that generally manipulate the state of the application. They do things like &quot;click this link&quot; and &quot;select that option&quot;. If an Action fails, or has an error, the execution of the current test is stopped.</p>
+	<p>Many Actions can be called with the &quot;AndWait&quot; suffix, e.g. "clickAndWait".
+	This suffix tells Selenium that the action will cause the browser to make a call to the server,
+	and that Selenium should wait for a new page to load.</p>
+	
+	<p><strong>Accessors</strong> examine the state of the application and store the results in variables, e.g. "storeTitle".  They are also used to automatically generate Assertions.</p>
+	<p><strong>Assertions</strong> are like Accessors, but they verify that the state of the application conforms to what is expected. Examples include &quot;make sure the page title is X&quot; and &quot;verify that this checkbox is checked&quot;.</p>
+	
+	<p>All Selenium Assertions can be used in 3 modes: &quot;assert&quot;, &quot;verify&quot;, and "waitFor". For example, you can "assertText", "verifyText" and "waitForText".  When an &quot;assert&quot; fails, the test is aborted. When a &quot;verify&quot; fails, the test will continue execution, logging the failure.  This allows a single &quot;assert&quot; to ensure that the application is on the correct page, followed by a bunch of &quot;verify&quot; assertions to test form field values, labels, etc.</p>
+	
+	<p>&quot;waitFor&quot; commands wait for some condition to become true (which can be useful for testing Ajax applications).
+	They will succeed immediately if the condition is already true.
+	However, they will fail and halt the test if the condition does not become true within the current timeout setting
+	(see the <strong>setTimeout</strong> action below).
+	</p>
+	<p><strong>Element Locators</strong> tell Selenium which HTML element a command refers to. Many commands require an Element Locator as the &quot;target&quot; attribute. Examples of Element Locators include &quot;elementId&quot; and &quot;document.forms[0].element&quot;. These are described more clearly in the next section.</p>
+	<p><strong>Patterns</strong> are used for various reasons, e.g. to specify the expected value of an input field, or identify a select option.  Selenium supports various types of pattern, including regular-expressions, all of which are described in more detail below.</p>
+	
+	
+</xsl:template>
+
+<xsl:template name="footer">
+	<h2><a name="parameter-construction-and-variables">Parameter construction and Variables</a></h2>
+	<blockquote>
+	<p>All Selenium command parameters can be constructed using both simple
+	variable substitution as well as full javascript. Both of these
+	mechanisms can access previously stored variables, but do so using
+	different syntax.</p>
+	<p><a name="storedVars"></a><strong>Stored Variables</strong></p>
+	<p>The commands <em>store</em>, <em>storeValue</em> and <em>storeText</em> can be used to store a variable
+	value for later access. Internally, these variables are stored in a map called &quot;storedVars&quot;,
+	with values keyed by the variable name. These commands are documented in the command reference.</p>
+	<p><strong>Variable substitution</strong></p>
+	<p>Variable substitution provides a simple way to include a previously stored variable in a
+	command parameter. This is a simple mechanism, by which the variable to substitute is indicated
+	by ${variableName}. Multiple variables can be substituted, and intermixed with static text.</p>
+	<p>Example:</p>
+	<blockquote>
+	<table border="1" class="table">
+	<colgroup>
+	<col width="18%" />
+	<col width="36%" />
+	<col width="45%" />
+	</colgroup>
+	<tbody valign="top">
+	<tr><td>store</td>
+	<td>Mr</td>
+	<td>title</td>
+	</tr>
+	<tr><td>storeValue</td>
+	<td>nameField</td>
+	<td>surname</td>
+	</tr>
+	<tr><td>store</td>
+	<td>${title} ${surname}</td>
+	<td>fullname</td>
+	</tr>
+	<tr><td>type</td>
+	<td>textElement</td>
+	<td>Full name is: ${fullname}</td>
+	</tr>
+	</tbody>
+	</table>
+	</blockquote>
+	<p><strong>Javascript evaluation</strong></p>
+	<p>Javascript evaluation provides the full power of javascript in constructing a command parameter.
+	To use this mechanism, the <em>entire</em> parameter value must be prefixed by
+	'javascript{' with a trailing '}'. The text inside the braces is evaluated as a javascript expression,
+	and can access previously stored variables using the <em>storedVars</em> map detailed above.
+	Note that variable substitution cannot be combined with javascript evaluation.</p>
+	<p>Example:</p>
+	<blockquote>
+	<table border="1" class="table">
+	<colgroup>
+	<col width="9%" />
+	<col width="44%" />
+	<col width="46%" />
+	</colgroup>
+	<tbody valign="top">
+	<tr><td>store</td>
+	<td>javascript{'merchant' + (new Date()).getTime()}</td>
+	<td>merchantId</td>
+	</tr>
+	<tr><td>type</td>
+	<td>textElement</td>
+	<td>javascript{storedVars['merchantId'].toUpperCase()}</td>
+	</tr>
+	</tbody>
+	</table>
+	</blockquote>
+	</blockquote>
+	
+	<div class="section" id="extending-selenium">
+	<h2><a name="extending-selenium">Extending Selenium</a></h2>
+	<blockquote>
+	<p>It can be quite simple to extend Selenium, adding your own actions, assertions and locator-strategies.
+	This is done with javascript by adding methods to the Selenium object prototype, and the PageBot
+	object prototype. On startup, Selenium will automatically look through methods on these prototypes,
+	using name patterns to recognise which ones are actions, assertions and locators.</p>
+	<p>The following examples try to give an indication of how Selenium can be extended with javascript.</p>
+	</blockquote>
+	<p><strong>Actions</strong></p>
+	<blockquote>
+	<p>All <em>doFoo</em> methods on the Selenium prototype are added as actions. For each action <em>foo</em> there
+	is also an action <em>fooAndWait</em> registered. An action method can take up to 2 parameters, which
+	will be passed the second and third column values in the test.</p>
+	<p>Example: Add a &quot;typeRepeated&quot; action to Selenium, which types the text twice into a text box.</p>
+	<pre class="literal-block">
+	Selenium.prototype.doTypeRepeated = function(locator, text) {
+	    // All locator-strategies are automatically handled by &quot;findElement&quot;
+	    var element = this.page().findElement(locator);
+	
+	    // Create the text to type
+	    var valueToType = text + text;
+	
+	    // Replace the element text with the new text
+	    this.page().replaceText(element, valueToType);
+	};
+	</pre>
+	</blockquote>
+	<p><strong>Accessors/Assertions</strong></p>
+	<blockquote>
+	<p>All <em>getFoo</em> and <em>isFoo</em> methods on the Selenium prototype are added as accessors (storeFoo). For each accessor there
+	is an <em>assertFoo</em>, <em>verifyFoo</em> and <em>waitForFoo</em> registered. An assert method can take up to 2 parameters, which
+	will be passed the second and third column values in the test.  You can also define your own assertions literally
+	as simple "assert" methods, which will also auto-generate "verify" and "waitFor" commands.</p>
+	<p>Example: Add a <em>valueRepeated</em> assertion, that makes sure that the element value
+	consists of the supplied text repeated. The 2 commands that would be available in tests would be
+	<em>assertValueRepeated</em> and <em>verifyValueRepeated</em>.</p>
+	<pre class="literal-block">
+	Selenium.prototype.assertValueRepeated = function(locator, text) {
+	    // All locator-strategies are automatically handled by &quot;findElement&quot;
+	    var element = this.page().findElement(locator);
+	
+	    // Create the text to verify
+	    var expectedValue = text + text;
+	
+	    // Get the actual element value
+	    var actualValue = element.value;
+	
+	    // Make sure the actual value matches the expected
+	    Assert.matches(expectedValue, actualValue);
+	};
+	</pre>
+	</blockquote>
+	<p><strong>Automatic availability of storeFoo, assertFoo, assertNotFoo, waitForFoo and waitForNotFoo for every getFoo</strong></p>
+	<blockquote>
+	<p>All <em>getFoo</em> and <em>isFoo</em> methods on the Selenium prototype automatically result in the availability
+	of storeFoo, assertFoo, assertNotFoo, verifyFoo, verifyNotFoo, waitForFoo, and waitForNotFoo commands.</p>
+	<p>Example, if you add a getTextLength() method, the following commands will automatically be available:
+	storeTextLength, assertTextLength, assertNotTextLength, verifyTextLength, verifyNotTextLength, waitForTextLength, and waitForNotTextLength commands.</p>
+	<pre class="literal-block">
+	Selenium.prototype.getTextLength = function(locator, text) {
+	    return this.getText(locator).length;
+	};
+	</pre>
+	
+	<p>Also note that the <em>assertValueRepeated</em> method described above could have been implemented using
+	isValueRepeated, with the added benefit of also automatically getting assertNotValueRepeated, storeValueRepeated,
+	waitForValueRepeated and waitForNotValueRepeated.</p>
+	</blockquote>
+	<p><strong>Locator Strategies</strong></p>
+	<blockquote>
+	<p>All <em>locateElementByFoo</em> methods on the PageBot prototype are added as locator-strategies. A locator strategy takes 2 parameters, the first being the locator string (minus the prefix), and the second being the document in which to search.</p>
+	<p>Example: Add a &quot;valuerepeated=&quot; locator, that finds the first element a value attribute equal to the the supplied value repeated.</p>
+	<pre class="literal-block">
+	// The &quot;inDocument&quot; is a the document you are searching.
+	PageBot.prototype.locateElementByValueRepeated = function(text, inDocument) {
+	    // Create the text to search for
+	    var expectedValue = text + text;
+	
+	    // Loop through all elements, looking for ones that have 
+	    // a value === our expected value
+	    var allElements = inDocument.getElementsByTagName(&quot;*&quot;);
+	    for (var i = 0; i &lt; allElements.length; i++) {
+	        var testElement = allElements[i];
+	        if (testElement.value &amp;&amp; testElement.value === expectedValue) {
+	            return testElement;
+	        }
+	    }
+	    return null;
+	};
+	</pre>
+	</blockquote>
+	<p><strong>user-extensions.js</strong></p>
+	<blockquote>
+	<p>By default, Selenium looks for a file called &quot;user-extensions.js&quot;, and loads the javascript code found in that file. This file provides a convenient location for adding features to Selenium, without needing to modify the core Selenium sources.</p>
+	<p>In the standard distibution, this file does not exist. Users can create this file and place their extension code in this common location, removing the need to modify the Selenium sources, and hopefully assisting with the upgrade process.</p>
+	</blockquote>
+	</div>
+</xsl:template>
+
+<!-- Ignore the top comment in iedoc.xml; just process all of its children -->
+<xsl:template match="top">
+	<xsl:apply-templates />
+</xsl:template>
+
+<xsl:template match="comment">
+	<xsl:apply-templates />
+</xsl:template>
+
+<!-- Print out assert* and all of its related assertions -->
+<xsl:template match="function" mode="assertion">
+	<dt><strong>
+	    <a>
+	        <xsl:attribute name="name">
+	            <xsl:value-of select="@name" />
+	        </xsl:attribute>
+	    </a>
+		<xsl:value-of select="@name" />
+		(
+			<xsl:apply-templates select="param" mode="declaration-action"/>
+		)
+	</strong></dt>
+	<dd>
+		<xsl:apply-templates select="comment" />
+		<p>Arguments:</p>
+		<ul>
+			<xsl:apply-templates select="param" mode="comment" />
+		</ul>
+		<p>Related Assertions, automatically generated:</p>
+		<ul>
+			<li> 
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">assertNot</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+			<li>
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">verify</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+			<li>
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">verifyNot</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+			<li>
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">waitFor</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+			<li>
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">waitForNot</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+		</ul>
+			
+	</dd>
+	<br/>
+</xsl:template>
+
+<!-- Print out store* and all of its related assertions -->
+<xsl:template match="function" mode="accessor">
+    <dt><strong>
+        <a>
+	        <xsl:attribute name="name">
+	            <xsl:call-template name="accessor-rename">
+        			<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+        			<xsl:with-param name="prefix">store</xsl:with-param>
+        		</xsl:call-template>
+	        </xsl:attribute>
+	    </a>
+		<xsl:call-template name="accessor-rename">
+			<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+			<xsl:with-param name="prefix">store</xsl:with-param>
+		</xsl:call-template>
+		
+		(
+			<xsl:apply-templates select="param" mode="declaration-accessor"/>
+			variableName
+		)
+		
+	</strong></dt>
+	<dd>
+		<xsl:apply-templates select="comment" />
+		<xsl:if test="count(./param) > 0">
+			<p>Arguments:</p>
+			<ul>
+				<xsl:apply-templates select="param" mode="comment" />
+				<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+			</ul>
+		</xsl:if>
+		<xsl:apply-templates select="return" />
+		<p>Related Assertions, automatically generated:</p>
+		<ul>
+		    <li> 
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">assert</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+			<li> 
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">assertNot</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+			<li>
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">verify</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+			<li>
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">verifyNot</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+			<li>
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">waitFor</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+			<li>
+				<xsl:call-template name="accessor-rename">
+					<xsl:with-param name="accessor"><xsl:value-of select="@name" /></xsl:with-param>
+					<xsl:with-param name="prefix">waitForNot</xsl:with-param>
+				</xsl:call-template>
+				(
+					<xsl:apply-templates select="param" mode="declaration-assertion"/>
+					<xsl:if test="./return/@type != 'boolean'">
+						<a href="#patterns">pattern</a>
+					</xsl:if>
+				)
+			</li>
+		</ul>
+			
+	</dd>
+	<br/>
+</xsl:template>
+
+<xsl:template match="function" mode="action">
+	<dt><strong>
+	    <a>
+	        <xsl:attribute name="name">
+	            <xsl:value-of select="@name" />
+	        </xsl:attribute>
+	    </a>
+		<xsl:value-of select="@name" />
+		(
+			<xsl:apply-templates select="param" mode="declaration-action"/>
+		)
+	</strong></dt>
+	<dd>
+		<xsl:apply-templates select="comment" />
+		<xsl:if test="count(./param) > 0">
+			<p>Arguments:</p>
+			<ul>
+				<xsl:apply-templates select="param" mode="comment" />
+			</ul>
+		</xsl:if>
+	</dd>
+	<br/>
+</xsl:template>
+
+<!-- In action mode, don't print out a comma after the last argument -->
+<xsl:template match="param" mode="declaration-action">
+	<xsl:value-of select="@name" />
+	<xsl:if test="position() != last()">
+		<xsl:text>,</xsl:text>
+	</xsl:if>
+</xsl:template>
+
+<!-- In accessor mode, always print out a comma, because we'll always append a final argument variableName -->
+<xsl:template match="param" mode="declaration-accessor">
+	<xsl:value-of select="@name" />,
+</xsl:template>
+
+<!-- In assertion mode, append a final pattern argument, unless the assertion is boolean, in which case no final argument -->
+<xsl:template match="param" mode="declaration-assertion">
+	<xsl:value-of select="@name" />
+	<xsl:if test="position() != last() or ../return/@type != 'boolean'">
+		<xsl:text>, </xsl:text>
+	</xsl:if>
+</xsl:template>
+
+<xsl:template match="param" mode="comment">
+	<li>
+		<xsl:value-of select="@name" />
+		<xsl:text> - </xsl:text>
+		<xsl:apply-templates/>
+	</li>
+</xsl:template>
+
+<xsl:template match="return">
+    <p><dl><dt>Returns: </dt><dd><xsl:apply-templates/></dd></dl></p>
+</xsl:template>
+
+<!-- When we encounter anything else, just copy it right on over! -->
+<xsl:template match="node()|@*" >
+   <xsl:copy>
+        <xsl:apply-templates select="node()|@*" />
+   </xsl:copy>
+</xsl:template> 
+
+<!-- Take an accessor named getFoo or isFoo and replace it with a new prefix, e.g. storeFoo -->
+<xsl:template name="accessor-rename">
+	<xsl:param name="accessor"/>
+    <xsl:param name="prefix"/>
+    <xsl:variable name="spaced">&nbsp;<xsl:value-of select="$accessor" /></xsl:variable>
+    <xsl:choose>
+    	<!-- If the accessor ends with "Present" and the prefix ends with "Not", then a simple replacement
+    	would get something ungrammatical, like assertNotTextPresent.  Instead, negate the prefix (assertNot becomes assert)
+    	and negate the accessor (TextPresent becomes TextNotPresent), so the command generated is
+    	assertTextNotPresent, which is much prettier -->
+    	<xsl:when test="contains(concat($prefix,'&nbsp;'),'Not&nbsp;') and contains(concat($accessor,'&nbsp;'),'Present&nbsp;')">
+    		<!-- negate the prefix -->
+    		<xsl:variable name="negatedPrefix">
+				<xsl:call-template name="search-and-replace">
+					<xsl:with-param name="input"><xsl:value-of select='$prefix' />&nbsp;</xsl:with-param>
+					<xsl:with-param name="search-string">Not&nbsp;</xsl:with-param>
+					<xsl:with-param name="replace-string"></xsl:with-param>
+				</xsl:call-template>
+			</xsl:variable>
+			<!-- negate the accessor -->
+			<xsl:variable name="negatedAccessor">
+				<xsl:call-template name="search-and-replace">
+					<xsl:with-param name="input"><xsl:value-of select='$accessor' />&nbsp;</xsl:with-param>
+					<xsl:with-param name="search-string">Present&nbsp;</xsl:with-param>
+					<xsl:with-param name="replace-string">NotPresent</xsl:with-param>
+				</xsl:call-template>
+			</xsl:variable>
+			<!-- replace "get" with the negated prefix -->
+			<xsl:variable name="getReplaced">
+				<xsl:call-template name="search-and-replace">
+					<xsl:with-param name="input">&nbsp;<xsl:value-of select='$negatedAccessor' /></xsl:with-param>
+					<xsl:with-param name="search-string">&nbsp;get</xsl:with-param>
+					<xsl:with-param name="replace-string"><xsl:value-of select='$negatedPrefix' /></xsl:with-param>
+				</xsl:call-template>
+			</xsl:variable>
+			<!-- replace "is" with the negated prefix -->
+			<xsl:variable name="isReplaced">
+				<xsl:call-template name="search-and-replace">
+					<xsl:with-param name="input"><xsl:value-of select='$getReplaced' /></xsl:with-param>
+					<xsl:with-param name="search-string">&nbsp;is</xsl:with-param>
+					<xsl:with-param name="replace-string"><xsl:value-of select='$negatedPrefix' /></xsl:with-param>
+				</xsl:call-template>
+			</xsl:variable>
+			<!-- replace "assert" with the negated prefix -->
+			<xsl:variable name="assertReplaced">
+				<xsl:call-template name="search-and-replace">
+					<xsl:with-param name="input"><xsl:value-of select='$isReplaced' /></xsl:with-param>
+					<xsl:with-param name="search-string">&nbsp;assert</xsl:with-param>
+					<xsl:with-param name="replace-string"><xsl:value-of select='$negatedPrefix' /></xsl:with-param>
+				</xsl:call-template>
+			</xsl:variable>
+			<!-- and print out the output -->
+			<xsl:value-of select="$assertReplaced"/>
+    	</xsl:when>
+    	<xsl:otherwise>
+    		<!-- replace "get" with the prefix -->
+		    <xsl:variable name="getReplaced">
+				<xsl:call-template name="search-and-replace">
+					<xsl:with-param name="input">&nbsp;<xsl:value-of select='$accessor' /></xsl:with-param>
+					<xsl:with-param name="search-string">&nbsp;get</xsl:with-param>
+					<xsl:with-param name="replace-string"><xsl:value-of select='$prefix' /></xsl:with-param>
+				</xsl:call-template>
+			</xsl:variable>
+			<!-- replace "is" with the prefix -->
+			<xsl:variable name="isReplaced">
+				<xsl:call-template name="search-and-replace">
+					<xsl:with-param name="input"><xsl:value-of select='$getReplaced' /></xsl:with-param>
+					<xsl:with-param name="search-string">&nbsp;is</xsl:with-param>
+					<xsl:with-param name="replace-string"><xsl:value-of select='$prefix' /></xsl:with-param>
+				</xsl:call-template>
+			</xsl:variable>
+			<!-- replace "assert" with the prefix -->
+			<xsl:variable name="assertReplaced">
+				<xsl:call-template name="search-and-replace">
+					<xsl:with-param name="input"><xsl:value-of select='$isReplaced' /></xsl:with-param>
+					<xsl:with-param name="search-string">&nbsp;assert</xsl:with-param>
+					<xsl:with-param name="replace-string"><xsl:value-of select='$prefix' /></xsl:with-param>
+				</xsl:call-template>
+			</xsl:variable>
+			<!-- and print out the output -->
+			<xsl:value-of select="$assertReplaced"/>
+		</xsl:otherwise>
+	</xsl:choose>
+	
+</xsl:template>
+
+
+<xsl:template name="search-and-replace">
+     <xsl:param name="input"/>
+     <xsl:param name="search-string"/>
+     <xsl:param name="replace-string"/>
+     <xsl:choose>
+          <!-- See if the input contains the search string -->
+          <xsl:when test="$search-string and 
+                           contains($input,$search-string)">
+          <!-- If so, then concatenate the substring before the search
+          string to the replacement string and to the result of
+          recursively applying this template to the remaining substring.
+          -->
+               <xsl:value-of 
+                    select="substring-before($input,$search-string)"/>
+               <xsl:value-of select="$replace-string"/>
+               <xsl:call-template name="search-and-replace">
+                    <xsl:with-param name="input"
+                    select="substring-after($input,$search-string)"/>
+                    <xsl:with-param name="search-string" 
+                    select="$search-string"/>
+                    <xsl:with-param name="replace-string" 
+                        select="$replace-string"/>
+               </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+               <!-- There are no more occurrences of the search string so 
+               just return the current input string -->
+               <xsl:value-of select="$input"/>
+          </xsl:otherwise>
+     </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/js.jar
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/doctool/js.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/index.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/index.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/index.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,63 @@
+<html>
+<head>
+<title>Selenium starting points</title>
+<script language="Javascript">
+function fixUnitTestLinks()
+{
+    var baseurl = document.location + "";
+    baseurl = baseurl.replace(/[^\/]*$/, '');
+    var links = document.getElementsByTagName("a");
+    for (var i = 0; i < links.length; i++) {
+        var link = links[i];
+        if (link.className == 'jsUnitSuite') {
+            link['href'] = link['href'].replace('BASEURL', baseurl);
+        }
+    }
+}
+</script>
+<style>
+li { margin-top: 6pt; }
+</style>
+</head>
+<body onload="fixUnitTestLinks();">
+
+<h1>Selenium</h1>
+<p>
+  <a href="core/TestRunner.html">Selenium TestRunner</a> - Select a test suite to run in Selenium
+</p>
+
+<p>
+<b>Acceptance tests:</b> These test-suites demonstrate/exercise the
+functionality of Selenium.
+</p>
+
+<ul>
+  <li>
+    <a href="core/TestRunner.html?test=../tests/TestSuite.html">Selenium TestSuite</a> - functional tests for Selenium. This suite of tests should pass in any supported browser.
+  </li>
+  <li>
+    <a href="core/TestRunner.html?test=../tests/ErrorCheckingTestSuite.html">Error Checking TestSuite</a> - tests for the error verification commands
+  </li>
+  <li>
+  	<a href="core/TestRunner.html?test=../tests/dogfood/TestSuite.html&multiWindow=true">Dogfood TestSuite</a> - test Selenium with Selenium
+  </li>
+</ul>
+
+<p><b>Unit-tests:</b> Use JsUnit to test Selenium internals.</p>
+
+<ul>
+
+  <li>
+      <a class="jsUnitSuite" href="jsunit/testRunner.html?testPage=BASEURLunittest/browserbot/suite.html">Selenium BrowserBot unit-tests</a>
+  </li>
+  <li>
+      <a class="jsUnitSuite" href="jsunit/testRunner.html?testPage=BASEURLunittest/htmlrunner/suite.html">Selenium TestRunner UI unit-tests</a>
+  </li>
+  <li>
+      <a class="jsUnitSuite" href="jsunit/testRunner.html?testPage=BASEURLunittest/jsmock/mock-tests.html">JsMock unit-tests</a>
+  </li>
+
+</ul>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/install-readme.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/install-readme.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/install-readme.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,9 @@
+Copy the "core" folder to a web accessible directory in the same web server as the application you want to test.
+In Apache, this would mean a subdirectory of "htdocs".
+
+Because of javascript security settings standard in most browsers, Selenium Core needs to be available on the same host and port as your application.  (If this doesn't work for you, you may need to try Selenium Remote Control or Selenium IDE.)
+
+Once deployed to the server, to run Selenium's self-tests, check out:
+http://<webservername>:<port>/core/TestRunner.html
+
+Read the website for more details. (http://www.openqa.org/selenium-core/usage.html)

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/css/jsUnitStyle.css
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/css/jsUnitStyle.css	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/css/jsUnitStyle.css	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,50 @@
+body {
+    margin-top: 0;
+    margin-bottom: 0;
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+    color: #000;
+    font-size: 0.8em;
+    background-color: #fff;
+}
+
+a:link, a:visited {
+    color: #00F;
+}
+
+a:hover {
+    color: #F00;
+}
+
+h1 {
+    font-size: 1.2em;
+    font-weight: bold;
+    color: #039;
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+h2 {
+    font-weight: bold;
+    color: #039;
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+h3 {
+    font-weight: bold;
+    color: #039;
+    text-decoration: underline;
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+h4 {
+    font-weight: bold;
+    color: #039;
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.jsUnitTestResultSuccess {
+    color: #000;
+}
+
+.jsUnitTestResultNotSuccess {
+    color: #F00;
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/css/readme
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/css/readme	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/css/readme	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,10 @@
+this file is required due to differences in behavior between Mozilla/Opera 
+and Internet Explorer.
+
+main-data.html calls kickOffTests() which calls top.testManager.start()
+in the top most frame. top.testManager.start() initializes the output 
+frames using document.write and HTML containing a relative <link> to the 
+jsUnitStyle.css file. In MSIE, the base href used to find the CSS file is 
+that of the top level frame however in Mozilla/Opera the base href is
+that of main-data.html. This leads to not-found for the jsUnitStyle.css
+in Mozilla/Opera. Creating app/css/jsUnitStyle.css works around this problem.

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/emptyPage.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/emptyPage.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/emptyPage.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>emptyPage</title>
+</head>
+
+<body>
+</body>
+</html>
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitCore.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitCore.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitCore.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,534 @@
+var JSUNIT_UNDEFINED_VALUE;
+var JSUNIT_VERSION = 2.2;
+var isTestPageLoaded = false;
+
+//hack for NS62 bug
+function jsUnitFixTop() {
+    var tempTop = top;
+    if (!tempTop) {
+        tempTop = window;
+        while (tempTop.parent) {
+            tempTop = tempTop.parent;
+            if (tempTop.top && tempTop.top.jsUnitTestSuite) {
+                tempTop = tempTop.top;
+                break;
+            }
+        }
+    }
+    try {
+        window.top = tempTop;
+    } catch (e) {
+    }
+}
+
+jsUnitFixTop();
+
+/**
+ + * A more functional typeof
+ + * @param Object o
+ + * @return String
+ + */
+function _trueTypeOf(something) {
+    var result = typeof something;
+    try {
+        switch (result) {
+            case 'string':
+            case 'boolean':
+            case 'number':
+                break;
+            case 'object':
+            case 'function':
+                switch (something.constructor)
+                        {
+                    case String:
+                        result = 'String';
+                        break;
+                    case Boolean:
+                        result = 'Boolean';
+                        break;
+                    case Number:
+                        result = 'Number';
+                        break;
+                    case Array:
+                        result = 'Array';
+                        break;
+                    case RegExp:
+                        result = 'RegExp';
+                        break;
+                    case Function:
+                        result = 'Function';
+                        break;
+                    default:
+                        var m = something.constructor.toString().match(/function\s*([^( ]+)\(/);
+                        if (m)
+                            result = m[1];
+                        else
+                            break;
+                }
+                break;
+        }
+    }
+    finally {
+        result = result.substr(0, 1).toUpperCase() + result.substr(1);
+        return result;
+    }
+}
+
+function _displayStringForValue(aVar) {
+    var result = '<' + aVar + '>';
+    if (!(aVar === null || aVar === top.JSUNIT_UNDEFINED_VALUE)) {
+        result += ' (' + _trueTypeOf(aVar) + ')';
+    }
+    return result;
+}
+
+function fail(failureMessage) {
+    throw new JsUnitException("Call to fail()", failureMessage);
+}
+
+function error(errorMessage) {
+    var errorObject = new Object();
+    errorObject.description = errorMessage;
+    errorObject.stackTrace = getStackTrace();
+    throw errorObject;
+}
+
+function argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) {
+    return args.length == expectedNumberOfNonCommentArgs + 1;
+}
+
+function commentArg(expectedNumberOfNonCommentArgs, args) {
+    if (argumentsIncludeComments(expectedNumberOfNonCommentArgs, args))
+        return args[0];
+
+    return null;
+}
+
+function nonCommentArg(desiredNonCommentArgIndex, expectedNumberOfNonCommentArgs, args) {
+    return argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) ?
+           args[desiredNonCommentArgIndex] :
+           args[desiredNonCommentArgIndex - 1];
+}
+
+function _validateArguments(expectedNumberOfNonCommentArgs, args) {
+    if (!( args.length == expectedNumberOfNonCommentArgs ||
+           (args.length == expectedNumberOfNonCommentArgs + 1 && typeof(args[0]) == 'string') ))
+        error('Incorrect arguments passed to assert function');
+}
+
+function _assert(comment, booleanValue, failureMessage) {
+    if (!booleanValue)
+        throw new JsUnitException(comment, failureMessage);
+}
+
+function assert() {
+    _validateArguments(1, arguments);
+    var booleanValue = nonCommentArg(1, 1, arguments);
+
+    if (typeof(booleanValue) != 'boolean')
+        error('Bad argument to assert(boolean)');
+
+    _assert(commentArg(1, arguments), booleanValue === true, 'Call to assert(boolean) with false');
+}
+
+function assertTrue() {
+    _validateArguments(1, arguments);
+    var booleanValue = nonCommentArg(1, 1, arguments);
+
+    if (typeof(booleanValue) != 'boolean')
+        error('Bad argument to assertTrue(boolean)');
+
+    _assert(commentArg(1, arguments), booleanValue === true, 'Call to assertTrue(boolean) with false');
+}
+
+function assertFalse() {
+    _validateArguments(1, arguments);
+    var booleanValue = nonCommentArg(1, 1, arguments);
+
+    if (typeof(booleanValue) != 'boolean')
+        error('Bad argument to assertFalse(boolean)');
+
+    _assert(commentArg(1, arguments), booleanValue === false, 'Call to assertFalse(boolean) with true');
+}
+
+function assertEquals() {
+    _validateArguments(2, arguments);
+    var var1 = nonCommentArg(1, 2, arguments);
+    var var2 = nonCommentArg(2, 2, arguments);
+    _assert(commentArg(2, arguments), var1 === var2, 'Expected ' + _displayStringForValue(var1) + ' but was ' + _displayStringForValue(var2));
+}
+
+function assertNotEquals() {
+    _validateArguments(2, arguments);
+    var var1 = nonCommentArg(1, 2, arguments);
+    var var2 = nonCommentArg(2, 2, arguments);
+    _assert(commentArg(2, arguments), var1 !== var2, 'Expected not to be ' + _displayStringForValue(var2));
+}
+
+function assertNull() {
+    _validateArguments(1, arguments);
+    var aVar = nonCommentArg(1, 1, arguments);
+    _assert(commentArg(1, arguments), aVar === null, 'Expected ' + _displayStringForValue(null) + ' but was ' + _displayStringForValue(aVar));
+}
+
+function assertNotNull() {
+    _validateArguments(1, arguments);
+    var aVar = nonCommentArg(1, 1, arguments);
+    _assert(commentArg(1, arguments), aVar !== null, 'Expected not to be ' + _displayStringForValue(null));
+}
+
+function assertUndefined() {
+    _validateArguments(1, arguments);
+    var aVar = nonCommentArg(1, 1, arguments);
+    _assert(commentArg(1, arguments), aVar === top.JSUNIT_UNDEFINED_VALUE, 'Expected ' + _displayStringForValue(top.JSUNIT_UNDEFINED_VALUE) + ' but was ' + _displayStringForValue(aVar));
+}
+
+function assertNotUndefined() {
+    _validateArguments(1, arguments);
+    var aVar = nonCommentArg(1, 1, arguments);
+    _assert(commentArg(1, arguments), aVar !== top.JSUNIT_UNDEFINED_VALUE, 'Expected not to be ' + _displayStringForValue(top.JSUNIT_UNDEFINED_VALUE));
+}
+
+function assertNaN() {
+    _validateArguments(1, arguments);
+    var aVar = nonCommentArg(1, 1, arguments);
+    _assert(commentArg(1, arguments), isNaN(aVar), 'Expected NaN');
+}
+
+function assertNotNaN() {
+    _validateArguments(1, arguments);
+    var aVar = nonCommentArg(1, 1, arguments);
+    _assert(commentArg(1, arguments), !isNaN(aVar), 'Expected not NaN');
+}
+
+function assertObjectEquals() {
+    _validateArguments(2, arguments);
+    var var1 = nonCommentArg(1, 2, arguments);
+    var var2 = nonCommentArg(2, 2, arguments);
+    var type;
+    var msg = commentArg(2, arguments)?commentArg(2, arguments):'';
+    var isSame = (var1 === var2);
+    //shortpath for references to same object
+    var isEqual = ( (type = _trueTypeOf(var1)) == _trueTypeOf(var2) );
+    if (isEqual && !isSame) {
+        switch (type) {
+            case 'String':
+            case 'Number':
+                isEqual = (var1 == var2);
+                break;
+            case 'Boolean':
+            case 'Date':
+                isEqual = (var1 === var2);
+                break;
+            case 'RegExp':
+            case 'Function':
+                isEqual = (var1.toString() === var2.toString());
+                break;
+            default: //Object | Array
+                var i;
+                if (isEqual = (var1.length === var2.length))
+                    for (i in var1)
+                        assertObjectEquals(msg + ' found nested ' + type + '@' + i + '\n', var1[i], var2[i]);
+        }
+        _assert(msg, isEqual, 'Expected ' + _displayStringForValue(var1) + ' but was ' + _displayStringForValue(var2));
+    }
+}
+
+assertArrayEquals = assertObjectEquals;
+
+function assertEvaluatesToTrue() {
+    _validateArguments(1, arguments);
+    var value = nonCommentArg(1, 1, arguments);
+    if (!value)
+        fail(commentArg(1, arguments));
+}
+
+function assertEvaluatesToFalse() {
+    _validateArguments(1, arguments);
+    var value = nonCommentArg(1, 1, arguments);
+    if (value)
+        fail(commentArg(1, arguments));
+}
+
+function assertHTMLEquals() {
+    _validateArguments(2, arguments);
+    var var1 = nonCommentArg(1, 2, arguments);
+    var var2 = nonCommentArg(2, 2, arguments);
+    var var1Standardized = standardizeHTML(var1);
+    var var2Standardized = standardizeHTML(var2);
+
+    _assert(commentArg(2, arguments), var1Standardized === var2Standardized, 'Expected ' + _displayStringForValue(var1Standardized) + ' but was ' + _displayStringForValue(var2Standardized));
+}
+
+function assertHashEquals() {
+    _validateArguments(2, arguments);
+    var var1 = nonCommentArg(1, 2, arguments);
+    var var2 = nonCommentArg(2, 2, arguments);
+    for (var key in var1) {
+        assertNotUndefined("Expected hash had key " + key + " that was not found", var2[key]);
+        assertEquals(
+                "Value for key " + key + " mismatch - expected = " + var1[key] + ", actual = " + var2[key],
+                var1[key], var2[key]
+                );
+    }
+    for (var key in var2) {
+        assertNotUndefined("Actual hash had key " + key + " that was not expected", var1[key]);
+    }
+}
+
+function assertRoughlyEquals() {
+    _validateArguments(3, arguments);
+    var expected = nonCommentArg(1, 3, arguments);
+    var actual = nonCommentArg(2, 3, arguments);
+    var tolerance = nonCommentArg(3, 3, arguments);
+    assertTrue(
+            "Expected " + expected + ", but got " + actual + " which was more than " + tolerance + " away",
+            Math.abs(expected - actual) < tolerance
+            );
+}
+
+function assertContains() {
+    _validateArguments(2, arguments);
+    var contained = nonCommentArg(1, 2, arguments);
+    var container = nonCommentArg(2, 2, arguments);
+    assertTrue(
+            "Expected '" + container + "' to contain '" + contained + "'",
+            container.indexOf(contained) != -1
+            );
+}
+
+function standardizeHTML(html) {
+    var translator = document.createElement("DIV");
+    translator.innerHTML = html;
+    return translator.innerHTML;
+}
+
+function isLoaded() {
+    return isTestPageLoaded;
+}
+
+function setUp() {
+}
+
+function tearDown() {
+}
+
+function getFunctionName(aFunction) {
+    var regexpResult = aFunction.toString().match(/function(\s*)(\w*)/);
+    if (regexpResult && regexpResult.length >= 2 && regexpResult[2]) {
+        return regexpResult[2];
+    }
+    return 'anonymous';
+}
+
+function getStackTrace() {
+    var result = '';
+
+    if (typeof(arguments.caller) != 'undefined') { // IE, not ECMA
+        for (var a = arguments.caller; a != null; a = a.caller) {
+            result += '> ' + getFunctionName(a.callee) + '\n';
+            if (a.caller == a) {
+                result += '*';
+                break;
+            }
+        }
+    }
+    else { // Mozilla, not ECMA
+        // fake an exception so we can get Mozilla's error stack
+        var testExcp;
+        try
+        {
+            foo.bar;
+        }
+        catch(testExcp)
+        {
+            var stack = parseErrorStack(testExcp);
+            for (var i = 1; i < stack.length; i++)
+            {
+                result += '> ' + stack[i] + '\n';
+            }
+        }
+    }
+
+    return result;
+}
+
+function parseErrorStack(excp)
+{
+    var stack = [];
+    var name;
+
+    if (!excp || !excp.stack)
+    {
+        return stack;
+    }
+
+    var stacklist = excp.stack.split('\n');
+
+    for (var i = 0; i < stacklist.length - 1; i++)
+    {
+        var framedata = stacklist[i];
+
+        name = framedata.match(/^(\w*)/)[1];
+        if (!name) {
+            name = 'anonymous';
+        }
+
+        stack[stack.length] = name;
+    }
+    // remove top level anonymous functions to match IE
+
+    while (stack.length && stack[stack.length - 1] == 'anonymous')
+    {
+        stack.length = stack.length - 1;
+    }
+    return stack;
+}
+
+function JsUnitException(comment, message) {
+    this.isJsUnitException = true;
+    this.comment = comment;
+    this.jsUnitMessage = message;
+    this.stackTrace = getStackTrace();
+}
+
+function warn() {
+    if (top.tracer != null)
+        top.tracer.warn(arguments[0], arguments[1]);
+}
+
+function inform() {
+    if (top.tracer != null)
+        top.tracer.inform(arguments[0], arguments[1]);
+}
+
+function info() {
+    inform(arguments[0], arguments[1]);
+}
+
+function debug() {
+    if (top.tracer != null)
+        top.tracer.debug(arguments[0], arguments[1]);
+}
+
+function setJsUnitTracer(aJsUnitTracer) {
+    top.tracer = aJsUnitTracer;
+}
+
+function trim(str) {
+    if (str == null)
+        return null;
+
+    var startingIndex = 0;
+    var endingIndex = str.length - 1;
+
+    while (str.substring(startingIndex, startingIndex + 1) == ' ')
+        startingIndex++;
+
+    while (str.substring(endingIndex, endingIndex + 1) == ' ')
+        endingIndex--;
+
+    if (endingIndex < startingIndex)
+        return '';
+
+    return str.substring(startingIndex, endingIndex + 1);
+}
+
+function isBlank(str) {
+    return trim(str) == '';
+}
+
+// the functions push(anArray, anObject) and pop(anArray)
+// exist because the JavaScript Array.push(anObject) and Array.pop()
+// functions are not available in IE 5.0
+
+function push(anArray, anObject) {
+    anArray[anArray.length] = anObject;
+}
+function pop(anArray) {
+    if (anArray.length >= 1) {
+        delete anArray[anArray.length - 1];
+        anArray.length--;
+    }
+}
+
+function jsUnitGetParm(name)
+{
+    if (typeof(top.jsUnitParmHash[name]) != 'undefined')
+    {
+        return top.jsUnitParmHash[name];
+    }
+    return null;
+}
+
+if (top && typeof(top.xbDEBUG) != 'undefined' && top.xbDEBUG.on && top.testManager)
+{
+    top.xbDebugTraceObject('top.testManager.containerTestFrame', 'JSUnitException');
+    // asserts
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', '_displayStringForValue');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'error');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'argumentsIncludeComments');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'commentArg');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'nonCommentArg');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', '_validateArguments');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', '_assert');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assert');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertTrue');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertEquals');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotEquals');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNull');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotNull');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertUndefined');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotUndefined');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNaN');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'assertNotNaN');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'isLoaded');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'setUp');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'tearDown');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'getFunctionName');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'getStackTrace');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'warn');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'inform');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'debug');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'setJsUnitTracer');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'trim');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'isBlank');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'newOnLoadEvent');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'push');
+    top.xbDebugTraceFunction('top.testManager.containerTestFrame', 'pop');
+}
+
+function newOnLoadEvent() {
+    isTestPageLoaded = true;
+}
+
+function jsUnitSetOnLoad(windowRef, onloadHandler)
+{
+    var isKonqueror = navigator.userAgent.indexOf('Konqueror/') != -1 ||
+                      navigator.userAgent.indexOf('Safari/') != -1;
+
+    if (typeof(windowRef.attachEvent) != 'undefined') {
+        // Internet Explorer, Opera
+        windowRef.attachEvent("onload", onloadHandler);
+    } else if (typeof(windowRef.addEventListener) != 'undefined' && !isKonqueror) {
+        // Mozilla, Konqueror
+        // exclude Konqueror due to load issues
+        windowRef.addEventListener("load", onloadHandler, false);
+    } else if (typeof(windowRef.document.addEventListener) != 'undefined' && !isKonqueror) {
+        // DOM 2 Events
+        // exclude Mozilla, Konqueror due to load issues
+        windowRef.document.addEventListener("load", onloadHandler, false);
+    } else if (typeof(windowRef.onload) != 'undefined' && windowRef.onload) {
+        windowRef.jsunit_original_onload = windowRef.onload;
+        windowRef.onload = function() {
+            windowRef.jsunit_original_onload();
+            onloadHandler();
+        };
+    } else {
+        // browsers that do not support windowRef.attachEvent or
+        // windowRef.addEventListener will override a page's own onload event
+        windowRef.onload = onloadHandler;
+    }
+}
+
+jsUnitSetOnLoad(window, newOnLoadEvent);
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitMockTimeout.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitMockTimeout.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitMockTimeout.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,81 @@
+// Mock setTimeout, clearTimeout
+// Contributed by Pivotal Computer Systems, www.pivotalsf.com
+
+var Clock = {
+    timeoutsMade: 0,
+    scheduledFunctions: {},
+    nowMillis: 0,
+    reset: function() {
+        this.scheduledFunctions = {};
+        this.nowMillis = 0;
+        this.timeoutsMade = 0;
+    },
+    tick: function(millis) {
+        var oldMillis = this.nowMillis;
+        var newMillis = oldMillis + millis;
+        this.runFunctionsWithinRange(oldMillis, newMillis);
+        this.nowMillis = newMillis;
+    },
+    runFunctionsWithinRange: function(oldMillis, nowMillis) {
+        var scheduledFunc;
+        var funcsToRun = [];
+        for (var timeoutKey in this.scheduledFunctions) {
+            scheduledFunc = this.scheduledFunctions[timeoutKey];
+            if (scheduledFunc != undefined &&
+                scheduledFunc.runAtMillis >= oldMillis &&
+                scheduledFunc.runAtMillis <= nowMillis) {
+                funcsToRun.push(scheduledFunc);
+                this.scheduledFunctions[timeoutKey] = undefined;
+            }
+        }
+
+        if (funcsToRun.length > 0) {
+            funcsToRun.sort(function(a, b) {
+                return a.runAtMillis - b.runAtMillis;
+            });
+            for (var i = 0; i < funcsToRun.length; ++i) {
+                try {
+                    this.nowMillis = funcsToRun[i].runAtMillis;
+                    funcsToRun[i].funcToCall();
+                    if (funcsToRun[i].recurring) {
+                        Clock.scheduleFunction(funcsToRun[i].timeoutKey,
+                                funcsToRun[i].funcToCall,
+                                funcsToRun[i].millis,
+                                true);
+                    }
+                } catch(e) {
+                }
+            }
+            this.runFunctionsWithinRange(oldMillis, nowMillis);
+        }
+    },
+    scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
+        Clock.scheduledFunctions[timeoutKey] = {
+            runAtMillis: Clock.nowMillis + millis,
+            funcToCall: funcToCall,
+            recurring: recurring,
+            timeoutKey: timeoutKey,
+            millis: millis
+        };
+    }
+};
+
+function setTimeout(funcToCall, millis) {
+    Clock.timeoutsMade = Clock.timeoutsMade + 1;
+    Clock.scheduleFunction(Clock.timeoutsMade, funcToCall, millis, false);
+    return Clock.timeoutsMade;
+}
+
+function setInterval(funcToCall, millis) {
+    Clock.timeoutsMade = Clock.timeoutsMade + 1;
+    Clock.scheduleFunction(Clock.timeoutsMade, funcToCall, millis, true);
+    return Clock.timeoutsMade;
+}
+
+function clearTimeout(timeoutKey) {
+    Clock.scheduledFunctions[timeoutKey] = undefined;
+}
+
+function clearInterval(timeoutKey) {
+    Clock.scheduledFunctions[timeoutKey] = undefined;
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTestManager.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTestManager.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTestManager.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,705 @@
+function jsUnitTestManager() {
+    this._windowForAllProblemMessages = null;
+
+    this.container = top.frames.testContainer
+    this.documentLoader = top.frames.documentLoader;
+    this.mainFrame = top.frames.mainFrame;
+
+    this.containerController = this.container.frames.testContainerController;
+    this.containerTestFrame = this.container.frames.testFrame;
+
+    var mainData = this.mainFrame.frames.mainData;
+
+    // form elements on mainData frame
+    this.testFileName = mainData.document.testRunnerForm.testFileName;
+    this.runButton = mainData.document.testRunnerForm.runButton;
+    this.traceLevel = mainData.document.testRunnerForm.traceLevel;
+    this.closeTraceWindowOnNewRun = mainData.document.testRunnerForm.closeTraceWindowOnNewRun;
+    this.timeout = mainData.document.testRunnerForm.timeout;
+    this.setUpPageTimeout = mainData.document.testRunnerForm.setUpPageTimeout;
+
+    // image output
+    this.progressBar = this.mainFrame.frames.mainProgress.document.progress;
+
+    this.problemsListField = this.mainFrame.frames.mainErrors.document.testRunnerForm.problemsList;
+    this.testCaseResultsField = this.mainFrame.frames.mainResults.document.resultsForm.testCases;
+    this.resultsTimeField = this.mainFrame.frames.mainResults.document.resultsForm.time;
+
+    // 'layer' output frames
+    this.uiFrames = new Object();
+    this.uiFrames.mainStatus = this.mainFrame.frames.mainStatus;
+
+    var mainCounts = this.mainFrame.frames.mainCounts;
+
+    this.uiFrames.mainCountsErrors = mainCounts.frames.mainCountsErrors;
+    this.uiFrames.mainCountsFailures = mainCounts.frames.mainCountsFailures;
+    this.uiFrames.mainCountsRuns = mainCounts.frames.mainCountsRuns;
+    this._baseURL = "";
+
+    this.setup();
+}
+
+// seconds to wait for each test page to load
+jsUnitTestManager.TESTPAGE_WAIT_SEC = 120;
+jsUnitTestManager.TIMEOUT_LENGTH = 20;
+
+// seconds to wait for setUpPage to complete
+jsUnitTestManager.SETUPPAGE_TIMEOUT = 120;
+
+// milliseconds to wait between polls on setUpPages
+jsUnitTestManager.SETUPPAGE_INTERVAL = 100;
+
+jsUnitTestManager.RESTORED_HTML_DIV_ID = "jsUnitRestoredHTML";
+
+jsUnitTestManager.prototype.setup = function () {
+    this.totalCount = 0;
+    this.errorCount = 0;
+    this.failureCount = 0;
+    this._suiteStack = Array();
+
+    var initialSuite = new top.jsUnitTestSuite();
+    push(this._suiteStack, initialSuite);
+}
+
+jsUnitTestManager.prototype.start = function () {
+    this._baseURL = this.resolveUserEnteredTestFileName();
+    var firstQuery = this._baseURL.indexOf("?");
+    if (firstQuery >= 0) {
+        this._baseURL = this._baseURL.substring(0, firstQuery);
+    }
+    var lastSlash = this._baseURL.lastIndexOf("/");
+    var lastRevSlash = this._baseURL.lastIndexOf("\\");
+    if (lastRevSlash > lastSlash) {
+        lastSlash = lastRevSlash;
+    }
+    if (lastSlash > 0) {
+        this._baseURL = this._baseURL.substring(0, lastSlash + 1);
+    }
+
+    this._timeRunStarted = new Date();
+    this.initialize();
+    setTimeout('top.testManager._nextPage();', jsUnitTestManager.TIMEOUT_LENGTH);
+}
+
+jsUnitTestManager.prototype.getBaseURL = function () {
+    return this._baseURL;
+}
+
+jsUnitTestManager.prototype.doneLoadingPage = function (pageName) {
+    //this.containerTestFrame.setTracer(top.tracer);
+    this._testFileName = pageName;
+    if (this.isTestPageSuite())
+        this._handleNewSuite();
+    else
+    {
+        this._testIndex = 0;
+        this._testsInPage = this.getTestFunctionNames();
+        this._numberOfTestsInPage = this._testsInPage.length;
+        this._runTest();
+    }
+}
+
+jsUnitTestManager.prototype._handleNewSuite = function () {
+    var allegedSuite = this.containerTestFrame.suite();
+    if (allegedSuite.isjsUnitTestSuite) {
+        var newSuite = allegedSuite.clone();
+        if (newSuite.containsTestPages())
+            push(this._suiteStack, newSuite);
+        this._nextPage();
+    }
+    else {
+        this.fatalError('Invalid test suite in file ' + this._testFileName);
+        this.abort();
+    }
+}
+
+jsUnitTestManager.prototype._runTest = function () {
+    if (this._testIndex + 1 > this._numberOfTestsInPage)
+    {
+        // execute tearDownPage *synchronously*
+        // (unlike setUpPage which is asynchronous)
+        if (typeof this.containerTestFrame.tearDownPage == 'function') {
+            this.containerTestFrame.tearDownPage();
+        }
+
+        this._nextPage();
+        return;
+    }
+
+    if (this._testIndex == 0) {
+        this.storeRestoredHTML();
+        if (typeof(this.containerTestFrame.setUpPage) == 'function') {
+            // first test for this page and a setUpPage is defined
+            if (typeof(this.containerTestFrame.setUpPageStatus) == 'undefined') {
+                // setUpPage() not called yet, so call it
+                this.containerTestFrame.setUpPageStatus = false;
+                this.containerTestFrame.startTime = new Date();
+                this.containerTestFrame.setUpPage();
+                // try test again later
+                setTimeout('top.testManager._runTest()', jsUnitTestManager.SETUPPAGE_INTERVAL);
+                return;
+            }
+
+            if (this.containerTestFrame.setUpPageStatus != 'complete') {
+                top.status = 'setUpPage not completed... ' + this.containerTestFrame.setUpPageStatus + ' ' + (new Date());
+                if ((new Date() - this.containerTestFrame.startTime) / 1000 > this.getsetUpPageTimeout()) {
+                    this.fatalError('setUpPage timed out without completing.');
+                    if (!this.userConfirm('Retry Test Run?')) {
+                        this.abort();
+                        return;
+                    }
+                    this.containerTestFrame.startTime = (new Date());
+                }
+                // try test again later
+                setTimeout('top.testManager._runTest()', jsUnitTestManager.SETUPPAGE_INTERVAL);
+                return;
+            }
+        }
+    }
+
+    top.status = '';
+    // either not first test, or no setUpPage defined, or setUpPage completed
+    this.executeTestFunction(this._testsInPage[this._testIndex]);
+    this.totalCount++;
+    this.updateProgressIndicators();
+    this._testIndex++;
+    setTimeout('top.testManager._runTest()', jsUnitTestManager.TIMEOUT_LENGTH);
+}
+
+jsUnitTestManager.prototype._done = function () {
+    var secondsSinceRunBegan = (new Date() - this._timeRunStarted) / 1000;
+    this.setStatus('Done (' + secondsSinceRunBegan + ' seconds)');
+    this._cleanUp();
+    if (top.shouldSubmitResults()) {
+        this.resultsTimeField.value = secondsSinceRunBegan;
+        top.submitResults();
+    }
+}
+
+jsUnitTestManager.prototype._nextPage = function () {
+    this._restoredHTML = null;
+    if (this._currentSuite().hasMorePages()) {
+        this.loadPage(this._currentSuite().nextPage());
+    }
+    else {
+        pop(this._suiteStack);
+        if (this._currentSuite() == null)
+            this._done();
+        else
+            this._nextPage();
+    }
+}
+
+jsUnitTestManager.prototype._currentSuite = function () {
+    var suite = null;
+
+    if (this._suiteStack && this._suiteStack.length > 0)
+        suite = this._suiteStack[this._suiteStack.length - 1];
+
+    return suite;
+}
+
+jsUnitTestManager.prototype.calculateProgressBarProportion = function () {
+    if (this.totalCount == 0)
+        return 0;
+    var currentDivisor = 1;
+    var result = 0;
+
+    for (var i = 0; i < this._suiteStack.length; i++) {
+        var aSuite = this._suiteStack[i];
+        currentDivisor *= aSuite.testPages.length;
+        result += (aSuite.pageIndex - 1) / currentDivisor;
+    }
+    result += (this._testIndex + 1) / (this._numberOfTestsInPage * currentDivisor);
+    return result;
+}
+
+jsUnitTestManager.prototype._cleanUp = function () {
+    this.containerController.setTestPage('./app/emptyPage.html');
+    this.finalize();
+    top.tracer.finalize();
+}
+
+jsUnitTestManager.prototype.abort = function () {
+    this.setStatus('Aborted');
+    this._cleanUp();
+}
+
+jsUnitTestManager.prototype.getTimeout = function () {
+    var result = jsUnitTestManager.TESTPAGE_WAIT_SEC;
+    try {
+        result = eval(this.timeout.value);
+    }
+    catch (e) {
+    }
+    return result;
+}
+
+jsUnitTestManager.prototype.getsetUpPageTimeout = function () {
+    var result = jsUnitTestManager.SETUPPAGE_TIMEOUT;
+    try {
+        result = eval(this.setUpPageTimeout.value);
+    }
+    catch (e) {
+    }
+    return result;
+}
+
+jsUnitTestManager.prototype.isTestPageSuite = function () {
+    var result = false;
+    if (typeof(this.containerTestFrame.suite) == 'function')
+    {
+        result = true;
+    }
+    return result;
+}
+
+jsUnitTestManager.prototype.getTestFunctionNames = function () {
+    var testFrame = this.containerTestFrame;
+    var testFunctionNames = new Array();
+    var i;
+
+    if (testFrame && typeof(testFrame.exposeTestFunctionNames) == 'function')
+        return testFrame.exposeTestFunctionNames();
+
+    if (testFrame &&
+        testFrame.document &&
+        typeof(testFrame.document.scripts) != 'undefined' &&
+        testFrame.document.scripts.length > 0) { // IE5 and up
+        var scriptsInTestFrame = testFrame.document.scripts;
+
+        for (i = 0; i < scriptsInTestFrame.length; i++) {
+            var someNames = this._extractTestFunctionNamesFromScript(scriptsInTestFrame[i]);
+            if (someNames)
+                testFunctionNames = testFunctionNames.concat(someNames);
+        }
+    }
+    else {
+        for (i in testFrame) {
+            if (i.substring(0, 4) == 'test' && typeof(testFrame[i]) == 'function')
+                push(testFunctionNames, i);
+        }
+    }
+    return testFunctionNames;
+}
+
+jsUnitTestManager.prototype._extractTestFunctionNamesFromScript = function (aScript) {
+    var result;
+    var remainingScriptToInspect = aScript.text;
+    var currentIndex = this._indexOfTestFunctionIn(remainingScriptToInspect);
+    while (currentIndex != -1) {
+        if (!result)
+            result = new Array();
+
+        var fragment = remainingScriptToInspect.substring(currentIndex, remainingScriptToInspect.length);
+        result = result.concat(fragment.substring('function '.length, fragment.indexOf('(')));
+        remainingScriptToInspect = remainingScriptToInspect.substring(currentIndex + 12, remainingScriptToInspect.length);
+        currentIndex = this._indexOfTestFunctionIn(remainingScriptToInspect);
+    }
+    return result;
+}
+
+jsUnitTestManager.prototype._indexOfTestFunctionIn = function (string) {
+    return string.indexOf('function test');
+}
+
+jsUnitTestManager.prototype.loadPage = function (testFileName) {
+    this._testFileName = testFileName;
+    this._loadAttemptStartTime = new Date();
+    this.setStatus('Opening Test Page "' + this._testFileName + '"');
+    this.containerController.setTestPage(this._testFileName);
+    this._callBackWhenPageIsLoaded();
+}
+
+jsUnitTestManager.prototype._callBackWhenPageIsLoaded = function () {
+    if ((new Date() - this._loadAttemptStartTime) / 1000 > this.getTimeout()) {
+        this.fatalError('Reading Test Page ' + this._testFileName + ' timed out.\nMake sure that the file exists and is a Test Page.');
+        if (this.userConfirm('Retry Test Run?')) {
+            this.loadPage(this._testFileName);
+            return;
+        } else {
+            this.abort();
+            return;
+        }
+    }
+    if (!this._isTestFrameLoaded()) {
+        setTimeout('top.testManager._callBackWhenPageIsLoaded();', jsUnitTestManager.TIMEOUT_LENGTH);
+        return;
+    }
+    this.doneLoadingPage(this._testFileName);
+}
+
+jsUnitTestManager.prototype._isTestFrameLoaded = function () {
+    try {
+        return this.containerController.isPageLoaded();
+    }
+    catch (e) {
+    }
+    return false;
+}
+
+jsUnitTestManager.prototype.executeTestFunction = function (functionName) {
+    this._testFunctionName = functionName;
+    this.setStatus('Running test "' + this._testFunctionName + '"');
+    var excep = null;
+    var timeBefore = new Date();
+    try {
+        if (this._restoredHTML)
+            top.testContainer.testFrame.document.getElementById(jsUnitTestManager.RESTORED_HTML_DIV_ID).innerHTML = this._restoredHTML;
+        if (this.containerTestFrame.setUp !== JSUNIT_UNDEFINED_VALUE)
+            this.containerTestFrame.setUp();
+        this.containerTestFrame[this._testFunctionName]();
+    }
+    catch (e1) {
+        excep = e1;
+    }
+    finally {
+        try {
+            if (this.containerTestFrame.tearDown !== JSUNIT_UNDEFINED_VALUE)
+                this.containerTestFrame.tearDown();
+        }
+        catch (e2) {
+            //Unlike JUnit, only assign a tearDown exception to excep if there is not already an exception from the test body
+            if (excep == null)
+                excep = e2;
+        }
+    }
+    var timeTaken = (new Date() - timeBefore) / 1000;
+    if (excep != null)
+        this._handleTestException(excep);
+    var serializedTestCaseString = this._currentTestFunctionNameWithTestPageName(true) + "|" + timeTaken + "|";
+    if (excep == null)
+        serializedTestCaseString += "S||";
+    else {
+        if (typeof(excep.isJsUnitException) != 'undefined' && excep.isJsUnitException)
+            serializedTestCaseString += "F|";
+        else {
+            serializedTestCaseString += "E|";
+        }
+        serializedTestCaseString += this._problemDetailMessageFor(excep);
+    }
+    this._addOption(this.testCaseResultsField,
+            serializedTestCaseString,
+            serializedTestCaseString);
+}
+
+jsUnitTestManager.prototype._currentTestFunctionNameWithTestPageName = function(useFullyQualifiedTestPageName) {
+    var testURL = this.containerTestFrame.location.href;
+    var testQuery = testURL.indexOf("?");
+    if (testQuery >= 0) {
+        testURL = testURL.substring(0, testQuery);
+    }
+    if (!useFullyQualifiedTestPageName) {
+        if (testURL.substring(0, this._baseURL.length) == this._baseURL)
+            testURL = testURL.substring(this._baseURL.length);
+    }
+    return testURL + ':' + this._testFunctionName;
+}
+
+jsUnitTestManager.prototype._addOption = function(listField, problemValue, problemMessage) {
+    if (typeof(listField.ownerDocument) != 'undefined'
+            && typeof(listField.ownerDocument.createElement) != 'undefined') {
+        // DOM Level 2 HTML method.
+        // this is required for Opera 7 since appending to the end of the
+        // options array does not work, and adding an Option created by new Option()
+        // and appended by listField.options.add() fails due to WRONG_DOCUMENT_ERR
+        var problemDocument = listField.ownerDocument;
+        var errOption = problemDocument.createElement('option');
+        errOption.setAttribute('value', problemValue);
+        errOption.appendChild(problemDocument.createTextNode(problemMessage));
+        listField.appendChild(errOption);
+    }
+    else {
+        // new Option() is DOM 0
+        errOption = new Option(problemMessage, problemValue);
+        if (typeof(listField.add) != 'undefined') {
+            // DOM 2 HTML
+            listField.add(errOption, null);
+        }
+        else if (typeof(listField.options.add) != 'undefined') {
+            // DOM 0
+            listField.options.add(errOption, null);
+        }
+        else {
+            // DOM 0
+            listField.options[listField.length] = errOption;
+        }
+    }
+}
+
+jsUnitTestManager.prototype._handleTestException = function (excep) {
+    var problemMessage = this._currentTestFunctionNameWithTestPageName(false) + ' ';
+    var errOption;
+    if (typeof(excep.isJsUnitException) == 'undefined' || !excep.isJsUnitException) {
+        problemMessage += 'had an error';
+        this.errorCount++;
+    }
+    else {
+        problemMessage += 'failed';
+        this.failureCount++;
+    }
+    var listField = this.problemsListField;
+    this._addOption(listField,
+            this._problemDetailMessageFor(excep),
+            problemMessage);
+}
+
+jsUnitTestManager.prototype._problemDetailMessageFor = function (excep) {
+    var result = null;
+    if (typeof(excep.isJsUnitException) != 'undefined' && excep.isJsUnitException) {
+        result = '';
+        if (excep.comment != null)
+            result += ('"' + excep.comment + '"\n');
+
+        result += excep.jsUnitMessage;
+
+        if (excep.stackTrace)
+            result += '\n\nStack trace follows:\n' + excep.stackTrace;
+    }
+    else {
+        result = 'Error message is:\n"';
+        result +=
+        (typeof(excep.description) == 'undefined') ?
+        excep :
+        excep.description;
+        result += '"';
+        if (typeof(excep.stack) != 'undefined') // Mozilla only
+            result += '\n\nStack trace follows:\n' + excep.stack;
+    }
+    return result;
+}
+
+jsUnitTestManager.prototype._setTextOnLayer = function (layerName, str) {
+    try {
+        var content;
+        if (content = this.uiFrames[layerName].document.getElementById('content'))
+            content.innerHTML = str;
+        else
+            throw 'No content div found.';
+    }
+    catch (e) {
+        var html = '';
+        html += '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
+        html += '<html><head><link rel="stylesheet" type="text/css" href="css/jsUnitStyle.css"><\/head>';
+        html += '<body><div id="content">';
+        html += str;
+        html += '<\/div><\/body>';
+        html += '<\/html>';
+        this.uiFrames[layerName].document.write(html);
+        this.uiFrames[layerName].document.close();
+    }
+}
+
+jsUnitTestManager.prototype.setStatus = function (str) {
+    this._setTextOnLayer('mainStatus', '<b>Status:<\/b> ' + str);
+}
+
+jsUnitTestManager.prototype._setErrors = function (n) {
+    this._setTextOnLayer('mainCountsErrors', '<b>Errors: <\/b>' + n);
+}
+
+jsUnitTestManager.prototype._setFailures = function (n) {
+    this._setTextOnLayer('mainCountsFailures', '<b>Failures:<\/b> ' + n);
+}
+
+jsUnitTestManager.prototype._setTotal = function (n) {
+    this._setTextOnLayer('mainCountsRuns', '<b>Runs:<\/b> ' + n);
+}
+
+jsUnitTestManager.prototype._setProgressBarImage = function (imgName) {
+    this.progressBar.src = imgName;
+}
+
+jsUnitTestManager.prototype._setProgressBarWidth = function (w) {
+    this.progressBar.width = w;
+}
+
+jsUnitTestManager.prototype.updateProgressIndicators = function () {
+    this._setTotal(this.totalCount);
+    this._setErrors(this.errorCount);
+    this._setFailures(this.failureCount);
+    this._setProgressBarWidth(300 * this.calculateProgressBarProportion());
+
+    if (this.errorCount > 0 || this.failureCount > 0)
+        this._setProgressBarImage('../images/red.gif');
+    else
+        this._setProgressBarImage('../images/green.gif');
+}
+
+jsUnitTestManager.prototype.showMessageForSelectedProblemTest = function () {
+    var problemTestIndex = this.problemsListField.selectedIndex;
+    if (problemTestIndex != -1)
+        this.fatalError(this.problemsListField[problemTestIndex].value);
+}
+
+jsUnitTestManager.prototype.showMessagesForAllProblemTests = function () {
+    if (this.problemsListField.length == 0)
+        return;
+
+    try {
+        if (this._windowForAllProblemMessages && !this._windowForAllProblemMessages.closed)
+            this._windowForAllProblemMessages.close();
+    }
+    catch(e) {
+    }
+
+    this._windowForAllProblemMessages = window.open('', '', 'width=600, height=350,status=no,resizable=yes,scrollbars=yes');
+    var resDoc = this._windowForAllProblemMessages.document;
+    resDoc.write('<html><head><link rel="stylesheet" href="../css/jsUnitStyle.css"><title>Tests with problems - JsUnit<\/title><head><body>');
+    resDoc.write('<p class="jsUnitSubHeading">Tests with problems (' + this.problemsListField.length + ' total) - JsUnit<\/p>');
+    resDoc.write('<p class="jsUnitSubSubHeading"><i>Running on ' + navigator.userAgent + '</i></p>');
+    for (var i = 0; i < this.problemsListField.length; i++)
+    {
+        resDoc.write('<p class="jsUnitDefault">');
+        resDoc.write('<b>' + (i + 1) + '. ');
+        resDoc.write(this.problemsListField[i].text);
+        resDoc.write('<\/b><\/p><p><pre>');
+        resDoc.write(this._makeHTMLSafe(this.problemsListField[i].value));
+        resDoc.write('<\/pre><\/p>');
+    }
+
+    resDoc.write('<\/body><\/html>');
+    resDoc.close();
+}
+
+jsUnitTestManager.prototype._makeHTMLSafe = function (string) {
+    string = string.replace(/&/g, '&amp;');
+    string = string.replace(/</g, '&lt;');
+    string = string.replace(/>/g, '&gt;');
+    return string;
+}
+
+jsUnitTestManager.prototype._clearProblemsList = function () {
+    var listField = this.problemsListField;
+    var initialLength = listField.options.length;
+
+    for (var i = 0; i < initialLength; i++)
+        listField.remove(0);
+}
+
+jsUnitTestManager.prototype.initialize = function () {
+    this.setStatus('Initializing...');
+    this._setRunButtonEnabled(false);
+    this._clearProblemsList();
+    this.updateProgressIndicators();
+    this.setStatus('Done initializing');
+}
+
+jsUnitTestManager.prototype.finalize = function () {
+    this._setRunButtonEnabled(true);
+}
+
+jsUnitTestManager.prototype._setRunButtonEnabled = function (b) {
+    this.runButton.disabled = !b;
+}
+
+jsUnitTestManager.prototype.getTestFileName = function () {
+    var rawEnteredFileName = this.testFileName.value;
+    var result = rawEnteredFileName;
+
+    while (result.indexOf('\\') != -1)
+        result = result.replace('\\', '/');
+
+    return result;
+}
+
+jsUnitTestManager.prototype.getTestFunctionName = function () {
+    return this._testFunctionName;
+}
+
+jsUnitTestManager.prototype.resolveUserEnteredTestFileName = function (rawText) {
+    var userEnteredTestFileName = top.testManager.getTestFileName();
+
+    // only test for file:// since Opera uses a different format
+    if (userEnteredTestFileName.indexOf('http://') == 0 || userEnteredTestFileName.indexOf('https://') == 0 || userEnteredTestFileName.indexOf('file://') == 0)
+        return userEnteredTestFileName;
+
+    return getTestFileProtocol() + this.getTestFileName();
+}
+
+jsUnitTestManager.prototype.storeRestoredHTML = function () {
+    if (document.getElementById && top.testContainer.testFrame.document.getElementById(jsUnitTestManager.RESTORED_HTML_DIV_ID))
+        this._restoredHTML = top.testContainer.testFrame.document.getElementById(jsUnitTestManager.RESTORED_HTML_DIV_ID).innerHTML;
+}
+
+jsUnitTestManager.prototype.fatalError = function(aMessage) {
+    if (top.shouldSubmitResults())
+        this.setStatus(aMessage);
+    else
+        alert(aMessage);
+}
+
+jsUnitTestManager.prototype.userConfirm = function(aMessage) {
+    if (top.shouldSubmitResults())
+        return false;
+    else
+        return confirm(aMessage);
+}
+
+function getTestFileProtocol() {
+    return getDocumentProtocol();
+}
+
+function getDocumentProtocol() {
+    var protocol = top.document.location.protocol;
+
+    if (protocol == "file:")
+        return "file:///";
+
+    if (protocol == "http:")
+        return "http://";
+
+    if (protocol == 'https:')
+        return 'https://';
+
+    if (protocol == "chrome:")
+        return "chrome://";
+
+    return null;
+}
+
+function browserSupportsReadingFullPathFromFileField() {
+    return !isOpera() && !isIE7();
+}
+
+function isOpera() {
+    return navigator.userAgent.toLowerCase().indexOf("opera") != -1;
+}
+
+function isIE7() {
+    return navigator.userAgent.toLowerCase().indexOf("msie 7") != -1;
+}
+
+function isBeingRunOverHTTP() {
+    return getDocumentProtocol() == "http://";
+}
+
+function getWebserver() {
+    if (isBeingRunOverHTTP()) {
+        var myUrl = location.href;
+        var myUrlWithProtocolStripped = myUrl.substring(myUrl.indexOf("/") + 2);
+        return myUrlWithProtocolStripped.substring(0, myUrlWithProtocolStripped.indexOf("/"));
+    }
+    return null;
+}
+
+// the functions push(anArray, anObject) and pop(anArray)
+// exist because the JavaScript Array.push(anObject) and Array.pop()
+// functions are not available in IE 5.0
+
+function push(anArray, anObject) {
+    anArray[anArray.length] = anObject;
+}
+
+function pop(anArray) {
+    if (anArray.length >= 1) {
+        delete anArray[anArray.length - 1];
+        anArray.length--;
+    }
+}
+
+if (xbDEBUG.on) {
+    xbDebugTraceObject('window', 'jsUnitTestManager');
+    xbDebugTraceFunction('window', 'getTestFileProtocol');
+    xbDebugTraceFunction('window', 'getDocumentProtocol');
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTestSuite.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTestSuite.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTestSuite.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,44 @@
+function jsUnitTestSuite() {
+    this.isjsUnitTestSuite = true;
+    this.testPages = Array();
+    this.pageIndex = 0;
+}
+
+jsUnitTestSuite.prototype.addTestPage = function (pageName)
+{
+    this.testPages[this.testPages.length] = pageName;
+}
+
+jsUnitTestSuite.prototype.addTestSuite = function (suite)
+{
+    for (var i = 0; i < suite.testPages.length; i++)
+        this.addTestPage(suite.testPages[i]);
+}
+
+jsUnitTestSuite.prototype.containsTestPages = function ()
+{
+    return this.testPages.length > 0;
+}
+
+jsUnitTestSuite.prototype.nextPage = function ()
+{
+    return this.testPages[this.pageIndex++];
+}
+
+jsUnitTestSuite.prototype.hasMorePages = function ()
+{
+    return this.pageIndex < this.testPages.length;
+}
+
+jsUnitTestSuite.prototype.clone = function ()
+{
+    var clone = new jsUnitTestSuite();
+    clone.testPages = this.testPages;
+    return clone;
+}
+
+if (xbDEBUG.on)
+{
+    xbDebugTraceObject('window', 'jsUnitTestSuite');
+}
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTracer.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTracer.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitTracer.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,102 @@
+var TRACE_LEVEL_NONE = new JsUnitTraceLevel(0, null);
+var TRACE_LEVEL_WARNING = new JsUnitTraceLevel(1, "#FF0000");
+var TRACE_LEVEL_INFO = new JsUnitTraceLevel(2, "#009966");
+var TRACE_LEVEL_DEBUG = new JsUnitTraceLevel(3, "#0000FF");
+
+function JsUnitTracer(testManager) {
+    this._testManager = testManager;
+    this._traceWindow = null;
+    this.popupWindowsBlocked = false;
+}
+
+JsUnitTracer.prototype.initialize = function() {
+    if (this._traceWindow != null && top.testManager.closeTraceWindowOnNewRun.checked)
+        this._traceWindow.close();
+    this._traceWindow = null;
+}
+
+JsUnitTracer.prototype.finalize = function() {
+    if (this._traceWindow != null) {
+        this._traceWindow.document.write('<\/body>\n<\/html>');
+        this._traceWindow.document.close();
+    }
+}
+
+JsUnitTracer.prototype.warn = function() {
+    this._trace(arguments[0], arguments[1], TRACE_LEVEL_WARNING);
+}
+
+JsUnitTracer.prototype.inform = function() {
+    this._trace(arguments[0], arguments[1], TRACE_LEVEL_INFO);
+}
+
+JsUnitTracer.prototype.debug = function() {
+    this._trace(arguments[0], arguments[1], TRACE_LEVEL_DEBUG);
+}
+
+JsUnitTracer.prototype._trace = function(message, value, traceLevel) {
+    if (!top.shouldSubmitResults() && this._getChosenTraceLevel().matches(traceLevel)) {
+        var traceString = message;
+        if (value)
+            traceString += ': ' + value;
+        var prefix = this._testManager.getTestFileName() + ":" +
+                     this._testManager.getTestFunctionName() + " - ";
+        this._writeToTraceWindow(prefix, traceString, traceLevel);
+    }
+}
+
+JsUnitTracer.prototype._getChosenTraceLevel = function() {
+    var levelNumber = eval(top.testManager.traceLevel.value);
+    return traceLevelByLevelNumber(levelNumber);
+}
+
+JsUnitTracer.prototype._writeToTraceWindow = function(prefix, traceString, traceLevel) {
+    var htmlToAppend = '<p class="jsUnitDefault">' + prefix + '<font color="' + traceLevel.getColor() + '">' + traceString + '</font><\/p>\n';
+    this._getTraceWindow().document.write(htmlToAppend);
+}
+
+JsUnitTracer.prototype._getTraceWindow = function() {
+    if (this._traceWindow == null && !top.shouldSubmitResults() && !this.popupWindowsBlocked) {
+        this._traceWindow = window.open('', '', 'width=600, height=350,status=no,resizable=yes,scrollbars=yes');
+        if (!this._traceWindow)
+            this.popupWindowsBlocked = true;
+        else {
+            var resDoc = this._traceWindow.document;
+            resDoc.write('<html>\n<head>\n<link rel="stylesheet" href="css/jsUnitStyle.css">\n<title>Tracing - JsUnit<\/title>\n<head>\n<body>');
+            resDoc.write('<h2>Tracing - JsUnit<\/h2>\n');
+            resDoc.write('<p class="jsUnitDefault"><i>(Traces are color coded: ');
+            resDoc.write('<font color="' + TRACE_LEVEL_WARNING.getColor() + '">Warning</font> - ');
+            resDoc.write('<font color="' + TRACE_LEVEL_INFO.getColor() + '">Information</font> - ');
+            resDoc.write('<font color="' + TRACE_LEVEL_DEBUG.getColor() + '">Debug</font>');
+            resDoc.write(')</i></p>');
+        }
+    }
+    return this._traceWindow;
+}
+
+if (xbDEBUG.on) {
+    xbDebugTraceObject('window', 'JsUnitTracer');
+}
+
+function JsUnitTraceLevel(levelNumber, color) {
+    this._levelNumber = levelNumber;
+    this._color = color;
+}
+
+JsUnitTraceLevel.prototype.matches = function(anotherTraceLevel) {
+    return this._levelNumber >= anotherTraceLevel._levelNumber;
+}
+
+JsUnitTraceLevel.prototype.getColor = function() {
+    return this._color;
+}
+
+function traceLevelByLevelNumber(levelNumber) {
+    switch (levelNumber) {
+        case 0: return TRACE_LEVEL_NONE;
+        case 1: return TRACE_LEVEL_WARNING;
+        case 2: return TRACE_LEVEL_INFO;
+        case 3: return TRACE_LEVEL_DEBUG;
+    }
+    return null;
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitVersionCheck.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitVersionCheck.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/jsUnitVersionCheck.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,59 @@
+var versionRequest;
+
+function isOutOfDate(newVersionNumber) {
+    return JSUNIT_VERSION < newVersionNumber;
+}
+
+function sendRequestForLatestVersion(url) {
+    versionRequest = createXmlHttpRequest();
+    if (versionRequest) {
+        versionRequest.onreadystatechange = requestStateChanged;
+        versionRequest.open("GET", url, true);
+        versionRequest.send(null);
+    }
+}
+
+function createXmlHttpRequest() {
+    if (window.XMLHttpRequest)
+        return new XMLHttpRequest();
+    else if (window.ActiveXObject)
+        return new ActiveXObject("Microsoft.XMLHTTP");
+}
+
+function requestStateChanged() {
+    if (versionRequest && versionRequest.readyState == 4) {
+        if (versionRequest.status == 200) {
+            var latestVersion = versionRequest.responseText;
+            if (isOutOfDate(latestVersion))
+                versionNotLatest(latestVersion);
+            else
+                versionLatest();
+        } else
+            versionCheckError();
+    }
+}
+
+function checkForLatestVersion(url) {
+    setLatestVersionDivHTML("Checking for newer version...");
+    try {
+        sendRequestForLatestVersion(url);
+    } catch (e) {
+        setLatestVersionDivHTML("An error occurred while checking for a newer version: " + e.message);
+    }
+}
+
+function versionNotLatest(latestVersion) {
+    setLatestVersionDivHTML('<font color="red">A newer version of JsUnit, version ' + latestVersion + ', is available.</font>');
+}
+
+function versionLatest() {
+    setLatestVersionDivHTML("You are running the latest version of JsUnit.");
+}
+
+function setLatestVersionDivHTML(string) {
+    document.getElementById("versionCheckDiv").innerHTML = string;
+}
+
+function versionCheckError() {
+    setLatestVersionDivHTML("An error occurred while checking for a newer version.");
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-errors.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-errors.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-errors.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title></title>
+    <link rel="stylesheet" type="text/css" href="../css/jsUnitStyle.css">
+</head>
+
+<body>
+<div id="content"><b>Errors:</b> 0</div>
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-failures.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-failures.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-failures.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title></title>
+    <link rel="stylesheet" type="text/css" href="../css/jsUnitStyle.css">
+</head>
+
+<body>
+<div id="content"><b>Failures:</b> 0</div>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-runs.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-runs.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts-runs.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title></title>
+    <link rel="stylesheet" type="text/css" href="../css/jsUnitStyle.css">
+</head>
+
+<body>
+<div id="content"><b>Runs:</b> 0</div>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-counts.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title></title>
+</head>
+
+<frameset cols="200,190,*" border="0">
+    <frame name="mainCountsRuns" src="main-counts-runs.html" scrolling="no" frameborder="0">
+    <frame name="mainCountsErrors" src="main-counts-errors.html" scrolling="no" frameborder="0">
+    <frame name="mainCountsFailures" src="main-counts-failures.html" scrolling="no" frameborder="0">
+
+    <noframes>
+        <body>
+        <p>jsUnit uses frames in order to remove dependencies upon a browser's implementation of document.getElementById
+            and HTMLElement.innerHTML.</p>
+        </body>
+    </noframes>
+</frameset>
+</html>
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-data.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-data.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-data.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,178 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit main-data.html</title>
+    <link rel="stylesheet" type="text/css" href="../css/jsUnitStyle.css">
+    <script language="JavaScript" type="text/javascript" src="jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="jsUnitVersionCheck.js"></script>
+    <script language="JavaScript" type="text/javascript">
+
+        function pageLoaded() {
+            giveFocusToTestFileNameField();
+        }
+
+        function giveFocusToTestFileNameField() {
+            if (document.testRunnerForm.testFileName.type != "hidden")
+                document.testRunnerForm.testFileName.focus();
+        }
+
+        function kickOffTests() {
+            //
+            //    Check if Init was called by onload handler
+            //
+            if (typeof(top.testManager) == 'undefined') {
+                top.init();
+            }
+
+            if (isBlank(top.testManager.getTestFileName())) {
+                top.testManager.fatalError('No Test Page specified.');
+                return;
+            }
+
+            top.testManager.setup();
+
+            top.testManager._currentSuite().addTestPage(top.testManager.resolveUserEnteredTestFileName());
+            top.tracer.initialize();
+
+            var traceLevel = document.forms.testRunnerForm.traceLevel;
+            if (traceLevel.value != '0')
+            {
+                var traceWindow = top.tracer._getTraceWindow();
+                if (traceWindow) {
+                    traceWindow.focus();
+                }
+                else {
+                    top.testManager.fatalError('Tracing requires popup windows, and popups are blocked in your browser.\n\nPlease enable popups if you wish to use tracing.');
+                }
+            }
+
+            top.testManager.start();
+        }
+
+    </script>
+</head>
+
+<body onload="pageLoaded();">
+<table width="100%" cellpadding="0" cellspacing="0" border="0" summary="jsUnit Information" bgcolor="#DDDDDD">
+    <tr>
+        <td width="1"><a href="http://www.jsunit.net" target="_blank"><img src="../images/logo_jsunit.gif" alt="JsUnit" border="0"/></a></td>
+        <td width="50">&nbsp;</td>
+        <th nowrap align="left">
+            <h4>JsUnit <script language="javascript">document.write(JSUNIT_VERSION);</script> TestRunner</h4>
+            <font size="-2"><i>Running on <script language="javascript" type="text/javascript">document.write(navigator.userAgent);</script>
+            </i></font>
+        </th>
+
+        <td nowrap align="right" valign="middle">
+            <font size="-2">
+                <b><a href="http://www.jsunit.net/" target="_blank">www.jsunit.net</a></b>&nbsp;&nbsp;<br>
+            </font>
+            <a href="http://www.pivotalsf.com/" target="top">
+                <img border="0" src="../images/powerby-transparent.gif" alt="Powered By Pivotal">
+            </a>
+        </td>
+    </tr>
+</table>
+
+<form name="testRunnerForm" action="">
+    <script type="text/javascript" language="javascript">
+        if (!jsUnitGetParm('testpage')) {
+            document.write("<p>Enter the filename of the Test Page to be run:</p>");
+        } else {
+            document.write("<br>");
+        };
+    </script>
+
+    <table cellpadding="0" cellspacing="0" border="0" summary="Form for entering test case location">
+        <tr>
+            <td align="center" valign="middle">
+                <script language="JavaScript" type="text/javascript">
+                    document.write(top.getDocumentProtocol());
+                </script>
+            </td>
+
+            <td nowrap align="center" valign="bottom">
+                &nbsp;
+                <script language="JavaScript" type="text/javascript">
+                    var specifiedTestPage = jsUnitGetParm('testpage');
+                    if (specifiedTestPage) {
+                        var html = '<input type="hidden" name="testFileName" value="';
+                        var valueString = '';
+                        if ((top.getDocumentProtocol() == 'http://' || top.getDocumentProtocol() == 'https://') && jsUnitGetParm('testpage').indexOf('/') == 0)
+                            valueString += top.location.host;
+                        valueString += specifiedTestPage;
+                        var testParms = top.jsUnitConstructTestParms();
+                        if (testParms != '') {
+                            valueString += '?';
+                            valueString += testParms;
+                        }
+                        html += valueString;
+                        html += '">';
+                        html += valueString;
+                        document.write(html);
+                    } else {
+                        if (top.getDocumentProtocol() == 'file:///' && top.browserSupportsReadingFullPathFromFileField())
+                            document.write('<input type="file" name="testFileName" size="60">');
+                        else
+                            document.write('<input type="text" name="testFileName" size="60">');
+                    }
+                </script>
+                <input type="button" name="runButton" value="Run" onclick="kickOffTests()">
+            </td>
+        </tr>
+    </table>
+    <br>
+    <hr>
+
+    <table cellpadding="0" cellspacing="0" border="0" summary="Choose Trace Level">
+        <tr>
+            <td nowrap>Trace level:</td>
+
+            <td><select name="traceLevel">
+                <option value="0" selected>
+                    no tracing
+                </option>
+
+                <option value="1">
+                    warning (lowest)
+                </option>
+
+                <option value="2">
+                    info
+                </option>
+
+                <option value="3">
+                    debug (highest)
+                </option>
+            </select></td>
+
+            <td>&nbsp;&nbsp;&nbsp;</td>
+
+            <td><input type="checkbox" name="closeTraceWindowOnNewRun" checked></td>
+            <td nowrap>Close old trace window on new run</td>
+
+            <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
+
+            <td nowrap>Page load timeout:</td>
+            <td>&nbsp;
+                <script language="javascript" type="text/javascript">
+                    document.write('<input type="text" size="2" name="timeout" value="' + top.jsUnitTestManager.TESTPAGE_WAIT_SEC + '">');
+                </script>
+            </td>
+
+            <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
+
+            <td nowrap>Setup page timeout:</td>
+            <td>&nbsp;
+                <script language="javascript" type="text/javascript">
+                    document.write('<input type="text" size="2" name="setUpPageTimeout" value="' + top.jsUnitTestManager.SETUPPAGE_TIMEOUT + '">');
+                </script>
+            </td>
+        </tr>
+    </table>
+    <hr>
+</form>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-errors.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-errors.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-errors.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit main-errors.html</title>
+    <link rel="stylesheet" type="text/css" href="../css/jsUnitStyle.css">
+</head>
+
+<body>
+<hr>
+
+<form name="testRunnerForm" action="javascript:top.testManager.showMessageForSelectedProblemTest()">
+    <p>Errors and failures:&nbsp;</p>
+    <select size="5" ondblclick="top.testManager.showMessageForSelectedProblemTest()" name="problemsList">
+        <option>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</option>
+    </select>
+    <br>
+    <input type="button" value="Show selected" onclick="top.testManager.showMessageForSelectedProblemTest()">
+    &nbsp;&nbsp;&nbsp;
+    <input type="button" value="Show all" onclick="top.testManager.showMessagesForAllProblemTests()">
+</form>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-frame.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-frame.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-frame.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
+<html>
+<head>
+    <title>jsUnit Main Frame</title>
+</head>
+<frameset rows="230,30,30,30,0,*" border="0">>
+    <frame name="mainData" src="main-data.html" scrolling="no" frameborder="0">
+    <frame name="mainStatus" src="main-status.html" scrolling="no" frameborder="0">
+    <frame name="mainProgress" src="main-progress.html" scrolling="no" frameborder="0">
+    <frame name="mainCounts" src="main-counts.html" scrolling="no" frameborder="0">
+    <frame name="mainResults" src="main-results.html" scrolling="no" frameborder="0">
+    <frame name="mainErrors" src="main-errors.html" scrolling="no" frameborder="0">
+    <noframes>
+        <body>
+        <p>Sorry, JsUnit requires frames.</p>
+        </body>
+    </noframes>
+</frameset>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-loader.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-loader.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-loader.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>jsUnit External Data Document loader</title>
+    <script language="JavaScript" type="text/javascript">
+
+        var loadStatus;
+        var callback = function () {
+        };
+
+        function buffer() {
+            return window.frames.documentBuffer;
+        }
+
+        function load(uri) {
+            loadStatus = 'loading';
+            buffer().document.location.href = uri;
+        }
+
+        function loadComplete() {
+            top.xbDEBUG.dump('main-loader.html:loadComplete(): loadStatus = ' + loadStatus + ' href=' + buffer().document.location.href);
+            if (loadStatus == 'loading') {
+                loadStatus = 'complete';
+                callback();
+                callback = function () {
+                };
+            }
+        }
+
+        if (top.xbDEBUG.on) {
+            var scopeName = 'main_loader_' + (new Date()).getTime();
+            top[scopeName] = window;
+            top.xbDebugTraceFunction(scopeName, 'buffer');
+            top.xbDebugTraceFunction(scopeName, 'load');
+            top.xbDebugTraceFunction(scopeName, 'loadComplete');
+        }
+
+    </script>
+</head>
+
+<body>
+<iframe name="documentBuffer" onload="loadComplete()"></iframe>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-progress.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-progress.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-progress.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit main-progress.html</title>
+    <link rel="stylesheet" type="text/css" href="../css/jsUnitStyle.css">
+</head>
+
+<body>
+<table width="375" cellpadding="0" cellspacing="0" border="0" summary="Test progress indicator">
+    <tr>
+        <td width="65" valign="top"><b>Progress:</b></td>
+
+        <td width="300" height="14" valign="middle">
+            <table width="300" cellpadding="0" cellspacing="0" border="1" summary="Progress image">
+                <tr>
+                    <td width="300" height="14" valign="top"><img name="progress" height="14" width="0"
+                                                                  alt="progress image" src="../images/green.gif"></td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-results.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-results.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-results.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit main-results.html</title>
+    <link rel="stylesheet" type="text/css" href="../css/jsUnitStyle.css">
+</head>
+
+<body>
+<script language="javascript" type="text/javascript">
+    var DEFAULT_SUBMIT_WEBSERVER = "localhost:8080";
+
+    function submitUrlFromSpecifiedUrl() {
+        var result = "";
+        var specifiedUrl = top.getSpecifiedResultUrl();
+        if (specifiedUrl.indexOf("http://") != 0)
+            result = "http://";
+        result += specifiedUrl;
+        return result;
+    }
+
+    function submitUrlFromTestRunnerLocation() {
+        var result = "http://";
+        var webserver = top.getWebserver();
+        if (webserver == null) // running over file:///
+            webserver = DEFAULT_SUBMIT_WEBSERVER;
+        result += webserver;
+        result += "/jsunit/acceptor";
+        return result;
+    }
+
+    var submitUrl = "";
+    if (top.wasResultUrlSpecified()) {
+        submitUrl = submitUrlFromSpecifiedUrl();
+    } else {
+        submitUrl = submitUrlFromTestRunnerLocation();
+    }
+
+    var formString = "<form name=\"resultsForm\" action=\"" + submitUrl + "\" method=\"post\" target=\"_top\">";
+    document.write(formString);
+</script>
+<input type="hidden" name="id">
+<input type="hidden" name="userAgent">
+<input type="hidden" name="jsUnitVersion">
+<input type="hidden" name="time">
+<input type="hidden" name="url">
+<input type="hidden" name="cacheBuster">
+<select size="5" name="testCases" multiple></select>
+</form>
+<script language="javascript" type="text/javascript">
+    function populateHeaderFields(id, userAgent, jsUnitVersion, baseURL) {
+        document.resultsForm.id.value = id;
+        document.resultsForm.userAgent.value = userAgent;
+        document.resultsForm.jsUnitVersion.value = jsUnitVersion;
+        document.resultsForm.url.value = baseURL;
+        document.resultsForm.cacheBuster.value = new Date().getTime();
+    }
+    function submitResults() {
+        var testCasesField = document.resultsForm.testCases;
+        for (var i = 0; i < testCasesField.length; i++) {
+            testCasesField[i].selected = true;
+        }
+        document.resultsForm.submit();
+    }
+</script>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-status.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-status.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/main-status.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit main-status.html</title>
+    <link rel="stylesheet" type="text/css" href="../css/jsUnitStyle.css">
+</head>
+
+<body>
+<div id="content"><b>Status:</b> (Idle)</div>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/testContainer.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/testContainer.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/testContainer.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Test Container</title>
+</head>
+<frameset rows="0, *" border="0">
+    <frame name="testContainerController" src="testContainerController.html">
+    <frame name="testFrame" src="emptyPage.html">
+    <noframes>
+        <body>
+        <p>Sorry, JsUnit requires frames.</p>
+        </body>
+    </noframes>
+</frameset>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/testContainerController.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/testContainerController.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/testContainerController.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Test Container Controller</title>
+    <script language="javascript" type="text/javascript">
+        var containerReady = false;
+
+        function init() {
+            containerReady = true;
+        }
+
+        function isPageLoaded() {
+            if (!containerReady)
+                return false;
+
+            var isTestPageLoaded = false;
+
+            try {
+                // attempt to access the var isTestPageLoaded in the testFrame
+                if (typeof(top.testManager.containerTestFrame.isTestPageLoaded) != 'undefined') {
+                    isTestPageLoaded = top.testManager.containerTestFrame.isTestPageLoaded;
+                }
+
+                // ok, if the above did not throw an exception, then the
+                // variable is defined. If the onload has not fired in the
+                // testFrame then isTestPageLoaded is still false. Otherwise
+                // the testFrame has set it to true
+            }
+            catch (e) {
+                // an error occured while attempting to access the isTestPageLoaded
+                // in the testFrame, therefore the testFrame has not loaded yet
+                isTestPageLoaded = false;
+            }
+            return isTestPageLoaded;
+        }
+
+        function isContainerReady() {
+            return containerReady;
+        }
+
+        function setNotReady() {
+            try {
+                // attempt to set the isTestPageLoaded variable
+                // in the test frame to false.
+                top.testManager.containerTestFrame.isTestPageLoaded = false;
+            }
+            catch (e) {
+                // testFrame.isTestPageLoaded not available... ignore
+            }
+        }
+        function setTestPage(testPageURI) {
+            setNotReady();
+            top.jsUnitParseParms(testPageURI);
+            testPageURI = appendCacheBusterParameterTo(testPageURI);
+            try {
+                top.testManager.containerTestFrame.location.href = testPageURI;
+            } catch (e) {
+            }
+        }
+
+        function appendCacheBusterParameterTo(testPageURI) {
+            if (testPageURI.indexOf("?") == -1)
+                testPageURI += "?";
+            else
+                testPageURI += "&";
+            testPageURI += "cacheBuster=";
+            testPageURI += new Date().getTime();
+            return testPageURI;
+        }
+    </script>
+</head>
+
+<body onload="init()">
+Test Container Controller
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/xbDebug.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/xbDebug.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/app/xbDebug.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,306 @@
+// xbDebug.js revision: 0.003 2002-02-26
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Licensed under Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ * Full Terms at /xbProjects-srce/license/mpl-tri-license.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Netscape code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Bob Clary <bclary at netscape.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ChangeLog:
+
+2002-02-25: bclary - modified xbDebugTraceOject to make sure 
+            that original versions of wrapped functions were not
+            rewrapped. This had caused an infinite loop in IE.
+
+2002-02-07: bclary - modified xbDebug.prototype.close to not null
+            the debug window reference. This can cause problems with
+	          Internet Explorer if the page is refreshed. These issues will
+	          be addressed at a later date.
+*/
+
+function xbDebug()
+{
+    this.on = false;
+    this.stack = new Array();
+    this.debugwindow = null;
+    this.execprofile = new Object();
+}
+
+xbDebug.prototype.push = function ()
+{
+    this.stack[this.stack.length] = this.on;
+    this.on = true;
+}
+
+xbDebug.prototype.pop = function ()
+{
+    this.on = this.stack[this.stack.length - 1];
+    --this.stack.length;
+}
+
+xbDebug.prototype.open = function ()
+{
+    if (this.debugwindow && !this.debugwindow.closed)
+        this.close();
+
+    this.debugwindow = window.open('about:blank', 'DEBUGWINDOW', 'height=400,width=600,resizable=yes,scrollbars=yes');
+
+    this.debugwindow.title = 'xbDebug Window';
+    this.debugwindow.document.write('<html><head><title>xbDebug Window</title></head><body><h3>Javascript Debug Window</h3></body></html>');
+    this.debugwindow.focus();
+}
+
+xbDebug.prototype.close = function ()
+{
+    if (!this.debugwindow)
+        return;
+
+    if (!this.debugwindow.closed)
+        this.debugwindow.close();
+
+    // bc 2002-02-07, other windows may still hold a reference to this: this.debugwindow = null;
+}
+
+xbDebug.prototype.dump = function (msg)
+{
+    if (!this.on)
+        return;
+
+    if (!this.debugwindow || this.debugwindow.closed)
+        this.open();
+
+    this.debugwindow.document.write(msg + '<br>');
+
+    return;
+}
+
+var xbDEBUG = new xbDebug();
+
+window.onunload = function () {
+    xbDEBUG.close();
+}
+
+function xbDebugGetFunctionName(funcref)
+{
+
+    if (!funcref)
+    {
+        return '';
+    }
+
+    if (funcref.name)
+        return funcref.name;
+
+    var name = funcref + '';
+    name = name.substring(name.indexOf(' ') + 1, name.indexOf('('));
+    funcref.name = name;
+
+    if (!name) alert('name not defined');
+    return name;
+}
+
+// emulate functionref.apply for IE mac and IE win < 5.5
+function xbDebugApplyFunction(funcname, funcref, thisref, argumentsref)
+{
+    var rv;
+
+    if (!funcref)
+    {
+        alert('xbDebugApplyFunction: funcref is null');
+    }
+
+    if (typeof(funcref.apply) != 'undefined')
+        return funcref.apply(thisref, argumentsref);
+
+    var applyexpr = 'thisref.xbDebug_orig_' + funcname + '(';
+    var i;
+
+    for (i = 0; i < argumentsref.length; i++)
+    {
+        applyexpr += 'argumentsref[' + i + '],';
+    }
+
+    if (argumentsref.length > 0)
+    {
+        applyexpr = applyexpr.substring(0, applyexpr.length - 1);
+    }
+
+    applyexpr += ')';
+
+    return eval(applyexpr);
+}
+
+function xbDebugCreateFunctionWrapper(scopename, funcname, precall, postcall)
+{
+    var wrappedfunc;
+    var scopeobject = eval(scopename);
+    var funcref = scopeobject[funcname];
+
+    scopeobject['xbDebug_orig_' + funcname] = funcref;
+
+    wrappedfunc = function ()
+    {
+        var rv;
+
+        precall(scopename, funcname, arguments);
+        rv = xbDebugApplyFunction(funcname, funcref, scopeobject, arguments);
+        postcall(scopename, funcname, arguments, rv);
+        return rv;
+    };
+
+    if (typeof(funcref.constructor) != 'undefined')
+        wrappedfunc.constructor = funcref.constuctor;
+
+    if (typeof(funcref.prototype) != 'undefined')
+        wrappedfunc.prototype = funcref.prototype;
+
+    scopeobject[funcname] = wrappedfunc;
+}
+
+function xbDebugCreateMethodWrapper(contextname, classname, methodname, precall, postcall)
+{
+    var context = eval(contextname);
+    var methodref = context[classname].prototype[methodname];
+
+    context[classname].prototype['xbDebug_orig_' + methodname] = methodref;
+
+    var wrappedmethod = function ()
+    {
+        var rv;
+        // eval 'this' at method run time to pick up reference to the object's instance
+        var thisref = eval('this');
+        // eval 'arguments' at method run time to pick up method's arguments
+        var argsref = arguments;
+
+        precall(contextname + '.' + classname, methodname, argsref);
+        rv = xbDebugApplyFunction(methodname, methodref, thisref, argsref);
+        postcall(contextname + '.' + classname, methodname, argsref, rv);
+        return rv;
+    };
+
+    return wrappedmethod;
+}
+
+function xbDebugPersistToString(obj)
+{
+    var s = '';
+    var p;
+
+    if (obj == null)
+        return 'null';
+
+    switch (typeof(obj))
+            {
+        case 'number':
+            return obj;
+        case 'string':
+            return '"' + obj + '"';
+        case 'undefined':
+            return 'undefined';
+        case 'boolean':
+            return obj + '';
+    }
+
+    if (obj.constructor)
+        return '[' + xbDebugGetFunctionName(obj.constructor) + ']';
+
+    return null;
+}
+
+function xbDebugTraceBefore(scopename, funcname, funcarguments)
+{
+    var i;
+    var s = '';
+    var execprofile = xbDEBUG.execprofile[scopename + '.' + funcname];
+    if (!execprofile)
+        execprofile = xbDEBUG.execprofile[scopename + '.' + funcname] = { started: 0, time: 0, count: 0 };
+
+    for (i = 0; i < funcarguments.length; i++)
+    {
+        s += xbDebugPersistToString(funcarguments[i]);
+        if (i < funcarguments.length - 1)
+            s += ', ';
+    }
+
+    xbDEBUG.dump('enter ' + scopename + '.' + funcname + '(' + s + ')');
+    execprofile.started = (new Date()).getTime();
+}
+
+function xbDebugTraceAfter(scopename, funcname, funcarguments, rv)
+{
+    var i;
+    var s = '';
+    var execprofile = xbDEBUG.execprofile[scopename + '.' + funcname];
+    if (!execprofile)
+        xbDEBUG.dump('xbDebugTraceAfter: execprofile not created for ' + scopename + '.' + funcname);
+    else if (execprofile.started == 0)
+        xbDEBUG.dump('xbDebugTraceAfter: execprofile.started == 0 for ' + scopename + '.' + funcname);
+    else
+    {
+        execprofile.time += (new Date()).getTime() - execprofile.started;
+        execprofile.count++;
+        execprofile.started = 0;
+    }
+
+    for (i = 0; i < funcarguments.length; i++)
+    {
+        s += xbDebugPersistToString(funcarguments[i]);
+        if (i < funcarguments.length - 1)
+            s += ', ';
+    }
+
+    xbDEBUG.dump('exit  ' + scopename + '.' + funcname + '(' + s + ')==' + xbDebugPersistToString(rv));
+}
+
+function xbDebugTraceFunction(scopename, funcname)
+{
+    xbDebugCreateFunctionWrapper(scopename, funcname, xbDebugTraceBefore, xbDebugTraceAfter);
+}
+
+function xbDebugTraceObject(contextname, classname)
+{
+    var classref = eval(contextname + '.' + classname);
+    var p;
+    var sp;
+
+    if (!classref || !classref.prototype)
+        return;
+
+    for (p in classref.prototype)
+    {
+        sp = p + '';
+        if (typeof(classref.prototype[sp]) == 'function' && (sp).indexOf('xbDebug_orig') == -1)
+        {
+            classref.prototype[sp] = xbDebugCreateMethodWrapper(contextname, classname, sp, xbDebugTraceBefore, xbDebugTraceAfter);
+        }
+    }
+}
+
+function xbDebugDumpProfile()
+{
+    var p;
+    var execprofile;
+    var avg;
+
+    for (p in xbDEBUG.execprofile)
+    {
+        execprofile = xbDEBUG.execprofile[p];
+        avg = Math.round(100 * execprofile.time / execprofile.count) / 100;
+        xbDEBUG.dump('Execution profile ' + p + ' called ' + execprofile.count + ' times. Total time=' + execprofile.time + 'ms. Avg Time=' + avg + 'ms.');
+    }
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/changelog.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/changelog.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/changelog.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,60 @@
+TRACING
+- Tracing is now color coded by trace level
+- Traces are now prefixed with the Test Page and Test Function from which the trace is made
+
+ASSERTION FUNCTIONS
+- assertArrayEquals(array1, array2) introduced
+- assertObjectEquals(object1, object2) introduced
+- assertHTMLEquals function introduced
+- assertEvaluatesToTrue and assertEvaluatesToFalse introduced
+- assertHashEquals     }
+- assertRoughlyEquals  } Pivotal functions
+- assertContains       }
+
+- changed expected/actual values display strings to use angle brackets, rather than square brackets
+
+- CLIENT-SIDE
+- HTML in result output is now correctly escaped
+- page load timeout changed to 120 seconds by default
+- setup page timeout change to 120 seconds by default
+- cache-buster for testpage retrieval & results submission
+- jsUnitRestoredHTMLDiv
+- turn off tracing, alerts, confirms when submitting
+- testPage parameter should be URL-encoded (only opera cares though)
+- Speed-up of Firefox/Mozilla (thanks to Chris Wesseling)
+- jsUnitMockTimeout.js (thanks to Pivotal, especially Nathan Wilmes)
+
+SERVER
+- start-browser scripts in bin
+- Migration of Java code to require Java 5.0
+- JSPs require a JDK
+- StandaloneTest and DistributedTest continue on after a failure in a particular browser or remote server respectively
+- StandaloneTest has a suite() method that makes the test run have multiple JUnit tests, one per browser
+- DistribuedTest has a suite() method that makes the test run have multiple JUnit tests, one per remote machine URL
+- Change to XML output format of test runs to include more information and be more hierarchical (machine->browser->test page->test case)
+- Logs are now prefixed with "JSTEST-" in order to match JUnit's "TEST-"
+- Logs now contain the browser ID (e.g. JSTEST-12345.5.xml means browser with ID 5); displayer servlet now takes an id and a browserId parameter
+- added support for launching the default system browser on Windows and UNIX (see the constant on net.jsunit.StandaloneTest)
+- StandaloneTest now runs tests in all specified browsers, even after an earlier browser failed
+- New "config" servlet that shows the configuration as XML of the server
+- Distributed Tests now send back an XML document that includes the XML for browser results as opposed to just a "success"/"failure" node
+- runner servlet takes a "url" querystring parameter that overrides the server's url property
+- test run requests to the JsUnitServer and the FarmServer are queued up and in serial so that different clients don't step on eachother
+- addition of new configuration parameter, "closeBrowsersAfterTestRuns", for whether to attempt to close browsers after test runs
+- addition of new configuration property, "timeoutSeconds", for how long to time browsers out
+- addition of new configuration property, "ignoreUnresponsiveRemoteMachines", for whether to care that remote machines don't uccessfully run the tests
+- addition of new configuration property, "description", which contains a human-readable description of the server
+- new index.jsp ("/") page
+- jsunit.org registered; redirects to edwardh.com/jsunit
+
+BUGS
+- fix for "retry test run" bug
+- bug 1070436 fixed
+- bug with multiple browsers and resultId specified fixed
+- Bug 1281427 fixed (test submission for Opera)
+- Safari fix
+- Bug 1431040 fixed
+
+ECLIPSE PLUGIN
+- Eclipse plugin version 1.0
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/css/jsUnitStyle.css
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/css/jsUnitStyle.css	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/css/jsUnitStyle.css	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,83 @@
+body {
+    margin-top: 0;
+    margin-bottom: 0;
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+    color: #000;
+    font-size: 0.8em;
+    background-color: #fff;
+}
+
+a:link, a:visited {
+    color: #00F;
+}
+
+a:hover {
+    color: #F00;
+}
+
+h1 {
+    font-size: 1.2em;
+    font-weight: bold;
+    color: #039;
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+h2 {
+    font-weight: bold;
+    color: #039;
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+h3 {
+    font-weight: bold;
+    color: #039;
+    text-decoration: underline;
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+h4 {
+    font-weight: bold;
+    color: #039;
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.jsUnitTestResultSuccess {
+    color: #000;
+}
+
+.jsUnitTestResultNotSuccess {
+    color: #F00;
+}
+
+.unselectedTab {
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+    height: 26px;
+    background: #FFFFFF;
+    border-style: solid;
+    border-bottom-width: 1px;
+    border-top-width: 1px;
+    border-left-width: 1px;
+    border-right-width: 1px;
+}
+
+.selectedTab {
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+    height: 26px;
+    background: #DDDDDD;
+    font-weight: bold;
+    border-style: solid;
+    border-bottom-width: 0px;
+    border-top-width: 1px;
+    border-left-width: 1px;
+    border-right-width: 1px;
+}
+
+.tabHeaderSeparator {
+    height: 26px;
+    background: #FFFFFF;
+    border-style: solid;
+    border-bottom-width: 1px;
+    border-top-width: 0px;
+    border-left-width: 0px;
+    border-right-width: 0px;
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/green.gif
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/green.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/logo_jsunit.gif
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/logo_jsunit.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/powerby-transparent.gif
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/powerby-transparent.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/red.gif
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/images/red.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/JDOM_license.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/JDOM_license.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/JDOM_license.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,56 @@
+/*-- 
+
+ $Id: JDOM_license.txt 961 2006-04-25 07:41:01Z ddeboer $
+
+ Copyright (C) 2000-2003 Jason Hunter & Brett McLaughlin.
+ All rights reserved.
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions, and the following disclaimer.
+ 
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions, and the disclaimer that follows 
+    these conditions in the documentation and/or other materials 
+    provided with the distribution.
+
+ 3. The name "JDOM" must not be used to endorse or promote products
+    derived from this software without prior written permission.  For
+    written permission, please contact <license AT jdom DOT org>.
+ 
+ 4. Products derived from this software may not be called "JDOM", nor
+    may "JDOM" appear in their name, without prior written permission
+    from the JDOM Project Management <pm AT jdom DOT org>.
+ 
+ In addition, we request (but do not require) that you include in the 
+ end-user documentation provided with the redistribution and/or in the 
+ software itself an acknowledgement equivalent to the following:
+     "This product includes software developed by the
+      JDOM Project (http://www.jdom.org/)."
+ Alternatively, the acknowledgment may be graphical using the logos 
+ available at http://www.jdom.org/images/logos.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 JDOM AUTHORS OR THE PROJECT
+ 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.
+
+ This software consists of voluntary contributions made by many 
+ individuals on behalf of the JDOM Project and was originally 
+ created by Jason Hunter <jhunter AT jdom DOT org> and
+ Brett McLaughlin <brett AT jdom DOT org>.  For more information on
+ the JDOM Project, please see <http://www.jdom.org/>.
+ 
+ */
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/Jetty_license.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/Jetty_license.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/Jetty_license.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,213 @@
+<HTML>
+<HEAD>
+    <TITLE>Jetty License</TITLE>
+</HEAD>
+
+<BODY BGCOLOR="#FFFFFF">
+<FONT FACE=ARIAL,HELVETICA>
+<CENTER><FONT SIZE=+3><B>Jetty License</B></FONT></CENTER>
+<CENTER><FONT SIZE=-1><B>$Revision: 961 $</B></FONT></CENTER>
+
+<B>Preamble:</B>
+
+<p>
+
+    The intent of this document is to state the conditions under which the
+    Jetty Package may be copied, such that the Copyright Holder maintains some
+    semblance of control over the development of the package, while giving the
+    users of the package the right to use, distribute and make reasonable
+    modifications to the Package in accordance with the goals and ideals of
+    the Open Source concept as described at
+    <A HREF="http://www.opensource.org">http://www.opensource.org</A>.
+
+<P>
+    It is the intent of this license to allow commercial usage of the Jetty
+    package, so long as the source code is distributed or suitable visible
+    credit given or other arrangements made with the copyright holders.
+
+<P><B>Definitions:</B>
+
+<P>
+
+<UL>
+    <LI> "Jetty" refers to the collection of Java classes that are
+        distributed as a HTTP server with servlet capabilities and
+        associated utilities.
+
+    <p>
+
+    <LI> "Package" refers to the collection of files distributed by the
+        Copyright Holder, and derivatives of that collection of files
+        created through textual modification.
+
+    <P>
+
+    <LI> "Standard Version" refers to such a Package if it has not been
+        modified, or has been modified in accordance with the wishes
+        of the Copyright Holder.
+
+    <P>
+
+    <LI> "Copyright Holder" is whoever is named in the copyright or
+        copyrights for the package. <BR>
+        Mort Bay Consulting Pty. Ltd. (Australia) is the "Copyright
+        Holder" for the Jetty package.
+
+    <P>
+
+    <LI> "You" is you, if you're thinking about copying or distributing
+        this Package.
+
+    <P>
+
+    <LI> "Reasonable copying fee" is whatever you can justify on the
+        basis of media cost, duplication charges, time of people involved,
+        and so on. (You will not be required to justify it to the
+        Copyright Holder, but only to the computing community at large
+        as a market that must bear the fee.)
+
+    <P>
+
+    <LI> "Freely Available" means that no fee is charged for the item
+        itself, though there may be fees involved in handling the item.
+        It also means that recipients of the item may redistribute it
+        under the same conditions they received it.
+
+    <P>
+</UL>
+
+0. The Jetty Package is Copyright (c) Mort Bay Consulting Pty. Ltd.
+(Australia) and others. Individual files in this package may contain
+additional copyright notices. The javax.servlet packages are copyright
+Sun Microsystems Inc. <P>
+
+    1. The Standard Version of the Jetty package is
+    available from <A HREF=http://jetty.mortbay.org>http://jetty.mortbay.org</A>.
+
+<P>
+
+    2. You may make and distribute verbatim copies of the source form
+    of the Standard Version of this Package without restriction, provided that
+    you include this license and all of the original copyright notices
+    and associated disclaimers.
+
+<P>
+
+    3. You may make and distribute verbatim copies of the compiled form of the
+    Standard Version of this Package without restriction, provided that you
+    include this license.
+
+<P>
+
+    4. You may apply bug fixes, portability fixes and other modifications
+    derived from the Public Domain or from the Copyright Holder. A Package
+    modified in such a way shall still be considered the Standard Version.
+
+<P>
+
+    5. You may otherwise modify your copy of this Package in any way, provided
+    that you insert a prominent notice in each changed file stating how and
+    when you changed that file, and provided that you do at least ONE of the
+    following:
+
+<P>
+
+<BLOCKQUOTE>
+    a) Place your modifications in the Public Domain or otherwise make them
+    Freely Available, such as by posting said modifications to Usenet or
+    an equivalent medium, or placing the modifications on a major archive
+    site such as ftp.uu.net, or by allowing the Copyright Holder to include
+    your modifications in the Standard Version of the Package.<P>
+
+    b) Use the modified Package only within your corporation or organization.
+
+    <P>
+
+        c) Rename any non-standard classes so the names do not conflict
+        with standard classes, which must also be provided, and provide
+        a separate manual page for each non-standard class that clearly
+        documents how it differs from the Standard Version.
+
+    <P>
+
+        d) Make other arrangements with the Copyright Holder.
+
+    <P>
+</BLOCKQUOTE>
+
+6. You may distribute modifications or subsets of this Package in source
+code or compiled form, provided that you do at least ONE of the following:<P>
+
+<BLOCKQUOTE>
+    a) Distribute this license and all original copyright messages, together
+    with instructions (in the about dialog, manual page or equivalent) on where
+    to get the complete Standard Version.<P>
+
+    b) Accompany the distribution with the machine-readable source of
+    the Package with your modifications. The modified package must include
+    this license and all of the original copyright notices and associated
+    disclaimers, together with instructions on where to get the complete
+    Standard Version.
+
+    <P>
+
+        c) Make other arrangements with the Copyright Holder.
+
+    <P>
+</BLOCKQUOTE>
+
+7. You may charge a reasonable copying fee for any distribution of this
+Package. You may charge any fee you choose for support of this Package.
+You may not charge a fee for this Package itself. However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial) software
+distribution provided that you meet the other distribution requirements
+of this license.<P>
+
+    8. Input to or the output produced from the programs of this Package
+    do not automatically fall under the copyright of this Package, but
+    belong to whomever generated them, and may be sold commercially, and
+    may be aggregated with this Package.
+
+<P>
+
+    9. Any program subroutines supplied by you and linked into this Package
+    shall not be considered part of this Package.
+
+<P>
+
+    10. The name of the Copyright Holder may not be used to endorse or promote
+    products derived from this software without specific prior written
+    permission.
+
+<P>
+
+    11. This license may change with each release of a Standard Version of
+    the Package. You may choose to use the license associated with version
+    you are using or the license of the latest Standard Version.
+
+<P>
+
+    12. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+    IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+<P>
+
+    13. If any superior law implies a warranty, the sole remedy under such shall
+    be , at the Copyright Holders option either a) return of any price paid or
+    b) use or reasonable endeavours to repair or replace the software.
+
+<P>
+
+    14. This license shall be read under the laws of Australia.
+
+<P>
+
+<center>The End</center>
+
+<center><FONT size=-1>This license was derived from the <I>Artistic</I> license published
+    on <a href=http://www.opensource.org>http://www.opensource.com</a></font></center>
+</FONT>
+
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/MPL-1.1.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/MPL-1.1.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/MPL-1.1.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,470 @@
+                          MOZILLA PUBLIC LICENSE
+                                Version 1.1
+
+                              ---------------
+
+1. Definitions.
+
+     1.0.1. "Commercial Use" means distribution or otherwise making the
+     Covered Code available to a third party.
+
+     1.1. "Contributor" means each entity that creates or contributes to
+     the creation of Modifications.
+
+     1.2. "Contributor Version" means the combination of the Original
+     Code, prior Modifications used by a Contributor, and the Modifications
+     made by that particular Contributor.
+
+     1.3. "Covered Code" means the Original Code or Modifications or the
+     combination of the Original Code and Modifications, in each case
+     including portions thereof.
+
+     1.4. "Electronic Distribution Mechanism" means a mechanism generally
+     accepted in the software development community for the electronic
+     transfer of data.
+
+     1.5. "Executable" means Covered Code in any form other than Source
+     Code.
+
+     1.6. "Initial Developer" means the individual or entity identified
+     as the Initial Developer in the Source Code notice required by Exhibit
+     A.
+
+     1.7. "Larger Work" means a work which combines Covered Code or
+     portions thereof with code not governed by the terms of this License.
+
+     1.8. "License" means this document.
+
+     1.8.1. "Licensable" means having the right to grant, to the maximum
+     extent possible, whether at the time of the initial grant or
+     subsequently acquired, any and all of the rights conveyed herein.
+
+     1.9. "Modifications" means any addition to or deletion from the
+     substance or structure of either the Original Code or any previous
+     Modifications. When Covered Code is released as a series of files, a
+     Modification is:
+          A. Any addition to or deletion from the contents of a file
+          containing Original Code or previous Modifications.
+
+          B. Any new file that contains any part of the Original Code or
+          previous Modifications.
+
+     1.10. "Original Code" means Source Code of computer software code
+     which is described in the Source Code notice required by Exhibit A as
+     Original Code, and which, at the time of its release under this
+     License is not already Covered Code governed by this License.
+
+     1.10.1. "Patent Claims" means any patent claim(s), now owned or
+     hereafter acquired, including without limitation,  method, process,
+     and apparatus claims, in any patent Licensable by grantor.
+
+     1.11. "Source Code" means the preferred form of the Covered Code for
+     making modifications to it, including all modules it contains, plus
+     any associated interface definition files, scripts used to control
+     compilation and installation of an Executable, or source code
+     differential comparisons against either the Original Code or another
+     well known, available Covered Code of the Contributor's choice. The
+     Source Code can be in a compressed or archival form, provided the
+     appropriate decompression or de-archiving software is widely available
+     for no charge.
+
+     1.12. "You" (or "Your")  means an individual or a legal entity
+     exercising rights under, and complying with all of the terms of, this
+     License or a future version of this License issued under Section 6.1.
+     For legal entities, "You" includes any entity which controls, is
+     controlled by, or is under common control with You. For purposes of
+     this definition, "control" means (a) the power, direct or indirect,
+     to cause the direction or management of such entity, whether by
+     contract or otherwise, or (b) ownership of more than fifty percent
+     (50%) of the outstanding shares or beneficial ownership of such
+     entity.
+
+2. Source Code License.
+
+     2.1. The Initial Developer Grant.
+     The Initial Developer hereby grants You a world-wide, royalty-free,
+     non-exclusive license, subject to third party intellectual property
+     claims:
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Initial Developer to use, reproduce,
+          modify, display, perform, sublicense and distribute the Original
+          Code (or portions thereof) with or without Modifications, and/or
+          as part of a Larger Work; and
+
+          (b) under Patents Claims infringed by the making, using or
+          selling of Original Code, to make, have made, use, practice,
+          sell, and offer for sale, and/or otherwise dispose of the
+          Original Code (or portions thereof).
+
+          (c) the licenses granted in this Section 2.1(a) and (b) are
+          effective on the date Initial Developer first distributes
+          Original Code under the terms of this License.
+
+          (d) Notwithstanding Section 2.1(b) above, no patent license is
+          granted: 1) for code that You delete from the Original Code; 2)
+          separate from the Original Code;  or 3) for infringements caused
+          by: i) the modification of the Original Code or ii) the
+          combination of the Original Code with other software or devices.
+
+     2.2. Contributor Grant.
+     Subject to third party intellectual property claims, each Contributor
+     hereby grants You a world-wide, royalty-free, non-exclusive license
+
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Contributor, to use, reproduce, modify,
+          display, perform, sublicense and distribute the Modifications
+          created by such Contributor (or portions thereof) either on an
+          unmodified basis, with other Modifications, as Covered Code
+          and/or as part of a Larger Work; and
+
+          (b) under Patent Claims infringed by the making, using, or
+          selling of  Modifications made by that Contributor either alone
+          and/or in combination with its Contributor Version (or portions
+          of such combination), to make, use, sell, offer for sale, have
+          made, and/or otherwise dispose of: 1) Modifications made by that
+          Contributor (or portions thereof); and 2) the combination of
+          Modifications made by that Contributor with its Contributor
+          Version (or portions of such combination).
+
+          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+          effective on the date Contributor first makes Commercial Use of
+          the Covered Code.
+
+          (d)    Notwithstanding Section 2.2(b) above, no patent license is
+          granted: 1) for any code that Contributor has deleted from the
+          Contributor Version; 2)  separate from the Contributor Version;
+          3)  for infringements caused by: i) third party modifications of
+          Contributor Version or ii)  the combination of Modifications made
+          by that Contributor with other software  (except as part of the
+          Contributor Version) or other devices; or 4) under Patent Claims
+          infringed by Covered Code in the absence of Modifications made by
+          that Contributor.
+
+3. Distribution Obligations.
+
+     3.1. Application of License.
+     The Modifications which You create or to which You contribute are
+     governed by the terms of this License, including without limitation
+     Section 2.2. The Source Code version of Covered Code may be
+     distributed only under the terms of this License or a future version
+     of this License released under Section 6.1, and You must include a
+     copy of this License with every copy of the Source Code You
+     distribute. You may not offer or impose any terms on any Source Code
+     version that alters or restricts the applicable version of this
+     License or the recipients' rights hereunder. However, You may include
+     an additional document offering the additional rights described in
+     Section 3.5.
+
+     3.2. Availability of Source Code.
+     Any Modification which You create or to which You contribute must be
+     made available in Source Code form under the terms of this License
+     either on the same media as an Executable version or via an accepted
+     Electronic Distribution Mechanism to anyone to whom you made an
+     Executable version available; and if made available via Electronic
+     Distribution Mechanism, must remain available for at least twelve (12)
+     months after the date it initially became available, or at least six
+     (6) months after a subsequent version of that particular Modification
+     has been made available to such recipients. You are responsible for
+     ensuring that the Source Code version remains available even if the
+     Electronic Distribution Mechanism is maintained by a third party.
+
+     3.3. Description of Modifications.
+     You must cause all Covered Code to which You contribute to contain a
+     file documenting the changes You made to create that Covered Code and
+     the date of any change. You must include a prominent statement that
+     the Modification is derived, directly or indirectly, from Original
+     Code provided by the Initial Developer and including the name of the
+     Initial Developer in (a) the Source Code, and (b) in any notice in an
+     Executable version or related documentation in which You describe the
+     origin or ownership of the Covered Code.
+
+     3.4. Intellectual Property Matters
+          (a) Third Party Claims.
+          If Contributor has knowledge that a license under a third party's
+          intellectual property rights is required to exercise the rights
+          granted by such Contributor under Sections 2.1 or 2.2,
+          Contributor must include a text file with the Source Code
+          distribution titled "LEGAL" which describes the claim and the
+          party making the claim in sufficient detail that a recipient will
+          know whom to contact. If Contributor obtains such knowledge after
+          the Modification is made available as described in Section 3.2,
+          Contributor shall promptly modify the LEGAL file in all copies
+          Contributor makes available thereafter and shall take other steps
+          (such as notifying appropriate mailing lists or newsgroups)
+          reasonably calculated to inform those who received the Covered
+          Code that new knowledge has been obtained.
+
+          (b) Contributor APIs.
+          If Contributor's Modifications include an application programming
+          interface and Contributor has knowledge of patent licenses which
+          are reasonably necessary to implement that API, Contributor must
+          also include this information in the LEGAL file.
+
+               (c)    Representations.
+          Contributor represents that, except as disclosed pursuant to
+          Section 3.4(a) above, Contributor believes that Contributor's
+          Modifications are Contributor's original creation(s) and/or
+          Contributor has sufficient rights to grant the rights conveyed by
+          this License.
+
+     3.5. Required Notices.
+     You must duplicate the notice in Exhibit A in each file of the Source
+     Code.  If it is not possible to put such notice in a particular Source
+     Code file due to its structure, then You must include such notice in a
+     location (such as a relevant directory) where a user would be likely
+     to look for such a notice.  If You created one or more Modification(s)
+     You may add your name as a Contributor to the notice described in
+     Exhibit A.  You must also duplicate this License in any documentation
+     for the Source Code where You describe recipients' rights or ownership
+     rights relating to Covered Code.  You may choose to offer, and to
+     charge a fee for, warranty, support, indemnity or liability
+     obligations to one or more recipients of Covered Code. However, You
+     may do so only on Your own behalf, and not on behalf of the Initial
+     Developer or any Contributor. You must make it absolutely clear than
+     any such warranty, support, indemnity or liability obligation is
+     offered by You alone, and You hereby agree to indemnify the Initial
+     Developer and every Contributor for any liability incurred by the
+     Initial Developer or such Contributor as a result of warranty,
+     support, indemnity or liability terms You offer.
+
+     3.6. Distribution of Executable Versions.
+     You may distribute Covered Code in Executable form only if the
+     requirements of Section 3.1-3.5 have been met for that Covered Code,
+     and if You include a notice stating that the Source Code version of
+     the Covered Code is available under the terms of this License,
+     including a description of how and where You have fulfilled the
+     obligations of Section 3.2. The notice must be conspicuously included
+     in any notice in an Executable version, related documentation or
+     collateral in which You describe recipients' rights relating to the
+     Covered Code. You may distribute the Executable version of Covered
+     Code or ownership rights under a license of Your choice, which may
+     contain terms different from this License, provided that You are in
+     compliance with the terms of this License and that the license for the
+     Executable version does not attempt to limit or alter the recipient's
+     rights in the Source Code version from the rights set forth in this
+     License. If You distribute the Executable version under a different
+     license You must make it absolutely clear that any terms which differ
+     from this License are offered by You alone, not by the Initial
+     Developer or any Contributor. You hereby agree to indemnify the
+     Initial Developer and every Contributor for any liability incurred by
+     the Initial Developer or such Contributor as a result of any such
+     terms You offer.
+
+     3.7. Larger Works.
+     You may create a Larger Work by combining Covered Code with other code
+     not governed by the terms of this License and distribute the Larger
+     Work as a single product. In such a case, You must make sure the
+     requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+     If it is impossible for You to comply with any of the terms of this
+     License with respect to some or all of the Covered Code due to
+     statute, judicial order, or regulation then You must: (a) comply with
+     the terms of this License to the maximum extent possible; and (b)
+     describe the limitations and the code they affect. Such description
+     must be included in the LEGAL file described in Section 3.4 and must
+     be included with all distributions of the Source Code. Except to the
+     extent prohibited by statute or regulation, such description must be
+     sufficiently detailed for a recipient of ordinary skill to be able to
+     understand it.
+
+5. Application of this License.
+
+     This License applies to code to which the Initial Developer has
+     attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+     6.1. New Versions.
+     Netscape Communications Corporation ("Netscape") may publish revised
+     and/or new versions of the License from time to time. Each version
+     will be given a distinguishing version number.
+
+     6.2. Effect of New Versions.
+     Once Covered Code has been published under a particular version of the
+     License, You may always continue to use it under the terms of that
+     version. You may also choose to use such Covered Code under the terms
+     of any subsequent version of the License published by Netscape. No one
+     other than Netscape has the right to modify the terms applicable to
+     Covered Code created under this License.
+
+     6.3. Derivative Works.
+     If You create or use a modified version of this License (which you may
+     only do in order to apply it to code which is not already Covered Code
+     governed by this License), You must (a) rename Your license so that
+     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+     "MPL", "NPL" or any confusingly similar phrase do not appear in your
+     license (except to note that your license differs from this License)
+     and (b) otherwise make it clear that Your version of the license
+     contains terms which differ from the Mozilla Public License and
+     Netscape Public License. (Filling in the name of the Initial
+     Developer, Original Code or Contributor in the notice described in
+     Exhibit A shall not of themselves be deemed to be modifications of
+     this License.)
+
+7. DISCLAIMER OF WARRANTY.
+
+     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION.
+
+     8.1.  This License and the rights granted hereunder will terminate
+     automatically if You fail to comply with terms herein and fail to cure
+     such breach within 30 days of becoming aware of the breach. All
+     sublicenses to the Covered Code which are properly granted shall
+     survive any termination of this License. Provisions which, by their
+     nature, must remain in effect beyond the termination of this License
+     shall survive.
+
+     8.2.  If You initiate litigation by asserting a patent infringement
+     claim (excluding declatory judgment actions) against Initial Developer
+     or a Contributor (the Initial Developer or Contributor against whom
+     You file such action is referred to as "Participant")  alleging that:
+
+     (a)  such Participant's Contributor Version directly or indirectly
+     infringes any patent, then any and all rights granted by such
+     Participant to You under Sections 2.1 and/or 2.2 of this License
+     shall, upon 60 days notice from Participant terminate prospectively,
+     unless if within 60 days after receipt of notice You either: (i)
+     agree in writing to pay Participant a mutually agreeable reasonable
+     royalty for Your past and future use of Modifications made by such
+     Participant, or (ii) withdraw Your litigation claim with respect to
+     the Contributor Version against such Participant.  If within 60 days
+     of notice, a reasonable royalty and payment arrangement are not
+     mutually agreed upon in writing by the parties or the litigation claim
+     is not withdrawn, the rights granted by Participant to You under
+     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+     the 60 day notice period specified above.
+
+     (b)  any software, hardware, or device, other than such Participant's
+     Contributor Version, directly or indirectly infringes any patent, then
+     any rights granted to You by such Participant under Sections 2.1(b)
+     and 2.2(b) are revoked effective as of the date You first made, used,
+     sold, distributed, or had made, Modifications made by that
+     Participant.
+
+     8.3.  If You assert a patent infringement claim against Participant
+     alleging that such Participant's Contributor Version directly or
+     indirectly infringes any patent where such claim is resolved (such as
+     by license or settlement) prior to the initiation of patent
+     infringement litigation, then the reasonable value of the licenses
+     granted by such Participant under Sections 2.1 or 2.2 shall be taken
+     into account in determining the amount or value of any payment or
+     license.
+
+     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
+     all end user license agreements (excluding distributors and resellers)
+     which have been validly granted by You or any distributor hereunder
+     prior to termination shall survive termination.
+
+9. LIMITATION OF LIABILITY.
+
+     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+10. U.S. GOVERNMENT END USERS.
+
+     The Covered Code is a "commercial item," as that term is defined in
+     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+     software" and "commercial computer software documentation," as such
+     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+     all U.S. Government End Users acquire Covered Code with only those
+     rights set forth herein.
+
+11. MISCELLANEOUS.
+
+     This License represents the complete agreement concerning subject
+     matter hereof. If any provision of this License is held to be
+     unenforceable, such provision shall be reformed only to the extent
+     necessary to make it enforceable. This License shall be governed by
+     California law provisions (except to the extent applicable law, if
+     any, provides otherwise), excluding its conflict-of-law provisions.
+     With respect to disputes in which at least one party is a citizen of,
+     or an entity chartered or registered to do business in the United
+     States of America, any litigation relating to this License shall be
+     subject to the jurisdiction of the Federal Courts of the Northern
+     District of California, with venue lying in Santa Clara County,
+     California, with the losing party responsible for costs, including
+     without limitation, court costs and reasonable attorneys' fees and
+     expenses. The application of the United Nations Convention on
+     Contracts for the International Sale of Goods is expressly excluded.
+     Any law or regulation which provides that the language of a contract
+     shall be construed against the drafter shall not apply to this
+     License.
+
+12. RESPONSIBILITY FOR CLAIMS.
+
+     As between Initial Developer and the Contributors, each party is
+     responsible for claims and damages arising, directly or indirectly,
+     out of its utilization of rights under this License and You agree to
+     work with Initial Developer and Contributors to distribute such
+     responsibility on an equitable basis. Nothing herein is intended or
+     shall be deemed to constitute any admission of liability.
+
+13. MULTIPLE-LICENSED CODE.
+
+     Initial Developer may designate portions of the Covered Code as
+     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
+     Developer permits you to utilize portions of the Covered Code under
+     Your choice of the NPL or the alternative licenses, if any, specified
+     by the Initial Developer in the file described in Exhibit A.
+
+EXHIBIT A -Mozilla Public License.
+
+     ``The contents of this file are subject to the Mozilla Public License
+     Version 1.1 (the "License"); you may not use this file except in
+     compliance with the License. You may obtain a copy of the License at
+     http://www.mozilla.org/MPL/
+
+     Software distributed under the License is distributed on an "AS IS"
+     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+     License for the specific language governing rights and limitations
+     under the License.
+
+     The Original Code is ______________________________________.
+
+     The Initial Developer of the Original Code is ________________________.
+     Portions created by ______________________ are Copyright (C) ______
+     _______________________. All Rights Reserved.
+
+     Contributor(s): ______________________________________.
+
+     Alternatively, the contents of this file may be used under the terms
+     of the _____ license (the  "[___] License"), in which case the
+     provisions of [______] License are applicable instead of those
+     above.  If you wish to allow use of your version of this file only
+     under the terms of the [____] License and not to allow others to use
+     your version of this file under the MPL, indicate your decision by
+     deleting  the provisions above and replace  them with the notice and
+     other provisions required by the [___] License.  If you do not delete
+     the provisions above, a recipient may use your version of this file
+     under either the MPL or the [___] License."
+
+     [NOTE: The text of this Exhibit A may differ slightly from the text of
+     the notices in the Source Code files of the Original Code. You should
+     use the text of this Exhibit A rather than the text found in the
+     Original Code Source Code for Your Modifications.]
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/gpl-2.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/gpl-2.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/gpl-2.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program 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.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/index.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/index.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/index.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,141 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<!-- JsUnit -->
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is Edward Hieatt code.
+   -
+   - The Initial Developer of the Original Code is
+   - Edward Hieatt, edward at jsunit.net.
+   - Portions created by the Initial Developer are Copyright (C) 2001
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   - Edward Hieatt, edward at jsunit.net (original author)
+   - Bob Clary, bc at bclary.comn
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either the GNU General Public License Version 2 or later (the "GPL"), or
+   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the LGPL or the GPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->
+
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Licensing</title>
+    <link rel="stylesheet" type="text/css" href="../app/css/jsUnitStyle.css">
+</head>
+
+<body>
+<table width="100%" cellpadding="0" cellspacing="0" border="1" summary="jsUnit Information">
+    <tr>
+        <th align="center" valign="top"><h1>JsUnit Licenses</h1></th>
+
+        <td align="right" valign="top">
+            <a href="http://www.jsunit.net/" target="_blank">JsUnit Home</a><br>
+            <a href="mailto:edward at jsunit.net">edward at jsunit.net</a><br>
+        </tr>
+</table>
+
+<p><h2>Third-party licenses:</h2>
+    <ul>
+        <li>JDOM: Portions of this software are copyright Copyright (C) 2000-2003 Jason Hunter & Brett McLaughlin. All
+            rights reserved. See <a href="JDOM_license.txt">JDOM_license.txt</a>.
+        <li>Jetty: Portions of this software are copyright � Mort Bay Consulting Pty. Ltd. (Australia) and others. All
+            Rights Reserved. See <a href="Jetty_license.html">Jetty_license.html</a>.
+        <li>Individual files in this package may contain additional copyright notices. The javax.servlet packages are
+            copyright Sun Microsystems Inc. All Rights Reserved.
+    </ul>
+</p>
+
+<p><h2>JsUnit licenses:</h2>
+    JsUnit is licensed under 3 different licenses giving you the freedom
+    to use, modify and distribute JsUnit in a variety of fashions.
+</p>
+
+<ol>
+    <li>
+        <p><a href="MPL-1.1.txt">Mozilla Public License 1.1</a></p>
+
+        <p>See <a href="http://www.mozilla.org/MPL/">mozilla.org</a>
+            for more details.</p>
+    </li>
+
+    <li>
+        <p><a href="gpl-2.txt">GNU Public License 2</a></p>
+
+        <p>See <a href="http://www.gnu.org/licenses/licenses.html">www.gnu.org</a>
+            for more details.</p>
+    </li>
+
+    <li>
+        <p><a href="lgpl-2.1.txt">GNU Lesser Public License 2.1</a></p>
+
+        <p>See <a href="http://www.gnu.org/licenses/licenses.html">www.gnu.org</a>
+            for more details.</p>
+    </li>
+</ol>
+
+<p>
+    Every Java and JavaScript source file in this distribution should be considered to be under the following licensing
+    terms.
+    <pre>
+        ***** BEGIN LICENSE BLOCK *****
+        - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+        -
+        - The contents of this file are subject to the Mozilla Public License Version
+        - 1.1 (the "License"); you may not use this file except in compliance with
+        - the License. You may obtain a copy of the License at
+        - http://www.mozilla.org/MPL/
+        -
+        - Software distributed under the License is distributed on an "AS IS" basis,
+        - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+        - for the specific language governing rights and limitations under the
+        - License.
+        -
+        - The Original Code is Edward Hieatt code.
+        -
+        - The Initial Developer of the Original Code is
+        - Edward Hieatt, edward at jsunit.net.
+        - Portions created by the Initial Developer are Copyright (C) 2003
+        - the Initial Developer. All Rights Reserved.
+        -
+        - Author Edward Hieatt, edward at jsunit.net
+        -
+        - Alternatively, the contents of this file may be used under the terms of
+        - either the GNU General Public License Version 2 or later (the "GPL"), or
+        - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+        - in which case the provisions of the GPL or the LGPL are applicable instead
+        - of those above. If you wish to allow use of your version of this file only
+        - under the terms of either the GPL or the LGPL, and not to allow others to
+        - use your version of this file under the terms of the MPL, indicate your
+        - decision by deleting the provisions above and replace them with the notice
+        - and other provisions required by the LGPL or the GPL. If you do not delete
+        - the provisions above, a recipient may use your version of this file under
+        - the terms of any one of the MPL, the GPL or the LGPL.
+        -
+        - ***** END LICENSE BLOCK *****
+    </pre>
+</p>
+</body>
+</html>
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/lgpl-2.1.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/lgpl-2.1.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/lgpl-2.1.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/mpl-tri-license-c.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/mpl-tri-license-c.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/mpl-tri-license-c.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,35 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is __________________________________________.
+ *
+ * The Initial Developer of the Original Code is
+ * ____________________________________________.
+ * Portions created by the Initial Developer are Copyright (C) 2___
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/mpl-tri-license-html.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/mpl-tri-license-html.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/licenses/mpl-tri-license-html.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,35 @@
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is __________________________________________.
+   -
+   - The Initial Developer of the Original Code is
+   - ____________________________________________.
+   - Portions created by the Initial Developer are Copyright (C) 2___
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either the GNU General Public License Version 2 or later (the "GPL"), or
+   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the LGPL or the GPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/readme.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/readme.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/readme.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,19 @@
+JsUnit
+Copyright (C) 2001-6 Edward Hieatt, edward at jsunit.net
+
+This program 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.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Please see http://www.jsunit.net/ for JsUnit documentation and
+the "licenses" directory for license information.
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/testRunner.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/testRunner.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/testRunner.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>JsUnit Test Runner</title>
+<script language="JavaScript" type="text/javascript" src="app/xbDebug.js"></script>
+<script language="JavaScript" type="text/javascript" src="app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript">
+    var DEFAULT_TEST_FRAME_HEIGHT = 250;
+
+    function jsUnitParseParms(string) {
+        var i;
+        var searchString = unescape(string);
+        var parameterHash = new Object();
+
+        if (!searchString) {
+            return parameterHash;
+        }
+
+        i = searchString.indexOf('?');
+        if (i != -1) {
+            searchString = searchString.substring(i + 1);
+        }
+
+        var parmList = searchString.split('&');
+        var a;
+        for (i = 0; i < parmList.length; i++) {
+            a = parmList[i].split('=');
+            a[0] = a[0].toLowerCase();
+            if (a.length > 1) {
+                parameterHash[a[0]] = a[1];
+            }
+            else {
+                parameterHash[a[0]] = true;
+            }
+        }
+        return parameterHash;
+    }
+
+    function jsUnitConstructTestParms() {
+        var p;
+        var parms = '';
+
+        for (p in jsUnitParmHash) {
+            var value = jsUnitParmHash[p];
+
+            if (!value ||
+                p == 'testpage' ||
+                p == 'autorun' ||
+                p == 'submitresults' ||
+                p == 'showtestframe' ||
+                p == 'resultid') {
+                continue;
+            }
+
+            if (parms) {
+                parms += '&';
+            }
+
+            parms += p;
+
+            if (typeof(value) != 'boolean') {
+                parms += '=' + value;
+            }
+        }
+        return escape(parms);
+    }
+
+    var jsUnitParmHash = jsUnitParseParms(document.location.search);
+
+    // set to true to turn debugging code on, false to turn it off.
+    xbDEBUG.on = jsUnitGetParm('debug') ? true : false;
+</script>
+
+<script language="JavaScript" type="text/javascript" src="app/jsUnitTestManager.js"></script>
+<script language="JavaScript" type="text/javascript" src="app/jsUnitTracer.js"></script>
+<script language="JavaScript" type="text/javascript" src="app/jsUnitTestSuite.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+    var testManager;
+    var utility;
+    var tracer;
+
+
+    if (!Array.prototype.push) {
+        Array.prototype.push = function (anObject) {
+            this[this.length] = anObject;
+        }
+    }
+
+    if (!Array.prototype.pop) {
+        Array.prototype.pop = function () {
+            if (this.length > 0) {
+                delete this[this.length - 1];
+                this.length--;
+            }
+        }
+    }
+
+    function shouldKickOffTestsAutomatically() {
+        return jsUnitGetParm('autorun') == "true";
+    }
+
+    function shouldShowTestFrame() {
+        return jsUnitGetParm('showtestframe');
+    }
+
+    function shouldSubmitResults() {
+        return jsUnitGetParm('submitresults');
+    }
+
+    function getResultId() {
+        if (jsUnitGetParm('resultid'))
+            return jsUnitGetParm('resultid');
+        return "";
+    }
+
+    function submitResults() {
+        window.mainFrame.mainData.document.testRunnerForm.runButton.disabled = true;
+        window.mainFrame.mainResults.populateHeaderFields(getResultId(), navigator.userAgent, JSUNIT_VERSION, testManager.resolveUserEnteredTestFileName());
+        window.mainFrame.mainResults.submitResults();
+    }
+
+    function wasResultUrlSpecified() {
+        return shouldSubmitResults() && jsUnitGetParm('submitresults') != 'true';
+    }
+
+    function getSpecifiedResultUrl() {
+        return jsUnitGetParm('submitresults');
+    }
+
+    function init() {
+        var testRunnerFrameset = document.getElementById('testRunnerFrameset');
+        if (shouldShowTestFrame() && testRunnerFrameset) {
+            var testFrameHeight;
+            if (jsUnitGetParm('showtestframe') == 'true')
+                testFrameHeight = DEFAULT_TEST_FRAME_HEIGHT;
+            else
+                testFrameHeight = jsUnitGetParm('showtestframe');
+            testRunnerFrameset.rows = '*,0,' + testFrameHeight;
+        }
+        testManager = new jsUnitTestManager();
+        tracer = new JsUnitTracer(testManager);
+        if (shouldKickOffTestsAutomatically()) {
+            window.mainFrame.mainData.kickOffTests();
+        }
+    }
+
+
+</script>
+</head>
+
+<frameset id="testRunnerFrameset" rows="*,0,0" border="0" onload="init()">
+
+    <frame frameborder="0" name="mainFrame" src="./app/main-frame.html">
+    <frame frameborder="0" name="documentLoader" src="./app/main-loader.html">
+    <frame frameborder="0" name="testContainer" src="./app/testContainer.html">
+
+    <noframes>
+        <body>
+        <p>Sorry, JsUnit requires support for frames.</p>
+        </body>
+    </noframes>
+</frameset>
+
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/version.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/version.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/jsunit/version.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1 @@
+JsUnit 22.2 alpha 11
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/readyState.xpi
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/readyState.xpi
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/reference.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/reference.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/reference.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,4504 @@
+<html>
+<head>
+<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>Selenium Reference</title>
+</head>
+<body>
+<h1>Selenium Reference</h1>
+<h2>Concepts</h2>
+<p>A <strong>command</strong> is what tells Selenium what to do. Selenium commands come in three 'flavors': <strong>Actions</strong>, <strong>Accessors</strong> and <strong>Assertions</strong>.
+	Each command call is one line in the test table of the form:</p>
+<blockquote>
+<table border="1" class="table">
+<colgroup>
+<col width="39%">
+<col width="33%">
+<col width="28%">
+</colgroup>
+<tbody valign="top">
+<tr>
+<td>command</td><td>target</td><td>value</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<p>
+<strong>Actions</strong> are commands that generally manipulate the state of the application. They do things like "click this link" and "select that option". If an Action fails, or has an error, the execution of the current test is stopped.</p>
+<p>Many Actions can be called with the "AndWait" suffix, e.g. "clickAndWait".
+	This suffix tells Selenium that the action will cause the browser to make a call to the server,
+	and that Selenium should wait for a new page to load.</p>
+<p>
+<strong>Accessors</strong> examine the state of the application and store the results in variables, e.g. "storeTitle".  They are also used to automatically generate Assertions.</p>
+<p>
+<strong>Assertions</strong> are like Accessors, but they verify that the state of the application conforms to what is expected. Examples include "make sure the page title is X" and "verify that this checkbox is checked".</p>
+<p>All Selenium Assertions can be used in 3 modes: "assert", "verify", and "waitFor". For example, you can "assertText", "verifyText" and "waitForText".  When an "assert" fails, the test is aborted. When a "verify" fails, the test will continue execution, logging the failure.  This allows a single "assert" to ensure that the application is on the correct page, followed by a bunch of "verify" assertions to test form field values, labels, etc.</p>
+<p>"waitFor" commands wait for some condition to become true (which can be useful for testing Ajax applications).
+	They will succeed immediately if the condition is already true.
+	However, they will fail and halt the test if the condition does not become true within the current timeout setting
+	(see the <strong>setTimeout</strong> action below).
+	</p>
+<p>
+<strong>Element Locators</strong> tell Selenium which HTML element a command refers to. Many commands require an Element Locator as the "target" attribute. Examples of Element Locators include "elementId" and "document.forms[0].element". These are described more clearly in the next section.</p>
+<p>
+<strong>Patterns</strong> are used for various reasons, e.g. to specify the expected value of an input field, or identify a select option.  Selenium supports various types of pattern, including regular-expressions, all of which are described in more detail below.</p>Defines an object that runs Selenium commands.
+
+<h3>
+<a name="locators"></a>Element Locators</h3>
+<p>
+Element Locators tell Selenium which HTML element a command refers to.
+The format of a locator is:</p>
+<blockquote>
+<em>locatorType</em><strong>=</strong><em>argument</em>
+</blockquote>
+<p>
+We support the following strategies for locating elements:
+</p>
+<ul>
+<li>
+<strong>identifier</strong>=<em>id</em>: 
+Select the element with the specified @id attribute. If no match is
+found, select the first element whose @name attribute is <em>id</em>.
+(This is normally the default; see below.)</li>
+<li>
+<strong>id</strong>=<em>id</em>:
+Select the element with the specified @id attribute.</li>
+<li>
+<strong>name</strong>=<em>name</em>:
+Select the first element with the specified @name attribute.
+<ul class="first last simple">
+<li>username</li>
+<li>name=username</li>
+</ul>
+<p>The name may optionally be followed by one or more <em>element-filters</em>, separated from the name by whitespace.  If the <em>filterType</em> is not specified, <strong>value</strong> is assumed.</p>
+<ul class="first last simple">
+<li>name=flavour value=chocolate</li>
+</ul>
+</li>
+<li>
+<strong>dom</strong>=<em>javascriptExpression</em>: 
+
+Find an element by evaluating the specified string.  This allows you to traverse the HTML Document Object
+Model using JavaScript.  Note that you must not return a value in this string; simply make it the last expression in the block.
+<ul class="first last simple">
+<li>dom=document.forms['myForm'].myDropdown</li>
+<li>dom=document.images[56]</li>
+<li>dom=function foo() { return document.links[1]; }; foo();</li>
+</ul>
+</li>
+<li>
+<strong>xpath</strong>=<em>xpathExpression</em>: 
+Locate an element using an XPath expression.
+<ul class="first last simple">
+<li>xpath=//img[@alt='The image alt text']</li>
+<li>xpath=//table[@id='table1']//tr[4]/td[2]</li>
+<li>xpath=//a[contains(@href,'#id1')]</li>
+<li>xpath=//a[contains(@href,'#id1')]/@class</li>
+<li>xpath=(//table[@class='stylee'])//th[text()='theHeaderText']/../td</li>
+<li>xpath=//input[@name='name2' and @value='yes']</li>
+<li>xpath=//*[text()="right"]</li>
+</ul>
+</li>
+<li>
+<strong>link</strong>=<em>textPattern</em>:
+Select the link (anchor) element which contains text matching the
+specified <em>pattern</em>.
+<ul class="first last simple">
+<li>link=The link text</li>
+</ul>
+</li>
+<li>
+<strong>css</strong>=<em>cssSelectorSyntax</em>:
+Select the element using css selectors. Please refer to <a href="http://www.w3.org/TR/REC-CSS2/selector.html">CSS2 selectors</a>, <a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">CSS3 selectors</a> for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package.
+<ul class="first last simple">
+<li>css=a[href="#id3"]</li>
+<li>css=span#firstChild + span</li>
+</ul>
+<p>Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after). </p>
+</li>
+</ul>
+<p>
+Without an explicit locator prefix, Selenium uses the following default
+strategies:
+</p>
+<ul class="simple">
+<li>
+<strong>dom</strong>, for locators starting with "document."</li>
+<li>
+<strong>xpath</strong>, for locators starting with "//"</li>
+<li>
+<strong>identifier</strong>, otherwise</li>
+</ul>
+<h3>
+<a name="element-filters">Element Filters</a>
+</h3>
+<blockquote>
+<p>Element filters can be used with a locator to refine a list of candidate elements.  They are currently used only in the 'name' element-locator.</p>
+<p>Filters look much like locators, ie.</p>
+<blockquote>
+<em>filterType</em><strong>=</strong><em>argument</em>
+</blockquote>
+<p>Supported element-filters are:</p>
+<p>
+<strong>value=</strong><em>valuePattern</em>
+</p>
+<blockquote>
+Matches elements based on their values.  This is particularly useful for refining a list of similarly-named toggle-buttons.</blockquote>
+<p>
+<strong>index=</strong><em>index</em>
+</p>
+<blockquote>
+Selects a single element based on its position in the list (offset from zero).</blockquote>
+</blockquote>
+<h3>
+<a name="patterns"></a>String-match Patterns</h3>
+<p>
+Various Pattern syntaxes are available for matching string values:
+</p>
+<ul>
+<li>
+<strong>glob:</strong><em>pattern</em>:
+Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a
+kind of limited regular-expression syntax typically used in command-line
+shells. In a glob pattern, "*" represents any sequence of characters, and "?"
+represents any single character. Glob patterns match against the entire
+string.</li>
+<li>
+<strong>regexp:</strong><em>regexp</em>:
+Match a string using a regular-expression. The full power of JavaScript
+regular-expressions is available.</li>
+<li>
+<strong>exact:</strong><em>string</em>:
+
+Match a string exactly, verbatim, without any of that fancy wildcard
+stuff.</li>
+</ul>
+<p>
+If no pattern prefix is specified, Selenium assumes that it's a "glob"
+pattern.
+</p>
+<h2>Selenium Actions</h2>
+<dl>
+<dt>
+<strong><a name="addLocationStrategy"></a>addLocationStrategy
+		(
+			strategyName,functionDefinition
+		)
+	</strong>
+</dt>
+<dd>Defines a new function for Selenium to locate elements on the page.
+For example,
+if you define the strategy "foo", and someone runs click("foo=blah"), we'll
+run your function, passing you the string "blah", and click on the element 
+that your function
+returns, or throw an "Element not found" error if your function returns null.
+
+We'll pass three arguments to your function:
+<ul>
+<li>locator: the string the user passed in</li>
+<li>inWindow: the currently selected window</li>
+<li>inDocument: the currently selected document</li>
+</ul>
+The function must return null if the element can't be found.<p>Arguments:</p>
+<ul>
+<li>strategyName - the name of the strategy to define; this should use only   letters [a-zA-Z] with no spaces or other punctuation.</li>
+<li>functionDefinition - a string defining the body of a function in JavaScript.   For example: <code>return inDocument.getElementById(locator);</code>
+</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="addSelection"></a>addSelection
+		(
+			locator,optionLocator
+		)
+	</strong>
+</dt>
+<dd>Add a selection to the set of selected options in a multi-select element using an option locator.
+
+ at see #doSelect for details of option locators<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> identifying a multi-select box</li>
+<li>optionLocator - an option locator (a label by default)</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="allowNativeXpath"></a>allowNativeXpath
+		(
+			allow
+		)
+	</strong>
+</dt>
+<dd>Specifies whether Selenium should use the native in-browser implementation
+of XPath (if any native version is available); if you pass "false" to
+this function, we will always use our pure-JavaScript xpath library.
+Using the pure-JS xpath library can improve the consistency of xpath
+element locators between different browser vendors, but the pure-JS
+version is much slower than the native implementations.<p>Arguments:</p>
+<ul>
+<li>allow - boolean, true means we'll prefer to use native XPath; false means we'll only use JS XPath</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="altKeyDown"></a>altKeyDown
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Press the alt key and hold it down until doAltUp() is called or a new page is loaded.</dd>
+<br>
+<dt>
+<strong><a name="altKeyUp"></a>altKeyUp
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Release the alt key.</dd>
+<br>
+<dt>
+<strong><a name="answerOnNextPrompt"></a>answerOnNextPrompt
+		(
+			answer
+		)
+	</strong>
+</dt>
+<dd>Instructs Selenium to return the specified answer string in response to
+the next JavaScript prompt [window.prompt()].<p>Arguments:</p>
+<ul>
+<li>answer - the answer to give in response to the prompt pop-up</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="assignId"></a>assignId
+		(
+			locator,identifier
+		)
+	</strong>
+</dt>
+<dd>Temporarily sets the "id" attribute of the specified element, so you can locate it in the future
+using its ID rather than a slow/complicated XPath.  This ID will disappear once the page is
+reloaded.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> pointing to an element</li>
+<li>identifier - a string to be used as the ID of the specified element</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="break"></a>break
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Halt the currently running test, and wait for the user to press the Continue button.
+This command is useful for debugging, but be careful when using it, because it will
+force automated tests to hang until a user intervenes manually.</dd>
+<br>
+<dt>
+<strong><a name="check"></a>check
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Check a toggle-button (checkbox/radio)<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="chooseCancelOnNextConfirmation"></a>chooseCancelOnNextConfirmation
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>By default, Selenium's overridden window.confirm() function will
+return true, as if the user had manually clicked OK; after running
+this command, the next call to confirm() will return false, as if
+the user had clicked Cancel.  Selenium will then resume using the
+default behavior for future confirmations, automatically returning 
+true (OK) unless/until you explicitly call this command for each
+confirmation.</dd>
+<br>
+<dt>
+<strong><a name="chooseOkOnNextConfirmation"></a>chooseOkOnNextConfirmation
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Undo the effect of calling chooseCancelOnNextConfirmation.  Note
+that Selenium's overridden window.confirm() function will normally automatically
+return true, as if the user had manually clicked OK, so you shouldn't
+need to use this command unless for some reason you need to change
+your mind prior to the next confirmation.  After any confirmation, Selenium will resume using the
+default behavior for future confirmations, automatically returning 
+true (OK) unless/until you explicitly call chooseCancelOnNextConfirmation for each
+confirmation.</dd>
+<br>
+<dt>
+<strong><a name="click"></a>click
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Clicks on a link, button, checkbox or radio button. If the click action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.<p>Arguments:</p>
+<ul>
+<li>locator - an element locator</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="clickAt"></a>clickAt
+		(
+			locator,coordString
+		)
+	</strong>
+</dt>
+<dd>Clicks on a link, button, checkbox or radio button. If the click action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.<p>Arguments:</p>
+<ul>
+<li>locator - an element locator</li>
+<li>coordString - specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="close"></a>close
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Simulates the user clicking the "close" button in the titlebar of a popup
+window or tab.</dd>
+<br>
+<dt>
+<strong><a name="controlKeyDown"></a>controlKeyDown
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Press the control key and hold it down until doControlUp() is called or a new page is loaded.</dd>
+<br>
+<dt>
+<strong><a name="controlKeyUp"></a>controlKeyUp
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Release the control key.</dd>
+<br>
+<dt>
+<strong><a name="createCookie"></a>createCookie
+		(
+			nameValuePair,optionsString
+		)
+	</strong>
+</dt>
+<dd>Create a new cookie whose path and domain are same with those of current page
+under test, unless you specified a path for this cookie explicitly.<p>Arguments:</p>
+<ul>
+<li>nameValuePair - name and value of the cookie in a format "name=value"</li>
+<li>optionsString - options for the cookie. Currently supported options include 'path' and 'max_age'.      the optionsString's format is "path=/path/, max_age=60". The order of options are irrelevant, the unit      of the value of 'max_age' is second.</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="deleteCookie"></a>deleteCookie
+		(
+			name,path
+		)
+	</strong>
+</dt>
+<dd>Delete a named cookie with specified path.<p>Arguments:</p>
+<ul>
+<li>name - the name of the cookie to be deleted</li>
+<li>path - the path property of the cookie to be deleted</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="doubleClick"></a>doubleClick
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Double clicks on a link, button, checkbox or radio button. If the double click action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.<p>Arguments:</p>
+<ul>
+<li>locator - an element locator</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="doubleClickAt"></a>doubleClickAt
+		(
+			locator,coordString
+		)
+	</strong>
+</dt>
+<dd>Doubleclicks on a link, button, checkbox or radio button. If the action
+causes a new page to load (like a link usually does), call
+waitForPageToLoad.<p>Arguments:</p>
+<ul>
+<li>locator - an element locator</li>
+<li>coordString - specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="dragAndDrop"></a>dragAndDrop
+		(
+			locator,movementsString
+		)
+	</strong>
+</dt>
+<dd>Drags an element a certain distance and then drops it<p>Arguments:</p>
+<ul>
+<li>locator - an element locator</li>
+<li>movementsString - offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="dragAndDropToObject"></a>dragAndDropToObject
+		(
+			locatorOfObjectToBeDragged,locatorOfDragDestinationObject
+		)
+	</strong>
+</dt>
+<dd>Drags an element and drops it on another element<p>Arguments:</p>
+<ul>
+<li>locatorOfObjectToBeDragged - an element to be dragged</li>
+<li>locatorOfDragDestinationObject - an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged  is dropped</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="dragdrop"></a>dragdrop
+		(
+			locator,movementsString
+		)
+	</strong>
+</dt>
+<dd>deprecated - use dragAndDrop instead<p>Arguments:</p>
+<ul>
+<li>locator - an element locator</li>
+<li>movementsString - offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="echo"></a>echo
+		(
+			message
+		)
+	</strong>
+</dt>
+<dd>Prints the specified message into the third table cell in your Selenese tables.
+Useful for debugging.<p>Arguments:</p>
+<ul>
+<li>message - the message to print</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="fireEvent"></a>fireEvent
+		(
+			locator,eventName
+		)
+	</strong>
+</dt>
+<dd>Explicitly simulate an event, to trigger the corresponding "on<em>event</em>"
+handler.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>eventName - the event name, e.g. "focus" or "blur"</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="getSpeed"></a>getSpeed
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Get execution speed (i.e., get the millisecond length of the delay following each selenium operation).  By default, there is no such delay, i.e.,
+the delay is 0 milliseconds.
+
+See also setSpeed.</dd>
+<br>
+<dt>
+<strong><a name="goBack"></a>goBack
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Simulates the user clicking the "back" button on their browser.</dd>
+<br>
+<dt>
+<strong><a name="highlight"></a>highlight
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Briefly changes the backgroundColor of the specified element yellow.  Useful for debugging.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="keyDown"></a>keyDown
+		(
+			locator,keySequence
+		)
+	</strong>
+</dt>
+<dd>Simulates a user pressing a key (without releasing it yet).<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>keySequence - Either be a string("\" followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: "w", "\119".</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="keyPress"></a>keyPress
+		(
+			locator,keySequence
+		)
+	</strong>
+</dt>
+<dd>Simulates a user pressing and releasing a key.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>keySequence - Either be a string("\" followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: "w", "\119".</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="keyUp"></a>keyUp
+		(
+			locator,keySequence
+		)
+	</strong>
+</dt>
+<dd>Simulates a user releasing a key.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>keySequence - Either be a string("\" followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: "w", "\119".</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="metaKeyDown"></a>metaKeyDown
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Press the meta key and hold it down until doMetaUp() is called or a new page is loaded.</dd>
+<br>
+<dt>
+<strong><a name="metaKeyUp"></a>metaKeyUp
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Release the meta key.</dd>
+<br>
+<dt>
+<strong><a name="mouseDown"></a>mouseDown
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Simulates a user pressing the mouse button (without releasing it yet) on
+the specified element.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="mouseDownAt"></a>mouseDownAt
+		(
+			locator,coordString
+		)
+	</strong>
+</dt>
+<dd>Simulates a user pressing the mouse button (without releasing it yet) at
+the specified location.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>coordString - specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="mouseMove"></a>mouseMove
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Simulates a user pressing the mouse button (without releasing it yet) on
+the specified element.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="mouseMoveAt"></a>mouseMoveAt
+		(
+			locator,coordString
+		)
+	</strong>
+</dt>
+<dd>Simulates a user pressing the mouse button (without releasing it yet) on
+the specified element.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>coordString - specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="mouseOut"></a>mouseOut
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Simulates a user moving the mouse pointer away from the specified element.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="mouseOver"></a>mouseOver
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Simulates a user hovering a mouse over the specified element.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="mouseUp"></a>mouseUp
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Simulates the event that occurs when the user releases the mouse button (i.e., stops
+holding the button down) on the specified element.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="mouseUpAt"></a>mouseUpAt
+		(
+			locator,coordString
+		)
+	</strong>
+</dt>
+<dd>Simulates the event that occurs when the user releases the mouse button (i.e., stops
+holding the button down) at the specified location.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>coordString - specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="open"></a>open
+		(
+			url
+		)
+	</strong>
+</dt>
+<dd>Opens an URL in the test frame. This accepts both relative and absolute
+URLs.
+
+The "open" command waits for the page to load before proceeding,
+ie. the "AndWait" suffix is implicit.
+
+<em>Note</em>: The URL must be on the same domain as the runner HTML
+due to security restrictions in the browser (Same Origin Policy). If you
+need to open an URL on another domain, use the Selenium Server to start a
+new browser session on that domain.<p>Arguments:</p>
+<ul>
+<li>url - the URL to open; may be relative or absolute</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="openWindow"></a>openWindow
+		(
+			url,windowID
+		)
+	</strong>
+</dt>
+<dd>Opens a popup window (if a window with that ID isn't already open).
+After opening the window, you'll need to select it using the selectWindow
+command.
+
+<p>This command can also be a useful workaround for bug SEL-339.  In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p>
+<p>Arguments:</p>
+<ul>
+<li>url - the URL to open, which can be blank</li>
+<li>windowID - the JavaScript window ID of the window to select</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="pause"></a>pause
+		(
+			waitTime
+		)
+	</strong>
+</dt>
+<dd>Wait for the specified amount of time (in milliseconds)<p>Arguments:</p>
+<ul>
+<li>waitTime - the amount of time to sleep (in milliseconds)</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="refresh"></a>refresh
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Simulates the user clicking the "Refresh" button on their browser.</dd>
+<br>
+<dt>
+<strong><a name="removeAllSelections"></a>removeAllSelections
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Unselects all of the selected options in a multi-select element.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> identifying a multi-select box</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="removeSelection"></a>removeSelection
+		(
+			locator,optionLocator
+		)
+	</strong>
+</dt>
+<dd>Remove a selection from the set of selected options in a multi-select element using an option locator.
+
+ at see #doSelect for details of option locators<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> identifying a multi-select box</li>
+<li>optionLocator - an option locator (a label by default)</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="runScript"></a>runScript
+		(
+			script
+		)
+	</strong>
+</dt>
+<dd>Creates a new "script" tag in the body of the current test window, and 
+adds the specified text into the body of the command.  Scripts run in
+this way can often be debugged more easily than scripts executed using
+Selenium's "getEval" command.  Beware that JS exceptions thrown in these script
+tags aren't managed by Selenium, so you should probably wrap your script
+in try/catch blocks if there is any chance that the script will throw
+an exception.<p>Arguments:</p>
+<ul>
+<li>script - the JavaScript snippet to run</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="select"></a>select
+		(
+			selectLocator,optionLocator
+		)
+	</strong>
+</dt>
+<dd>Select an option from a drop-down using an option locator.
+
+<p>
+Option locators provide different ways of specifying options of an HTML
+Select element (e.g. for selecting a specific option, or for asserting
+that the selected option satisfies a specification). There are several
+forms of Select Option Locator.
+</p>
+<ul>
+<li>
+<strong>label</strong>=<em>labelPattern</em>:
+matches options based on their labels, i.e. the visible text. (This
+is the default.)
+<ul class="first last simple">
+<li>label=regexp:^[Oo]ther</li>
+</ul>
+</li>
+<li>
+<strong>value</strong>=<em>valuePattern</em>:
+matches options based on their values.
+<ul class="first last simple">
+<li>value=other</li>
+</ul>
+</li>
+<li>
+<strong>id</strong>=<em>id</em>:
+
+matches options based on their ids.
+<ul class="first last simple">
+<li>id=option1</li>
+</ul>
+</li>
+<li>
+<strong>index</strong>=<em>index</em>:
+matches an option based on its index (offset from zero).
+<ul class="first last simple">
+<li>index=2</li>
+</ul>
+</li>
+</ul>
+<p>
+If no option locator prefix is provided, the default behaviour is to match on <strong>label</strong>.
+</p>
+<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>optionLocator - an option locator (a label by default)</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="selectFrame"></a>selectFrame
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Selects a frame within the current window.  (You may invoke this command
+multiple times to select nested frames.)  To select the parent frame, use
+"relative=parent" as a locator; to select the top frame, use "relative=top".
+You can also select a frame by its 0-based index number; select the first frame with
+"index=0", or the third frame with "index=2".
+
+<p>You may also use a DOM expression to identify the frame you want directly,
+like this: <code>dom=frames["main"].frames["subframe"]</code>
+</p>
+<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> identifying a frame or iframe</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="selectWindow"></a>selectWindow
+		(
+			windowID
+		)
+	</strong>
+</dt>
+<dd>Selects a popup window; once a popup window has been selected, all
+commands go to that window. To select the main window again, use null
+as the target.
+
+<p>Note that there is a big difference between a window's internal JavaScript "name" property
+and the "title" of a given window's document (which is normally what you actually see, as an end user,
+in the title bar of the window).  The "name" is normally invisible to the end-user; it's the second 
+parameter "windowName" passed to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag)
+(which selenium intercepts).</p>
+<p>Selenium has several strategies for finding the window object referred to by the "windowID" parameter.</p>
+<p>1.) if windowID is null, (or the string "null") then it is assumed the user is referring to the original window instantiated by the browser).</p>
+<p>2.) if the value of the "windowID" parameter is a JavaScript variable name in the current application window, then it is assumed
+that this variable contains the return value from a call to the JavaScript window.open() method.</p>
+<p>3.) Otherwise, selenium looks in a hash it maintains that maps string names to window "names".</p>
+<p>4.) If <i>that</i> fails, we'll try looping over all of the known windows to try to find the appropriate "title".
+Since "title" is not necessarily unique, this may have unexpected behavior.</p>
+<p>If you're having trouble figuring out what is the name of a window that you want to manipulate, look at the selenium log messages
+which identify the names of windows created via window.open (and therefore intercepted by selenium).  You will see messages
+like the following for each window as it is opened:</p>
+<p>
+<code>debug: window.open call intercepted; window ID (which you can use with selectWindow()) is "myNewWindow"</code>
+</p>
+<p>In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+(This is bug SEL-339.)  In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p>
+<p>Arguments:</p>
+<ul>
+<li>windowID - the JavaScript window ID of the window to select</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="setBrowserLogLevel"></a>setBrowserLogLevel
+		(
+			logLevel
+		)
+	</strong>
+</dt>
+<dd>Sets the threshold for browser-side logging messages; log messages beneath this threshold will be discarded.
+Valid logLevel strings are: "debug", "info", "warn", "error" or "off".
+To see the browser logs, you need to
+either show the log window in GUI mode, or enable browser-side logging in Selenium RC.<p>Arguments:</p>
+<ul>
+<li>logLevel - one of the following: "debug", "info", "warn", "error" or "off"</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="setCursorPosition"></a>setCursorPosition
+		(
+			locator,position
+		)
+	</strong>
+</dt>
+<dd>Moves the text cursor to the specified position in the given input element or textarea.
+This method will fail if the specified element isn't an input element or textarea.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> pointing to an input element or textarea</li>
+<li>position - the numerical position of the cursor in the field; position should be 0 to move the position to the beginning of the field.  You can also set the cursor to -1 to move it to the end of the field.</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="setMouseSpeed"></a>setMouseSpeed
+		(
+			pixels
+		)
+	</strong>
+</dt>
+<dd>Configure the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+<p>Setting this value to 0 means that we'll send a "mousemove" event to every single pixel
+in between the start location and the end location; that can be very slow, and may
+cause some browsers to force the JavaScript to timeout.</p>
+<p>If the mouse speed is greater than the distance between the two dragged objects, we'll
+just send one "mousemove" at the start location and then one final one at the end location.</p>
+<p>Arguments:</p>
+<ul>
+<li>pixels - the number of pixels between "mousemove" events</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="setSpeed"></a>setSpeed
+		(
+			value
+		)
+	</strong>
+</dt>
+<dd>Set execution speed (i.e., set the millisecond length of a delay which will follow each selenium operation).  By default, there is no such delay, i.e.,
+the delay is 0 milliseconds.<p>Arguments:</p>
+<ul>
+<li>value - the number of milliseconds to pause after operation</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="setTimeout"></a>setTimeout
+		(
+			timeout
+		)
+	</strong>
+</dt>
+<dd>Specifies the amount of time that Selenium will wait for actions to complete.
+
+<p>Actions that require waiting include "open" and the "waitFor*" actions.</p>
+The default timeout is 30 seconds.<p>Arguments:</p>
+<ul>
+<li>timeout - a timeout in milliseconds, after which the action will return with an error</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="shiftKeyDown"></a>shiftKeyDown
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Press the shift key and hold it down until doShiftUp() is called or a new page is loaded.</dd>
+<br>
+<dt>
+<strong><a name="shiftKeyUp"></a>shiftKeyUp
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Release the shift key.</dd>
+<br>
+<dt>
+<strong><a name="store"></a>store
+		(
+			expression,variableName
+		)
+	</strong>
+</dt>
+<dd>This command is a synonym for storeExpression.<p>Arguments:</p>
+<ul>
+<li>expression - the value to store</li>
+<li>variableName - the name of a <a href="#storedVars">variable</a> in which the result is to be stored.</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="submit"></a>submit
+		(
+			formLocator
+		)
+	</strong>
+</dt>
+<dd>Submit the specified form. This is particularly useful for forms without
+submit buttons, e.g. single-input "Search" forms.<p>Arguments:</p>
+<ul>
+<li>formLocator - an <a href="#locators">element locator</a> for the form you want to submit</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="type"></a>type
+		(
+			locator,value
+		)
+	</strong>
+</dt>
+<dd>Sets the value of an input field, as though you typed it in.
+
+<p>Can also be used to set the value of combo boxes, check boxes, etc. In these cases,
+value should be the value of the option selected, not the visible text.</p>
+<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>value - the value to type</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="typeKeys"></a>typeKeys
+		(
+			locator,value
+		)
+	</strong>
+</dt>
+<dd>Simulates keystroke events on the specified element, as though you typed the value key-by-key.
+
+<p>This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string;
+this is useful for dynamic UI widgets (like auto-completing combo boxes) that require explicit key events.</p>
+<p>Unlike the simple "type" command, which forces the specified value into the page directly, this command
+may or may not have any visible effect, even in cases where typing keys would normally have a visible effect.
+For example, if you use "typeKeys" on a form element, you may or may not see the results of what you typed in
+the field.</p>
+<p>In some cases, you may need to use the simple "type" command to set the value of the field and then the "typeKeys" command to
+send the keystroke events corresponding to what you just typed.</p>
+<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>value - the value to type</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="uncheck"></a>uncheck
+		(
+			locator
+		)
+	</strong>
+</dt>
+<dd>Uncheck a toggle-button (checkbox/radio)<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="waitForCondition"></a>waitForCondition
+		(
+			script,timeout
+		)
+	</strong>
+</dt>
+<dd>Runs the specified JavaScript snippet repeatedly until it evaluates to "true".
+The snippet may have multiple lines, but only the result of the last line
+will be considered.
+
+<p>Note that, by default, the snippet will be run in the runner's test window, not in the window
+of your application.  To get the window of your application, you can use
+the JavaScript snippet <code>selenium.browserbot.getCurrentWindow()</code>, and then
+run your JavaScript in there</p>
+<p>Arguments:</p>
+<ul>
+<li>script - the JavaScript snippet to run</li>
+<li>timeout - a timeout in milliseconds, after which this command will return with an error</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="waitForFrameToLoad"></a>waitForFrameToLoad
+		(
+			frameAddress,timeout
+		)
+	</strong>
+</dt>
+<dd>Waits for a new frame to load.
+
+<p>Selenium constantly keeps track of new pages and frames loading, 
+and sets a "newPageLoaded" flag when it first notices a page load.</p>
+
+See waitForPageToLoad for more information.<p>Arguments:</p>
+<ul>
+<li>frameAddress - FrameAddress from the server side</li>
+<li>timeout - a timeout in milliseconds, after which this command will return with an error</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="waitForPageToLoad"></a>waitForPageToLoad
+		(
+			timeout
+		)
+	</strong>
+</dt>
+<dd>Waits for a new page to load.
+
+<p>You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc.
+(which are only available in the JS API).</p>
+<p>Selenium constantly keeps track of new pages loading, and sets a "newPageLoaded"
+flag when it first notices a page load.  Running any other Selenium command after
+turns the flag to false.  Hence, if you want to wait for a page to load, you must
+wait immediately after a Selenium command that caused a page-load.</p>
+<p>Arguments:</p>
+<ul>
+<li>timeout - a timeout in milliseconds, after which this command will return with an error</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="waitForPopUp"></a>waitForPopUp
+		(
+			windowID,timeout
+		)
+	</strong>
+</dt>
+<dd>Waits for a popup window to appear and load up.<p>Arguments:</p>
+<ul>
+<li>windowID - the JavaScript window ID of the window that will appear</li>
+<li>timeout - a timeout in milliseconds, after which the action will return with an error</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="windowFocus"></a>windowFocus
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Gives focus to the currently selected window</dd>
+<br>
+<dt>
+<strong><a name="windowMaximize"></a>windowMaximize
+		(
+			
+		)
+	</strong>
+</dt>
+<dd>Resize currently selected window to take up the entire screen</dd>
+<br>
+</dl>
+<h2>Selenium Accessors</h2>
+<dl>
+<dt>
+<strong><a name="assertErrorOnNext"></a>assertErrorOnNext
+		(
+			message
+		)
+	</strong>
+</dt>
+<dd>Tell Selenium to expect an error on the next command execution.<p>Arguments:</p>
+<ul>
+<li>message - The error message we should expect.  This command will fail if the wrong error message appears.</li>
+</ul>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertNotErrorOnNext
+				(
+					message
+				)
+			</li>
+<li>verifyErrorOnNext
+				(
+					message
+				)
+			</li>
+<li>verifyNotErrorOnNext
+				(
+					message
+				)
+			</li>
+<li>waitForErrorOnNext
+				(
+					message
+				)
+			</li>
+<li>waitForNotErrorOnNext
+				(
+					message
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="assertFailureOnNext"></a>assertFailureOnNext
+		(
+			message
+		)
+	</strong>
+</dt>
+<dd>Tell Selenium to expect a failure on the next command execution.<p>Arguments:</p>
+<ul>
+<li>message - The failure message we should expect.  This command will fail if the wrong failure message appears.</li>
+</ul>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertNotFailureOnNext
+				(
+					message
+				)
+			</li>
+<li>verifyFailureOnNext
+				(
+					message
+				)
+			</li>
+<li>verifyNotFailureOnNext
+				(
+					message
+				)
+			</li>
+<li>waitForFailureOnNext
+				(
+					message
+				)
+			</li>
+<li>waitForNotFailureOnNext
+				(
+					message
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="assertSelected"></a>assertSelected
+		(
+			selectLocator,optionLocator
+		)
+	</strong>
+</dt>
+<dd>Verifies that the selected option of a drop-down satisfies the optionSpecifier.  <i>Note that this command is deprecated; you should use assertSelectedLabel, assertSelectedValue, assertSelectedIndex, or assertSelectedId instead.</i>
+<p>See the select command for more information about option locators.</p>
+<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>optionLocator - an option locator, typically just an option label (e.g. "John Smith")</li>
+</ul>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertNotSelected
+				(
+					selectLocator, optionLocator
+				)
+			</li>
+<li>verifySelected
+				(
+					selectLocator, optionLocator
+				)
+			</li>
+<li>verifyNotSelected
+				(
+					selectLocator, optionLocator
+				)
+			</li>
+<li>waitForSelected
+				(
+					selectLocator, optionLocator
+				)
+			</li>
+<li>waitForNotSelected
+				(
+					selectLocator, optionLocator
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeAlert"></a>storeAlert
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts.
+
+<p>Getting an alert has the same effect as manually clicking OK. If an
+alert is generated but you do not get/verify it, the next Selenium action
+will fail.</p>
+<p>NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert
+dialog.</p>
+<p>NOTE: Selenium does NOT support JavaScript alerts that are generated in a
+page's onload() event handler. In this case a visible dialog WILL be
+generated and Selenium will hang until someone manually clicks OK.</p>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>The message of the most recent JavaScript alert</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertAlert
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotAlert
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyAlert
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotAlert
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForAlert
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotAlert
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeAllButtons"></a>storeAllButtons
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns the IDs of all buttons on the page.
+
+<p>If a given button has no ID, it will appear as "" in this array.</p>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the IDs of all buttons on the page</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertAllButtons
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotAllButtons
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyAllButtons
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotAllButtons
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForAllButtons
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotAllButtons
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeAllFields"></a>storeAllFields
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns the IDs of all input fields on the page.
+
+<p>If a given field has no ID, it will appear as "" in this array.</p>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the IDs of all field on the page</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertAllFields
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotAllFields
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyAllFields
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotAllFields
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForAllFields
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotAllFields
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeAllLinks"></a>storeAllLinks
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns the IDs of all links on the page.
+
+<p>If a given link has no ID, it will appear as "" in this array.</p>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the IDs of all links on the page</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertAllLinks
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotAllLinks
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyAllLinks
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotAllLinks
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForAllLinks
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotAllLinks
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeAllWindowIds"></a>storeAllWindowIds
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns the IDs of all windows that the browser knows about.<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the IDs of all windows that the browser knows about.</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertAllWindowIds
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotAllWindowIds
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyAllWindowIds
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotAllWindowIds
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForAllWindowIds
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotAllWindowIds
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeAllWindowNames"></a>storeAllWindowNames
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns the names of all windows that the browser knows about.<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the names of all windows that the browser knows about.</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertAllWindowNames
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotAllWindowNames
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyAllWindowNames
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotAllWindowNames
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForAllWindowNames
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotAllWindowNames
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeAllWindowTitles"></a>storeAllWindowTitles
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns the titles of all windows that the browser knows about.<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the titles of all windows that the browser knows about.</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertAllWindowTitles
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotAllWindowTitles
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyAllWindowTitles
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotAllWindowTitles
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForAllWindowTitles
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotAllWindowTitles
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeAttribute"></a>storeAttribute
+		
+		(
+			attributeLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets the value of an element attribute.<p>Arguments:</p>
+<ul>
+<li>attributeLocator - an element locator followed by an @ sign and then the name of the attribute, e.g. "foo at bar"</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the value of the specified attribute</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertAttribute
+				(
+					attributeLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotAttribute
+				(
+					attributeLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyAttribute
+				(
+					attributeLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotAttribute
+				(
+					attributeLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForAttribute
+				(
+					attributeLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotAttribute
+				(
+					attributeLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeAttributeFromAllWindows"></a>storeAttributeFromAllWindows
+		
+		(
+			attributeName,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns every instance of some attribute from all known windows.<p>Arguments:</p>
+<ul>
+<li>attributeName - name of an attribute on the windows</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the set of values of this attribute from all known windows.</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertAttributeFromAllWindows
+				(
+					attributeName, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotAttributeFromAllWindows
+				(
+					attributeName, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyAttributeFromAllWindows
+				(
+					attributeName, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotAttributeFromAllWindows
+				(
+					attributeName, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForAttributeFromAllWindows
+				(
+					attributeName, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotAttributeFromAllWindows
+				(
+					attributeName, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeBodyText"></a>storeBodyText
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets the entire text of the page.<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the entire text of the page</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertBodyText
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotBodyText
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyBodyText
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotBodyText
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForBodyText
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotBodyText
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeConfirmation"></a>storeConfirmation
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Retrieves the message of a JavaScript confirmation dialog generated during
+the previous action.
+
+<p>
+By default, the confirm function will return true, having the same effect
+as manually clicking OK. This can be changed by prior execution of the
+chooseCancelOnNextConfirmation command. If an confirmation is generated
+but you do not get/verify it, the next Selenium action will fail.
+</p>
+<p>
+NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible
+dialog.
+</p>
+<p>
+NOTE: Selenium does NOT support JavaScript confirmations that are
+generated in a page's onload() event handler. In this case a visible
+dialog WILL be generated and Selenium will hang until you manually click
+OK.
+</p>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the message of the most recent JavaScript confirmation dialog</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertConfirmation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotConfirmation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyConfirmation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotConfirmation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForConfirmation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotConfirmation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeCookie"></a>storeCookie
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Return all cookies of the current page under test.<p>
+<dl>
+<dt>Returns: </dt>
+<dd>all cookies of the current page under test</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertCookie
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotCookie
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyCookie
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotCookie
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForCookie
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotCookie
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeCursorPosition"></a>storeCursorPosition
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers.
+
+<p>Specifically, if the cursor/selection has been cleared by JavaScript, this command will tend to
+return the position of the last location of the cursor, even though the cursor is now gone from the page.  This is filed as <a href="http://jira.openqa.org/browse/SEL-243">SEL-243</a>.</p>
+This method will fail if the specified element isn't an input element or textarea, or there is no cursor in the element.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> pointing to an input element or textarea</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the numerical position of the cursor in the field</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertCursorPosition
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotCursorPosition
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyCursorPosition
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotCursorPosition
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForCursorPosition
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotCursorPosition
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeElementHeight"></a>storeElementHeight
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Retrieves the height of an element<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> pointing to an element</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>height of an element in pixels</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertElementHeight
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotElementHeight
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyElementHeight
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotElementHeight
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForElementHeight
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotElementHeight
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeElementIndex"></a>storeElementIndex
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Get the relative index of an element to its parent (starting from 0). The comment node and empty text node
+will be ignored.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> pointing to an element</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>of relative index of the element to its parent (starting from 0)</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertElementIndex
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotElementIndex
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyElementIndex
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotElementIndex
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForElementIndex
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotElementIndex
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeElementPositionLeft"></a>storeElementPositionLeft
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Retrieves the horizontal position of an element<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> pointing to an element OR an element itself</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>of pixels from the edge of the frame.</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertElementPositionLeft
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotElementPositionLeft
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyElementPositionLeft
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotElementPositionLeft
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForElementPositionLeft
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotElementPositionLeft
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeElementPositionTop"></a>storeElementPositionTop
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Retrieves the vertical position of an element<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> pointing to an element OR an element itself</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>of pixels from the edge of the frame.</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertElementPositionTop
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotElementPositionTop
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyElementPositionTop
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotElementPositionTop
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForElementPositionTop
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotElementPositionTop
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeElementWidth"></a>storeElementWidth
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Retrieves the width of an element<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> pointing to an element</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>width of an element in pixels</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertElementWidth
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotElementWidth
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyElementWidth
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotElementWidth
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForElementWidth
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotElementWidth
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeEval"></a>storeEval
+		
+		(
+			script,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets the result of evaluating the specified JavaScript snippet.  The snippet may
+have multiple lines, but only the result of the last line will be returned.
+
+<p>Note that, by default, the snippet will run in the context of the "selenium"
+object itself, so <code>this</code> will refer to the Selenium object.  Use <code>window</code> to
+refer to the window of your application, e.g. <code>window.document.getElementById('foo')</code>
+</p>
+<p>If you need to use
+a locator to refer to a single element in your application page, you can
+use <code>this.browserbot.findElement("id=foo")</code> where "id=foo" is your locator.</p>
+<p>Arguments:</p>
+<ul>
+<li>script - the JavaScript snippet to run</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the results of evaluating the snippet</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertEval
+				(
+					script, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotEval
+				(
+					script, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyEval
+				(
+					script, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotEval
+				(
+					script, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForEval
+				(
+					script, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotEval
+				(
+					script, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeExpression"></a>storeExpression
+		
+		(
+			expression,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns the specified expression.
+
+<p>This is useful because of JavaScript preprocessing.
+It is used to generate commands like assertExpression and waitForExpression.</p>
+<p>Arguments:</p>
+<ul>
+<li>expression - the value to return</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the value passed in</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertExpression
+				(
+					expression, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotExpression
+				(
+					expression, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyExpression
+				(
+					expression, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotExpression
+				(
+					expression, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForExpression
+				(
+					expression, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotExpression
+				(
+					expression, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeHtmlSource"></a>storeHtmlSource
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns the entire HTML source between the opening and
+closing "html" tags.<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the entire HTML source</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertHtmlSource
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotHtmlSource
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyHtmlSource
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotHtmlSource
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForHtmlSource
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotHtmlSource
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeLocation"></a>storeLocation
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets the absolute URL of the current page.<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the absolute URL of the current page</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertLocation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotLocation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyLocation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotLocation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForLocation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotLocation
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeMouseSpeed"></a>storeMouseSpeed
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns the number of pixels between "mousemove" events during dragAndDrop commands (default=10).<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the number of pixels between "mousemove" events during dragAndDrop commands (default=10)</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertMouseSpeed
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotMouseSpeed
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyMouseSpeed
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotMouseSpeed
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForMouseSpeed
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotMouseSpeed
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storePrompt"></a>storePrompt
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Retrieves the message of a JavaScript question prompt dialog generated during
+the previous action.
+
+<p>Successful handling of the prompt requires prior execution of the
+answerOnNextPrompt command. If a prompt is generated but you
+do not get/verify it, the next Selenium action will fail.</p>
+<p>NOTE: under Selenium, JavaScript prompts will NOT pop up a visible
+dialog.</p>
+<p>NOTE: Selenium does NOT support JavaScript prompts that are generated in a
+page's onload() event handler. In this case a visible dialog WILL be
+generated and Selenium will hang until someone manually clicks OK.</p>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the message of the most recent JavaScript question prompt</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertPrompt
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotPrompt
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyPrompt
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotPrompt
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForPrompt
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotPrompt
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeSelectedId"></a>storeSelectedId
+		
+		(
+			selectLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets option element ID for selected option in the specified select element.<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the selected option ID in the specified select drop-down</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertSelectedId
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotSelectedId
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifySelectedId
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotSelectedId
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForSelectedId
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotSelectedId
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeSelectedIds"></a>storeSelectedIds
+		
+		(
+			selectLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets all option element IDs for selected options in the specified select or multi-select element.<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>an array of all selected option IDs in the specified select drop-down</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertSelectedIds
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotSelectedIds
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifySelectedIds
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotSelectedIds
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForSelectedIds
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotSelectedIds
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeSelectedIndex"></a>storeSelectedIndex
+		
+		(
+			selectLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets option index (option number, starting at 0) for selected option in the specified select element.<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the selected option index in the specified select drop-down</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertSelectedIndex
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotSelectedIndex
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifySelectedIndex
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotSelectedIndex
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForSelectedIndex
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotSelectedIndex
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeSelectedIndexes"></a>storeSelectedIndexes
+		
+		(
+			selectLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets all option indexes (option number, starting at 0) for selected options in the specified select or multi-select element.<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>an array of all selected option indexes in the specified select drop-down</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertSelectedIndexes
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotSelectedIndexes
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifySelectedIndexes
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotSelectedIndexes
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForSelectedIndexes
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotSelectedIndexes
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeSelectedLabel"></a>storeSelectedLabel
+		
+		(
+			selectLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets option label (visible text) for selected option in the specified select element.<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the selected option label in the specified select drop-down</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertSelectedLabel
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotSelectedLabel
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifySelectedLabel
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotSelectedLabel
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForSelectedLabel
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotSelectedLabel
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeSelectedLabels"></a>storeSelectedLabels
+		
+		(
+			selectLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets all option labels (visible text) for selected options in the specified select or multi-select element.<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>an array of all selected option labels in the specified select drop-down</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertSelectedLabels
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotSelectedLabels
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifySelectedLabels
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotSelectedLabels
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForSelectedLabels
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotSelectedLabels
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeSelectedValue"></a>storeSelectedValue
+		
+		(
+			selectLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets option value (value attribute) for selected option in the specified select element.<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the selected option value in the specified select drop-down</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertSelectedValue
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotSelectedValue
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifySelectedValue
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotSelectedValue
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForSelectedValue
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotSelectedValue
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeSelectedValues"></a>storeSelectedValues
+		
+		(
+			selectLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets all option values (value attributes) for selected options in the specified select or multi-select element.<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>an array of all selected option values in the specified select drop-down</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertSelectedValues
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotSelectedValues
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifySelectedValues
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotSelectedValues
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForSelectedValues
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotSelectedValues
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeSelectOptions"></a>storeSelectOptions
+		
+		(
+			selectLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets all option labels in the specified select drop-down.<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>an array of all option labels in the specified select drop-down</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertSelectOptions
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotSelectOptions
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifySelectOptions
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotSelectOptions
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForSelectOptions
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotSelectOptions
+				(
+					selectLocator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeTable"></a>storeTable
+		
+		(
+			tableCellAddress,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets the text from a cell of a table. The cellAddress syntax
+tableLocator.row.column, where row and column start at 0.<p>Arguments:</p>
+<ul>
+<li>tableCellAddress - a cell address, e.g. "foo.1.4"</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the text from the specified cell</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertTable
+				(
+					tableCellAddress, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotTable
+				(
+					tableCellAddress, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyTable
+				(
+					tableCellAddress, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotTable
+				(
+					tableCellAddress, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForTable
+				(
+					tableCellAddress, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotTable
+				(
+					tableCellAddress, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeText"></a>storeText
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets the text of an element. This works for any element that contains
+text. This command uses either the textContent (Mozilla-like browsers) or
+the innerText (IE-like browsers) of the element, which is the rendered
+text shown to the user.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the text of the element</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertText
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotText
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyText
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotText
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForText
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotText
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeTitle"></a>storeTitle
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets the title of the current page.<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the title of the current page</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertTitle
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotTitle
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyTitle
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotTitle
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForTitle
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotTitle
+				(
+					<a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeValue"></a>storeValue
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets the (whitespace-trimmed) value of an input field (or anything else with a value parameter).
+For checkbox/radio elements, the value will be "on" or "off" depending on
+whether the element is checked or not.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the element value, or "on/off" for checkbox/radio elements</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertValue
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotValue
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyValue
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotValue
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForValue
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotValue
+				(
+					locator, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeWhetherThisFrameMatchFrameExpression"></a>storeWhetherThisFrameMatchFrameExpression
+		
+		(
+			currentFrameString,
+target,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Determine whether current/locator identify the frame containing this running code.
+
+<p>This is useful in proxy injection mode, where this code runs in every
+browser frame and window, and sometimes the selenium server needs to identify
+the "current" frame.  In this case, when the test calls selectFrame, this
+routine is called for each frame to figure out which one has been selected.
+The selected frame will return true, while all others will return false.</p>
+<p>Arguments:</p>
+<ul>
+<li>currentFrameString - starting frame</li>
+<li>target - new frame (which might be relative to the current one)</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if the new frame is this code's window</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertWhetherThisFrameMatchFrameExpression
+				(
+					currentFrameString, target
+				)
+			</li>
+<li>assertNotWhetherThisFrameMatchFrameExpression
+				(
+					currentFrameString, target
+				)
+			</li>
+<li>verifyWhetherThisFrameMatchFrameExpression
+				(
+					currentFrameString, target
+				)
+			</li>
+<li>verifyNotWhetherThisFrameMatchFrameExpression
+				(
+					currentFrameString, target
+				)
+			</li>
+<li>waitForWhetherThisFrameMatchFrameExpression
+				(
+					currentFrameString, target
+				)
+			</li>
+<li>waitForNotWhetherThisFrameMatchFrameExpression
+				(
+					currentFrameString, target
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeWhetherThisWindowMatchWindowExpression"></a>storeWhetherThisWindowMatchWindowExpression
+		
+		(
+			currentWindowString,
+target,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Determine whether currentWindowString plus target identify the window containing this running code.
+
+<p>This is useful in proxy injection mode, where this code runs in every
+browser frame and window, and sometimes the selenium server needs to identify
+the "current" window.  In this case, when the test calls selectWindow, this
+routine is called for each window to figure out which one has been selected.
+The selected window will return true, while all others will return false.</p>
+<p>Arguments:</p>
+<ul>
+<li>currentWindowString - starting window</li>
+<li>target - new window (which might be relative to the current one, e.g., "_parent")</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if the new window is this code's window</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertWhetherThisWindowMatchWindowExpression
+				(
+					currentWindowString, target
+				)
+			</li>
+<li>assertNotWhetherThisWindowMatchWindowExpression
+				(
+					currentWindowString, target
+				)
+			</li>
+<li>verifyWhetherThisWindowMatchWindowExpression
+				(
+					currentWindowString, target
+				)
+			</li>
+<li>verifyNotWhetherThisWindowMatchWindowExpression
+				(
+					currentWindowString, target
+				)
+			</li>
+<li>waitForWhetherThisWindowMatchWindowExpression
+				(
+					currentWindowString, target
+				)
+			</li>
+<li>waitForNotWhetherThisWindowMatchWindowExpression
+				(
+					currentWindowString, target
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeXpathCount"></a>storeXpathCount
+		
+		(
+			xpath,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Returns the number of nodes that match the specified xpath, eg. "//table" would give
+the number of tables.<p>Arguments:</p>
+<ul>
+<li>xpath - the xpath expression to evaluate. do NOT wrap this expression in a 'count()' function; we will do that for you.</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>the number of nodes that match the specified xpath</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertXpathCount
+				(
+					xpath, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>assertNotXpathCount
+				(
+					xpath, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyXpathCount
+				(
+					xpath, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>verifyNotXpathCount
+				(
+					xpath, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForXpathCount
+				(
+					xpath, <a href="#patterns">pattern</a>
+				)
+			</li>
+<li>waitForNotXpathCount
+				(
+					xpath, <a href="#patterns">pattern</a>
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeAlertPresent"></a>storeAlertPresent
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Has an alert occurred?
+
+<p>
+This function never throws an exception
+</p>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if there is an alert</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertAlertPresent
+				(
+					
+				)
+			</li>
+<li>assertAlertNotPresent
+				(
+					
+				)
+			</li>
+<li>verifyAlertPresent
+				(
+					
+				)
+			</li>
+<li>verifyAlertNotPresent
+				(
+					
+				)
+			</li>
+<li>waitForAlertPresent
+				(
+					
+				)
+			</li>
+<li>waitForAlertNotPresent
+				(
+					
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeChecked"></a>storeChecked
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Gets whether a toggle-button (checkbox/radio) is checked.  Fails if the specified element doesn't exist or isn't a toggle-button.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a> pointing to a checkbox or radio button</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if the checkbox is checked, false otherwise</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertChecked
+				(
+					locator
+				)
+			</li>
+<li>assertNotChecked
+				(
+					locator
+				)
+			</li>
+<li>verifyChecked
+				(
+					locator
+				)
+			</li>
+<li>verifyNotChecked
+				(
+					locator
+				)
+			</li>
+<li>waitForChecked
+				(
+					locator
+				)
+			</li>
+<li>waitForNotChecked
+				(
+					locator
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeConfirmationPresent"></a>storeConfirmationPresent
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Has confirm() been called?
+
+<p>
+This function never throws an exception
+</p>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if there is a pending confirmation</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertConfirmationPresent
+				(
+					
+				)
+			</li>
+<li>assertConfirmationNotPresent
+				(
+					
+				)
+			</li>
+<li>verifyConfirmationPresent
+				(
+					
+				)
+			</li>
+<li>verifyConfirmationNotPresent
+				(
+					
+				)
+			</li>
+<li>waitForConfirmationPresent
+				(
+					
+				)
+			</li>
+<li>waitForConfirmationNotPresent
+				(
+					
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeEditable"></a>storeEditable
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Determines whether the specified input element is editable, ie hasn't been disabled.
+This method will fail if the specified element isn't an input element.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if the input element is editable, false otherwise</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertEditable
+				(
+					locator
+				)
+			</li>
+<li>assertNotEditable
+				(
+					locator
+				)
+			</li>
+<li>verifyEditable
+				(
+					locator
+				)
+			</li>
+<li>verifyNotEditable
+				(
+					locator
+				)
+			</li>
+<li>waitForEditable
+				(
+					locator
+				)
+			</li>
+<li>waitForNotEditable
+				(
+					locator
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeElementPresent"></a>storeElementPresent
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Verifies that the specified element is somewhere on the page.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if the element is present, false otherwise</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertElementPresent
+				(
+					locator
+				)
+			</li>
+<li>assertElementNotPresent
+				(
+					locator
+				)
+			</li>
+<li>verifyElementPresent
+				(
+					locator
+				)
+			</li>
+<li>verifyElementNotPresent
+				(
+					locator
+				)
+			</li>
+<li>waitForElementPresent
+				(
+					locator
+				)
+			</li>
+<li>waitForElementNotPresent
+				(
+					locator
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeOrdered"></a>storeOrdered
+		
+		(
+			locator1,
+locator2,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Check if these two elements have same parent and are ordered siblings in the DOM. Two same elements will
+not be considered ordered.<p>Arguments:</p>
+<ul>
+<li>locator1 - an <a href="#locators">element locator</a> pointing to the first element</li>
+<li>locator2 - an <a href="#locators">element locator</a> pointing to the second element</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if element1 is the previous sibling of element2, false otherwise</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertOrdered
+				(
+					locator1, locator2
+				)
+			</li>
+<li>assertNotOrdered
+				(
+					locator1, locator2
+				)
+			</li>
+<li>verifyOrdered
+				(
+					locator1, locator2
+				)
+			</li>
+<li>verifyNotOrdered
+				(
+					locator1, locator2
+				)
+			</li>
+<li>waitForOrdered
+				(
+					locator1, locator2
+				)
+			</li>
+<li>waitForNotOrdered
+				(
+					locator1, locator2
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storePromptPresent"></a>storePromptPresent
+		
+		(
+			
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Has a prompt occurred?
+
+<p>
+This function never throws an exception
+</p>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if there is a pending prompt</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertPromptPresent
+				(
+					
+				)
+			</li>
+<li>assertPromptNotPresent
+				(
+					
+				)
+			</li>
+<li>verifyPromptPresent
+				(
+					
+				)
+			</li>
+<li>verifyPromptNotPresent
+				(
+					
+				)
+			</li>
+<li>waitForPromptPresent
+				(
+					
+				)
+			</li>
+<li>waitForPromptNotPresent
+				(
+					
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeSomethingSelected"></a>storeSomethingSelected
+		
+		(
+			selectLocator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Determines whether some option in a drop-down menu is selected.<p>Arguments:</p>
+<ul>
+<li>selectLocator - an <a href="#locators">element locator</a> identifying a drop-down menu</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if some option has been selected, false otherwise</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertSomethingSelected
+				(
+					selectLocator
+				)
+			</li>
+<li>assertNotSomethingSelected
+				(
+					selectLocator
+				)
+			</li>
+<li>verifySomethingSelected
+				(
+					selectLocator
+				)
+			</li>
+<li>verifyNotSomethingSelected
+				(
+					selectLocator
+				)
+			</li>
+<li>waitForSomethingSelected
+				(
+					selectLocator
+				)
+			</li>
+<li>waitForNotSomethingSelected
+				(
+					selectLocator
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeTextPresent"></a>storeTextPresent
+		
+		(
+			pattern,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Verifies that the specified text pattern appears somewhere on the rendered page shown to the user.<p>Arguments:</p>
+<ul>
+<li>pattern - a <a href="#patterns">pattern</a> to match with the text of the page</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if the pattern matches the text, false otherwise</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertTextPresent
+				(
+					pattern
+				)
+			</li>
+<li>assertTextNotPresent
+				(
+					pattern
+				)
+			</li>
+<li>verifyTextPresent
+				(
+					pattern
+				)
+			</li>
+<li>verifyTextNotPresent
+				(
+					pattern
+				)
+			</li>
+<li>waitForTextPresent
+				(
+					pattern
+				)
+			</li>
+<li>waitForTextNotPresent
+				(
+					pattern
+				)
+			</li>
+</ul>
+</dd>
+<br>
+<dt>
+<strong><a name="storeVisible"></a>storeVisible
+		
+		(
+			locator,
+
+			variableName
+		)
+		
+	</strong>
+</dt>
+<dd>Determines if the specified element is visible. An
+element can be rendered invisible by setting the CSS "visibility"
+property to "hidden", or the "display" property to "none", either for the
+element itself or one if its ancestors.  This method will fail if
+the element is not present.<p>Arguments:</p>
+<ul>
+<li>locator - an <a href="#locators">element locator</a>
+</li>
+<li>variableName -
+                    the name of a <a href="#storedVars">variable</a> in which the result is to be stored.
+                </li>
+</ul>
+<p>
+<dl>
+<dt>Returns: </dt>
+<dd>true if the specified element is visible, false otherwise</dd>
+</dl>
+</p>
+<p>Related Assertions, automatically generated:</p>
+<ul>
+<li>assertVisible
+				(
+					locator
+				)
+			</li>
+<li>assertNotVisible
+				(
+					locator
+				)
+			</li>
+<li>verifyVisible
+				(
+					locator
+				)
+			</li>
+<li>verifyNotVisible
+				(
+					locator
+				)
+			</li>
+<li>waitForVisible
+				(
+					locator
+				)
+			</li>
+<li>waitForNotVisible
+				(
+					locator
+				)
+			</li>
+</ul>
+</dd>
+<br>
+</dl>
+<h2>
+<a name="parameter-construction-and-variables">Parameter construction and Variables</a>
+</h2>
+<blockquote>
+<p>All Selenium command parameters can be constructed using both simple
+	variable substitution as well as full javascript. Both of these
+	mechanisms can access previously stored variables, but do so using
+	different syntax.</p>
+<p>
+<a name="storedVars"></a><strong>Stored Variables</strong>
+</p>
+<p>The commands <em>store</em>, <em>storeValue</em> and <em>storeText</em> can be used to store a variable
+	value for later access. Internally, these variables are stored in a map called "storedVars",
+	with values keyed by the variable name. These commands are documented in the command reference.</p>
+<p>
+<strong>Variable substitution</strong>
+</p>
+<p>Variable substitution provides a simple way to include a previously stored variable in a
+	command parameter. This is a simple mechanism, by which the variable to substitute is indicated
+	by ${variableName}. Multiple variables can be substituted, and intermixed with static text.</p>
+<p>Example:</p>
+<blockquote>
+<table border="1" class="table">
+<colgroup>
+<col width="18%">
+<col width="36%">
+<col width="45%">
+</colgroup>
+<tbody valign="top">
+<tr>
+<td>store</td><td>Mr</td><td>title</td>
+</tr>
+<tr>
+<td>storeValue</td><td>nameField</td><td>surname</td>
+</tr>
+<tr>
+<td>store</td><td>${title} ${surname}</td><td>fullname</td>
+</tr>
+<tr>
+<td>type</td><td>textElement</td><td>Full name is: ${fullname}</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<p>
+<strong>Javascript evaluation</strong>
+</p>
+<p>Javascript evaluation provides the full power of javascript in constructing a command parameter.
+	To use this mechanism, the <em>entire</em> parameter value must be prefixed by
+	'javascript{' with a trailing '}'. The text inside the braces is evaluated as a javascript expression,
+	and can access previously stored variables using the <em>storedVars</em> map detailed above.
+	Note that variable substitution cannot be combined with javascript evaluation.</p>
+<p>Example:</p>
+<blockquote>
+<table border="1" class="table">
+<colgroup>
+<col width="9%">
+<col width="44%">
+<col width="46%">
+</colgroup>
+<tbody valign="top">
+<tr>
+<td>store</td><td>javascript{'merchant' + (new Date()).getTime()}</td><td>merchantId</td>
+</tr>
+<tr>
+<td>type</td><td>textElement</td><td>javascript{storedVars['merchantId'].toUpperCase()}</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+</blockquote>
+<div class="section" id="extending-selenium">
+<h2>
+<a name="extending-selenium">Extending Selenium</a>
+</h2>
+<blockquote>
+<p>It can be quite simple to extend Selenium, adding your own actions, assertions and locator-strategies.
+	This is done with javascript by adding methods to the Selenium object prototype, and the PageBot
+	object prototype. On startup, Selenium will automatically look through methods on these prototypes,
+	using name patterns to recognise which ones are actions, assertions and locators.</p>
+<p>The following examples try to give an indication of how Selenium can be extended with javascript.</p>
+</blockquote>
+<p>
+<strong>Actions</strong>
+</p>
+<blockquote>
+<p>All <em>doFoo</em> methods on the Selenium prototype are added as actions. For each action <em>foo</em> there
+	is also an action <em>fooAndWait</em> registered. An action method can take up to 2 parameters, which
+	will be passed the second and third column values in the test.</p>
+<p>Example: Add a "typeRepeated" action to Selenium, which types the text twice into a text box.</p>
+<pre class="literal-block">
+	Selenium.prototype.doTypeRepeated = function(locator, text) {
+	    // All locator-strategies are automatically handled by "findElement"
+	    var element = this.page().findElement(locator);
+	
+	    // Create the text to type
+	    var valueToType = text + text;
+	
+	    // Replace the element text with the new text
+	    this.page().replaceText(element, valueToType);
+	};
+	</pre>
+</blockquote>
+<p>
+<strong>Accessors/Assertions</strong>
+</p>
+<blockquote>
+<p>All <em>getFoo</em> and <em>isFoo</em> methods on the Selenium prototype are added as accessors (storeFoo). For each accessor there
+	is an <em>assertFoo</em>, <em>verifyFoo</em> and <em>waitForFoo</em> registered. An assert method can take up to 2 parameters, which
+	will be passed the second and third column values in the test.  You can also define your own assertions literally
+	as simple "assert" methods, which will also auto-generate "verify" and "waitFor" commands.</p>
+<p>Example: Add a <em>valueRepeated</em> assertion, that makes sure that the element value
+	consists of the supplied text repeated. The 2 commands that would be available in tests would be
+	<em>assertValueRepeated</em> and <em>verifyValueRepeated</em>.</p>
+<pre class="literal-block">
+	Selenium.prototype.assertValueRepeated = function(locator, text) {
+	    // All locator-strategies are automatically handled by "findElement"
+	    var element = this.page().findElement(locator);
+	
+	    // Create the text to verify
+	    var expectedValue = text + text;
+	
+	    // Get the actual element value
+	    var actualValue = element.value;
+	
+	    // Make sure the actual value matches the expected
+	    Assert.matches(expectedValue, actualValue);
+	};
+	</pre>
+</blockquote>
+<p>
+<strong>Automatic availability of storeFoo, assertFoo, assertNotFoo, waitForFoo and waitForNotFoo for every getFoo</strong>
+</p>
+<blockquote>
+<p>All <em>getFoo</em> and <em>isFoo</em> methods on the Selenium prototype automatically result in the availability
+	of storeFoo, assertFoo, assertNotFoo, verifyFoo, verifyNotFoo, waitForFoo, and waitForNotFoo commands.</p>
+<p>Example, if you add a getTextLength() method, the following commands will automatically be available:
+	storeTextLength, assertTextLength, assertNotTextLength, verifyTextLength, verifyNotTextLength, waitForTextLength, and waitForNotTextLength commands.</p>
+<pre class="literal-block">
+	Selenium.prototype.getTextLength = function(locator, text) {
+	    return this.getText(locator).length;
+	};
+	</pre>
+<p>Also note that the <em>assertValueRepeated</em> method described above could have been implemented using
+	isValueRepeated, with the added benefit of also automatically getting assertNotValueRepeated, storeValueRepeated,
+	waitForValueRepeated and waitForNotValueRepeated.</p>
+</blockquote>
+<p>
+<strong>Locator Strategies</strong>
+</p>
+<blockquote>
+<p>All <em>locateElementByFoo</em> methods on the PageBot prototype are added as locator-strategies. A locator strategy takes 2 parameters, the first being the locator string (minus the prefix), and the second being the document in which to search.</p>
+<p>Example: Add a "valuerepeated=" locator, that finds the first element a value attribute equal to the the supplied value repeated.</p>
+<pre class="literal-block">
+	// The "inDocument" is a the document you are searching.
+	PageBot.prototype.locateElementByValueRepeated = function(text, inDocument) {
+	    // Create the text to search for
+	    var expectedValue = text + text;
+	
+	    // Loop through all elements, looking for ones that have 
+	    // a value === our expected value
+	    var allElements = inDocument.getElementsByTagName("*");
+	    for (var i = 0; i &lt; allElements.length; i++) {
+	        var testElement = allElements[i];
+	        if (testElement.value &amp;&amp; testElement.value === expectedValue) {
+	            return testElement;
+	        }
+	    }
+	    return null;
+	};
+	</pre>
+</blockquote>
+<p>
+<strong>user-extensions.js</strong>
+</p>
+<blockquote>
+<p>By default, Selenium looks for a file called "user-extensions.js", and loads the javascript code found in that file. This file provides a convenient location for adding features to Selenium, without needing to modify the core Selenium sources.</p>
+<p>In the standard distibution, this file does not exist. Users can create this file and place their extension code in this common location, removing the need to modify the Selenium sources, and hopefully assisting with the upgrade process.</p>
+</blockquote>
+</div>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/compiler.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/compiler.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/compiler.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,3259 @@
+// This requires strands.js
+ /* ***** BEGIN LICENSE BLOCK *****
+  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+  *
+  * The contents of this file are subject to the Mozilla Public License Version
+  * 1.1 (the "License"); you may not use this file except in compliance with
+  * the License. You may obtain a copy of the License at
+  * http://www.mozilla.org/MPL/
+  *
+  * Software distributed under the License is distributed on an "AS IS" basis,
+  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  * for the specific language governing rights and limitations under the
+  * License.
+  *
+  * The Original Code is the Narcissus JavaScript engine.
+  *
+  * The Initial Developer of the Original Code is
+  * Brendan Eich <brendan at mozilla.org>.
+  * Portions created by the Initial Developer are Copyright (C) 2004
+  * the Initial Developer. All Rights Reserved.
+  *
+  * Contributor(s):
+  *
+  * Alternatively, the contents of this file may be used under the terms of
+  * either the GNU General Public License Version 2 or later (the "GPL"), or
+  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  * in which case the provisions of the GPL or the LGPL are applicable instead
+  * of those above. If you wish to allow use of your version of this file only
+  * under the terms of either the GPL or the LGPL, and not to allow others to
+  * use your version of this file under the terms of the MPL, indicate your
+  * decision by deleting the provisions above and replace them with the notice
+  * and other provisions required by the GPL or the LGPL. If you do not delete
+  * the provisions above, a recipient may use your version of this file under
+  * the terms of any one of the MPL, the GPL or the LGPL.
+  *
+  * ***** END LICENSE BLOCK ***** */
+ 
+ /*
+  * Narcissus - JS implemented in JS.
+  *
+  * Well-known constants and lookup tables.  Many consts are generated from the
+  * tokens table via eval to minimize redundancy, so consumers must be compiled
+  * separately to take advantage of the simple switch-case constant propagation
+  * done by SpiderMonkey.
+  */
+ 
+ /*
+  * Authenteo edit:
+  * Added the # operators
+  * strands edit:
+  * - Neil: combine jsdefs.js and jsparse.js into a single file as part of an
+  *   effort to reduce namespace pollution.
+  * - Neil: make opTypeName order explicit for env compatibility.  The original
+  *   source relied on a SpiderMonkey specific behavior where object key
+  *   iteration occurs in the same order in which the keys were defined in 
+  *   the object.
+ Ê* - Neil: perf optimizations for OOM+ parse speedup
+ Ê* - Neil: make code x-env-compatible
+ Ê* - chocolateboy 2006-06-01: add support for $ in identifiers and remove support for ` as the first character as per:
+ Ê* Ê http://www.mozilla.org/js/language/es4/formal/lexer-semantics.html#N-InitialIdentifierCharacter and
+ Ê* Ê http://www.mozilla.org/js/language/es4/formal/lexer-semantics.html#N-ContinuingIdentifierCharacter
+  */
+
+Narcissus = {};
+
+(function() {
+
+	// EDIT: remove references to global to avoid namespace pollution
+
+	 // EDIT: add yielding op
+	 var tokens = [
+		 // End of source.
+		 "END",
+	 
+		 // Operators and punctuators.  Some pair-wise order matters, e.g. (+, -)
+		 // and (UNARY_PLUS, UNARY_MINUS).
+		 "\n", ";",
+		 ",",
+		 "=",
+		 "?", ":", "CONDITIONAL",
+		 "||",
+		 "&&",
+		 "|",
+		 "^",
+		 "&",
+		 "->",
+		 "==", "!=", "===", "!==",
+		 "<", "<=", ">=", ">",
+		 "<<", ">>", ">>>",
+		 "+", "-",
+		 "*", "/", "%",
+		 "#","!", "~", "UNARY_PLUS", "UNARY_MINUS",
+		 "++", "--",
+		 ".",".#",
+		 "#[", "[", "]",
+		 "{", "}",
+		 "(", ")",
+	 
+		 // Nonterminal tree node type codes.
+		 "SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX", "TRANSIENT_INDEX",
+		 "ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER",
+		 "GROUP", "LIST",
+	 
+		 // Terminals.
+		 "IDENTIFIER", "NUMBER", "STRING", "REGEXP",
+	 
+		 // Keywords.
+		 "break",
+		 "case", "catch", "const", "continue",
+		 "debugger", "default", "delete", "do",
+		 "else", "enum",
+		 "false", "finally", "for", "function",
+		 "if", "in", "instanceof", "is",
+		 "new", "null",
+		 "return",
+		 "switch",
+		 "this", "throw", "true", "try", "typeof",
+		 "var", "void",
+		 "while", "with" // EDIT: remove trailing comma (breaks IE)
+	 ];
+	 
+	 // Operator and punctuator mapping from token to tree node type name.
+	 // NB: superstring tokens (e.g., ++) must come before their substring token
+	 // counterparts (+ in the example), so that the opRegExp regular expression
+	 // synthesized from this list makes the longest possible match.
+	// EDIT: NB comment above indicates reliance on SpiderMonkey-specific
+	//       behavior in the ordering of key iteration -- see EDIT below.
+	// EDIT: add yeilding op
+	 var opTypeNames = {
+		 '\n':   "NEWLINE",
+		 ';':    "SEMICOLON",
+		 ',':    "COMMA",
+		 '?':    "HOOK",
+		 ':':    "COLON",
+		 '||':   "OR",
+		 '&&':   "AND",
+		 '|':    "BITWISE_OR",
+		 '^':    "BITWISE_XOR",
+		 '&':    "BITWISE_AND",
+		 '->':   "YIELDING",
+		 '===':  "STRICT_EQ",
+		 '==':   "EQ",
+		 '=':    "ASSIGN",
+		 '!==':  "STRICT_NE",
+		 '!=':   "NE",
+		 '<<':   "LSH",
+		 '<=':   "LE",
+		 '<':    "LT",
+		 '>>>':  "URSH",
+		 '>>':   "RSH",
+		 '>=':   "GE",
+		 '>':    "GT",
+		 '++':   "INCREMENT",
+		 '--':   "DECREMENT",
+		 '+':    "PLUS",
+		 '-':    "MINUS",
+		 '*':    "MUL",
+		 '/':    "DIV",
+		 '%':    "MOD",
+		 '#': "OBJECT_ID_REFERENCE",
+		 '!':    "NOT",
+		 '~':    "BITWISE_NOT",
+		 '.#':    "TRANSIENT_DOT",
+		 '.':    "DOT",
+		 '#[':    "TRANSIENT_LEFT_BRACKET",
+		 '[':    "LEFT_BRACKET",
+		 ']':    "RIGHT_BRACKET",
+		 '{':    "LEFT_CURLY",
+		 '}':    "RIGHT_CURLY",
+		 '(':    "LEFT_PAREN",
+		 ')':    "RIGHT_PAREN"
+	 };
+	
+	// EDIT: created separate opTypeOrder array to indicate the order in which
+	//       to evaluate opTypeNames.  (Apparently, SpiderMonkey must iterate
+	//       hash keys in the order in which they are defined, an implementation
+	//       detail which the original narcissus code relied on.)
+	// EDIT: add yielding op
+	 var opTypeOrder = [
+		 '\n',
+		 ';',
+		 ',',
+		 '?',
+		 ':',
+		 '||',
+		 '&&',
+		 '|',
+		 '^',
+		 '&',
+		 '->',
+		 '===',
+		 '==',
+		 '=',
+		 '!==',
+		 '!=',
+		 '<<',
+		 '<=',
+		 '<',
+		 '>>>',
+		 '>>',
+		 '>=',
+		 '>',
+		 '++',
+		 '--',
+		 '+',
+		 '-',
+		 '*',
+		 '/',
+		 '%',
+		 '#',
+		 '!',
+		 '~',
+		 '.#',
+		 '.',
+		 '#[',
+		 '[',
+		 ']',
+		 '{',
+		 '}',
+		 '(',
+		 ')'
+	 ];
+	 
+	 // Hash of keyword identifier to tokens index.  NB: we must null __proto__ to
+	 // avoid toString, etc. namespace pollution.
+	 var keywords = {__proto__: null};
+	 
+	 // Define const END, etc., based on the token names.  Also map name to index.
+	 // EDIT: use "var " prefix to make definitions local to this function
+	 var consts = "var ";
+	 for (var i = 0, j = tokens.length; i < j; i++) {
+		 if (i > 0)
+			 consts += ", ";
+		 var t = tokens[i];
+		 var name;
+		 if (/^[a-z]/.test(t)) {
+			 name = t.toUpperCase();
+			 keywords[t] = i;
+		 } else {
+			 name = (/^\W/.test(t) ? opTypeNames[t] : t);
+		 }
+		 consts += name + " = " + i;
+		 this[name] = i;
+		 tokens[t] = i;
+	 }
+	 eval(consts + ";");
+	 
+	 // Map assignment operators to their indexes in the tokens array.
+	 var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%'];
+	 
+	 for (i = 0, j = assignOps.length; i < j; i++) {
+		 t = assignOps[i];
+		 assignOps[t] = tokens[t];
+	 }
+	 /* vim: set sw=4 ts=8 et tw=80: */
+	 /* ***** BEGIN LICENSE BLOCK *****
+	  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+	  *
+	  * The contents of this file are subject to the Mozilla Public License Version
+	  * 1.1 (the "License"); you may not use this file except in compliance with
+	  * the License. You may obtain a copy of the License at
+	  * http://www.mozilla.org/MPL/
+	  *
+	  * Software distributed under the License is distributed on an "AS IS" basis,
+	  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+	  * for the specific language governing rights and limitations under the
+	  * License.
+	  *
+	  * The Original Code is the Narcissus JavaScript engine.
+	  *
+	  * The Initial Developer of the Original Code is
+	  * Brendan Eich <brendan at mozilla.org>.
+	  * Portions created by the Initial Developer are Copyright (C) 2004
+	  * the Initial Developer. All Rights Reserved.
+	  *
+	  * Contributor(s):
+	  *
+	  * Alternatively, the contents of this file may be used under the terms of
+	  * either the GNU General Public License Version 2 or later (the "GPL"), or
+	  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+	  * in which case the provisions of the GPL or the LGPL are applicable instead
+	  * of those above. If you wish to allow use of your version of this file only
+	  * under the terms of either the GPL or the LGPL, and not to allow others to
+	  * use your version of this file under the terms of the MPL, indicate your
+	  * decision by deleting the provisions above and replace them with the notice
+	  * and other provisions required by the GPL or the LGPL. If you do not delete
+	  * the provisions above, a recipient may use your version of this file under
+	  * the terms of any one of the MPL, the GPL or the LGPL.
+	  *
+	  * ***** END LICENSE BLOCK ***** */
+	 
+	 /*
+	  * Narcissus - JS implemented in JS.
+	  *
+	  * Lexical scanner and parser.
+	  */
+	  
+	  
+	 // Build a regexp that recognizes operators and punctuators (except newline).
+	 var opRegExpSrc = "^(?:";
+	 
+	 // EDIT: change for loop from iterating through opTypeNames keys to using
+	 //       opTypeOrder array so that we're not dependent on SpiderMonkey's
+	 //       key order default behavior.
+	 // EDIT: change regex structure for OOM perf improvement
+	 for (var i = 0; i < opTypeOrder.length; i++) {
+		 var op = opTypeOrder[i];
+		 if (op == '\n')
+			 continue;
+		 if (opRegExpSrc != "^(?:")
+			 opRegExpSrc += "|";
+		 
+		 // EDIT: expand out this regexp for environments that don't support $&
+		 //opRegExpSrc += op.replace(/[?|^&(){}\[\]+\-*\/\.]/g, "\\$&");
+		 op = op.replace(/\?/g, "\\?");
+		 op = op.replace(/\|/g, "\\|");
+		 op = op.replace(/\^/g, "\\^");
+		 op = op.replace(/\&/g, "\\&");
+		 op = op.replace(/\(/g, "\\(");
+		 op = op.replace(/\)/g, "\\)");
+		 op = op.replace(/\{/g, "\\{");
+		 op = op.replace(/\}/g, "\\}");
+		 op = op.replace(/\[/g, "\\[");
+		 op = op.replace(/\]/g, "\\]");
+		 op = op.replace(/\+/g, "\\+");
+		 op = op.replace(/\-/g, "\\-");
+		 op = op.replace(/\*/g, "\\*");
+		 op = op.replace(/\//g, "\\/");
+		 op = op.replace(/\./g, "\\.");
+		 opRegExpSrc += op;
+	 }
+	 opRegExpSrc += ")";
+	 var opRegExp = new RegExp(opRegExpSrc);
+	 
+	 // A regexp to match floating point literals (but not integer literals).
+	 // EDIT: change regex structure for OOM perf improvement
+	 var fpRegExp = /^(?:\d+\.\d*(?:[eE][-+]?\d+)?|\d+(?:\.\d*)?[eE][-+]?\d+|\.\d+(?:[eE][-+]?\d+)?)/;
+	 
+	 function Tokenizer(s, f, l) {
+		 this.cursor = 0;
+		 this.source = String(s);
+		 this.tokens = [];
+		 this.tokenIndex = 0;
+		 this.lookahead = 0;
+		 this.scanNewlines = false;
+		 this.scanOperand = true;
+		 this.filename = f || "";
+		 this.lineno = l ? l : 1;
+	 }
+	  
+	 Tokenizer.prototype = {
+	 
+	 // EDIT: change "input" from a getter to a regular method for compatibility
+	 //       with older JavaScript versions
+		 input: function() {
+			 return this.source.substring(this.cursor);
+		 },
+	 
+	 // EDIT: change "done" from a getter to a regular method for compatibility
+	 //       with older JavaScript versions
+		 done: function() {
+			 return this.peek() == END;
+		 },
+	 
+	 // EDIT: change "token" from a getter to a regular method for compatibility
+	 //       with older JavaScript versions
+		 token: function() {
+			 return this.tokens[this.tokenIndex];
+		 },
+	 
+		 match: function (tt) {
+			 return this.get() == tt || this.unget();
+		 },
+	 
+		 mustMatch: function (tt) {
+			 if (!this.match(tt)) {
+				 throw this.newSyntaxError("Missing " + tokens[tt].toLowerCase());
+			 }
+			 return this.token();
+		 },
+	 
+		 peek: function () {
+			 var tt;
+			 if (this.lookahead) {
+				 tt = this.tokens[(this.tokenIndex + this.lookahead) & 3].type;
+			 } else {
+				 tt = this.get();
+				 this.unget();
+			 }
+			 return tt;
+		 },
+	 
+		 peekOnSameLine: function () {
+			 this.scanNewlines = true;
+			 var tt = this.peek();
+			 this.scanNewlines = false;
+			 return tt;
+		 },
+	 
+		 get: function () {
+			 var token;
+			 while (this.lookahead) {
+				 --this.lookahead;
+				 this.tokenIndex = (this.tokenIndex + 1) & 3;
+				 token = this.tokens[this.tokenIndex];
+				 if (token.type != NEWLINE || this.scanNewlines)
+					 return token.type;
+			 }
+	 
+			 for (;;) {
+				 var input = this.input();
+				 var firstChar = input.charCodeAt(0);
+				 // EDIT: check first char, then use regex
+				 // valid regex whitespace includes char codes: 9 10 11 12 13 32
+				 if(firstChar == 32 || (firstChar >= 9 && firstChar <= 13)) {
+					 var match = input.match(this.scanNewlines ? /^[ \t]+/ : /^\s+/); // EDIT: use x-browser regex syntax
+					 if (match) {
+						 var spaces = match[0];
+						 this.cursor += spaces.length;
+						 var newlines = spaces.match(/\n/g);
+						 if (newlines)
+							 this.lineno += newlines.length;
+						 input = this.input();
+					}
+				 }
+	 
+				 // EDIT: improve perf by checking first string char before proceeding to regex,
+				 //       use x-browser regex syntax
+				 if (input.charCodeAt(0) != 47 || !(match = input.match(/^\/(?:\*(?:.|\n)*?\*\/|\/.*)/)))
+					 break;
+				 var comment = match[0];
+				 this.cursor += comment.length;
+				 newlines = comment.match(/\n/g);
+				 if (newlines)
+					 this.lineno += newlines.length
+			 }
+	 
+			 this.tokenIndex = (this.tokenIndex + 1) & 3;
+			 token = this.tokens[this.tokenIndex];
+			 if (!token)
+				 this.tokens[this.tokenIndex] = token = {};
+	 
+			 if (!input)
+				 return token.type = END;
+	
+			 var firstChar = input.charCodeAt(0);
+			 
+			 // EDIT: guard by checking char codes before going to regex
+			 if ((firstChar == 46 || (firstChar > 47 && firstChar < 58)) && 
+				 (match = input.match(fpRegExp))) { // EDIT: use x-browser regex syntax
+				 token.type = NUMBER;
+				 token.value = parseFloat(match[0]);
+			 } else if ((firstChar > 47 && firstChar < 58) && 
+						(match = input.match(/^(?:0[xX][\da-fA-F]+|0[0-7]*|\d+)/))) { // EDIT: change regex structure for OOM perf improvement,
+																					  //       use x-browser regex syntax
+				 token.type = NUMBER;
+				 token.value = parseInt(match[0]);
+			 } else if (((firstChar > 47 && firstChar < 58)  ||   // EDIT: add guards to check before using regex
+						 (firstChar > 64 && firstChar < 91)  || 
+						 (firstChar > 96 && firstChar < 123) ||   // EDIT: exclude `
+						 (firstChar == 36 || firstChar == 95)) && // EDIT: allow $ + mv _ here
+						(match = input.match(/^[$\w]+/))) {       // EDIT: allow $, use x-browser regex syntax
+				 var id = match[0];
+				 // EDIT: check the type of the value in the keywords hash, as different envs
+				 //       expose implicit Object properties that SpiderMonkey does not.
+				 token.type = typeof(keywords[id]) == "number" ? keywords[id] : IDENTIFIER;
+				 token.value = id;
+			 } else if ((firstChar == 34 || firstChar == 39) && 
+						(match = input.match(/^(?:"(?:\\.|[^"])*"|'(?:[^']|\\.)*')/))) { //"){  // EDIT: change regex structure for OOM perf improvement,
+																								//       use x-browser regex syntax
+				 token.type = STRING;
+				 token.value = eval(match[0]);
+			 } else if (this.scanOperand && firstChar == 47 && // EDIT: improve perf by guarding with first char check
+						(match = input.match(/^\/((?:\\.|[^\/])+)\/([gi]*)/))) { // EDIT: use x-browser regex syntax
+				 token.type = REGEXP;
+				 token.value = new RegExp(match[1], match[2]);
+			 } else if ((match = input.match(opRegExp))) { // EDIT: use x-browser regex syntax
+				 var op = match[0];
+				 // EDIT: IE doesn't support indexing of strings -- use charAt
+				 if (assignOps[op] && input.charAt(op.length) == '=') {
+					 token.type = ASSIGN;
+					 token.assignOp = eval(opTypeNames[op]);
+					 match[0] += '=';
+				 } else {
+					 token.type = eval(opTypeNames[op]);
+					 if (this.scanOperand &&
+						 (token.type == PLUS || token.type == MINUS)) {
+						 token.type += UNARY_PLUS - PLUS;
+					 }
+					 token.assignOp = null;
+				 }
+				 token.value = op;
+			 } else {
+				 throw this.newSyntaxError("Illegal token");
+			 }
+	
+			 token.start = this.cursor;
+			 this.cursor += match[0].length;
+			 token.end = this.cursor;
+			 token.lineno = this.lineno;
+			 return token.type;
+		 },
+	 
+		 unget: function () {
+			 if (++this.lookahead == 4) throw "PANIC: too much lookahead!";
+			 this.tokenIndex = (this.tokenIndex - 1) & 3;
+		 },
+	 
+		 newSyntaxError: function (m) {
+			 var e = new SyntaxError(m, this.filename, this.lineno);
+			 e.lineNumber = this.lineno; // EDIT: x-browser exception handling
+			 e.source = this.source;
+			 e.cursor = this.cursor;
+			 return e;
+		 }
+	 };
+	 
+	 function CompilerContext(inFunction) {
+		 this.inFunction = inFunction;
+		 this.stmtStack = [];
+		 this.funDecls = [];
+		 this.varDecls = [];
+	 }
+	 
+	 var CCp = CompilerContext.prototype;
+	 CCp.bracketLevel = CCp.curlyLevel = CCp.parenLevel = CCp.hookLevel = 0;
+	 CCp.ecmaStrictMode = CCp.inForLoopInit = false;
+	 
+	 function Script(t, x) {
+		 var n = Statements(t, x);
+		 n.type = SCRIPT;
+		 n.funDecls = x.funDecls;
+		 n.varDecls = x.varDecls;
+		 return n;
+	 }
+	 
+	// EDIT: change "top" method to be a regular method, rather than defined
+	//       via the SpiderMonkey-specific __defineProperty__
+	
+	 // Node extends Array, which we extend slightly with a top-of-stack method.
+	 Array.prototype.top = function() {
+		return this.length && this[this.length-1];
+	 }
+	 
+	 function Node(t, type) {
+		 // EDIT: "inherit" from Array in an x-browser way.
+		 var _this = [];
+		 for (var n in Node.prototype)
+		 	_this[n] = Node.prototype[n];
+
+		 _this.constructor = Node;
+
+		 var token = t.token();
+		 if (token) {
+			 _this.type = type || token.type;
+			 _this.value = token.value;
+			 _this.lineno = token.lineno;
+			 _this.start = token.start;
+			 _this.end = token.end;
+		 } else {
+			 _this.type = type;
+			 _this.lineno = t.lineno;
+		 }
+		 _this.tokenizer = t;
+	 
+		 for (var i = 2; i < arguments.length; i++) 
+			_this.push(arguments[i]);
+		
+		 return _this;
+	 }
+	 
+	 var Np = Node.prototype; // EDIT: don't inherit from array
+	 Np.toSource = Object.prototype.toSource;
+	 	
+	 // Always use push to add operands to an expression, to update start and end.
+	 Np.push = function (kid) {
+		 if (kid.start < this.start)
+			 this.start = kid.start;
+		 if (this.end < kid.end)
+			 this.end = kid.end;
+	
+		 this[this.length] = kid;
+	 }
+	 
+	 Node.indentLevel = 0;
+	 
+	 Np.toString = function () {
+		 var a = [];
+		 for (var i in this) {
+			 if (this.hasOwnProperty(i) && i != 'type' && i != 'parent' && typeof(this[i]) != 'function') {
+				 // EDIT,BUG: add check for 'target' to prevent infinite recursion
+				 if(i != 'target')
+					 a.push({id: i, value: this[i]});
+				 else
+					 a.push({id: i, value: "[token: " + this[i].value + "]"});
+			 }
+					
+		 }
+		 a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; });
+		 INDENTATION = "    ";
+		 var n = ++Node.indentLevel;
+		 var t = tokens[this.type];
+		 var s = "{\n" + INDENTATION.repeat(n) +
+				 "type: " + (/^\W/.test(t) ? opTypeNames[t] : t.toUpperCase());
+		 for (i = 0; i < a.length; i++) {
+			 s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value;}
+		 n = --Node.indentLevel;
+		 s += "\n" + INDENTATION.repeat(n) + "}";
+		 return s;
+	 }
+	 
+	 Np.getSource = function () {
+		 return this.tokenizer.source.slice(this.start, this.end);
+	 };
+	 
+	// EDIT: change "filename" method to be a regular method, rather than defined
+	//       via the SpiderMonkey-specific __defineGetter__
+	 Np.filename = function () { return this.tokenizer.filename; };
+	 
+	 String.prototype.repeat = function (n) {
+		 var s = "", t = this + s;
+		 while (--n >= 0)
+			 s += t;
+		 return s;
+	 }
+	 
+	 // Statement stack and nested statement handler.
+	 function nest(t, x, node, func, end) {
+		 x.stmtStack.push(node);
+		 var n = func(t, x);
+		 x.stmtStack.pop();
+		 end && t.mustMatch(end);
+		 return n;
+	 }
+	 
+	 function Statements(t, x) {
+		 var n = Node(t, BLOCK);
+		 x.stmtStack.push(n);
+		 while (!t.done() && t.peek() != RIGHT_CURLY) 
+			 n.push(Statement(t, x));
+		 x.stmtStack.pop();
+		 return n;
+	 }
+	 
+	 function Block(t, x) {
+		t.mustMatch(LEFT_CURLY);
+		 var n = Statements(t, x);
+		 t.mustMatch(RIGHT_CURLY);
+		 return n;
+	 }
+	 
+	 var DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2;
+	 
+	 function Statement(t, x) {
+		 var i, label, n, n2, ss, tt = t.get();
+	 
+		 // Cases for statements ending in a right curly return early, avoiding the
+		 // common semicolon insertion magic after this switch.
+		switch (tt) {
+		   case FUNCTION:
+			 return FunctionDefinition(t, x, true,DECLARED_FORM);
+									   /*(x.stmtStack.length > 1)
+									   ? STATEMENT_FORM
+									   : DECLARED_FORM);*/
+	 
+		   case LEFT_CURLY:
+			 n = Statements(t, x);
+			 t.mustMatch(RIGHT_CURLY);
+			 return n;
+	 
+		   case IF:
+			 n = Node(t);
+			 n.condition = ParenExpression(t, x);
+			 x.stmtStack.push(n);
+			 n.thenPart = Statement(t, x);
+			 n.elsePart = t.match(ELSE) ? Statement(t, x) : null;
+			 x.stmtStack.pop();
+			 return n;
+	 
+		   case SWITCH:
+			 n = Node(t);
+			 t.mustMatch(LEFT_PAREN);
+			 n.discriminant = Expression(t, x);
+			 t.mustMatch(RIGHT_PAREN);
+			 n.cases = [];
+			 n.defaultIndex = -1;
+			 x.stmtStack.push(n);
+			 t.mustMatch(LEFT_CURLY);
+			 while ((tt = t.get()) != RIGHT_CURLY) {
+				 switch (tt) {
+				   case DEFAULT:
+					 if (n.defaultIndex >= 0)
+						 throw t.newSyntaxError("More than one switch default");
+					 // FALL THROUGH
+				   case CASE:
+					 n2 = Node(t);
+					 if (tt == DEFAULT)
+						 n.defaultIndex = n.cases.length;
+					 else
+						 n2.caseLabel = Expression(t, x, COLON);
+					 break;
+				   default:
+					 throw t.newSyntaxError("Invalid switch case");
+				 }
+				 t.mustMatch(COLON);
+				 n2.statements = Node(t, BLOCK);
+				 while ((tt=t.peek()) != CASE && tt != DEFAULT && tt != RIGHT_CURLY)
+					 n2.statements.push(Statement(t, x));
+				 n.cases.push(n2);
+			 }
+			 x.stmtStack.pop();
+			 return n;
+	 
+		   case FOR:
+			 n = Node(t);
+			 n.isLoop = true;
+			 t.mustMatch(LEFT_PAREN);
+			 if ((tt = t.peek()) != SEMICOLON) {
+				 x.inForLoopInit = true;
+				 if (tt == VAR || tt == CONST) {
+					 t.get();
+					 n2 = Variables(t, x);
+				 } else {
+					 n2 = Expression(t, x);
+				 }
+				 x.inForLoopInit = false;
+			 }
+			 if (n2 && t.match(IN)) {
+				 n.type = FOR_IN;
+				 if (n2.type == VAR) {
+					 if (n2.length != 1) {
+						 throw new SyntaxError("Invalid for..in left-hand side",
+											   t.filename, n2.lineno);
+					 }
+	 
+					 // NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name.
+					 n.iterator = n2[0];
+					 n.varDecl = n2;
+				 } else {
+					 n.iterator = n2;
+					 n.varDecl = null;
+				 }
+				 n.object = Expression(t, x);
+			 } else {
+				 n.setup = n2 || null;
+				 t.mustMatch(SEMICOLON);
+				 n.condition = (t.peek() == SEMICOLON) ? null : Expression(t, x);
+				 t.mustMatch(SEMICOLON);
+				 n.update = (t.peek() == RIGHT_PAREN) ? null : Expression(t, x);
+			 }
+			 t.mustMatch(RIGHT_PAREN);
+			 n.body = nest(t, x, n, Statement);
+			 return n;
+	 
+		   case WHILE:
+			 n = Node(t);
+			 n.isLoop = true;
+			 n.condition = ParenExpression(t, x);
+			 n.body = nest(t, x, n, Statement);
+			 return n;
+	 
+		   case DO:
+			 n = Node(t);
+			 n.isLoop = true;
+			 n.body = nest(t, x, n, Statement, WHILE);
+			 n.condition = ParenExpression(t, x);
+			 if (!x.ecmaStrictMode) {
+				 // <script language="JavaScript"> (without version hints) may need
+				 // automatic semicolon insertion without a newline after do-while.
+				 // See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
+				 t.match(SEMICOLON);
+				 return n;
+			 }
+			 break;
+	 
+		   case BREAK:
+		   case CONTINUE:
+			 n = Node(t);
+			 if (t.peekOnSameLine() == IDENTIFIER) {
+				 t.get();
+				 n.label = t.token().value;
+			 }
+			 ss = x.stmtStack;
+			 i = ss.length;
+			 label = n.label;
+			 if (label) {
+				 do {
+					 if (--i < 0)
+						 throw t.newSyntaxError("Label not found");
+				 } while (ss[i].label != label);
+			 } else {
+				 do {
+					 if (--i < 0) {
+						 throw t.newSyntaxError("Invalid " + ((tt == BREAK)
+															  ? "break"
+															  : "continue"));
+					 }
+				 } while (!ss[i].isLoop && (tt != BREAK || ss[i].type != SWITCH));
+			 }
+			 n.target = ss[i];
+			 break;
+	 
+		   case TRY:
+			 n = Node(t);
+			 n.tryBlock = Block(t, x);
+			 n.catchClauses = [];
+			 while (t.match(CATCH)) {
+				 n2 = Node(t);
+				 t.mustMatch(LEFT_PAREN);
+				 n2.varName = t.mustMatch(IDENTIFIER).value;
+				 if (t.match(IF)) {
+					 if (x.ecmaStrictMode)
+						 throw t.newSyntaxError("Illegal catch guard");
+					 if (n.catchClauses.length && !n.catchClauses.top().guard)
+						 throw t.newSyntaxError("Guarded catch after unguarded");
+					 n2.guard = Expression(t, x);
+				 } else {
+					 n2.guard = null;
+				 }
+				 t.mustMatch(RIGHT_PAREN);
+				 n2.block = Block(t, x);
+				 n.catchClauses.push(n2);
+			 }
+			 if (t.match(FINALLY))
+				 n.finallyBlock = Block(t, x);
+			 if (!n.catchClauses.length && !n.finallyBlock)
+				 throw t.newSyntaxError("Invalid try statement");
+			 return n;
+	 
+		   case CATCH:
+		   case FINALLY:
+			 throw t.newSyntaxError(tokens[tt] + " without preceding try");
+	 
+		   case THROW:
+			 n = Node(t);
+			 n.exception = Expression(t, x);
+			 break;
+	 
+		   case RETURN:
+			 if (!x.inFunction)
+				 throw t.newSyntaxError("Invalid return");
+			 n = Node(t);
+			 tt = t.peekOnSameLine();
+			 // EDIT,BUG?: rather that set n.value (which already has meaning for
+			 //            nodes), set n.expression
+			 if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY)
+				 n.expression = Expression(t, x);
+			 break;
+	 
+		   case WITH:
+			 n = Node(t);
+			 n.object = ParenExpression(t, x);
+			 n.body = nest(t, x, n, Statement);
+			 return n;
+	 
+		   case VAR:
+		   case CONST:
+			 n = Variables(t, x);
+			 break;
+	 
+		   case DEBUGGER:
+			 n = Node(t);
+			 break;
+	 
+		   case NEWLINE:
+		   case SEMICOLON:
+			 n = Node(t, SEMICOLON);
+			 n.expression = null;
+			 return n;
+	 
+		   default:
+			 if (tt == IDENTIFIER && t.peek() == COLON) {
+				 label = t.token().value;
+				 ss = x.stmtStack;
+				 for (i = ss.length-1; i >= 0; --i) {
+					 if (ss[i].label == label)
+						 throw t.newSyntaxError("Duplicate label");
+				 }
+				 t.get();
+				 n = Node(t, LABEL);
+				 n.label = label;
+				 n.statement = nest(t, x, n, Statement);
+				 return n;
+			 }
+	 
+			 n = Node(t, SEMICOLON);
+			 t.unget();
+			 n.expression = Expression(t, x);
+			 n.end = n.expression.end;
+			 break;
+		 }
+	 
+		 if (t.lineno == t.token().lineno) {
+			 tt = t.peekOnSameLine();
+			 if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY)
+				 throw t.newSyntaxError("Missing ; before statement " + tokens[tt]);
+		 }
+		 t.match(SEMICOLON);
+		 return n;
+	 }
+	 function TypeDefinition(t,v) {
+		if (Narcissus.typeChecking && t.peek() == COLON) { // Edit: Added this for making typed variable
+			t.get();
+			if (t.peek() == OBJECT_ID_REFERENCE)
+				t.match(OBJECT_ID_REFERENCE)
+			if (t.peek() == FUNCTION) {				
+				t.match(FUNCTION)
+				v.varType = {value:"function"};
+			}
+			else if (t.peek() == MUL) {
+				t.match(MUL)
+				v.varType = {value:"any"};				
+			}
+			else {
+				t.mustMatch(IDENTIFIER);
+				v.varType = Node(t);
+			}
+		}
+	 	
+	 }
+	 function FunctionDefinition(t, x, requireName, functionForm) {
+		 var f = Node(t);
+		 if (f.type != FUNCTION)
+			 f.type = (f.value == "get") ? GETTER : SETTER;
+		 if (t.match(IDENTIFIER))
+			 f.name = t.token().value;
+		 else if (requireName)
+			 throw t.newSyntaxError("Missing function identifier");
+
+	
+		 t.mustMatch(LEFT_PAREN);
+		 f.params = [];
+		 var tt;
+		 while ((tt = t.get()) != RIGHT_PAREN) {
+			 if (tt != IDENTIFIER)
+				 throw t.newSyntaxError("Missing formal parameter");
+			var param = Node(t);
+			 f.params.push(param);
+			 TypeDefinition(t,param);
+			 if (t.peek() != RIGHT_PAREN)
+				 t.mustMatch(COMMA);
+		 }
+		 TypeDefinition(t,f);
+		 	 	
+		 t.mustMatch(LEFT_CURLY);
+		 var x2 = new CompilerContext(true);
+		 f.body = Script(t, x2);
+		 t.mustMatch(RIGHT_CURLY);
+		 f.end = t.token().end;
+	 
+		 f.functionForm = functionForm;
+		 if (functionForm == DECLARED_FORM)
+			 x.funDecls.push(f);
+		 return f;
+	 }
+	 
+	 function Variables(t, x) {
+		 var n = Node(t);
+		 do {
+ 		 	 if (!(t.match(IDENTIFIER) || t.match(COLON))) 
+			 	throw t.newSyntaxError("Invalid variable initialization");
+			 var n2 = Node(t);
+			 TypeDefinition(t,n2);
+			 n2.name = n2.value;
+			 if (t.match(ASSIGN)) {
+				 if (t.token().assignOp)
+					 throw t.newSyntaxError("Invalid variable initialization");
+				 n2.initializer = Expression(t, x, COMMA);
+			 }
+			 n2.readOnly = (n.type == CONST);
+			 n.push(n2);
+			 x.varDecls.push(n2);
+		 } while (t.match(COMMA));
+		 return n;
+	 }
+	 
+	 function ParenExpression(t, x) {
+		 t.mustMatch(LEFT_PAREN);
+		 var n = Expression(t, x);
+		 t.mustMatch(RIGHT_PAREN);
+		 return n;
+	 }
+	 
+	 // EDIT: add yielding op precedence
+	 var opPrecedence = {
+		 SEMICOLON: 0,
+		 COMMA: 1,
+		 ASSIGN: 2,
+		 HOOK: 3, COLON: 3, CONDITIONAL: 3,
+		 OR: 4,
+		 AND: 5,
+		 BITWISE_OR: 6,
+		 BITWISE_XOR: 7,
+		 BITWISE_AND: 8,
+		 EQ: 9, NE: 9, STRICT_EQ: 9, STRICT_NE: 9,
+		 LT: 10, LE: 10, GE: 10, GT: 10, IN: 10, INSTANCEOF: 10, IS: 10,
+		 LSH: 11, RSH: 11, URSH: 11,
+		 PLUS: 12, MINUS: 12,
+		 MUL: 13, DIV: 13, MOD: 13,
+		 DELETE: 14, VOID: 14, TYPEOF: 14, // PRE_INCREMENT: 14, PRE_DECREMENT: 14,
+		 NOT: 14, BITWISE_NOT: 14, UNARY_PLUS: 14, UNARY_MINUS: 14,
+		 INCREMENT: 15, DECREMENT: 15,     // postfix
+		 NEW: 16,
+		 YIELDING: 17,
+		 TRANSIENT_DOT: 18,
+		 DOT: 18,OBJECT_ID_REFERENCE: 19
+	 };
+	 
+	 // Map operator type code to precedence.
+	 // EDIT: slurp opPrecence items into array first, because IE includes
+	 //       modified hash items in iterator when modified during iteration
+	 var opPrecedenceItems = [];
+	 for (i in opPrecedence) 
+		opPrecedenceItems.push(i);
+	 
+	 for (var i = 0; i < opPrecedenceItems.length; i++) {
+		var item = opPrecedenceItems[i];
+		opPrecedence[eval(item)] = opPrecedence[item];
+	 }
+	
+	 var opArity = {
+		 COMMA: -2,
+		 ASSIGN: 2,
+		 CONDITIONAL: 3,
+		 OR: 2,
+		 AND: 2,
+		 BITWISE_OR: 2,
+		 BITWISE_XOR: 2,
+		 BITWISE_AND: 2,
+		 EQ: 2, NE: 2, STRICT_EQ: 2, STRICT_NE: 2,
+		 LT: 2, LE: 2, GE: 2, GT: 2, IN: 2, INSTANCEOF: 2, IS: 2,
+		 LSH: 2, RSH: 2, URSH: 2,
+		 PLUS: 2, MINUS: 2,
+		 MUL: 2, DIV: 2, MOD: 2,
+		 DELETE: 1, VOID: 1, TYPEOF: 1,  OBJECT_ID_REFERENCE: 1,// PRE_INCREMENT: 1, PRE_DECREMENT: 1,
+		 NOT: 1, BITWISE_NOT: 1, UNARY_PLUS: 1, UNARY_MINUS: 1,
+		 INCREMENT: 1, DECREMENT: 1,     // postfix
+		 NEW: 1, NEW_WITH_ARGS: 2, DOT: 2, TRANSIENT_DOT: 2, INDEX: 2, TRANSIENT_INDEX: 2, CALL: 2, YIELDING: 3,
+		 ARRAY_INIT: 1, OBJECT_INIT: 1, GROUP: 1
+	 };
+	 
+	 // Map operator type code to arity.
+	 // EDIT: same as above
+	 var opArityItems = [];
+	 for (i in opArity)
+		opArityItems.push(i);
+	 
+	 for (var i = 0; i < opArityItems.length; i++) {
+		var item = opArityItems[i];
+		opArity[eval(item)] = opArity[item];
+	 }
+	 
+	 function Expression(t, x, stop) {
+		 var n, id, tt, operators = [], operands = [];
+		 var bl = x.bracketLevel, cl = x.curlyLevel, pl = x.parenLevel,
+			 hl = x.hookLevel;
+	 
+		 function reduce() {
+			 var n = operators.pop();
+			 var op = n.type;
+			 var arity = opArity[op];
+			 if (arity == -2) {
+				 // Flatten left-associative trees.
+				 var left = operands.length >= 2 && operands[operands.length-2];
+				 if (left.type == op) {
+					 var right = operands.pop();
+					 left.push(right);
+					 return left;
+				 }
+				 arity = 2;
+			 }
+	 
+			 // Always use push to add operands to n, to update start and end.
+			 // EDIT: provide second argument to splice or IE won't work.
+			 var index = operands.length - arity;
+			 var a = operands.splice(index, operands.length - index);
+			 for (var i = 0; i < arity; i++)
+				 n.push(a[i]);
+	 
+			 // Include closing bracket or postfix operator in [start,end).
+			 if (n.end < t.token().end)
+				 n.end = t.token().end;
+
+			 operands.push(n);
+			 return n;
+		 }
+	 
+	 loop:
+		 while ((tt = t.get()) != END) {
+			 if (tt == stop &&
+				 x.bracketLevel == bl && x.curlyLevel == cl && x.parenLevel == pl &&
+				 x.hookLevel == hl) {
+				 // Stop only if tt matches the optional stop parameter, and that
+				 // token is not quoted by some kind of bracket.
+				 break;
+			 }
+			 switch (tt) {
+			   case SEMICOLON:
+				 // NB: cannot be empty, Statement handled that.
+				 break loop;
+	 
+			   case ASSIGN:
+			   case HOOK:
+			   case COLON:
+				 if (t.scanOperand)
+					 break loop;
+				 // Use >, not >=, for right-associative ASSIGN and HOOK/COLON.
+				 while (opPrecedence[operators.top().type] > opPrecedence[tt])
+					 reduce();
+				 if (tt == COLON) {
+					 n = operators.top();
+					 if (n.type != HOOK)
+						 throw t.newSyntaxError("Invalid label");
+					 n.type = CONDITIONAL;
+					 --x.hookLevel;
+				 } else {
+					 operators.push(Node(t));
+					 if (tt == ASSIGN)
+						 operands.top().assignOp = t.token().assignOp;
+					 else
+						 ++x.hookLevel;      // tt == HOOK
+				 }
+				 t.scanOperand = true;
+				 break;
+	 
+			   case IN:
+				 // An in operator should not be parsed if we're parsing the head of
+				 // a for (...) loop, unless it is in the then part of a conditional
+				 // expression, or parenthesized somehow.
+				 if (x.inForLoopInit && !x.hookLevel &&
+					 !x.bracketLevel && !x.curlyLevel && !x.parenLevel) {
+					 break loop;
+				 }
+				 // FALL THROUGH
+			   case COMMA:
+				 // Treat comma as left-associative so reduce can fold left-heavy
+				 // COMMA trees into a single array.
+				 // FALL THROUGH
+			   case OR:
+			   case AND:
+			   case BITWISE_OR:
+			   case BITWISE_XOR:
+			   case BITWISE_AND:
+			   case EQ: case NE: case STRICT_EQ: case STRICT_NE:
+			   case LT: case LE: case GE: case GT:
+			   case INSTANCEOF: case IS:
+			   case LSH: case RSH: case URSH:
+			   case PLUS: case MINUS:
+			   case MUL: case DIV: case MOD:
+			   case DOT: case TRANSIENT_DOT:
+				 if (t.scanOperand)
+					 break loop;
+				 while (opPrecedence[operators.top().type] >= opPrecedence[tt])
+					 reduce();
+				 if (tt == DOT) {
+					 t.mustMatch(IDENTIFIER);
+					 operands.push(Node(t, DOT, operands.pop(), Node(t)));
+				 } else if (tt == TRANSIENT_DOT) {
+					 t.mustMatch(IDENTIFIER);
+					 operands.push(Node(t, TRANSIENT_DOT, operands.pop(), Node(t)));
+				 } else {
+					 operators.push(Node(t));
+					 t.scanOperand = true;
+				 }
+				 break;
+	 
+			   case DELETE: case VOID: case TYPEOF:
+			   case NOT: case BITWISE_NOT: case UNARY_PLUS: case UNARY_MINUS: case OBJECT_ID_REFERENCE:
+			   case NEW:
+				 if (!t.scanOperand)
+					 break loop;
+				 operators.push(Node(t));
+				 break;
+	 
+			   case INCREMENT: case DECREMENT:
+				 if (t.scanOperand) {
+					 operators.push(Node(t));  // prefix increment or decrement
+				 } else {
+					 // Use >, not >=, so postfix has higher precedence than prefix.
+					 while (opPrecedence[operators.top().type] > opPrecedence[tt])
+						 reduce();
+					 n = Node(t, tt, operands.pop());
+					 n.postfix = true;
+					 operands.push(n);
+				 }
+				 break;
+	 
+			   case FUNCTION:
+				 if (!t.scanOperand)
+					 break loop;
+				 operands.push(FunctionDefinition(t, x, false, EXPRESSED_FORM));
+				 t.scanOperand = false;
+				 break;
+	 
+			   case NULL: case THIS: case TRUE: case FALSE:
+			   case IDENTIFIER: case NUMBER: case STRING: case REGEXP:
+				 if (!t.scanOperand)
+					 break loop;
+				 operands.push(Node(t));
+				 t.scanOperand = false;
+				 break;
+	 
+			   case LEFT_BRACKET:
+				 if (t.scanOperand) {
+					 // Array initialiser.  Parse using recursive descent, as the
+					 // sub-grammar here is not an operator grammar.
+					 n = Node(t, ARRAY_INIT);
+					 while ((tt = t.peek()) != RIGHT_BRACKET) {
+						 if (tt == COMMA) {
+							 t.get();
+							 n.push(null);
+							 continue;
+						 }
+						 n.push(Expression(t, x, COMMA));
+						 if (!t.match(COMMA))
+							 break;
+					 }
+					 t.mustMatch(RIGHT_BRACKET);
+					 operands.push(n);
+					 t.scanOperand = false;
+				 } else {
+					 // Property indexing operator.
+					 operators.push(Node(t, INDEX));
+					 t.scanOperand = true;
+					 ++x.bracketLevel;
+				 }
+				 break;
+	 		   case TRANSIENT_LEFT_BRACKET:
+				 // Property indexing operator.
+				 operators.push(new Node(t, TRANSIENT_INDEX));
+				 t.scanOperand = true;
+				 ++x.bracketLevel;
+				 break;	 		   
+			   case RIGHT_BRACKET:
+				 if (t.scanOperand || x.bracketLevel == bl)
+					 break loop;
+				 do {
+				 	var type = reduce().type;
+				 }
+				 while (type != INDEX && type != TRANSIENT_INDEX)
+				 --x.bracketLevel;
+				 break;
+	 
+			   case LEFT_CURLY:
+				 if (!t.scanOperand)
+					 break loop;
+				 // Object initialiser.  As for array initialisers (see above),
+				 // parse using recursive descent.
+				 ++x.curlyLevel;
+				 n = Node(t, OBJECT_INIT);
+			   object_init:
+				 if (!t.match(RIGHT_CURLY)) {
+					 do {
+						 tt = t.get();
+						 if ((t.token().value == "get" || t.token().value == "set") &&
+							 t.peek() == IDENTIFIER) {
+							 if (x.ecmaStrictMode)
+								 throw t.newSyntaxError("Illegal property accessor");
+							 n.push(FunctionDefinition(t, x, true, EXPRESSED_FORM));
+						 } else {
+							 switch (tt) {
+							   case IDENTIFIER:
+							   case NUMBER:
+							   case STRING:
+								 id = Node(t);
+								 break;
+							   case RIGHT_CURLY:
+								 if (x.ecmaStrictMode)
+									 throw t.newSyntaxError("Illegal trailing ,");
+								 break object_init;
+							   default:
+								 throw t.newSyntaxError("Invalid property name");
+							 }
+							 t.mustMatch(COLON);
+							 n.push(Node(t, PROPERTY_INIT, id,
+											 Expression(t, x, COMMA)));
+						 }
+					 } while (t.match(COMMA));
+					 t.mustMatch(RIGHT_CURLY);
+				 }
+				 operands.push(n);
+				 t.scanOperand = false;
+				 --x.curlyLevel;
+				 break;
+	 
+			   case RIGHT_CURLY:
+				 if (!t.scanOperand && x.curlyLevel != cl)
+					 throw "PANIC: right curly botch";
+				 break loop;
+	 
+			   case YIELDING:
+				 while (opPrecedence[operators.top().type] > opPrecedence[YIELDING])
+					 reduce();
+				 t.mustMatch(LEFT_PAREN);
+				 var yielding = true;
+				 // FALL THROUGH
+				 
+			   case LEFT_PAREN:
+				 if (t.scanOperand) {
+					 operators.push(Node(t, GROUP));
+				 } else {
+					 while (opPrecedence[operators.top().type] > opPrecedence[NEW])
+						 reduce();
+	 
+					 // Handle () now, to regularize the n-ary case for n > 0.
+					 // We must set scanOperand in case there are arguments and
+					 // the first one is a regexp or unary+/-.
+					 n = operators.top();
+					 t.scanOperand = true;
+					 if (t.match(RIGHT_PAREN)) {
+						 if (n.type == NEW) {
+						 	n.type = NEW_WITH_ARGS;
+							 --operators.length;
+							 n.push(operands.pop());
+						 } else {
+							 n = Node(t, CALL, operands.pop(),
+										  Node(t, LIST));
+						 }
+						 operands.push(n);
+						 t.scanOperand = false;
+						 n.yielding = yielding || false;
+						 break;
+					 }
+					 if (n.type == NEW) {
+						 n.type = NEW_WITH_ARGS;
+					 } else {
+						 n = Node(t, CALL);
+						 operators.push(n);
+					 }
+					 n.yielding = yielding || false;
+				 }
+				 ++x.parenLevel;
+				 break;
+	 
+			   case RIGHT_PAREN:
+				 if (t.scanOperand || x.parenLevel == pl)
+					 break loop;
+				 while ((tt = reduce().type) != GROUP && tt != CALL &&
+						tt != NEW_WITH_ARGS) {
+					 continue;
+				 }
+				 if (tt != GROUP) {
+					 n = operands.top();
+					 if (n[1].type != COMMA)
+						 n[1] = Node(t, LIST, n[1]);
+					 else
+						 n[1].type = LIST;
+				 }
+				 --x.parenLevel;
+				 break;
+	 
+			   // Automatic semicolon insertion means we may scan across a newline
+			   // and into the beginning of another statement.  If so, break out of
+			   // the while loop and let the t.scanOperand logic handle errors.
+			   default:
+				 break loop;
+			 }
+		 }
+	 
+		 if (x.hookLevel != hl)
+			 throw t.newSyntaxError("Missing : after ?");
+		 if (t.scanOperand)
+			 throw t.newSyntaxError("Missing operand");
+	 
+		 // Resume default mode, scanning for operands, not operators.
+		 t.scanOperand = true;
+		 t.unget();
+		 while (operators.length)
+			 reduce();
+		 return operands.pop();
+	 }
+	 
+	 function parse(s, f, l) {
+		 var t = new Tokenizer(s, f, l);
+		 var x = new CompilerContext(false);
+		 var n = Script(t, x);
+		 if (!t.done())
+			 throw t.newSyntaxError("Syntax error");
+		 return n;
+	 }
+	 
+	 // make stuff visible to StrandsCompiler
+	 this.parse      = parse;
+	 this.Node       = Node;
+	 this.tokens     = tokens;
+	 this.consts     = consts;
+	 
+}).call(Narcissus);
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Narrative JavaScript compiler.
+ *
+ * The Initial Developer of the Original Code is
+ * Neil Mix (neilmix -at- gmail -dot- com).
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+NJS_CODE = Narcissus.REGEXP; // fake node type for strands code segment
+
+// declare it this way so that when it is evaled it gets put in the global scope
+StrandsCompiler = function (options) {
+	this.nodeSequence = 0;
+	this.options = options || {};
+	this.parseBooleanOptions("exceptions", true);
+	this.parseBooleanOptions("defaultYield", true);
+}
+
+var strandscp = StrandsCompiler.prototype;
+
+strandscp.parseBooleanOptions = function (/*...*/) {
+	var options = this.options;
+	for (var i = 0; i < arguments.length; i += 2) {
+		var name = arguments[i];
+		var value = options[name];
+		if (value == null) {
+			options[name] = arguments[i+1];
+		} else {
+			if (typeof(value) == "string")
+				value = value.toLowerCase();
+	
+			options[name] = value == "yes" || value == "true" || value == "on" || value == "1";
+		}
+	}
+}
+
+strandscp.compile = function (/*string*/ code, /*string*/ scriptName,thisObject,scopeObject) {
+	if (!_frm)
+		var frame = {_cp:0};
+	else
+		var frame = _frm(this,arguments,[],[]);
+	if (!frame._cp) {
+		Narcissus.typeChecking = this.options.typeChecking;
+		frame.n = Narcissus.parse(code, scriptName, 1); 
+		this.treeify(frame.n);
+		var resolver = frame.resolver = new StrandsScopeResolver();
+		resolver.push(frame.n, false);
+		resolver.globalChecking = !(!thisObject);
+		resolver.thisObject = thisObject;
+		resolver.scopeObject = scopeObject || {};
+		frame._cp = 1;	
+	}
+	if (_frm) {
+		try {
+			var errors = strandscp.currentError = {};
+			frame.n = this.assemble(frame.n, frame.resolver, this.options);
+		} catch (e) {
+			if (strands && e == strands.Suspension) // this is a continuation escape, suspend so we can try again later
+				return frame._s();
+			throw e; // this is a real error
+		}
+	}
+	else {
+		var errors = strandscp.currentError = {};
+		frame.n = this.assemble(frame.n, frame.resolver, this.options);
+	}
+	if (errors.next)
+		throw errors.next;
+	var writer = new StrandsScriptWriter();
+	if (writer.debug = this.options.debug)
+		writer.sourceLines = code.split('\n');
+	writer.add(frame.n);
+	return writer.toString();
+}
+
+strandscp.treeify = function(n) {
+	if (n.type == Narcissus.SWITCH) {
+		// hack.  *sigh*  n.cases is an array, not a Node.  So we have
+		// to fool our treeification process into thinking this node
+		// has Node children.
+		for (var i = 0; i < n.cases.length; i++) {
+			n[i] = n.cases[i];
+		}
+		n.length = n.cases.length;
+	}
+	
+	if (n.type == Narcissus.TRY) {
+		// another hack.  catchClauses is a regular array.
+		for (var i = 0; i < n.catchClauses.length; i++) {
+			n["catch" + i] = n.catchClauses[i];
+		}
+	}
+	
+	// for-in constructs will use the identifier node within its varDecl
+	// as the iterator value, which means it may get operated on twice
+	// during the treeification process.
+	if( n.treeified )
+		return;
+
+	n.treeified = true;
+	if (this.options.defaultYield && (n.type==Narcissus.CALL || n.type==Narcissus.NEW || n.type==Narcissus.NEW_WITH_ARGS || n.type==Narcissus.INDEX || n.type==Narcissus.DOT))
+		n.yielding = !n.yielding;
+	for (var i in n) {
+		if (n[i] != null &&
+		    typeof(n[i]) == 'object' &&
+		    n[i].constructor == Narcissus.Node &&
+			i != "target" &&
+			i != "parent") 
+		{
+			var child = n[i];
+
+			// set this before treeification so that our parent is available
+			// in the post-treeification below.
+			child.parent = n;
+
+			this.treeify(child);
+			
+			if(child.yielding && child.type != Narcissus.FUNCTION)
+				n.yielding = true;
+		}
+	}
+
+	if (n.type == Narcissus.TRY) {
+		n = n.parent;
+		while (n != null && n.type != Narcissus.FUNCTION)
+			n = n.parent;
+		
+		if (n != null)
+			n.hasTry = true;
+	}
+}
+
+strandscp.noderize = function(opts) {
+	var n = new Array();
+	n.nodeID = this.nodeSequence++;
+	n.toString = Narcissus.Node.prototype.toString;
+	if (opts) {
+		for (var i in opts) {
+			n[i] = opts[i];
+		}
+	}
+	n.isNode = true;
+	return n;
+}
+
+strandscp.assemble = function(root, scopeResolver, options) {
+	function ObjectType(object,field) {		
+		this.object = object;
+		this.field = field;
+	}
+	ObjectType.prototype = {
+		getValue : function() {
+			if (this.value)
+				return this.value;
+			else if (this.object && this.field)
+				exceptingGet(this.object,this.field);
+		},
+		getType : function() {
+			if (this.object && this.field)
+				return exceptingGet(exceptingGet(this.object,"structure"), this.field);
+		}
+	}
+	// make consts names available to execute()
+	eval(Narcissus.consts);
+	// declare these locally so they can be used within closures
+	var noderize = StrandsCompiler.prototype.noderize; 
+	var assemble = StrandsCompiler.prototype.assemble;
+	
+	var codePtrSequence = 0;
+	var executed = [];
+	
+	var stack = [];
+	var exPtrStack = [];
+		
+	var statements = [];
+	while (root.length) {
+		tailFrame = null;
+		statements.push(execute(root.shift()));
+	}
+
+	for( var i = 0; i < statements.length; i++ ) {
+		if (statements[i])
+			root.push(statements[i]);
+	}
+	var lineno;
+	return root;
+	
+	function execute(node) {
+		if (node == null)
+			return null;
+
+		stack.push(node);
+		lineno = node.lineno;
+		switch(node.type) {
+		  case SCRIPT:
+			throw new Error("what's a script doing in a statement?");
+
+		  case FUNCTION:
+		  	if (scopeResolver.isYielding() && node.name) {
+		  		node.scoped = true;
+		  		node.name = scopeResolver.addSymbol(node.name,FUNCTION_TYPE);				
+		  	}
+			if (isYielding(node.body)) {
+				scopeResolver.push(node.body, true);
+				var vars = '';
+				var scopedVars = scopeResolver.scopes[scopeResolver.scopes.length-1];
+				for (var i in scopedVars) {
+					vars += ',"' + i + '"';
+				}
+				vars = vars.substring(1);
+				var params = '';
+				for (var i = 0; i < node.params.length; i++) {
+					params += ',"' + node.params[i].value + '"';
+					scopeResolver.addSymbol(node.params[i].value,node.params[i].varType||ANY_TYPE);
+				}
+				params = params.substring(1);
+				var openNodes = [
+					codeNode('var _scope=_frm(this,arguments,[~],[~]);with(_scope){', // we could make it more compact in non debug mode with: codeNode(options.debug?'var _scope=_frm(this,arguments,[~],[~]);with(_scope){':'with(_frm(this,arguments,[~],[~])){',
+				             params,vars),
+				    codeNode('while(1){'),
+				    (!options.exceptions ?
+						codeNode("$_noex=1;") :
+				        node.hasTry ?
+				          codeNode('try{') :
+				          codeNode('')),
+				    codeNode('switch(_cp){case 0:')
+				];
+
+				while (openNodes.length)
+					node.body.unshift(openNodes.pop());
+
+				node.body.push(codeNode('return;' + (options.persistent?'case -1:return _s()}':'}')));
+				if (options.exceptions && node.hasTry)
+					node.body.push(codeNode("}catch(ex){_s(ex)}"));
+				node.body.push(codeNode("}}"));
+
+				assemble(node.body, scopeResolver, options);
+				node.params = []; // get rid of the parameters so that the JS compression doesn't eliminate them.
+				scopeResolver.pop();
+			} else {
+				for (var i = 0; i < node.params.length; i++)  {
+					scopeResolver.addSymbol(node.params[i].value,node.params[i].varType||ANY_TYPE);
+					node.params[i] = node.params[i].value;
+				}
+				
+				scopeResolver.push(node.body, false);
+				assemble(node.body, scopeResolver, options);
+				scopeResolver.pop();
+			}
+			break;
+  		  case ASSIGN: case DELETE:
+	  		var propName = node[0].value;
+		  	if (options.persistence && node[0].type == DOT && withinYielding(node)) {
+		  		if (node.type == DELETE)
+		  			node[1] = noderize({
+			  					type: LIST,
+					  			0: node[0][0],
+			  					1: codeNode('"' + propName + '"')});
+		  		else {
+			  		node[1] = noderize({
+			  			type: LIST,
+			  			0: node[0][0],
+			  			1: codeNode('"' + propName + '"'),
+			  			2: node[1]});
+		  		}
+		  		node.type=CALL;
+		  		node[0] = codeNode("_p");
+		  	}
+		  	else 	if (options.persistence && node[0].type == INDEX && withinYielding(node)) {
+		  		var index = node[0][1];
+		  		if (node.type == DELETE)
+		  			node[1] = noderize({
+			  					type: LIST,
+					  			0: node[0][0],
+			  					1: index});
+		  		else {
+			  		node[1] = noderize({
+			  			type: LIST,
+			  			0: node[0][0],
+			  			1: index,
+			  			2: node[1]});
+				  	}
+		  		node.type=CALL;
+		  		node[0] = codeNode("_p");
+		  	}
+		  	else {
+				for (var i = 0; i < node.length; i++) {
+					node[i] = execute(node[i]);
+				}
+				if (node.type != DELETE)
+			  		typeCheck(node[0],node[1]);
+				break;
+		  	}
+		  	// fall through
+		  case NEW: case NEW_WITH_ARGS: case CALL:
+			// If it is persistent, we can go right to the transients on function calls
+			if (node[0].type == TRANSIENT_DOT)
+				node[0].type	 = DOT;
+			if (node[0].type == TRANSIENT_INDEX) 
+				node[0].type = INDEX;
+			
+			// execute our identifier and args *first*
+			node[0] = execute(node[0]);
+			if (node[0].value == "spawn" && node[0].type == IDENTIFIER) {
+				node[1].yielding = false;
+				break;
+			}
+			var varType;
+			if (node.type == NEW)
+				varType = node[0].varType;
+			if (!isYielding(node) || !withinYielding(node))
+				break;
+			if (node[1]) {// new (without args) doesn't have a list
+				if (node.type == NEW_WITH_ARGS)
+					node[1].type = ARRAY_INIT;
+				var newArgs = execute(node[1]);
+			}
+			
+			codePtrSequence++;
+			
+			// update our code pointer
+			addCode("_cp=~;",
+					codePtrSequence);
+
+			// set up our re-entry point
+			newCodeSegment(codePtrSequence);
+			if (node.type!=CALL) { // This is the persistent JavaScript extension to do news on objects
+				node = noderize({type: CALL,
+											yielding: true,
+											0 : codeNode("_new"),
+											1 : noderize({type: LIST,
+																  0:node[0]})
+				});
+				if (newArgs)
+					node[1][1] = newArgs;
+			}							 
+			// remove the call node from the stack,
+			// replace it with rv if necessary
+			if (stack.length > 1 && stack[stack.length-2].type == SEMICOLON) {
+				// simple semicolon expression.  don't bother
+				// with retval -- there aren't any dependencies
+				replaceNode(null);
+			} else {
+				var newNode = codeNode("_r.v~", 
+									 codePtrSequence);
+				newNode.varType = varType;
+				replaceNode(newNode);
+			}
+			
+			var checkRetNode = noderize({
+				type: IF,
+				condition: noderize({
+					type: STRICT_EQ,
+					0: noderize({
+						type: GROUP,
+						0: noderize({
+							type: ASSIGN,
+							0: codeNode("_r.v~",
+										codePtrSequence),
+							1: node
+						})
+					}),
+					1: codeNode("_S")
+				}),
+				thenPart: noderize({
+					type: SEMICOLON,
+					expression: codeNode("return _s()")
+				})
+			});
+			
+			// add our call as a statement at the top level
+			statements.push(checkRetNode);
+			
+			break;
+
+ 		  case IF:
+ 		  	node.condition = execute(node.condition);
+			if (isYielding(node.thenPart) ||
+				(node.elsePart && isYielding(node.elsePart)))
+			{				
+				var thenPtr = ++codePtrSequence;
+				if (node.elsePart)
+					var elsePtr = ++codePtrSequence;
+				var endPtr = ++codePtrSequence;
+				newConditional(node.condition, thenPtr, elsePtr || endPtr);
+				newCodeSegment(thenPtr);
+				
+				// thenPart
+				execBlock(node.thenPart);
+				gotoCodeSegment(endPtr);
+
+				// elsePart
+				if (node.elsePart) {
+					newCodeSegment(elsePtr);
+					execBlock(node.elsePart);
+					gotoCodeSegment(endPtr);
+				}
+				
+				// end if
+				newCodeSegment(endPtr);
+
+				replaceNode(null);
+			} else {
+				// make sure we catch any breaks or continues
+				node.thenPart = execute(node.thenPart);
+				if (node.elsePart)
+					node.elsePart = execute(node.elsePart);
+			}
+			break;
+
+  		  case FOR_IN: // varDecl/iterator, object, body
+			if (node.varDecl == null) {
+				node.iterator = execute(node.iterator);
+			} else {
+				node.varDecl = execute(node.varDecl);
+			}
+			node.object = execute(node.object);
+			
+/*			if (!isYielding(node.body)) { // we will always do it so they we can weed out dont-enums.
+				node.body = execute(node.body);
+				break;
+			}*/
+			
+			// grab all items from the object and stick them in a local array
+			var iterId = codePtrSequence;
+			statements.push(noderize({
+				type: NJS_CODE, 
+				value: subst("_r.iter~=_keys(", iterId),
+				lineno: node.object.lineno
+			}));
+			statements.push(node.object);
+			addCode(");");
+
+			// change the FOR_IN into a regular FOR
+			node.type = FOR;
+			node.setup = codeNode("_r.ctr~=0;", iterId);
+			node.condition = codeNode("_r.ctr~<_r.iter~.length",
+				                      iterId, iterId);
+			node.update = codeNode("_r.ctr~++", iterId);
+			var initializer = codeNode("_r.iter~[_r.ctr~]",
+				                        iterId, iterId);
+			 // make sure our body is a block so we can add a statement to it
+			if (node.body.type != BLOCK)
+				node.body = noderize({type: BLOCK, yielding: true, 0: node.body});
+
+			if (node.varDecl == null) {
+				// iterator -- create an assignment
+				var assign = noderize({type: ASSIGN});
+				assign.push(node.iterator);
+				assign.push(initializer);
+				node.body.unshift(noderize({type: SEMICOLON, expression: assign}));
+			} else {
+				// varDecl -- use the initializer
+				node.varDecl[0].initializer = initializer;
+				node.body.unshift(node.varDecl);
+			}
+			node.iterator = null;
+			node.varDecl = null;
+			// FALL THROUGH
+			
+		  case FOR:
+			node.setup = execute(node.setup);
+			if (!isYielding(node.body) && !isYielding(node.update))  {
+				node.condition = execute(node.condition);
+				node.update    = execute(node.update);
+				node.body      = execute(node.body);
+				break;
+			}
+			
+			// turn it into a WHILE statement
+			node.type = WHILE;
+			
+			// move the setup before the while
+			if(node.setup.type != VAR  && node.setup.type != NJS_CODE)
+				node.setup = noderize({type: SEMICOLON, expression: node.setup});
+
+			statements.push(node.setup);
+			node.setup = null;
+
+			// make sure our body is a block so we can add a statement to it
+			if (node.body.type != BLOCK)
+				node.body = noderize({type: BLOCK, 0: node.body});
+
+			node.updatePtr = ++codePtrSequence;
+			node.body.push(newCodeSegmentNode(node.updatePtr));
+			
+			// make sure the proper update happens in the block
+			node.body.push(noderize({type: SEMICOLON, expression: node.update}));
+			node.update = null; 		  		
+
+			// FALL THROUGH
+
+ 		  case WHILE:
+ 		  	if (isYielding(node)) {
+ 		  		node.continuePtr = ++codePtrSequence;
+ 		  		newCodeSegment(node.continuePtr);
+ 		  		node.condition = execute(node.condition);
+
+ 		  		var bodyPtr  = ++codePtrSequence;
+ 		  		node.breakPtr = ++codePtrSequence;
+ 		  		newConditional(node.condition, bodyPtr, node.breakPtr);
+ 		  		newCodeSegment(bodyPtr);
+ 		  		execBlock(node.body);
+ 		  		gotoCodeSegment(node.continuePtr);
+ 		  		newCodeSegment(node.breakPtr);
+
+				replaceNode(null);
+ 		  	} else {
+ 		  		node.condition = execute(node.condition);
+ 		  		node.body = execute(node.body);
+ 		  	}
+ 		  	break;
+ 		  
+ 		  case DO:
+ 		  	if (isYielding(node)) {
+ 		  		node.continuePtr = ++codePtrSequence;
+				node.breakPtr = ++codePtrSequence;
+ 		  		newCodeSegment(node.continuePtr);
+ 		  		execBlock(node.body);
+
+ 		  		newConditional(execute(node.condition), node.continuePtr, node.breakPtr);
+ 		  		newCodeSegment(node.breakPtr);
+
+ 		  		replaceNode(null);
+ 		  	} else {
+ 		  		node.condition = execute(node.condition);
+ 		  		node.body = execute(node.body);
+ 		  	}
+ 		    break;
+		  
+		  case BREAK:
+			if (node.target.breakPtr != null) {
+				replaceNode(codeNode("_cp=~;break;", 
+					                 node.target.breakPtr));
+			}
+			break;
+		  	
+		  case CONTINUE:
+			if (node.target.continuePtr != null) {
+				replaceNode(codeNode("_cp=~;break;", 
+					                 node.target.updatePtr || node.target.continuePtr));
+			}
+			break;
+		  
+		  case SWITCH:
+			if (!isYielding(node))
+				break;
+			
+			node.breakPtr = ++codePtrSequence;
+			var conditional = null;
+			if (node.defaultIndex >= 0) {
+				node[node.defaultIndex].codePtr = ++codePtrSequence;
+				conditional = codeNode(node[node.defaultIndex].codePtr);
+			} else {
+				conditional = codeNode(node.breakPtr);
+			}
+			
+			for (var i = node.length - 1; i >= 0; i--) {
+				if (i == node.defaultIndex)
+					continue;
+				
+				// adjust the line numbering of the case label nodes
+				removeLineNumbers(node[i].caseLabel);
+				
+				node[i].codePtr = ++codePtrSequence;
+				conditional = noderize({
+					type: CONDITIONAL,
+					0: noderize({
+						type: EQ,
+						0: node.discriminant,
+						1: node[i].caseLabel
+					}),
+					1: codeNode(node[i].codePtr),
+					2: conditional
+				});
+			}
+			
+			statements.push(noderize({
+				type: SEMICOLON,
+				expression: noderize({
+					type: ASSIGN,
+					0: codeNode("_cp"),
+					1: execute(conditional)
+				})
+			}));
+			statements.push(codeNode("break;"));
+			
+			for (var i = 0; i < node.length; i++) {
+				newCodeSegment(node[i].codePtr);
+				execBlock(node[i].statements);
+			}
+			
+			newCodeSegment(node.breakPtr);
+			
+			replaceNode(null);
+			break;
+			
+		  case WITH:
+		    if (isYielding(node)) {
+		  		//throw new Error("yielding within " + Narcissus.tokens[node.type].toUpperCase() + " not supported");
+		    	var oldStatements = statements;
+		    	var innerStatements = statements = [];	    	
+		    	node.body.unshift(codeNode('switch(_cp){case ~:',codePtrSequence));
+		    	var body = execute(node.body);
+		    	for (var i = 0; i < body.length; i++)
+		    		if (!body[i]) {
+		    			body.splice(i--,1);
+		    		}
+		    	statements = oldStatements;
+		    	
+	    		for( var i = 0; i < innerStatements.length; i++ ) {
+					if (innerStatements[i]) {
+						if (innerStatements[i].codeSegmentId)
+							newCodeSegment(innerStatements[i].codeSegmentId);							
+						node.body.push(innerStatements[i]);
+					}
+				}
+		    	codePtrSequence++;
+		    	node.body.push(codeNode('_cp=~}break;',codePtrSequence));
+		    	statements.push(node);
+		    	replaceNode(null);
+		    	newCodeSegment(codePtrSequence);
+		    }
+			break;
+
+		  case TRY:
+		 	if (!isYielding(node))
+		 		break;
+		 	
+		 	if (!options.exceptions)
+		 		throw new Error("yielding within try/catch/finally not allowed when the exceptions are turned off in the compiler");
+
+			//   set codeptr for catches, finally, endptr
+			for (var i = 0; i < node.catchClauses.length; i++) {
+				node.catchClauses[i].codePtr = ++codePtrSequence;
+			}
+			
+			if (node.finallyBlock)
+				node.finallyBlock.codePtr = ++codePtrSequence;
+
+			var endPtr = ++codePtrSequence;
+			
+			// set exception codePtr
+			var exCodePtr = node.catchClauses.length ?
+			                node.catchClauses[0].codePtr :
+			                node.finallyBlock.codePtr;
+			
+			addCode("_r.ecp=~;", exCodePtr);
+			exPtrStack.push(exCodePtr);
+			execBlock(node.tryBlock);
+			node.finallyBlock ? gotoCodeSegment(node.finallyBlock.codePtr) :
+			                    gotoCodeSegment(endPtr);
+			exPtrStack.pop();
+
+			for (var i = 0; i < node.catchClauses.length; i++) {
+				var clause = node.catchClauses[i];
+				newCodeSegment(clause.codePtr);
+
+				if (i == 0) {
+					// first catch block
+					// set exception codePtr appropriately
+					addCode("_r.ecp=~;", 
+						                node.finallyBlock ? 
+						                node.finallyBlock.codePtr :
+						                (exPtrStack.top() || "null"));
+					// reset throwing flag to prevent infinite loopage
+					addCode("$_thr=false;");
+				}
+
+				// set our exception var.  This will override any masked
+				// variables with the same name.  Technically this is
+				// incorrect behavior.  I should fix this, but I'm too 
+				// lazy right now.
+				scopeResolver.addSymbol(clause.varName,ANY_TYPE)
+				addCode("~ = $_ex;", scopeResolver.getSymbol(clause.varName));
+
+				if (clause.guard) {
+					clause.guard = execute(clause.guard);
+					statements.push(noderize({
+						type: NJS_CODE,
+						value: "if(!(",
+						lineno: clause.guard.lineno
+					}));
+					statements.push(clause.guard);
+					addCode(")) {");
+
+					// handle missed guard clause carefully						
+					if (i < node.catchClauses.length - 1) {
+						gotoCodeSegment(node.catchClauses[i+1].codePtr);
+					} else if (node.finallyBlock) {
+						gotoCodeSegment(node.finallyBlock.codePtr);
+					} else if (exPtrStack.length) {
+						gotoCodeSegment(exPtrStack.top());
+					} else {
+						addCode("throw ~;", scopeResolver.getSymbol(clause.varName));
+					}
+					
+					addCode("}");
+				}
+			
+				if (node.finallyBlock)
+					exPtrStack.push(node.finallyBlock.codePtr);
+				execBlock(clause.block);
+				if (node.finallyBlock)
+					exPtrStack.pop();
+				
+				// handle successful execution of catch clause
+				if (node.finallyBlock) {
+					gotoCodeSegment(node.finallyBlock.codePtr);
+				} else {
+					gotoCodeSegment(endPtr);
+				}
+			}
+			
+			if (node.finallyBlock) {
+				newCodeSegment(node.finallyBlock.codePtr);
+
+				// set the exception code pointer
+				addCode("_r.ecp=~;", 
+								    exPtrStack.top() || "null");
+
+				execBlock(node.finallyBlock);
+
+				// if we're throwing, rethrow, otherwise goto endPtr
+				addCode("if($_thr){");
+				if (exPtrStack.length) {
+					gotoCodeSegment(exPtrStack.top());
+				} else {
+					addCode("$_except($_ex);return;");
+				}
+				addCode("}else{");
+				gotoCodeSegment(endPtr);
+				addCode("}");
+			}
+
+			newCodeSegment(endPtr);
+			
+			replaceNode(null);
+			break;
+		  case TRUE: case FALSE: node.varType = "Boolean"; break;
+		  case NUMBER: 
+		  	node.varType = "Number"; 
+		  break;
+		  case STRING: 
+		  	node.varType = "String";
+		  	break;
+			
+		  case THIS: 
+  		  	node.varType = new ObjectType();
+  		  	node.varType.value = scopeResolver.thisObject;
+  		  	node.varType.type = scopeResolver.thisObject;
+		  case DEBUGGER: case LABEL: case NULL:
+		  case REGEXP: case NJS_CODE:
+			// nothing to do
+			break;
+		
+		  case IDENTIFIER:
+			node.value = scopeResolver.getSymbol(node.value);
+		    node.initializer = execute(node.initializer);
+		    if (node.initializer)
+			    typeCheck(node,node.initializer);
+		  	break;
+			
+		  case THROW:
+			node.exception = execute(node.exception);
+			break;
+ 
+		  case RETURN:
+		  case SEMICOLON:
+			node.expression = execute(node.expression);
+			break;
+		  
+		  case OR: case AND:
+		  	// because of the "guarding" nature of boolean comparisons, we need to
+		  	// pull out comparisons with right-side yields into their own
+		  	// statements and transform them separately.
+		  	var left = node[0];
+		  	var right = node[1];
+		  	
+		  	node[0] = left = execute(left);
+		  	if (!isYielding(right)) {
+		  		node[1] = execute(right);
+		  		break;
+		  	}
+
+			var condVar = "_c" + codePtrSequence;
+			
+			// put the left in it's own assign statement
+			statements.push(noderize({
+				type: SEMICOLON,
+				expression: noderize({
+					type: ASSIGN,
+					0: codeNode("var ~", condVar),
+					1: left
+				})
+			}));
+			
+			// create a boolean node that indicates whether or not the left guards
+			// against execution of the right
+			var cond = codeNode(condVar);
+			if (node.type == OR) {
+				cond = noderize({
+					type: NOT,
+					value: "!",
+					0: cond
+				});
+			}
+			
+			// create an if node that checks the guarded value and executes
+			// the right if appropriate
+			var guard = noderize({
+				type: IF,
+				condition: cond,
+				thenPart: noderize({
+					type: SEMICOLON,
+					expression: noderize({
+						type: ASSIGN,
+						0: codeNode(condVar),
+						1: right,
+						yielding: true
+					}),
+					parent: node,
+					yielding: true
+				}),
+				yielding: true
+			});
+			// execute the if node as if it were top-level
+			var tmpStack = stack;
+			stack = [];
+			statements.push(execute(guard));
+			stack = tmpStack;
+			
+			// finally, hand back the result of the guarding process
+			node.type = NJS_CODE;
+			node.value = condVar;
+		  	break;
+		  case VAR:
+		  	if(scopeResolver.isYielding())
+		  		node.scoped = true;
+  			for (var i = 0; i < node.length; i++) {
+				node[i] = execute(node[i]);
+			}
+			break;
+		  
+
+		  case EQ: case NE: case STRICT_EQ: case STRICT_NE:
+		  case LT: case LE: case GE: case GT:
+		  case TYPEOF: case NOT: case INSTANCEOF:
+		  	node.varType = "Boolean";
+  			for (var i = 0; i < node.length; i++) {
+				node[i] = execute(node[i]);
+			}
+			break;
+		  case UNARY_PLUS: case UNARY_MINUS: case INCREMENT: case DECREMENT:
+		  case LSH: case RSH: case URSH:
+		  case MINUS: case MUL: case DIV: case MOD:
+		  	node.varType = "Number";
+  			for (var i = 0; i < node.length; i++) {
+				node[i] = execute(node[i]); // should do a type check here
+			}
+			break;
+		  case ARRAY_INIT: case OBJECT_INIT:
+		  	node.varType = "Object";
+  			for (var i = 0; i < node.length; i++) {
+				node[i] = execute(node[i]);
+			}
+			break;
+		  
+		  case PLUS: // TODO: This is pretty complicate type case
+  			for (var i = 0; i < node.length; i++) {
+				node[i] = execute(node[i]); // should do a type check here
+			}
+		  	if (node[0].varType == "Number" && node[1].varType == "Number")
+		  		node.varType = "Number";
+		  	if (node[0].varType == "String" || node[1].varType == "String")
+		  		node.varType = "String";
+			break;
+		  case CONDITIONAL:
+		  	// pull out comparisons with right-side yields into their own
+		  	// statements and transform them separately.
+		  	var cond = node[0];
+		  	var left = node[1];
+		  	var right = node[2];
+					  	
+		  	node[0] = cond = execute(cond);
+		  	if (!isYielding(left) && !isYielding(right)) {
+		  		node[1] = execute(left);
+		  		node[2] = execute(right);
+		  		break;
+		  	}
+
+			var condVar = "_c" + codePtrSequence;
+			
+			// put the left in it's own assign statement
+			statements.push(noderize({
+				type: SEMICOLON,
+				expression: codeNode("var ~", condVar)
+			}));
+			
+			// create an if node that checks the guarded value and executes
+			// the right if appropriate
+			var guard = noderize({
+				type: IF,
+				condition: cond,
+				thenPart: noderize({
+					type: SEMICOLON,
+					expression: noderize({
+						type: ASSIGN,
+						0: codeNode(condVar),
+						1: left,
+						yielding: true
+					}),
+					parent: node,
+					yielding: true
+				}),
+				elsePart: noderize({
+					type: SEMICOLON,
+					expression: noderize({
+						type: ASSIGN,
+						0: codeNode(condVar),
+						1: right,
+						yielding: true
+					}),
+					parent: node,
+					yielding: true
+				}),
+				yielding: true
+			});
+			// execute the if node as if it were top-level
+			var tmpStack = stack;
+			stack = [];
+			statements.push(execute(guard));
+			stack = tmpStack;
+			
+			// finally, hand back the result of the guarding process
+			node.type = NJS_CODE;
+			node.value = condVar;
+		  	break;
+		  case BITWISE_OR: case BITWISE_XOR: case BITWISE_AND:
+		  case BITWISE_NOT:
+		  case VOID: 		  
+		  case IN: 
+		  case COMMA: 
+		  case LIST:
+		  case GROUP: case BLOCK:
+			for (var i = 0; i < node.length; i++) {
+				node[i] = execute(node[i]);
+			}
+			break;
+		  case OBJECT_ID_REFERENCE:
+		  	var get = noderize({
+						type: CALL,
+						parent: node.parent,
+						yielding: true,
+						0: codeNode("_ref"),
+						1: noderize({
+							type:LIST,
+							0:node[0]})});
+		  	get = execute(get);
+		  	node.type = get.type;
+		  	node.value = get.value;
+		  	node.varType = new ObjectType(scopeResolver.scopeObject,node[0].value);
+		  	break;
+		  case IS:
+		  	node.type = CALL;
+		  	node[1] = execute(noderize({type: LIST,
+		  													0: node[0],
+		  													1: node[1]}));
+		  	node[0] = codeNode("_is");
+		  	break;
+		  case INDEX: 
+		  	if (options.persistence && withinYielding(node) && node.parent.type != CALL) {
+			  	var get = noderize({
+							type: CALL,
+							parent: node.parent,
+							yielding: true,
+							0: codeNode("_g"),
+							1: noderize({
+								type:LIST,
+								0:node[0],
+								1:node[1]})});
+			  	get = execute(get);
+			  	node.type = get.type;
+			  	node.value = get.value;
+			  	break;
+			}
+			// else fall through
+		  case TRANSIENT_INDEX:
+				for (var i = 0; i < node.length; i++) {
+					node[i] = execute(node[i]);
+				}
+				node.type = INDEX;
+				break;
+		  case DOT:
+			if (options.persistence && withinYielding(node) && node.parent.type != CALL) {			// don't execute n[1] because it might resolve to a scoped var		
+			  	var get = noderize({
+							type: CALL,
+							parent: node.parent,
+							yielding: true,
+							0: codeNode("_g"),
+							1: noderize({
+								type:LIST,
+								0:node[0],
+								1:codeNode('"' + node[1].value + '"')})});
+			  	get = execute(get);
+			  	node.type = get.type;
+			  	node.value = get.value;
+			  	if (typeof node[0].varType == "Object")
+			  		node.varType = new ObjectType(node[0].varType.getValue(),node[1].value);
+			  	break;
+			}
+			// else fall through to TRANSIENT_DOT
+		  case TRANSIENT_DOT:
+//		  		if (!options.persistence)
+	//				throw new Error("direct reference syntax (.#) not supported without persevere");
+			  	node[0] = execute(node[0]);
+				node.type = DOT;
+		  		break;
+
+		  case PROPERTY_INIT:
+			// don't execute n[0] because it might resolve to a scoped var
+		  	node[1] = execute(node[1]);
+		  	break;
+
+		  default:
+			throw new Error("PANIC: unknown node type " + Narcissus.tokens[node.type]);
+		}
+		
+		return stack.pop();
+	}
+	
+	function subst(str /*, ... */) {
+		for(var i = 1; i < arguments.length; i++) {
+			str = str.replace("~", arguments[i]);
+		}
+		return str;
+	}
+
+	function replaceNode(node) {
+		stack.pop();
+		stack.push(node);
+	}
+	
+	function execBlock(set) {
+		if (set.type == BLOCK) {
+
+			for (var i = 0; i < set.length; i++) {
+				statements.push(execute(set[i]));
+			}
+			return set;
+		} else {
+			set = execute(set);
+			statements.push(set);
+			return set;
+		}
+	}
+	
+	function newCodeSegment(id) {
+		var newNode = newCodeSegmentNode(id);
+		newNode.codeSegmentId = id;
+		statements.push(newNode);
+	}
+	
+	function newCodeSegmentNode(id) {
+		return codeNode("case ~:", id)
+	}
+	
+	function gotoCodeSegment(id) {
+		addCode("_cp=~;break;",id);
+	}
+
+	function newConditional(node, thenPtr, elsePtr) {
+		// turn the if(cond) into something like:
+		//   njf0.cp = (cond) ? 1 : 2; break; case 1:
+		statements.push(noderize({
+			type: NJS_CODE,
+			value: subst("_cp=("),
+			lineno: node.lineno
+		}));
+		statements.push(node);
+		addCode(")?~:~;break;", thenPtr, elsePtr);
+	}
+	
+	function addCode(str/*, ...*/) {
+		statements.push(codeNode.apply(this, arguments));
+	}
+	
+	function codeNode(str/*, ...*/) {
+		return noderize({
+			type: NJS_CODE,
+			value: subst.apply(this, arguments)
+		})
+	}
+
+	function isYielding(node) {
+		return node != null && node.yielding;
+	}
+	function withinYielding(node) {
+		var parentNode = node;
+		while (parentNode) {
+			if (parentNode.type == FUNCTION)
+				return parentNode.yielding;
+			parentNode = parentNode.parent;
+		}
+		return false;
+	}
+		
+	function removeLineNumbers(node) {
+		delete node.lineno;
+		for (n in node) {
+			if (node[n] != null 
+			    && typeof(node[n]) == "object" 
+			    && n != "parent"
+			    && node[n].isNode) 
+			{
+				removeLineNumbers(node[n]);
+			}
+		}
+	}
+	function exceptingGet(object,field) {
+		var value = pjs.get(object,field);
+		if (value == strands.Suspension)
+			throw value;
+		return value;
+	}
+	function typeCheck(variable, value) {
+		if (!options.typeChecking)
+			return value;
+		var valueType = value.varType;
+		if (typeof valueType == "object") // we allow varTypes to be just strings but usually they are objects
+			valueType = valueType.type;
+		if (!valueType)
+			valueType = scopeResolver.getType(value.value);
+		if (!valueType)
+			valueType = ANY_TYPE;
+		if (!variable || variable == ANY_TYPE)
+			return value;
+		if (typeof variable != "string")
+			var variableType =scopeResolver.getType(variable.value);
+		if (variableType && variableType != valueType && variableType != ANY_TYPE)
+			addError("Can not assign a value of type " + valueType + " when a " + variableType + " is required");
+		if (value.varType) {
+			variableType = scopeResolver.getSymbolObject(variable.value);
+			if (variableType)
+				variableType.value = value.varType.value;
+		}
+		return value;
+	}
+	function addError(message) {
+		strandscp.currentError = strandscp.currentError.next ={message:message, lineNumber:lineno || 0};
+	}
+}
+var FUNCTION_TYPE = {value:"function"};
+var ANY_TYPE = {toString:function() {return "any"}};	
+
+
+function StrandsScopeResolver() {
+	this.scopes = [];
+	this.yieldingStatus = [];
+}
+
+var nsrp = StrandsScopeResolver.prototype;
+nsrp.addError = function(message) {
+	strandscp.currentError = strandscp.currentError.next ={message:message, lineNumber:lineno || 0};
+}
+nsrp.push = function(n, isYielding) {
+	this.scopes.push({});
+	this.yieldingStatus.push(isYielding);
+	if(n.varDecls) {
+		for(var i = 0; i < n.varDecls.length; i++) {
+			this.addSymbol(n.varDecls[i].value,n.varDecls[i].varType||ANY_TYPE);
+		}
+	}
+	if(n.funDecls) {
+		for(var i = 0; i < n.funDecls.length; i++) {
+			this.addSymbol(n.funDecls[i].name,FUNCTION_TYPE);
+		}
+	}
+}
+nsrp.pop = function() {
+	this.yieldingStatus.pop();
+	return this.scopes.pop();
+}
+
+// we need to namespace all symbols so that we don't
+// accidentally run across native object members
+// (such as "constructor")
+nsrp.addSymbol = function(name,type) {
+	this.scopes.top()[name] = {type:type.value};
+	return this.getSymbol(name);
+}
+nsrp.getType = function(name) {
+	var object = this.getSymbolObject(name);
+	if (object && object.type=="any")
+		return ANY_TYPE;
+	if (object && typeof object.type=="string" && object.type != "String" && object.type != "Number" && object.type != "Boolean"&& object.type != "Object" && object.type != "function") {
+		var symbolObject = this.getSymbolObject(object.type);
+		if (!symbolObject || !symbolObject.value) 
+			this.addError("You must use a known symbol with a known value as a type",lineno);
+		object.type = this.getSymbolObject(object.type).value;
+	}
+	if (object && object.type)
+		return object.type;
+	return ANY_TYPE;
+}
+nsrp.getSymbolObject = function(name) {
+	for (var i = this.scopes.length; i > 0;) {
+		i--;
+		if (this.scopes[i][name]) 
+			return this.scopes[i][name];
+	}
+	return;
+}
+
+nsrp.getSymbol = function(name) {
+	if(!this.scopes.top()[name] && Object.prototype[name])
+		throw new Exception("You can not reference a variable in a outer scope (or global scope) with a name from Object.prototype.  Please rename your variable in order to reference it");
+	if (!this.globalChecking || window[name] || this.scopeObject[name])
+		return name;
+	for (var i = this.scopes.length; i > 0;) {
+		i--;
+		if (this.scopes[i][name]) 
+			return name;
+	}
+	this.addError("The identifier " + name + " was not found");	
+	return name;
+}
+nsrp.getCurrentFrame = function() {
+	var id = this.scopes.length - 1;
+	if (id < 0) {
+		throw new Error("compiler error: empty scope resolver");
+	}
+	return "njf" + id;
+}
+nsrp.isYielding = function() {
+	if(this.scopes.length == 0)
+		return false;
+	
+	return this.yieldingStatus.top();
+}
+
+nsrp.dump = function() {
+	for (var i= this.scopes.length - 1; i >= 0; i--) {
+		var list = "frame " + i + ": ";
+		for (var n in this.scopes[i]) 
+			list += n + ", ";
+
+		print(list);
+	}
+}
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Narrative JavaScript compiler.
+ *
+ * The Initial Developer of the Original Code is
+ * Neil Mix (neilmix -at- gmail -dot- com).
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+function StrandsScriptWriter() {
+	this.lines = [];
+	this.infix_lookup = {};
+	for( var i = 0; i < this.infix_operators.length; i++ ) {
+		this.infix_lookup[this.infix_operators[i]] = true;
+	}
+	this.prefix_lookup = {};
+	for( var i = 0; i < this.prefix_operators.length; i++ ) {
+		this.prefix_lookup[this.prefix_operators[i]] = true;
+	}
+	this.simple_lookup = {};
+	for( var i = 0; i < this.simple_tokens.length; i++ ) {
+		this.simple_lookup[this.simple_tokens[i]] = true;
+	}
+}
+
+StrandsScriptWriter.dump = function(n) {
+	var o = new StrandsScriptWriter();
+	o.add(n);
+	print(o);
+}
+
+var strandsswp = StrandsScriptWriter.prototype;
+
+strandsswp.infix_operators = [
+	 ',',
+	 '||',
+	 '&&',
+	 '|',
+	 '^',
+	 '&',
+	 '===',
+	 '==',
+	 '!==',
+	 '!=',
+	 '<<',
+	 '<=',
+	 '<',
+	 '>>>',
+	 '>>',
+	 '>=',
+	 '>',
+	 '-',
+	 '+',
+	 '*',
+	 '/',
+	 '%',
+	 '.',
+	 '.#',
+	 '='
+];
+
+strandsswp.prefix_operators = [
+	'!',
+	'~',
+	'unary_plus',
+	'unary_minus'
+];
+
+strandsswp.simple_tokens = [
+	"identifier",
+	"number",
+	"regexp",
+	"true",
+	"false",
+	"null",
+	"this"
+];
+
+strandsswp.add = function(n) {
+	if( n == null ) 
+	throw new Error("null token");
+	if( arguments.length > 1 ) throw new Error("too many args");
+	if( Narcissus.tokens[n.type] == null ) throw new Error("not a valid token: " + n);
+	var type = Narcissus.tokens[n.type].toLowerCase();
+	var method = "write_" + type;
+	if( this[method] ) {
+		this[method](n);
+	} else if( this.infix_lookup[type] ) {
+		this.write_infix_operator(n);
+	} else if( this.prefix_lookup[type] ) {
+		this.write_prefix_operator(n);
+	} else if( this.simple_lookup[type] ) {
+		this.write(n, n.value);
+	} else {
+		throw new Error("ScriptWriter Error: unknown type: " + Narcissus.tokens[n.type]);
+	}
+}
+
+strandsswp.addBlock = function(n) {
+	// the compiler can rewrite single statements into multiple statements
+	// therefore, we should put brackets around single statements to be safe.
+	if(n.type == Narcissus.BLOCK) {
+		this.add(n);
+	} else {
+		this.write(n, "{");
+		this.add(n);
+		this.write(null, "}");
+	}
+}
+
+strandsswp.write = function(n, text) {
+	if (text == null) 
+		throw new Error("null text: " + n);
+	var lineno = n && n.lineno >= this.lines.length ? n.lineno : this.lines.length - 1;
+	var line = this.lines[lineno] || [];
+	line.push(text);
+	this.lines[lineno] = line;
+}
+
+strandsswp.last = function() {
+	return this.lines.top().top();
+}
+
+strandsswp.pop = function() {
+	return this.lines.top().pop();
+}
+
+strandsswp.toString = function() {
+	var output = [];
+	// Note: line numbers start at 1
+	for( var i = 1; i < this.lines.length+1; i++ ) {
+		if( this.lines[i] != null ) {
+			if (this.debug && this.sourceLines[i-1])
+				output.push("/*" + this.sourceLines[i-1].replace(/\*\//g,'* ') + "\t\t\t\t\t\t*/");
+			for( var j = 0; j < this.lines[i].length; j++ ) {
+				output.push(this.lines[i][j]);
+			}
+		}
+		else {
+			if (this.debug && this.sourceLines[i-1])
+				output.push("/*" + this.sourceLines[i-1].replace(/\*\//g,'* ') + "\t\t\t\t\t\t*/");
+		}
+		output.push("\n");
+	}
+	return output.join("");
+}
+
+strandsswp.write_script = function(n,output) {
+	for (var i = 0; i < n.length; i++) {
+		this.add(n[i]);
+	}
+}
+
+strandsswp.write_infix_operator = function(n) {
+	this.add(n[0]);
+	if (n.type == Narcissus.ASSIGN && n[0].assignOp != null)
+		this.write(n, Narcissus.tokens[n[0].assignOp]);
+	this.write(n, Narcissus.tokens[n.type]); // don't use n.value -- that's incorrect for DOT
+	this.add(n[1]);
+}
+
+strandsswp.write_prefix_operator = function(n) {
+	this.write(n, n.value);
+	this.add(n[0]);
+}
+
+strandsswp.write_function = function(n) {
+	if(n.scoped) {
+		this.write(n, n.name);
+		this.write(n, " = ");
+	}
+	this.write(n, "function");
+	if(n.name && !n.scoped) {
+		this.write(n, " ");
+		this.write(n, n.name);
+	}
+	this.write(n, "(");
+	for (var i = 0; i < n.params.length; i++)
+		if (n.params[i].value)
+			n.params[i] = n.params[i].value;
+	this.write(n, n.params);
+	this.write(null, "){");
+	this.add(n.body);
+	this.write(null, "}");
+	if(n.scoped) {
+		this.write(null, ";");
+	}
+}
+
+strandsswp.write_var = function(n) {
+	if(!n.scoped) this.write(n, "var ");
+	for( var i = 0; i < n.length; i++ ) {
+		this.write(n[i], n[i].value);
+		if( n[i].initializer ) {
+			this.write(n[i], "=");
+			this.add(n[i].initializer);
+		}
+		if( i == n.length - 1 ) {
+			this.write(null, ";");
+		} else {
+			this.write(n[i], ",");
+		}
+	}
+}
+
+strandsswp["write_;"] = function(n) {
+	if(!n.expression) 
+		return;
+	this.add(n.expression);
+	this.write(null, ";");
+}
+
+
+strandsswp.write_conditional = function(n) {
+	this.add(n[0]);
+	this.write(null, "?");
+	this.add(n[1]);
+	this.write(null, ":");
+	this.add(n[2]);
+}
+
+strandsswp["write_++"] = function(n) {
+	if( n.postfix ) {
+		this.add(n[0]);
+		this.write(n, "++");
+	} else {
+		this.write(n, "++");
+		this.add(n[0]);
+	}
+}
+
+strandsswp["write_--"] = function(n) {
+	if( n.postfix ) {
+		this.add(n[0]);
+		this.write(n, "--");
+	} else {
+		this.write(n, "--");
+		this.add(n[0]);
+	}
+}
+
+strandsswp.write_index = function(n) {
+	this.add(n[0]);
+	this.write(null, '[');
+	this.add(n[1]);
+	this.write(null, ']');
+}
+
+strandsswp.write_array_init = function(n) {
+	this.write(n, '[');
+	for( var i = 0; i < n.length; i++ ) {
+		if (i > 0) {
+			this.write(null, ",");
+		}
+		this.add(n[i]);
+	}
+	this.write(null, ']');
+}
+
+strandsswp.write_object_init = function(n) {
+	this.write(n, '{');
+	for(var i = 0; i < n.length; i++) {
+		this.add(n[i]);
+		if( i != n.length - 1 ) {
+			this.write(n[i], ',');
+		}
+	}
+	this.write(null, '}');
+}
+
+strandsswp.write_property_init = function(n) {
+	this.add(n[0]);
+	this.write(n[0], ':');
+	this.add(n[1]);
+}
+
+strandsswp.write_block = function(n) {
+	this.write(n, '{');
+	for( var i = 0; i < n.length; i++ ) {
+		this.add(n[i]);
+	}
+	this.write(null, "}");
+}
+
+strandsswp.write_group = function(n) {
+	this.write(n, '(');
+	for( var i = 0; i < n.length; i++ ) {
+		this.add(n[i]);
+	}
+	this.write(null, ")");
+}
+
+strandsswp.write_list = function(n) {
+	this.write(null, '(');
+	for( var i = 0; i < n.length; i++ ) {
+		this.add(n[i]);
+		if( i != n.length - 1 ) {
+			this.write(null, ",");
+		}
+	}
+	this.write(n, ')');
+}
+
+strandsswp.write_label = function(n) {
+	this.write(n, n.label);
+	this.write(n, ":");
+	this.add(n.statement);
+}
+
+strandsswp.write_for = function(n) {
+	this.write(n, "for(");
+	this.add(n.setup);
+	// var statements are never associated with a semicolon, so our
+	// write statements automatically insert one.  Therefore, we
+	// need to check if a semicolon was already inserted for us.
+	if(this.last() != ';') this.write(null, ";");
+	this.add(n.condition);
+	this.write(null, ";");
+	this.add(n.update);
+	this.write(null, ")");
+	this.add(n.body);
+}
+
+strandsswp.write_call = function(n) {
+	this.add(n[0]);
+	this.add(n[1]);
+}
+
+strandsswp.write_new_with_args = function(n) {
+	this.write(n, "new ");
+	this.add(n[0]);
+	if (n[1])
+		this.add(n[1]);
+}
+
+strandsswp.write_new = function(n) {
+	this.write(n, "new ");
+	this.add(n[0]);
+	this.write(null, "()");
+}
+
+strandsswp.write_string = function(n) {
+	var value = n.value.replace(/(\\|")/g, "\\$1");
+	value = value.replace(/\n/g, "\\n");
+	this.write(n, '"');
+	this.write(n, value);
+	this.write(n, '"');
+}
+
+strandsswp.write_switch = function(n) {
+	this.write(n, "switch(");
+	this.add(n.discriminant);
+	this.write(null, "){");
+	for( var i = 0; i < n.cases.length; i++ ) {
+		this.add(n.cases[i]);
+	}
+	this.write(null, "}");
+}
+
+strandsswp.write_case = function(n) {
+	this.write(n, "case ");
+	this.add(n.caseLabel);
+	this.write(null, ":");
+	this.add(n.statements);
+}
+
+strandsswp.write_default = function(n) {
+	this.write(n, "default:");
+	this.add(n.statements);
+}
+
+strandsswp.write_delete = function(n) {
+	this.write(n, "delete ");
+	for( var i = 0; i < n.length; i++ ) {
+		this.add(n[i]);
+	}
+}
+
+strandsswp.write_while = function(n) {
+	this.write(n, "while(");
+	this.add(n.condition);
+	this.write(null, ")");
+	this.add(n.body);
+}
+
+strandsswp.write_do = function(n) {
+	this.write(n, "do");
+	this.add(n.body);
+	this.write(n.condition, " while(");
+	this.add(n.condition);
+	this.write(null, ");");
+}
+
+strandsswp.write_if = function(n) {
+	this.write(n, "if(");
+	this.add(n.condition);
+	this.write(null, ")");
+	this.addBlock(n.thenPart);
+	if(n.elsePart != null ) {
+		this.write(n.elsePart, " else ");
+		this.add(n.elsePart);
+	}
+}
+
+strandsswp.write_typeof = function(n) {
+	this.write(n, "typeof ");
+	this.add(n[0]);
+}
+strandsswp.write_instanceof = function(n) {
+	this.add(n[0]);
+	this.write(n, " instanceof ");
+	this.add(n[1]);
+}
+
+strandsswp.write_try = function(n) {
+	this.write(n, "try ");
+	this.add(n.tryBlock);
+	for( var i = 0; i < n.catchClauses.length; i++ ) {
+		var clause = n.catchClauses[i];
+		this.write(clause, " catch(");
+		this.write(null, clause.varName);
+		if (clause.guard) {
+			this.write(null, " if(");
+			this.add(clause.guard);
+			this.write(null, ")");
+		}
+		this.write(null, ")");
+		this.add(clause.block);
+	}
+	if( n.finallyBlock != null ) {
+		this.write(n.finallyBlock, " finally ");
+		this.add(n.finallyBlock);
+	}
+}
+
+strandsswp.write_throw = function(n) {
+	this.write(n, "throw(");
+	this.add(n.exception);
+	this.write(n, ");");
+}
+
+strandsswp.write_for_in = function(n) {
+	this.write(n, "for(");
+	if( n.varDecl == null ) {
+		this.add(n.iterator);
+	} else {
+		this.add(n.varDecl);
+		// variable writes automatically add a semicolon,
+		// we need to remove it.
+		this.pop();
+	}
+	this.write(null, " in ");
+	this.add(n.object);
+	this.write(null, ")");
+	this.add(n.body);
+}
+
+strandsswp.write_with = function(n) {
+	this.write(n, "with(");
+	this.add(n.object);
+	this.write(null, ")");
+	this.add(n.body);
+}
+
+strandsswp.write_void = function(n) {
+	this.write(n, "void ");
+	this.add(n[0]);
+}
+
+strandsswp.write_break = function(n) {
+	this.write(n, "break;");
+}
+
+strandsswp.write_continue = function(n) {
+	this.write(n, "continue;");
+}
+
+strandsswp.write_debugger = function(n) {
+	this.write(n, "debugger;");
+}
+
+strandsswp.write_return = function(n) {
+	this.write(n, "return");
+	if( n.expression ) { // yes, value has two possible meanings...
+		this.write(null, " ");
+		this.add(n.expression);
+	}
+	this.write(null, ";");
+}
+
+strands.compiler = new StrandsCompiler({exceptions: true, persistence:false, compress : false});
+strands.compiler.loadAndCompile = function(url){
+	var frame = _frm(this,arguments,['url'],[]);
+	var result = strands.request(frame.url,'GET');		
+	if (result == frame._S) return frame._s();
+	eval(this.compile(result),url);
+}
+strands.compiler.Function = function(source,thisObject,scopeObject,runAt) {
+	with(_frm(this,arguments,['source','thisObject','scopeObject','runAt'],[])) {
+		if (!source)
+			source = "function() {\n}";
+		 var code = this.compile("temp=" + source, "input",thisObject,scopeObject);
+		 if (code == _S) return _s();	 
+		var func = eval(code);
+		if (runAt == "server") {
+			func = function() {
+				persevere.serverCall(this,arguments);
+			}
+			func._psv15 = func.toString();
+		}
+		else
+			func._psv15 = code.substring(5,code.length-3);
+		func['function'] = source;
+		if (runAt)
+			func.runAt = runAt;
+		return func;
+		}
+}
+
+strands.request = function(url, method, postData) {
+	var frame = _frm(this,arguments,[],[]);
+	if (frame._cp == 0) {
+		var getXMLHttpRequest = function () {
+			if (parent.XMLHttpRequest)
+		        return new parent.XMLHttpRequest();
+			else if (window.ActiveXObject)
+		        return new ActiveXObject("Microsoft.XMLHTTP");
+		}
+	 	var xhr = getXMLHttpRequest();
+		frame.future = new Future();
+		var ajaxDataReader = function () {
+			if (xhr.readyState == 4) {
+		        // only if "OK"
+		        var loaded;
+		        try {
+		        	var status = xhr.status;
+		        	loaded = xhr.responseText.length > 0;//firefox can throw an exception right here
+		        } catch(e) {}
+		        if (loaded) 
+    				frame.future.fulfill(xhr.responseText);				
+				else
+					frame.future.interrupt();
+        		xhr = null; // This is to correct for IE memory leak
+			}
+		}
+		frame._cp = 1;
+	    xhr.open(method || "POST", url, true); 
+		xhr.onreadystatechange = ajaxDataReader;
+	    xhr.send(postData);
+	}
+	var result = frame.future.result();
+	if (result == frame._S) frame._s();
+	return result;
+}
+strands.compiler.tryCatchCompile = function(source,name,persistence,debug) {
+	try {
+		this.options.persistence = persistence;
+		this.options.debug = debug;
+		return this.compile(source);
+	}
+	catch (e) {
+		return "alert('ERROR in " + name.replace(/'/g,'') + ": line " + e.lineNumber + ": " + e.message + "');";
+	}
+}
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/selenium-strands.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/selenium-strands.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/selenium-strands.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,55 @@
+
+strands.compiler.options.debug = true;
+
+// by default, debugMode is private.  overriding the strands.errorWrapper to forcibly enable debug mode
+strands.errorWrapper = function(func) {
+	var newFunc = function() {
+		func.apply(this,arguments);			
+	}
+	newFunc.noTryCatch = func;
+	return newFunc;
+}
+
+strands.compiler.eval = function(script) {
+    eval(this.compile(script));
+}
+
+// simple function to wait until the specified function returns true
+Selenium.prototype.pollForDecoratedCondition = function(condition) { while (!condition()) {sleep(10);} };
+strands.compiler.eval("Selenium.prototype.pollForDecoratedCondition = " + Selenium.prototype.pollForDecoratedCondition.toString());
+
+// replace all of the doBlah functions with blah functions
+for (var functionName in Selenium.prototype) {
+    var match = /^do([A-Z].+)$/.exec(functionName);
+    if (match) {
+        var shortName = match[1];
+        // lowercase first character
+        var shortName = shortName.charAt(0).toLowerCase() + shortName.substr(1);
+        // replace waitFor functions with functions that automatically wait
+        if (/^waitFor/.test(shortName) || "open" == shortName) {
+            var evalString = "Selenium.prototype."+shortName+"=function() {\n" +
+                "this.pollForDecoratedCondition(this."+ functionName +
+                ".apply(this,arguments));\n};";
+            strands.compiler.eval(evalString);
+        } else {
+            Selenium.prototype[shortName] = Selenium.prototype[functionName];
+        }
+    }
+    // TODO blahAndWait functions
+}
+
+Selenium.prototype.bustCache = function(url) {
+    // DGF this function didn't compile correctly, not sure why
+    url += (url.indexOf("?") == -1 ? "?" : "&");
+    url += "cacheBuster=" + new Date().getTime();
+    return url;
+}
+
+Selenium.prototype.loadAndRunTest = function(testUrl) {
+    testUrl = this.bustCache(testUrl);
+    var testScript = strands.request(testUrl);
+    var evalString = "selenium.runTest = function() { with (this) { " + testScript + " } }";
+    strands.compiler.eval(evalString);
+    this.runTest();
+}
+strands.compiler.eval("Selenium.prototype.loadAndRunTest = " + Selenium.prototype.loadAndRunTest.toString());

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/simpletest.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/simpletest.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/simpletest.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,7 @@
+open("../tests/html/test_click_page1.html");
+for (var i = 0; i < 3; i++) {
+    click("link");
+    waitForPageToLoad();
+    click("previousPage");
+    waitForPageToLoad();
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/strands-ext.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/strands-ext.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/strands-ext.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,102 @@
+/**
+Strands Extra Functions - JavaScript Cooperative Threading and Coroutine support
+Copyright (C) 2007 Xucia Incorporation
+Author - Kris Zyp - kriszyp at xucia.com
+ */
+/**
+ * Makes an XHR (Ajax) request using the given url, method, and post data (for POST requests) and 
+	returns contents that came from the server.
+
+ * @param {Object} url
+ * @param {Object} method
+ * @param {Object} postData
+ */
+strands.request = function(url, method, postData) {
+	var frame = _frm(this,arguments,[],[]);
+	if (frame._cp == 0) {
+		var getXMLHttpRequest = function () {
+			if (parent.XMLHttpRequest)
+		        return new parent.XMLHttpRequest();
+			else if (window.ActiveXObject)
+		        return new ActiveXObject("Microsoft.XMLHTTP");
+		}
+	 	var xhr = getXMLHttpRequest();
+		frame.future = new Future();
+		var ajaxDataReader = function () {
+			if (xhr.readyState == 4) {
+		        // only if "OK"
+		        var loaded;
+		        try {
+		        	var status = xhr.status;
+		        	loaded = xhr.responseText.length > 0;//firefox can throw an exception right here
+		        } catch(e) {}
+		        if (loaded) 
+    				frame.future.fulfill(xhr.responseText);				
+				else
+					frame.future.interrupt();
+        		xhr = null; // This is to correct for IE memory leak
+			}
+		}
+		frame._cp = 1;
+	    xhr.open(method || "POST", url, true); 
+		xhr.onreadystatechange = ajaxDataReader;
+	    xhr.send(postData);
+	}
+	var result = frame.future.result();
+	if (result == frame._S) frame._s();
+	return result;
+}
+		/**
+		 * This function will can be used to synchronize access to a critical section of code. If there is other code with a 
+		 * lock on the lock object, than the code will be executed once the lock is released
+		 * @param lockObject	This is the object to use lock. Two competing threads can use a single lock object to safeguard a critical section
+		 * @param func		This is function to execute while locking out other execution of this function
+		 * @param notExclusive	Setting this to true will cause the function to be executed even if other code is using the lock object. The lock will be incremented so that synchronize will still block for calls that don't use this parameter
+		 */
+strands.synchronize = function(lockObject,func,notExclusive) {
+			return function() { // $_lock is the number of functions currently executing
+										// $_wfs are the functions that are currently resulting to access this code
+				with(_frm(this,arguments,[],[])) {
+					if (_cp == 0) {
+						if (lockObject.$_lock && !notExclusive)  {
+							var future = new Future();
+							push(lockObject.$_wfs,future.fulfill);
+							return future.result();
+						}
+						else {
+							if (!lockObject.$_wfs)
+								lockObject.$_wfs = [];		
+							if (lockObject.$_lock)
+								lockObject.$_lock++;
+							else {
+								lockObject.$_lock = 1;			
+							}
+						}
+					}
+					_cp=1;
+					try {
+						var retValue = func.apply(this,arguments);			
+						return retValue;
+					}
+					finally {
+						if (retValue == _S) return _s();// if it is suspending we must keep it locked, so this a clever way to avoid the next code
+							lockObject.$_lock--;
+							if (lockObject.$_lock < 0) // Assertion
+								throw new Error("Lock count less than zero");
+							if (lockObject.$_wfs.length > 0)
+								setTimeout(function() {			
+									synchronize(lockObject,lockObject.$_wfs.pop());
+								},1);			
+					}
+				}
+			}
+		}
+	/**
+	 * This can be used by idempotent functions that are written in native code that make calls to compiled code
+	 */
+strands.rerunLater = function() {
+			// If a rerunnable function calls a strands function in needs to call this after a narrative frame suspends. This will make it so there will not be a reference to this frame and it will not be required to be executed
+			if (suspendedFrame)
+				delete suspendedFrame._r.func;
+			return Suspend;
+		}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/strands.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/strands.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/strands.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,273 @@
+/**
+Strands - JavaScript Cooperative Threading and Coroutine support
+Copyright (C) 2007 Xucia Incorporation
+Author - Kris Zyp - kriszyp at xucia.com
+ /* ***** BEGIN LICENSE BLOCK *****
+  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+  *
+  * The contents of this file are subject to the Mozilla Public License Version
+  * 1.1 (the "License"); you may not use this file except in compliance with
+  * the License. You may obtain a copy of the License at
+  * http://www.mozilla.org/MPL/
+  *
+  * Software distributed under the License is distributed on an "AS IS" basis,
+  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  * for the specific language governing rights and limitations under the
+  * License.
+  * ***** END LICENSE BLOCK ***** */
+function temp() {
+	var standardPush = Array.prototype.push;
+	var debugMode =document.location.search.indexOf("debug=true") > -1;
+
+	var push = function(obj,value) {
+		return standardPush.call(obj,value); // preserve default push behavoir
+	}
+	var suspendedFrame = null;
+	var currentThread = {};
+	var Suspend = {	// returned value when a coroutine is suspended, and state of the top frame when suspended
+		toString: function() { return "SUSPENDED" },
+		get: function() {return Suspend },
+		call:function() {return Suspend }
+	}
+	/**
+	 * This is wrap the given function in a try catch when debug mode is off. If an error is thrown, the error message will be displayed with an alert
+	 */		
+	var tryCatch = function(func) {
+		return strands.errorWrapper(func)();
+	}
+	var specificNotify = {};
+	Future = function(func,args,thisObj,callback) {
+		this.topFrames = [];
+		var self = this;
+		this.fulfill = function(retValue) {
+			if (retValue == specificNotify) {
+				var targetFrames = retValue.targetFrames;
+				retValue = retValue.retValue;
+			}
+			else
+				self.value = retValue;
+			var frame;
+			while (frame = (targetFrames || self.topFrames).shift()) { // iterate through all the resulting threads/frames
+				// This is the beginning of a resume
+				currentThread = {};
+		//		checkRestState();
+				if (!frame._r) 
+					throw new Error("Future called without being in a result state");
+				frame.retValue = retValue; 
+				if (frame._r.NRY)
+					frame._r.thread = currentThread;
+				while (frame._r.parentFrame) {
+					frame = frame._r.parentFrame; // now the bottom frame
+					frame._r.thread = currentThread;
+				}
+				if (frame._r.func) {
+					suspendedFrame = frame; // the .parents indicates it was a NotReadyYet function, so there is no suspended frames above it
+					tryCatch(function() {
+						frame._r.func.call(frame._r.frameThis); // now resume	
+					});
+				}
+				else {
+					//A thread was resumed with no call stack
+					suspendedFrame = null;
+				}
+			}
+		}
+		if (func) {
+			if (callback)
+				this.addListener(callback);
+			(function() {
+				with(_frm(this,arguments,[],[])) {
+					var value = func.apply(thisObj || window,args||[]);
+					if (value===_S) return _s();
+					self.fulfill(value);
+				}
+			})();			
+		}
+	}
+	Future.prototype = {
+		addListener : function(func) {
+			push(this.topFrames,func);
+		},
+		interrupt : function () {
+			this.fulfill(strands.INTERRUPTED);
+		},
+		isDone : function() {
+			return this.hasOwnProperty('value');
+		},
+		result : function(timeout) {
+			if (this.hasOwnProperty('value') || (suspendedFrame && suspendedFrame.retValue)) { // The future has been called, we can resume
+				var value = (suspendedFrame ? suspendedFrame.retValue : 0) || this.value;
+				suspendedFrame = null; // make sure we don't get any callers picking this up, we may not need this
+				if (value == strands.TIMEOUT || value== strands.INTERRUPTED)
+					throw value;
+				return value;// the future has already been fulfilled so we can just return the result
+			}
+			var topFrame = {}
+			push(this.topFrames,topFrame);
+			topFrame._r = {};
+			suspendedFrame = topFrame;
+			if (timeout) {
+				var self = this;
+				setTimeout(function() {
+					self.fulfill(specificNotify = {retValue:strands.TIMEOUT,targetFrames:[topFrame]});
+				},timeout);
+			}
+			return Suspend;
+		}
+	}
+
+	var CallFrame = function() {
+		this._r = {};		
+	}
+	var Construct = function() {};
+	var defaultScope = CallFrame.prototype = {
+		_s : function(exception) { // this handles exceptions as well as suspensions
+				if (exception) {
+					if (this._r.ecp == null)
+						throw exception;
+					this.$_thr = true;
+					this.$_ex  = exception;
+					this._cp  = this._r.ecp;
+					return;
+				}
+				var info = this._r;
+				if (!suspendedFrame)
+					NoSuspendedFrame; // This can be caused by returning Suspend without actually being in a suspension, or if _S ends up in a variable
+				suspendedFrame._r.parentFrame = this;
+				info.childFrame = suspendedFrame; // it needs to be reexecuted
+				suspendedFrame = this;
+				if (this._r.thread == currentThread) // Assertion
+					SuspensionFrameShouldNotBeCurrentThread;
+				return Suspend;
+			},
+		_S : Suspend,
+		_keys : function(obj) {
+			var keys = [];
+			for(var n in obj) 
+				push(keys,n)	;
+			return keys;
+		},
+
+		_new : function(value,args) { // create a new instance of an object or constructor
+			if (value === Suspend)
+				return value;
+			var frame = _frm(this,arguments,[],[]);				
+			if (frame._cp == 0) {
+				frame._cp=1;
+				frame.Const= value;
+				Construct.prototype = value.prototype;
+				if (value === String || value === Number) // these classes must not have a target this
+					return args.length?new value(args[0]):new value;
+				if (value !== Date) { // date does not have to directly instantiated, but it does need an undefined scope, it also needs to be able to handle variable arguements
+					frame.newThis = new Construct();
+				}
+			}
+			if ((value = frame.Const.apply(frame.newThis,args?args:[])) == Suspend) return frame._s();
+			if (value instanceof Object) return value; // if the new returns a different value than "this"			
+			return frame.newThis;
+		},
+		_cp : 0
+	}
+	/** 
+	 * This creates a new Strands call frame. It becomes the scope for the function maintains variables and parameters across suspensions and resumes. It should only be used in compiled code
+	 */	
+	_frm = function(frameThis,args,argNames,varNames,noScopeNeeded) {
+		if (args.caller) args.caller=0; // this fixes a big memory leak;
+		if (suspendedFrame) {
+			// if it is loading we start a new frame
+			if (suspendedFrame._r.thread == currentThread && suspendedFrame._r.func && !suspendedFrame._r.NRY) {// this means we are resuming
+				var frame = suspendedFrame;
+				//TODO: Implement this:
+				if (frame._r.func != args.callee && frame._r.func.toString() != args.callee.toString()) {// this means the function that is being called has changed, the function has been replaced, so we need to call the orginal one
+	//				if (this!=frame._r.frameThis) {
+						suspendedFrame = null;
+						StackAlterationError("The function has changed, the means for handling this right now is not complete");
+		//			}
+/*					var retValue = frame.func.call(frame.frameThis);
+					if (retValue == _S){
+						// this is the tricky part, we need to call the next function and have it be guaranteed to return a _S
+					}
+					else {// we need to come up with a way to ensure that we have the right rv#
+						frame["rv" + frame.cp++] = retValue;  //make sure we increment the cp so we are the next part
+					}
+					return frame;*/
+				}
+				delete frame._r.thread;
+	
+				suspendedFrame = frame._r.childFrame; // if this is undefined it means we are at the top of the resume stack
+				delete frame._r.childFrame; // for assertions
+				if (suspendedFrame && suspendedFrame._r) {//Assertion stuff
+					if (! suspendedFrame._r.parentFrame)
+						SuspendedFrameHasNoParentFrame;
+					else
+						delete suspendedFrame._r.parentFrame;				
+				}
+				return frame;
+			}
+			else { // this case means that there is a suspendedFrame variable left over from a previous resume/suspension
+				// It should only be a left over from a suspension, and it should be the bottom frame.  A resume should null out suspendedFrame
+	
+				suspendedFrame = null;  // a suspension took place somewhere else, so now we can get rid of this
+			}
+		}
+		frame = new CallFrame;
+		frame._cp = 0; // Why is this needed for opera to work? somewhere the prototype _cp is getting set, need to figure out why
+		frame.arguments = args;
+		frame._scope = frame;
+		frame._r.func = args.callee;
+		frame._r.frameThis  = frameThis;
+		for( var i = 0; i < argNames.length; i++ ) 
+			frame[argNames[i]] = args[i];
+		for( var i = 0; i < varNames.length; i++ ) 
+			frame[varNames[i]] = undefined; // declare all the variables
+		return frame;
+	}
+	
+	/**
+	 * Suspend execution for the given amount time
+	 * @param time	milliseconds to pause
+	 */
+	sleep = function(time) {
+		var frame = _frm(this,arguments,[],[]);
+		if (!frame._cp) { // if it is zero
+			frame._cp = 1;
+			setTimeout((frame.future = new Future).fulfill,time);
+			frame.future.result();
+			return frame._s();
+		}
+		frame.future.result(); // this is the result operation to resume
+	}
+	
+	strands = { 
+		defaultScope : defaultScope,
+		loadScope : function(){},
+		/**
+		 * This function that will be called to return a function that should execute the provided function in order to initialize the stack 
+		 * Any time a new stack is created, the returned function will be used. This provides a mechanism to wrap all processing
+		 * within a try catch.
+		 */
+		errorWrapper : function(func) {
+			var newFunc = function() {
+				if (debugMode)
+					return func.apply(this,arguments);			
+				try {
+					return func.apply(this,arguments);			
+				}
+				catch (e) {
+					alert(e.message || e);
+				}
+			}
+			newFunc.noTryCatch = func;
+			return newFunc;
+		},
+		TIMEOUT : {toString:function(){return "Thread timeout"}},
+		INTERRUPTED : {toString:function(){return "Thread interrupted"}},
+		sleep : sleep,
+		/**
+		 * This is a constant that is returned from functions to indicate that the code execution is suspending
+		 */
+		Suspension : Suspend
+
+	}
+};
+temp();

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/test.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/test.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/strands/test.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,24 @@
+<html debug="true"><head>
+        <script language="JavaScript" type="text/javascript" src="../core/scripts/htmlutils.js"></script>
+        <script language="JavaScript" type="text/javascript" src="../core/lib/cssQuery/cssQuery-p.js"></script>
+        <script language="JavaScript" type="text/javascript" src="../core/scripts/selenium-browserdetect.js"></script>
+        <script language="JavaScript" type="text/javascript" src="../core/scripts/selenium-browserbot.js"></script>
+        <script language="JavaScript" type="text/javascript" src="../core/scripts/find_matching_child.js"></script>
+        <script language="JavaScript" type="text/javascript" src="../core/scripts/selenium-api.js"></script>
+        <script language="JavaScript" type="text/javascript" src="../core/scripts/selenium-logging.js"></script>
+        <script language="JavaScript" type="text/javascript" src="../core/xpath/misc.js"></script>
+        <script language="JavaScript" type="text/javascript" src="../core/xpath/dom.js"></script>
+        <script language="JavaScript" type="text/javascript" src="../core/xpath/xpath.js"></script>
+        <script src="strands.js"></script>
+        <script src="compiler.js"></script>
+<script src="selenium-strands.js"></script>        
+<body ><iframe id="seleniumframe" src="about:blank"></iframe>
+
+
+<script>
+    var selWin = document.getElementById("seleniumframe").contentWindow;
+    selenium = Selenium.createForWindow(selWin);
+    selenium.loadAndRunTest("simpletest.js");
+</script>
+    
+    </body></html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/DogFoodTestSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/DogFoodTestSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/DogFoodTestSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,41 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Dogfood Test Suite</title>
+
+</head>
+
+<body>
+
+<table     cellpadding="1"
+           cellspacing="1"
+           border="1">
+        <tbody>
+            <tr><td><b>Dogfood Test Suite</b></td></tr>
+            <tr><td><a href="./dogfood/TestBaseUrl.html">TestBaseUrl</a></td></tr>
+            <tr><td><a href="./dogfood/TestRunSuccessfulTests.html">TestRunSuccessfulTests</a></td></tr>
+            <tr><td><a href="./dogfood/TestRunFailedTests.html">TestRunFailedTests</a></td></tr>
+            <tr><td><a href="./dogfood/TestPauseAndResume.html">TestPauseAndResume</a></td></tr>
+            <tr><td><a href="./dogfood/TestBreakPoint.html">TestBreakPoint</a></td></tr>
+            <tr><td><a href="./dogfood/TestFailures.html">TestFailures</a></td></tr>
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/ErrorCheckingTestSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/ErrorCheckingTestSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/ErrorCheckingTestSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,38 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Test Suite</title>
+
+</head>
+
+<body>
+
+<table     cellpadding="1"
+           cellspacing="1"
+           border="1">
+        <tbody>
+            <tr id="errorTitle"><td><b>Error Checking Test Suite</b></td></tr>
+            <tr id="errorCaseTitle"><td><a href="./TestErrorChecking.html">TestErrorChecking</a></td></tr>
+            <tr><td><a id="TestType" href="./TestType.html">TestType</a></td></tr>
+            <tr><td><a href="./TestSelect.html">TestSelect</a></td></tr>            
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/FailingTestSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/FailingTestSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/FailingTestSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,57 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Test Suite</title>
+<script language="JavaScript" type="text/javascript" src="../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript">
+    function filterTestsForBrowser() {
+        var suiteTable = document.getElementById("suiteTable");
+        var skippedTests = document.getElementById("skippedTests");
+
+        for(rowNum = suiteTable.rows.length - 1; rowNum >= 0; rowNum--)
+        {
+            var row = suiteTable.rows[rowNum];
+            var filterString = row.getAttribute("unless");
+            if (filterString && eval(filterString))
+            {
+              var cellHTML = row.cells[0].innerHTML;
+              suiteTable.deleteRow(rowNum);
+
+              var newRow = skippedTests.insertRow(1);
+              var newCell = newRow.insertCell(0)
+              newCell.innerHTML = cellHTML;
+            }
+        }
+    }
+</script>
+
+</head>
+
+<body onload="filterTestsForBrowser()">
+
+<table     cellpadding="1"
+           cellspacing="1"
+           border="1">
+        <tbody>
+            <tr><td><b>Test Suite</b></td></tr>
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/GoogleTestSearch.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/GoogleTestSearch.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/GoogleTestSearch.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,69 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Open</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Google Test Search<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>http://www.google.com/webhp?hl=en</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Google</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>q</td>
+      <td>Selenium OpenQA</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>q</td>
+      <td>Selenium OpenQA</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>btnG</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextPresent</td>
+      <td>openqa.org</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Selenium OpenQA - Google Search</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/GoogleTestSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/GoogleTestSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/GoogleTestSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,32 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Test Suite</title>
+
+</head>
+
+<body>
+  <table cellpadding="1" cellspacing="1"border="1">
+    <tbody>
+      <tr><td><b>Test Suite</b></td></tr>
+      <tr><td><a href="./GoogleTestSearch.html">GoogleTestSearch</a></td></tr>
+    </tbody>
+  </table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/PassingTestSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/PassingTestSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/PassingTestSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,37 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Test Suite</title>
+
+</head>
+
+<body>
+
+<table     cellpadding="1"
+           cellspacing="1"
+           border="1">
+        <tbody>
+            <tr id="passingSuite"><td><b>Test Suite</b></td></tr>
+            <tr id="passingTest"><td><a id="TestType" href="./TestType.html">TestType</a></td></tr>
+            <tr><td><a href="./TestSelect.html">TestSelect</a></td></tr>            
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/ShortTestSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/ShortTestSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/ShortTestSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,36 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Test Suite</title>
+
+</head>
+
+<body>
+
+<table     cellpadding="1"
+           cellspacing="1"
+           border="1">
+        <tbody>
+            <tr><td><b>Test Suite</b></td></tr>
+            <tr><td><a href="./TestLocators.html">TestLocators</a></td></tr>
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestAddLocationStrategy.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestAddLocationStrategy.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestAddLocationStrategy.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,85 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2007 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestAddLocationStrategy</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestAddLocationStrategy<br>
+      </td>
+    </tr>
+    
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_click_page1.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>addLocationStrategy</td>
+      <td>foo</td>
+      <td>return inDocument.getElementById(locator);</td>
+    </tr>
+    <tr>
+      <td>assertElementPresent</td>
+      <td>foo=link</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>refreshAndWait</td>
+      <td>&nbsp;</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertElementPresent</td>
+      <td>foo=link</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>Error evaluating function definition*</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>addLocationStrategy</td>
+      <td>bar</td>
+      <td>[[[;</td>
+    </tr>
+    <tr>
+      <td>addLocationStrategy</td>
+      <td>bar</td>
+      <td>thisVariableDoesNotExist;</td>
+    </tr>
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>Error executing strategy function bar*</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertElementPresent</td>
+      <td>bar=link</td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestAlerts.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestAlerts.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestAlerts.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,199 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Alert Verifification</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Alert verifyment<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_verify_alert.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>verifyAlertNotPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>assertAlertNotPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    
+    <tr>
+      <td>click</td>
+      <td>oneAlert</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlertPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForAlertPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertAlertPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    
+    <tr>
+      <td>verifyAlert</td>
+      <td>Store Below 494 degrees K!</td>
+      <td></td>
+    </tr>
+   
+    <tr>
+      <td>click</td>
+      <td>oneAlert</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>storeAlert</td>
+      <td>myVar</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyExpression</td>
+      <td>${myVar}</td>
+      <td>Store Below 494 degrees K!</td>
+    </tr>
+   
+    <tr>
+      <td>click</td>
+      <td>twoAlerts</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>* 220 degrees C!</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>regexp:^Store Below 429 degrees F!</td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>clickAndWait</td>
+      <td>alertAndLeave</td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>verifyAlert</td>
+      <td>I'm Melting! I'm Melting!</td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_verify_alert.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>There were no alerts</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>noAlert</td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>click</td>
+      <td>oneAlert</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'Store Below 494 degrees K!' did not match 'wrongAlert'</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>wrongAlert</td>
+      <td></td>
+    </tr>
+
+   <tr>
+      <td>click</td>
+      <td>twoAlerts</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'Store Below 220 degrees C!' did not match 'Store Below 429 degrees F!'</td>
+      <td></td>
+    </tr>
+      <tr>
+      <td>verifyAlert</td>
+      <td>Store Below 429 degrees F!</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'Store Below 429 degrees F!' did not match 'Store Below 220 degrees C!'</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>Store Below 220 degrees C!</td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>click</td>
+      <td>oneAlert</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>There was an unexpected Alert! [Store Below 494 degrees K!]</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_verify_alert.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestBasicAuth.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestBasicAuth.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestBasicAuth.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,44 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2007 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestBasicAuth</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestBasicAuth<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>http://alice:foo@localhost:4444/selenium-server/tests/html/basicAuth/index.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertTitle</td>
+      <td>welcome</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestBrowserVersion.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestBrowserVersion.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestBrowserVersion.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,61 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta content="text/html; charset=ISO-8859-1"
+          http-equiv="content-type">
+    <title>Test Browser Version</title>
+    <script language="JavaScript" type="text/javascript" src="../core/scripts/selenium-browserdetect.js"></script>
+    <script>
+
+        function load() {
+            var output = document.getElementById("output");
+            for (key in navigator) {
+                var node = document.createTextNode("navigator." + key + " -> " + navigator[key]);
+                output.appendChild(node);
+                output.appendChild(document.createElement("br"));
+            }
+            for (key in browserVersion) {
+                var node = document.createTextNode("browserVersion." + key + " -> " + browserVersion[key]);
+                output.appendChild(node);
+                output.appendChild(document.createElement("br"));
+            }
+        }
+
+    </script>
+
+</head>
+
+<body onload="load();">
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr>
+            <td rowspan="1" colspan="3">Test Browser Version<br>
+            </td>
+        </tr>
+        <tr>
+            <td>echo</td>
+            <td>javascript{browserVersion.name}</td>
+            <td>&nbsp;</td>
+        </tr>
+    </tbody>
+</table>
+
+<div id="output"></div>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCheckUncheck.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCheckUncheck.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCheckUncheck.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,133 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test check/uncheck of toggle-buttons</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <th colspan="3">Test check/uncheck of toggle-buttons</th>
+    </tr>
+    <tr>
+      <td colspan="3"><em>"toggle buttons" == check-boxes and radio-buttons</em></td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_check_uncheck.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td colspan="3"><em>check initial state</em></td>
+    </tr>
+    <tr>
+      <td>verifyChecked</td>
+      <td>base-spud</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotChecked</td>
+      <td>base-rice</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyChecked</td>
+      <td>option-cheese</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotChecked</td>
+      <td>option-onions</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td colspan="3"><em>okay, now start pushing buttons</em></td>
+    </tr>
+    <tr>
+      <td>check</td>
+      <td>base-rice</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotChecked</td>
+      <td>base-spud</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyChecked</td>
+      <td>base-rice</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>uncheck</td>
+      <td>option-cheese</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotChecked</td>
+      <td>option-cheese</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>check</td>
+      <td>option-onions</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyChecked</td>
+      <td>option-onions</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td colspan="3"><em>address elements by name+value</em></td>
+    </tr>
+    <tr>
+      <td>verifyNotChecked</td>
+      <td>option-chilli</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>check</td>
+      <td>option chilli</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyChecked</td>
+      <td>option-chilli</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>uncheck</td>
+      <td>option index=3</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotChecked</td>
+      <td>option-chilli</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClick.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClick.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClick.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,213 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Click</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Click<br>
+      </td>
+    </tr>
+    
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_click_page1.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+  </tbody>
+</table>
+    <p>Click a regular link</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>verifyText</td>
+      <td>link</td>
+      <td>Click here for next page</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>link</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page Target</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>previousPage</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page 1</td>
+      <td>&nbsp;</td>
+    </tr>
+</tbody>
+</table>
+    <p>Click a link with an enclosed image</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    
+    <tr>
+      <td>clickAndWait</td>
+      <td>linkWithEnclosedImage</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page Target</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>previousPage</td>
+      <td>&nbsp;</td>
+    </tr>
+</tbody>
+</table>
+    <p>Click an image enclosed by a link</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    
+    <tr>
+      <td>clickAndWait</td>
+      <td>enclosedImage</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page Target</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>previousPage</td>
+      <td>&nbsp;</td>
+    </tr>
+</tbody>
+</table>
+<p>Click an image enclosed by a div enclosed by a link (SEL-451)</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    
+    <tr>
+      <td>clickAndWait</td>
+      <td>extraEnclosedImage</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page Target</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>previousPage</td>
+      <td>&nbsp;</td>
+    </tr>
+</tbody>
+</table>
+    <p>Click a link with an href anchor target within this page</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    
+    <tr>
+      <td>click</td>
+      <td>linkToAnchorOnThisPage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page 1</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Timed out after 500ms</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>waitForPageToLoad</td>
+      <td>500</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>setTimeout</td>
+      <td>30000</td>
+      <td>&nbsp;</td>
+    </tr>
+</tbody>
+</table>
+
+    <p>Click a link where onclick returns false</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    
+    <tr>
+      <td>click</td>
+      <td>linkWithOnclickReturnsFalse</td>
+      <td></td>
+    </tr>
+</tbody>
+</table>
+    <p>Need a pause to give the page a chance to reload (so this test can fail)</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    
+    <tr><td>pause</td><td>300</td><td/></tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page 1</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>setTimeout</td>
+      <td>5000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_click_page1.html</td>
+      <td>&nbsp;</td>
+    </tr>
+</tbody>
+</table>
+    <p>Try double clicking</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>doubleClick</td>
+      <td>doubleClickable</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertAlert</td>
+      <td>double clicked!</td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClickBlankTarget.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClickBlankTarget.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClickBlankTarget.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,212 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestClickBlankTarget</title>
+</head>
+<body>
+    <p>This test reproduces bug SEL-272</p>
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestClickBlankTarget<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/Frames.html</td> 
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>selectFrame</td>
+      <td>bottomFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>click</td>
+      <td>changeBlank</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>waitForPopUp</td>
+      <td>_blank</td>
+      <td>10000</td>
+    </tr>
+    
+    <tr>
+      <td>selectWindow</td>
+      <td>_blank</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    
+    <tr>
+      <td>click</td>
+      <td>changeSpan</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>close</td>
+      <td>&nbsp;</td>
+      <td>&nbsp;</td>
+    </tr>
+
+</tbody>
+</table>
+<p>And again, from the wrong frame</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>    
+    <tr>
+      <td>selectWindow</td>
+      <td>null</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>changeBlank</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>waitForPopUp</td>
+      <td>_blank</td>
+      <td>10000</td>
+    </tr>
+    
+    <tr>
+      <td>selectWindow</td>
+      <td>_blank</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    
+    <tr>
+      <td>click</td>
+      <td>changeSpan</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>close</td>
+      <td>&nbsp;</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+<p>Also try submitting a form with a _blank target</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>selectWindow</td>
+      <td>null</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>selectFrame</td>
+      <td>bottomFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>submit</td>
+      <td>formBlank</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>waitForPopUp</td>
+      <td>_blank</td>
+      <td>10000</td>
+    </tr>
+    
+    <tr>
+      <td>selectWindow</td>
+      <td>_blank</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    
+    <tr>
+      <td>click</td>
+      <td>changeSpan</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>close</td>
+      <td>&nbsp;</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+<p>window.open(url, _blank)</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>selectWindow</td>
+      <td>null</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_select_window.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>click</td>
+      <td>popupBlank</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>waitForPopUp</td>
+      <td>_blank</td>
+      <td>10000</td>
+    </tr>
+    
+    <tr>
+      <td>selectWindow</td>
+      <td>_blank</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    
+    <tr>
+      <td>verifyTitle</td>
+      <td>Select Window Popup</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>close</td>
+      <td>&nbsp;</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClickJavascriptHref.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClickJavascriptHref.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestClickJavascriptHref.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,130 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Click</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestClickJavaScriptHref<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_click_javascript_page.html</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+<p>Click a regular link</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>link</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>link clicked: foo</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+<p>Click link with multiple javascript calls</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>linkWithMultipleJavascriptStatements</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>alert1</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>alert2</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>alert3</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+<p>Click a link with javascript:void() href</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>linkWithJavascriptVoidHref</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>onclick</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page 1</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+<p>Click a link where onclick returns false</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>linkWithOnclickReturnsFalse</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page 1</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+<p>No alert should be raised.</p>
+<p>Click an image enclosed in a link</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>enclosedImage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>enclosedImage clicked</td>
+      <td>&nbsp;</td>
+    </tr>
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCommandError.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCommandError.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCommandError.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,79 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Command Error</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Command Error<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_verifications.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyErrorOnNext</td>
+      <td>Element notALink not found</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>notALink</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyErrorOnNext</td>
+      <td>Element noSuchSelect not found</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>select</td>
+      <td>noSuchSelect</td>
+      <td>somelabel</td>
+    </tr>
+    <tr>
+      <td>verifyErrorOnNext</td>
+      <td>Option with label 'noSuchLabel' not found</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>label=noSuchLabel</td>
+    </tr>
+    <tr>
+      <td>verifyErrorOnNext</td>
+      <td>Specified element is not a Select (has no options)</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>select</td>
+      <td>theText</td>
+      <td>label=noSuchLabel</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestComments.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestComments.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestComments.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,83 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Comments</title>
+</head>
+<body>
+
+<!-- These are just excerpts from TestVerifications.html, with comments added -->
+
+<p>Here we demonstrate how comments can be interspersed within Selenium
+tests.</p>
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <th colspan="3">Test Comments</th>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_verifications.html?foo=bar</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td colspan="3"><i>Any row with fewer than 3 cells is ignored</i></td>
+    </tr>
+
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_verifications.html*</td>
+      <td>&nbsp;</td>
+      <td><i>anything after the 3rd cell is ignored</i></td>
+    </tr>
+
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>the text value</td>
+    </tr>
+
+  </tbody>
+</table>
+
+<ul>
+  <li>Multiple test-tables may occur.
+  <li>Any text/markup outside of tables is ignored.
+</ul>
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>verifyValue</td>
+      <td>theHidden</td>
+      <td>the hidden value</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>theSpan</td>
+      <td>this is the span</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestConfirmations.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestConfirmations.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestConfirmations.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,181 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Confirmation Verifification</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test verify Confirmation<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_confirm.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>chooseCancelOnNextConfirmation</td>
+      <td></td>
+      <td></td>
+    </tr>      
+    <tr>
+      <td>click</td>
+      <td>confirmAndLeave</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyConfirmationPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForConfirmationPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertConfirmationPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    
+    <tr>
+      <td>verifyConfirmation</td>
+      <td>You are about to go to a dummy page.</td>
+      <td></td>
+    </tr>   
+    <tr>
+      <td>verifyTitle</td>
+      <td>Test Confirm</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>confirmAndLeave</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyConfirmation</td>
+      <td>*dummy page*</td>
+      <td></td>
+    </tr>
+    
+    <tr>
+      <td>verifyTitle</td>
+      <td>Dummy Page</td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_confirm.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Test Confirm</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>chooseCancelOnNextConfirmation</td>
+      <td></td>
+      <td></td>
+    </tr>      
+    <tr>
+      <td>chooseOkOnNextConfirmation</td>
+      <td></td>
+      <td></td>
+    </tr>      
+    <tr>
+      <td>clickAndWait</td>
+      <td>confirmAndLeave</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyConfirmation</td>
+      <td>*dummy page*</td>
+      <td></td>
+    </tr>
+    
+    <tr>
+      <td>verifyTitle</td>
+      <td>Dummy Page</td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_confirm.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>There were no confirmations</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyConfirmation</td>
+      <td>This should fail - there are no confirmations</td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>clickAndWait</td>
+      <td>confirmAndLeave</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'You are about to go to a dummy page.' did not match 'this should fail - wrong confirmation'</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyConfirmation</td>
+      <td>this should fail - wrong confirmation</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_confirm.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>confirmAndLeave</td>
+      <td></td>
+    </tr>
+      <tr>
+        <td>assertErrorOnNext</td>
+        <td>There was an unexpected Confirmation! [You are about to go to a dummy page.]</td>
+        <td></td>
+      </tr>
+      <tr>
+        <td>open</td>
+        <td>../tests/html/test_confirm.html</td>
+        <td>&nbsp;</td>
+      </tr>
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCookie.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCookie.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCookie.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,223 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta content="text/html; charset=ISO-8859-1"
+          http-equiv="content-type">
+    <title>Test Cookie</title>
+</head>
+<body>
+    
+    <p>WARNING! You can only run this test off of a "real" webserver (i.e. over http); you can't run this test directly off of the file system.</p>
+    <p>Also note that the paths used in this test don't have to actually exist; we can get 404 errors on every one of these pages but still successfully test the cookie functionality.</p>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr>
+            <td rowspan="1" colspan="3">Test Cookie<br>
+            </td>
+        </tr>
+        <tr>
+            <td>storeEval</td>
+            <td>parseUrl(canonicalize(absolutify("html", selenium.browserbot.baseUrl))).pathname;</td>
+            <td>base</td>
+        </tr>
+        <tr>
+            <td>echo</td>
+            <td>${base}</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>open</td>
+            <td>${base}/path1/cookie1.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>deleteCookie</td>
+            <td> testCookieWithSameName</td>
+            <td> /</td>
+        </tr>
+        <tr>
+            <td>deleteCookie</td>
+            <td> addedCookieForPath1</td>
+            <td>${base}/path1/</td>
+        </tr>
+        <tr>
+            <td>assertCookie</td>
+            <td></td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>open</td>
+            <td>${base}/path2/cookie2.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>deleteCookie</td>
+            <td> testCookieWithSameName</td>
+            <td> ${base}/path2/</td>
+        </tr>
+        <tr>
+            <td>deleteCookie</td>
+            <td> addedCookieForPath2</td>
+            <td> ${base}/path2/</td>
+        </tr>
+        <tr>
+            <td>assertCookie</td>
+            <td></td>
+            <td>&nbsp;</td>
+        </tr>
+    </tbody>
+</table>
+
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr>
+            <td>open</td>
+            <td>${base}/path1/cookie1.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>createCookie</td>
+            <td>addedCookieForPath1=new value1</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>createCookie</td>
+            <td>addedCookieForPath2=new value2</td>
+            <td>path=${base}/path2/, max_age=60</td>
+        </tr>
+        <tr>
+            <td>open</td>
+            <td>${base}/path1/cookie1.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyCookie</td>
+            <td>regex:addedCookieForPath1=new value1</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyNotCookie</td>
+            <td>regex:testCookie</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyNotCookie</td>
+            <td>regex:addedCookieForPath2</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>deleteCookie</td>
+            <td> addedCookieForPath1</td>
+            <td>${base}/path1/</td>
+        </tr>
+        <tr>
+            <td>verifyCookie</td>
+            <td></td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>open</td>
+            <td>${base}/path2/cookie2.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyCookie</td>
+            <td>addedCookieForPath2=new value2</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyNotCookie</td>
+            <td>regex:addedCookieForPath1</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>deleteCookie</td>
+            <td> addedCookieForPath2</td>
+            <td>${base}/path2/</td>
+        </tr>
+        <tr>
+            <td>verifyCookie</td>
+            <td></td>
+            <td>&nbsp;</td>
+        </tr>
+    </tbody>
+</table>
+
+Testing creating cookies with same name but diffrent paths
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr>
+            <td>createCookie</td>
+            <td>testCookieWithSameName=new value1</td>
+            <td>path=/</td>
+        </tr>
+        <tr>
+            <td>createCookie</td>
+            <td>testCookieWithSameName=new value2</td>
+            <td>path=${base}/path2/</td>
+        </tr>
+        <tr>
+            <td>open</td>
+            <td>${base}/path1/cookie1.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyCookie</td>
+            <td>testCookieWithSameName=new value1</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>open</td>
+            <td>${base}/path2/cookie2.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyCookie</td>
+            <td>regex:testCookieWithSameName=new value1</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyCookie</td>
+            <td>regex:testCookieWithSameName=new value2</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>deleteCookie</td>
+            <td> testCookieWithSameName</td>
+            <td>${base}/path2/</td>
+        </tr>
+        <tr>
+            <td>open</td>
+            <td>${base}/path2/cookie2.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyCookie</td>
+            <td>testCookieWithSameName=new value1</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyNotCookie</td>
+            <td>regex:testCookieWithSameName=new value2</td>
+            <td>&nbsp;</td>
+        </tr>
+    </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCssLocators.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCssLocators.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCssLocators.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,279 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta content="text/html; charset=ISO-8859-1"
+          http-equiv="content-type">
+    <title>Test CSS Locators</title>
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr>
+            <td rowspan="1" colspan="3">Test CSS Locators<br>
+            </td>
+        </tr>
+        <!--
+        Unimplemented features:
+            namespace
+            pseudo element
+                ::first-line
+                ::first-letter
+                ::selection
+                ::before
+                ::after
+            pseudo class including:
+                :nth-of-type
+                :nth-last-of-type
+                :first-of-type
+                :last-of-type
+                :only-of-type
+                :visited
+                :hover
+                :active
+                :focus
+                :indeterminate
+        -->
+
+        <tr>
+            <td>open</td>
+            <td>../tests/html/test_locators.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <!--css2 selector test-->
+        <!--universal selector-->
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>css=*</td>
+            <td>&nbsp;</td>
+        </tr>
+
+        <!--only element type-->
+        <tr>
+            <td>verifyText</td>
+            <td>css=p</td>
+            <td>this is the first element in the document</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=a</td>
+            <td>this is the first element</td>
+        </tr>
+        <!--id selector-->
+        <tr>
+            <td>verifyText</td>
+            <td>css=a#id3</td>
+            <td>this is the third element</td>
+        </tr>
+        <!--attribute selector-->
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>css=input[name]</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=a[href="#id3"]</td>
+            <td>this is the third element</td>
+        </tr>
+        <tr>
+            <td>verifyElementNotPresent</td>
+            <td>css=span[selenium:foo]</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=a[class~="class2"]</td>
+            <td>this is the fifth element</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=a[lang|="en"]</td>
+            <td>this is the sixth element</td>
+        </tr>
+
+        <!--class selector-->
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>css=a.a1</td>
+            <td>this is the first element</td>
+        </tr>
+        <!--pseudo class selector-->
+        <tr>
+            <td>verifyText</td>
+            <td>css=th:first-child</td>
+            <td>theHeaderText</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=a:lang(en)</td>
+            <td>this is the first element</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=#linkPseudoTest :link</td>
+            <td>link pseudo test</td>
+        </tr>
+        <!--descendant combinator-->
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#combinatorTest a</td>
+            <td>and grandson</td>
+        </tr>
+        <!--child combinator-->
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#combinatorTest &gt; span</td>
+            <td>this is a child and grandson</td>
+        </tr>
+        <!--preceding combinator-->
+        <tr>
+            <td>verifyText</td>
+            <td>css=span#firstChild + span</td>
+            <td>another child</td>
+        </tr>
+
+        <!--css3 selector test-->
+        <!--attribuite test-->
+        <tr>
+            <td>verifyText</td>
+            <td>css=a[name^="foo"]</td>
+            <td>foobar</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=a[name$="foo"]</td>
+            <td>barfoo</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=a[name*="zoo"]</td>
+            <td>foozoobar</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=a[name*="name"][alt]</td>
+            <td>this is the second element</td>
+        </tr>
+        <!--pseudo class test-->
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>css=html:root</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#structuralPseudo :nth-child(2n)</td>
+            <td>span2</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#structuralPseudo :nth-child(2)</td>
+            <td>span2</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#structuralPseudo :nth-child(-n+6)</td>
+            <td>span1</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#structuralPseudo :nth-last-child(4n+1)</td>
+            <td>span4</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#structuralPseudo :nth-last-child(2)</td>
+            <td>div3</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#structuralPseudo :nth-last-child(-n+6)</td>
+            <td>span3</td>
+        </tr>
+
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#structuralPseudo :first-child</td>
+            <td>span1</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#structuralPseudo :last-child</td>
+            <td>div4</td>
+        </tr>
+
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#onlyChild span:only-child</td>
+            <td>only child</td>
+        </tr>
+
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>css=span:empty</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#targetTest span:target</td>
+            <td>target</td>
+        </tr>
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>css=input[type="text"]:enabled</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>css=input[type="text"]:disabled</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>css=input[type="checkbox"]:checked</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=a:contains("zoo")</td>
+            <td>foozoobar</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#structuralPseudo span:not(:first-child)</td>
+            <td>span2</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#structuralPseudo :not(span):not(:last-child)</td>
+            <td>div1</td>
+        </tr>
+        <!--combinator test-->
+        <tr>
+            <td>verifyText</td>
+            <td>css=div#combinatorTest span#firstChild ~ span</td>
+            <td>another child</td>
+        </tr>
+
+
+
+    </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCursorPosition.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCursorPosition.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestCursorPosition.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,101 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestCursorPosition</title>
+</head>
+<body>
+    Note: This test is unreliable on Firefox, but it will normally work if you run it manually.  <a href="http://jira.openqa.org/browse/SEL-469">SEL-469</a>
+    
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestCursorPosition<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_type_page1.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyFailureOnNext</td>
+      <td>There is no cursor on this page!</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyCursorPosition</td>
+      <td>username</td>
+      <td>8</td>
+    </tr>
+    <tr>
+      <td>windowFocus</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>username</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>username</td>
+      <td>TestUser</td>
+    </tr>
+    <tr>
+      <td>setCursorPosition</td>
+      <td>username</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <td>verifyCursorPosition</td>
+      <td>username</td>
+      <td>0</td>
+    </tr>
+    <tr>
+      <td>setCursorPosition</td>
+      <td>username</td>
+      <td>-1</td>
+    </tr>
+    <tr>
+      <td>verifyCursorPosition</td>
+      <td>username</td>
+      <td>8</td>
+    </tr>
+    <tr>
+      <td>refreshAndWait</td>
+      <td>&nbsp;</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyFailureOnNext</td>
+      <td>There is no cursor on this page!</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyCursorPosition</td>
+      <td>username</td>
+      <td>8</td>
+    </tr>
+  </tbody>
+</table>
+Cursor location seems to be non-deterministic across browsers when there is no cursor.  This is filed as <a href="http://jira.openqa.org/browse/SEL-243">SEL-243</a>.
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestDojoDragAndDrop.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestDojoDragAndDrop.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestDojoDragAndDrop.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,47 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>TestDojoDragDrop</title>
+</head>
+<body>
+
+Warning: IE gives an access denied when running this test off the file system, but it works great off of a real live web server.
+
+<table cellpadding="1" cellspacing="1" border="1">
+<tbody>
+<tr><td rowspan="1" colspan="3">TestDojoDragDrop</td></tr>
+<tr>
+	<td>open</td>
+	<td>../tests/html/dojo-0.4.0-mini/tests/dnd/test_simple.html</td>
+	<td></td>
+</tr>
+<tr>
+	<td>dragAndDropToObject</td>
+	<td>//li[text()='list 1 item 3']</td>
+	<td>//li[text()='list 2 item 1']</td>
+</tr>
+<tr>
+	<td>assertTextPresent</td>
+	<td>either side of me*list 1 item 3</td>
+	<td></td>
+</tr>
+</tbody></table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestDragAndDrop.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestDragAndDrop.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestDragAndDrop.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,54 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>TestElementPresent</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<tbody>
+<tr><td rowspan="1" colspan="3">TestDragDrop</td></tr>
+<tr>
+	<td>open</td>
+	<td>../tests/html/slider/example.html</td>
+	<td></td>
+</tr>
+<tr>
+	<td>dragdrop</td>
+	<td>id=slider01</td>
+	<td>800,0</td>
+</tr>
+<tr>
+    	<td>assertValue</td>
+    	<td>id=output1</td>
+    	<td>20</td>
+</tr>
+<tr>
+	<td>dragdrop</td>
+	<td>id=slider01</td>
+	<td>-800,0</td>
+</tr>
+<tr>
+    	<td>assertValue</td>
+    	<td>id=output1</td>
+    	<td>0</td>
+</tr>
+</tbody></table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEditable.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEditable.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEditable.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,118 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test verifyEditable</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test verifyEditable<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_editable.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>verifyEditable</td>
+      <td>normal_text</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>verifyEditable</td>
+      <td>normal_select</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>verifyNotEditable</td>
+      <td>disabled_text</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>verifyNotEditable</td>
+      <td>disabled_select</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>true</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyNotEditable</td>
+      <td>normal_text</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>true</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyNotEditable</td>
+      <td>normal_select</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyEditable</td>
+      <td>disabled_text</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyEditable</td>
+      <td>disabled_select</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Element fake_input is not an input.</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyEditable</td>
+      <td>fake_input</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementIndex.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementIndex.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementIndex.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,65 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>TestElementIndex</title>
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr><td rowspan="1" colspan="3">TestElementIndex</td></tr>
+        <tr>
+            <td>open</td>
+            <td>../tests/html/test_element_order.html</td>
+            <td></td>
+        </tr>
+        <tr>
+            <td>assertElementIndex</td>
+            <td>d2</td>
+            <td>1</td>
+        </tr>
+        <tr>
+            <td>assertElementIndex</td>
+            <td>d1.1.1</td>
+            <td>0</td>
+        </tr>
+        <tr>
+            <td>verifyElementIndex</td>
+            <td>d2</td>
+            <td>1</td>
+        </tr>
+        <tr>
+            <td>verifyElementIndex</td>
+            <td>d1.2</td>
+            <td>5</td>
+        </tr>
+        <tr>
+            <td>assertNotElementIndex</td>
+            <td>d2</td>
+            <td>2</td>
+        </tr>
+        <tr>
+            <td>verifyNotElementIndex</td>
+            <td>d2</td>
+            <td>2</td>
+        </tr>
+    </tbody></table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementOrder.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementOrder.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementOrder.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,60 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>TestElementOrder</title>
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr><td rowspan="1" colspan="3">TestElementOrder</td></tr>
+        <tr>
+            <td>open</td>
+            <td>../tests/html/test_element_order.html</td>
+            <td></td>
+        </tr>
+        <tr>
+            <td>assertOrdered</td>
+            <td>s1.1</td>
+            <td>d1.1</td>
+        </tr>
+        <tr>
+            <td>assertNotOrdered</td>
+            <td>s1.1</td>
+            <td>s1.1</td>
+        </tr>
+        <tr>
+            <td>verifyOrdered</td>
+            <td>s1.1</td>
+            <td>d1.1</td>
+        </tr>
+        <tr>
+            <td>assertNotOrdered</td>
+            <td>d1.1</td>
+            <td>s1.1</td>
+        </tr>
+        <tr>
+            <td>verifyNotOrdered</td>
+            <td>s1.1</td>
+            <td>d2</td>
+        </tr>
+    </tbody></table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementPresent.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementPresent.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestElementPresent.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,69 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>TestElementPresent</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<tbody>
+<tr><td rowspan="1" colspan="3">TestElementPresent</td></tr>
+<tr>
+	<td>open</td>
+	<td>../tests/html/test_element_present.html</td>
+	<td></td>
+</tr>
+<tr>
+	<td>assertElementPresent</td>
+	<td>aLink</td>
+	<td></td>
+</tr>
+<tr>
+	<td>click</td>
+	<td>removeLinkAfterAWhile</td>
+	<td></td>
+</tr>
+<tr>
+	<td>waitForElementNotPresent</td>
+	<td>aLink</td>
+	<td></td>
+</tr>
+<tr>
+	<td>assertElementNotPresent</td>
+	<td>aLink</td>
+	<td></td>
+</tr>
+<tr>
+	<td>click</td>
+	<td>addLinkAfterAWhile</td>
+	<td></td>
+</tr>
+<tr>
+	<td>waitForElementPresent</td>
+	<td>aLink</td>
+	<td></td>
+</tr>
+<tr>
+	<td>assertElementPresent</td>
+	<td>aLink</td>
+	<td></td>
+</tr>
+</tbody></table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestErrorChecking.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestErrorChecking.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestErrorChecking.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,115 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- FIXME:DAZ Revert to 405 - these test must fail in order to verify that our "expectError" commands actually check the error value.
+     This should be made clearer in the test name and index.html -->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Click</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <p><em>This test should fail!</em></p>
+  <p>The following test script checks that when verifyFailureOnNext and verifyErrorOnNext are
+  used, the following command should fail and the verified error message must match the actual message.</p>
+  <p>If this functionality is working correctly, each of the following verifyText commands
+  should fail because the verifyFailure/Error doesn't match.</p>
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Tests for expectError and expectFailure commands<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_click_page1.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- These tests should all fail, as they are checking the error checking commands. -->
+    <tr>
+      <td>verifyFailureOnNext</td>
+      <td>The next command actually succeeds so this verify is wrong</td>
+      <td></td>
+    </tr>
+    <tr id="failure1">
+      <td>verifyText</td>
+      <td>link</td>
+      <td>Click here for next page</td>
+    </tr>
+    <tr>
+      <td>verifyErrorOnNext</td>
+      <td>The next command is an action that succeeds (instead of an accessor)</td>
+      <td></td>
+    </tr>
+    <tr id="failure2">
+      <td>echo</td>
+      <td>foo</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyFailureOnNext</td>
+      <td>The next command does fail, but this is the wrong message</td>
+      <td></td>
+    </tr>
+    <tr id="failure3">
+      <td>verifyText</td>
+      <td>link</td>
+      <td>foo</td>
+    </tr>
+
+    <tr>
+      <td>verifyErrorOnNext</td>
+      <td>The next command actually succeeds so this verify is wrong</td>
+      <td></td>
+    </tr>
+    <tr id="failure4">
+      <td>verifyText</td>
+      <td>link</td>
+      <td>Click here for next page</td>
+    </tr>
+    <tr>
+      <td>verifyErrorOnNext</td>
+      <td>The next command has a failure, not an error</td>
+      <td></td>
+    </tr>
+    <tr id="failure5">
+      <td>verifyText</td>
+      <td>link</td>
+      <td>foo</td>
+    </tr>
+  </tbody>
+</table>
+
+This next command must come last, because it causes a real error.
+
+<table>
+  <tbody>
+    <tr>
+      <td>verifyErrorOnNext</td>
+      <td>The next command does error, but this is the wrong message</td>
+      <td></td>
+    </tr>
+    <tr id="failure6">
+      <td>verifyText</td>
+      <td>notAlink</td>
+      <td>foo</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEval.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEval.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEval.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,44 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Eval</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Eval<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertEval</td>
+      <td>window.document.title</td>
+      <td>Open Test</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEvilClosingWindow.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEvilClosingWindow.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestEvilClosingWindow.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,76 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test SelectWindow</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test selectWindow<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_select_window.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>popupPage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopUp</td>
+      <td>myPopupWindow</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>myPopupWindow</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window_popup.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>close</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>Current window or frame is closed!</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window_popup.html</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>Leave the test in a selected window - the next test should begin in the main window</p>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFailingAssert.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFailingAssert.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFailingAssert.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,69 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Failing Assert</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Failing Assert</td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_verifications.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>Actual value 'the text value' did not match 'not the text value'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>assertValue</td>
+      <td>theText</td>
+      <td>not the text value</td>
+    </tr>
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>Actual value 'the text value' did match 'the text value'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>assertNotValue</td>
+      <td>theText</td>
+      <td>the text value</td>
+    </tr>
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>This element has no value; is it really a form field?</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>assertValue</td>
+      <td>theTable</td>
+      <td>x</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFailingVerifications.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFailingVerifications.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFailingVerifications.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,198 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Failing Verifications</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Failing Verifications</td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_verifications.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value '*test_verifications.html' did not match '*/tests/html/not_test_verifications.html'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/not_test_verifications.html</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'the text value' did not match 'not the text value'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>not the text value</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'the text value' did match 'the text value'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyNotValue</td>
+      <td>theText</td>
+      <td>the text value</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'the hidden value' did not match 'not the hidden value'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theHidden</td>
+      <td>not the hidden value</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'this is the span' did not match 'this is not the span'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>theSpan</td>
+      <td>this is not the span</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyTextPresent</td>
+      <td>this is not the span</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>true</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyTextNotPresent</td>
+      <td>this is the span</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>notTheSpan</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>true</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>theSpan</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'c' did not match 'a'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyTable</td>
+      <td>theTable.2.0</td>
+      <td>a</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Expected '2' but was '1'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>index=2</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'option2' did not match 'opt*3'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>value=opt*3</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'second option' did not match 'third option'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>third option</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'first option,second option,third\,\,option' did not match 'first\\,option,second option'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifySelectOptions</td>
+      <td>theSelect</td>
+      <td>first\\,option,second option</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'foo' did not match 'bar'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>theText at class</td>
+      <td>bar</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Actual value 'foo' did match 'foo'</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyNotAttribute</td>
+      <td>theText at class</td>
+      <td>foo</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFocusOnBlur.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFocusOnBlur.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFocusOnBlur.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,58 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Focus On Blur</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Focus On Blur<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_focus_on_blur.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>testInput</td>
+      <td>test</td>
+    </tr>
+    <tr>
+      <td>fireEvent</td>
+      <td>testInput</td>
+      <td>blur</td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>Bad value</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>testInput</td>
+      <td>somethingelse</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesClick.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesClick.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesClick.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,148 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestFramesClick</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestFramesClick<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/Frames.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>mainFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_click_page1.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Click a regular link -->
+    <tr>
+      <td>verifyText</td>
+      <td>link</td>
+      <td>Click here for next page</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>link</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page Target</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>previousPage</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page 1</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Click a link with an enclosed image -->
+    <tr>
+      <td>clickAndWait</td>
+      <td>linkWithEnclosedImage</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page Target</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>previousPage</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Click an image enclosed by a link -->
+    <tr>
+      <td>clickAndWait</td>
+      <td>enclosedImage</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page Target</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>previousPage</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Click a link with an href anchor target within this page -->
+    <tr>
+      <td>click</td>
+      <td>linkToAnchorOnThisPage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page 1</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Click a link where onclick returns false -->
+    <tr>
+      <td>click</td>
+      <td>linkWithOnclickReturnsFalse</td>
+      <td></td>
+    </tr>
+    <!-- Need a pause to give the page a chance to reload (so this test can fail) -->
+    <tr><td>pause</td><td>300</td><td/></tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page 1</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>setTimeout</td>
+      <td>5000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_click_page1.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- TODO Click a link with a target attribute -->
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesClickJavascriptHref.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesClickJavascriptHref.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesClickJavascriptHref.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,64 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestFramesClickJavaScriptHrefInWrongFrame</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestFramesClickJavaScriptHrefInWrongFrame<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/Frames.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>mainFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_click_javascript_page.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>relative=top</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>link</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAlert</td>
+      <td>link clicked: foo</td>
+      <td>&nbsp;</td>
+    </tr>
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesNested.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesNested.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesNested.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,207 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestFramesNested</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestFramesNested<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/NestedFrames.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>NestedFrames</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextNotPresent</td>
+      <td>This is a test</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>mainFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>NestedFrames2</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>mainFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>AUT</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>mainFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextPresent</td>
+      <td>This is a test</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>relative=up</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>AUT</td>
+      <td>&nbsp;</td>
+    </tr>
+	<tr>
+      <td>verifyTextNotPresent</td>
+      <td>This is a test</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>Select a frame using a DOM expression pointing to the frame itself</p>
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>selectFrame</td>
+      <td>relative=top</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>NestedFrames</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>dom=window.frames[1]</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>NestedFrames2</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>Select a frame by index</p>
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>selectFrame</td>
+      <td>relative=top</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>NestedFrames</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>index=1</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>NestedFrames2</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>Select a frame using a locator expression pointing to the frame element (in this case, "foo" is the ID of the mainFrame element)</p>
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>selectFrame</td>
+      <td>relative=top</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>NestedFrames</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>foo</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>NestedFrames2</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>Select a frame using a DOM expression pointing to a nested frame</p>
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>selectFrame</td>
+      <td>relative=top</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>NestedFrames</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>dom=window.frames["mainFrame"].frames["mainFrame"]</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>AUT</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesOpen.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesOpen.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesOpen.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,92 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestFramesOpen</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestFramesOpen<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/Frames.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectFrame</td>
+      <td>mainFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextPresent</td>
+      <td>This is a test of the open command.<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_page.slow.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_page.slow.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>setTimeout</td>
+      <td>5000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesSpecialTargets.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesSpecialTargets.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFramesSpecialTargets.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,111 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestFramesSpecialTargets</title>
+</head>
+<body>
+    <p>This test reproduces bug SEL-272</p>
+    
+    <p>_top</p>
+    
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestFramesSpecialTargets<br>
+      </td>
+    </tr>
+    
+    <tr>
+      <td>openWindow</td>
+      <td>../tests/html/Frames.html</td> 
+      <td>SpecialTargets</td>
+    </tr>
+    
+    <tr>
+      <td>waitForPopUp</td>
+      <td>SpecialTargets</td> 
+      <td>10000</td>
+    </tr>
+    
+    <tr>
+      <td>selectWindow</td>
+      <td>SpecialTargets</td> 
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>selectFrame</td>
+      <td>bottomFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>clickAndWait</td>
+      <td>changeTop</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>click</td>
+      <td>changeSpan</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>_parent</p>
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/Frames.html</td> 
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>selectFrame</td>
+      <td>bottomFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>clickAndWait</td>
+      <td>changeParent</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>click</td>
+      <td>changeSpan</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>close</td>
+      <td></td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFunkEventHandling.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFunkEventHandling.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestFunkEventHandling.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,53 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Funky Event Handling</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Funky Event Handling<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_funky_event_handling.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>clickMe</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>pause</td>
+      <td>1000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextNotPresent</td>
+      <td>You shouldn't be here!</td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestGoBack.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestGoBack.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestGoBack.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,81 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2005 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Back and Forward</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Back and Forward<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_click_page1.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page 1</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Click a regular link -->
+    <tr>
+      <td>clickAndWait</td>
+      <td>link</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page Target</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>goBackAndWait</td>
+      <td>&nbsp;</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page 1</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- history.forward() generates 'Permission Denied' in IE 
+    <tr>
+      <td>goForward</td>
+      <td>&nbsp;</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Click Page Target</td>
+      <td>&nbsp;</td>
+    </tr>
+    -->
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestHighlight.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestHighlight.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestHighlight.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,46 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Highlight</title>
+</head>
+<body>
+    <p>Note that this test only verifies that the "highlight" command doesn't throw; it doesn't (can't?) prove that the element's appearance changed, nor does it test the "Highlight Elements" checkbox in the control panel.</p><p>(That would be a good dogfood test, but since we can't verify the appearance change, what's the point?)</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Highlight<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_locators.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>highlight</td>
+      <td>id1</td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestHtmlSource.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestHtmlSource.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestHtmlSource.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,39 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+  Test getHtmlSource Command
+  
+  Robert Zimmermann - 2006-10-25
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test HtmlSource</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test HtmlSource<br>
+      </td>
+    </tr>
+    
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_html_source.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+        <td>verifyHtmlSource</td>
+        <td>*Text is here*</td>
+        <td></td>
+    </tr>
+    <tr>
+        <td>verifyNotHtmlSource</td>
+        <td>*can not be found*</td>
+        <td></td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestImplicitLocators.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestImplicitLocators.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestImplicitLocators.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,81 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Implicit Locators</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Implicit Locators<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_locators.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>verifyText</td>
+      <td>id1</td>
+      <td>this is the first element</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>id1 at class</td>
+      <td>a1</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>name1</td>
+      <td>this is the second element</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>name1 at class</td>
+      <td>a2</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>document.links[1]</td>
+      <td>this is the second element</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>document.links[1]@class</td>
+      <td>a2</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>//img[contains(@src, 'banner.gif')]/@alt</td>
+      <td>banner</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>//body/a[2]</td>
+      <td>this is the second element</td>
+    </tr>
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-for-loops.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-for-loops.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-for-loops.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,38 @@
+<html>
+<head>
+ <title>Test JavaScript For-Loops</title>
+ <link rel="stylesheet" type="text/css" href="../core/selenium_table.css" />
+
+ <script type="text/javascript" src="../mochikit/MochiKit/MochiKit.js"></script>
+ <script type="text/javascript" src="../core/scripts/narcissus-defs.js"></script>
+ <script type="text/javascript" src="../core/scripts/narcissus-parse.js"></script>
+ <script type="text/javascript" src="../core/scripts/narcissus-exec.js"></script>
+ <script type="text/javascript" src="../core/scripts/js2html.js"></script>
+
+ <script id="setup" type="text/javascript">
+    // put any setup or utility functions here..
+
+ </script>
+
+</head>
+<body onload="jsparse(document);">
+
+<script id="sejs" type="text/se+js">
+
+_url = "../tests//html/show_message.html?msg="
+
+
+for (var _i=0;_i<25;_i++) {
+    se.doOpen(_url+_i)
+    se.doWaitForPageToLoad(5000)
+}
+
+se.doOpen(_url+'the')
+se.doWaitForPageToLoad(5000)
+se.doOpen(_url+'end')
+se.doWaitForPageToLoad(5000)
+
+</script>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-functions.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-functions.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-functions.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,38 @@
+<html>
+<head>
+ <title>Test JavaScript Functions</title>
+ <link rel="stylesheet" type="text/css" href="../core/selenium_table.css" />
+
+ <script type="text/javascript" src="../core/scripts/narcissus-defs.js"></script>
+ <script type="text/javascript" src="../core/scripts/narcissus-parse.js"></script>
+ <script type="text/javascript" src="../core/scripts/narcissus-exec.js"></script>
+ <script type="text/javascript" src="../core/scripts/js2html.js"></script>
+
+ <script id="setup" type="text/javascript">
+    // put any setup or utility functions here..
+
+ </script>
+
+</head>
+<body onload="jsparse(document);">
+
+<script id="sejs" type="text/se+js">
+
+var _url = "../tests/html/show_message.html?msg="
+
+
+// You can refactor common code into functions...
+puts = function(i) {
+    se.doOpen(_url+i)
+    se.doWaitForPageToLoad(5000)
+};
+
+puts ('This_was_printed_from_inside_a_function')
+
+// define and call in one line
+one_liner = function() { se.doOpen(_url+'Neat!'); se.doWaitForPageToLoad(5000); }()
+
+</script>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-if-then-else.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-if-then-else.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJS-if-then-else.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,47 @@
+<html>
+<head>
+ <title>Test JavaScript If-Then-Else</title>
+ <link rel="stylesheet" type="text/css" href="../core/selenium_table.css" />
+
+ <script type="text/javascript" src="../mochikit/MochiKit/MochiKit.js"></script>
+ <script type="text/javascript" src="../core/scripts/narcissus-defs.js"></script>
+ <script type="text/javascript" src="../core/scripts/narcissus-parse.js"></script>
+ <script type="text/javascript" src="../core/scripts/narcissus-exec.js"></script>
+ <script type="text/javascript" src="../core/scripts/js2html.js"></script>
+
+ <script id="setup" type="text/javascript">
+    // put any setup or utility functions here..
+
+ </script>
+
+</head>
+<body onload="jsparse(document);">
+
+<script id="sejs" type="text/se+js" onload="jsparse(document);">
+var _url = "../tests/html/show_message.html?msg="
+
+puts = function(message) {
+    se.doOpen(_url+message)
+};
+
+if (1) {
+    if (1) {
+        // a nested function call inside two 'if' blocks...
+        puts ('hello')
+        puts ('world')
+    }
+}
+
+if (1) {
+    if (0) {
+        // a nested function call inside two 'if' blocks...
+        puts ("this_won't_get_called")
+    } else {
+        puts ("no_goto_here!")
+    }
+}
+
+</script>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJSSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJSSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJSSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,59 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Test Suite</title>
+<link rel="stylesheet" type="text/css" href="../core/selenium.css" />
+<script language="JavaScript" type="text/javascript" src="../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript">
+    function filterTestsForBrowser() {
+        var suiteTable = document.getElementById("suiteTable");
+        var skippedTests = document.getElementById("skippedTests");
+
+        for(rowNum = suiteTable.rows.length - 1; rowNum >= 0; rowNum--)
+        {
+            var row = suiteTable.rows[rowNum];
+            var filterString = row.getAttribute("unless");
+            if (filterString && eval(filterString))
+            {
+              var cellHTML = row.cells[0].innerHTML;
+              suiteTable.deleteRow(rowNum);
+
+              var newRow = skippedTests.insertRow(1);
+              var newCell = newRow.insertCell(0)
+              newCell.innerHTML = cellHTML;
+            }
+        }
+    }
+</script>
+</head>
+
+<body onload="filterTestsForBrowser()">
+
+    <table id="suiteTable" class="selenium">
+        <tbody>
+            <tr><td><b>Se/JS Test Suite</b></td></tr>
+            <tr><td><a href="./TestJS-functions.html">TestJS-functions</a></td></tr>
+            <tr><td><a href="./TestJS-if-then-else.html">TestJS-if-then-else</a></td></tr>
+            <tr><td><a href="./TestJS-for-loops.html">TestJS-for-loops</a></td></tr>
+
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJavaScriptAttributes.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJavaScriptAttributes.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJavaScriptAttributes.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,49 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test JavaScript Attributes</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestJavaScriptAttributes<br>
+      </td>
+    </tr>
+    
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_javascript_attributes.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>//a[@onclick="alert('foo')"]</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertAlert</td>
+      <td>foo</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJavascriptParameters.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJavascriptParameters.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestJavascriptParameters.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,119 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test javascript evaluation of parameters*</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test javascript evaluation of parameters<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_store_value.html</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>javascript{[1,2,3,4,5].join(':')}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>1:2:3:4:5</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>javascript{'the' + 'Text'}</td>
+      <td>javascript{10 * 5}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>50</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>javascript{10 + 10 + 10 + 10 + 10}</td>
+    </tr>
+
+    <!-- Check a complex expression -->
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>javascript{
+              function square(n) {
+                  return n * n;
+              };
+              '25 * 25 = ' + square(25);
+          }
+      </td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>25 * 25 = 625</td>
+    </tr>
+
+    <!-- Demonstrate interation between variable substitution and javascript -->
+    <tr>
+      <td>store</td>
+      <td>the value</td>
+      <td>var1</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>javascript{'${var1}'.toUpperCase()}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>${VAR1}</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>javascript{storedVars['var1'].toUpperCase()}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>THE VALUE</td>
+    </tr>
+    <tr>
+      <td>verifyExpression</td>
+      <td>javascript{storedVars['var1'].toUpperCase()}</td>
+      <td>THE VALUE</td>
+    </tr>
+    <tr>
+      <td>verifyExpression</td>
+      <td>javascript{selenium.getValue('theText')}</td>
+      <td>regexp:TH[Ee] VALUE</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestLocators.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestLocators.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestLocators.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,186 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Locators</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Locators<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_locators.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Id location -->
+    <tr>
+      <td>verifyText</td>
+      <td>id=id1</td>
+      <td>this is the first element</td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>id=name1</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>id=id4</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>id=id1 at class</td>
+      <td>a1</td>
+    </tr>
+
+    <!-- name location -->
+    <tr>
+      <td>verifyText</td>
+      <td>name=name1</td>
+      <td>this is the second element</td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>name=id1</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>name=notAName</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>name=name1 at class</td>
+      <td>a2</td>
+    </tr>
+
+    <!-- class location -->
+
+    <tr>
+      <td>verifyText</td>
+      <td>class=a3</td>
+      <td>this is the third element</td>
+    </tr>
+
+    <!-- alt location -->
+
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>alt=banner</td>
+      <td></td>
+    </tr>
+
+    <!-- identifier location -->
+    <tr>
+      <td>verifyText</td>
+      <td>identifier=id1</td>
+      <td>this is the first element</td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>identifier=id4</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>identifier=id1 at class</td>
+      <td>a1</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>identifier=name1</td>
+      <td>this is the second element</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>identifier=name1 at class</td>
+      <td>a2</td>
+    </tr>
+
+
+    <!-- DOM Traversal location -->
+    <tr>
+      <td>verifyText</td>
+      <td>dom=document.links[1]</td>
+      <td>this is the second element</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>dom=function foo() {return document.links[1];}; foo();</td>
+      <td>this is the second element</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>dom=document.links[1]@class</td>
+      <td>a2</td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>dom=document.links[9]</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>dom=foo</td>
+      <td></td>
+    </tr>
+
+    <!-- Link location -->
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>link=this is the second element</td>
+      <td>a2</td>
+    </tr>
+    <tr>
+        <td>assertTextPresent</td>
+        <td>this is the second element</td>
+        <td></td>
+    </tr>
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>link=this * second element</td>
+      <td>a2</td>
+    </tr>
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>link=regexp:this [aeiou]s the second element</td>
+      <td>a2</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>link=this is the second element at class</td>
+      <td>a2</td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>link=this is not an element</td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestModalDialog.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestModalDialog.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestModalDialog.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,98 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Modal Dialog</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestModalDialog (Only works in IE)<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_modal_dialog.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_modal_dialog.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Modal Dialog Host Window</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>changeText</td>
+      <td>before modal dialog</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>modal</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopup</td>
+      <td>Modal Dialog Popup</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>Modal Dialog Popup</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Modal Dialog Popup</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>change</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>close</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>Modal Dialog Host Window</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>changeText</td>
+      <td>after modal dialog</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Modal Dialog Popup</td>
+      <td>&nbsp;</td>
+    </tr>       
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestMultiSelect.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestMultiSelect.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestMultiSelect.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,175 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Select</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Multiple Select<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_multiselect.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertSelectedLabels</td>
+      <td>theSelect</td>
+      <td>Second Option</td>
+    </tr>
+  </tbody>
+</table>
+
+    <p>Select a single element (replaces existing selection)</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>index=4</td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabels</td>
+      <td>theSelect</td>
+      <td>Fifth Option</td>
+    </tr>
+  </tbody>
+</table>
+
+    <p>Select additional elements</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>addSelection</td>
+      <td>theSelect</td>
+      <td>Third Option</td>
+    </tr>
+    <tr>
+      <td>addSelection</td>
+      <td>theSelect</td>
+      <td>value=</td>
+    </tr>
+
+    <tr>
+      <td>verifySelectedLabels</td>
+      <td>theSelect</td>
+      <td>Third Option,Fifth Option,Empty Value Option</td>
+    </tr>
+
+    <tr>
+      <td>removeSelection</td>
+      <td>theSelect</td>
+      <td>id=o7</td>
+    </tr>
+    <tr>
+       <td>verifySelectedLabels</td>
+       <td>theSelect</td>
+       <td>Third Option,Fifth Option</td>
+     </tr>
+    <tr>
+      <td>removeSelection</td>
+      <td>theSelect</td>
+      <td>label=Fifth Option</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>Third Option</td>
+    </tr>
+
+    <tr>
+      <td>addSelection</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabels</td>
+      <td>theSelect</td>
+      <td>Third Option,</td>
+    </tr>
+
+    <tr>
+      <td>removeSelection</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>removeSelection</td>
+      <td>theSelect</td>
+      <td>Third Option</td>
+    </tr>
+
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>No option selected</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>No option selected</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabels</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyNotSomethingSelected</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>addSelection</td>
+      <td>theSelect</td>
+      <td>Third Option</td>
+    </tr>
+    <tr>
+      <td>addSelection</td>
+      <td>theSelect</td>
+      <td>value=</td>
+    </tr>
+    <tr>
+      <td>removeAllSelections</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyNotSomethingSelected</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpen.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpen.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpen.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,134 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Open</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Open<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <!-- Should really split these verifications into their own test file.-->
+    <tr>
+      <td>verifyLocation</td>
+      <td>regexp:.*/tests/html/[Tt]est_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotLocation</td>
+      <td>*/foo.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextPresent</td>
+      <td>glob:This is a test of the open command.<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextPresent</td>
+      <td>This is a test of the open command.<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextPresent</td>
+      <td>exact:This is a test of<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextPresent</td>
+      <td>regexp:This is a test of<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextPresent</td>
+      <td>regexp:T*his is a test of<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextNotPresent</td>
+      <td>exact:XXXXThis is a test of<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTextNotPresent</td>
+      <td>regexp:ThXXXXXXXXXis is a test of<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_page.slow.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_page.slow.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>setTimeout</td>
+      <td>5000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpenInTargetFrame.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpenInTargetFrame.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpenInTargetFrame.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,93 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta content="text/html; charset=ISO-8859-1"
+          http-equiv="content-type">
+    <title>TestFramesOpenWithHREF</title>
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr>
+            <td rowspan="1" colspan="3">TestOpenInTargetFrame<br>
+            </td>
+        </tr>
+
+
+        <tr>
+            <td>open</td>
+            <td>../tests/html/test_open_in_target_frame.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>selectFrame</td>
+            <td>rightFrame</td>
+            <td>&nbsp;</td>
+        </tr>
+
+        <tr>
+            <td>click</td>
+            <td>link=Show new frame in leftFrame</td>
+            <td>&nbsp;</td>
+        </tr>
+
+         <!-- we are forced to do a pause instead of clickandwait here,
+                for currently we can not detect target frame loading in ie yet -->
+
+
+
+        <tr>
+            <td>pause</td>
+            <td>1500</td>
+            <td>&nbsp;</td>
+        </tr>
+
+        <tr>
+            <td>verifyTextPresent</td>
+            <td>Show new frame in leftFrame</td>
+            <td>&nbsp;</td>
+        </tr>
+
+        <tr>
+            <td>selectFrame</td>
+            <td>relative=top</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>selectFrame</td>
+            <td>leftFrame</td>
+            <td>&nbsp;</td>
+        </tr>
+
+        <tr>
+            <td>verifyTextPresent</td>
+            <td>content loaded</td>
+            <td>&nbsp;</td>
+        </tr>
+
+        <tr>
+            <td>verifyTextNotPresent</td>
+            <td>This is frame LEFT</td>
+            <td>&nbsp;</td>
+        </tr>
+
+    </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpen_SSV_syntax.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpen_SSV_syntax.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestOpen_SSV_syntax.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,21 @@
+<html>
+<head>
+ <title>Test Open</title>
+ <link rel="stylesheet" type="text/css" href="../core/selenium_table.css" />
+ <script type="text/javascript" src="../core/scripts/se2html.js"></script>
+</head>
+<body onload="separse(document);">
+
+    <script id="testcase" type="text/se++">
+        // Test Open (using new syntax)
+        open              "../tests/html/test_open.html"
+        verifyLocation    "*/tests/html/test_open.html"
+        verifyTextPresent "This is a test of the open command."
+        break             ""
+        open              "../tests/html/test_page.slow.html"
+        verifyLocation    "*/tests/html/test_page.slow.html"
+        verifyTitle       "Slow Loading Page"    // this is a comment
+    </script>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPatternMatching.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPatternMatching.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPatternMatching.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,155 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Pattern Matching</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Pattern Matching<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_verifications.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- check glob (wildcard) matching -->
+
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>*text*</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theHidden</td>
+      <td>* hidden value</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>theSpan</td>
+      <td>* span</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>second *</td>
+    </tr>
+    <tr>
+      <td>verifySelectOptions</td>
+      <td>theSelect</td>
+      <td>first*,second*,third*</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>theText at class</td>
+      <td>?oo</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theTextarea</td>
+      <td>Line 1*</td>
+    </tr>
+
+    <!-- check regexp (wildcard) matching -->
+
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>regexp:^[a-z ]+$</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theHidden</td>
+      <td>regexp:dd</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>theSpan</td>
+      <td>regexp:span$</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>regexp:second .*</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>theText at class</td>
+      <td>regexp:^f</td>
+    </tr>
+
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>regex:^[a-z ]+$</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theHidden</td>
+      <td>regex:dd</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>theSpan</td>
+      <td>regex:span$</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>regex:second .*</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>theText at class</td>
+      <td>regex:^f</td>
+    </tr>
+
+    <!-- check exact matching -->
+
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>exact:the text value</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>exact:second option</td>
+    </tr>
+
+    <!-- check a mixture of strategies -->
+
+    <tr>
+      <td>verifySelectOptions</td>
+      <td>theSelect</td>
+      <td>regexp:^first.*?,second option,third*</td>
+    </tr>
+
+  </tbody>
+</table>
+</body>
+
+
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPause.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPause.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPause.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,78 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Select and Pause</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Select and Pause for Reload<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_reload_onchange_page.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <!-- Make sure we can pause even when the page doesn't change -->
+    <tr>
+      <td>pause</td>
+      <td>100</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr><td>verifyTitle</td><td>Reload Page</td><td/></tr>
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>Second Option</td>
+    </tr>
+
+    <!-- Make sure we can pause to wait for a page reload -->
+    <!-- Must pause longer than the slow-loading page takes (500ms) -->
+    <tr>
+      <td>pause</td>
+      <td>5000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>theSelect</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>theSpan</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPrompt.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPrompt.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestPrompt.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,109 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Prompting Verifification</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test verify Prompting<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_prompt.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyPromptNotPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>assertPromptNotPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>answerOnNextPrompt</td>
+      <td>no</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>promptAndLeave</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyPromptPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPromptPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>assertPromptPresent</td>
+      <td></td>
+      <td></td>
+    </tr>
+    
+    <tr>
+      <td>verifyPrompt</td>
+      <td>Type 'yes' and click OK</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Test Prompt</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>answerOnNextPrompt</td>
+      <td>yes</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>promptAndLeave</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyPrompt</td>
+      <td>*'yes'*</td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>verifyTitle</td>
+      <td>Dummy Page</td>
+      <td></td>
+    </tr>
+
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestQuickOpen.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestQuickOpen.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestQuickOpen.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,55 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Open</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+        <td rowspan="1" colspan="3">Test Quick Open<br>
+      </td>
+    </tr>
+    <!--<tr>
+      <td>setTimeout</td>
+      <td>5000</td>
+      <td>&nbsp;</td>
+    </tr>-->
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_page.slow.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+    	<td>verifyTextPresent</td>
+    	<td>This is a slow-loading page</td>
+    	<td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestRefresh.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestRefresh.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestRefresh.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,133 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Refresh</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Refresh<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_page.slow.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_page.slow.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page<br>
+      </td>
+      <td>&nbsp;</td>
+    </tr>
+</table>
+
+Change the text dynamically, then refresh to make sure the text reverts
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>changeSpan</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertTextPresent</td>
+      <td>Changed the text</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>refreshAndWait</td>
+      <td>&nbsp;</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertTextNotPresent</td>
+      <td>Changed the text</td>
+      <td>&nbsp;</td>
+    </tr>
+</table>
+
+Change the text dynamically again, then click on a button to do a slow refresh
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>changeSpan</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertTextPresent</td>
+      <td>Changed the text</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>slowRefresh</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertTextNotPresent</td>
+      <td>Changed the text</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+And again with an anchor in the URL
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>anchor</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>changeSpan</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertTextPresent</td>
+      <td>Changed the text</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>refreshAndWait</td>
+      <td>&nbsp;</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertTextNotPresent</td>
+      <td>Changed the text</td>
+      <td>&nbsp;</td>
+    </tr>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelect.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelect.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelect.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,253 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Select</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Select<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_select.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertSomethingSelected</td>
+      <td>theSelect</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertSelected</td>
+      <td>theSelect</td>
+      <td>Second Option</td>
+    </tr>
+</tbody>
+</table>
+
+Index
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>index=4</td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabel</td>
+      <td>theSelect</td>
+      <td>Fifth Option</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>index=4</td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabel</td>
+      <td>theSelect</td>
+      <td>Fifth Option</td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabels</td>
+      <td>theSelect</td>
+      <td>Fifth Option</td>
+    </tr>
+</tbody>
+</table>
+Label
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>    
+
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>Third Option</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>Third Option</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>label=Third Option</td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabel</td>
+      <td>theSelect</td>
+      <td>Third Option</td>
+    </tr>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>label=Fourth Option</td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabel</td>
+      <td>theSelect</td>
+      <td>Fourth Option</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>Fourth Option</td>
+    </tr>
+</tbody>
+</table>
+Value
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>value=option6</td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabel</td>
+      <td>theSelect</td>
+      <td>Sixth Option</td>
+    </tr>
+    <tr>
+      <td>verifySelectedValue</td>
+      <td>theSelect</td>
+      <td>option6</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>value=option6</td>
+    </tr>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>value=</td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabel</td>
+      <td>theSelect</td>
+      <td>Empty Value Option</td>
+    </tr>
+</tbody>
+</table>
+
+IDs
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>id=o4</td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabel</td>
+      <td>theSelect</td>
+      <td>Fourth Option</td>
+    </tr>
+    <tr>
+      <td>verifySelectedId</td>
+      <td>theSelect</td>
+      <td>o4</td>
+    </tr>
+</tbody>
+</table>
+
+No text = Empty label
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabel</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifySelectedLabels</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+</tbody>
+</table>
+Select an option that doesn't exist
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>Option with label 'Not an option' not found</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>Not an option</td>
+    </tr>
+</tbody>
+</table>
+Multi-select commands will not work
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>Not a multi-select</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>addSelection</td>
+      <td>theSelect</td>
+      <td>Fourth Option</td>
+    </tr>
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>Not a multi-select</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>removeSelection</td>
+      <td>theSelect</td>
+      <td>Fourth Option</td>
+    </tr>
+</tbody>
+</table>
+assertSelectOptions
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    
+    <tr>
+      <td>verifySelectOptions</td>
+      <td>theSelect</td>
+      <td>First Option,Second Option,Third Option,Fourth Option,Fifth Option,Sixth Option,Empty Value Option,</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectMultiLevelFrame.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectMultiLevelFrame.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectMultiLevelFrame.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,57 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2005 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Multi-Level Frame</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Multi-Level Frame<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_multi_level_frame.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Select first level frame -->
+    <tr>
+      <td>selectFrame</td>
+      <td>frame2</td>
+      <td>&nbsp;</td>
+    </tr>
+   <tr>
+      <td>selectFrame</td>
+      <td>theFrame</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertTextPresent</td>
+      <td>Click here for next page via absolute link</td>
+      <td>&nbsp;</td>
+    </tr>
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectWindow.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectWindow.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectWindow.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,187 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test SelectWindow</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test selectWindow<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_select_window.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>popupPage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopUp</td>
+      <td>myPopupWindow</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>myPopupWindow</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window_popup.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Select Window Popup</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyAllWindowNames</td>
+      <td>*,*</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyAllWindowNames</td>
+      <td>regexp:myPopupWindow</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>close</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>null</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>click</td>
+      <td>popupPage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopUp</td>
+      <td>myPopupWindow</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>myPopupWindow</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window_popup.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>close</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>null</td>
+      <td>&nbsp;</td>
+    </tr>
+</table>
+<p>Select an anonymous window (one that isn't assigned to a variable)</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>popupAnonymous</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopUp</td>
+      <td>anonymouspopup</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>anonymouspopup</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window_popup.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>closePage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>null</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>Try onclick close handler</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>popupAnonymous</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopUp</td>
+      <td>anonymouspopup</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>anonymouspopup</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window_popup.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>closePage2</td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+
+<p>Leave the test in a selected window - the next test should begin in the main window</p>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectWindowTitle.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectWindowTitle.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSelectWindowTitle.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,187 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test SelectWindow by title</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test selectWindow by title<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_select_window.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>popupPage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopUp</td>
+      <td>myPopupWindow</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>Select Window Popup</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window_popup.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Select Window Popup</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyAllWindowNames</td>
+      <td>*,*</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyAllWindowNames</td>
+      <td>regexp:myPopupWindow</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>close</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>null</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>click</td>
+      <td>popupPage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopUp</td>
+      <td>myPopupWindow</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>myPopupWindow</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window_popup.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>close</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>null</td>
+      <td>&nbsp;</td>
+    </tr>
+</table>
+<p>Select an anonymous window (one that isn't assigned to a variable)</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>popupAnonymous</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopUp</td>
+      <td>anonymouspopup</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>anonymouspopup</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window_popup.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>closePage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>null</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+<p>Try onclick close handler</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>popupAnonymous</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopUp</td>
+      <td>anonymouspopup</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>anonymouspopup</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_select_window_popup.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>closePage2</td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+
+<p>Leave the test in a selected window - the next test should begin in the main window</p>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestStore.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestStore.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestStore.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,193 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test store*</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test store and variations<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_verifications.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>storeValue</td>
+      <td>theHidden</td>
+      <td>storedHiddenValue</td>
+    </tr>
+    <tr>
+      <td>storeText</td>
+      <td>theSpan</td>
+      <td>storedSpanText</td>
+    </tr>
+    <tr>
+      <td>storeAttribute</td>
+      <td>theText at class</td>
+      <td>storedTextClass</td>
+    </tr>
+    <tr>
+        <td>storeTitle</td>
+    	<td>storedTitle</td>
+        <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>store</td>
+      <td>PLAIN TEXT</td>
+      <td>textVariable</td>
+    </tr>
+    <tr>
+              <!-- The expression for the 2nd parm used to be: javascript{'javascript' + 'Variable'}
+      For me to support this kind of thing, I would need to save "stored" variables in a 
+      hash, since the names wouldn't be known at compile time.  I think this is not the
+      best translation; surely test writers will prefer that new variables in the native
+      language be instantiated.  So I'm changing the expression to a literal:  -ns  -->
+
+      <td>store</td>
+      <td>javascript{'Pi ~= ' +
+                     (Math.round(Math.PI * 100) / 100)}</td> 
+      <td>javascriptVariable</td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_store_value.html</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>${storedHiddenValue}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>the hidden value</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>${storedSpanText}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>this is the span</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>${storedTextClass}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>foo</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>${textVariable}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>PLAIN TEXT</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>${javascriptVariable}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>Pi ~= 3.14</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>${storedTitle}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>theTitle</td>
+    </tr>
+
+    <!-- Test multiple output variables in a single expression -->
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>'${storedHiddenValue}'_'${storedSpanText}'</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>'the hidden value'_'this is the span'</td>
+    </tr>
+
+    <!-- backward compatibility -->
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_just_text.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr> <!-- This new command should replace the old usage of storeValue -->
+      <td>storeBodyText</td>
+      <td>storedBodyText</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_store_value.html</td>
+      <td/>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theText</td>
+      <td>${storedBodyText}</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>This is the entire text of the page.</td>
+    </tr>
+
+    <tr>
+      <td>verifyExpression</td>
+      <td>${storedBodyText}</td>
+      <td>This is the entire text of the page.</td>
+    </tr>
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSubmit.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSubmit.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSubmit.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,76 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2005 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestSubmit</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestSubmit<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_submit.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>submit</td>
+      <td>searchForm</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertAlert</td>
+      <td>onsubmit called</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>check</td>
+      <td>okayToSubmit</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>submit</td>
+      <td>searchForm</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertAlert</td>
+      <td>onsubmit called</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertAlert</td>
+      <td>form submitted</td>
+      <td>&nbsp;</td>
+    </tr>
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSuite-UserExtensions.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSuite-UserExtensions.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSuite-UserExtensions.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,36 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Test Suite</title>
+
+</head>
+
+<body>
+
+<table     cellpadding="1"
+           cellspacing="1"
+           border="1">
+        <tbody>
+            <tr><td><b>Test Suite</b></td></tr>
+            <tr><td><a href="./TestUserExtensions.html">TestUserExtensions</a></td></tr>
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,164 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Test Suite</title>
+<script language="JavaScript" type="text/javascript" src="../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript">
+    var DISABLED = true; // used to flag failing tests
+
+    function filterTestsForBrowser() {
+        var suiteTable = document.getElementById("suiteTable");
+        var skippedTests = document.getElementById("skippedTests");
+
+        for(rowNum = suiteTable.rows.length - 1; rowNum >= 0; rowNum--)
+        {
+            var row = suiteTable.rows[rowNum];
+            var filterString = row.getAttribute("unless");
+            if (filterString && eval(filterString))
+            {
+              var cellHTML = row.cells[0].innerHTML;
+              suiteTable.deleteRow(rowNum);
+
+              var newRow = skippedTests.insertRow(1);
+              var newCell = newRow.insertCell(0)
+              newCell.innerHTML = cellHTML;
+            }
+        }
+    }
+    
+    function isFileURL() {
+        var p = window.location.protocol;
+        var result = ("file:" == p);
+        return result;
+    }
+</script>
+</head>
+
+<body onload="filterTestsForBrowser()">
+
+    <table id="suiteTable"    cellpadding="1"
+           cellspacing="1"
+           border="1"
+           class="selenium">
+        <tbody>
+		<tr><td><b>Test Suite</b></td></tr>
+            <tr unless="browserVersion.isSafari" ><td><a href="./events/TestMouseEvents.html">TestMouseEvents</a></td></tr>
+		    <tr><td><a href="./TestBrowserVersion.html">TestBrowserVersion</a></td></tr>
+		    
+            <tr><td><a href="./TestJavaScriptAttributes.html">TestJavaScriptAttributes</a></td></tr>
+            
+            <tr unless="browserVersion.isKonqueror || browserVersion.isSafari"><td><a href="./events/TestKeyEvents.html">TestKeyEvents</a></td></tr>
+            <tr unless="browserVersion.isKonqueror || browserVersion.isSafari"><td><a href="./TestVisibility.html">TestVisibility</a></td></tr>
+                                                                   
+            <tr><td><a href="./TestFramesOpen.html">TestFramesOpen</a></td></tr>
+            <tr><td><a href="./TestFramesNested.html">TestFramesNested</a></td></tr>
+            <tr><td><a href="./TestFramesClick.html">TestFramesClick</a></td></tr>
+            <tr unless="browserVersion.isOpera"><td><a href="./TestFramesSpecialTargets.html">TestFramesSpecialTargets</a></td></tr>
+            <tr unless="browserVersion.isOpera"><td><a href="./TestClickBlankTarget.html">TestClickBlankTarget</a></td></tr>
+            <tr><td><a href="./TestSelectMultiLevelFrame.html">TestSelectMultiLevelFrame</a></td></tr>
+            <tr><td><a href="./TestOpenInTargetFrame.html">TestOpenInTargetFrame</a></td></tr>
+            
+
+            <tr unless="isFileURL()"><td><a href="./TestCookie.html">TestCookie</a></td></tr>
+            <tr><td><a href="./TestEval.html">TestEval</a></td></tr>
+            
+            <tr><td><a href="./TestOpen.html">TestOpen</a></td></tr>
+            <tr><td><a href="./TestClick.html">TestClick</a></td></tr>
+            <tr><td><a href="./TestClickJavascriptHref.html">TestClickJavascriptHref</a></td></tr>
+            <tr><td><a href="./TestClickJavascriptHref.html">TestFramesClickJavascriptHref</a></td></tr>
+            <tr unless="browserVersion.appearsToBeBrokenInitialIE6"><td><a href="./TestSelectWindow.html">TestSelectWindow</a></td></tr>
+			<tr unless="browserVersion.appearsToBeBrokenInitialIE6"><td><a href="./TestSelectWindowTitle.html">TestSelectWindowTitle</a></td></tr>
+            <tr unless="browserVersion.appearsToBeBrokenInitialIE6"><td><a href="./TestWaitInPopupWindow.html">TestWaitInPopupWindow</a></td></tr>
+
+            <tr><td><a id="TestType" href="./TestType.html">TestType</a></td></tr>
+            <tr unless="browserVersion.isKonqueror"><td><a id="TestTypeRichText" href="./TestTypeRichText.html">TestTypeRichText</a></td></tr>
+            <tr><td><a href="./TestSelect.html">TestSelect</a></td></tr>
+            <tr><td><a href="./TestMultiSelect.html">TestMultiSelect</a></td></tr>
+            <tr><td><a href="./TestSubmit.html">TestSubmit</a></td></tr>
+            <tr><td><a href="./TestCheckUncheck.html">TestCheckUncheck</a></td></tr>
+            
+            <tr><td><a href="./TestStore.html">TestStore</a></td></tr>
+            <tr><td><a href="./TestJavascriptParameters.html">TestJavascriptParameters</a></td></tr>
+            <tr><td><a href="./TestPause.html">TestPause</a></td></tr>
+            <tr><td><a href="./TestWait.html">TestWait</a></td></tr>
+            
+            <tr><td><a href="./TestWaitFor.html">TestWaitFor</a></td></tr>
+            <tr><td><a href="./TestWaitForNot.html">TestWaitForNot</a></td></tr>
+            <tr><td><a href="./TestVerifications.html">TestVerifications</a></td></tr>
+            <tr><td><a href="./TestTextWhitespace.html">TestTextWhitespace</a></td></tr>
+            <tr><td><a href="./TestPatternMatching.html">TestPatternMatching</a></td></tr>
+            <tr><td><a href="./TestLocators.html">TestLocators</a></td></tr>
+            <tr><td><a href="./TestCssLocators.html">TestCssLocators</a></td></tr>
+            <tr><td><a href="./TestElementIndex.html">TestElementIndex</a></td></tr>
+            <tr><td><a href="./TestElementOrder.html">TestElementOrder</a></td></tr>
+            <tr><td><a href="./TestImplicitLocators.html">TestImplicitLocators</a></td></tr>
+            <tr><td><a href="./TestXPathLocators.html">TestXPathLocators</a></td></tr>
+            <tr unless="DISABLED"><td><a href="./TestXPathLocatorInXHtml.html">TestXPathLocatorInXHtml</a></td></tr>
+            <tr unless="browserVersion.isKonqueror || browserVersion.isSafari || browserVersion.isHTA"><td><a href="./TestGoBack.html">TestGoBack</a></td></tr>
+            <tr unless="browserVersion.isOpera"><td><a href="./TestRefresh.html">TestRefresh</a></td></tr>
+
+            <tr><td><a href="./TestHtmlSource.html">TestHtmlSource</a></td></tr>
+            <tr><td><a href="./TestComments.html">TestComments</a></td></tr>
+            <tr><td><a href="./events/TestLinkEvents.html">TestLinkEvents</a></td></tr>
+            <tr><td><a href="./events/TestButtonEvents.html">TestButtonEvents</a></td></tr>
+            <tr><td><a href="./events/TestSelectEvents.html">TestSelectEvents</a></td></tr>
+            <tr unless="browserVersion.isOpera"><td><a href="./events/TestRadioEvents.html">TestRadioEvents</a></td></tr>
+            <tr unless="browserVersion.isOpera"><td><a href="./events/TestCheckboxEvents.html">TestCheckboxEvents</a></td></tr>
+            <tr><td><a href="./events/TestTextEvents.html">TestTextEvents</a></td></tr>
+            <tr><td><a href="./events/TestFireEvents.html">TestFireEvents</a></td></tr>
+            <tr><td><a href="./TestFocusOnBlur.html">TestFocusOnBlur</a></td></tr>
+            <tr><td><a href="./TestAlerts.html">TestAlerts</a></td></tr>
+            <tr><td><a href="./TestConfirmations.html">TestConfirmations</a></td></tr>
+            <tr><td><a href="./TestPrompt.html">TestPrompt</a></td></tr>
+            <tr><td><a href="./TestEditable.html">TestEditable</a></td></tr>
+            <tr unless="browserVersion.isFirefox || browserVersion.isOpera || browserVersion.isMozilla || browserVersion.isKonqueror || browserVersion.isSafari"><td><a href="./TestCursorPosition.html">TestCursorPosition</a></td></tr>
+            <tr unless="browserVersion.isKonqueror || browserVersion.isSafari"><td><a href="./TestDragAndDrop.html">TestDragAndDrop</a></td></tr>
+            <tr unless="(isFileURL() && browserVersion.isIE) || browserVersion.isKonqueror || browserVersion.isSafari"><td><a href="./TestDojoDragAndDrop.html">TestDojoDragAndDrop</a></td></tr>
+
+            <tr><td><a href="./TestFailingVerifications.html">TestFailingVerifications</a></td></tr>
+            <tr><td><a href="./TestFailingAssert.html">TestFailingAssert</a></td></tr>
+            <tr><td><a href="./TestCommandError.html">TestCommandError</a></td></tr>
+            <tr><td><a href="./TestElementPresent.html">TestElementPresent</a></td></tr>
+            <tr unless="browserVersion.isSafari"><td><a href="./TestFunkEventHandling.html">TestFunkEventHandling</a></td></tr>
+            <tr><td><a href="./TestEvilClosingWindow.html">TestEvilClosingWindow</a></td></tr>
+            <tr><td><a href="./TestAddLocationStrategy.html">TestAddLocationStrategy</a></td></tr>
+            <tr><td><a href="./TestHighlight.html">TestHighlight</a></td></tr>
+            <tr><td><a href="./dogfood/TestBaseUrl.html">DogfoodTestBaseUrl</a></td></tr>
+            <tr><td><a href="./dogfood/TestRunSuccessfulTests.html">DogfoodTestRunSuccessfulTests</a></td></tr>
+            <tr><td><a href="./dogfood/TestRunFailedTests.html">DogfoodTestRunFailedTests</a></td></tr>
+            <tr><td><a href="./dogfood/TestPauseAndResume.html">DogfoodTestPauseAndResume</a></td></tr>
+            <tr><td><a href="./dogfood/TestBreakPoint.html">DogfoodTestBreakPoint</a></td></tr>
+            <tr><td><a href="./dogfood/TestFailures.html">DogfoodTestFailures</a></td></tr>
+
+        </tbody>
+    </table>
+
+    <br />
+    <em>Not supported in this browser</em>
+    <table id="skippedTests" cellpadding="1"
+           cellspacing="1"
+           border="1"
+           class="selenium">
+        <tbody>
+            <tr><td><b>Skipped Tests</b></td></tr>
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestTextWhitespace.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestTextWhitespace.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestTextWhitespace.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,101 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Verifications</title>
+</head>
+<body>
+<br>
+Test for markup and newlines in text content.
+<p>Our rule for whitespace is that all non-visible whitespace (including &amp;nbsp;) should be replaced with a single space, but all visible newlines (&lt;br&gt;, &lt;p&gt;, and &lt;pre&gt;formatted newlines) should be preserved.</p>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Text Content<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_text_content.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>nonTextMarkup</td>
+      <td>
+      There is non-visible and visible markup here that doesn't change the text content
+      </td>
+    </tr>
+    <!-- Match exactly the same space characters -->
+    <tr>
+      <td>verifyText</td>
+      <td>spaces</td>
+      <td>exact:1 space|2  space|3   space|1&nbsp;nbsp|2&nbsp;&nbsp;nbsp|3&nbsp;&nbsp;&nbsp;nbsp|2 &nbsp;space_nbsp|2&nbsp; nbsp_space|3 &nbsp; space_nbsp_space|3&nbsp; &nbsp;nbsp_space_nbsp</td>
+    </tr>
+      <tr>
+        <td>verifyText</td>
+      <td>tabcharacter</td>
+      <td>tab character between</td>
+    </tr>
+      <tr>
+        <td>verifyText</td>
+        <td>nonVisibleNewlines</td>
+        <td>non visible newlines between</td>
+      </tr>
+
+      <tr>
+        <td>verifyText</td>
+        <td>visibleNewlines</td>
+        <td>regexp:visible\s*newlines\s*between</td>
+      </tr>
+      <tr>
+        <td>verifyNotText</td>
+        <td>visibleNewlines</td>
+        <td>visible newlines between</td>
+      </tr>
+      <tr>
+        <td>verifyText</td>
+        <td>paragraphs</td>
+        <td>First paragraph*Second paragraph</td>
+      </tr>
+      <tr>
+        <td>verifyNotText</td>
+        <td>paragraphs</td>
+        <td>First paragraph Second paragraph</td>
+      </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>preformatted</td>
+      <td>preformatted*newline</td>
+    </tr>
+    <tr>
+      <td>verifyNotText</td>
+      <td>preformatted</td>
+      <td>preformatted newline</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>mixedMarkup</td>
+      <td>visible*newlines and markup and non-visible newlines and markup*With*a paragraph*and*pre*formatted*text</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestType.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestType.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestType.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,120 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta content="text/html; charset=ISO-8859-1"
+          http-equiv="content-type">
+    <title>Test Type</title>
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr>
+            <td rowspan="1" colspan="3">Test Type<br>
+            </td>
+        </tr>
+        <tr>
+            <td>open</td>
+            <td>../tests/html/test_type_page1.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyValue</td>
+            <td>username</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>shiftKeyDown</td>
+            <td></td>
+            <td></td>
+        </tr>
+        <tr>
+            <td>type</td>
+            <td>username</td>
+            <td>x</td>
+        </tr>
+        <tr>
+            <td>verifyValue</td>
+            <td>username</td>
+            <td>X</td>
+        </tr>
+        <tr>
+            <td>shiftKeyUp</td>
+            <td></td>
+            <td></td>
+        </tr>
+        <tr>
+            <td>type</td>
+            <td>username</td>
+            <td>TestUserWithLongName</td>
+        </tr>
+        <tr>
+            <td>verifyValue</td>
+            <td>username</td>
+            <td>TestUserWi</td>
+        </tr>
+        <tr>
+            <td>type</td>
+            <td>username</td>
+            <td>TestUser</td>
+        </tr>
+        <tr>
+            <td>verifyValue</td>
+            <td>username</td>
+            <td>TestUser</td>
+        </tr>
+        <tr>
+            <td>verifyValue</td>
+            <td>password</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>type</td>
+            <td>password</td>
+            <td>testUserPasswordIsVeryLong</td>
+        </tr>
+        <tr>
+            <td>verifyValue</td>
+            <td>password</td>
+            <td>testUserPasswordIsVe</td>
+        </tr>
+        <tr>
+            <td>type</td>
+            <td>password</td>
+            <td>testUserPassword</td>
+        </tr>
+        <tr>
+            <td>verifyValue</td>
+            <td>password</td>
+            <td>testUserPassword</td>
+        </tr>
+        <tr>
+            <td>clickAndWait</td>
+            <td>submitButton</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyTextPresent</td>
+            <td>Welcome, TestUser!<br>
+            </td>
+            <td>&nbsp;</td>
+        </tr>
+    </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestTypeRichText.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestTypeRichText.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestTypeRichText.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,59 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta content="text/html; charset=ISO-8859-1"
+          http-equiv="content-type">
+    <title>TestTypeRichText</title>
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr>
+            <td rowspan="1" colspan="3">TestTypeRichText<br>
+            </td>
+        </tr>
+        <tr>
+            <td>open</td>
+            <td>../tests/html/test_rich_text.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>selectFrame</td>
+            <td>richtext</td>
+            <td></td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>//body</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>type</td>
+            <td>//body</td>
+            <td>hello world</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>//body</td>
+            <td>hello world</td>
+        </tr>
+    </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestUserExtensions.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestUserExtensions.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestUserExtensions.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,98 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test For Sample User Functions</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test For Sample User Functions<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_type_page1.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>typeRepeated</td>
+      <td>username</td>
+      <td>Test</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>username</td>
+      <td>TestTest</td>
+    </tr>
+    <tr>
+      <td>verifyValueRepeated</td>
+      <td>username</td>
+      <td>Test</td>
+    </tr>
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>valuerepeated=Test</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>valuerepeated=X</td>
+      <td></td>
+    </tr>
+
+    <!-- Type 'Test' twice into the element with value='TestUserTestUser' -->
+    <tr>
+      <td>typeRepeated</td>
+      <td>valuerepeated=Test</td>
+      <td>X</td>
+    </tr>
+    <!-- Verify that we now CAN find an element with value == 'X' repeated -->
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>valuerepeated=X</td>
+      <td></td>
+    </tr>
+    
+    <!-- Test getTextLength -->
+    <tr>
+      <td>storeTextLength</td>
+      <td>//h3</td>
+      <td>myVar</td>
+    </tr>
+    <tr>
+      <td>verifyTextLength</td>
+      <td>//h3</td>
+      <td>regexp:4[1-5]</td>
+    </tr>
+    <tr>
+      <td>waitForTextLength</td>
+      <td>//h3</td>
+      <td>${myVar}</td>
+    </tr>
+    <tr>
+      <td>verifyNotTextLength</td>
+      <td>//h3</td>
+      <td>46</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestVerifications.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestVerifications.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestVerifications.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,172 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Verifications</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Verifications<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_verifications.html?foo=bar</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_verifications.html*</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_verifications.html?foo=bar</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theText</td>
+      <td>the text value</td>
+    </tr>
+    <tr>
+      <td>verifyNotValue</td>
+      <td>theText</td>
+      <td>not the text value</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theHidden</td>
+      <td>the hidden value</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>theSpan</td>
+      <td>this is the span</td>
+    </tr>
+    <tr>
+      <td>verifyNotText</td>
+      <td>theSpan</td>
+      <td>blah blah</td>
+    </tr>
+    <tr>
+      <td>verifyTextPresent</td>
+      <td>this is the span</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTextNotPresent</td>
+      <td>this is not the span</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>theSpan</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyElementPresent</td>
+      <td>theText</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyElementNotPresent</td>
+      <td>unknown</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTable</td>
+      <td>theTable.0.0</td>
+      <td>th1</td>
+    </tr>
+    <tr>
+      <td>verifyTable</td>
+      <td>theTable.1.0</td>
+      <td>a</td>
+    </tr>
+    <tr>
+      <td>verifyTable</td>
+      <td>theTable.2.1</td>
+      <td>d</td>
+    </tr>
+    <tr>
+      <td>verifyTable</td>
+      <td>theTable.3.1</td>
+      <td>f2</td>
+    </tr>
+    <tr>
+      <td>verifySelected
+
+</td>
+      <td>theSelect</td>
+      <td>index=1</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>value=option2</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>label=second option</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>second option</td>
+    </tr>
+    <tr>
+      <td>verifySelected</td>
+      <td>theSelect</td>
+      <td>id=o2</td>
+    </tr>
+    <tr>
+      <td>verifySelectOptions</td>
+      <td>theSelect</td>
+      <td>first option,second option,third\,\,option</td>
+    </tr>
+    <tr>
+      <td>verifyAttribute</td>
+      <td>theText at class</td>
+      <td>foo</td>
+    </tr>
+    <tr>
+      <td>verifyNotAttribute</td>
+      <td>theText at class</td>
+      <td>fox</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>theTitle</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotTitle</td>
+      <td>Blah Blah</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestVisibility.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestVisibility.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestVisibility.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,177 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Visiblity</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+
+    <tr>
+      <td rowspan="1" colspan="3">Test Visiblity<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_visibility.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>verifyVisible</td>
+      <td>visibleParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotVisible</td>
+      <td>hiddenParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotVisible</td>
+      <td>suppressedParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotVisible</td>
+      <td>classSuppressedParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotVisible</td>
+      <td>jsClassSuppressedParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotVisible</td>
+      <td>hiddenSubElement</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyVisible</td>
+      <td>visibleSubElement</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotVisible</td>
+      <td>suppressedSubElement</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyNotVisible</td>
+      <td>jsHiddenParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>true</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyNotVisible</td>
+      <td>visibleParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyVisible</td>
+      <td>hiddenParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyVisible</td>
+      <td>suppressedParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyVisible</td>
+      <td>classSuppressedParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyVisible</td>
+      <td>jsClassSuppressedParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyVisible</td>
+      <td>hiddenSubElement</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyVisible</td>
+      <td>suppressedSubElement</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>false</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyVisible</td>
+      <td>jsHiddenParagraph</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertErrorOnNext</td>
+      <td>Element nonExistentElement not found</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyVisible</td>
+      <td>nonExistentElement</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWait.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWait.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWait.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,117 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Select and Pause</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test AndWait commands for Reload<br>
+      </td>
+    </tr>
+    <!-- Link click -->
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_reload_onchange_page.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>theLink</td>
+      <td></td>
+    </tr>
+    <!-- Page should reload -->
+    <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page</td>
+      <td></td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_reload_onchange_page.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>selectAndWait</td>
+      <td>theSelect</td>
+      <td>Second Option</td>
+    </tr>
+    <!-- Page should reload -->
+    <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page</td>
+      <td></td>
+    </tr>
+
+    <!-- Textbox with onblur -->
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_reload_onchange_page.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theTextbox</td>
+      <td>new value</td>
+    </tr>
+    <tr>
+      <td>fireEventAndWait</td>
+      <td>theTextbox</td>
+      <td>blur</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page</td>
+      <td></td>
+    </tr>
+
+    <!-- Submit button -->
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_reload_onchange_page.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>theSubmit</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>slowPage_reload</td>
+      <td>&nbsp;</td>
+    </tr>
+   <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page</td>
+      <td></td>
+    </tr>
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitFor.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitFor.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitFor.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,167 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test WaitFor</title>
+</head>
+<body>
+First test waitForValue
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test WaitForValue<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_async_event.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertValue</td>
+      <td>theField</td>
+      <td>oldValue</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>theButton</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertValue</td>
+      <td>theField</td>
+      <td>oldValue</td>
+    </tr>
+    <tr>
+      <td>waitForValue</td>
+      <td>theField</td>
+      <td>regexp:n[aeiou]wValue</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theField</td>
+      <td>newValue</td>
+    </tr>
+
+  </tbody>
+</table>
+
+<br>
+Test waitForText
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>assertText</td>
+      <td>theSpan</td>
+      <td>Some text</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>theSpanButton</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertText</td>
+      <td>theSpan</td>
+      <td>Some text</td>
+    </tr>
+    <tr>
+      <td>waitForText</td>
+      <td>theSpan</td>
+      <td>regexp:Some n[aeiou]w text</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>theSpan</td>
+      <td>Some new text</td>
+    </tr>
+  </tbody>
+</table>
+
+<br>
+Test waiting for an alert.
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>click</td>
+      <td>theAlertButton</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>waitForAlert</td>
+      <td>regexp:An [aeiou]lert</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+<br>
+Test for waiting for something on a new page.
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_reload_onchange_page.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>theLink</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForTitle</td>
+      <td>Slow Loading Page</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page</td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+
+<br>
+Test for waiting for something that never arrives
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>setTimeout</td>
+      <td>500</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertFailureOnNext</td>
+      <td>Timed out after 500ms</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>waitForTextPresent</td>
+      <td>thisTextIsNotPresent</td>
+      <td></td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitForNot.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitForNot.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitForNot.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,98 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test WaitForNot</title>
+</head>
+<body>
+First test waitForNotValue
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test WaitForValueNot<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_async_event.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertValue</td>
+      <td>theField</td>
+      <td>oldValue</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>theButton</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertValue</td>
+      <td>theField</td>
+      <td>oldValue</td>
+    </tr>
+    <tr>
+      <td>waitForNotValue</td>
+      <td>theField</td>
+      <td>regexp:oldValu[aei]</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theField</td>
+      <td>newValue</td>
+    </tr>
+
+  </tbody>
+</table>
+
+<br>
+Now test waitForTextNot
+
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td>assertText</td>
+      <td>theSpan</td>
+      <td>Some text</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>theSpanButton</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>assertText</td>
+      <td>theSpan</td>
+      <td>Some text</td>
+    </tr>
+    <tr>
+      <td>waitForNotText</td>
+      <td>theSpan</td>
+      <td>regexp:Some te[xyz]t</td>
+    </tr>
+    <tr>
+      <td>verifyText</td>
+      <td>theSpan</td>
+      <td>Some new text</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitInPopupWindow.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitInPopupWindow.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestWaitInPopupWindow.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,107 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test SelectWindow</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test selectWindow<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_select_window.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>popupPage</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>waitForPopUp</td>
+      <td>myPopupWindow</td>
+      <td>5000</td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>myPopupWindow</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Select Window Popup</td>
+      <td>&nbsp;</td>
+    </tr>
+</table>
+<p>Check page transitions in popup window</p>
+<p>quick loading page<p>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tr>
+      <td>setTimeout</td>
+      <td>5000</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>link=Click to load new page</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Reload Page</td>
+      <td>&nbsp;</td>
+    </tr>
+</table>
+
+<p>slow loading page<p>
+<table cellpadding="1" cellspacing="1" border="1">
+	<tr>
+      <td>setTimeout</td>
+      <td>30000</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>clickAndWait</td>
+      <td>link=Click here</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyTitle</td>
+      <td>Slow Loading Page</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>close</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>selectWindow</td>
+      <td>null</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestXPathLocatorInXHtml.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestXPathLocatorInXHtml.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestXPathLocatorInXHtml.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,45 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta content="text/html; charset=ISO-8859-1"
+          http-equiv="content-type">
+    <title>Test XPath Locators In XHTML</title>
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr>
+            <td rowspan="1" colspan="3">Test XPath Locators In XHTML<br>
+            </td>
+        </tr>
+        <tr>
+            <td>open</td>
+            <td>../tests/html/test_locators.xhtml</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>xpath=//x:body</td>
+            <td>&nbsp;</td>
+        </tr>
+
+    </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestXPathLocators.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestXPathLocators.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/TestXPathLocators.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,191 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta content="text/html; charset=ISO-8859-1"
+          http-equiv="content-type">
+        <title>Test XPath Locators</title>
+<script language="JavaScript" type="text/javascript" src="../core/scripts/selenium-browserdetect.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../tests/filter-tests-for-browser.js"></script>
+
+
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+        <tr>
+            <td rowspan="1" colspan="3">Test XPath Locators<br>
+            </td>
+        </tr>
+
+        <tr>
+            <td>open</td>
+            <td>../tests/html/test_locators.html</td>
+            <td>&nbsp;</td>
+        </tr>
+
+        <tr>
+            <td>verifyText</td>
+            <td>xpath=//a</td>
+            <td>this is the first element</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>xpath=//a[@class='a2']</td>
+            <td>this is the second element</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>xpath=//*[@class='a2']</td>
+            <td>this is the second element</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>xpath=//a[2]</td>
+            <td>this is the second element</td>
+        </tr>
+        <tr>
+            <td>verifyText</td>
+            <td>xpath=//a[position()=2]</td>
+            <td>this is the second element</td>
+        </tr>
+        <tr>
+            <td>verifyElementNotPresent</td>
+            <td>xpath=//a[@href='foo']</td>
+            <td></td>
+        </tr>
+        <tr>
+            <td>verifyAttribute</td>
+            <td>xpath=//a[contains(@href,'#id1')]/@class</td>
+            <td>a1</td>
+        </tr>
+        
+    </tbody>
+</table>
+<p>SEL-165: search for text w/ and w/out nbsp: "this is the&amp;nbsp;second element" and "this is the second element"</p>
+
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>xpath=//a[text()="this is the&nbsp;second element"]</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>xpath=//a[text()="this is the second element"]</td>
+            <td>&nbsp;</td>
+        </tr>
+
+    </tbody>
+</table>
+<table cellpadding="1" cellspacing="1" border="1">
+    <tbody>
+
+        <tr>
+            <td>verifyText</td>
+            <td>//a</td>
+            <td>this is the first element</td>
+        </tr>
+        <tr>
+            <td>verifyAttribute</td>
+            <td>//a[contains(@href,'#id1')]/@class</td>
+            <td>a1</td>
+        </tr>
+
+
+        <tr>
+            <td>verifyText</td>
+            <td>xpath=(//table[@class='stylee'])//th[text()='theHeaderText']/../td</td>
+            <td>theCellText</td>
+        </tr>
+
+        <tr>
+            <td>click</td>
+            <td>//input[@name='name2' and @value='yes']</td>
+            <td>&nbsp;</td>
+        </tr>
+
+        <tr><td colspan="3"><i>test for SEL-242</i></td></tr>
+
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>xpath=//*[text()="right"]</td>
+            <td>&nbsp;</td>
+        </tr>
+        
+        <tr><td colspan="3"><i>test for SEL-444</i></td></tr>
+
+        <tr>
+            <td>verifyValue</td>
+            <td>xpath=//div[@id='nested1']/div[1]//input[2]</td>
+            <td>nested3b</td>
+        </tr>
+        
+        <tr><td colspan="3"><i>test for SEL-486 and assignId</i></td></tr>
+
+        <tr>
+            <td>verifyValue</td>
+            <td>xpath=id('nested1')/div[1]//input[2]</td>
+            <td>nested3b</td>
+        </tr>
+        
+        <tr>
+            <td>verifyValue</td>
+            <td>xpath=id('anotherNested')//div[contains(@id, 'useful')]//input</td>
+            <td>winner</td>
+        </tr>
+        
+        <tr>
+            <td>assignId</td>
+            <td>xpath=//*[text()="right"]</td>
+            <td>rightButton</td>
+        </tr>
+        
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>rightButton</td>
+            <td>&nbsp;</td>
+        </tr>
+        
+        <tr><td colspan="3"><i>xpath counting</i></td></tr>
+
+        <tr>
+            <td>verifyXpathCount</td>
+            <td>id('nested1')/div[1]//input</td>
+            <td>2</td>
+        </tr>
+        
+        <tr>
+            <td>verifyXpathCount</td>
+            <td>//div[@id='nonexistent']</td>
+            <td>0</td>
+        </tr>
+
+        <tr><td colspan="3"><i>test for SEL-347</i></td></tr>
+
+        <tr>
+            <td>verifyElementPresent</td>
+            <td>xpath=//a[@href="javascript:doFoo('a', 'b')"]</td>
+            <td>&nbsp;</td>
+        </tr>
+    </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestBaseUrl.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestBaseUrl.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestBaseUrl.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Run Failed Tests</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Run Failed Tests<br>
+      </td>
+    </tr>
+    <tr>
+      <td>setTimeout</td>
+      <td>120000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>storeEval</td>
+      <td>selenium.browserbot.baseUrl</td>
+      <td>myBaseUrl</td>
+    </tr>
+    
+    <tr>
+      <td>echo</td>
+      <td>${myBaseUrl}</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../core/TestRunner.html?test=../tests/dogfood/aut/BaseUrl1TestSuite.html&baseUrl=${myBaseUrl}</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>waitForEval</td>
+      <td>window.htmlTestRunner.testCaseLoaded</td>
+      <td>true</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>runSuite</td>
+        <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+    	<td>waitForText</td>
+    	<td>testRuns</td>
+    	<td>1</td>
+    </tr>
+    
+    <tr>
+    	<td>verifyText</td>
+    	<td>commandPasses</td>
+    	<td>1</td>
+    </tr>
+    
+    <tr>
+      <td>open</td>
+      <td>../core/TestRunner.html?test=../tests/dogfood/aut/BaseUrl2TestSuite.html&baseUrl=${myBaseUrl}/../html/</td>
+      <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+      <td>waitForEval</td>
+      <td>window.htmlTestRunner.testCaseLoaded</td>
+      <td>true</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>runSuite</td>
+        <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+    	<td>waitForText</td>
+    	<td>testRuns</td>
+    	<td>1</td>
+    </tr>
+    
+    <tr>
+    	<td>verifyText</td>
+    	<td>commandPasses</td>
+    	<td>1</td>
+    </tr>
+        
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestBreakPoint.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestBreakPoint.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestBreakPoint.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,118 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Break Point</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Break Point<br>
+      </td>
+    </tr>
+    <tr>
+      <td>setTimeout</td>
+      <td>120000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../core/TestRunner.html?test=../tests/dogfood/aut/PauseTestSuite.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>waitForEval</td>
+      <td>window.htmlTestRunner.testCaseLoaded</td>
+      <td>true</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>runSuite</td>
+        <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+        <td>waitForText</td>
+        <td>content</td>
+        <td>Loading text...</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>breakpointable</td>
+        <td>&nbsp;</td>
+    </tr>
+    
+    <tr>
+        <td>verifyAttribute</td>
+        <td>breakpointable at class</td>
+        <td>regex:breakpoint</td>
+    </tr>
+
+    <tr>
+        <td>waitForText</td>
+        <td>content</td>
+        <td>This is final text</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>pauseTest</td>
+        <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+        <td>waitForText</td>
+        <td>commandPasses</td>
+        <td>2</td>
+    </tr>
+    <tr>
+        <td>verifyAttribute</td>
+        <td>pauseTitle at class</td>
+        <td>regex:status_passed</td>
+    </tr>
+
+    <tr>
+        <td>verifyAttribute</td>
+        <td>breakpointable at class</td>
+        <td>regex:breakpoint</td>
+    </tr>
+    <tr>
+        <td>verifyAttribute</td>
+        <td>breakpointable at class</td>
+        <td>regex:status_passed</td>
+    </tr>
+    <!-- I tried to remove the break point but the clicking didn't work
+    <tr>
+        <td>click</td>
+        <td>//tr[@class='breakpoint status_passed']</td>
+        <td>&nbsp;</td>
+    </tr>
+    <tr>
+        <td>verifyAttribute</td>
+        <td>breakpointable at class</td>
+        <td>status_passed</td>
+    </tr>
+    -->
+
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestFailures.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestFailures.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestFailures.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,99 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Failures</title>
+</head>
+<body>
+    
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Failures<br>
+      </td>
+    </tr>
+    <tr>
+      <td>setTimeout</td>
+      <td>120000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../core/TestRunner.html?test=../tests/ErrorCheckingTestSuite.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>waitForEval</td>
+      <td>window.htmlTestRunner.testCaseLoaded</td>
+      <td>true</td>
+    </tr>
+
+    <tr>
+      <td rowspan="1" colspan="3">Run First Test Case<br>
+      </td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>runSeleniumTest</td>
+        <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+        <td>waitForText</td>
+        <td>testRuns</td>
+        <td>1</td>
+    </tr>
+
+    <tr>
+        <td>verifyAttribute</td>
+        <td>failure1 at class</td>
+        <td>regex:status_failed</td>
+    </tr>
+    <tr>
+        <td>verifyAttribute</td>
+        <td>failure2 at class</td>
+        <td>regex:status_failed</td>
+    </tr>
+    <tr>
+        <td>verifyAttribute</td>
+        <td>failure3 at class</td>
+        <td>regex:status_failed</td>
+    </tr>
+    <tr>
+        <td>verifyAttribute</td>
+        <td>failure4 at class</td>
+        <td>regex:status_failed</td>
+    </tr>
+    <tr>
+        <td>verifyAttribute</td>
+        <td>failure5 at class</td>
+        <td>regex:status_failed</td>
+    </tr>
+    <tr>
+        <td>verifyAttribute</td>
+        <td>failure6 at class</td>
+        <td>regex:status_failed</td>
+    </tr>
+
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestPauseAndResume.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestPauseAndResume.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestPauseAndResume.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,120 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Pause And Resume</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Pause And Resume<br>
+      </td>
+    </tr>
+
+    <tr><td colspan="3">in normal case the AUT test should fail</td></tr>
+    <tr>
+      <td>setTimeout</td>
+      <td>120000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../core/TestRunner.html?test=../tests/dogfood/aut/PauseTestSuite.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>waitForEval</td>
+      <td>window.htmlTestRunner.testCaseLoaded</td>
+      <td>true</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>runSuite</td>
+        <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+        <td>waitForText</td>
+        <td>testFailures</td>
+        <td>1</td>
+    </tr>
+    <tr>
+        <td>verifyAttribute</td>
+        <td>pauseTitle at class</td>
+        <td>regex:status_failed</td>
+    </tr>
+
+    <tr><td colspan="3">click pause to make the test pass</td></tr>
+    <tr>
+      <td>open</td>
+      <td>../core/TestRunner.html?test=../tests/dogfood/aut/PauseTestSuite.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>waitForEval</td>
+      <td>window.htmlTestRunner.testCaseLoaded</td>
+      <td>true</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>runSuite</td>
+        <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+        <td>waitForText</td>
+        <td>content</td>
+        <td>Loading text...</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>pauseTest</td>
+        <td>&nbsp;</td>
+    </tr>
+    
+
+    <tr>
+        <td>waitForText</td>
+        <td>content</td>
+        <td>This is final text</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>pauseTest</td>
+        <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+        <td>waitForText</td>
+        <td>commandPasses</td>
+        <td>2</td>
+    </tr>
+    <tr>
+        <td>verifyAttribute</td>
+        <td>pauseTitle at class</td>
+        <td>regex:status_passed</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestRunFailedTests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestRunFailedTests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestRunFailedTests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,96 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Run Failed Tests</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Run Failed Tests<br>
+      </td>
+    </tr>
+    <tr>
+      <td>setTimeout</td>
+      <td>120000</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../core/TestRunner.html?test=../tests/ErrorCheckingTestSuite.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>waitForEval</td>
+      <td>window.htmlTestRunner.testCaseLoaded</td>
+      <td>true</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>runSuite</td>
+        <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+    	<td>waitForText</td>
+    	<td>testRuns</td>
+    	<td>3</td>
+    </tr>
+    
+    <tr>
+    	<td>verifyText</td>
+    	<td>commandPasses</td>
+    	<td>37</td>
+    </tr>
+    <tr>
+    	<td>verifyText</td>
+    	<td>testFailures</td>
+    	<td>1</td>
+    </tr>
+    <tr>
+    	<td>verifyText</td>
+    	<td>commandFailures</td>
+    	<td>5</td>
+    </tr>
+    
+    <tr>
+    	<td>verifyText</td>
+    	<td>commandErrors</td>
+    	<td>1</td>
+    </tr>
+    
+    <tr>
+        <td>verifyAttribute</td>
+        <td>errorTitle at class</td>
+        <td>regex:status_failed</td>
+    </tr>
+    
+    <tr>
+        <td>verifyAttribute</td>
+        <td>errorCaseTitle at class</td>
+        <td>regex:status_failed</td>
+    </tr>
+        
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestRunSuccessfulTests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestRunSuccessfulTests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/TestRunSuccessfulTests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,98 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestRunSuccessfulTests</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestRunSuccessfulTests<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>setTimeout</td>
+      <td>120000</td>
+      <td>&nbsp;</td>
+    </tr>
+
+
+    <tr>
+      <td>open</td>
+      <td>../core/TestRunner.html?test=../tests/PassingTestSuite.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>waitForEval</td>
+      <td>window.htmlTestRunner.testCaseLoaded</td>
+      <td>true</td>
+    </tr>
+    <tr>
+        <td>click</td>
+        <td>runSuite</td>
+        <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+    	<td>waitForText</td>
+    	<td>testRuns</td>
+    	<td>2</td>
+    </tr>
+    
+    <tr>
+    	<td>verifyText</td>
+    	<td>commandPasses</td>
+    	<td>31</td>
+    </tr>
+    <tr>
+    	<td>verifyText</td>
+    	<td>testFailures</td>
+    	<td>0</td>
+    </tr>
+    <tr>
+    	<td>verifyText</td>
+    	<td>commandFailures</td>
+    	<td>0</td>
+    </tr>
+    
+    <tr>
+    	<td>verifyText</td>
+    	<td>commandErrors</td>
+    	<td>0</td>
+    </tr>
+    
+    <tr>
+        <td>verifyAttribute</td>
+        <td>passingSuite at class</td>
+        <td>regex:status_passed</td>
+    </tr>
+
+    <tr>
+        <td>verifyAttribute</td>
+        <td>passingTest at class</td>
+        <td>regex:status_passed</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/BaseUrl1TestSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/BaseUrl1TestSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/BaseUrl1TestSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,34 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Dogfood AUT: BaseUrl1</title>
+
+</head>
+
+<body>
+
+<table id="test_suite_table" cellpadding="1" cellspacing="1" border="1">
+        <tbody>
+            <tr><td><b>Dogfood AUT: BaseUrl1</b></td></tr>
+            <tr><td><a href="./TestBaseUrl1.html">TestBaseUrl1</a></td></tr>
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/BaseUrl2TestSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/BaseUrl2TestSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/BaseUrl2TestSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,34 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Dogfood AUT: BaseUrl2</title>
+
+</head>
+
+<body>
+
+<table id="test_suite_table" cellpadding="1" cellspacing="1" border="1">
+        <tbody>
+            <tr><td><b>Dogfood AUT: BaseUrl2</b></td></tr>
+            <tr><td><a href="./TestBaseUrl2.html">TestBaseUrl2</a></td></tr>
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/PauseTestSuite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/PauseTestSuite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/PauseTestSuite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,36 @@
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Dogfood AUT: Pause</title>
+
+</head>
+
+<body>
+
+<table     cellpadding="1"
+           cellspacing="1"
+           border="1">
+        <tbody>
+            <tr id="pauseTitle"><td><b>Dogfood AUT: Pause</b></td></tr>
+            <tr><td><a href="./TestTimeout.html">TestTimeout</a></td></tr>
+        </tbody>
+    </table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestBaseUrl1.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestBaseUrl1.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestBaseUrl1.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,49 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestBaseUrl1</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestBaseUrl1<br>
+      </td>
+    </tr>
+    <tr>
+      <td>echo</td>
+      <td>javascript{selenium.browserbot.baseUrl}</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestBaseUrl2.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestBaseUrl2.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestBaseUrl2.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,49 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>TestBaseUrl2</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">TestBaseUrl2<br>
+      </td>
+    </tr>
+    <tr>
+      <td>echo</td>
+      <td>javascript{selenium.browserbot.baseUrl}</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyLocation</td>
+      <td>*/tests/html/test_open.html</td>
+      <td>&nbsp;</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestTimeout.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestTimeout.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/TestTimeout.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,64 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Timeout</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Timeout<br>
+      </td>
+    </tr>
+
+    <tr>
+      <td>open</td>
+      <td>../../../tests/dogfood/aut/html/test_timeout.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>click</td>
+      <td>start</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>verifyText</td>
+      <td>content</td>
+      <td>Loading text...</td>
+    </tr>
+
+    <tr>
+      <td>waitForText</td>
+      <td>content</td>
+      <td>This text will change after a few seconds...</td>
+    </tr>
+
+    <tr id="breakpointable">
+      <td>verifyText</td>
+      <td>content</td>
+      <td>This is final text</td>
+    </tr>
+  </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/html/banner.gif
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/html/banner.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/html/test_timeout.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/html/test_timeout.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/dogfood/aut/html/test_timeout.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,49 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head>
+
+<script>
+    function loadContent() {
+        var content = document.getElementById('content');
+        start();
+   }
+   
+    function start() {
+        document.getElementById('content').innerHTML = 'Loading text...';
+        window.setTimeout(load, 1000);
+    }
+    
+    function load() {
+        document.getElementById('content').innerHTML = 'This text will change after a few seconds...';
+        window.setTimeout(finish, 1000);
+    }
+    
+    function finish() {
+        document.getElementById('content').innerHTML = 'This is final text';
+    }
+</script>
+
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+This is a test page.
+
+<input id='start' type='button' value='Start Loading Content' onclick='loadContent()'/>
+<h3 id='content'></h3>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestButtonEvents.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestButtonEvents.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestButtonEvents.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,72 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Button Events</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Button Events<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_form_events.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Click the button -->
+    <tr>
+      <td>click</td>
+      <td>theButton</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theButton)} {click(theButton)}</td>
+    </tr>
+    <tr>
+       <td>type</td>
+       <td>eventlog</td>
+       <td></td>
+    </tr>
+
+    <!-- Click the submit -->
+    <tr>
+      <td>click</td>
+      <td>theSubmit</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theSubmit)} {click(theSubmit)} {submit}</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestCheckboxEvents.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestCheckboxEvents.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestCheckboxEvents.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,107 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Checkbox Events</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Checkbox Events<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_form_events.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theCheckbox</td>
+      <td>off</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Check the checkbox (initially unchecked) -->
+    <tr>
+      <td>click</td>
+      <td>theCheckbox</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theCheckbox</td>
+      <td>on</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theCheckbox)}*</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>*{click(theCheckbox)}*</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>*{change(theCheckbox)}*</td>
+    </tr>
+
+    <!-- Click the checkbox again, unchecking it -->
+    <tr>
+      <td>type</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>theCheckbox</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theCheckbox</td>
+      <td>off</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theCheckbox)}*</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>*{click(theCheckbox)}*</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>*{change(theCheckbox)}*</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestFireEvents.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestFireEvents.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestFireEvents.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,75 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Fire Events</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Fire Events<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_form_events.html</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>assertValue</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <tr>
+      <td>fireEvent</td>
+      <td>theTextbox</td>
+      <td>focus</td>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theTextbox)}</td>
+    </tr>
+
+    <tr>
+      <td>type</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>fireEvent</td>
+      <td>theSelect</td>
+      <td>change</td>
+    </tr>
+    <tr>
+      <td>fireEvent</td>
+      <td>theSelect</td>
+      <td>blur</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{change(theSelect)} {blur(theSelect)}</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestKeyEvents.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestKeyEvents.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestKeyEvents.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,102 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+        http-equiv="content-type">
+  <title>Test Selenium</title>
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Key Events<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_form_events.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <!--
+    
+Unfortunately on some browsers (notably Firefox running in chrome mode), sending modified keys leads to
+ commands executing which ruined the test run.  In this case, a dialog box appeared to allow the user to
+ save the current page.  This is not what we want in an automated test run, so I am disabling this part of the test.
+    
+    <tr>
+      <td>altKeyDown</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>keyPress</td>
+      <td>theTextbox</td>
+      <td>s</td>
+    </tr>
+    <tr>
+      <td>altKeyUp</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>controlKeyDown</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>keyPress</td>
+      <td>theTextbox</td>
+      <td>s</td>
+    </tr>
+    <tr>
+      <td>controlKeyUp</td>
+      <td></td>
+      <td></td>
+    </tr>
+    -->
+    <tr>
+      <td>keyPress</td>
+      <td>theTextbox</td>
+      <td>w</td>
+    </tr>
+    <tr>
+      <td>keyPress</td>
+      <td>theTextbox</td>
+      <td>s</td>
+    </tr>
+    <tr>
+      <td>keyUp</td>
+      <td>theTextbox</td>
+      <td>\44</td>
+    </tr>
+    <tr>
+      <td>keyDown</td>
+      <td>theTextbox</td>
+      <td>\98</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{keypress(theTextbox - 119)} {keypress(theTextbox - 115)} {keyup(theTextbox - 44)} {keydown(theTextbox - 98)} </td>
+    </tr>
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestLinkEvents.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestLinkEvents.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestLinkEvents.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,43 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Click Link Events</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Click Link Events<br>
+      </td>
+    </tr>
+    <tr><td>open</td><td>../tests/html/test_form_events.html</td><td>&nbsp;</td></tr>
+    <tr><td>verifyValue</td><td>eventlog</td><td>&nbsp;</td></tr>
+
+    <!-- Click the button -->
+    <tr><td>click</td><td>theLink</td><td></td></tr>
+    <tr><td>verifyValue</td><td>eventlog</td><td>{focus(theLink)} {click(theLink)}</td></tr>
+    <tr><td>verifyAlert</td><td>link clicked</td><td/></tr>
+
+    <!-- Perform another action to ensure that there are no more alerts -->
+    <tr><td>click</td><td>theButton</td><td></td></tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestMouseEvents.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestMouseEvents.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestMouseEvents.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,74 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+        http-equiv="content-type">
+  <title>Test Selenium</title>
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Mouse Events<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_form_events.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>mouseOver</td>
+      <td>theTextbox</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>mouseOver</td>
+      <td>theButton</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>controlKeyDown</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>mouseDown</td>
+      <td>theTextbox</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>controlKeyUp</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>mouseDown</td>
+      <td>theButton</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{mouseover(theTextbox)} {mouseover(theButton)} {mousedown(theTextbox ctrlKeyDown)} {mousedown(theButton)}</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestRadioEvents.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestRadioEvents.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestRadioEvents.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,150 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Radio Button Events</title>
+  <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+  <script language="JavaScript" type="text/javascript" src="../../tests/filter-tests-for-browser.js"></script>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Radio Button Events<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_form_events.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theRadio1</td>
+      <td>off</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theRadio2</td>
+      <td>off</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+	
+   <tr>
+      <td>click</td>
+      <td>theRadio1</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theRadio1</td>
+      <td>on</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theRadio2</td>
+      <td>off</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theRadio1)} *</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>*{click(theRadio1)}*</td>
+    </tr>
+    <tr if="!browserVersion.isSafari">
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>*{change(theRadio1)}*</td>
+    </tr>
+    
+
+    <tr>
+      <td>type</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>theRadio2</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theRadio1</td>
+      <td>off</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theRadio2</td>
+      <td>on</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theRadio2)} *</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>*{click(theRadio2)}*</td>
+    </tr>
+    <tr if="!browserVersion.isSafari">
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>*{change(theRadio2)}*</td>
+    </tr>
+   
+
+    <tr>
+      <td>type</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>click</td>
+      <td>theRadio2</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theRadio1</td>
+      <td>off</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theRadio2</td>
+      <td>on</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theRadio2)} {click(theRadio2)}</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestSelectEvents.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestSelectEvents.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestSelectEvents.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,110 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Select Events</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Select Events<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_form_events.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theSelect</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Select an option -->
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>First Option</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theSelect</td>
+      <td>option1</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theSelect)} {change(theSelect)}</td>
+    </tr>
+
+    <!-- Select the same option again-->
+    <tr>
+      <td>type</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>First Option</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theSelect</td>
+      <td>option1</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theSelect)}</td>
+    </tr>
+
+    <!-- Select the option with no value-->
+    <tr>
+      <td>type</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>select</td>
+      <td>theSelect</td>
+      <td>Empty Option</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theSelect</td>
+      <td></td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theSelect)} {change(theSelect)}</td>
+    </tr>
+
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestTextEvents.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestTextEvents.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/TestTextEvents.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,87 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>Test Type Text Events</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <tbody>
+    <tr>
+      <td rowspan="1" colspan="3">Test Type Text Events<br>
+      </td>
+    </tr>
+    <tr>
+      <td>open</td>
+      <td>../tests/html/test_form_events.html</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theTextbox</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+
+    <!-- Set the textbox value from blank -->
+    <tr>
+      <td>type</td>
+      <td>theTextbox</td>
+      <td>first value</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theTextbox</td>
+      <td>first value</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theTextbox)} {select(theTextbox)} {change(theTextbox)}</td>
+    </tr>
+
+    <!-- Clear the event log, and change the value -->
+    <tr>
+      <td>type</td>
+      <td>eventlog</td>
+      <td>&nbsp;</td>
+    </tr>
+    <tr>
+      <td>type</td>
+      <td>theTextbox</td>
+      <td>changed value</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>theTextbox</td>
+      <td>changed value</td>
+    </tr>
+    <tr>
+      <td>verifyValue</td>
+      <td>eventlog</td>
+      <td>{focus(theTextbox)} {select(theTextbox)} {change(theTextbox)}</td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/readme.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/readme.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/events/readme.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,26 @@
+This directory contains tests for browser event emulation when driven by the
+Selenium javascript engine. There are subtle differences in the eventing
+for different browsers, which Selenium attempts to emulate when driving
+the app with Javascript.
+
+Current Implementation:
+-----------------------
+Currently, only the focus, select, click, change and blur events are supported for elements of
+types INPUT-TEXT, INPUT-RADIO, INPUT-CHECKBOX, INPUT-BUTTON and SELECT.
+
+The focus event is not supported for the "window" object.
+
+Other HTML events, together with all keyboard and mouse events are not currently emulated.
+
+Key Browser differences:
+----------------
+1) Firefox PR1 has a bug which allows "focus" and "blur" events to bubble when
+the target element is a CHECKBOX, RADIO, BUTTON or SELECT.
+(see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-htmlevents)
+*** SeleniumA does _not_ currently emulate this behaviour.
+
+2) Internet explorer has a bug whereby "select" and "change" events do not bubble up from
+target elements to enclosing elements.
+*** SeleniumA does currently emulate this behaviour.
+
+3) The window.onfocus event is not supported.

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/filter-tests-for-browser.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/filter-tests-for-browser.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/filter-tests-for-browser.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,22 @@
+function filterTestsForBrowser() {
+    var testTables = document.getElementsByTagName("table");
+    for (var i = 0; i < testTables.length; i++)
+    {
+        filterTestTableForBrowser(testTables[i]);
+    }
+}
+
+function filterTestTableForBrowser(testTable)
+{
+    for(rowNum = testTable.rows.length - 1; rowNum >= 0; rowNum--)
+    {
+        var row = testTable.rows[rowNum];
+        var filterString = row.getAttribute("if");
+        if (filterString && !eval(filterString))
+        {
+          testTable.deleteRow(rowNum)
+        }
+    }
+}
+
+window.onload=filterTestsForBrowser;
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/FastLoadingFrameWithSlowLoadingFrame.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/FastLoadingFrameWithSlowLoadingFrame.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/FastLoadingFrameWithSlowLoadingFrame.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,18 @@
+<HTML>
+<HEAD>
+<TITLE> AUT </TITLE>
+<META HTTP-EQUIV="Content-Language" CONTENT="en">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+
+
+
+<FRAMESET  ROWS="40,*,81" cols="100%" FRAMEBORDER="NO" BORDER="0" FRAMESPACING="0">
+  <FRAME SRC="test_fast_reloader.html" NAME="topFrame" SCROLLING="NO" NORESIZE >
+  <FRAME SRC="test_slow_reloader.html" APPLICATION="yes" NAME="slow">
+</FRAMESET>
+<NOFRAMES>
+<BODY>
+</BODY>
+</NOFRAMES>
+</HTML>
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/Frames.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/Frames.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/Frames.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,19 @@
+<HTML>
+<HEAD>
+<TITLE> AUT </TITLE>
+<META HTTP-EQUIV="Content-Language" CONTENT="en">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+
+
+
+<FRAMESET  ROWS="40,*,81" cols="100%" FRAMEBORDER="NO" BORDER="0" FRAMESPACING="0">
+  <FRAME SRC="test_top.html" NAME="topFrame" SCROLLING="NO" NORESIZE >
+  <FRAME SRC="test_open.html" APPLICATION="yes" NAME="mainFrame">
+  <FRAME SRC="test_bottom.html" NAME="bottomFrame" SCROLLING="NO" NORESIZE>
+</FRAMESET>
+<NOFRAMES>
+<BODY onload="init()">
+</BODY>
+</NOFRAMES>
+</HTML>
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/NestedFrames.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/NestedFrames.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/NestedFrames.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,21 @@
+<HTML>
+<HEAD>
+<TITLE> NestedFrames </TITLE>
+<META HTTP-EQUIV="Content-Language" CONTENT="en">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+
+
+
+<FRAMESET  ROWS="40,*" cols="100%" FRAMEBORDER="NO" BORDER="0" FRAMESPACING="0">
+  <FRAME SRC="test_top.html" NAME="topFrame" SCROLLING="NO" NORESIZE >
+  <FRAMESET ROWS="*, 40" cols="100%" FRAMEBORDER="NO" BORDER="0" FRAMESPACING="0">
+	  <FRAME SRC="NestedFrames2.slow.html" APPLICATION="yes" NAME="mainFrame" id="foo">
+	  <FRAME SRC="test_bottom.html" NAME="bottomFrame" SCROLLING="NO" NORESIZE>
+	</FRAMESET>
+</FRAMESET>
+<NOFRAMES>
+<BODY onload="init()">
+</BODY>
+</NOFRAMES>
+</HTML>
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/NestedFrames2.slow.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/NestedFrames2.slow.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/NestedFrames2.slow.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,19 @@
+<HTML>
+<HEAD>
+<TITLE> NestedFrames2 </TITLE>
+<META HTTP-EQUIV="Content-Language" CONTENT="en">
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+
+
+
+<FRAMESET  ROWS="40,*,81" cols="100%" FRAMEBORDER="NO" BORDER="0" FRAMESPACING="0">
+  <FRAME SRC="test_top.html" NAME="topFrame" SCROLLING="NO" NORESIZE >
+  <FRAME SRC="Frames.html" APPLICATION="yes" NAME="mainFrame">
+  <FRAME SRC="test_bottom.html" NAME="bottomFrame" SCROLLING="NO" NORESIZE>
+</FRAMESET>
+<NOFRAMES>
+<BODY onload="init()">
+</BODY>
+</NOFRAMES>
+</HTML>
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/ajax_autocompleter2_test.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/ajax_autocompleter2_test.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/ajax_autocompleter2_test.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,68 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <title>script.aculo.us Autocompleter functional test file</title>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+  <script src="prototype.js" type="text/javascript"></script>
+  <script src="scriptaculous.js" type="text/javascript"></script>
+  <style type="text/css" media="screen">
+    .selected { background-color: #888; }
+  </style>
+</head>
+<body>
+<h1>script.aculo.us Autocompleter functional test file</h1>
+
+
+
+This is an incremental Ajax autocompleter. Type something, then type a comma, than type more.<br/>
+Autocompleter ac1: <input type="text" id="ac1" autocomplete="off"/>
+<div id="ac1update" style="display:none;border:1px solid black;background-color:white;position:relative;"></div>
+
+<script type="text/javascript" language="javascript" charset="utf-8">
+// <![CDATA[
+  new Ajax.Autocompleter('ac1','ac1update','_autocomplete_result.html', { tokens: ','} );
+// ]]>
+</script>
+
+<br/><br/>
+Non-incremental Ajax autocompleter.<br/>
+Autocompleter ac2: <input id="ac2" type="text" autocomplete="off"/>
+<div id="ac2update" style="display:none;border:1px solid black;background-color:white;"></div>
+
+
+<script type="text/javascript" language="javascript" charset="utf-8">
+// <![CDATA[
+  new Ajax.Autocompleter('ac2','ac2update','_autocomplete_result.html');
+// ]]>
+</script>
+
+<br/><br/>
+Non-incremental Ajax autocompleter.<br/>
+Autocompleter ac3: <input id="ac3" type="text" autocomplete="off"/>
+<div id="ac3update" style="display:none;border:1px solid black;background-color:white;"></div>
+
+
+<script type="text/javascript" language="javascript" charset="utf-8">
+// <![CDATA[
+  new Ajax.Autocompleter('ac3','ac3update','_autocomplete_result.html');
+// ]]>
+</script>
+
+<br/><br/>
+
+Local incremental array autocompleter ac4<br/> with full-search. Type 'Jac', hit enter a few <br/>times, type 'gne'.<br/> <textarea rows=5 cols=40 id="ac4" autocomplete="off"></textarea>
+<div id="ac4update" style="display:none;border:1px solid black;background-color:white;"></div>
+
+<script type="text/javascript" language="javascript" charset="utf-8">
+// <![CDATA[
+  new Autocompleter.Local('ac4','ac4update',
+  new Array("John Jackson", "", "Jack Johnson", "", "Jane Agnews"), { tokens: new Array(',','\n'), fullSearch: true, partialSearch: true });
+// ]]>
+</script>
+<br /><br /><br />
+<br /><br /><br />
+<div id="debug" style="font-size:11px;"></div>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/builder.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/builder.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/builder.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,101 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// See scriptaculous.js for full license.
+
+var Builder = {
+  NODEMAP: {
+    AREA: 'map',
+    CAPTION: 'table',
+    COL: 'table',
+    COLGROUP: 'table',
+    LEGEND: 'fieldset',
+    OPTGROUP: 'select',
+    OPTION: 'select',
+    PARAM: 'object',
+    TBODY: 'table',
+    TD: 'table',
+    TFOOT: 'table',
+    TH: 'table',
+    THEAD: 'table',
+    TR: 'table'
+  },
+  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+  //       due to a Firefox bug
+  node: function(elementName) {
+    elementName = elementName.toUpperCase();
+    
+    // try innerHTML approach
+    var parentTag = this.NODEMAP[elementName] || 'div';
+    var parentElement = document.createElement(parentTag);
+    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+    } catch(e) {}
+    var element = parentElement.firstChild || null;
+      
+    // see if browser added wrapping tags
+    if(element && (element.tagName != elementName))
+      element = element.getElementsByTagName(elementName)[0];
+    
+    // fallback to createElement approach
+    if(!element) element = document.createElement(elementName);
+    
+    // abort if nothing could be created
+    if(!element) return;
+
+    // attributes (or text)
+    if(arguments[1])
+      if(this._isStringOrNumber(arguments[1]) ||
+        (arguments[1] instanceof Array)) {
+          this._children(element, arguments[1]);
+        } else {
+          var attrs = this._attributes(arguments[1]);
+          if(attrs.length) {
+            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+              parentElement.innerHTML = "<" +elementName + " " +
+                attrs + "></" + elementName + ">";
+            } catch(e) {}
+            element = parentElement.firstChild || null;
+            // workaround firefox 1.0.X bug
+            if(!element) {
+              element = document.createElement(elementName);
+              for(attr in arguments[1]) 
+                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+            }
+            if(element.tagName != elementName)
+              element = parentElement.getElementsByTagName(elementName)[0];
+            }
+        } 
+
+    // text, or array of children
+    if(arguments[2])
+      this._children(element, arguments[2]);
+
+     return element;
+  },
+  _text: function(text) {
+     return document.createTextNode(text);
+  },
+  _attributes: function(attributes) {
+    var attrs = [];
+    for(attribute in attributes)
+      attrs.push((attribute=='className' ? 'class' : attribute) +
+          '="' + attributes[attribute].toString().escapeHTML() + '"');
+    return attrs.join(" ");
+  },
+  _children: function(element, children) {
+    if(typeof children=='object') { // array can hold nodes and text
+      children.flatten().each( function(e) {
+        if(typeof e=='object')
+          element.appendChild(e)
+        else
+          if(Builder._isStringOrNumber(e))
+            element.appendChild(Builder._text(e));
+      });
+    } else
+      if(Builder._isStringOrNumber(children)) 
+         element.appendChild(Builder._text(children));
+  },
+  _isStringOrNumber: function(param) {
+    return(typeof param=='string' || typeof param=='number');
+  }
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/controls.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/controls.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/controls.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,815 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+//  Richard Livsey
+//  Rahul Bhargava
+//  Rob Wills
+// 
+// See scriptaculous.js for full license.
+
+// Autocompleter.Base handles all the autocompletion functionality 
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least, 
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method 
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most 
+// useful when one of the tokens is \n (a newline), as it 
+// allows smart autocompletion after linebreaks.
+
+var Autocompleter = {}
+Autocompleter.Base = function() {};
+Autocompleter.Base.prototype = {
+  baseInitialize: function(element, update, options) {
+    this.element     = $(element); 
+    this.update      = $(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+
+    if (this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || {};
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+    function(element, update){ 
+      if(!update.style.position || update.style.position=='absolute') {
+        update.style.position = 'absolute';
+        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
+      }
+      Effect.Appear(update,{duration:0.15});
+    };
+    this.options.onHide = this.options.onHide || 
+    function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if (typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && 
+      (navigator.appVersion.indexOf('MSIE')>0) &&
+      (navigator.userAgent.indexOf('Opera')<0) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+  
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix);
+    this.iefix.style.zIndex = 1;
+    this.update.style.zIndex = 2;
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+         return;
+      }
+     else 
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
+         (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+        
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+  
+  markPrevious: function() {
+    if(this.index > 0) this.index--
+      else this.index = this.entryCount-1;
+  },
+  
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++
+      else this.index = 0;
+  },
+  
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    
+    var lastTokenPos = this.findLastToken();
+    if (lastTokenPos != -1) {
+      var newValue = this.element.value.substr(0, lastTokenPos + 1);
+      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value;
+    } else {
+      this.element.value = value;
+    }
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.firstChild);
+
+      if(this.update.firstChild && this.update.firstChild.childNodes) {
+        this.entryCount = 
+          this.update.firstChild.childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+
+      this.index = 0;
+      this.render();
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    if(this.getToken().length>=this.options.minChars) {
+      this.startIndicator();
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+
+  getToken: function() {
+    var tokenPos = this.findLastToken();
+    if (tokenPos != -1)
+      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+    else
+      var ret = this.element.value;
+
+    return /\n/.test(ret) ? '' : ret;
+  },
+
+  findLastToken: function() {
+    var lastTokenPos = -1;
+
+    for (var i=0; i<this.options.tokens.length; i++) {
+      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
+      if (thisTokenPos > lastTokenPos)
+        lastTokenPos = thisTokenPos;
+    }
+    return lastTokenPos;
+  }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || {});
+  }
+});
+
+// AJAX in-place editor
+//
+// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = $(element);
+
+    this.options = Object.extend({
+      okButton: true,
+      okText: "ok",
+      cancelLink: true,
+      cancelText: "cancel",
+      savingText: "Saving...",
+      clickToEditText: "Click to edit",
+      okText: "ok",
+      rows: 1,
+      onComplete: function(transport, element) {
+        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+      },
+      onFailure: function(transport) {
+        alert("Error communicating with the server: " + transport.responseText.stripTags());
+      },
+      callback: function(form) {
+        return Form.serialize(form);
+      },
+      handleLineBreaks: true,
+      loadingText: 'Loading...',
+      savingClassName: 'inplaceeditor-saving',
+      loadingClassName: 'inplaceeditor-loading',
+      formClassName: 'inplaceeditor-form',
+      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+      highlightendcolor: "#FFFFFF",
+      externalControl: null,
+      submitOnBlur: false,
+      ajaxOptions: {},
+      evalScripts: false
+    }, options || {});
+
+    if(!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + "-inplaceeditor";
+      if ($(this.options.formId)) {
+        // there's already a form with that name, don't specify an id
+        this.options.formId = null;
+      }
+    }
+    
+    if (this.options.externalControl) {
+      this.options.externalControl = $(this.options.externalControl);
+    }
+    
+    this.originalBackground = Element.getStyle(this.element, 'background-color');
+    if (!this.originalBackground) {
+      this.originalBackground = "transparent";
+    }
+    
+    this.element.title = this.options.clickToEditText;
+    
+    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+    Event.observe(this.element, 'click', this.onclickListener);
+    Event.observe(this.element, 'mouseover', this.mouseoverListener);
+    Event.observe(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.observe(this.options.externalControl, 'click', this.onclickListener);
+      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  },
+  enterEditMode: function(evt) {
+    if (this.saving) return;
+    if (this.editing) return;
+    this.editing = true;
+    this.onEnterEditMode();
+    if (this.options.externalControl) {
+      Element.hide(this.options.externalControl);
+    }
+    Element.hide(this.element);
+    this.createForm();
+    this.element.parentNode.insertBefore(this.form, this.element);
+    Field.scrollFreeActivate(this.editField);
+    // stop the event to avoid a page refresh in Safari
+    if (evt) {
+      Event.stop(evt);
+    }
+    return false;
+  },
+  createForm: function() {
+    this.form = document.createElement("form");
+    this.form.id = this.options.formId;
+    Element.addClassName(this.form, this.options.formClassName)
+    this.form.onsubmit = this.onSubmit.bind(this);
+
+    this.createEditField();
+
+    if (this.options.textarea) {
+      var br = document.createElement("br");
+      this.form.appendChild(br);
+    }
+
+    if (this.options.okButton) {
+      okButton = document.createElement("input");
+      okButton.type = "submit";
+      okButton.value = this.options.okText;
+      okButton.className = 'editor_ok_button';
+      this.form.appendChild(okButton);
+    }
+
+    if (this.options.cancelLink) {
+      cancelLink = document.createElement("a");
+      cancelLink.href = "#";
+      cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+      cancelLink.onclick = this.onclickCancel.bind(this);
+      cancelLink.className = 'editor_cancel';      
+      this.form.appendChild(cancelLink);
+    }
+  },
+  hasHTMLLineBreaks: function(string) {
+    if (!this.options.handleLineBreaks) return false;
+    return string.match(/<br/i) || string.match(/<p>/i);
+  },
+  convertHTMLLineBreaks: function(string) {
+    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
+  },
+  createEditField: function() {
+    var text;
+    if(this.options.loadTextURL) {
+      text = this.options.loadingText;
+    } else {
+      text = this.getText();
+    }
+
+    var obj = this;
+    
+    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
+      this.options.textarea = false;
+      var textField = document.createElement("input");
+      textField.obj = this;
+      textField.type = "text";
+      textField.name = "value";
+      textField.value = text;
+      textField.style.backgroundColor = this.options.highlightcolor;
+      textField.className = 'editor_field';
+      var size = this.options.size || this.options.cols || 0;
+      if (size != 0) textField.size = size;
+      if (this.options.submitOnBlur)
+        textField.onblur = this.onSubmit.bind(this);
+      this.editField = textField;
+    } else {
+      this.options.textarea = true;
+      var textArea = document.createElement("textarea");
+      textArea.obj = this;
+      textArea.name = "value";
+      textArea.value = this.convertHTMLLineBreaks(text);
+      textArea.rows = this.options.rows;
+      textArea.cols = this.options.cols || 40;
+      textArea.className = 'editor_field';      
+      if (this.options.submitOnBlur)
+        textArea.onblur = this.onSubmit.bind(this);
+      this.editField = textArea;
+    }
+    
+    if(this.options.loadTextURL) {
+      this.loadExternalText();
+    }
+    this.form.appendChild(this.editField);
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  loadExternalText: function() {
+    Element.addClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = true;
+    new Ajax.Request(
+      this.options.loadTextURL,
+      Object.extend({
+        asynchronous: true,
+        onComplete: this.onLoadedExternalText.bind(this)
+      }, this.options.ajaxOptions)
+    );
+  },
+  onLoadedExternalText: function(transport) {
+    Element.removeClassName(this.form, this.options.loadingClassName);
+    this.editField.disabled = false;
+    this.editField.value = transport.responseText.stripTags();
+  },
+  onclickCancel: function() {
+    this.onComplete();
+    this.leaveEditMode();
+    return false;
+  },
+  onFailure: function(transport) {
+    this.options.onFailure(transport);
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+      this.oldInnerHTML = null;
+    }
+    return false;
+  },
+  onSubmit: function() {
+    // onLoading resets these so we need to save them away for the Ajax call
+    var form = this.form;
+    var value = this.editField.value;
+    
+    // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
+    // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
+    // to be displayed indefinitely
+    this.onLoading();
+    
+    if (this.options.evalScripts) {
+      new Ajax.Request(
+        this.url, Object.extend({
+          parameters: this.options.callback(form, value),
+          onComplete: this.onComplete.bind(this),
+          onFailure: this.onFailure.bind(this),
+          asynchronous:true, 
+          evalScripts:true
+        }, this.options.ajaxOptions));
+    } else  {
+      new Ajax.Updater(
+        { success: this.element,
+          // don't update on failure (this could be an option)
+          failure: null }, 
+        this.url, Object.extend({
+          parameters: this.options.callback(form, value),
+          onComplete: this.onComplete.bind(this),
+          onFailure: this.onFailure.bind(this)
+        }, this.options.ajaxOptions));
+    }
+    // stop the event to avoid a page refresh in Safari
+    if (arguments.length > 1) {
+      Event.stop(arguments[0]);
+    }
+    return false;
+  },
+  onLoading: function() {
+    this.saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  showSaving: function() {
+    this.oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    Element.addClassName(this.element, this.options.savingClassName);
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+  },
+  removeForm: function() {
+    if(this.form) {
+      if (this.form.parentNode) Element.remove(this.form);
+      this.form = null;
+    }
+  },
+  enterHover: function() {
+    if (this.saving) return;
+    this.element.style.backgroundColor = this.options.highlightcolor;
+    if (this.effect) {
+      this.effect.cancel();
+    }
+    Element.addClassName(this.element, this.options.hoverClassName)
+  },
+  leaveHover: function() {
+    if (this.options.backgroundColor) {
+      this.element.style.backgroundColor = this.oldBackground;
+    }
+    Element.removeClassName(this.element, this.options.hoverClassName)
+    if (this.saving) return;
+    this.effect = new Effect.Highlight(this.element, {
+      startcolor: this.options.highlightcolor,
+      endcolor: this.options.highlightendcolor,
+      restorecolor: this.originalBackground
+    });
+  },
+  leaveEditMode: function() {
+    Element.removeClassName(this.element, this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.style.backgroundColor = this.originalBackground;
+    Element.show(this.element);
+    if (this.options.externalControl) {
+      Element.show(this.options.externalControl);
+    }
+    this.editing = false;
+    this.saving = false;
+    this.oldInnerHTML = null;
+    this.onLeaveEditMode();
+  },
+  onComplete: function(transport) {
+    this.leaveEditMode();
+    this.options.onComplete.bind(this)(transport, this.element);
+  },
+  onEnterEditMode: function() {},
+  onLeaveEditMode: function() {},
+  dispose: function() {
+    if (this.oldInnerHTML) {
+      this.element.innerHTML = this.oldInnerHTML;
+    }
+    this.leaveEditMode();
+    Event.stopObserving(this.element, 'click', this.onclickListener);
+    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+    if (this.options.externalControl) {
+      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+    }
+  }
+};
+
+Ajax.InPlaceCollectionEditor = Class.create();
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
+Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
+  createEditField: function() {
+    if (!this.cached_selectTag) {
+      var selectTag = document.createElement("select");
+      var collection = this.options.collection || [];
+      var optionTag;
+      collection.each(function(e,i) {
+        optionTag = document.createElement("option");
+        optionTag.value = (e instanceof Array) ? e[0] : e;
+        if(this.options.value==optionTag.value) optionTag.selected = true;
+        optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
+        selectTag.appendChild(optionTag);
+      }.bind(this));
+      this.cached_selectTag = selectTag;
+    }
+
+    this.editField = this.cached_selectTag;
+    if(this.options.loadTextURL) this.loadExternalText();
+    this.form.appendChild(this.editField);
+    this.options.callback = function(form, value) {
+      return "value=" + encodeURIComponent(value);
+    }
+  }
+});
+
+// Delayed observer, like Form.Element.Observer, 
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create();
+Form.Element.DelayedObserver.prototype = {
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element); 
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/dragdrop.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/dragdrop.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/dragdrop.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,915 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi at oriontransfer.co.nz)
+// 
+// See scriptaculous.js for full license.
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null,
+      tree:       false
+    }, arguments[1] || {});
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if((typeof containment == 'object') && 
+        (containment.constructor == Array)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+    
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+  
+  findDeepestChild: function(drops) {
+    deepest = drops[0];
+      
+    for (i = 1; i < drops.length; ++i)
+      if (Element.isParent(drops[i].element, deepest.element))
+        deepest = drops[i];
+    
+    return deepest;
+  },
+
+  isContained: function(element, drop) {
+    var containmentNode;
+    if(drop.tree) {
+      containmentNode = element.treeNode; 
+    } else {
+      containmentNode = element.parentNode;
+    }
+    return drop._containers.detect(function(c) { return containmentNode == c });
+  },
+  
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect( 
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    var affected = [];
+    
+    if(this.last_active) this.deactivate(this.last_active);
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop))
+        affected.push(drop);
+    });
+        
+    if(affected.length>0) {
+      drop = Droppables.findDeepestChild(affected);
+      Position.within(drop.element, point[0], point[1]);
+      if(drop.onHover)
+        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+      
+      Droppables.activate(drop);
+    }
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) 
+        this.last_active.onDrop(element, this.last_active.element, event);
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+}
+
+var Draggables = {
+  drags: [],
+  observers: [],
+  
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+      
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+  
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+  
+  activate: function(draggable) {
+    window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+    this.activeDraggable = draggable;
+  },
+  
+  deactivate: function() {
+    this.activeDraggable = null;
+  },
+  
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // the same coordinates, prevent needless redrawing (moz bug?)
+    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+    this._lastPointer = pointer;
+    this.activeDraggable.updateDrag(event, pointer);
+  },
+  
+  endDrag: function(event) {
+    if(!this.activeDraggable) return;
+    this._lastPointer = null;
+    this.activeDraggable.endDrag(event);
+    this.activeDraggable = null;
+  },
+  
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+  
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+  
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+  
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+  },
+  
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable.prototype = {
+  initialize: function(element) {
+    var options = Object.extend({
+      handle: false,
+      starteffect: function(element) {
+        element._opacity = Element.getOpacity(element); 
+        new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
+      },
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
+      },
+      endeffect: function(element) {
+        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity}); 
+      },
+      zindex: 1000,
+      revert: false,
+      scroll: false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      snap: false   // false, or xy or [x,y] or function(x,y){ return [x,y] }
+    }, arguments[1] || {});
+
+    this.element = $(element);
+    
+    if(options.handle && (typeof options.handle == 'string')) {
+      var h = Element.childrenWithClassName(this.element, options.handle, true);
+      if(h.length>0) this.handle = h[0];
+    }
+    if(!this.handle) this.handle = $(options.handle);
+    if(!this.handle) this.handle = this.element;
+    
+    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
+      options.scroll = $(options.scroll);
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.delta    = this.currentDelta();
+    this.options  = options;
+    this.dragging = false;   
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+    
+    Draggables.register(this);
+  },
+  
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+  
+  currentDelta: function() {
+    return([
+      parseInt(Element.getStyle(this.element,'left') || '0'),
+      parseInt(Element.getStyle(this.element,'top') || '0')]);
+  },
+  
+  initDrag: function(event) {
+    if(Event.isLeftClick(event)) {    
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if(src.tagName && (
+        src.tagName=='INPUT' ||
+        src.tagName=='SELECT' ||
+        src.tagName=='OPTION' ||
+        src.tagName=='BUTTON' ||
+        src.tagName=='TEXTAREA')) return;
+        
+      if(this.element._revert) {
+        this.element._revert.cancel();
+        this.element._revert = null;
+      }
+      
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var pos     = Position.cumulativeOffset(this.element);
+      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+      
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+  
+  startDrag: function(event) {
+    this.dragging = true;
+    
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+    
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+    
+    if(this.options.scroll) {
+      if (this.options.scroll == window) {
+        var where = this._getWindowScroll(this.options.scroll);
+        this.originalScrollLeft = where.left;
+        this.originalScrollTop = where.top;
+      } else {
+        this.originalScrollLeft = this.options.scroll.scrollLeft;
+        this.originalScrollTop = this.options.scroll.scrollTop;
+      }
+    }
+    
+    Draggables.notify('onStart', this, event);
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+  
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+    Position.prepare();
+    Droppables.show(pointer, this.element);
+    Draggables.notify('onDrag', this, event);
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+    
+    if(this.options.scroll) {
+      this.stopScrolling();
+      
+      var p;
+      if (this.options.scroll == window) {
+        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+      } else {
+        p = Position.page(this.options.scroll);
+        p[0] += this.options.scroll.scrollLeft;
+        p[1] += this.options.scroll.scrollTop;
+        p.push(p[0]+this.options.scroll.offsetWidth);
+        p.push(p[1]+this.options.scroll.offsetHeight);
+      }
+      var speed = [0,0];
+      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+      this.startScrolling(speed);
+    }
+    
+    // fix AppleWebKit rendering
+    if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+    
+    Event.stop(event);
+  },
+  
+  finishDrag: function(event, success) {
+    this.dragging = false;
+
+    if(this.options.ghosting) {
+      Position.relativize(this.element);
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    if(success) Droppables.fire(event, this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && typeof revert == 'function') revert = revert(this.element);
+    
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      this.options.reverteffect(this.element, 
+        d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+  
+  keyPress: function(event) {
+    if(event.keyCode!=Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+  
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.stopScrolling();
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+  
+  draw: function(point) {
+    var pos = Position.cumulativeOffset(this.element);
+    var d = this.currentDelta();
+    pos[0] -= d[0]; pos[1] -= d[1];
+    
+    if(this.options.scroll && (this.options.scroll != window)) {
+      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+    }
+    
+    var p = [0,1].map(function(i){ 
+      return (point[i]-pos[i]-this.offset[i]) 
+    }.bind(this));
+    
+    if(this.options.snap) {
+      if(typeof this.options.snap == 'function') {
+        p = this.options.snap(p[0],p[1],this);
+      } else {
+      if(this.options.snap instanceof Array) {
+        p = p.map( function(v, i) {
+          return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
+      } else {
+        p = p.map( function(v) {
+          return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
+      }
+    }}
+    
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = p[0] + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = p[1] + "px";
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  },
+  
+  stopScrolling: function() {
+    if(this.scrollInterval) {
+      clearInterval(this.scrollInterval);
+      this.scrollInterval = null;
+      Draggables._lastScrollPointer = null;
+    }
+  },
+  
+  startScrolling: function(speed) {
+    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+    this.lastScrolled = new Date();
+    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+  },
+  
+  scroll: function() {
+    var current = new Date();
+    var delta = current - this.lastScrolled;
+    this.lastScrolled = current;
+    if(this.options.scroll == window) {
+      with (this._getWindowScroll(this.options.scroll)) {
+        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+          var d = delta / 1000;
+          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+        }
+      }
+    } else {
+      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
+    }
+    
+    Position.prepare();
+    Droppables.show(Draggables._lastPointer, this.element);
+    Draggables.notify('onDrag', this);
+    Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+    Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+    Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+    if (Draggables._lastScrollPointer[0] < 0)
+      Draggables._lastScrollPointer[0] = 0;
+    if (Draggables._lastScrollPointer[1] < 0)
+      Draggables._lastScrollPointer[1] = 0;
+    this.draw(Draggables._lastScrollPointer);
+    
+    if(this.options.change) this.options.change(this);
+  },
+  
+  _getWindowScroll: function(w) {
+    var T, L, W, H;
+    with (w.document) {
+      if (w.document.documentElement && documentElement.scrollTop) {
+        T = documentElement.scrollTop;
+        L = documentElement.scrollLeft;
+      } else if (w.document.body) {
+        T = body.scrollTop;
+        L = body.scrollLeft;
+      }
+      if (w.innerWidth) {
+        W = w.innerWidth;
+        H = w.innerHeight;
+      } else if (w.document.documentElement && documentElement.clientWidth) {
+        W = documentElement.clientWidth;
+        H = documentElement.clientHeight;
+      } else {
+        W = body.offsetWidth;
+        H = body.offsetHeight
+      }
+    }
+    return { top: T, left: L, width: W, height: H };
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+}
+
+var Sortable = {
+  sortables: {},
+  
+  _findRootElement: function(element) {
+    while (element.tagName != "BODY") {  
+      if(element.id && Sortable.sortables[element.id]) return element;
+      element = element.parentNode;
+    }
+  },
+
+  options: function(element) {
+    element = Sortable._findRootElement($(element));
+    if(!element) return;
+    return Sortable.sortables[element.id];
+  },
+  
+  destroy: function(element){
+    var s = Sortable.options(element);
+    
+    if(s) {
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+      
+      delete Sortable.sortables[s.element.id];
+    }
+  },
+
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,
+      treeTag:     'ul',
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      hoverclass:  null,
+      ghosting:    false,
+      scroll:      false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      format:      /^[^_]*_(.*)$/,
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || {});
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      scroll:      options.scroll,
+      scrollSpeed: options.scrollSpeed,
+      scrollSensitivity: options.scrollSensitivity,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      tree:        options.tree,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover
+      //greedy:      !options.dropOnEmpty
+    }
+    
+    var options_for_tree = {
+      onHover:      Sortable.onEmptyHover,
+      overlap:      options.overlap,
+      containment:  options.containment,
+      hoverclass:   options.hoverclass
+    }
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // drop on empty handling
+    if(options.dropOnEmpty || options.tree) {
+      Droppables.add(element, options_for_tree);
+      options.droppables.push(element);
+    }
+
+    (this.findElements(element, options) || []).each( function(e) {
+      // handles are per-draggable
+      var handle = options.handle ? 
+        Element.childrenWithClassName(e, options.handle)[0] : e;    
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      if(options.tree) e.treeNode = element;
+      options.droppables.push(e);      
+    });
+    
+    if(options.tree) {
+      (Sortable.findTreeElements(element, options) || []).each( function(e) {
+        Droppables.add(e, options_for_tree);
+        e.treeNode = element;
+        options.droppables.push(e);
+      });
+    }
+
+    // keep reference
+    this.sortables[element.id] = options;
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.tag);
+  },
+  
+  findTreeElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.treeTag);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(Element.isParent(dropon, element)) return;
+
+    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+      return;
+    } else if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+  
+  onEmptyHover: function(element, dropon, overlap) {
+    var oldParentNode = element.parentNode;
+    var droponOptions = Sortable.options(dropon);
+        
+    if(!Element.isParent(dropon, element)) {
+      var index;
+      
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
+      var child = null;
+            
+      if(children) {
+        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+        
+        for (index = 0; index < children.length; index += 1) {
+          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+            offset -= Element.offsetSize (children[index], droponOptions.overlap);
+          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+            child = index + 1 < children.length ? children[index + 1] : null;
+            break;
+          } else {
+            child = children[index];
+            break;
+          }
+        }
+      }
+      
+      dropon.insertBefore(element, child);
+      
+      Sortable.options(oldParentNode).onChange(element);
+      droponOptions.onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Element.hide(Sortable._marker);
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      Sortable._marker = $('dropmarker') || document.createElement('DIV');
+      Element.hide(Sortable._marker);
+      Element.addClassName(Sortable._marker, 'dropmarker');
+      Sortable._marker.style.position = 'absolute';
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }    
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.style.left = offsets[0] + 'px';
+    Sortable._marker.style.top = offsets[1] + 'px';
+    
+    if(position=='after')
+      if(sortable.overlap == 'horizontal') 
+        Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+      else
+        Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+    
+    Element.show(Sortable._marker);
+  },
+  
+  _tree: function(element, options, parent) {
+    var children = Sortable.findElements(element, options) || [];
+  
+    for (var i = 0; i < children.length; ++i) {
+      var match = children[i].id.match(options.format);
+
+      if (!match) continue;
+      
+      var child = {
+        id: encodeURIComponent(match ? match[1] : null),
+        element: element,
+        parent: parent,
+        children: new Array,
+        position: parent.children.length,
+        container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
+      }
+      
+      /* Get the element containing the children and recurse over it */
+      if (child.container)
+        this._tree(child.container, options, child)
+      
+      parent.children.push (child);
+    }
+
+    return parent; 
+  },
+
+  /* Finds the first element of the given tag type within a parent element.
+    Used for finding the first LI[ST] within a L[IST]I[TEM].*/
+  _findChildrenElement: function (element, containerTag) {
+    if (element && element.hasChildNodes)
+      for (var i = 0; i < element.childNodes.length; ++i)
+        if (element.childNodes[i].tagName == containerTag)
+          return element.childNodes[i];
+  
+    return null;
+  },
+
+  tree: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag: sortableOptions.tag,
+      treeTag: sortableOptions.treeTag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format
+    }, arguments[1] || {});
+    
+    var root = {
+      id: null,
+      parent: null,
+      children: new Array,
+      container: element,
+      position: 0
+    }
+    
+    return Sortable._tree (element, options, root);
+  },
+
+  /* Construct a [i] index for a particular node */
+  _constructIndex: function(node) {
+    var index = '';
+    do {
+      if (node.id) index = '[' + node.position + ']' + index;
+    } while ((node = node.parent) != null);
+    return index;
+  },
+
+  sequence: function(element) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[1] || {});
+    
+    return $(this.findElements(element, options) || []).map( function(item) {
+      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+    });
+  },
+
+  setSequence: function(element, new_sequence) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[2] || {});
+    
+    var nodeMap = {};
+    this.findElements(element, options).each( function(n) {
+        if (n.id.match(options.format))
+            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+        n.parentNode.removeChild(n);
+    });
+   
+    new_sequence.each(function(ident) {
+      var n = nodeMap[ident];
+      if (n) {
+        n[1].appendChild(n[0]);
+        delete nodeMap[ident];
+      }
+    });
+  },
+  
+  serialize: function(element) {
+    element = $(element);
+    var options = Object.extend(Sortable.options(element), arguments[1] || {});
+    var name = encodeURIComponent(
+      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+    
+    if (options.tree) {
+      return Sortable.tree(element, arguments[1]).children.map( function (item) {
+        return [name + Sortable._constructIndex(item) + "=" + 
+                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+      }).flatten().join('&');
+    } else {
+      return Sortable.sequence(element, arguments[1]).map( function(item) {
+        return name + "[]=" + encodeURIComponent(item);
+      }).join('&');
+    }
+  }
+}
+
+/* Returns true if child is contained within element */
+Element.isParent = function(child, element) {
+  if (!child.parentNode || child == element) return false;
+
+  if (child.parentNode == element) return true;
+
+  return Element.isParent(child.parentNode, element);
+}
+
+Element.findChildren = function(element, only, recursive, tagName) {    
+  if(!element.hasChildNodes()) return null;
+  tagName = tagName.toUpperCase();
+  if(only) only = [only].flatten();
+  var elements = [];
+  $A(element.childNodes).each( function(e) {
+    if(e.tagName && e.tagName.toUpperCase()==tagName &&
+      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+        elements.push(e);
+    if(recursive) {
+      var grandchildren = Element.findChildren(e, only, recursive, tagName);
+      if(grandchildren) elements.push(grandchildren);
+    }
+  });
+
+  return (elements.length>0 ? elements.flatten() : []);
+}
+
+Element.offsetSize = function (element, type) {
+  if (type == 'vertical' || type == 'height')
+    return element.offsetHeight;
+  else
+    return element.offsetWidth;
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/effects.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/effects.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/effects.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,958 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+//  Justin Palmer (http://encytemedia.com/)
+//  Mark Pilgrim (http://diveintomark.org/)
+//  Martin Bialasinki
+// 
+// See scriptaculous.js for full license.  
+
+// converts rgb() and #xxx to #xxxxxx format,  
+// returns self (or first argument) if not convertable  
+String.prototype.parseColor = function() {  
+  var color = '#';  
+  if(this.slice(0,4) == 'rgb(') {  
+    var cols = this.slice(4,this.length-1).split(',');  
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
+  } else {  
+    if(this.slice(0,1) == '#') {  
+      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
+      if(this.length==7) color = this.toLowerCase();  
+    }  
+  }  
+  return(color.length==7 ? color : (arguments[0] || this));  
+}
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+  }).flatten().join('');
+}
+
+Element.collectTextNodesIgnoreClass = function(element, className) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
+        Element.collectTextNodesIgnoreClass(node, className) : ''));
+  }).flatten().join('');
+}
+
+Element.setContentZoom = function(element, percent) {
+  element = $(element);  
+  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
+  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+}
+
+Element.getOpacity = function(element){  
+  var opacity;
+  if (opacity = Element.getStyle(element, 'opacity'))  
+    return parseFloat(opacity);  
+  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
+    if(opacity[1]) return parseFloat(opacity[1]) / 100;  
+  return 1.0;  
+}
+
+Element.setOpacity = function(element, value){  
+  element= $(element);  
+  if (value == 1){
+    Element.setStyle(element, { opacity: 
+      (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
+      0.999999 : null });
+    if(/MSIE/.test(navigator.userAgent))  
+      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
+  } else {  
+    if(value < 0.00001) value = 0;  
+    Element.setStyle(element, {opacity: value});
+    if(/MSIE/.test(navigator.userAgent))  
+     Element.setStyle(element, 
+       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
+                 'alpha(opacity='+value*100+')' });  
+  }
+}  
+ 
+Element.getInlineOpacity = function(element){  
+  return $(element).style.opacity || '';
+}  
+
+Element.childrenWithClassName = function(element, className, findFirst) {
+  var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
+  var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { 
+    return (c.className && c.className.match(classNameRegExp));
+  });
+  if(!results) results = [];
+  return results;
+}
+
+Element.forceRerendering = function(element) {
+  try {
+    element = $(element);
+    var n = document.createTextNode(' ');
+    element.appendChild(n);
+    element.removeChild(n);
+  } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Array.prototype.call = function() {
+  var args = arguments;
+  this.each(function(f){ f.apply(this, args) });
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+    element = $(element);
+    $A(element.childNodes).each( function(child) {
+      if(child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            Builder.node('span',{style: tagifyStyle},
+              character == ' ' ? String.fromCharCode(160) : character), 
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if(((typeof element == 'object') || 
+        (typeof element == 'function')) && 
+       (element.length))
+      elements = element;
+    else
+      elements = $(element).childNodes;
+      
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || {});
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  },
+  PAIRS: {
+    'slide':  ['SlideDown','SlideUp'],
+    'blind':  ['BlindDown','BlindUp'],
+    'appear': ['Appear','Fade']
+  },
+  toggle: function(element, effect) {
+    element = $(element);
+    effect = (effect || 'appear').toLowerCase();
+    var options = Object.extend({
+      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+    }, arguments[2] || {});
+    Effect[element.visible() ? 
+      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+  }
+};
+
+var Effect2 = Effect; // deprecated
+
+/* ------------- transitions ------------- */
+
+Effect.Transitions = {}
+
+Effect.Transitions.linear = function(pos) {
+  return pos;
+}
+Effect.Transitions.sinoidal = function(pos) {
+  return (-Math.cos(pos*Math.PI)/2) + 0.5;
+}
+Effect.Transitions.reverse  = function(pos) {
+  return 1-pos;
+}
+Effect.Transitions.flicker = function(pos) {
+  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+}
+Effect.Transitions.wobble = function(pos) {
+  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+}
+Effect.Transitions.pulse = function(pos) {
+  return (Math.floor(pos*10) % 2 == 0 ? 
+    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
+}
+Effect.Transitions.none = function(pos) {
+  return 0;
+}
+Effect.Transitions.full = function(pos) {
+  return 1;
+}
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create();
+Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
+  initialize: function() {
+    this.effects  = [];
+    this.interval = null;
+  },
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+    
+    var position = (typeof effect.options.queue == 'string') ? 
+      effect.options.queue : effect.options.queue.position;
+    
+    switch(position) {
+      case 'front':
+        // move unstarted effects after this effect  
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        break;
+      case 'end':
+        // start effect after last queued effect has finished
+        timestamp = this.effects.pluck('finishOn').max() || timestamp;
+        break;
+    }
+    
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+
+    if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+      this.effects.push(effect);
+    
+    if(!this.interval) 
+      this.interval = setInterval(this.loop.bind(this), 40);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if(this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    this.effects.invoke('loop', timePos);
+  }
+});
+
+Effect.Queues = {
+  instances: $H(),
+  get: function(queueName) {
+    if(typeof queueName != 'string') return queueName;
+    
+    if(!this.instances[queueName])
+      this.instances[queueName] = new Effect.ScopedQueue();
+      
+    return this.instances[queueName];
+  }
+}
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.DefaultOptions = {
+  transition: Effect.Transitions.sinoidal,
+  duration:   1.0,   // seconds
+  fps:        25.0,  // max. 25fps due to Effect.Queue implementation
+  sync:       false, // true for combining
+  from:       0.0,
+  to:         1.0,
+  delay:      0.0,
+  queue:      'parallel'
+}
+
+Effect.Base = function() {};
+Effect.Base.prototype = {
+  position: null,
+  start: function(options) {
+    this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn + (this.options.duration*1000);
+    this.event('beforeStart');
+    if(!this.options.sync)
+      Effect.Queues.get(typeof this.options.queue == 'string' ? 
+        'global' : this.options.queue.scope).add(this);
+  },
+  loop: function(timePos) {
+    if(timePos >= this.startOn) {
+      if(timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if(this.finish) this.finish(); 
+        this.event('afterFinish');
+        return;  
+      }
+      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
+      var frame = Math.round(pos * this.options.fps * this.options.duration);
+      if(frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  render: function(pos) {
+    if(this.state == 'idle') {
+      this.state = 'running';
+      this.event('beforeSetup');
+      if(this.setup) this.setup();
+      this.event('afterSetup');
+    }
+    if(this.state == 'running') {
+      if(this.options.transition) pos = this.options.transition(pos);
+      pos *= (this.options.to-this.options.from);
+      pos += this.options.from;
+      this.position = pos;
+      this.event('beforeUpdate');
+      if(this.update) this.update(pos);
+      this.event('afterUpdate');
+    }
+  },
+  cancel: function() {
+    if(!this.options.sync)
+      Effect.Queues.get(typeof this.options.queue == 'string' ? 
+        'global' : this.options.queue.scope).remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if(this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+}
+
+Effect.Parallel = Class.create();
+Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if(effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Opacity = Class.create();
+Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    // make this work on IE on elements without 'layout'
+    if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
+      this.element.setStyle({zoom: 1});
+    var options = Object.extend({
+      from: this.element.getOpacity() || 0.0,
+      to:   1.0
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  update: function(position) {
+    this.element.setOpacity(position);
+  }
+});
+
+Effect.Move = Class.create();
+Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    var options = Object.extend({
+      x:    0,
+      y:    0,
+      mode: 'relative'
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function() {
+    // Bug in Opera: Opera returns the "real" position of a static element or
+    // relative element that does not have top/left explicitly set.
+    // ==> Always set top and left for position relative elements in your stylesheets 
+    // (to 0 if you do not need them) 
+    this.element.makePositioned();
+    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
+    if(this.options.mode == 'absolute') {
+      // absolute movement, so we need to calc deltaX and deltaY
+      this.options.x = this.options.x - this.originalLeft;
+      this.options.y = this.options.y - this.originalTop;
+    }
+  },
+  update: function(position) {
+    this.element.setStyle({
+      left: this.options.x  * position + this.originalLeft + 'px',
+      top:  this.options.y  * position + this.originalTop  + 'px'
+    });
+  }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+  return new Effect.Move(element, 
+    Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
+};
+
+Effect.Scale = Class.create();
+Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
+  initialize: function(element, percent) {
+    this.element = $(element)
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || {});
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = this.element.getStyle('position');
+    
+    this.originalStyle = {};
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+      
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+    
+    var fontSize = this.element.getStyle('font-size') || '100%';
+    ['em','px','%'].each( function(fontSizeType) {
+      if(fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+    
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+    
+    this.dims = null;
+    if(this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if(/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if(!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if(this.options.scaleContent && this.fontSize)
+      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = {};
+    if(this.options.scaleX) d.width = width + 'px';
+    if(this.options.scaleY) d.height = height + 'px';
+    if(this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if(this.elementPositioning == 'absolute') {
+        if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if(this.options.scaleY) d.top = -topd + 'px';
+        if(this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    this.element.setStyle(d);
+  }
+});
+
+Effect.Highlight = Class.create();
+Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if(this.element.getStyle('display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = {
+      backgroundImage: this.element.getStyle('background-image') };
+    this.element.setStyle({backgroundImage: 'none'});
+    if(!this.options.endcolor)
+      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+    if(!this.options.restorecolor)
+      this.options.restorecolor = this.element.getStyle('background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    this.element.setStyle(Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = Class.create();
+Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    this.start(arguments[1] || {});
+  },
+  setup: function() {
+    Position.prepare();
+    var offsets = Position.cumulativeOffset(this.element);
+    if(this.options.offset) offsets[1] += this.options.offset;
+    var max = window.innerHeight ? 
+      window.height - window.innerHeight :
+      document.body.scrollHeight - 
+        (document.documentElement.clientHeight ? 
+          document.documentElement.clientHeight : document.body.clientHeight);
+    this.scrollStart = Position.deltaY;
+    this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
+  },
+  update: function(position) {
+    Position.prepare();
+    window.scrollTo(Position.deltaX, 
+      this.scrollStart + (position*this.delta));
+  }
+});
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  var options = Object.extend({
+  from: element.getOpacity() || 1.0,
+  to:   0.0,
+  afterFinishInternal: function(effect) { 
+    if(effect.options.to!=0) return;
+    effect.element.hide();
+    effect.element.setStyle({opacity: oldOpacity}); 
+  }}, arguments[1] || {});
+  return new Effect.Opacity(element,options);
+}
+
+Effect.Appear = function(element) {
+  element = $(element);
+  var options = Object.extend({
+  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+  to:   1.0,
+  // force Safari to render floated elements properly
+  afterFinishInternal: function(effect) {
+    effect.element.forceRerendering();
+  },
+  beforeSetup: function(effect) {
+    effect.element.setOpacity(effect.options.from);
+    effect.element.show(); 
+  }}, arguments[1] || {});
+  return new Effect.Opacity(element,options);
+}
+
+Effect.Puff = function(element) {
+  element = $(element);
+  var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
+  return new Effect.Parallel(
+   [ new Effect.Scale(element, 200, 
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
+     Object.extend({ duration: 1.0, 
+      beforeSetupInternal: function(effect) {
+        effect.effects[0].element.setStyle({position: 'absolute'}); },
+      afterFinishInternal: function(effect) {
+         effect.effects[0].element.hide();
+         effect.effects[0].element.setStyle(oldStyle); }
+     }, arguments[1] || {})
+   );
+}
+
+Effect.BlindUp = function(element) {
+  element = $(element);
+  element.makeClipping();
+  return new Effect.Scale(element, 0, 
+    Object.extend({ scaleContent: false, 
+      scaleX: false, 
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) {
+        effect.element.hide();
+        effect.element.undoClipping();
+      } 
+    }, arguments[1] || {})
+  );
+}
+
+Effect.BlindDown = function(element) {
+  element = $(element);
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, 
+    Object.extend({ scaleContent: false, 
+      scaleX: false,
+      scaleFrom: 0,
+      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+      restoreAfterFinish: true,
+      afterSetup: function(effect) {
+        effect.element.makeClipping();
+        effect.element.setStyle({height: '0px'});
+        effect.element.show(); 
+      },  
+      afterFinishInternal: function(effect) {
+        effect.element.undoClipping();
+      }
+    }, arguments[1] || {})
+  );
+}
+
+Effect.SwitchOff = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  return new Effect.Appear(element, { 
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, { 
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) { 
+          effect.element.makePositioned();
+          effect.element.makeClipping();
+        },
+        afterFinishInternal: function(effect) {
+          effect.element.hide();
+          effect.element.undoClipping();
+          effect.element.undoPositioned();
+          effect.element.setStyle({opacity: oldOpacity});
+        }
+      })
+    }
+  });
+}
+
+Effect.DropOut = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left'),
+    opacity: element.getInlineOpacity() };
+  return new Effect.Parallel(
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) {
+          effect.effects[0].element.makePositioned(); 
+        },
+        afterFinishInternal: function(effect) {
+          effect.effects[0].element.hide();
+          effect.effects[0].element.undoPositioned();
+          effect.effects[0].element.setStyle(oldStyle);
+        } 
+      }, arguments[1] || {}));
+}
+
+Effect.Shake = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left') };
+    return new Effect.Move(element, 
+      { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+        effect.element.undoPositioned();
+        effect.element.setStyle(oldStyle);
+  }}) }}) }}) }}) }}) }});
+}
+
+Effect.SlideDown = function(element) {
+  element = $(element);
+  element.cleanWhitespace();
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false, 
+    scaleFrom: window.opera ? 0 : 1,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.firstChild.makePositioned();
+      if(window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping();
+      effect.element.setStyle({height: '0px'});
+      effect.element.show(); },
+    afterUpdateInternal: function(effect) {
+      effect.element.firstChild.setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping(); 
+      // IE will crash if child is undoPositioned first
+      if(/MSIE/.test(navigator.userAgent)){
+        effect.element.undoPositioned();
+        effect.element.firstChild.undoPositioned();
+      }else{
+        effect.element.firstChild.undoPositioned();
+        effect.element.undoPositioned();
+      }
+      effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
+    }, arguments[1] || {})
+  );
+}
+  
+Effect.SlideUp = function(element) {
+  element = $(element);
+  element.cleanWhitespace();
+  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+  return new Effect.Scale(element, window.opera ? 0 : 1,
+   Object.extend({ scaleContent: false, 
+    scaleX: false, 
+    scaleMode: 'box',
+    scaleFrom: 100,
+    restoreAfterFinish: true,
+    beforeStartInternal: function(effect) {
+      effect.element.makePositioned();
+      effect.element.firstChild.makePositioned();
+      if(window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping();
+      effect.element.show(); },  
+    afterUpdateInternal: function(effect) {
+      effect.element.firstChild.setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
+    afterFinishInternal: function(effect) {
+      effect.element.hide();
+      effect.element.undoClipping();
+      effect.element.firstChild.undoPositioned();
+      effect.element.undoPositioned();
+      effect.element.setStyle({bottom: oldInnerBottom}); }
+   }, arguments[1] || {})
+  );
+}
+
+// Bug in opera makes the TD containing this element expand for a instance after finish 
+Effect.Squish = function(element) {
+  return new Effect.Scale(element, window.opera ? 1 : 0, 
+    { restoreAfterFinish: true,
+      beforeSetup: function(effect) {
+        effect.element.makeClipping(effect.element); },  
+      afterFinishInternal: function(effect) {
+        effect.element.hide(effect.element); 
+        effect.element.undoClipping(effect.element); }
+  });
+}
+
+Effect.Grow = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || {});
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();    
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Move(element, {
+    x: initialMoveX,
+    y: initialMoveY,
+    duration: 0.01, 
+    beforeSetup: function(effect) {
+      effect.element.hide();
+      effect.element.makeClipping();
+      effect.element.makePositioned();
+    },
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) {
+               effect.effects[0].element.setStyle({height: '0px'});
+               effect.effects[0].element.show(); 
+             },
+             afterFinishInternal: function(effect) {
+               effect.effects[0].element.undoClipping();
+               effect.effects[0].element.undoPositioned();
+               effect.effects[0].element.setStyle(oldStyle); 
+             }
+           }, options)
+      )
+    }
+  });
+}
+
+Effect.Shrink = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || {});
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':  
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+    ], Object.extend({            
+         beforeStartInternal: function(effect) {
+           effect.effects[0].element.makePositioned();
+           effect.effects[0].element.makeClipping(); },
+         afterFinishInternal: function(effect) {
+           effect.effects[0].element.hide();
+           effect.effects[0].element.undoClipping();
+           effect.effects[0].element.undoPositioned();
+           effect.effects[0].element.setStyle(oldStyle); }
+       }, options)
+  );
+}
+
+Effect.Pulsate = function(element) {
+  element = $(element);
+  var options    = arguments[1] || {};
+  var oldOpacity = element.getInlineOpacity();
+  var transition = options.transition || Effect.Transitions.sinoidal;
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+  reverser.bind(transition);
+  return new Effect.Opacity(element, 
+    Object.extend(Object.extend({  duration: 3.0, from: 0,
+      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+}
+
+Effect.Fold = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  Element.makeClipping(element);
+  return new Effect.Scale(element, 5, Object.extend({   
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, { 
+      scaleContent: false, 
+      scaleY: false,
+      afterFinishInternal: function(effect) {
+        effect.element.hide();
+        effect.element.undoClipping(); 
+        effect.element.setStyle(oldStyle);
+      } });
+  }}, arguments[1] || {}));
+};
+
+['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
+ 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( 
+  function(f) { Element.Methods[f] = Element[f]; }
+);
+
+Element.Methods.visualEffect = function(element, effect, options) {
+  s = effect.gsub(/_/, '-').camelize();
+  effect_class = s.charAt(0).toUpperCase() + s.substring(1);
+  new Effect[effect_class](element, options);
+  return $(element);
+};
+
+Element.addMethods();
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/prototype.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/prototype.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/prototype.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,2006 @@
+/*  Prototype JavaScript framework, version 1.5.0_rc0
+ *  (c) 2005 Sam Stephenson <sam at conio.net>
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.5.0_rc0',
+  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+
+  emptyFunction: function() {},
+  K: function(x) {return x}
+}
+
+var Class = {
+  create: function() {
+    return function() {
+      this.initialize.apply(this, arguments);
+    }
+  }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+  for (var property in source) {
+    destination[property] = source[property];
+  }
+  return destination;
+}
+
+Object.inspect = function(object) {
+  try {
+    if (object == undefined) return 'undefined';
+    if (object == null) return 'null';
+    return object.inspect ? object.inspect() : object.toString();
+  } catch (e) {
+    if (e instanceof RangeError) return '...';
+    throw e;
+  }
+}
+
+Function.prototype.bind = function() {
+  var __method = this, args = $A(arguments), object = args.shift();
+  return function() {
+    return __method.apply(object, args.concat($A(arguments)));
+  }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this;
+  return function(event) {
+    return __method.call(object, event || window.event);
+  }
+}
+
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    var digits = this.toString(16);
+    if (this < 16) return '0' + digits;
+    return digits;
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  }
+});
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0; i < arguments.length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+
+    return returnValue;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.callback();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+}
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += (replacement(match) || '').toString();
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = count === undefined ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return this;
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = truncation === undefined ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : this;
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var div = document.createElement('div');
+    var text = document.createTextNode(this);
+    div.appendChild(text);
+    return div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = document.createElement('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+  },
+
+  toQueryParams: function() {
+    var pairs = this.match(/^\??(.*)$/)[1].split('&');
+    return pairs.inject({}, function(params, pairString) {
+      var pair = pairString.split('=');
+      params[pair[0]] = pair[1];
+      return params;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  camelize: function() {
+    var oStringList = this.split('-');
+    if (oStringList.length == 1) return oStringList[0];
+
+    var camelizedString = this.indexOf('-') == 0
+      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+      : oStringList[0];
+
+    for (var i = 1, len = oStringList.length; i < len; i++) {
+      var s = oStringList[i];
+      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+    }
+
+    return camelizedString;
+  },
+
+  inspect: function() {
+    return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (typeof replacement == 'function') return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+}
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var Template = Class.create();
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+Template.prototype = {
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern  = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    return this.template.gsub(this.pattern, function(match) {
+      var before = match[1];
+      if (before == '\\') return match[2];
+      return before + (object[match[3]] || '').toString();
+    });
+  }
+}
+
+var $break    = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+  each: function(iterator) {
+    var index = 0;
+    try {
+      this._each(function(value) {
+        try {
+          iterator(value, index++);
+        } catch (e) {
+          if (e != $continue) throw e;
+        }
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+  },
+
+  all: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!(iterator || Prototype.K)(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator) {
+    var result = true;
+    this.each(function(value, index) {
+      if (result = !!(iterator || Prototype.K)(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  detect: function (iterator) {
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(pattern, iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      var stringValue = value.toString();
+      if (stringValue.match(pattern))
+        results.push((iterator || Prototype.K)(value, index));
+    })
+    return results;
+  },
+
+  include: function(object) {
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inject: function(memo, iterator) {
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.collect(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator) {
+    var result;
+    this.each(function(value, index) {
+      value = (iterator || Prototype.K)(value, index);
+      if (result == undefined || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator) {
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      ((iterator || Prototype.K)(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value, index) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator) {
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator) {
+    return this.collect(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.collect(Prototype.K);
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (typeof args.last() == 'function')
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+}
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) {
+    return iterable.toArray();
+  } else {
+    var results = [];
+    for (var i = 0; i < iterable.length; i++)
+      results.push(iterable[i]);
+    return results;
+  }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse)
+  Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0; i < this.length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != undefined || value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(value && value.constructor == Array ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  indexOf: function(object) {
+    for (var i = 0; i < this.length; i++)
+      if (this[i] == object) return i;
+    return -1;
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  }
+});
+var Hash = {
+  _each: function(iterator) {
+    for (var key in this) {
+      var value = this[key];
+      if (typeof value == 'function') continue;
+
+      var pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator(pair);
+    }
+  },
+
+  keys: function() {
+    return this.pluck('key');
+  },
+
+  values: function() {
+    return this.pluck('value');
+  },
+
+  merge: function(hash) {
+    return $H(hash).inject($H(this), function(mergedHash, pair) {
+      mergedHash[pair.key] = pair.value;
+      return mergedHash;
+    });
+  },
+
+  toQueryString: function() {
+    return this.map(function(pair) {
+      return pair.map(encodeURIComponent).join('=');
+    }).join('&');
+  },
+
+  inspect: function() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  }
+}
+
+function $H(object) {
+  var hash = Object.extend({}, object || {});
+  Object.extend(hash, Enumerable);
+  Object.extend(hash, Hash);
+  return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    do {
+      iterator(value);
+      value = value.succ();
+    } while (this.include(value));
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+}
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responderToAdd) {
+    if (!this.include(responderToAdd))
+      this.responders.push(responderToAdd);
+  },
+
+  unregister: function(responderToRemove) {
+    this.responders = this.responders.without(responderToRemove);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (responder[callback] && typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate: function() {
+    Ajax.activeRequestCount++;
+  },
+
+  onComplete: function() {
+    Ajax.activeRequestCount--;
+  }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      parameters:   ''
+    }
+    Object.extend(this.options, options || {});
+  },
+
+  responseIsSuccess: function() {
+    return this.transport.status == undefined
+        || this.transport.status == 0
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  responseIsFailure: function() {
+    return !this.responseIsSuccess();
+  }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(url, options) {
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    var parameters = this.options.parameters || '';
+    if (parameters.length > 0) parameters += '&_=';
+
+    try {
+      this.url = url;
+      if (this.options.method == 'get' && parameters.length > 0)
+        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+      Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.options.method, this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) {
+        this.transport.onreadystatechange = this.onStateChange.bind(this);
+        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+      }
+
+      this.setRequestHeaders();
+
+      var body = this.options.postBody ? this.options.postBody : parameters;
+      this.transport.send(this.options.method == 'post' ? body : null);
+
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  setRequestHeaders: function() {
+    var requestHeaders =
+      ['X-Requested-With', 'XMLHttpRequest',
+       'X-Prototype-Version', Prototype.Version,
+       'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
+
+    if (this.options.method == 'post') {
+      requestHeaders.push('Content-type', this.options.contentType);
+
+      /* Force "Connection: close" for Mozilla browsers to work around
+       * a bug where XMLHttpReqeuest sends an incorrect Content-length
+       * header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType)
+        requestHeaders.push('Connection', 'close');
+    }
+
+    if (this.options.requestHeaders)
+      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+    for (var i = 0; i < requestHeaders.length; i += 2)
+      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState != 1)
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  header: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) {}
+  },
+
+  evalJSON: function() {
+    try {
+      return eval('(' + this.header('X-JSON') + ')');
+    } catch (e) {}
+  },
+
+  evalResponse: function() {
+    try {
+      return eval(this.transport.responseText);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  respondToReadyState: function(readyState) {
+    var event = Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (event == 'Complete') {
+      try {
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + event, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+    if (event == 'Complete')
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+  initialize: function(container, url, options) {
+    this.containers = {
+      success: container.success ? $(container.success) : $(container),
+      failure: container.failure ? $(container.failure) :
+        (container.success ? null : $(container))
+    }
+
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+
+    var onComplete = this.options.onComplete || Prototype.emptyFunction;
+    this.options.onComplete = (function(transport, object) {
+      this.updateContent();
+      onComplete(transport, object);
+    }).bind(this);
+
+    this.request(url);
+  },
+
+  updateContent: function() {
+    var receiver = this.responseIsSuccess() ?
+      this.containers.success : this.containers.failure;
+    var response = this.transport.responseText;
+
+    if (!this.options.evalScripts)
+      response = response.stripScripts();
+
+    if (receiver) {
+      if (this.options.insertion) {
+        new this.options.insertion(receiver, response);
+      } else {
+        Element.update(receiver, response);
+      }
+    }
+
+    if (this.responseIsSuccess()) {
+      if (this.onComplete)
+        setTimeout(this.onComplete.bind(this), 10);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+  initialize: function(container, url, options) {
+    this.setOptions(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = {};
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(request) {
+    if (this.options.decay) {
+      this.decay = (request.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = request.responseText;
+    }
+    this.timer = setTimeout(this.onTimerEvent.bind(this),
+      this.decay * this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $() {
+  var results = [], element;
+  for (var i = 0; i < arguments.length; i++) {
+    element = arguments[i];
+    if (typeof element == 'string')
+      element = document.getElementById(element);
+    results.push(Element.extend(element));
+  }
+  return results.length < 2 ? results[0] : results;
+}
+
+document.getElementsByClassName = function(className, parentElement) {
+  var children = ($(parentElement) || document.body).getElementsByTagName('*');
+  return $A(children).inject([], function(elements, child) {
+    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+      elements.push(Element.extend(child));
+    return elements;
+  });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element)
+  var Element = new Object();
+
+Element.extend = function(element) {
+  if (!element) return;
+  if (_nativeExtensions) return element;
+
+  if (!element._extended && element.tagName && element != window) {
+    var methods = Element.Methods, cache = Element.extend.cache;
+    for (property in methods) {
+      var value = methods[property];
+      if (typeof value == 'function')
+        element[property] = cache.findOrStore(value);
+    }
+  }
+
+  element._extended = true;
+  return element;
+}
+
+Element.extend.cache = {
+  findOrStore: function(value) {
+    return this[value] = this[value] || function() {
+      return value.apply(null, [this].concat($A(arguments)));
+    }
+  }
+}
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      Element[Element.visible(element) ? 'hide' : 'show'](element);
+    }
+  },
+
+  hide: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = 'none';
+    }
+  },
+
+  show: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = '';
+    }
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+  },
+
+  update: function(element, html) {
+    $(element).innerHTML = html.stripScripts();
+    setTimeout(function() {html.evalScripts()}, 10);
+  },
+
+  replace: function(element, html) {
+    element = $(element);
+    if (element.outerHTML) {
+      element.outerHTML = html.stripScripts();
+    } else {
+      var range = element.ownerDocument.createRange();
+      range.selectNodeContents(element);
+      element.parentNode.replaceChild(
+        range.createContextualFragment(html.stripScripts()), element);
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+  },
+
+  getHeight: function(element) {
+    element = $(element);
+    return element.offsetHeight;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).include(className);
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).add(className);
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return Element.classNames(element).remove(className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    for (var i = 0; i < element.childNodes.length; i++) {
+      var node = element.childNodes[i];
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        Element.remove(node);
+    }
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.match(/^\s*$/);
+  },
+
+  childOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    while (element = element.parentNode)
+      if (element == ancestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var x = element.x ? element.x : element.offsetLeft,
+        y = element.y ? element.y : element.offsetTop;
+    window.scrollTo(x, y);
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    var value = element.style[style.camelize()];
+    if (!value) {
+      if (document.defaultView && document.defaultView.getComputedStyle) {
+        var css = document.defaultView.getComputedStyle(element, null);
+        value = css ? css.getPropertyValue(style) : null;
+      } else if (element.currentStyle) {
+        value = element.currentStyle[style.camelize()];
+      }
+    }
+
+    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+    return value == 'auto' ? null : value;
+  },
+
+  setStyle: function(element, style) {
+    element = $(element);
+    for (var name in style)
+      element.style[name.camelize()] = style[name];
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    if (Element.getStyle(element, 'display') != 'none')
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = '';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = 'none';
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return;
+    element._overflow = element.style.overflow;
+    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+      element.style.overflow = 'hidden';
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return;
+    element.style.overflow = element._overflow;
+    element._overflow = undefined;
+  }
+}
+
+Object.extend(Element, Element.Methods);
+
+var _nativeExtensions = false;
+
+if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+  var HTMLElement = {}
+  HTMLElement.prototype = document.createElement('div').__proto__;
+}
+
+Element.addMethods = function(methods) {
+  Object.extend(Element.Methods, methods || {});
+
+  if(typeof HTMLElement != 'undefined') {
+    var methods = Element.Methods, cache = Element.extend.cache;
+    for (property in methods) {
+      var value = methods[property];
+      if (typeof value == 'function')
+        HTMLElement.prototype[property] = cache.findOrStore(value);
+    }
+    _nativeExtensions = true;
+  }
+}
+
+Element.addMethods();
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+  this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+  initialize: function(element, content) {
+    this.element = $(element);
+    this.content = content.stripScripts();
+
+    if (this.adjacency && this.element.insertAdjacentHTML) {
+      try {
+        this.element.insertAdjacentHTML(this.adjacency, this.content);
+      } catch (e) {
+        var tagName = this.element.tagName.toLowerCase();
+        if (tagName == 'tbody' || tagName == 'tr') {
+          this.insertContent(this.contentFromAnonymousTable());
+        } else {
+          throw e;
+        }
+      }
+    } else {
+      this.range = this.element.ownerDocument.createRange();
+      if (this.initializeRange) this.initializeRange();
+      this.insertContent([this.range.createContextualFragment(this.content)]);
+    }
+
+    setTimeout(function() {content.evalScripts()}, 10);
+  },
+
+  contentFromAnonymousTable: function() {
+    var div = document.createElement('div');
+    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+    return $A(div.childNodes[0].childNodes[0].childNodes);
+  }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+  initializeRange: function() {
+    this.range.setStartBefore(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment, this.element);
+    }).bind(this));
+  }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(true);
+  },
+
+  insertContent: function(fragments) {
+    fragments.reverse(false).each((function(fragment) {
+      this.element.insertBefore(fragment, this.element.firstChild);
+    }).bind(this));
+  }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.appendChild(fragment);
+    }).bind(this));
+  }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+  initializeRange: function() {
+    this.range.setStartAfter(this.element);
+  },
+
+  insertContent: function(fragments) {
+    fragments.each((function(fragment) {
+      this.element.parentNode.insertBefore(fragment,
+        this.element.nextSibling);
+    }).bind(this));
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set(this.toArray().concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set(this.select(function(className) {
+      return className != classNameToRemove;
+    }).join(' '));
+  },
+
+  toString: function() {
+    return this.toArray().join(' ');
+  }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Selector = Class.create();
+Selector.prototype = {
+  initialize: function(expression) {
+    this.params = {classNames: []};
+    this.expression = expression.toString().strip();
+    this.parseExpression();
+    this.compileMatcher();
+  },
+
+  parseExpression: function() {
+    function abort(message) { throw 'Parse error in selector: ' + message; }
+
+    if (this.expression == '')  abort('empty expression');
+
+    var params = this.params, expr = this.expression, match, modifier, clause, rest;
+    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
+      params.attributes = params.attributes || [];
+      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
+      expr = match[1];
+    }
+
+    if (expr == '*') return this.params.wildcard = true;
+
+    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
+      modifier = match[1], clause = match[2], rest = match[3];
+      switch (modifier) {
+        case '#':       params.id = clause; break;
+        case '.':       params.classNames.push(clause); break;
+        case '':
+        case undefined: params.tagName = clause.toUpperCase(); break;
+        default:        abort(expr.inspect());
+      }
+      expr = rest;
+    }
+
+    if (expr.length > 0) abort(expr.inspect());
+  },
+
+  buildMatchExpression: function() {
+    var params = this.params, conditions = [], clause;
+
+    if (params.wildcard)
+      conditions.push('true');
+    if (clause = params.id)
+      conditions.push('element.id == ' + clause.inspect());
+    if (clause = params.tagName)
+      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
+    if ((clause = params.classNames).length > 0)
+      for (var i = 0; i < clause.length; i++)
+        conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
+    if (clause = params.attributes) {
+      clause.each(function(attribute) {
+        var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
+        var splitValueBy = function(delimiter) {
+          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
+        }
+
+        switch (attribute.operator) {
+          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
+          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
+          case '|=':      conditions.push(
+                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
+                          ); break;
+          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
+          case '':
+          case undefined: conditions.push(value + ' != null'); break;
+          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
+        }
+      });
+    }
+
+    return conditions.join(' && ');
+  },
+
+  compileMatcher: function() {
+    this.match = new Function('element', 'if (!element.tagName) return false; \
+      return ' + this.buildMatchExpression());
+  },
+
+  findElements: function(scope) {
+    var element;
+
+    if (element = $(this.params.id))
+      if (this.match(element))
+        if (!scope || Element.childOf(element, scope))
+          return [element];
+
+    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
+
+    var results = [];
+    for (var i = 0; i < scope.length; i++)
+      if (this.match(element = scope[i]))
+        results.push(Element.extend(element));
+
+    return results;
+  },
+
+  toString: function() {
+    return this.expression;
+  }
+}
+
+function $$() {
+  return $A(arguments).map(function(expression) {
+    return expression.strip().split(/\s+/).inject([null], function(results, expr) {
+      var selector = new Selector(expr);
+      return results.map(selector.findElements.bind(selector)).flatten();
+    });
+  }).flatten();
+}
+var Field = {
+  clear: function() {
+    for (var i = 0; i < arguments.length; i++)
+      $(arguments[i]).value = '';
+  },
+
+  focus: function(element) {
+    $(element).focus();
+  },
+
+  present: function() {
+    for (var i = 0; i < arguments.length; i++)
+      if ($(arguments[i]).value == '') return false;
+    return true;
+  },
+
+  select: function(element) {
+    $(element).select();
+  },
+
+  activate: function(element) {
+    element = $(element);
+    element.focus();
+    if (element.select)
+      element.select();
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+  serialize: function(form) {
+    var elements = Form.getElements($(form));
+    var queryComponents = new Array();
+
+    for (var i = 0; i < elements.length; i++) {
+      var queryComponent = Form.Element.serialize(elements[i]);
+      if (queryComponent)
+        queryComponents.push(queryComponent);
+    }
+
+    return queryComponents.join('&');
+  },
+
+  getElements: function(form) {
+    form = $(form);
+    var elements = new Array();
+
+    for (var tagName in Form.Element.Serializers) {
+      var tagElements = form.getElementsByTagName(tagName);
+      for (var j = 0; j < tagElements.length; j++)
+        elements.push(tagElements[j]);
+    }
+    return elements;
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name)
+      return inputs;
+
+    var matchingInputs = new Array();
+    for (var i = 0; i < inputs.length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) ||
+          (name && input.name != name))
+        continue;
+      matchingInputs.push(input);
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.blur();
+      element.disabled = 'true';
+    }
+  },
+
+  enable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.disabled = '';
+    }
+  },
+
+  findFirstElement: function(form) {
+    return Form.getElements(form).find(function(element) {
+      return element.type != 'hidden' && !element.disabled &&
+        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    Field.activate(Form.findFirstElement(form));
+  },
+
+  reset: function(form) {
+    $(form).reset();
+  }
+}
+
+Form.Element = {
+  serialize: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+
+    if (parameter) {
+      var key = encodeURIComponent(parameter[0]);
+      if (key.length == 0) return;
+
+      if (parameter[1].constructor != Array)
+        parameter[1] = [parameter[1]];
+
+      return parameter[1].map(function(value) {
+        return key + '=' + encodeURIComponent(value);
+      }).join('&');
+    }
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+
+    if (parameter)
+      return parameter[1];
+  }
+}
+
+Form.Element.Serializers = {
+  input: function(element) {
+    switch (element.type.toLowerCase()) {
+      case 'submit':
+      case 'hidden':
+      case 'password':
+      case 'text':
+        return Form.Element.Serializers.textarea(element);
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element);
+    }
+    return false;
+  },
+
+  inputSelector: function(element) {
+    if (element.checked)
+      return [element.name, element.value];
+  },
+
+  textarea: function(element) {
+    return [element.name, element.value];
+  },
+
+  select: function(element) {
+    return Form.Element.Serializers[element.type == 'select-one' ?
+      'selectOne' : 'selectMany'](element);
+  },
+
+  selectOne: function(element) {
+    var value = '', opt, index = element.selectedIndex;
+    if (index >= 0) {
+      opt = element.options[index];
+      value = opt.value || opt.text;
+    }
+    return [element.name, value];
+  },
+
+  selectMany: function(element) {
+    var value = [];
+    for (var i = 0; i < element.length; i++) {
+      var opt = element.options[i];
+      if (opt.selected)
+        value.push(opt.value || opt.text);
+    }
+    return [element.name, value];
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+  initialize: function(element, frequency, callback) {
+    this.frequency = frequency;
+    this.element   = $(element);
+    this.callback  = callback;
+
+    this.lastValue = this.getValue();
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  onTimerEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    var elements = Form.getElements(this.element);
+    for (var i = 0; i < elements.length; i++)
+      this.registerCallback(elements[i]);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        case 'password':
+        case 'text':
+        case 'textarea':
+        case 'select-one':
+        case 'select-multiple':
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) {
+  var Event = new Object();
+}
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+
+  element: function(event) {
+    return event.target || event.srcElement;
+  },
+
+  isLeftClick: function(event) {
+    return (((event.which) && (event.which == 1)) ||
+            ((event.button) && (event.button == 1)));
+  },
+
+  pointerX: function(event) {
+    return event.pageX || (event.clientX +
+      (document.documentElement.scrollLeft || document.body.scrollLeft));
+  },
+
+  pointerY: function(event) {
+    return event.pageY || (event.clientY +
+      (document.documentElement.scrollTop || document.body.scrollTop));
+  },
+
+  stop: function(event) {
+    if (event.preventDefault) {
+      event.preventDefault();
+      event.stopPropagation();
+    } else {
+      event.returnValue = false;
+      event.cancelBubble = true;
+    }
+  },
+
+  // find the first node with the given tagName, starting from the
+  // node the event was triggered on; traverses the DOM upwards
+  findElement: function(event, tagName) {
+    var element = Event.element(event);
+    while (element.parentNode && (!element.tagName ||
+        (element.tagName.toUpperCase() != tagName.toUpperCase())))
+      element = element.parentNode;
+    return element;
+  },
+
+  observers: false,
+
+  _observeAndCache: function(element, name, observer, useCapture) {
+    if (!this.observers) this.observers = [];
+    if (element.addEventListener) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.addEventListener(name, observer, useCapture);
+    } else if (element.attachEvent) {
+      this.observers.push([element, name, observer, useCapture]);
+      element.attachEvent('on' + name, observer);
+    }
+  },
+
+  unloadCache: function() {
+    if (!Event.observers) return;
+    for (var i = 0; i < Event.observers.length; i++) {
+      Event.stopObserving.apply(this, Event.observers[i]);
+      Event.observers[i][0] = null;
+    }
+    Event.observers = false;
+  },
+
+  observe: function(element, name, observer, useCapture) {
+    var element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.attachEvent))
+      name = 'keydown';
+
+    this._observeAndCache(element, name, observer, useCapture);
+  },
+
+  stopObserving: function(element, name, observer, useCapture) {
+    var element = $(element);
+    useCapture = useCapture || false;
+
+    if (name == 'keypress' &&
+        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+        || element.detachEvent))
+      name = 'keydown';
+
+    if (element.removeEventListener) {
+      element.removeEventListener(name, observer, useCapture);
+    } else if (element.detachEvent) {
+      element.detachEvent('on' + name, observer);
+    }
+  }
+});
+
+/* prevent memory leaks in IE */
+if (navigator.appVersion.match(/\bMSIE\b/))
+  Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  realOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        p = Element.getStyle(element, 'position');
+        if (p == 'relative' || p == 'absolute') break;
+      }
+    } while (element);
+    return [valueL, valueT];
+  },
+
+  offsetParent: function(element) {
+    if (element.offsetParent) return element.offsetParent;
+    if (element == document.body) return element;
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return element;
+
+    return document.body;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = this.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = this.realOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = this.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  clone: function(source, target) {
+    source = $(source);
+    target = $(target);
+    target.style.position = 'absolute';
+    var offsets = this.cumulativeOffset(source);
+    target.style.top    = offsets[1] + 'px';
+    target.style.left   = offsets[0] + 'px';
+    target.style.width  = source.offsetWidth + 'px';
+    target.style.height = source.offsetHeight + 'px';
+  },
+
+  page: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent==document.body)
+        if (Element.getStyle(element,'position')=='absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      valueT -= element.scrollTop  || 0;
+      valueL -= element.scrollLeft || 0;
+    } while (element = element.parentNode);
+
+    return [valueL, valueT];
+  },
+
+  clone: function(source, target) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || {})
+
+    // find page position of source
+    source = $(source);
+    var p = Position.page(source);
+
+    // find coordinate system to use
+    target = $(target);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(target,'position') == 'absolute') {
+      parent = Position.offsetParent(target);
+      delta = Position.page(parent);
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
+    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.style.position == 'absolute') return;
+    Position.prepare();
+
+    var offsets = Position.positionedOffset(element);
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';;
+    element.style.left   = left + 'px';;
+    element.style.width  = width + 'px';;
+    element.style.height = height + 'px';;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.style.position == 'relative') return;
+    Position.prepare();
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+  }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned.  For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+  Position.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return [valueL, valueT];
+  }
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/scriptaculous.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/scriptaculous.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/scriptaculous.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,47 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var Scriptaculous = {
+  Version: '1.6.1',
+  require: function(libraryName) {
+    // inserting via DOM fails in Safari 2.0, so brute force approach
+    document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
+  },
+  load: function() {
+    if((typeof Prototype=='undefined') || 
+       (typeof Element == 'undefined') || 
+       (typeof Element.Methods=='undefined') ||
+       parseFloat(Prototype.Version.split(".")[0] + "." +
+                  Prototype.Version.split(".")[1]) < 1.5)
+       throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0");
+    
+    $A(document.getElementsByTagName("script")).findAll( function(s) {
+      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+    }).each( function(s) {
+      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+      var includes = s.src.match(/\?.*load=([a-z,]*)/);
+      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each(
+       function(include) { Scriptaculous.require(path+include+'.js') });
+    });
+  }
+}
+
+Scriptaculous.load();
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/slider.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/slider.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/slider.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,283 @@
+// Copyright (c) 2005 Marty Haught, Thomas Fuchs 
+//
+// See http://script.aculo.us for more info
+// 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if(!Control) var Control = {};
+Control.Slider = Class.create();
+
+// options:
+//  axis: 'vertical', or 'horizontal' (default)
+//
+// callbacks:
+//  onChange(value)
+//  onSlide(value)
+Control.Slider.prototype = {
+  initialize: function(handle, track, options) {
+    var slider = this;
+    
+    if(handle instanceof Array) {
+      this.handles = handle.collect( function(e) { return $(e) });
+    } else {
+      this.handles = [$(handle)];
+    }
+    
+    this.track   = $(track);
+    this.options = options || {};
+
+    this.axis      = this.options.axis || 'horizontal';
+    this.increment = this.options.increment || 1;
+    this.step      = parseInt(this.options.step || '1');
+    this.range     = this.options.range || $R(0,1);
+    
+    this.value     = 0; // assure backwards compat
+    this.values    = this.handles.map( function() { return 0 });
+    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
+    this.options.startSpan = $(this.options.startSpan || null);
+    this.options.endSpan   = $(this.options.endSpan || null);
+
+    this.restricted = this.options.restricted || false;
+
+    this.maximum   = this.options.maximum || this.range.end;
+    this.minimum   = this.options.minimum || this.range.start;
+
+    // Will be used to align the handle onto the track, if necessary
+    this.alignX = parseInt(this.options.alignX || '0');
+    this.alignY = parseInt(this.options.alignY || '0');
+    
+    this.trackLength = this.maximumOffset() - this.minimumOffset();
+    this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth;
+
+    this.active   = false;
+    this.dragging = false;
+    this.disabled = false;
+
+    if(this.options.disabled) this.setDisabled();
+
+    // Allowed values array
+    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
+    if(this.allowedValues) {
+      this.minimum = this.allowedValues.min();
+      this.maximum = this.allowedValues.max();
+    }
+
+    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+    this.eventMouseMove = this.update.bindAsEventListener(this);
+
+    // Initialize handles in reverse (make sure first handle is active)
+    this.handles.each( function(h,i) {
+      i = slider.handles.length-1-i;
+      slider.setValue(parseFloat(
+        (slider.options.sliderValue instanceof Array ? 
+          slider.options.sliderValue[i] : slider.options.sliderValue) || 
+         slider.range.start), i);
+      Element.makePositioned(h); // fix IE
+      Event.observe(h, "mousedown", slider.eventMouseDown);
+    });
+    
+    Event.observe(this.track, "mousedown", this.eventMouseDown);
+    Event.observe(document, "mouseup", this.eventMouseUp);
+    Event.observe(document, "mousemove", this.eventMouseMove);
+    
+    this.initialized = true;
+  },
+  dispose: function() {
+    var slider = this;    
+    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
+    Event.stopObserving(document, "mouseup", this.eventMouseUp);
+    Event.stopObserving(document, "mousemove", this.eventMouseMove);
+    this.handles.each( function(h) {
+      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
+    });
+  },
+  setDisabled: function(){
+    this.disabled = true;
+  },
+  setEnabled: function(){
+    this.disabled = false;
+  },  
+  getNearestValue: function(value){
+    if(this.allowedValues){
+      if(value >= this.allowedValues.max()) return(this.allowedValues.max());
+      if(value <= this.allowedValues.min()) return(this.allowedValues.min());
+      
+      var offset = Math.abs(this.allowedValues[0] - value);
+      var newValue = this.allowedValues[0];
+      this.allowedValues.each( function(v) {
+        var currentOffset = Math.abs(v - value);
+        if(currentOffset <= offset){
+          newValue = v;
+          offset = currentOffset;
+        } 
+      });
+      return newValue;
+    }
+    if(value > this.range.end) return this.range.end;
+    if(value < this.range.start) return this.range.start;
+    return value;
+  },
+  setValue: function(sliderValue, handleIdx){
+    if(!this.active) {
+      this.activeHandle    = this.handles[handleIdx];
+      this.activeHandleIdx = handleIdx;
+      this.updateStyles();
+    }
+    handleIdx = handleIdx || this.activeHandleIdx || 0;
+    if(this.initialized && this.restricted) {
+      if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
+        sliderValue = this.values[handleIdx-1];
+      if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
+        sliderValue = this.values[handleIdx+1];
+    }
+    sliderValue = this.getNearestValue(sliderValue);
+    this.values[handleIdx] = sliderValue;
+    this.value = this.values[0]; // assure backwards compat
+    
+    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = 
+      this.translateToPx(sliderValue);
+    
+    this.drawSpans();
+    if(!this.dragging || !this.event) this.updateFinished();
+  },
+  setValueBy: function(delta, handleIdx) {
+    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, 
+      handleIdx || this.activeHandleIdx || 0);
+  },
+  translateToPx: function(value) {
+    return Math.round(
+      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * 
+      (value - this.range.start)) + "px";
+  },
+  translateToValue: function(offset) {
+    return ((offset/(this.trackLength-this.handleLength) * 
+      (this.range.end-this.range.start)) + this.range.start);
+  },
+  getRange: function(range) {
+    var v = this.values.sortBy(Prototype.K); 
+    range = range || 0;
+    return $R(v[range],v[range+1]);
+  },
+  minimumOffset: function(){
+    return(this.isVertical() ? this.alignY : this.alignX);
+  },
+  maximumOffset: function(){
+    return(this.isVertical() ?
+      this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX);
+  },  
+  isVertical:  function(){
+    return (this.axis == 'vertical');
+  },
+  drawSpans: function() {
+    var slider = this;
+    if(this.spans)
+      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
+    if(this.options.startSpan)
+      this.setSpan(this.options.startSpan,
+        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
+    if(this.options.endSpan)
+      this.setSpan(this.options.endSpan, 
+        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
+  },
+  setSpan: function(span, range) {
+    if(this.isVertical()) {
+      span.style.top = this.translateToPx(range.start);
+      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
+    } else {
+      span.style.left = this.translateToPx(range.start);
+      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
+    }
+  },
+  updateStyles: function() {
+    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
+    Element.addClassName(this.activeHandle, 'selected');
+  },
+  startDrag: function(event) {
+    if(Event.isLeftClick(event)) {
+      if(!this.disabled){
+        this.active = true;
+        
+        var handle = Event.element(event);
+        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
+        if(handle==this.track) {
+          var offsets  = Position.cumulativeOffset(this.track); 
+          this.event = event;
+          this.setValue(this.translateToValue( 
+           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
+          ));
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        } else {
+          // find the handle (prevents issues with Safari)
+          while((this.handles.indexOf(handle) == -1) && handle.parentNode) 
+            handle = handle.parentNode;
+        
+          this.activeHandle    = handle;
+          this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+          this.updateStyles();
+        
+          var offsets  = Position.cumulativeOffset(this.activeHandle);
+          this.offsetX = (pointer[0] - offsets[0]);
+          this.offsetY = (pointer[1] - offsets[1]);
+        }
+      }
+      Event.stop(event);
+    }
+  },
+  update: function(event) {
+   if(this.active) {
+      if(!this.dragging) this.dragging = true;
+      this.draw(event);
+      // fix AppleWebKit rendering
+      if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+      Event.stop(event);
+   }
+  },
+  draw: function(event) {
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    var offsets = Position.cumulativeOffset(this.track);
+    pointer[0] -= this.offsetX + offsets[0];
+    pointer[1] -= this.offsetY + offsets[1];
+    this.event = event;
+    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
+    if(this.initialized && this.options.onSlide)
+      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
+  },
+  endDrag: function(event) {
+    if(this.active && this.dragging) {
+      this.finishDrag(event, true);
+      Event.stop(event);
+    }
+    this.active = false;
+    this.dragging = false;
+  },  
+  finishDrag: function(event, success) {
+    this.active = false;
+    this.dragging = false;
+    this.updateFinished();
+  },
+  updateFinished: function() {
+    if(this.initialized && this.options.onChange) 
+      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
+    this.event = null;
+  }
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/unittest.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/unittest.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/ajax/unittest.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,383 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+//           (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+  var options = Object.extend({
+    pointerX: 0,
+    pointerY: 0,
+    buttons: 0
+  }, arguments[2] || {});
+  var oEvent = document.createEvent("MouseEvents");
+  oEvent.initMouseEvent(eventName, true, true, document.defaultView, 
+    options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, 
+    false, false, false, false, 0, $(element));
+  
+  if(this.mark) Element.remove(this.mark);
+  this.mark = document.createElement('div');
+  this.mark.appendChild(document.createTextNode(" "));
+  document.body.appendChild(this.mark);
+  this.mark.style.position = 'absolute';
+  this.mark.style.top = options.pointerY + "px";
+  this.mark.style.left = options.pointerX + "px";
+  this.mark.style.width = "5px";
+  this.mark.style.height = "5px;";
+  this.mark.style.borderTop = "1px solid red;"
+  this.mark.style.borderLeft = "1px solid red;"
+  
+  if(this.step)
+    alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+  
+  $(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+  var options = Object.extend({
+    ctrlKey: false,
+    altKey: false,
+    shiftKey: false,
+    metaKey: false,
+    keyCode: 0,
+    charCode: 0
+  }, arguments[2] || {});
+
+  var oEvent = document.createEvent("KeyEvents");
+  oEvent.initKeyEvent(eventName, true, true, window, 
+    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+    options.keyCode, options.charCode );
+  $(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+  for(var i=0; i<command.length; i++) {
+    Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+  }
+};
+
+var Test = {}
+Test.Unit = {};
+
+// security exception workaround
+Test.Unit.inspect = Object.inspect;
+
+Test.Unit.Logger = Class.create();
+Test.Unit.Logger.prototype = {
+  initialize: function(log) {
+    this.log = $(log);
+    if (this.log) {
+      this._createLogTable();
+    }
+  },
+  start: function(testName) {
+    if (!this.log) return;
+    this.testName = testName;
+    this.lastLogLine = document.createElement('tr');
+    this.statusCell = document.createElement('td');
+    this.nameCell = document.createElement('td');
+    this.nameCell.appendChild(document.createTextNode(testName));
+    this.messageCell = document.createElement('td');
+    this.lastLogLine.appendChild(this.statusCell);
+    this.lastLogLine.appendChild(this.nameCell);
+    this.lastLogLine.appendChild(this.messageCell);
+    this.loglines.appendChild(this.lastLogLine);
+  },
+  finish: function(status, summary) {
+    if (!this.log) return;
+    this.lastLogLine.className = status;
+    this.statusCell.innerHTML = status;
+    this.messageCell.innerHTML = this._toHTML(summary);
+  },
+  message: function(message) {
+    if (!this.log) return;
+    this.messageCell.innerHTML = this._toHTML(message);
+  },
+  summary: function(summary) {
+    if (!this.log) return;
+    this.logsummary.innerHTML = this._toHTML(summary);
+  },
+  _createLogTable: function() {
+    this.log.innerHTML =
+    '<div id="logsummary"></div>' +
+    '<table id="logtable">' +
+    '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+    '<tbody id="loglines"></tbody>' +
+    '</table>';
+    this.logsummary = $('logsummary')
+    this.loglines = $('loglines');
+  },
+  _toHTML: function(txt) {
+    return txt.escapeHTML().replace(/\n/g,"<br/>");
+  }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+  initialize: function(testcases) {
+    this.options = Object.extend({
+      testLog: 'testlog'
+    }, arguments[1] || {});
+    this.options.resultsURL = this.parseResultsURLQueryParameter();
+    if (this.options.testLog) {
+      this.options.testLog = $(this.options.testLog) || null;
+    }
+    if(this.options.tests) {
+      this.tests = [];
+      for(var i = 0; i < this.options.tests.length; i++) {
+        if(/^test/.test(this.options.tests[i])) {
+          this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+        }
+      }
+    } else {
+      if (this.options.test) {
+        this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+      } else {
+        this.tests = [];
+        for(var testcase in testcases) {
+          if(/^test/.test(testcase)) {
+            this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"]));
+          }
+        }
+      }
+    }
+    this.currentTest = 0;
+    this.logger = new Test.Unit.Logger(this.options.testLog);
+    setTimeout(this.runTests.bind(this), 1000);
+  },
+  parseResultsURLQueryParameter: function() {
+    return window.location.search.parseQuery()["resultsURL"];
+  },
+  // Returns:
+  //  "ERROR" if there was an error,
+  //  "FAILURE" if there was a failure, or
+  //  "SUCCESS" if there was neither
+  getResult: function() {
+    var hasFailure = false;
+    for(var i=0;i<this.tests.length;i++) {
+      if (this.tests[i].errors > 0) {
+        return "ERROR";
+      }
+      if (this.tests[i].failures > 0) {
+        hasFailure = true;
+      }
+    }
+    if (hasFailure) {
+      return "FAILURE";
+    } else {
+      return "SUCCESS";
+    }
+  },
+  postResults: function() {
+    if (this.options.resultsURL) {
+      new Ajax.Request(this.options.resultsURL, 
+        { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+    }
+  },
+  runTests: function() {
+    var test = this.tests[this.currentTest];
+    if (!test) {
+      // finished!
+      this.postResults();
+      this.logger.summary(this.summary());
+      return;
+    }
+    if(!test.isWaiting) {
+      this.logger.start(test.name);
+    }
+    test.run();
+    if(test.isWaiting) {
+      this.logger.message("Waiting for " + test.timeToWait + "ms");
+      setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+    } else {
+      this.logger.finish(test.status(), test.summary());
+      this.currentTest++;
+      // tail recursive, hopefully the browser will skip the stackframe
+      this.runTests();
+    }
+  },
+  summary: function() {
+    var assertions = 0;
+    var failures = 0;
+    var errors = 0;
+    var messages = [];
+    for(var i=0;i<this.tests.length;i++) {
+      assertions +=   this.tests[i].assertions;
+      failures   +=   this.tests[i].failures;
+      errors     +=   this.tests[i].errors;
+    }
+    return (
+      this.tests.length + " tests, " + 
+      assertions + " assertions, " + 
+      failures   + " failures, " +
+      errors     + " errors");
+  }
+}
+
+Test.Unit.Assertions = Class.create();
+Test.Unit.Assertions.prototype = {
+  initialize: function() {
+    this.assertions = 0;
+    this.failures   = 0;
+    this.errors     = 0;
+    this.messages   = [];
+  },
+  summary: function() {
+    return (
+      this.assertions + " assertions, " + 
+      this.failures   + " failures, " +
+      this.errors     + " errors" + "\n" +
+      this.messages.join("\n"));
+  },
+  pass: function() {
+    this.assertions++;
+  },
+  fail: function(message) {
+    this.failures++;
+    this.messages.push("Failure: " + message);
+  },
+  info: function(message) {
+    this.messages.push("Info: " + message);
+  },
+  error: function(error) {
+    this.errors++;
+    this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
+  },
+  status: function() {
+    if (this.failures > 0) return 'failed';
+    if (this.errors > 0) return 'error';
+    return 'passed';
+  },
+  assert: function(expression) {
+    var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+    try { expression ? this.pass() : 
+      this.fail(message); }
+    catch(e) { this.error(e); }
+  },
+  assertEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEqual";
+    try { (expected == actual) ? this.pass() :
+      this.fail(message + ': expected "' + Test.Unit.inspect(expected) + 
+        '", actual "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertEnumEqual: function(expected, actual) {
+    var message = arguments[2] || "assertEnumEqual";
+    try { $A(expected).length == $A(actual).length && 
+      expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
+        this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + 
+          ', actual ' + Test.Unit.inspect(actual)); }
+    catch(e) { this.error(e); }
+  },
+  assertNotEqual: function(expected, actual) {
+    var message = arguments[2] || "assertNotEqual";
+    try { (expected != actual) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertNull: function(obj) {
+    var message = arguments[1] || 'assertNull'
+    try { (obj==null) ? this.pass() : 
+      this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+    catch(e) { this.error(e); }
+  },
+  assertHidden: function(element) {
+    var message = arguments[1] || 'assertHidden';
+    this.assertEqual("none", element.style.display, message);
+  },
+  assertNotNull: function(object) {
+    var message = arguments[1] || 'assertNotNull';
+    this.assert(object != null, message);
+  },
+  assertInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertInstanceOf';
+    try { 
+      (actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was not an instance of the expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  assertNotInstanceOf: function(expected, actual) {
+    var message = arguments[2] || 'assertNotInstanceOf';
+    try { 
+      !(actual instanceof expected) ? this.pass() : 
+      this.fail(message + ": object was an instance of the not expected type"); }
+    catch(e) { this.error(e); } 
+  },
+  _isVisible: function(element) {
+    element = $(element);
+    if(!element.parentNode) return true;
+    this.assertNotNull(element);
+    if(element.style && Element.getStyle(element, 'display') == 'none')
+      return false;
+    
+    return this._isVisible(element.parentNode);
+  },
+  assertNotVisible: function(element) {
+    this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+  },
+  assertVisible: function(element) {
+    this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+  },
+  benchmark: function(operation, iterations) {
+    var startAt = new Date();
+    (iterations || 1).times(operation);
+    var timeTaken = ((new Date())-startAt);
+    this.info((arguments[2] || 'Operation') + ' finished ' + 
+       iterations + ' iterations in ' + (timeTaken/1000)+'s' );
+    return timeTaken;
+  }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+  initialize: function(name, test, setup, teardown) {
+    Test.Unit.Assertions.prototype.initialize.bind(this)();
+    this.name           = name;
+    this.test           = test || function() {};
+    this.setup          = setup || function() {};
+    this.teardown       = teardown || function() {};
+    this.isWaiting      = false;
+    this.timeToWait     = 1000;
+  },
+  wait: function(time, nextPart) {
+    this.isWaiting = true;
+    this.test = nextPart;
+    this.timeToWait = time;
+  },
+  run: function() {
+    try {
+      try {
+        if (!this.isWaiting) this.setup.bind(this)();
+        this.isWaiting = false;
+        this.test.bind(this)();
+      } finally {
+        if(!this.isWaiting) {
+          this.teardown.bind(this)();
+        }
+      }
+    }
+    catch(e) { this.error(e); }
+  }
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/banner.gif
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/banner.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/basicAuth/index.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/basicAuth/index.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/basicAuth/index.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,3 @@
+<html><head><title>Welcome</title></head>
+<body><h1 id="welcome">Welcome</h1></body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/LICENSE
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/LICENSE	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/LICENSE	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,195 @@
+Dojo is availble under *either* the terms of the modified BSD license *or* the
+Academic Free License version 2.1. As a recipient of Dojo, you may choose which
+license to receive this code under (except as noted in per-module LICENSE
+files). Some modules may not be the copyright of the Dojo Foundation. These
+modules contain explicit declarations of copyright in both the LICENSE files in
+the directories in which they reside and in the code itself. No external
+contributions are allowed under licenses which are fundamentally incompatible
+with the AFL or BSD licenses that Dojo is distributed under.
+
+The text of the AFL and BSD licenses is reproduced below. 
+
+-------------------------------------------------------------------------------
+The "New" BSD License:
+**********************
+
+Copyright (c) 2005, The Dojo Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, this
+	list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright notice,
+	this list of conditions and the following disclaimer in the documentation
+	and/or other materials provided with the distribution.
+  * Neither the name of the Dojo Foundation nor the names of its contributors
+	may be used to endorse or promote products derived from this software
+	without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------------------
+The Academic Free License, v. 2.1:
+**********************************
+
+This Academic Free License (the "License") applies to any original work of
+authorship (the "Original Work") whose owner (the "Licensor") has placed the
+following notice immediately following the copyright notice for the Original
+Work:
+
+Licensed under the Academic Free License version 2.1
+
+1) Grant of Copyright License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license to do the
+following:
+
+a) to reproduce the Original Work in copies;
+
+b) to prepare derivative works ("Derivative Works") based upon the Original
+Work;
+
+c) to distribute copies of the Original Work and Derivative Works to the
+public;
+
+d) to perform the Original Work publicly; and
+
+e) to display the Original Work publicly.
+
+2) Grant of Patent License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license, under patent
+claims owned or controlled by the Licensor that are embodied in the Original
+Work as furnished by the Licensor, to make, use, sell and offer for sale the
+Original Work and Derivative Works.
+
+3) Grant of Source Code License. The term "Source Code" means the preferred
+form of the Original Work for making modifications to it and all available
+documentation describing how to modify the Original Work. Licensor hereby
+agrees to provide a machine-readable copy of the Source Code of the Original
+Work along with each copy of the Original Work that Licensor distributes.
+Licensor reserves the right to satisfy this obligation by placing a
+machine-readable copy of the Source Code in an information repository
+reasonably calculated to permit inexpensive and convenient access by You for as
+long as Licensor continues to distribute the Original Work, and by publishing
+the address of that information repository in a notice immediately following
+the copyright notice that applies to the Original Work.
+
+4) Exclusions From License Grant. Neither the names of Licensor, nor the names
+of any contributors to the Original Work, nor any of their trademarks or
+service marks, may be used to endorse or promote products derived from this
+Original Work without express prior written permission of the Licensor. Nothing
+in this License shall be deemed to grant any rights to trademarks, copyrights,
+patents, trade secrets or any other intellectual property of Licensor except as
+expressly stated herein. No patent license is granted to make, use, sell or
+offer to sell embodiments of any patent claims other than the licensed claims
+defined in Section 2. No right is granted to the trademarks of Licensor even if
+such marks are included in the Original Work. Nothing in this License shall be
+interpreted to prohibit Licensor from licensing under different terms from this
+License any Original Work that Licensor otherwise would have a right to
+license.
+
+5) This section intentionally omitted.
+
+6) Attribution Rights. You must retain, in the Source Code of any Derivative
+Works that You create, all copyright, patent or trademark notices from the
+Source Code of the Original Work, as well as any notices of licensing and any
+descriptive text identified therein as an "Attribution Notice." You must cause
+the Source Code for any Derivative Works that You create to carry a prominent
+Attribution Notice reasonably calculated to inform recipients that You have
+modified the Original Work.
+
+7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
+the copyright in and to the Original Work and the patent rights granted herein
+by Licensor are owned by the Licensor or are sublicensed to You under the terms
+of this License with the permission of the contributor(s) of those copyrights
+and patent rights. Except as expressly stated in the immediately proceeding
+sentence, the Original Work is provided under this License on an "AS IS" BASIS
+and WITHOUT WARRANTY, either express or implied, including, without limitation,
+the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU.
+This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No
+license to Original Work is granted hereunder except under this disclaimer.
+
+8) Limitation of Liability. Under no circumstances and under no legal theory,
+whether in tort (including negligence), contract, or otherwise, shall the
+Licensor be liable to any person for any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License
+or the use of the Original Work including, without limitation, damages for loss
+of goodwill, work stoppage, computer failure or malfunction, or any and all
+other commercial damages or losses. This limitation of liability shall not
+apply to liability for death or personal injury resulting from Licensor's
+negligence to the extent applicable law prohibits such limitation. Some
+jurisdictions do not allow the exclusion or limitation of incidental or
+consequential damages, so this exclusion and limitation may not apply to You.
+
+9) Acceptance and Termination. If You distribute copies of the Original Work or
+a Derivative Work, You must make a reasonable effort under the circumstances to
+obtain the express assent of recipients to the terms of this License. Nothing
+else but this License (or another written agreement between Licensor and You)
+grants You permission to create Derivative Works based upon the Original Work
+or to exercise any of the rights granted in Section 1 herein, and any attempt
+to do so except under the terms of this License (or another written agreement
+between Licensor and You) is expressly prohibited by U.S. copyright law, the
+equivalent laws of other countries, and by international treaty. Therefore, by
+exercising any of the rights granted to You in Section 1 herein, You indicate
+Your acceptance of this License and all of its terms and conditions.
+
+10) Termination for Patent Action. This License shall terminate automatically
+and You may no longer exercise any of the rights granted to You by this License
+as of the date You commence an action, including a cross-claim or counterclaim,
+against Licensor or any licensee alleging that the Original Work infringes a
+patent. This termination provision shall not apply for an action alleging
+patent infringement by combinations of the Original Work with other software or
+hardware.
+
+11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
+License may be brought only in the courts of a jurisdiction wherein the
+Licensor resides or in which Licensor conducts its primary business, and under
+the laws of that jurisdiction excluding its conflict-of-law provisions. The
+application of the United Nations Convention on Contracts for the International
+Sale of Goods is expressly excluded. Any use of the Original Work outside the
+scope of this License or after its termination shall be subject to the
+requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et
+seq., the equivalent laws of other countries, and international treaty. This
+section shall survive the termination of this License.
+
+12) Attorneys Fees. In any action to enforce the terms of this License or
+seeking damages relating thereto, the prevailing party shall be entitled to
+recover its costs and expenses, including, without limitation, reasonable
+attorneys' fees and costs incurred in connection with such action, including
+any appeal of such action. This section shall survive the termination of this
+License.
+
+13) Miscellaneous. This License represents the complete agreement concerning
+the subject matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent necessary to
+make it enforceable.
+
+14) Definition of "You" in This License. "You" throughout this License, whether
+in upper or lower case, means an individual or a legal entity exercising rights
+under, and complying with all of the terms of, this License. For legal
+entities, "You" includes any entity that controls, is controlled by, or is
+under common control with you. For purposes of this definition, "control" means
+(i) the power, direct or indirect, to cause the direction or management of such
+entity, whether by contract or otherwise, or (ii) ownership of fifty percent
+(50%) or more of the outstanding shares, or (iii) beneficial ownership of such
+entity.
+
+15) Right to Use. You may use the Original Work in all ways not otherwise
+restricted or conditioned by this License or by law, and Licensor promises not
+to interfere with or be responsible for such uses by You.
+
+This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved.
+Permission is hereby granted to copy and distribute this license without
+modification. This license may not be modified without the express written
+permission of its copyright owner.

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/README
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/README	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/README	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,176 @@
+The Dojo Toolkit
+----------------
+
+Dojo is a portable JavaScript toolkit for web application developers and
+JavaScript professionals. Dojo solves real-world problems by providing powerful
+abstractions and solid, tested implementations.
+
+Getting Started
+---------------
+
+To use Dojo in your application, download one of the pre-built editions from the
+Dojo website, http://dojotoolkit.org. Once you have downloaded the file you will
+need to unzip the archive in your website root. At a minimum, you will need to
+extract:
+
+    src/ (folder)
+    dojo.js
+    iframe_history.html
+
+To begin using dojo, include dojo in your pages by using:
+
+    <script type="text/javascript" src="/path/to/dojo.js"></script>
+
+Depending on the edition that you have downloaded, this base dojo.js file may or
+may not include the modules you wish to use in your application. The files which
+have been "baked in" to the dojo.js that is part of your distribution are listed
+in the file build.txt that is part of the top-level directory that is created
+when you unpack the archive. To ensure modules you wish to use are available,
+use dojo.require() to request them. A very rich application might include:
+
+    <script type="text/javascript" src="/path/to/dojo.js"></script>
+    <script type="text/javascript">
+        dojo.require("dojo.event.*");       // sophisticated AOP event handling
+        dojo.require("dojo.io.*");          // for Ajax requests
+        dojo.require("dojo.storage.*");     // a persistent local data cache
+        dojo.require("dojo.json");          // serialization to JSON
+        dojo.require("dojo.dnd.*");         // drag-and-drop
+        dojo.require("dojo.lfx.*");         // animations and eye candy
+        dojo.require("dojo.widget.Editor2");// stable, portable HTML WYSIWYG
+    </script>
+
+Note that only those modules which are *not* already "baked in" to dojo.js by
+the edition's build process are requested by dojo.require(). This helps make
+your application faster without forcing you to use a build tool while in
+development. See "Building Dojo" and "Working From Source" for more details.
+
+
+Compatibility
+-------------
+
+In addition to it's suite of unit-tests for core system components, Dojo has
+been tested on almost every modern browser, including:
+
+    - IE 5.5+
+    - Mozilla 1.5+, Firefox 1.0+
+    - Safari 1.3.9+
+    - Konqueror 3.4+
+    - Opera 8.5+
+
+Note that some widgets and features may not perform exactly the same on every
+browser due to browser implementation differences.
+
+For those looking to use Dojo in non-browser environments, please see "Working
+From Source".
+
+
+Documentation and Getting Help
+------------------------------
+
+Articles outlining major Dojo systems are linked from:
+
+    http://dojotoolkit.org/docs/
+
+Toolkit APIs are listed in outline form at:
+
+    http://dojotoolkit.org/docs/apis/
+
+And documented in full at:
+
+    http://manual.dojotoolkit.org/
+
+The project also maintains a JotSpot Wiki at:
+
+    http://dojo.jot.com/
+
+A FAQ has been extracted from mailing list traffic:
+
+    http://dojo.jot.com/FAQ
+
+And the main Dojo user mailing list is archived and made searchable at:
+
+    http://news.gmane.org/gmane.comp.web.dojo.user/
+
+You can sign up for this list, which is a great place to ask questions, at:
+
+    http://dojotoolkit.org/mailman/listinfo/dojo-interest
+
+The Dojo developers also tend to hang out in IRC and help people with Dojo
+problems. You're most likely to find them at:
+
+    irc.freenode.net #dojo
+
+Note that 2PM Wed PST in this channel is reserved for a weekly meeting between
+project developers, although anyone is welcome to participate.
+
+
+Working From Source
+-------------------
+
+The core of Dojo is a powerful package system that allows developers to optimize
+Dojo for deployment while using *exactly the same* application code in
+development. Therefore, working from source is almost exactly like working from
+a pre-built edition. Pre-built editions are significantly faster to load than
+working from source, but are not as flexible when in development.
+
+There are multiple ways to get the source. Nightly snapshots of the Dojo source
+repository are available at:
+
+    http://archive.dojotoolkit.org/nightly.tgz
+
+Anonymous Subversion access is also available:
+
+    %> svn co http://svn.dojotoolkit.org/dojo/trunk/ dojo
+
+Each of these sources will include some extra directories not included in the
+pre-packaged editions, including command-line tests and build tools for
+constructing your own packages.
+
+Running the command-line unit test suite requires Ant 1.6. If it is installed
+and in your path, you can run the tests using:
+
+    %> cd buildscripts
+    %> ant test
+
+The command-line test harness makes use of Rhino, a JavaScript interpreter
+written in Java. Once you have a copy of Dojo's source tree, you have a copy of
+Rhino. From the root directory, you can use Rhino interactively to load Dojo:
+
+    %> java -jar buildscripts/lib/js.jar
+    Rhino 1.5 release 3 2002 01 27
+    js> load("dojo.js");
+    js> print(dojo);
+    [object Object]
+    js> quit();
+
+This environment is wonderful for testing raw JavaScript functionality in, or
+even for scripting your system. Since Rhino has full access to anything in
+Java's classpath, the sky is the limit!
+
+Building Dojo
+-------------
+
+Dojo requires Ant 1.6.x in order to build correctly. While using Dojo from
+source does *NOT* require that you make a build, speeding up your application by
+constructing a custom profile build does.
+
+Once you have Ant and a source snapshot of Dojo, you can make your own profile
+build ("edition") which includes only those modules your application uses by
+customizing one of the files in:
+
+    [dojo]/buildscripts/profiles/
+
+These files are named *.profile.js and each one contains a list of modules to
+include in a build. If we created a new profile called "test.profile.js", we
+could then make a profile build using it by doing:
+
+    %> cd buildscripts
+    %> ant -Dprofile=test -Ddocless=true release intern-strings
+
+If the build is successful, your newly minted and compressed  profile build will
+be placed in [dojo]/release/dojo/
+
+-------------------------------------------------------------------------------
+Copyright (c) 2004-2006, The Dojo Foundation, All Rights Reserved
+
+vim:ts=4:et:tw=80:shiftwidth=4:

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/build.txt
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/build.txt	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/build.txt	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,39 @@
+
+Files baked into this package:
+
+dojoGuardStart.js,
+../src/bootstrap1.js,
+../src/loader.js,
+dojoGuardEnd.js,
+../src/hostenv_browser.js,
+../src/bootstrap2.js,
+../src/string/common.js,
+../src/string.js,
+../src/lang/common.js,
+../src/lang/extras.js,
+../src/io/common.js,
+../src/lang/array.js,
+../src/lang/func.js,
+../src/string/extras.js,
+../src/dom.js,
+../src/undo/browser.js,
+../src/io/BrowserIO.js,
+../src/io/cookie.js,
+../src/io/__package__.js,
+../src/io.js,
+../src/event/common.js,
+../src/event/topic.js,
+../src/event/browser.js,
+../src/event/__package__.js,
+../src/gfx/color.js,
+../src/lfx/Animation.js,
+../src/uri/Uri.js,
+../src/html/style.js,
+../src/html/display.js,
+../src/html/color.js,
+../src/html/common.js,
+../src/html/layout.js,
+../src/lfx/html.js,
+../src/lfx/__package__.js
+
+		
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/dojo.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/dojo.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/dojo.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,6246 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/*
+	This is a compiled version of Dojo, built for deployment and not for
+	development. To get an editable version, please visit:
+
+		http://dojotoolkit.org
+
+	for documentation and information on getting the source.
+*/
+
+if(typeof dojo=="undefined"){
+var dj_global=this;
+var dj_currentContext=this;
+function dj_undef(_1,_2){
+return (typeof (_2||dj_currentContext)[_1]=="undefined");
+}
+if(dj_undef("djConfig",this)){
+var djConfig={};
+}
+if(dj_undef("dojo",this)){
+var dojo={};
+}
+dojo.global=function(){
+return dj_currentContext;
+};
+dojo.locale=djConfig.locale;
+dojo.version={major:0,minor:4,patch:0,flag:"",revision:Number("$Rev: 6258 $".match(/[0-9]+/)[0]),toString:function(){
+with(dojo.version){
+return major+"."+minor+"."+patch+flag+" ("+revision+")";
+}
+}};
+dojo.evalProp=function(_3,_4,_5){
+if((!_4)||(!_3)){
+return undefined;
+}
+if(!dj_undef(_3,_4)){
+return _4[_3];
+}
+return (_5?(_4[_3]={}):undefined);
+};
+dojo.parseObjPath=function(_6,_7,_8){
+var _9=(_7||dojo.global());
+var _a=_6.split(".");
+var _b=_a.pop();
+for(var i=0,l=_a.length;i<l&&_9;i++){
+_9=dojo.evalProp(_a[i],_9,_8);
+}
+return {obj:_9,prop:_b};
+};
+dojo.evalObjPath=function(_e,_f){
+if(typeof _e!="string"){
+return dojo.global();
+}
+if(_e.indexOf(".")==-1){
+return dojo.evalProp(_e,dojo.global(),_f);
+}
+var ref=dojo.parseObjPath(_e,dojo.global(),_f);
+if(ref){
+return dojo.evalProp(ref.prop,ref.obj,_f);
+}
+return null;
+};
+dojo.errorToString=function(_11){
+if(!dj_undef("message",_11)){
+return _11.message;
+}else{
+if(!dj_undef("description",_11)){
+return _11.description;
+}else{
+return _11;
+}
+}
+};
+dojo.raise=function(_12,_13){
+if(_13){
+_12=_12+": "+dojo.errorToString(_13);
+}
+try{
+if(djConfig.isDebug){
+dojo.hostenv.println("FATAL exception raised: "+_12);
+}
+}
+catch(e){
+}
+throw _13||Error(_12);
+};
+dojo.debug=function(){
+};
+dojo.debugShallow=function(obj){
+};
+dojo.profile={start:function(){
+},end:function(){
+},stop:function(){
+},dump:function(){
+}};
+function dj_eval(_15){
+return dj_global.eval?dj_global.eval(_15):eval(_15);
+}
+dojo.unimplemented=function(_16,_17){
+var _18="'"+_16+"' not implemented";
+if(_17!=null){
+_18+=" "+_17;
+}
+dojo.raise(_18);
+};
+dojo.deprecated=function(_19,_1a,_1b){
+var _1c="DEPRECATED: "+_19;
+if(_1a){
+_1c+=" "+_1a;
+}
+if(_1b){
+_1c+=" -- will be removed in version: "+_1b;
+}
+dojo.debug(_1c);
+};
+dojo.render=(function(){
+function vscaffold(_1d,_1e){
+var tmp={capable:false,support:{builtin:false,plugin:false},prefixes:_1d};
+for(var i=0;i<_1e.length;i++){
+tmp[_1e[i]]=false;
+}
+return tmp;
+}
+return {name:"",ver:dojo.version,os:{win:false,linux:false,osx:false},html:vscaffold(["html"],["ie","opera","khtml","safari","moz"]),svg:vscaffold(["svg"],["corel","adobe","batik"]),vml:vscaffold(["vml"],["ie"]),swf:vscaffold(["Swf","Flash","Mm"],["mm"]),swt:vscaffold(["Swt"],["ibm"])};
+})();
+dojo.hostenv=(function(){
+var _21={isDebug:false,allowQueryConfig:false,baseScriptUri:"",baseRelativePath:"",libraryScriptUri:"",iePreventClobber:false,ieClobberMinimal:true,preventBackButtonFix:true,delayMozLoadingFix:false,searchIds:[],parseWidgets:true};
+if(typeof djConfig=="undefined"){
+djConfig=_21;
+}else{
+for(var _22 in _21){
+if(typeof djConfig[_22]=="undefined"){
+djConfig[_22]=_21[_22];
+}
+}
+}
+return {name_:"(unset)",version_:"(unset)",getName:function(){
+return this.name_;
+},getVersion:function(){
+return this.version_;
+},getText:function(uri){
+dojo.unimplemented("getText","uri="+uri);
+}};
+})();
+dojo.hostenv.getBaseScriptUri=function(){
+if(djConfig.baseScriptUri.length){
+return djConfig.baseScriptUri;
+}
+var uri=new String(djConfig.libraryScriptUri||djConfig.baseRelativePath);
+if(!uri){
+dojo.raise("Nothing returned by getLibraryScriptUri(): "+uri);
+}
+var _25=uri.lastIndexOf("/");
+djConfig.baseScriptUri=djConfig.baseRelativePath;
+return djConfig.baseScriptUri;
+};
+(function(){
+var _26={pkgFileName:"__package__",loading_modules_:{},loaded_modules_:{},addedToLoadingCount:[],removedFromLoadingCount:[],inFlightCount:0,modulePrefixes_:{dojo:{name:"dojo",value:"src"}},setModulePrefix:function(_27,_28){
+this.modulePrefixes_[_27]={name:_27,value:_28};
+},moduleHasPrefix:function(_29){
+var mp=this.modulePrefixes_;
+return Boolean(mp[_29]&&mp[_29].value);
+},getModulePrefix:function(_2b){
+if(this.moduleHasPrefix(_2b)){
+return this.modulePrefixes_[_2b].value;
+}
+return _2b;
+},getTextStack:[],loadUriStack:[],loadedUris:[],post_load_:false,modulesLoadedListeners:[],unloadListeners:[],loadNotifying:false};
+for(var _2c in _26){
+dojo.hostenv[_2c]=_26[_2c];
+}
+})();
+dojo.hostenv.loadPath=function(_2d,_2e,cb){
+var uri;
+if(_2d.charAt(0)=="/"||_2d.match(/^\w+:/)){
+uri=_2d;
+}else{
+uri=this.getBaseScriptUri()+_2d;
+}
+if(djConfig.cacheBust&&dojo.render.html.capable){
+uri+="?"+String(djConfig.cacheBust).replace(/\W+/g,"");
+}
+try{
+return !_2e?this.loadUri(uri,cb):this.loadUriAndCheck(uri,_2e,cb);
+}
+catch(e){
+dojo.debug(e);
+return false;
+}
+};
+dojo.hostenv.loadUri=function(uri,cb){
+if(this.loadedUris[uri]){
+return true;
+}
+var _33=this.getText(uri,null,true);
+if(!_33){
+return false;
+}
+this.loadedUris[uri]=true;
+if(cb){
+_33="("+_33+")";
+}
+var _34=dj_eval(_33);
+if(cb){
+cb(_34);
+}
+return true;
+};
+dojo.hostenv.loadUriAndCheck=function(uri,_36,cb){
+var ok=true;
+try{
+ok=this.loadUri(uri,cb);
+}
+catch(e){
+dojo.debug("failed loading ",uri," with error: ",e);
+}
+return Boolean(ok&&this.findModule(_36,false));
+};
+dojo.loaded=function(){
+};
+dojo.unloaded=function(){
+};
+dojo.hostenv.loaded=function(){
+this.loadNotifying=true;
+this.post_load_=true;
+var mll=this.modulesLoadedListeners;
+for(var x=0;x<mll.length;x++){
+mll[x]();
+}
+this.modulesLoadedListeners=[];
+this.loadNotifying=false;
+dojo.loaded();
+};
+dojo.hostenv.unloaded=function(){
+var mll=this.unloadListeners;
+while(mll.length){
+(mll.pop())();
+}
+dojo.unloaded();
+};
+dojo.addOnLoad=function(obj,_3d){
+var dh=dojo.hostenv;
+if(arguments.length==1){
+dh.modulesLoadedListeners.push(obj);
+}else{
+if(arguments.length>1){
+dh.modulesLoadedListeners.push(function(){
+obj[_3d]();
+});
+}
+}
+if(dh.post_load_&&dh.inFlightCount==0&&!dh.loadNotifying){
+dh.callLoaded();
+}
+};
+dojo.addOnUnload=function(obj,_40){
+var dh=dojo.hostenv;
+if(arguments.length==1){
+dh.unloadListeners.push(obj);
+}else{
+if(arguments.length>1){
+dh.unloadListeners.push(function(){
+obj[_40]();
+});
+}
+}
+};
+dojo.hostenv.modulesLoaded=function(){
+if(this.post_load_){
+return;
+}
+if(this.loadUriStack.length==0&&this.getTextStack.length==0){
+if(this.inFlightCount>0){
+dojo.debug("files still in flight!");
+return;
+}
+dojo.hostenv.callLoaded();
+}
+};
+dojo.hostenv.callLoaded=function(){
+if(typeof setTimeout=="object"){
+setTimeout("dojo.hostenv.loaded();",0);
+}else{
+dojo.hostenv.loaded();
+}
+};
+dojo.hostenv.getModuleSymbols=function(_42){
+var _43=_42.split(".");
+for(var i=_43.length;i>0;i--){
+var _45=_43.slice(0,i).join(".");
+if((i==1)&&!this.moduleHasPrefix(_45)){
+_43[0]="../"+_43[0];
+}else{
+var _46=this.getModulePrefix(_45);
+if(_46!=_45){
+_43.splice(0,i,_46);
+break;
+}
+}
+}
+return _43;
+};
+dojo.hostenv._global_omit_module_check=false;
+dojo.hostenv.loadModule=function(_47,_48,_49){
+if(!_47){
+return;
+}
+_49=this._global_omit_module_check||_49;
+var _4a=this.findModule(_47,false);
+if(_4a){
+return _4a;
+}
+if(dj_undef(_47,this.loading_modules_)){
+this.addedToLoadingCount.push(_47);
+}
+this.loading_modules_[_47]=1;
+var _4b=_47.replace(/\./g,"/")+".js";
+var _4c=_47.split(".");
+var _4d=this.getModuleSymbols(_47);
+var _4e=((_4d[0].charAt(0)!="/")&&!_4d[0].match(/^\w+:/));
+var _4f=_4d[_4d.length-1];
+var ok;
+if(_4f=="*"){
+_47=_4c.slice(0,-1).join(".");
+while(_4d.length){
+_4d.pop();
+_4d.push(this.pkgFileName);
+_4b=_4d.join("/")+".js";
+if(_4e&&_4b.charAt(0)=="/"){
+_4b=_4b.slice(1);
+}
+ok=this.loadPath(_4b,!_49?_47:null);
+if(ok){
+break;
+}
+_4d.pop();
+}
+}else{
+_4b=_4d.join("/")+".js";
+_47=_4c.join(".");
+var _51=!_49?_47:null;
+ok=this.loadPath(_4b,_51);
+if(!ok&&!_48){
+_4d.pop();
+while(_4d.length){
+_4b=_4d.join("/")+".js";
+ok=this.loadPath(_4b,_51);
+if(ok){
+break;
+}
+_4d.pop();
+_4b=_4d.join("/")+"/"+this.pkgFileName+".js";
+if(_4e&&_4b.charAt(0)=="/"){
+_4b=_4b.slice(1);
+}
+ok=this.loadPath(_4b,_51);
+if(ok){
+break;
+}
+}
+}
+if(!ok&&!_49){
+dojo.raise("Could not load '"+_47+"'; last tried '"+_4b+"'");
+}
+}
+if(!_49&&!this["isXDomain"]){
+_4a=this.findModule(_47,false);
+if(!_4a){
+dojo.raise("symbol '"+_47+"' is not defined after loading '"+_4b+"'");
+}
+}
+return _4a;
+};
+dojo.hostenv.startPackage=function(_52){
+var _53=String(_52);
+var _54=_53;
+var _55=_52.split(/\./);
+if(_55[_55.length-1]=="*"){
+_55.pop();
+_54=_55.join(".");
+}
+var _56=dojo.evalObjPath(_54,true);
+this.loaded_modules_[_53]=_56;
+this.loaded_modules_[_54]=_56;
+return _56;
+};
+dojo.hostenv.findModule=function(_57,_58){
+var lmn=String(_57);
+if(this.loaded_modules_[lmn]){
+return this.loaded_modules_[lmn];
+}
+if(_58){
+dojo.raise("no loaded module named '"+_57+"'");
+}
+return null;
+};
+dojo.kwCompoundRequire=function(_5a){
+var _5b=_5a["common"]||[];
+var _5c=_5a[dojo.hostenv.name_]?_5b.concat(_5a[dojo.hostenv.name_]||[]):_5b.concat(_5a["default"]||[]);
+for(var x=0;x<_5c.length;x++){
+var _5e=_5c[x];
+if(_5e.constructor==Array){
+dojo.hostenv.loadModule.apply(dojo.hostenv,_5e);
+}else{
+dojo.hostenv.loadModule(_5e);
+}
+}
+};
+dojo.require=function(_5f){
+dojo.hostenv.loadModule.apply(dojo.hostenv,arguments);
+};
+dojo.requireIf=function(_60,_61){
+var _62=arguments[0];
+if((_62===true)||(_62=="common")||(_62&&dojo.render[_62].capable)){
+var _63=[];
+for(var i=1;i<arguments.length;i++){
+_63.push(arguments[i]);
+}
+dojo.require.apply(dojo,_63);
+}
+};
+dojo.requireAfterIf=dojo.requireIf;
+dojo.provide=function(_65){
+return dojo.hostenv.startPackage.apply(dojo.hostenv,arguments);
+};
+dojo.registerModulePath=function(_66,_67){
+return dojo.hostenv.setModulePrefix(_66,_67);
+};
+dojo.setModulePrefix=function(_68,_69){
+dojo.deprecated("dojo.setModulePrefix(\""+_68+"\", \""+_69+"\")","replaced by dojo.registerModulePath","0.5");
+return dojo.registerModulePath(_68,_69);
+};
+dojo.exists=function(obj,_6b){
+var p=_6b.split(".");
+for(var i=0;i<p.length;i++){
+if(!obj[p[i]]){
+return false;
+}
+obj=obj[p[i]];
+}
+return true;
+};
+dojo.hostenv.normalizeLocale=function(_6e){
+return _6e?_6e.toLowerCase():dojo.locale;
+};
+dojo.hostenv.searchLocalePath=function(_6f,_70,_71){
+_6f=dojo.hostenv.normalizeLocale(_6f);
+var _72=_6f.split("-");
+var _73=[];
+for(var i=_72.length;i>0;i--){
+_73.push(_72.slice(0,i).join("-"));
+}
+_73.push(false);
+if(_70){
+_73.reverse();
+}
+for(var j=_73.length-1;j>=0;j--){
+var loc=_73[j]||"ROOT";
+var _77=_71(loc);
+if(_77){
+break;
+}
+}
+};
+dojo.hostenv.localesGenerated;
+dojo.hostenv.registerNlsPrefix=function(){
+dojo.registerModulePath("nls","nls");
+};
+dojo.hostenv.preloadLocalizations=function(){
+if(dojo.hostenv.localesGenerated){
+dojo.hostenv.registerNlsPrefix();
+function preload(_78){
+_78=dojo.hostenv.normalizeLocale(_78);
+dojo.hostenv.searchLocalePath(_78,true,function(loc){
+for(var i=0;i<dojo.hostenv.localesGenerated.length;i++){
+if(dojo.hostenv.localesGenerated[i]==loc){
+dojo["require"]("nls.dojo_"+loc);
+return true;
+}
+}
+return false;
+});
+}
+preload();
+var _7b=djConfig.extraLocale||[];
+for(var i=0;i<_7b.length;i++){
+preload(_7b[i]);
+}
+}
+dojo.hostenv.preloadLocalizations=function(){
+};
+};
+dojo.requireLocalization=function(_7d,_7e,_7f){
+dojo.hostenv.preloadLocalizations();
+var _80=[_7d,"nls",_7e].join(".");
+var _81=dojo.hostenv.findModule(_80);
+if(_81){
+if(djConfig.localizationComplete&&_81._built){
+return;
+}
+var _82=dojo.hostenv.normalizeLocale(_7f).replace("-","_");
+var _83=_80+"."+_82;
+if(dojo.hostenv.findModule(_83)){
+return;
+}
+}
+_81=dojo.hostenv.startPackage(_80);
+var _84=dojo.hostenv.getModuleSymbols(_7d);
+var _85=_84.concat("nls").join("/");
+var _86;
+dojo.hostenv.searchLocalePath(_7f,false,function(loc){
+var _88=loc.replace("-","_");
+var _89=_80+"."+_88;
+var _8a=false;
+if(!dojo.hostenv.findModule(_89)){
+dojo.hostenv.startPackage(_89);
+var _8b=[_85];
+if(loc!="ROOT"){
+_8b.push(loc);
+}
+_8b.push(_7e);
+var _8c=_8b.join("/")+".js";
+_8a=dojo.hostenv.loadPath(_8c,null,function(_8d){
+var _8e=function(){
+};
+_8e.prototype=_86;
+_81[_88]=new _8e();
+for(var j in _8d){
+_81[_88][j]=_8d[j];
+}
+});
+}else{
+_8a=true;
+}
+if(_8a&&_81[_88]){
+_86=_81[_88];
+}else{
+_81[_88]=_86;
+}
+});
+};
+(function(){
+var _90=djConfig.extraLocale;
+if(_90){
+if(!_90 instanceof Array){
+_90=[_90];
+}
+var req=dojo.requireLocalization;
+dojo.requireLocalization=function(m,b,_94){
+req(m,b,_94);
+if(_94){
+return;
+}
+for(var i=0;i<_90.length;i++){
+req(m,b,_90[i]);
+}
+};
+}
+})();
+}
+if(typeof window!="undefined"){
+(function(){
+if(djConfig.allowQueryConfig){
+var _96=document.location.toString();
+var _97=_96.split("?",2);
+if(_97.length>1){
+var _98=_97[1];
+var _99=_98.split("&");
+for(var x in _99){
+var sp=_99[x].split("=");
+if((sp[0].length>9)&&(sp[0].substr(0,9)=="djConfig.")){
+var opt=sp[0].substr(9);
+try{
+djConfig[opt]=eval(sp[1]);
+}
+catch(e){
+djConfig[opt]=sp[1];
+}
+}
+}
+}
+}
+if(((djConfig["baseScriptUri"]=="")||(djConfig["baseRelativePath"]==""))&&(document&&document.getElementsByTagName)){
+var _9d=document.getElementsByTagName("script");
+var _9e=/(__package__|dojo|bootstrap1)\.js([\?\.]|$)/i;
+for(var i=0;i<_9d.length;i++){
+var src=_9d[i].getAttribute("src");
+if(!src){
+continue;
+}
+var m=src.match(_9e);
+if(m){
+var _a2=src.substring(0,m.index);
+if(src.indexOf("bootstrap1")>-1){
+_a2+="../";
+}
+if(!this["djConfig"]){
+djConfig={};
+}
+if(djConfig["baseScriptUri"]==""){
+djConfig["baseScriptUri"]=_a2;
+}
+if(djConfig["baseRelativePath"]==""){
+djConfig["baseRelativePath"]=_a2;
+}
+break;
+}
+}
+}
+var dr=dojo.render;
+var drh=dojo.render.html;
+var drs=dojo.render.svg;
+var dua=(drh.UA=navigator.userAgent);
+var dav=(drh.AV=navigator.appVersion);
+var t=true;
+var f=false;
+drh.capable=t;
+drh.support.builtin=t;
+dr.ver=parseFloat(drh.AV);
+dr.os.mac=dav.indexOf("Macintosh")>=0;
+dr.os.win=dav.indexOf("Windows")>=0;
+dr.os.linux=dav.indexOf("X11")>=0;
+drh.opera=dua.indexOf("Opera")>=0;
+drh.khtml=(dav.indexOf("Konqueror")>=0)||(dav.indexOf("Safari")>=0);
+drh.safari=dav.indexOf("Safari")>=0;
+var _aa=dua.indexOf("Gecko");
+drh.mozilla=drh.moz=(_aa>=0)&&(!drh.khtml);
+if(drh.mozilla){
+drh.geckoVersion=dua.substring(_aa+6,_aa+14);
+}
+drh.ie=(document.all)&&(!drh.opera);
+drh.ie50=drh.ie&&dav.indexOf("MSIE 5.0")>=0;
+drh.ie55=drh.ie&&dav.indexOf("MSIE 5.5")>=0;
+drh.ie60=drh.ie&&dav.indexOf("MSIE 6.0")>=0;
+drh.ie70=drh.ie&&dav.indexOf("MSIE 7.0")>=0;
+var cm=document["compatMode"];
+drh.quirks=(cm=="BackCompat")||(cm=="QuirksMode")||drh.ie55||drh.ie50;
+dojo.locale=dojo.locale||(drh.ie?navigator.userLanguage:navigator.language).toLowerCase();
+dr.vml.capable=drh.ie;
+drs.capable=f;
+drs.support.plugin=f;
+drs.support.builtin=f;
+var _ac=window["document"];
+var tdi=_ac["implementation"];
+if((tdi)&&(tdi["hasFeature"])&&(tdi.hasFeature("org.w3c.dom.svg","1.0"))){
+drs.capable=t;
+drs.support.builtin=t;
+drs.support.plugin=f;
+}
+if(drh.safari){
+var tmp=dua.split("AppleWebKit/")[1];
+var ver=parseFloat(tmp.split(" ")[0]);
+if(ver>=420){
+drs.capable=t;
+drs.support.builtin=t;
+drs.support.plugin=f;
+}
+}
+})();
+dojo.hostenv.startPackage("dojo.hostenv");
+dojo.render.name=dojo.hostenv.name_="browser";
+dojo.hostenv.searchIds=[];
+dojo.hostenv._XMLHTTP_PROGIDS=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"];
+dojo.hostenv.getXmlhttpObject=function(){
+var _b0=null;
+var _b1=null;
+try{
+_b0=new XMLHttpRequest();
+}
+catch(e){
+}
+if(!_b0){
+for(var i=0;i<3;++i){
+var _b3=dojo.hostenv._XMLHTTP_PROGIDS[i];
+try{
+_b0=new ActiveXObject(_b3);
+}
+catch(e){
+_b1=e;
+}
+if(_b0){
+dojo.hostenv._XMLHTTP_PROGIDS=[_b3];
+break;
+}
+}
+}
+if(!_b0){
+return dojo.raise("XMLHTTP not available",_b1);
+}
+return _b0;
+};
+dojo.hostenv._blockAsync=false;
+dojo.hostenv.getText=function(uri,_b5,_b6){
+if(!_b5){
+this._blockAsync=true;
+}
+var _b7=this.getXmlhttpObject();
+function isDocumentOk(_b8){
+var _b9=_b8["status"];
+return Boolean((!_b9)||((200<=_b9)&&(300>_b9))||(_b9==304));
+}
+if(_b5){
+var _ba=this,_bb=null,gbl=dojo.global();
+var xhr=dojo.evalObjPath("dojo.io.XMLHTTPTransport");
+_b7.onreadystatechange=function(){
+if(_bb){
+gbl.clearTimeout(_bb);
+_bb=null;
+}
+if(_ba._blockAsync||(xhr&&xhr._blockAsync)){
+_bb=gbl.setTimeout(function(){
+_b7.onreadystatechange.apply(this);
+},10);
+}else{
+if(4==_b7.readyState){
+if(isDocumentOk(_b7)){
+_b5(_b7.responseText);
+}
+}
+}
+};
+}
+_b7.open("GET",uri,_b5?true:false);
+try{
+_b7.send(null);
+if(_b5){
+return null;
+}
+if(!isDocumentOk(_b7)){
+var err=Error("Unable to load "+uri+" status:"+_b7.status);
+err.status=_b7.status;
+err.responseText=_b7.responseText;
+throw err;
+}
+}
+catch(e){
+this._blockAsync=false;
+if((_b6)&&(!_b5)){
+return null;
+}else{
+throw e;
+}
+}
+this._blockAsync=false;
+return _b7.responseText;
+};
+dojo.hostenv.defaultDebugContainerId="dojoDebug";
+dojo.hostenv._println_buffer=[];
+dojo.hostenv._println_safe=false;
+dojo.hostenv.println=function(_bf){
+if(!dojo.hostenv._println_safe){
+dojo.hostenv._println_buffer.push(_bf);
+}else{
+try{
+var _c0=document.getElementById(djConfig.debugContainerId?djConfig.debugContainerId:dojo.hostenv.defaultDebugContainerId);
+if(!_c0){
+_c0=dojo.body();
+}
+var div=document.createElement("div");
+div.appendChild(document.createTextNode(_bf));
+_c0.appendChild(div);
+}
+catch(e){
+try{
+document.write("<div>"+_bf+"</div>");
+}
+catch(e2){
+window.status=_bf;
+}
+}
+}
+};
+dojo.addOnLoad(function(){
+dojo.hostenv._println_safe=true;
+while(dojo.hostenv._println_buffer.length>0){
+dojo.hostenv.println(dojo.hostenv._println_buffer.shift());
+}
+});
+function dj_addNodeEvtHdlr(_c2,_c3,fp,_c5){
+var _c6=_c2["on"+_c3]||function(){
+};
+_c2["on"+_c3]=function(){
+fp.apply(_c2,arguments);
+_c6.apply(_c2,arguments);
+};
+return true;
+}
+function dj_load_init(e){
+var _c8=(e&&e.type)?e.type.toLowerCase():"load";
+if(arguments.callee.initialized||(_c8!="domcontentloaded"&&_c8!="load")){
+return;
+}
+arguments.callee.initialized=true;
+if(typeof (_timer)!="undefined"){
+clearInterval(_timer);
+delete _timer;
+}
+var _c9=function(){
+if(dojo.render.html.ie){
+dojo.hostenv.makeWidgets();
+}
+};
+if(dojo.hostenv.inFlightCount==0){
+_c9();
+dojo.hostenv.modulesLoaded();
+}else{
+dojo.addOnLoad(_c9);
+}
+}
+if(document.addEventListener){
+if(dojo.render.html.opera||(dojo.render.html.moz&&!djConfig.delayMozLoadingFix)){
+document.addEventListener("DOMContentLoaded",dj_load_init,null);
+}
+window.addEventListener("load",dj_load_init,null);
+}
+if(dojo.render.html.ie&&dojo.render.os.win){
+document.attachEvent("onreadystatechange",function(e){
+if(document.readyState=="complete"){
+dj_load_init();
+}
+});
+}
+if(/(WebKit|khtml)/i.test(navigator.userAgent)){
+var _timer=setInterval(function(){
+if(/loaded|complete/.test(document.readyState)){
+dj_load_init();
+}
+},10);
+}
+if(dojo.render.html.ie){
+dj_addNodeEvtHdlr(window,"beforeunload",function(){
+dojo.hostenv._unloading=true;
+window.setTimeout(function(){
+dojo.hostenv._unloading=false;
+},0);
+});
+}
+dj_addNodeEvtHdlr(window,"unload",function(){
+dojo.hostenv.unloaded();
+if((!dojo.render.html.ie)||(dojo.render.html.ie&&dojo.hostenv._unloading)){
+dojo.hostenv.unloaded();
+}
+});
+dojo.hostenv.makeWidgets=function(){
+var _cb=[];
+if(djConfig.searchIds&&djConfig.searchIds.length>0){
+_cb=_cb.concat(djConfig.searchIds);
+}
+if(dojo.hostenv.searchIds&&dojo.hostenv.searchIds.length>0){
+_cb=_cb.concat(dojo.hostenv.searchIds);
+}
+if((djConfig.parseWidgets)||(_cb.length>0)){
+if(dojo.evalObjPath("dojo.widget.Parse")){
+var _cc=new dojo.xml.Parse();
+if(_cb.length>0){
+for(var x=0;x<_cb.length;x++){
+var _ce=document.getElementById(_cb[x]);
+if(!_ce){
+continue;
+}
+var _cf=_cc.parseElement(_ce,null,true);
+dojo.widget.getParser().createComponents(_cf);
+}
+}else{
+if(djConfig.parseWidgets){
+var _cf=_cc.parseElement(dojo.body(),null,true);
+dojo.widget.getParser().createComponents(_cf);
+}
+}
+}
+}
+};
+dojo.addOnLoad(function(){
+if(!dojo.render.html.ie){
+dojo.hostenv.makeWidgets();
+}
+});
+try{
+if(dojo.render.html.ie){
+document.namespaces.add("v","urn:schemas-microsoft-com:vml");
+document.createStyleSheet().addRule("v\\:*","behavior:url(#default#VML)");
+}
+}
+catch(e){
+}
+dojo.hostenv.writeIncludes=function(){
+};
+if(!dj_undef("document",this)){
+dj_currentDocument=this.document;
+}
+dojo.doc=function(){
+return dj_currentDocument;
+};
+dojo.body=function(){
+return dojo.doc().body||dojo.doc().getElementsByTagName("body")[0];
+};
+dojo.byId=function(id,doc){
+if((id)&&((typeof id=="string")||(id instanceof String))){
+if(!doc){
+doc=dj_currentDocument;
+}
+var ele=doc.getElementById(id);
+if(ele&&(ele.id!=id)&&doc.all){
+ele=null;
+eles=doc.all[id];
+if(eles){
+if(eles.length){
+for(var i=0;i<eles.length;i++){
+if(eles[i].id==id){
+ele=eles[i];
+break;
+}
+}
+}else{
+ele=eles;
+}
+}
+}
+return ele;
+}
+return id;
+};
+dojo.setContext=function(_d4,_d5){
+dj_currentContext=_d4;
+dj_currentDocument=_d5;
+};
+dojo._fireCallback=function(_d6,_d7,_d8){
+if((_d7)&&((typeof _d6=="string")||(_d6 instanceof String))){
+_d6=_d7[_d6];
+}
+return (_d7?_d6.apply(_d7,_d8||[]):_d6());
+};
+dojo.withGlobal=function(_d9,_da,_db,_dc){
+var _dd;
+var _de=dj_currentContext;
+var _df=dj_currentDocument;
+try{
+dojo.setContext(_d9,_d9.document);
+_dd=dojo._fireCallback(_da,_db,_dc);
+}
+finally{
+dojo.setContext(_de,_df);
+}
+return _dd;
+};
+dojo.withDoc=function(_e0,_e1,_e2,_e3){
+var _e4;
+var _e5=dj_currentDocument;
+try{
+dj_currentDocument=_e0;
+_e4=dojo._fireCallback(_e1,_e2,_e3);
+}
+finally{
+dj_currentDocument=_e5;
+}
+return _e4;
+};
+}
+(function(){
+if(typeof dj_usingBootstrap!="undefined"){
+return;
+}
+var _e6=false;
+var _e7=false;
+var _e8=false;
+if((typeof this["load"]=="function")&&((typeof this["Packages"]=="function")||(typeof this["Packages"]=="object"))){
+_e6=true;
+}else{
+if(typeof this["load"]=="function"){
+_e7=true;
+}else{
+if(window.widget){
+_e8=true;
+}
+}
+}
+var _e9=[];
+if((this["djConfig"])&&((djConfig["isDebug"])||(djConfig["debugAtAllCosts"]))){
+_e9.push("debug.js");
+}
+if((this["djConfig"])&&(djConfig["debugAtAllCosts"])&&(!_e6)&&(!_e8)){
+_e9.push("browser_debug.js");
+}
+var _ea=djConfig["baseScriptUri"];
+if((this["djConfig"])&&(djConfig["baseLoaderUri"])){
+_ea=djConfig["baseLoaderUri"];
+}
+for(var x=0;x<_e9.length;x++){
+var _ec=_ea+"src/"+_e9[x];
+if(_e6||_e7){
+load(_ec);
+}else{
+try{
+document.write("<scr"+"ipt type='text/javascript' src='"+_ec+"'></scr"+"ipt>");
+}
+catch(e){
+var _ed=document.createElement("script");
+_ed.src=_ec;
+document.getElementsByTagName("head")[0].appendChild(_ed);
+}
+}
+}
+})();
+dojo.provide("dojo.string.common");
+dojo.string.trim=function(str,wh){
+if(!str.replace){
+return str;
+}
+if(!str.length){
+return str;
+}
+var re=(wh>0)?(/^\s+/):(wh<0)?(/\s+$/):(/^\s+|\s+$/g);
+return str.replace(re,"");
+};
+dojo.string.trimStart=function(str){
+return dojo.string.trim(str,1);
+};
+dojo.string.trimEnd=function(str){
+return dojo.string.trim(str,-1);
+};
+dojo.string.repeat=function(str,_f4,_f5){
+var out="";
+for(var i=0;i<_f4;i++){
+out+=str;
+if(_f5&&i<_f4-1){
+out+=_f5;
+}
+}
+return out;
+};
+dojo.string.pad=function(str,len,c,dir){
+var out=String(str);
+if(!c){
+c="0";
+}
+if(!dir){
+dir=1;
+}
+while(out.length<len){
+if(dir>0){
+out=c+out;
+}else{
+out+=c;
+}
+}
+return out;
+};
+dojo.string.padLeft=function(str,len,c){
+return dojo.string.pad(str,len,c,1);
+};
+dojo.string.padRight=function(str,len,c){
+return dojo.string.pad(str,len,c,-1);
+};
+dojo.provide("dojo.string");
+dojo.provide("dojo.lang.common");
+dojo.lang.inherits=function(_103,_104){
+if(typeof _104!="function"){
+dojo.raise("dojo.inherits: superclass argument ["+_104+"] must be a function (subclass: ["+_103+"']");
+}
+_103.prototype=new _104();
+_103.prototype.constructor=_103;
+_103.superclass=_104.prototype;
+_103["super"]=_104.prototype;
+};
+dojo.lang._mixin=function(obj,_106){
+var tobj={};
+for(var x in _106){
+if((typeof tobj[x]=="undefined")||(tobj[x]!=_106[x])){
+obj[x]=_106[x];
+}
+}
+if(dojo.render.html.ie&&(typeof (_106["toString"])=="function")&&(_106["toString"]!=obj["toString"])&&(_106["toString"]!=tobj["toString"])){
+obj.toString=_106.toString;
+}
+return obj;
+};
+dojo.lang.mixin=function(obj,_10a){
+for(var i=1,l=arguments.length;i<l;i++){
+dojo.lang._mixin(obj,arguments[i]);
+}
+return obj;
+};
+dojo.lang.extend=function(_10d,_10e){
+for(var i=1,l=arguments.length;i<l;i++){
+dojo.lang._mixin(_10d.prototype,arguments[i]);
+}
+return _10d;
+};
+dojo.inherits=dojo.lang.inherits;
+dojo.mixin=dojo.lang.mixin;
+dojo.extend=dojo.lang.extend;
+dojo.lang.find=function(_111,_112,_113,_114){
+if(!dojo.lang.isArrayLike(_111)&&dojo.lang.isArrayLike(_112)){
+dojo.deprecated("dojo.lang.find(value, array)","use dojo.lang.find(array, value) instead","0.5");
+var temp=_111;
+_111=_112;
+_112=temp;
+}
+var _116=dojo.lang.isString(_111);
+if(_116){
+_111=_111.split("");
+}
+if(_114){
+var step=-1;
+var i=_111.length-1;
+var end=-1;
+}else{
+var step=1;
+var i=0;
+var end=_111.length;
+}
+if(_113){
+while(i!=end){
+if(_111[i]===_112){
+return i;
+}
+i+=step;
+}
+}else{
+while(i!=end){
+if(_111[i]==_112){
+return i;
+}
+i+=step;
+}
+}
+return -1;
+};
+dojo.lang.indexOf=dojo.lang.find;
+dojo.lang.findLast=function(_11a,_11b,_11c){
+return dojo.lang.find(_11a,_11b,_11c,true);
+};
+dojo.lang.lastIndexOf=dojo.lang.findLast;
+dojo.lang.inArray=function(_11d,_11e){
+return dojo.lang.find(_11d,_11e)>-1;
+};
+dojo.lang.isObject=function(it){
+if(typeof it=="undefined"){
+return false;
+}
+return (typeof it=="object"||it===null||dojo.lang.isArray(it)||dojo.lang.isFunction(it));
+};
+dojo.lang.isArray=function(it){
+return (it&&it instanceof Array||typeof it=="array");
+};
+dojo.lang.isArrayLike=function(it){
+if((!it)||(dojo.lang.isUndefined(it))){
+return false;
+}
+if(dojo.lang.isString(it)){
+return false;
+}
+if(dojo.lang.isFunction(it)){
+return false;
+}
+if(dojo.lang.isArray(it)){
+return true;
+}
+if((it.tagName)&&(it.tagName.toLowerCase()=="form")){
+return false;
+}
+if(dojo.lang.isNumber(it.length)&&isFinite(it.length)){
+return true;
+}
+return false;
+};
+dojo.lang.isFunction=function(it){
+if(!it){
+return false;
+}
+if((typeof (it)=="function")&&(it=="[object NodeList]")){
+return false;
+}
+return (it instanceof Function||typeof it=="function");
+};
+dojo.lang.isString=function(it){
+return (typeof it=="string"||it instanceof String);
+};
+dojo.lang.isAlien=function(it){
+if(!it){
+return false;
+}
+return !dojo.lang.isFunction()&&/\{\s*\[native code\]\s*\}/.test(String(it));
+};
+dojo.lang.isBoolean=function(it){
+return (it instanceof Boolean||typeof it=="boolean");
+};
+dojo.lang.isNumber=function(it){
+return (it instanceof Number||typeof it=="number");
+};
+dojo.lang.isUndefined=function(it){
+return ((typeof (it)=="undefined")&&(it==undefined));
+};
+dojo.provide("dojo.lang.extras");
+dojo.lang.setTimeout=function(func,_129){
+var _12a=window,_12b=2;
+if(!dojo.lang.isFunction(func)){
+_12a=func;
+func=_129;
+_129=arguments[2];
+_12b++;
+}
+if(dojo.lang.isString(func)){
+func=_12a[func];
+}
+var args=[];
+for(var i=_12b;i<arguments.length;i++){
+args.push(arguments[i]);
+}
+return dojo.global().setTimeout(function(){
+func.apply(_12a,args);
+},_129);
+};
+dojo.lang.clearTimeout=function(_12e){
+dojo.global().clearTimeout(_12e);
+};
+dojo.lang.getNameInObj=function(ns,item){
+if(!ns){
+ns=dj_global;
+}
+for(var x in ns){
+if(ns[x]===item){
+return new String(x);
+}
+}
+return null;
+};
+dojo.lang.shallowCopy=function(obj,deep){
+var i,ret;
+if(obj===null){
+return null;
+}
+if(dojo.lang.isObject(obj)){
+ret=new obj.constructor();
+for(i in obj){
+if(dojo.lang.isUndefined(ret[i])){
+ret[i]=deep?dojo.lang.shallowCopy(obj[i],deep):obj[i];
+}
+}
+}else{
+if(dojo.lang.isArray(obj)){
+ret=[];
+for(i=0;i<obj.length;i++){
+ret[i]=deep?dojo.lang.shallowCopy(obj[i],deep):obj[i];
+}
+}else{
+ret=obj;
+}
+}
+return ret;
+};
+dojo.lang.firstValued=function(){
+for(var i=0;i<arguments.length;i++){
+if(typeof arguments[i]!="undefined"){
+return arguments[i];
+}
+}
+return undefined;
+};
+dojo.lang.getObjPathValue=function(_137,_138,_139){
+with(dojo.parseObjPath(_137,_138,_139)){
+return dojo.evalProp(prop,obj,_139);
+}
+};
+dojo.lang.setObjPathValue=function(_13a,_13b,_13c,_13d){
+if(arguments.length<4){
+_13d=true;
+}
+with(dojo.parseObjPath(_13a,_13c,_13d)){
+if(obj&&(_13d||(prop in obj))){
+obj[prop]=_13b;
+}
+}
+};
+dojo.provide("dojo.io.common");
+dojo.io.transports=[];
+dojo.io.hdlrFuncNames=["load","error","timeout"];
+dojo.io.Request=function(url,_13f,_140,_141){
+if((arguments.length==1)&&(arguments[0].constructor==Object)){
+this.fromKwArgs(arguments[0]);
+}else{
+this.url=url;
+if(_13f){
+this.mimetype=_13f;
+}
+if(_140){
+this.transport=_140;
+}
+if(arguments.length>=4){
+this.changeUrl=_141;
+}
+}
+};
+dojo.lang.extend(dojo.io.Request,{url:"",mimetype:"text/plain",method:"GET",content:undefined,transport:undefined,changeUrl:undefined,formNode:undefined,sync:false,bindSuccess:false,useCache:false,preventCache:false,load:function(type,data,_144,_145){
+},error:function(type,_147,_148,_149){
+},timeout:function(type,_14b,_14c,_14d){
+},handle:function(type,data,_150,_151){
+},timeoutSeconds:0,abort:function(){
+},fromKwArgs:function(_152){
+if(_152["url"]){
+_152.url=_152.url.toString();
+}
+if(_152["formNode"]){
+_152.formNode=dojo.byId(_152.formNode);
+}
+if(!_152["method"]&&_152["formNode"]&&_152["formNode"].method){
+_152.method=_152["formNode"].method;
+}
+if(!_152["handle"]&&_152["handler"]){
+_152.handle=_152.handler;
+}
+if(!_152["load"]&&_152["loaded"]){
+_152.load=_152.loaded;
+}
+if(!_152["changeUrl"]&&_152["changeURL"]){
+_152.changeUrl=_152.changeURL;
+}
+_152.encoding=dojo.lang.firstValued(_152["encoding"],djConfig["bindEncoding"],"");
+_152.sendTransport=dojo.lang.firstValued(_152["sendTransport"],djConfig["ioSendTransport"],false);
+var _153=dojo.lang.isFunction;
+for(var x=0;x<dojo.io.hdlrFuncNames.length;x++){
+var fn=dojo.io.hdlrFuncNames[x];
+if(_152[fn]&&_153(_152[fn])){
+continue;
+}
+if(_152["handle"]&&_153(_152["handle"])){
+_152[fn]=_152.handle;
+}
+}
+dojo.lang.mixin(this,_152);
+}});
+dojo.io.Error=function(msg,type,num){
+this.message=msg;
+this.type=type||"unknown";
+this.number=num||0;
+};
+dojo.io.transports.addTransport=function(name){
+this.push(name);
+this[name]=dojo.io[name];
+};
+dojo.io.bind=function(_15a){
+if(!(_15a instanceof dojo.io.Request)){
+try{
+_15a=new dojo.io.Request(_15a);
+}
+catch(e){
+dojo.debug(e);
+}
+}
+var _15b="";
+if(_15a["transport"]){
+_15b=_15a["transport"];
+if(!this[_15b]){
+dojo.io.sendBindError(_15a,"No dojo.io.bind() transport with name '"+_15a["transport"]+"'.");
+return _15a;
+}
+if(!this[_15b].canHandle(_15a)){
+dojo.io.sendBindError(_15a,"dojo.io.bind() transport with name '"+_15a["transport"]+"' cannot handle this type of request.");
+return _15a;
+}
+}else{
+for(var x=0;x<dojo.io.transports.length;x++){
+var tmp=dojo.io.transports[x];
+if((this[tmp])&&(this[tmp].canHandle(_15a))){
+_15b=tmp;
+break;
+}
+}
+if(_15b==""){
+dojo.io.sendBindError(_15a,"None of the loaded transports for dojo.io.bind()"+" can handle the request.");
+return _15a;
+}
+}
+this[_15b].bind(_15a);
+_15a.bindSuccess=true;
+return _15a;
+};
+dojo.io.sendBindError=function(_15e,_15f){
+if((typeof _15e.error=="function"||typeof _15e.handle=="function")&&(typeof setTimeout=="function"||typeof setTimeout=="object")){
+var _160=new dojo.io.Error(_15f);
+setTimeout(function(){
+_15e[(typeof _15e.error=="function")?"error":"handle"]("error",_160,null,_15e);
+},50);
+}else{
+dojo.raise(_15f);
+}
+};
+dojo.io.queueBind=function(_161){
+if(!(_161 instanceof dojo.io.Request)){
+try{
+_161=new dojo.io.Request(_161);
+}
+catch(e){
+dojo.debug(e);
+}
+}
+var _162=_161.load;
+_161.load=function(){
+dojo.io._queueBindInFlight=false;
+var ret=_162.apply(this,arguments);
+dojo.io._dispatchNextQueueBind();
+return ret;
+};
+var _164=_161.error;
+_161.error=function(){
+dojo.io._queueBindInFlight=false;
+var ret=_164.apply(this,arguments);
+dojo.io._dispatchNextQueueBind();
+return ret;
+};
+dojo.io._bindQueue.push(_161);
+dojo.io._dispatchNextQueueBind();
+return _161;
+};
+dojo.io._dispatchNextQueueBind=function(){
+if(!dojo.io._queueBindInFlight){
+dojo.io._queueBindInFlight=true;
+if(dojo.io._bindQueue.length>0){
+dojo.io.bind(dojo.io._bindQueue.shift());
+}else{
+dojo.io._queueBindInFlight=false;
+}
+}
+};
+dojo.io._bindQueue=[];
+dojo.io._queueBindInFlight=false;
+dojo.io.argsFromMap=function(map,_167,last){
+var enc=/utf/i.test(_167||"")?encodeURIComponent:dojo.string.encodeAscii;
+var _16a=[];
+var _16b=new Object();
+for(var name in map){
+var _16d=function(elt){
+var val=enc(name)+"="+enc(elt);
+_16a[(last==name)?"push":"unshift"](val);
+};
+if(!_16b[name]){
+var _170=map[name];
+if(dojo.lang.isArray(_170)){
+dojo.lang.forEach(_170,_16d);
+}else{
+_16d(_170);
+}
+}
+}
+return _16a.join("&");
+};
+dojo.io.setIFrameSrc=function(_171,src,_173){
+try{
+var r=dojo.render.html;
+if(!_173){
+if(r.safari){
+_171.location=src;
+}else{
+frames[_171.name].location=src;
+}
+}else{
+var idoc;
+if(r.ie){
+idoc=_171.contentWindow.document;
+}else{
+if(r.safari){
+idoc=_171.document;
+}else{
+idoc=_171.contentWindow;
+}
+}
+if(!idoc){
+_171.location=src;
+return;
+}else{
+idoc.location.replace(src);
+}
+}
+}
+catch(e){
+dojo.debug(e);
+dojo.debug("setIFrameSrc: "+e);
+}
+};
+dojo.provide("dojo.lang.array");
+dojo.lang.has=function(obj,name){
+try{
+return typeof obj[name]!="undefined";
+}
+catch(e){
+return false;
+}
+};
+dojo.lang.isEmpty=function(obj){
+if(dojo.lang.isObject(obj)){
+var tmp={};
+var _17a=0;
+for(var x in obj){
+if(obj[x]&&(!tmp[x])){
+_17a++;
+break;
+}
+}
+return _17a==0;
+}else{
+if(dojo.lang.isArrayLike(obj)||dojo.lang.isString(obj)){
+return obj.length==0;
+}
+}
+};
+dojo.lang.map=function(arr,obj,_17e){
+var _17f=dojo.lang.isString(arr);
+if(_17f){
+arr=arr.split("");
+}
+if(dojo.lang.isFunction(obj)&&(!_17e)){
+_17e=obj;
+obj=dj_global;
+}else{
+if(dojo.lang.isFunction(obj)&&_17e){
+var _180=obj;
+obj=_17e;
+_17e=_180;
+}
+}
+if(Array.map){
+var _181=Array.map(arr,_17e,obj);
+}else{
+var _181=[];
+for(var i=0;i<arr.length;++i){
+_181.push(_17e.call(obj,arr[i]));
+}
+}
+if(_17f){
+return _181.join("");
+}else{
+return _181;
+}
+};
+dojo.lang.reduce=function(arr,_184,obj,_186){
+var _187=_184;
+var ob=obj?obj:dj_global;
+dojo.lang.map(arr,function(val){
+_187=_186.call(ob,_187,val);
+});
+return _187;
+};
+dojo.lang.forEach=function(_18a,_18b,_18c){
+if(dojo.lang.isString(_18a)){
+_18a=_18a.split("");
+}
+if(Array.forEach){
+Array.forEach(_18a,_18b,_18c);
+}else{
+if(!_18c){
+_18c=dj_global;
+}
+for(var i=0,l=_18a.length;i<l;i++){
+_18b.call(_18c,_18a[i],i,_18a);
+}
+}
+};
+dojo.lang._everyOrSome=function(_18f,arr,_191,_192){
+if(dojo.lang.isString(arr)){
+arr=arr.split("");
+}
+if(Array.every){
+return Array[_18f?"every":"some"](arr,_191,_192);
+}else{
+if(!_192){
+_192=dj_global;
+}
+for(var i=0,l=arr.length;i<l;i++){
+var _195=_191.call(_192,arr[i],i,arr);
+if(_18f&&!_195){
+return false;
+}else{
+if((!_18f)&&(_195)){
+return true;
+}
+}
+}
+return Boolean(_18f);
+}
+};
+dojo.lang.every=function(arr,_197,_198){
+return this._everyOrSome(true,arr,_197,_198);
+};
+dojo.lang.some=function(arr,_19a,_19b){
+return this._everyOrSome(false,arr,_19a,_19b);
+};
+dojo.lang.filter=function(arr,_19d,_19e){
+var _19f=dojo.lang.isString(arr);
+if(_19f){
+arr=arr.split("");
+}
+var _1a0;
+if(Array.filter){
+_1a0=Array.filter(arr,_19d,_19e);
+}else{
+if(!_19e){
+if(arguments.length>=3){
+dojo.raise("thisObject doesn't exist!");
+}
+_19e=dj_global;
+}
+_1a0=[];
+for(var i=0;i<arr.length;i++){
+if(_19d.call(_19e,arr[i],i,arr)){
+_1a0.push(arr[i]);
+}
+}
+}
+if(_19f){
+return _1a0.join("");
+}else{
+return _1a0;
+}
+};
+dojo.lang.unnest=function(){
+var out=[];
+for(var i=0;i<arguments.length;i++){
+if(dojo.lang.isArrayLike(arguments[i])){
+var add=dojo.lang.unnest.apply(this,arguments[i]);
+out=out.concat(add);
+}else{
+out.push(arguments[i]);
+}
+}
+return out;
+};
+dojo.lang.toArray=function(_1a5,_1a6){
+var _1a7=[];
+for(var i=_1a6||0;i<_1a5.length;i++){
+_1a7.push(_1a5[i]);
+}
+return _1a7;
+};
+dojo.provide("dojo.lang.func");
+dojo.lang.hitch=function(_1a9,_1aa){
+var fcn=(dojo.lang.isString(_1aa)?_1a9[_1aa]:_1aa)||function(){
+};
+return function(){
+return fcn.apply(_1a9,arguments);
+};
+};
+dojo.lang.anonCtr=0;
+dojo.lang.anon={};
+dojo.lang.nameAnonFunc=function(_1ac,_1ad,_1ae){
+var nso=(_1ad||dojo.lang.anon);
+if((_1ae)||((dj_global["djConfig"])&&(djConfig["slowAnonFuncLookups"]==true))){
+for(var x in nso){
+try{
+if(nso[x]===_1ac){
+return x;
+}
+}
+catch(e){
+}
+}
+}
+var ret="__"+dojo.lang.anonCtr++;
+while(typeof nso[ret]!="undefined"){
+ret="__"+dojo.lang.anonCtr++;
+}
+nso[ret]=_1ac;
+return ret;
+};
+dojo.lang.forward=function(_1b2){
+return function(){
+return this[_1b2].apply(this,arguments);
+};
+};
+dojo.lang.curry=function(ns,func){
+var _1b5=[];
+ns=ns||dj_global;
+if(dojo.lang.isString(func)){
+func=ns[func];
+}
+for(var x=2;x<arguments.length;x++){
+_1b5.push(arguments[x]);
+}
+var _1b7=(func["__preJoinArity"]||func.length)-_1b5.length;
+function gather(_1b8,_1b9,_1ba){
+var _1bb=_1ba;
+var _1bc=_1b9.slice(0);
+for(var x=0;x<_1b8.length;x++){
+_1bc.push(_1b8[x]);
+}
+_1ba=_1ba-_1b8.length;
+if(_1ba<=0){
+var res=func.apply(ns,_1bc);
+_1ba=_1bb;
+return res;
+}else{
+return function(){
+return gather(arguments,_1bc,_1ba);
+};
+}
+}
+return gather([],_1b5,_1b7);
+};
+dojo.lang.curryArguments=function(ns,func,args,_1c2){
+var _1c3=[];
+var x=_1c2||0;
+for(x=_1c2;x<args.length;x++){
+_1c3.push(args[x]);
+}
+return dojo.lang.curry.apply(dojo.lang,[ns,func].concat(_1c3));
+};
+dojo.lang.tryThese=function(){
+for(var x=0;x<arguments.length;x++){
+try{
+if(typeof arguments[x]=="function"){
+var ret=(arguments[x]());
+if(ret){
+return ret;
+}
+}
+}
+catch(e){
+dojo.debug(e);
+}
+}
+};
+dojo.lang.delayThese=function(farr,cb,_1c9,_1ca){
+if(!farr.length){
+if(typeof _1ca=="function"){
+_1ca();
+}
+return;
+}
+if((typeof _1c9=="undefined")&&(typeof cb=="number")){
+_1c9=cb;
+cb=function(){
+};
+}else{
+if(!cb){
+cb=function(){
+};
+if(!_1c9){
+_1c9=0;
+}
+}
+}
+setTimeout(function(){
+(farr.shift())();
+cb();
+dojo.lang.delayThese(farr,cb,_1c9,_1ca);
+},_1c9);
+};
+dojo.provide("dojo.string.extras");
+dojo.string.substituteParams=function(_1cb,hash){
+var map=(typeof hash=="object")?hash:dojo.lang.toArray(arguments,1);
+return _1cb.replace(/\%\{(\w+)\}/g,function(_1ce,key){
+if(typeof (map[key])!="undefined"&&map[key]!=null){
+return map[key];
+}
+dojo.raise("Substitution not found: "+key);
+});
+};
+dojo.string.capitalize=function(str){
+if(!dojo.lang.isString(str)){
+return "";
+}
+if(arguments.length==0){
+str=this;
+}
+var _1d1=str.split(" ");
+for(var i=0;i<_1d1.length;i++){
+_1d1[i]=_1d1[i].charAt(0).toUpperCase()+_1d1[i].substring(1);
+}
+return _1d1.join(" ");
+};
+dojo.string.isBlank=function(str){
+if(!dojo.lang.isString(str)){
+return true;
+}
+return (dojo.string.trim(str).length==0);
+};
+dojo.string.encodeAscii=function(str){
+if(!dojo.lang.isString(str)){
+return str;
+}
+var ret="";
+var _1d6=escape(str);
+var _1d7,re=/%u([0-9A-F]{4})/i;
+while((_1d7=_1d6.match(re))){
+var num=Number("0x"+_1d7[1]);
+var _1da=escape("&#"+num+";");
+ret+=_1d6.substring(0,_1d7.index)+_1da;
+_1d6=_1d6.substring(_1d7.index+_1d7[0].length);
+}
+ret+=_1d6.replace(/\+/g,"%2B");
+return ret;
+};
+dojo.string.escape=function(type,str){
+var args=dojo.lang.toArray(arguments,1);
+switch(type.toLowerCase()){
+case "xml":
+case "html":
+case "xhtml":
+return dojo.string.escapeXml.apply(this,args);
+case "sql":
+return dojo.string.escapeSql.apply(this,args);
+case "regexp":
+case "regex":
+return dojo.string.escapeRegExp.apply(this,args);
+case "javascript":
+case "jscript":
+case "js":
+return dojo.string.escapeJavaScript.apply(this,args);
+case "ascii":
+return dojo.string.encodeAscii.apply(this,args);
+default:
+return str;
+}
+};
+dojo.string.escapeXml=function(str,_1df){
+str=str.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;").replace(/"/gm,"&quot;");
+if(!_1df){
+str=str.replace(/'/gm,"&#39;");
+}
+return str;
+};
+dojo.string.escapeSql=function(str){
+return str.replace(/'/gm,"''");
+};
+dojo.string.escapeRegExp=function(str){
+return str.replace(/\\/gm,"\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm,"\\$1");
+};
+dojo.string.escapeJavaScript=function(str){
+return str.replace(/(["'\f\b\n\t\r])/gm,"\\$1");
+};
+dojo.string.escapeString=function(str){
+return ("\""+str.replace(/(["\\])/g,"\\$1")+"\"").replace(/[\f]/g,"\\f").replace(/[\b]/g,"\\b").replace(/[\n]/g,"\\n").replace(/[\t]/g,"\\t").replace(/[\r]/g,"\\r");
+};
+dojo.string.summary=function(str,len){
+if(!len||str.length<=len){
+return str;
+}
+return str.substring(0,len).replace(/\.+$/,"")+"...";
+};
+dojo.string.endsWith=function(str,end,_1e8){
+if(_1e8){
+str=str.toLowerCase();
+end=end.toLowerCase();
+}
+if((str.length-end.length)<0){
+return false;
+}
+return str.lastIndexOf(end)==str.length-end.length;
+};
+dojo.string.endsWithAny=function(str){
+for(var i=1;i<arguments.length;i++){
+if(dojo.string.endsWith(str,arguments[i])){
+return true;
+}
+}
+return false;
+};
+dojo.string.startsWith=function(str,_1ec,_1ed){
+if(_1ed){
+str=str.toLowerCase();
+_1ec=_1ec.toLowerCase();
+}
+return str.indexOf(_1ec)==0;
+};
+dojo.string.startsWithAny=function(str){
+for(var i=1;i<arguments.length;i++){
+if(dojo.string.startsWith(str,arguments[i])){
+return true;
+}
+}
+return false;
+};
+dojo.string.has=function(str){
+for(var i=1;i<arguments.length;i++){
+if(str.indexOf(arguments[i])>-1){
+return true;
+}
+}
+return false;
+};
+dojo.string.normalizeNewlines=function(text,_1f3){
+if(_1f3=="\n"){
+text=text.replace(/\r\n/g,"\n");
+text=text.replace(/\r/g,"\n");
+}else{
+if(_1f3=="\r"){
+text=text.replace(/\r\n/g,"\r");
+text=text.replace(/\n/g,"\r");
+}else{
+text=text.replace(/([^\r])\n/g,"$1\r\n").replace(/\r([^\n])/g,"\r\n$1");
+}
+}
+return text;
+};
+dojo.string.splitEscaped=function(str,_1f5){
+var _1f6=[];
+for(var i=0,_1f8=0;i<str.length;i++){
+if(str.charAt(i)=="\\"){
+i++;
+continue;
+}
+if(str.charAt(i)==_1f5){
+_1f6.push(str.substring(_1f8,i));
+_1f8=i+1;
+}
+}
+_1f6.push(str.substr(_1f8));
+return _1f6;
+};
+dojo.provide("dojo.dom");
+dojo.dom.ELEMENT_NODE=1;
+dojo.dom.ATTRIBUTE_NODE=2;
+dojo.dom.TEXT_NODE=3;
+dojo.dom.CDATA_SECTION_NODE=4;
+dojo.dom.ENTITY_REFERENCE_NODE=5;
+dojo.dom.ENTITY_NODE=6;
+dojo.dom.PROCESSING_INSTRUCTION_NODE=7;
+dojo.dom.COMMENT_NODE=8;
+dojo.dom.DOCUMENT_NODE=9;
+dojo.dom.DOCUMENT_TYPE_NODE=10;
+dojo.dom.DOCUMENT_FRAGMENT_NODE=11;
+dojo.dom.NOTATION_NODE=12;
+dojo.dom.dojoml="http://www.dojotoolkit.org/2004/dojoml";
+dojo.dom.xmlns={svg:"http://www.w3.org/2000/svg",smil:"http://www.w3.org/2001/SMIL20/",mml:"http://www.w3.org/1998/Math/MathML",cml:"http://www.xml-cml.org",xlink:"http://www.w3.org/1999/xlink",xhtml:"http://www.w3.org/1999/xhtml",xul:"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",xbl:"http://www.mozilla.org/xbl",fo:"http://www.w3.org/1999/XSL/Format",xsl:"http://www.w3.org/1999/XSL/Transform",xslt:"http://www.w3.org/1999/XSL/Transform",xi:"http://www.w3.org/2001/XInclude",xforms:"http://www.w3.org/2002/01/xforms",saxon:"http://icl.com/saxon",xalan:"http://xml.apache.org/xslt",xsd:"http://www.w3.org/2001/XMLSchema",dt:"http://www.w3.org/2001/XMLSchema-datatypes",xsi:"http://www.w3.org/2001/XMLSchema-instance",rdf:"http://www.w3.org/1999/02/22-rdf-syntax-ns#",rdfs:"http://www.w3.org/2000/01/rdf-schema#",dc:"http://purl.org/dc/elements/1.1/",dcq:"http://purl.org/dc/qualifiers/1.0","soap-env":"http://schemas.xmlsoap.org/soap/envelope/",wsdl:"http://schemas.xmlsoap.org/wsdl/",AdobeExtensions:"http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"};
+dojo.dom.isNode=function(wh){
+if(typeof Element=="function"){
+try{
+return wh instanceof Element;
+}
+catch(E){
+}
+}else{
+return wh&&!isNaN(wh.nodeType);
+}
+};
+dojo.dom.getUniqueId=function(){
+var _1fa=dojo.doc();
+do{
+var id="dj_unique_"+(++arguments.callee._idIncrement);
+}while(_1fa.getElementById(id));
+return id;
+};
+dojo.dom.getUniqueId._idIncrement=0;
+dojo.dom.firstElement=dojo.dom.getFirstChildElement=function(_1fc,_1fd){
+var node=_1fc.firstChild;
+while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE){
+node=node.nextSibling;
+}
+if(_1fd&&node&&node.tagName&&node.tagName.toLowerCase()!=_1fd.toLowerCase()){
+node=dojo.dom.nextElement(node,_1fd);
+}
+return node;
+};
+dojo.dom.lastElement=dojo.dom.getLastChildElement=function(_1ff,_200){
+var node=_1ff.lastChild;
+while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE){
+node=node.previousSibling;
+}
+if(_200&&node&&node.tagName&&node.tagName.toLowerCase()!=_200.toLowerCase()){
+node=dojo.dom.prevElement(node,_200);
+}
+return node;
+};
+dojo.dom.nextElement=dojo.dom.getNextSiblingElement=function(node,_203){
+if(!node){
+return null;
+}
+do{
+node=node.nextSibling;
+}while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE);
+if(node&&_203&&_203.toLowerCase()!=node.tagName.toLowerCase()){
+return dojo.dom.nextElement(node,_203);
+}
+return node;
+};
+dojo.dom.prevElement=dojo.dom.getPreviousSiblingElement=function(node,_205){
+if(!node){
+return null;
+}
+if(_205){
+_205=_205.toLowerCase();
+}
+do{
+node=node.previousSibling;
+}while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE);
+if(node&&_205&&_205.toLowerCase()!=node.tagName.toLowerCase()){
+return dojo.dom.prevElement(node,_205);
+}
+return node;
+};
+dojo.dom.moveChildren=function(_206,_207,trim){
+var _209=0;
+if(trim){
+while(_206.hasChildNodes()&&_206.firstChild.nodeType==dojo.dom.TEXT_NODE){
+_206.removeChild(_206.firstChild);
+}
+while(_206.hasChildNodes()&&_206.lastChild.nodeType==dojo.dom.TEXT_NODE){
+_206.removeChild(_206.lastChild);
+}
+}
+while(_206.hasChildNodes()){
+_207.appendChild(_206.firstChild);
+_209++;
+}
+return _209;
+};
+dojo.dom.copyChildren=function(_20a,_20b,trim){
+var _20d=_20a.cloneNode(true);
+return this.moveChildren(_20d,_20b,trim);
+};
+dojo.dom.removeChildren=function(node){
+var _20f=node.childNodes.length;
+while(node.hasChildNodes()){
+node.removeChild(node.firstChild);
+}
+return _20f;
+};
+dojo.dom.replaceChildren=function(node,_211){
+dojo.dom.removeChildren(node);
+node.appendChild(_211);
+};
+dojo.dom.removeNode=function(node){
+if(node&&node.parentNode){
+return node.parentNode.removeChild(node);
+}
+};
+dojo.dom.getAncestors=function(node,_214,_215){
+var _216=[];
+var _217=(_214&&(_214 instanceof Function||typeof _214=="function"));
+while(node){
+if(!_217||_214(node)){
+_216.push(node);
+}
+if(_215&&_216.length>0){
+return _216[0];
+}
+node=node.parentNode;
+}
+if(_215){
+return null;
+}
+return _216;
+};
+dojo.dom.getAncestorsByTag=function(node,tag,_21a){
+tag=tag.toLowerCase();
+return dojo.dom.getAncestors(node,function(el){
+return ((el.tagName)&&(el.tagName.toLowerCase()==tag));
+},_21a);
+};
+dojo.dom.getFirstAncestorByTag=function(node,tag){
+return dojo.dom.getAncestorsByTag(node,tag,true);
+};
+dojo.dom.isDescendantOf=function(node,_21f,_220){
+if(_220&&node){
+node=node.parentNode;
+}
+while(node){
+if(node==_21f){
+return true;
+}
+node=node.parentNode;
+}
+return false;
+};
+dojo.dom.innerXML=function(node){
+if(node.innerXML){
+return node.innerXML;
+}else{
+if(node.xml){
+return node.xml;
+}else{
+if(typeof XMLSerializer!="undefined"){
+return (new XMLSerializer()).serializeToString(node);
+}
+}
+}
+};
+dojo.dom.createDocument=function(){
+var doc=null;
+var _223=dojo.doc();
+if(!dj_undef("ActiveXObject")){
+var _224=["MSXML2","Microsoft","MSXML","MSXML3"];
+for(var i=0;i<_224.length;i++){
+try{
+doc=new ActiveXObject(_224[i]+".XMLDOM");
+}
+catch(e){
+}
+if(doc){
+break;
+}
+}
+}else{
+if((_223.implementation)&&(_223.implementation.createDocument)){
+doc=_223.implementation.createDocument("","",null);
+}
+}
+return doc;
+};
+dojo.dom.createDocumentFromText=function(str,_227){
+if(!_227){
+_227="text/xml";
+}
+if(!dj_undef("DOMParser")){
+var _228=new DOMParser();
+return _228.parseFromString(str,_227);
+}else{
+if(!dj_undef("ActiveXObject")){
+var _229=dojo.dom.createDocument();
+if(_229){
+_229.async=false;
+_229.loadXML(str);
+return _229;
+}else{
+dojo.debug("toXml didn't work?");
+}
+}else{
+var _22a=dojo.doc();
+if(_22a.createElement){
+var tmp=_22a.createElement("xml");
+tmp.innerHTML=str;
+if(_22a.implementation&&_22a.implementation.createDocument){
+var _22c=_22a.implementation.createDocument("foo","",null);
+for(var i=0;i<tmp.childNodes.length;i++){
+_22c.importNode(tmp.childNodes.item(i),true);
+}
+return _22c;
+}
+return ((tmp.document)&&(tmp.document.firstChild?tmp.document.firstChild:tmp));
+}
+}
+}
+return null;
+};
+dojo.dom.prependChild=function(node,_22f){
+if(_22f.firstChild){
+_22f.insertBefore(node,_22f.firstChild);
+}else{
+_22f.appendChild(node);
+}
+return true;
+};
+dojo.dom.insertBefore=function(node,ref,_232){
+if(_232!=true&&(node===ref||node.nextSibling===ref)){
+return false;
+}
+var _233=ref.parentNode;
+_233.insertBefore(node,ref);
+return true;
+};
+dojo.dom.insertAfter=function(node,ref,_236){
+var pn=ref.parentNode;
+if(ref==pn.lastChild){
+if((_236!=true)&&(node===ref)){
+return false;
+}
+pn.appendChild(node);
+}else{
+return this.insertBefore(node,ref.nextSibling,_236);
+}
+return true;
+};
+dojo.dom.insertAtPosition=function(node,ref,_23a){
+if((!node)||(!ref)||(!_23a)){
+return false;
+}
+switch(_23a.toLowerCase()){
+case "before":
+return dojo.dom.insertBefore(node,ref);
+case "after":
+return dojo.dom.insertAfter(node,ref);
+case "first":
+if(ref.firstChild){
+return dojo.dom.insertBefore(node,ref.firstChild);
+}else{
+ref.appendChild(node);
+return true;
+}
+break;
+default:
+ref.appendChild(node);
+return true;
+}
+};
+dojo.dom.insertAtIndex=function(node,_23c,_23d){
+var _23e=_23c.childNodes;
+if(!_23e.length){
+_23c.appendChild(node);
+return true;
+}
+var _23f=null;
+for(var i=0;i<_23e.length;i++){
+var _241=_23e.item(i)["getAttribute"]?parseInt(_23e.item(i).getAttribute("dojoinsertionindex")):-1;
+if(_241<_23d){
+_23f=_23e.item(i);
+}
+}
+if(_23f){
+return dojo.dom.insertAfter(node,_23f);
+}else{
+return dojo.dom.insertBefore(node,_23e.item(0));
+}
+};
+dojo.dom.textContent=function(node,text){
+if(arguments.length>1){
+var _244=dojo.doc();
+dojo.dom.replaceChildren(node,_244.createTextNode(text));
+return text;
+}else{
+if(node.textContent!=undefined){
+return node.textContent;
+}
+var _245="";
+if(node==null){
+return _245;
+}
+for(var i=0;i<node.childNodes.length;i++){
+switch(node.childNodes[i].nodeType){
+case 1:
+case 5:
+_245+=dojo.dom.textContent(node.childNodes[i]);
+break;
+case 3:
+case 2:
+case 4:
+_245+=node.childNodes[i].nodeValue;
+break;
+default:
+break;
+}
+}
+return _245;
+}
+};
+dojo.dom.hasParent=function(node){
+return node&&node.parentNode&&dojo.dom.isNode(node.parentNode);
+};
+dojo.dom.isTag=function(node){
+if(node&&node.tagName){
+for(var i=1;i<arguments.length;i++){
+if(node.tagName==String(arguments[i])){
+return String(arguments[i]);
+}
+}
+}
+return "";
+};
+dojo.dom.setAttributeNS=function(elem,_24b,_24c,_24d){
+if(elem==null||((elem==undefined)&&(typeof elem=="undefined"))){
+dojo.raise("No element given to dojo.dom.setAttributeNS");
+}
+if(!((elem.setAttributeNS==undefined)&&(typeof elem.setAttributeNS=="undefined"))){
+elem.setAttributeNS(_24b,_24c,_24d);
+}else{
+var _24e=elem.ownerDocument;
+var _24f=_24e.createNode(2,_24c,_24b);
+_24f.nodeValue=_24d;
+elem.setAttributeNode(_24f);
+}
+};
+dojo.provide("dojo.undo.browser");
+try{
+if((!djConfig["preventBackButtonFix"])&&(!dojo.hostenv.post_load_)){
+document.write("<iframe style='border: 0px; width: 1px; height: 1px; position: absolute; bottom: 0px; right: 0px; visibility: visible;' name='djhistory' id='djhistory' src='"+(dojo.hostenv.getBaseScriptUri()+"iframe_history.html")+"'></iframe>");
+}
+}
+catch(e){
+}
+if(dojo.render.html.opera){
+dojo.debug("Opera is not supported with dojo.undo.browser, so back/forward detection will not work.");
+}
+dojo.undo.browser={initialHref:window.location.href,initialHash:window.location.hash,moveForward:false,historyStack:[],forwardStack:[],historyIframe:null,bookmarkAnchor:null,locationTimer:null,setInitialState:function(args){
+this.initialState=this._createState(this.initialHref,args,this.initialHash);
+},addToHistory:function(args){
+this.forwardStack=[];
+var hash=null;
+var url=null;
+if(!this.historyIframe){
+this.historyIframe=window.frames["djhistory"];
+}
+if(!this.bookmarkAnchor){
+this.bookmarkAnchor=document.createElement("a");
+dojo.body().appendChild(this.bookmarkAnchor);
+this.bookmarkAnchor.style.display="none";
+}
+if(args["changeUrl"]){
+hash="#"+((args["changeUrl"]!==true)?args["changeUrl"]:(new Date()).getTime());
+if(this.historyStack.length==0&&this.initialState.urlHash==hash){
+this.initialState=this._createState(url,args,hash);
+return;
+}else{
+if(this.historyStack.length>0&&this.historyStack[this.historyStack.length-1].urlHash==hash){
+this.historyStack[this.historyStack.length-1]=this._createState(url,args,hash);
+return;
+}
+}
+this.changingUrl=true;
+setTimeout("window.location.href = '"+hash+"'; dojo.undo.browser.changingUrl = false;",1);
+this.bookmarkAnchor.href=hash;
+if(dojo.render.html.ie){
+url=this._loadIframeHistory();
+var _254=args["back"]||args["backButton"]||args["handle"];
+var tcb=function(_256){
+if(window.location.hash!=""){
+setTimeout("window.location.href = '"+hash+"';",1);
+}
+_254.apply(this,[_256]);
+};
+if(args["back"]){
+args.back=tcb;
+}else{
+if(args["backButton"]){
+args.backButton=tcb;
+}else{
+if(args["handle"]){
+args.handle=tcb;
+}
+}
+}
+var _257=args["forward"]||args["forwardButton"]||args["handle"];
+var tfw=function(_259){
+if(window.location.hash!=""){
+window.location.href=hash;
+}
+if(_257){
+_257.apply(this,[_259]);
+}
+};
+if(args["forward"]){
+args.forward=tfw;
+}else{
+if(args["forwardButton"]){
+args.forwardButton=tfw;
+}else{
+if(args["handle"]){
+args.handle=tfw;
+}
+}
+}
+}else{
+if(dojo.render.html.moz){
+if(!this.locationTimer){
+this.locationTimer=setInterval("dojo.undo.browser.checkLocation();",200);
+}
+}
+}
+}else{
+url=this._loadIframeHistory();
+}
+this.historyStack.push(this._createState(url,args,hash));
+},checkLocation:function(){
+if(!this.changingUrl){
+var hsl=this.historyStack.length;
+if((window.location.hash==this.initialHash||window.location.href==this.initialHref)&&(hsl==1)){
+this.handleBackButton();
+return;
+}
+if(this.forwardStack.length>0){
+if(this.forwardStack[this.forwardStack.length-1].urlHash==window.location.hash){
+this.handleForwardButton();
+return;
+}
+}
+if((hsl>=2)&&(this.historyStack[hsl-2])){
+if(this.historyStack[hsl-2].urlHash==window.location.hash){
+this.handleBackButton();
+return;
+}
+}
+}
+},iframeLoaded:function(evt,_25c){
+if(!dojo.render.html.opera){
+var _25d=this._getUrlQuery(_25c.href);
+if(_25d==null){
+if(this.historyStack.length==1){
+this.handleBackButton();
+}
+return;
+}
+if(this.moveForward){
+this.moveForward=false;
+return;
+}
+if(this.historyStack.length>=2&&_25d==this._getUrlQuery(this.historyStack[this.historyStack.length-2].url)){
+this.handleBackButton();
+}else{
+if(this.forwardStack.length>0&&_25d==this._getUrlQuery(this.forwardStack[this.forwardStack.length-1].url)){
+this.handleForwardButton();
+}
+}
+}
+},handleBackButton:function(){
+var _25e=this.historyStack.pop();
+if(!_25e){
+return;
+}
+var last=this.historyStack[this.historyStack.length-1];
+if(!last&&this.historyStack.length==0){
+last=this.initialState;
+}
+if(last){
+if(last.kwArgs["back"]){
+last.kwArgs["back"]();
+}else{
+if(last.kwArgs["backButton"]){
+last.kwArgs["backButton"]();
+}else{
+if(last.kwArgs["handle"]){
+last.kwArgs.handle("back");
+}
+}
+}
+}
+this.forwardStack.push(_25e);
+},handleForwardButton:function(){
+var last=this.forwardStack.pop();
+if(!last){
+return;
+}
+if(last.kwArgs["forward"]){
+last.kwArgs.forward();
+}else{
+if(last.kwArgs["forwardButton"]){
+last.kwArgs.forwardButton();
+}else{
+if(last.kwArgs["handle"]){
+last.kwArgs.handle("forward");
+}
+}
+}
+this.historyStack.push(last);
+},_createState:function(url,args,hash){
+return {"url":url,"kwArgs":args,"urlHash":hash};
+},_getUrlQuery:function(url){
+var _265=url.split("?");
+if(_265.length<2){
+return null;
+}else{
+return _265[1];
+}
+},_loadIframeHistory:function(){
+var url=dojo.hostenv.getBaseScriptUri()+"iframe_history.html?"+(new Date()).getTime();
+this.moveForward=true;
+dojo.io.setIFrameSrc(this.historyIframe,url,false);
+return url;
+}};
+dojo.provide("dojo.io.BrowserIO");
+dojo.io.checkChildrenForFile=function(node){
+var _268=false;
+var _269=node.getElementsByTagName("input");
+dojo.lang.forEach(_269,function(_26a){
+if(_268){
+return;
+}
+if(_26a.getAttribute("type")=="file"){
+_268=true;
+}
+});
+return _268;
+};
+dojo.io.formHasFile=function(_26b){
+return dojo.io.checkChildrenForFile(_26b);
+};
+dojo.io.updateNode=function(node,_26d){
+node=dojo.byId(node);
+var args=_26d;
+if(dojo.lang.isString(_26d)){
+args={url:_26d};
+}
+args.mimetype="text/html";
+args.load=function(t,d,e){
+while(node.firstChild){
+if(dojo["event"]){
+try{
+dojo.event.browser.clean(node.firstChild);
+}
+catch(e){
+}
+}
+node.removeChild(node.firstChild);
+}
+node.innerHTML=d;
+};
+dojo.io.bind(args);
+};
+dojo.io.formFilter=function(node){
+var type=(node.type||"").toLowerCase();
+return !node.disabled&&node.name&&!dojo.lang.inArray(["file","submit","image","reset","button"],type);
+};
+dojo.io.encodeForm=function(_274,_275,_276){
+if((!_274)||(!_274.tagName)||(!_274.tagName.toLowerCase()=="form")){
+dojo.raise("Attempted to encode a non-form element.");
+}
+if(!_276){
+_276=dojo.io.formFilter;
+}
+var enc=/utf/i.test(_275||"")?encodeURIComponent:dojo.string.encodeAscii;
+var _278=[];
+for(var i=0;i<_274.elements.length;i++){
+var elm=_274.elements[i];
+if(!elm||elm.tagName.toLowerCase()=="fieldset"||!_276(elm)){
+continue;
+}
+var name=enc(elm.name);
+var type=elm.type.toLowerCase();
+if(type=="select-multiple"){
+for(var j=0;j<elm.options.length;j++){
+if(elm.options[j].selected){
+_278.push(name+"="+enc(elm.options[j].value));
+}
+}
+}else{
+if(dojo.lang.inArray(["radio","checkbox"],type)){
+if(elm.checked){
+_278.push(name+"="+enc(elm.value));
+}
+}else{
+_278.push(name+"="+enc(elm.value));
+}
+}
+}
+var _27e=_274.getElementsByTagName("input");
+for(var i=0;i<_27e.length;i++){
+var _27f=_27e[i];
+if(_27f.type.toLowerCase()=="image"&&_27f.form==_274&&_276(_27f)){
+var name=enc(_27f.name);
+_278.push(name+"="+enc(_27f.value));
+_278.push(name+".x=0");
+_278.push(name+".y=0");
+}
+}
+return _278.join("&")+"&";
+};
+dojo.io.FormBind=function(args){
+this.bindArgs={};
+if(args&&args.formNode){
+this.init(args);
+}else{
+if(args){
+this.init({formNode:args});
+}
+}
+};
+dojo.lang.extend(dojo.io.FormBind,{form:null,bindArgs:null,clickedButton:null,init:function(args){
+var form=dojo.byId(args.formNode);
+if(!form||!form.tagName||form.tagName.toLowerCase()!="form"){
+throw new Error("FormBind: Couldn't apply, invalid form");
+}else{
+if(this.form==form){
+return;
+}else{
+if(this.form){
+throw new Error("FormBind: Already applied to a form");
+}
+}
+}
+dojo.lang.mixin(this.bindArgs,args);
+this.form=form;
+this.connect(form,"onsubmit","submit");
+for(var i=0;i<form.elements.length;i++){
+var node=form.elements[i];
+if(node&&node.type&&dojo.lang.inArray(["submit","button"],node.type.toLowerCase())){
+this.connect(node,"onclick","click");
+}
+}
+var _285=form.getElementsByTagName("input");
+for(var i=0;i<_285.length;i++){
+var _286=_285[i];
+if(_286.type.toLowerCase()=="image"&&_286.form==form){
+this.connect(_286,"onclick","click");
+}
+}
+},onSubmit:function(form){
+return true;
+},submit:function(e){
+e.preventDefault();
+if(this.onSubmit(this.form)){
+dojo.io.bind(dojo.lang.mixin(this.bindArgs,{formFilter:dojo.lang.hitch(this,"formFilter")}));
+}
+},click:function(e){
+var node=e.currentTarget;
+if(node.disabled){
+return;
+}
+this.clickedButton=node;
+},formFilter:function(node){
+var type=(node.type||"").toLowerCase();
+var _28d=false;
+if(node.disabled||!node.name){
+_28d=false;
+}else{
+if(dojo.lang.inArray(["submit","button","image"],type)){
+if(!this.clickedButton){
+this.clickedButton=node;
+}
+_28d=node==this.clickedButton;
+}else{
+_28d=!dojo.lang.inArray(["file","submit","reset","button"],type);
+}
+}
+return _28d;
+},connect:function(_28e,_28f,_290){
+if(dojo.evalObjPath("dojo.event.connect")){
+dojo.event.connect(_28e,_28f,this,_290);
+}else{
+var fcn=dojo.lang.hitch(this,_290);
+_28e[_28f]=function(e){
+if(!e){
+e=window.event;
+}
+if(!e.currentTarget){
+e.currentTarget=e.srcElement;
+}
+if(!e.preventDefault){
+e.preventDefault=function(){
+window.event.returnValue=false;
+};
+}
+fcn(e);
+};
+}
+}});
+dojo.io.XMLHTTPTransport=new function(){
+var _293=this;
+var _294={};
+this.useCache=false;
+this.preventCache=false;
+function getCacheKey(url,_296,_297){
+return url+"|"+_296+"|"+_297.toLowerCase();
+}
+function addToCache(url,_299,_29a,http){
+_294[getCacheKey(url,_299,_29a)]=http;
+}
+function getFromCache(url,_29d,_29e){
+return _294[getCacheKey(url,_29d,_29e)];
+}
+this.clearCache=function(){
+_294={};
+};
+function doLoad(_29f,http,url,_2a2,_2a3){
+if(((http.status>=200)&&(http.status<300))||(http.status==304)||(location.protocol=="file:"&&(http.status==0||http.status==undefined))||(location.protocol=="chrome:"&&(http.status==0||http.status==undefined))){
+var ret;
+if(_29f.method.toLowerCase()=="head"){
+var _2a5=http.getAllResponseHeaders();
+ret={};
+ret.toString=function(){
+return _2a5;
+};
+var _2a6=_2a5.split(/[\r\n]+/g);
+for(var i=0;i<_2a6.length;i++){
+var pair=_2a6[i].match(/^([^:]+)\s*:\s*(.+)$/i);
+if(pair){
+ret[pair[1]]=pair[2];
+}
+}
+}else{
+if(_29f.mimetype=="text/javascript"){
+try{
+ret=dj_eval(http.responseText);
+}
+catch(e){
+dojo.debug(e);
+dojo.debug(http.responseText);
+ret=null;
+}
+}else{
+if(_29f.mimetype=="text/json"||_29f.mimetype=="application/json"){
+try{
+ret=dj_eval("("+http.responseText+")");
+}
+catch(e){
+dojo.debug(e);
+dojo.debug(http.responseText);
+ret=false;
+}
+}else{
+if((_29f.mimetype=="application/xml")||(_29f.mimetype=="text/xml")){
+ret=http.responseXML;
+if(!ret||typeof ret=="string"||!http.getResponseHeader("Content-Type")){
+ret=dojo.dom.createDocumentFromText(http.responseText);
+}
+}else{
+ret=http.responseText;
+}
+}
+}
+}
+if(_2a3){
+addToCache(url,_2a2,_29f.method,http);
+}
+_29f[(typeof _29f.load=="function")?"load":"handle"]("load",ret,http,_29f);
+}else{
+var _2a9=new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText);
+_29f[(typeof _29f.error=="function")?"error":"handle"]("error",_2a9,http,_29f);
+}
+}
+function setHeaders(http,_2ab){
+if(_2ab["headers"]){
+for(var _2ac in _2ab["headers"]){
+if(_2ac.toLowerCase()=="content-type"&&!_2ab["contentType"]){
+_2ab["contentType"]=_2ab["headers"][_2ac];
+}else{
+http.setRequestHeader(_2ac,_2ab["headers"][_2ac]);
+}
+}
+}
+}
+this.inFlight=[];
+this.inFlightTimer=null;
+this.startWatchingInFlight=function(){
+if(!this.inFlightTimer){
+this.inFlightTimer=setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();",10);
+}
+};
+this.watchInFlight=function(){
+var now=null;
+if(!dojo.hostenv._blockAsync&&!_293._blockAsync){
+for(var x=this.inFlight.length-1;x>=0;x--){
+try{
+var tif=this.inFlight[x];
+if(!tif||tif.http._aborted||!tif.http.readyState){
+this.inFlight.splice(x,1);
+continue;
+}
+if(4==tif.http.readyState){
+this.inFlight.splice(x,1);
+doLoad(tif.req,tif.http,tif.url,tif.query,tif.useCache);
+}else{
+if(tif.startTime){
+if(!now){
+now=(new Date()).getTime();
+}
+if(tif.startTime+(tif.req.timeoutSeconds*1000)<now){
+if(typeof tif.http.abort=="function"){
+tif.http.abort();
+}
+this.inFlight.splice(x,1);
+tif.req[(typeof tif.req.timeout=="function")?"timeout":"handle"]("timeout",null,tif.http,tif.req);
+}
+}
+}
+}
+catch(e){
+try{
+var _2b0=new dojo.io.Error("XMLHttpTransport.watchInFlight Error: "+e);
+tif.req[(typeof tif.req.error=="function")?"error":"handle"]("error",_2b0,tif.http,tif.req);
+}
+catch(e2){
+dojo.debug("XMLHttpTransport error callback failed: "+e2);
+}
+}
+}
+}
+clearTimeout(this.inFlightTimer);
+if(this.inFlight.length==0){
+this.inFlightTimer=null;
+return;
+}
+this.inFlightTimer=setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();",10);
+};
+var _2b1=dojo.hostenv.getXmlhttpObject()?true:false;
+this.canHandle=function(_2b2){
+return _2b1&&dojo.lang.inArray(["text/plain","text/html","application/xml","text/xml","text/javascript","text/json","application/json"],(_2b2["mimetype"].toLowerCase()||""))&&!(_2b2["formNode"]&&dojo.io.formHasFile(_2b2["formNode"]));
+};
+this.multipartBoundary="45309FFF-BD65-4d50-99C9-36986896A96F";
+this.bind=function(_2b3){
+if(!_2b3["url"]){
+if(!_2b3["formNode"]&&(_2b3["backButton"]||_2b3["back"]||_2b3["changeUrl"]||_2b3["watchForURL"])&&(!djConfig.preventBackButtonFix)){
+dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request","Use dojo.undo.browser.addToHistory() instead.","0.4");
+dojo.undo.browser.addToHistory(_2b3);
+return true;
+}
+}
+var url=_2b3.url;
+var _2b5="";
+if(_2b3["formNode"]){
+var ta=_2b3.formNode.getAttribute("action");
+if((ta)&&(!_2b3["url"])){
+url=ta;
+}
+var tp=_2b3.formNode.getAttribute("method");
+if((tp)&&(!_2b3["method"])){
+_2b3.method=tp;
+}
+_2b5+=dojo.io.encodeForm(_2b3.formNode,_2b3.encoding,_2b3["formFilter"]);
+}
+if(url.indexOf("#")>-1){
+dojo.debug("Warning: dojo.io.bind: stripping hash values from url:",url);
+url=url.split("#")[0];
+}
+if(_2b3["file"]){
+_2b3.method="post";
+}
+if(!_2b3["method"]){
+_2b3.method="get";
+}
+if(_2b3.method.toLowerCase()=="get"){
+_2b3.multipart=false;
+}else{
+if(_2b3["file"]){
+_2b3.multipart=true;
+}else{
+if(!_2b3["multipart"]){
+_2b3.multipart=false;
+}
+}
+}
+if(_2b3["backButton"]||_2b3["back"]||_2b3["changeUrl"]){
+dojo.undo.browser.addToHistory(_2b3);
+}
+var _2b8=_2b3["content"]||{};
+if(_2b3.sendTransport){
+_2b8["dojo.transport"]="xmlhttp";
+}
+do{
+if(_2b3.postContent){
+_2b5=_2b3.postContent;
+break;
+}
+if(_2b8){
+_2b5+=dojo.io.argsFromMap(_2b8,_2b3.encoding);
+}
+if(_2b3.method.toLowerCase()=="get"||!_2b3.multipart){
+break;
+}
+var t=[];
+if(_2b5.length){
+var q=_2b5.split("&");
+for(var i=0;i<q.length;++i){
+if(q[i].length){
+var p=q[i].split("=");
+t.push("--"+this.multipartBoundary,"Content-Disposition: form-data; name=\""+p[0]+"\"","",p[1]);
+}
+}
+}
+if(_2b3.file){
+if(dojo.lang.isArray(_2b3.file)){
+for(var i=0;i<_2b3.file.length;++i){
+var o=_2b3.file[i];
+t.push("--"+this.multipartBoundary,"Content-Disposition: form-data; name=\""+o.name+"\"; filename=\""+("fileName" in o?o.fileName:o.name)+"\"","Content-Type: "+("contentType" in o?o.contentType:"application/octet-stream"),"",o.content);
+}
+}else{
+var o=_2b3.file;
+t.push("--"+this.multipartBoundary,"Content-Disposition: form-data; name=\""+o.name+"\"; filename=\""+("fileName" in o?o.fileName:o.name)+"\"","Content-Type: "+("contentType" in o?o.contentType:"application/octet-stream"),"",o.content);
+}
+}
+if(t.length){
+t.push("--"+this.multipartBoundary+"--","");
+_2b5=t.join("\r\n");
+}
+}while(false);
+var _2be=_2b3["sync"]?false:true;
+var _2bf=_2b3["preventCache"]||(this.preventCache==true&&_2b3["preventCache"]!=false);
+var _2c0=_2b3["useCache"]==true||(this.useCache==true&&_2b3["useCache"]!=false);
+if(!_2bf&&_2c0){
+var _2c1=getFromCache(url,_2b5,_2b3.method);
+if(_2c1){
+doLoad(_2b3,_2c1,url,_2b5,false);
+return;
+}
+}
+var http=dojo.hostenv.getXmlhttpObject(_2b3);
+var _2c3=false;
+if(_2be){
+var _2c4=this.inFlight.push({"req":_2b3,"http":http,"url":url,"query":_2b5,"useCache":_2c0,"startTime":_2b3.timeoutSeconds?(new Date()).getTime():0});
+this.startWatchingInFlight();
+}else{
+_293._blockAsync=true;
+}
+if(_2b3.method.toLowerCase()=="post"){
+if(!_2b3.user){
+http.open("POST",url,_2be);
+}else{
+http.open("POST",url,_2be,_2b3.user,_2b3.password);
+}
+setHeaders(http,_2b3);
+http.setRequestHeader("Content-Type",_2b3.multipart?("multipart/form-data; boundary="+this.multipartBoundary):(_2b3.contentType||"application/x-www-form-urlencoded"));
+try{
+http.send(_2b5);
+}
+catch(e){
+if(typeof http.abort=="function"){
+http.abort();
+}
+doLoad(_2b3,{status:404},url,_2b5,_2c0);
+}
+}else{
+var _2c5=url;
+if(_2b5!=""){
+_2c5+=(_2c5.indexOf("?")>-1?"&":"?")+_2b5;
+}
+if(_2bf){
+_2c5+=(dojo.string.endsWithAny(_2c5,"?","&")?"":(_2c5.indexOf("?")>-1?"&":"?"))+"dojo.preventCache="+new Date().valueOf();
+}
+if(!_2b3.user){
+http.open(_2b3.method.toUpperCase(),_2c5,_2be);
+}else{
+http.open(_2b3.method.toUpperCase(),_2c5,_2be,_2b3.user,_2b3.password);
+}
+setHeaders(http,_2b3);
+try{
+http.send(null);
+}
+catch(e){
+if(typeof http.abort=="function"){
+http.abort();
+}
+doLoad(_2b3,{status:404},url,_2b5,_2c0);
+}
+}
+if(!_2be){
+doLoad(_2b3,http,url,_2b5,_2c0);
+_293._blockAsync=false;
+}
+_2b3.abort=function(){
+try{
+http._aborted=true;
+}
+catch(e){
+}
+return http.abort();
+};
+return;
+};
+dojo.io.transports.addTransport("XMLHTTPTransport");
+};
+dojo.provide("dojo.io.cookie");
+dojo.io.cookie.setCookie=function(name,_2c7,days,path,_2ca,_2cb){
+var _2cc=-1;
+if(typeof days=="number"&&days>=0){
+var d=new Date();
+d.setTime(d.getTime()+(days*24*60*60*1000));
+_2cc=d.toGMTString();
+}
+_2c7=escape(_2c7);
+document.cookie=name+"="+_2c7+";"+(_2cc!=-1?" expires="+_2cc+";":"")+(path?"path="+path:"")+(_2ca?"; domain="+_2ca:"")+(_2cb?"; secure":"");
+};
+dojo.io.cookie.set=dojo.io.cookie.setCookie;
+dojo.io.cookie.getCookie=function(name){
+var idx=document.cookie.lastIndexOf(name+"=");
+if(idx==-1){
+return null;
+}
+var _2d0=document.cookie.substring(idx+name.length+1);
+var end=_2d0.indexOf(";");
+if(end==-1){
+end=_2d0.length;
+}
+_2d0=_2d0.substring(0,end);
+_2d0=unescape(_2d0);
+return _2d0;
+};
+dojo.io.cookie.get=dojo.io.cookie.getCookie;
+dojo.io.cookie.deleteCookie=function(name){
+dojo.io.cookie.setCookie(name,"-",0);
+};
+dojo.io.cookie.setObjectCookie=function(name,obj,days,path,_2d7,_2d8,_2d9){
+if(arguments.length==5){
+_2d9=_2d7;
+_2d7=null;
+_2d8=null;
+}
+var _2da=[],_2db,_2dc="";
+if(!_2d9){
+_2db=dojo.io.cookie.getObjectCookie(name);
+}
+if(days>=0){
+if(!_2db){
+_2db={};
+}
+for(var prop in obj){
+if(prop==null){
+delete _2db[prop];
+}else{
+if(typeof obj[prop]=="string"||typeof obj[prop]=="number"){
+_2db[prop]=obj[prop];
+}
+}
+}
+prop=null;
+for(var prop in _2db){
+_2da.push(escape(prop)+"="+escape(_2db[prop]));
+}
+_2dc=_2da.join("&");
+}
+dojo.io.cookie.setCookie(name,_2dc,days,path,_2d7,_2d8);
+};
+dojo.io.cookie.getObjectCookie=function(name){
+var _2df=null,_2e0=dojo.io.cookie.getCookie(name);
+if(_2e0){
+_2df={};
+var _2e1=_2e0.split("&");
+for(var i=0;i<_2e1.length;i++){
+var pair=_2e1[i].split("=");
+var _2e4=pair[1];
+if(isNaN(_2e4)){
+_2e4=unescape(pair[1]);
+}
+_2df[unescape(pair[0])]=_2e4;
+}
+}
+return _2df;
+};
+dojo.io.cookie.isSupported=function(){
+if(typeof navigator.cookieEnabled!="boolean"){
+dojo.io.cookie.setCookie("__TestingYourBrowserForCookieSupport__","CookiesAllowed",90,null);
+var _2e5=dojo.io.cookie.getCookie("__TestingYourBrowserForCookieSupport__");
+navigator.cookieEnabled=(_2e5=="CookiesAllowed");
+if(navigator.cookieEnabled){
+this.deleteCookie("__TestingYourBrowserForCookieSupport__");
+}
+}
+return navigator.cookieEnabled;
+};
+if(!dojo.io.cookies){
+dojo.io.cookies=dojo.io.cookie;
+}
+dojo.provide("dojo.io.*");
+dojo.provide("dojo.io");
+dojo.deprecated("dojo.io","replaced by dojo.io.*","0.5");
+dojo.provide("dojo.event.common");
+dojo.event=new function(){
+this._canTimeout=dojo.lang.isFunction(dj_global["setTimeout"])||dojo.lang.isAlien(dj_global["setTimeout"]);
+function interpolateArgs(args,_2e7){
+var dl=dojo.lang;
+var ao={srcObj:dj_global,srcFunc:null,adviceObj:dj_global,adviceFunc:null,aroundObj:null,aroundFunc:null,adviceType:(args.length>2)?args[0]:"after",precedence:"last",once:false,delay:null,rate:0,adviceMsg:false};
+switch(args.length){
+case 0:
+return;
+case 1:
+return;
+case 2:
+ao.srcFunc=args[0];
+ao.adviceFunc=args[1];
+break;
+case 3:
+if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){
+ao.adviceType="after";
+ao.srcObj=args[0];
+ao.srcFunc=args[1];
+ao.adviceFunc=args[2];
+}else{
+if((dl.isString(args[1]))&&(dl.isString(args[2]))){
+ao.srcFunc=args[1];
+ao.adviceFunc=args[2];
+}else{
+if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){
+ao.adviceType="after";
+ao.srcObj=args[0];
+ao.srcFunc=args[1];
+var _2ea=dl.nameAnonFunc(args[2],ao.adviceObj,_2e7);
+ao.adviceFunc=_2ea;
+}else{
+if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){
+ao.adviceType="after";
+ao.srcObj=dj_global;
+var _2ea=dl.nameAnonFunc(args[0],ao.srcObj,_2e7);
+ao.srcFunc=_2ea;
+ao.adviceObj=args[1];
+ao.adviceFunc=args[2];
+}
+}
+}
+}
+break;
+case 4:
+if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){
+ao.adviceType="after";
+ao.srcObj=args[0];
+ao.srcFunc=args[1];
+ao.adviceObj=args[2];
+ao.adviceFunc=args[3];
+}else{
+if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){
+ao.adviceType=args[0];
+ao.srcObj=dj_global;
+ao.srcFunc=args[1];
+ao.adviceObj=args[2];
+ao.adviceFunc=args[3];
+}else{
+if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){
+ao.adviceType=args[0];
+ao.srcObj=dj_global;
+var _2ea=dl.nameAnonFunc(args[1],dj_global,_2e7);
+ao.srcFunc=_2ea;
+ao.adviceObj=args[2];
+ao.adviceFunc=args[3];
+}else{
+if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){
+ao.srcObj=args[1];
+ao.srcFunc=args[2];
+var _2ea=dl.nameAnonFunc(args[3],dj_global,_2e7);
+ao.adviceObj=dj_global;
+ao.adviceFunc=_2ea;
+}else{
+if(dl.isObject(args[1])){
+ao.srcObj=args[1];
+ao.srcFunc=args[2];
+ao.adviceObj=dj_global;
+ao.adviceFunc=args[3];
+}else{
+if(dl.isObject(args[2])){
+ao.srcObj=dj_global;
+ao.srcFunc=args[1];
+ao.adviceObj=args[2];
+ao.adviceFunc=args[3];
+}else{
+ao.srcObj=ao.adviceObj=ao.aroundObj=dj_global;
+ao.srcFunc=args[1];
+ao.adviceFunc=args[2];
+ao.aroundFunc=args[3];
+}
+}
+}
+}
+}
+}
+break;
+case 6:
+ao.srcObj=args[1];
+ao.srcFunc=args[2];
+ao.adviceObj=args[3];
+ao.adviceFunc=args[4];
+ao.aroundFunc=args[5];
+ao.aroundObj=dj_global;
+break;
+default:
+ao.srcObj=args[1];
+ao.srcFunc=args[2];
+ao.adviceObj=args[3];
+ao.adviceFunc=args[4];
+ao.aroundObj=args[5];
+ao.aroundFunc=args[6];
+ao.once=args[7];
+ao.delay=args[8];
+ao.rate=args[9];
+ao.adviceMsg=args[10];
+break;
+}
+if(dl.isFunction(ao.aroundFunc)){
+var _2ea=dl.nameAnonFunc(ao.aroundFunc,ao.aroundObj,_2e7);
+ao.aroundFunc=_2ea;
+}
+if(dl.isFunction(ao.srcFunc)){
+ao.srcFunc=dl.getNameInObj(ao.srcObj,ao.srcFunc);
+}
+if(dl.isFunction(ao.adviceFunc)){
+ao.adviceFunc=dl.getNameInObj(ao.adviceObj,ao.adviceFunc);
+}
+if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){
+ao.aroundFunc=dl.getNameInObj(ao.aroundObj,ao.aroundFunc);
+}
+if(!ao.srcObj){
+dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc);
+}
+if(!ao.adviceObj){
+dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc);
+}
+if(!ao.adviceFunc){
+dojo.debug("bad adviceFunc for srcFunc: "+ao.srcFunc);
+dojo.debugShallow(ao);
+}
+return ao;
+}
+this.connect=function(){
+if(arguments.length==1){
+var ao=arguments[0];
+}else{
+var ao=interpolateArgs(arguments,true);
+}
+if(dojo.lang.isString(ao.srcFunc)&&(ao.srcFunc.toLowerCase()=="onkey")){
+if(dojo.render.html.ie){
+ao.srcFunc="onkeydown";
+this.connect(ao);
+}
+ao.srcFunc="onkeypress";
+}
+if(dojo.lang.isArray(ao.srcObj)&&ao.srcObj!=""){
+var _2ec={};
+for(var x in ao){
+_2ec[x]=ao[x];
+}
+var mjps=[];
+dojo.lang.forEach(ao.srcObj,function(src){
+if((dojo.render.html.capable)&&(dojo.lang.isString(src))){
+src=dojo.byId(src);
+}
+_2ec.srcObj=src;
+mjps.push(dojo.event.connect.call(dojo.event,_2ec));
+});
+return mjps;
+}
+var mjp=dojo.event.MethodJoinPoint.getForMethod(ao.srcObj,ao.srcFunc);
+if(ao.adviceFunc){
+var mjp2=dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj,ao.adviceFunc);
+}
+mjp.kwAddAdvice(ao);
+return mjp;
+};
+this.log=function(a1,a2){
+var _2f4;
+if((arguments.length==1)&&(typeof a1=="object")){
+_2f4=a1;
+}else{
+_2f4={srcObj:a1,srcFunc:a2};
+}
+_2f4.adviceFunc=function(){
+var _2f5=[];
+for(var x=0;x<arguments.length;x++){
+_2f5.push(arguments[x]);
+}
+dojo.debug("("+_2f4.srcObj+")."+_2f4.srcFunc,":",_2f5.join(", "));
+};
+this.kwConnect(_2f4);
+};
+this.connectBefore=function(){
+var args=["before"];
+for(var i=0;i<arguments.length;i++){
+args.push(arguments[i]);
+}
+return this.connect.apply(this,args);
+};
+this.connectAround=function(){
+var args=["around"];
+for(var i=0;i<arguments.length;i++){
+args.push(arguments[i]);
+}
+return this.connect.apply(this,args);
+};
+this.connectOnce=function(){
+var ao=interpolateArgs(arguments,true);
+ao.once=true;
+return this.connect(ao);
+};
+this._kwConnectImpl=function(_2fc,_2fd){
+var fn=(_2fd)?"disconnect":"connect";
+if(typeof _2fc["srcFunc"]=="function"){
+_2fc.srcObj=_2fc["srcObj"]||dj_global;
+var _2ff=dojo.lang.nameAnonFunc(_2fc.srcFunc,_2fc.srcObj,true);
+_2fc.srcFunc=_2ff;
+}
+if(typeof _2fc["adviceFunc"]=="function"){
+_2fc.adviceObj=_2fc["adviceObj"]||dj_global;
+var _2ff=dojo.lang.nameAnonFunc(_2fc.adviceFunc,_2fc.adviceObj,true);
+_2fc.adviceFunc=_2ff;
+}
+_2fc.srcObj=_2fc["srcObj"]||dj_global;
+_2fc.adviceObj=_2fc["adviceObj"]||_2fc["targetObj"]||dj_global;
+_2fc.adviceFunc=_2fc["adviceFunc"]||_2fc["targetFunc"];
+return dojo.event[fn](_2fc);
+};
+this.kwConnect=function(_300){
+return this._kwConnectImpl(_300,false);
+};
+this.disconnect=function(){
+if(arguments.length==1){
+var ao=arguments[0];
+}else{
+var ao=interpolateArgs(arguments,true);
+}
+if(!ao.adviceFunc){
+return;
+}
+if(dojo.lang.isString(ao.srcFunc)&&(ao.srcFunc.toLowerCase()=="onkey")){
+if(dojo.render.html.ie){
+ao.srcFunc="onkeydown";
+this.disconnect(ao);
+}
+ao.srcFunc="onkeypress";
+}
+var mjp=dojo.event.MethodJoinPoint.getForMethod(ao.srcObj,ao.srcFunc);
+return mjp.removeAdvice(ao.adviceObj,ao.adviceFunc,ao.adviceType,ao.once);
+};
+this.kwDisconnect=function(_303){
+return this._kwConnectImpl(_303,true);
+};
+};
+dojo.event.MethodInvocation=function(_304,obj,args){
+this.jp_=_304;
+this.object=obj;
+this.args=[];
+for(var x=0;x<args.length;x++){
+this.args[x]=args[x];
+}
+this.around_index=-1;
+};
+dojo.event.MethodInvocation.prototype.proceed=function(){
+this.around_index++;
+if(this.around_index>=this.jp_.around.length){
+return this.jp_.object[this.jp_.methodname].apply(this.jp_.object,this.args);
+}else{
+var ti=this.jp_.around[this.around_index];
+var mobj=ti[0]||dj_global;
+var meth=ti[1];
+return mobj[meth].call(mobj,this);
+}
+};
+dojo.event.MethodJoinPoint=function(obj,_30c){
+this.object=obj||dj_global;
+this.methodname=_30c;
+this.methodfunc=this.object[_30c];
+this.squelch=false;
+};
+dojo.event.MethodJoinPoint.getForMethod=function(obj,_30e){
+if(!obj){
+obj=dj_global;
+}
+if(!obj[_30e]){
+obj[_30e]=function(){
+};
+if(!obj[_30e]){
+dojo.raise("Cannot set do-nothing method on that object "+_30e);
+}
+}else{
+if((!dojo.lang.isFunction(obj[_30e]))&&(!dojo.lang.isAlien(obj[_30e]))){
+return null;
+}
+}
+var _30f=_30e+"$joinpoint";
+var _310=_30e+"$joinpoint$method";
+var _311=obj[_30f];
+if(!_311){
+var _312=false;
+if(dojo.event["browser"]){
+if((obj["attachEvent"])||(obj["nodeType"])||(obj["addEventListener"])){
+_312=true;
+dojo.event.browser.addClobberNodeAttrs(obj,[_30f,_310,_30e]);
+}
+}
+var _313=obj[_30e].length;
+obj[_310]=obj[_30e];
+_311=obj[_30f]=new dojo.event.MethodJoinPoint(obj,_310);
+obj[_30e]=function(){
+var args=[];
+if((_312)&&(!arguments.length)){
+var evt=null;
+try{
+if(obj.ownerDocument){
+evt=obj.ownerDocument.parentWindow.event;
+}else{
+if(obj.documentElement){
+evt=obj.documentElement.ownerDocument.parentWindow.event;
+}else{
+if(obj.event){
+evt=obj.event;
+}else{
+evt=window.event;
+}
+}
+}
+}
+catch(e){
+evt=window.event;
+}
+if(evt){
+args.push(dojo.event.browser.fixEvent(evt,this));
+}
+}else{
+for(var x=0;x<arguments.length;x++){
+if((x==0)&&(_312)&&(dojo.event.browser.isEvent(arguments[x]))){
+args.push(dojo.event.browser.fixEvent(arguments[x],this));
+}else{
+args.push(arguments[x]);
+}
+}
+}
+return _311.run.apply(_311,args);
+};
+obj[_30e].__preJoinArity=_313;
+}
+return _311;
+};
+dojo.lang.extend(dojo.event.MethodJoinPoint,{unintercept:function(){
+this.object[this.methodname]=this.methodfunc;
+this.before=[];
+this.after=[];
+this.around=[];
+},disconnect:dojo.lang.forward("unintercept"),run:function(){
+var obj=this.object||dj_global;
+var args=arguments;
+var _319=[];
+for(var x=0;x<args.length;x++){
+_319[x]=args[x];
+}
+var _31b=function(marr){
+if(!marr){
+dojo.debug("Null argument to unrollAdvice()");
+return;
+}
+var _31d=marr[0]||dj_global;
+var _31e=marr[1];
+if(!_31d[_31e]){
+dojo.raise("function \""+_31e+"\" does not exist on \""+_31d+"\"");
+}
+var _31f=marr[2]||dj_global;
+var _320=marr[3];
+var msg=marr[6];
+var _322;
+var to={args:[],jp_:this,object:obj,proceed:function(){
+return _31d[_31e].apply(_31d,to.args);
+}};
+to.args=_319;
+var _324=parseInt(marr[4]);
+var _325=((!isNaN(_324))&&(marr[4]!==null)&&(typeof marr[4]!="undefined"));
+if(marr[5]){
+var rate=parseInt(marr[5]);
+var cur=new Date();
+var _328=false;
+if((marr["last"])&&((cur-marr.last)<=rate)){
+if(dojo.event._canTimeout){
+if(marr["delayTimer"]){
+clearTimeout(marr.delayTimer);
+}
+var tod=parseInt(rate*2);
+var mcpy=dojo.lang.shallowCopy(marr);
+marr.delayTimer=setTimeout(function(){
+mcpy[5]=0;
+_31b(mcpy);
+},tod);
+}
+return;
+}else{
+marr.last=cur;
+}
+}
+if(_320){
+_31f[_320].call(_31f,to);
+}else{
+if((_325)&&((dojo.render.html)||(dojo.render.svg))){
+dj_global["setTimeout"](function(){
+if(msg){
+_31d[_31e].call(_31d,to);
+}else{
+_31d[_31e].apply(_31d,args);
+}
+},_324);
+}else{
+if(msg){
+_31d[_31e].call(_31d,to);
+}else{
+_31d[_31e].apply(_31d,args);
+}
+}
+}
+};
+var _32b=function(){
+if(this.squelch){
+try{
+return _31b.apply(this,arguments);
+}
+catch(e){
+dojo.debug(e);
+}
+}else{
+return _31b.apply(this,arguments);
+}
+};
+if((this["before"])&&(this.before.length>0)){
+dojo.lang.forEach(this.before.concat(new Array()),_32b);
+}
+var _32c;
+try{
+if((this["around"])&&(this.around.length>0)){
+var mi=new dojo.event.MethodInvocation(this,obj,args);
+_32c=mi.proceed();
+}else{
+if(this.methodfunc){
+_32c=this.object[this.methodname].apply(this.object,args);
+}
+}
+}
+catch(e){
+if(!this.squelch){
+dojo.raise(e);
+}
+}
+if((this["after"])&&(this.after.length>0)){
+dojo.lang.forEach(this.after.concat(new Array()),_32b);
+}
+return (this.methodfunc)?_32c:null;
+},getArr:function(kind){
+var type="after";
+if((typeof kind=="string")&&(kind.indexOf("before")!=-1)){
+type="before";
+}else{
+if(kind=="around"){
+type="around";
+}
+}
+if(!this[type]){
+this[type]=[];
+}
+return this[type];
+},kwAddAdvice:function(args){
+this.addAdvice(args["adviceObj"],args["adviceFunc"],args["aroundObj"],args["aroundFunc"],args["adviceType"],args["precedence"],args["once"],args["delay"],args["rate"],args["adviceMsg"]);
+},addAdvice:function(_331,_332,_333,_334,_335,_336,once,_338,rate,_33a){
+var arr=this.getArr(_335);
+if(!arr){
+dojo.raise("bad this: "+this);
+}
+var ao=[_331,_332,_333,_334,_338,rate,_33a];
+if(once){
+if(this.hasAdvice(_331,_332,_335,arr)>=0){
+return;
+}
+}
+if(_336=="first"){
+arr.unshift(ao);
+}else{
+arr.push(ao);
+}
+},hasAdvice:function(_33d,_33e,_33f,arr){
+if(!arr){
+arr=this.getArr(_33f);
+}
+var ind=-1;
+for(var x=0;x<arr.length;x++){
+var aao=(typeof _33e=="object")?(new String(_33e)).toString():_33e;
+var a1o=(typeof arr[x][1]=="object")?(new String(arr[x][1])).toString():arr[x][1];
+if((arr[x][0]==_33d)&&(a1o==aao)){
+ind=x;
+}
+}
+return ind;
+},removeAdvice:function(_345,_346,_347,once){
+var arr=this.getArr(_347);
+var ind=this.hasAdvice(_345,_346,_347,arr);
+if(ind==-1){
+return false;
+}
+while(ind!=-1){
+arr.splice(ind,1);
+if(once){
+break;
+}
+ind=this.hasAdvice(_345,_346,_347,arr);
+}
+return true;
+}});
+dojo.provide("dojo.event.topic");
+dojo.event.topic=new function(){
+this.topics={};
+this.getTopic=function(_34b){
+if(!this.topics[_34b]){
+this.topics[_34b]=new this.TopicImpl(_34b);
+}
+return this.topics[_34b];
+};
+this.registerPublisher=function(_34c,obj,_34e){
+var _34c=this.getTopic(_34c);
+_34c.registerPublisher(obj,_34e);
+};
+this.subscribe=function(_34f,obj,_351){
+var _34f=this.getTopic(_34f);
+_34f.subscribe(obj,_351);
+};
+this.unsubscribe=function(_352,obj,_354){
+var _352=this.getTopic(_352);
+_352.unsubscribe(obj,_354);
+};
+this.destroy=function(_355){
+this.getTopic(_355).destroy();
+delete this.topics[_355];
+};
+this.publishApply=function(_356,args){
+var _356=this.getTopic(_356);
+_356.sendMessage.apply(_356,args);
+};
+this.publish=function(_358,_359){
+var _358=this.getTopic(_358);
+var args=[];
+for(var x=1;x<arguments.length;x++){
+args.push(arguments[x]);
+}
+_358.sendMessage.apply(_358,args);
+};
+};
+dojo.event.topic.TopicImpl=function(_35c){
+this.topicName=_35c;
+this.subscribe=function(_35d,_35e){
+var tf=_35e||_35d;
+var to=(!_35e)?dj_global:_35d;
+return dojo.event.kwConnect({srcObj:this,srcFunc:"sendMessage",adviceObj:to,adviceFunc:tf});
+};
+this.unsubscribe=function(_361,_362){
+var tf=(!_362)?_361:_362;
+var to=(!_362)?null:_361;
+return dojo.event.kwDisconnect({srcObj:this,srcFunc:"sendMessage",adviceObj:to,adviceFunc:tf});
+};
+this._getJoinPoint=function(){
+return dojo.event.MethodJoinPoint.getForMethod(this,"sendMessage");
+};
+this.setSquelch=function(_365){
+this._getJoinPoint().squelch=_365;
+};
+this.destroy=function(){
+this._getJoinPoint().disconnect();
+};
+this.registerPublisher=function(_366,_367){
+dojo.event.connect(_366,_367,this,"sendMessage");
+};
+this.sendMessage=function(_368){
+};
+};
+dojo.provide("dojo.event.browser");
+dojo._ie_clobber=new function(){
+this.clobberNodes=[];
+function nukeProp(node,prop){
+try{
+node[prop]=null;
+}
+catch(e){
+}
+try{
+delete node[prop];
+}
+catch(e){
+}
+try{
+node.removeAttribute(prop);
+}
+catch(e){
+}
+}
+this.clobber=function(_36b){
+var na;
+var tna;
+if(_36b){
+tna=_36b.all||_36b.getElementsByTagName("*");
+na=[_36b];
+for(var x=0;x<tna.length;x++){
+if(tna[x]["__doClobber__"]){
+na.push(tna[x]);
+}
+}
+}else{
+try{
+window.onload=null;
+}
+catch(e){
+}
+na=(this.clobberNodes.length)?this.clobberNodes:document.all;
+}
+tna=null;
+var _36f={};
+for(var i=na.length-1;i>=0;i=i-1){
+var el=na[i];
+try{
+if(el&&el["__clobberAttrs__"]){
+for(var j=0;j<el.__clobberAttrs__.length;j++){
+nukeProp(el,el.__clobberAttrs__[j]);
+}
+nukeProp(el,"__clobberAttrs__");
+nukeProp(el,"__doClobber__");
+}
+}
+catch(e){
+}
+}
+na=null;
+};
+};
+if(dojo.render.html.ie){
+dojo.addOnUnload(function(){
+dojo._ie_clobber.clobber();
+try{
+if((dojo["widget"])&&(dojo.widget["manager"])){
+dojo.widget.manager.destroyAll();
+}
+}
+catch(e){
+}
+try{
+window.onload=null;
+}
+catch(e){
+}
+try{
+window.onunload=null;
+}
+catch(e){
+}
+dojo._ie_clobber.clobberNodes=[];
+});
+}
+dojo.event.browser=new function(){
+var _373=0;
+this.normalizedEventName=function(_374){
+switch(_374){
+case "CheckboxStateChange":
+case "DOMAttrModified":
+case "DOMMenuItemActive":
+case "DOMMenuItemInactive":
+case "DOMMouseScroll":
+case "DOMNodeInserted":
+case "DOMNodeRemoved":
+case "RadioStateChange":
+return _374;
+break;
+default:
+return _374.toLowerCase();
+break;
+}
+};
+this.clean=function(node){
+if(dojo.render.html.ie){
+dojo._ie_clobber.clobber(node);
+}
+};
+this.addClobberNode=function(node){
+if(!dojo.render.html.ie){
+return;
+}
+if(!node["__doClobber__"]){
+node.__doClobber__=true;
+dojo._ie_clobber.clobberNodes.push(node);
+node.__clobberAttrs__=[];
+}
+};
+this.addClobberNodeAttrs=function(node,_378){
+if(!dojo.render.html.ie){
+return;
+}
+this.addClobberNode(node);
+for(var x=0;x<_378.length;x++){
+node.__clobberAttrs__.push(_378[x]);
+}
+};
+this.removeListener=function(node,_37b,fp,_37d){
+if(!_37d){
+var _37d=false;
+}
+_37b=dojo.event.browser.normalizedEventName(_37b);
+if((_37b=="onkey")||(_37b=="key")){
+if(dojo.render.html.ie){
+this.removeListener(node,"onkeydown",fp,_37d);
+}
+_37b="onkeypress";
+}
+if(_37b.substr(0,2)=="on"){
+_37b=_37b.substr(2);
+}
+if(node.removeEventListener){
+node.removeEventListener(_37b,fp,_37d);
+}
+};
+this.addListener=function(node,_37f,fp,_381,_382){
+if(!node){
+return;
+}
+if(!_381){
+var _381=false;
+}
+_37f=dojo.event.browser.normalizedEventName(_37f);
+if((_37f=="onkey")||(_37f=="key")){
+if(dojo.render.html.ie){
+this.addListener(node,"onkeydown",fp,_381,_382);
+}
+_37f="onkeypress";
+}
+if(_37f.substr(0,2)!="on"){
+_37f="on"+_37f;
+}
+if(!_382){
+var _383=function(evt){
+if(!evt){
+evt=window.event;
+}
+var ret=fp(dojo.event.browser.fixEvent(evt,this));
+if(_381){
+dojo.event.browser.stopEvent(evt);
+}
+return ret;
+};
+}else{
+_383=fp;
+}
+if(node.addEventListener){
+node.addEventListener(_37f.substr(2),_383,_381);
+return _383;
+}else{
+if(typeof node[_37f]=="function"){
+var _386=node[_37f];
+node[_37f]=function(e){
+_386(e);
+return _383(e);
+};
+}else{
+node[_37f]=_383;
+}
+if(dojo.render.html.ie){
+this.addClobberNodeAttrs(node,[_37f]);
+}
+return _383;
+}
+};
+this.isEvent=function(obj){
+return (typeof obj!="undefined")&&(typeof Event!="undefined")&&(obj.eventPhase);
+};
+this.currentEvent=null;
+this.callListener=function(_389,_38a){
+if(typeof _389!="function"){
+dojo.raise("listener not a function: "+_389);
+}
+dojo.event.browser.currentEvent.currentTarget=_38a;
+return _389.call(_38a,dojo.event.browser.currentEvent);
+};
+this._stopPropagation=function(){
+dojo.event.browser.currentEvent.cancelBubble=true;
+};
+this._preventDefault=function(){
+dojo.event.browser.currentEvent.returnValue=false;
+};
+this.keys={KEY_BACKSPACE:8,KEY_TAB:9,KEY_CLEAR:12,KEY_ENTER:13,KEY_SHIFT:16,KEY_CTRL:17,KEY_ALT:18,KEY_PAUSE:19,KEY_CAPS_LOCK:20,KEY_ESCAPE:27,KEY_SPACE:32,KEY_PAGE_UP:33,KEY_PAGE_DOWN:34,KEY_END:35,KEY_HOME:36,KEY_LEFT_ARROW:37,KEY_UP_ARROW:38,KEY_RIGHT_ARROW:39,KEY_DOWN_ARROW:40,KEY_INSERT:45,KEY_DELETE:46,KEY_HELP:47,KEY_LEFT_WINDOW:91,KEY_RIGHT_WINDOW:92,KEY_SELECT:93,KEY_NUMPAD_0:96,KEY_NUMPAD_1:97,KEY_NUMPAD_2:98,KEY_NUMPAD_3:99,KEY_NUMPAD_4:100,KEY_NUMPAD_5:101,KEY_NUMPAD_6:102,KEY_NUMPAD_7:103,KEY_NUMPAD_8:104,KEY_NUMPAD_9:105,KEY_NUMPAD_MULTIPLY:106,KEY_NUMPAD_PLUS:107,KEY_NUMPAD_ENTER:108,KEY_NUMPAD_MINUS:109,KEY_NUMPAD_PERIOD:110,KEY_NUMPAD_DIVIDE:111,KEY_F1:112,KEY_F2:113,KEY_F3:114,KEY_F4:115,KEY_F5:116,KEY_F6:117,KEY_F7:118,KEY_F8:119,KEY_F9:120,KEY_F10:121,KEY_F11:122,KEY_F12:123,KEY_F13:124,KEY_F14:125,KEY_F15:126,KEY_NUM_LOCK:144,KEY_SCROLL_LOCK:145};
+this.revKeys=[];
+for(var key in this.keys){
+this.revKeys[this.keys[key]]=key;
+}
+this.fixEvent=function(evt,_38d){
+if(!evt){
+if(window["event"]){
+evt=window.event;
+}
+}
+if((evt["type"])&&(evt["type"].indexOf("key")==0)){
+evt.keys=this.revKeys;
+for(var key in this.keys){
+evt[key]=this.keys[key];
+}
+if(evt["type"]=="keydown"&&dojo.render.html.ie){
+switch(evt.keyCode){
+case evt.KEY_SHIFT:
+case evt.KEY_CTRL:
+case evt.KEY_ALT:
+case evt.KEY_CAPS_LOCK:
+case evt.KEY_LEFT_WINDOW:
+case evt.KEY_RIGHT_WINDOW:
+case evt.KEY_SELECT:
+case evt.KEY_NUM_LOCK:
+case evt.KEY_SCROLL_LOCK:
+case evt.KEY_NUMPAD_0:
+case evt.KEY_NUMPAD_1:
+case evt.KEY_NUMPAD_2:
+case evt.KEY_NUMPAD_3:
+case evt.KEY_NUMPAD_4:
+case evt.KEY_NUMPAD_5:
+case evt.KEY_NUMPAD_6:
+case evt.KEY_NUMPAD_7:
+case evt.KEY_NUMPAD_8:
+case evt.KEY_NUMPAD_9:
+case evt.KEY_NUMPAD_PERIOD:
+break;
+case evt.KEY_NUMPAD_MULTIPLY:
+case evt.KEY_NUMPAD_PLUS:
+case evt.KEY_NUMPAD_ENTER:
+case evt.KEY_NUMPAD_MINUS:
+case evt.KEY_NUMPAD_DIVIDE:
+break;
+case evt.KEY_PAUSE:
+case evt.KEY_TAB:
+case evt.KEY_BACKSPACE:
+case evt.KEY_ENTER:
+case evt.KEY_ESCAPE:
+case evt.KEY_PAGE_UP:
+case evt.KEY_PAGE_DOWN:
+case evt.KEY_END:
+case evt.KEY_HOME:
+case evt.KEY_LEFT_ARROW:
+case evt.KEY_UP_ARROW:
+case evt.KEY_RIGHT_ARROW:
+case evt.KEY_DOWN_ARROW:
+case evt.KEY_INSERT:
+case evt.KEY_DELETE:
+case evt.KEY_F1:
+case evt.KEY_F2:
+case evt.KEY_F3:
+case evt.KEY_F4:
+case evt.KEY_F5:
+case evt.KEY_F6:
+case evt.KEY_F7:
+case evt.KEY_F8:
+case evt.KEY_F9:
+case evt.KEY_F10:
+case evt.KEY_F11:
+case evt.KEY_F12:
+case evt.KEY_F12:
+case evt.KEY_F13:
+case evt.KEY_F14:
+case evt.KEY_F15:
+case evt.KEY_CLEAR:
+case evt.KEY_HELP:
+evt.key=evt.keyCode;
+break;
+default:
+if(evt.ctrlKey||evt.altKey){
+var _38f=evt.keyCode;
+if(_38f>=65&&_38f<=90&&evt.shiftKey==false){
+_38f+=32;
+}
+if(_38f>=1&&_38f<=26&&evt.ctrlKey){
+_38f+=96;
+}
+evt.key=String.fromCharCode(_38f);
+}
+}
+}else{
+if(evt["type"]=="keypress"){
+if(dojo.render.html.opera){
+if(evt.which==0){
+evt.key=evt.keyCode;
+}else{
+if(evt.which>0){
+switch(evt.which){
+case evt.KEY_SHIFT:
+case evt.KEY_CTRL:
+case evt.KEY_ALT:
+case evt.KEY_CAPS_LOCK:
+case evt.KEY_NUM_LOCK:
+case evt.KEY_SCROLL_LOCK:
+break;
+case evt.KEY_PAUSE:
+case evt.KEY_TAB:
+case evt.KEY_BACKSPACE:
+case evt.KEY_ENTER:
+case evt.KEY_ESCAPE:
+evt.key=evt.which;
+break;
+default:
+var _38f=evt.which;
+if((evt.ctrlKey||evt.altKey||evt.metaKey)&&(evt.which>=65&&evt.which<=90&&evt.shiftKey==false)){
+_38f+=32;
+}
+evt.key=String.fromCharCode(_38f);
+}
+}
+}
+}else{
+if(dojo.render.html.ie){
+if(!evt.ctrlKey&&!evt.altKey&&evt.keyCode>=evt.KEY_SPACE){
+evt.key=String.fromCharCode(evt.keyCode);
+}
+}else{
+if(dojo.render.html.safari){
+switch(evt.keyCode){
+case 63232:
+evt.key=evt.KEY_UP_ARROW;
+break;
+case 63233:
+evt.key=evt.KEY_DOWN_ARROW;
+break;
+case 63234:
+evt.key=evt.KEY_LEFT_ARROW;
+break;
+case 63235:
+evt.key=evt.KEY_RIGHT_ARROW;
+break;
+default:
+evt.key=evt.charCode>0?String.fromCharCode(evt.charCode):evt.keyCode;
+}
+}else{
+evt.key=evt.charCode>0?String.fromCharCode(evt.charCode):evt.keyCode;
+}
+}
+}
+}
+}
+}
+if(dojo.render.html.ie){
+if(!evt.target){
+evt.target=evt.srcElement;
+}
+if(!evt.currentTarget){
+evt.currentTarget=(_38d?_38d:evt.srcElement);
+}
+if(!evt.layerX){
+evt.layerX=evt.offsetX;
+}
+if(!evt.layerY){
+evt.layerY=evt.offsetY;
+}
+var doc=(evt.srcElement&&evt.srcElement.ownerDocument)?evt.srcElement.ownerDocument:document;
+var _391=((dojo.render.html.ie55)||(doc["compatMode"]=="BackCompat"))?doc.body:doc.documentElement;
+if(!evt.pageX){
+evt.pageX=evt.clientX+(_391.scrollLeft||0);
+}
+if(!evt.pageY){
+evt.pageY=evt.clientY+(_391.scrollTop||0);
+}
+if(evt.type=="mouseover"){
+evt.relatedTarget=evt.fromElement;
+}
+if(evt.type=="mouseout"){
+evt.relatedTarget=evt.toElement;
+}
+this.currentEvent=evt;
+evt.callListener=this.callListener;
+evt.stopPropagation=this._stopPropagation;
+evt.preventDefault=this._preventDefault;
+}
+return evt;
+};
+this.stopEvent=function(evt){
+if(window.event){
+evt.returnValue=false;
+evt.cancelBubble=true;
+}else{
+evt.preventDefault();
+evt.stopPropagation();
+}
+};
+};
+dojo.provide("dojo.event.*");
+dojo.provide("dojo.gfx.color");
+dojo.gfx.color.Color=function(r,g,b,a){
+if(dojo.lang.isArray(r)){
+this.r=r[0];
+this.g=r[1];
+this.b=r[2];
+this.a=r[3]||1;
+}else{
+if(dojo.lang.isString(r)){
+var rgb=dojo.gfx.color.extractRGB(r);
+this.r=rgb[0];
+this.g=rgb[1];
+this.b=rgb[2];
+this.a=g||1;
+}else{
+if(r instanceof dojo.gfx.color.Color){
+this.r=r.r;
+this.b=r.b;
+this.g=r.g;
+this.a=r.a;
+}else{
+this.r=r;
+this.g=g;
+this.b=b;
+this.a=a;
+}
+}
+}
+};
+dojo.gfx.color.Color.fromArray=function(arr){
+return new dojo.gfx.color.Color(arr[0],arr[1],arr[2],arr[3]);
+};
+dojo.extend(dojo.gfx.color.Color,{toRgb:function(_399){
+if(_399){
+return this.toRgba();
+}else{
+return [this.r,this.g,this.b];
+}
+},toRgba:function(){
+return [this.r,this.g,this.b,this.a];
+},toHex:function(){
+return dojo.gfx.color.rgb2hex(this.toRgb());
+},toCss:function(){
+return "rgb("+this.toRgb().join()+")";
+},toString:function(){
+return this.toHex();
+},blend:function(_39a,_39b){
+var rgb=null;
+if(dojo.lang.isArray(_39a)){
+rgb=_39a;
+}else{
+if(_39a instanceof dojo.gfx.color.Color){
+rgb=_39a.toRgb();
+}else{
+rgb=new dojo.gfx.color.Color(_39a).toRgb();
+}
+}
+return dojo.gfx.color.blend(this.toRgb(),rgb,_39b);
+}});
+dojo.gfx.color.named={white:[255,255,255],black:[0,0,0],red:[255,0,0],green:[0,255,0],lime:[0,255,0],blue:[0,0,255],navy:[0,0,128],gray:[128,128,128],silver:[192,192,192]};
+dojo.gfx.color.blend=function(a,b,_39f){
+if(typeof a=="string"){
+return dojo.gfx.color.blendHex(a,b,_39f);
+}
+if(!_39f){
+_39f=0;
+}
+_39f=Math.min(Math.max(-1,_39f),1);
+_39f=((_39f+1)/2);
+var c=[];
+for(var x=0;x<3;x++){
+c[x]=parseInt(b[x]+((a[x]-b[x])*_39f));
+}
+return c;
+};
+dojo.gfx.color.blendHex=function(a,b,_3a4){
+return dojo.gfx.color.rgb2hex(dojo.gfx.color.blend(dojo.gfx.color.hex2rgb(a),dojo.gfx.color.hex2rgb(b),_3a4));
+};
+dojo.gfx.color.extractRGB=function(_3a5){
+var hex="0123456789abcdef";
+_3a5=_3a5.toLowerCase();
+if(_3a5.indexOf("rgb")==0){
+var _3a7=_3a5.match(/rgba*\((\d+), *(\d+), *(\d+)/i);
+var ret=_3a7.splice(1,3);
+return ret;
+}else{
+var _3a9=dojo.gfx.color.hex2rgb(_3a5);
+if(_3a9){
+return _3a9;
+}else{
+return dojo.gfx.color.named[_3a5]||[255,255,255];
+}
+}
+};
+dojo.gfx.color.hex2rgb=function(hex){
+var _3ab="0123456789ABCDEF";
+var rgb=new Array(3);
+if(hex.indexOf("#")==0){
+hex=hex.substring(1);
+}
+hex=hex.toUpperCase();
+if(hex.replace(new RegExp("["+_3ab+"]","g"),"")!=""){
+return null;
+}
+if(hex.length==3){
+rgb[0]=hex.charAt(0)+hex.charAt(0);
+rgb[1]=hex.charAt(1)+hex.charAt(1);
+rgb[2]=hex.charAt(2)+hex.charAt(2);
+}else{
+rgb[0]=hex.substring(0,2);
+rgb[1]=hex.substring(2,4);
+rgb[2]=hex.substring(4);
+}
+for(var i=0;i<rgb.length;i++){
+rgb[i]=_3ab.indexOf(rgb[i].charAt(0))*16+_3ab.indexOf(rgb[i].charAt(1));
+}
+return rgb;
+};
+dojo.gfx.color.rgb2hex=function(r,g,b){
+if(dojo.lang.isArray(r)){
+g=r[1]||0;
+b=r[2]||0;
+r=r[0]||0;
+}
+var ret=dojo.lang.map([r,g,b],function(x){
+x=new Number(x);
+var s=x.toString(16);
+while(s.length<2){
+s="0"+s;
+}
+return s;
+});
+ret.unshift("#");
+return ret.join("");
+};
+dojo.provide("dojo.lfx.Animation");
+dojo.lfx.Line=function(_3b4,end){
+this.start=_3b4;
+this.end=end;
+if(dojo.lang.isArray(_3b4)){
+var diff=[];
+dojo.lang.forEach(this.start,function(s,i){
+diff[i]=this.end[i]-s;
+},this);
+this.getValue=function(n){
+var res=[];
+dojo.lang.forEach(this.start,function(s,i){
+res[i]=(diff[i]*n)+s;
+},this);
+return res;
+};
+}else{
+var diff=end-_3b4;
+this.getValue=function(n){
+return (diff*n)+this.start;
+};
+}
+};
+dojo.lfx.easeDefault=function(n){
+if(dojo.render.html.khtml){
+return (parseFloat("0.5")+((Math.sin((n+parseFloat("1.5"))*Math.PI))/2));
+}else{
+return (0.5+((Math.sin((n+1.5)*Math.PI))/2));
+}
+};
+dojo.lfx.easeIn=function(n){
+return Math.pow(n,3);
+};
+dojo.lfx.easeOut=function(n){
+return (1-Math.pow(1-n,3));
+};
+dojo.lfx.easeInOut=function(n){
+return ((3*Math.pow(n,2))-(2*Math.pow(n,3)));
+};
+dojo.lfx.IAnimation=function(){
+};
+dojo.lang.extend(dojo.lfx.IAnimation,{curve:null,duration:1000,easing:null,repeatCount:0,rate:25,handler:null,beforeBegin:null,onBegin:null,onAnimate:null,onEnd:null,onPlay:null,onPause:null,onStop:null,play:null,pause:null,stop:null,connect:function(evt,_3c3,_3c4){
+if(!_3c4){
+_3c4=_3c3;
+_3c3=this;
+}
+_3c4=dojo.lang.hitch(_3c3,_3c4);
+var _3c5=this[evt]||function(){
+};
+this[evt]=function(){
+var ret=_3c5.apply(this,arguments);
+_3c4.apply(this,arguments);
+return ret;
+};
+return this;
+},fire:function(evt,args){
+if(this[evt]){
+this[evt].apply(this,(args||[]));
+}
+return this;
+},repeat:function(_3c9){
+this.repeatCount=_3c9;
+return this;
+},_active:false,_paused:false});
+dojo.lfx.Animation=function(_3ca,_3cb,_3cc,_3cd,_3ce,rate){
+dojo.lfx.IAnimation.call(this);
+if(dojo.lang.isNumber(_3ca)||(!_3ca&&_3cb.getValue)){
+rate=_3ce;
+_3ce=_3cd;
+_3cd=_3cc;
+_3cc=_3cb;
+_3cb=_3ca;
+_3ca=null;
+}else{
+if(_3ca.getValue||dojo.lang.isArray(_3ca)){
+rate=_3cd;
+_3ce=_3cc;
+_3cd=_3cb;
+_3cc=_3ca;
+_3cb=null;
+_3ca=null;
+}
+}
+if(dojo.lang.isArray(_3cc)){
+this.curve=new dojo.lfx.Line(_3cc[0],_3cc[1]);
+}else{
+this.curve=_3cc;
+}
+if(_3cb!=null&&_3cb>0){
+this.duration=_3cb;
+}
+if(_3ce){
+this.repeatCount=_3ce;
+}
+if(rate){
+this.rate=rate;
+}
+if(_3ca){
+dojo.lang.forEach(["handler","beforeBegin","onBegin","onEnd","onPlay","onStop","onAnimate"],function(item){
+if(_3ca[item]){
+this.connect(item,_3ca[item]);
+}
+},this);
+}
+if(_3cd&&dojo.lang.isFunction(_3cd)){
+this.easing=_3cd;
+}
+};
+dojo.inherits(dojo.lfx.Animation,dojo.lfx.IAnimation);
+dojo.lang.extend(dojo.lfx.Animation,{_startTime:null,_endTime:null,_timer:null,_percent:0,_startRepeatCount:0,play:function(_3d1,_3d2){
+if(_3d2){
+clearTimeout(this._timer);
+this._active=false;
+this._paused=false;
+this._percent=0;
+}else{
+if(this._active&&!this._paused){
+return this;
+}
+}
+this.fire("handler",["beforeBegin"]);
+this.fire("beforeBegin");
+if(_3d1>0){
+setTimeout(dojo.lang.hitch(this,function(){
+this.play(null,_3d2);
+}),_3d1);
+return this;
+}
+this._startTime=new Date().valueOf();
+if(this._paused){
+this._startTime-=(this.duration*this._percent/100);
+}
+this._endTime=this._startTime+this.duration;
+this._active=true;
+this._paused=false;
+var step=this._percent/100;
+var _3d4=this.curve.getValue(step);
+if(this._percent==0){
+if(!this._startRepeatCount){
+this._startRepeatCount=this.repeatCount;
+}
+this.fire("handler",["begin",_3d4]);
+this.fire("onBegin",[_3d4]);
+}
+this.fire("handler",["play",_3d4]);
+this.fire("onPlay",[_3d4]);
+this._cycle();
+return this;
+},pause:function(){
+clearTimeout(this._timer);
+if(!this._active){
+return this;
+}
+this._paused=true;
+var _3d5=this.curve.getValue(this._percent/100);
+this.fire("handler",["pause",_3d5]);
+this.fire("onPause",[_3d5]);
+return this;
+},gotoPercent:function(pct,_3d7){
+clearTimeout(this._timer);
+this._active=true;
+this._paused=true;
+this._percent=pct;
+if(_3d7){
+this.play();
+}
+return this;
+},stop:function(_3d8){
+clearTimeout(this._timer);
+var step=this._percent/100;
+if(_3d8){
+step=1;
+}
+var _3da=this.curve.getValue(step);
+this.fire("handler",["stop",_3da]);
+this.fire("onStop",[_3da]);
+this._active=false;
+this._paused=false;
+return this;
+},status:function(){
+if(this._active){
+return this._paused?"paused":"playing";
+}else{
+return "stopped";
+}
+return this;
+},_cycle:function(){
+clearTimeout(this._timer);
+if(this._active){
+var curr=new Date().valueOf();
+var step=(curr-this._startTime)/(this._endTime-this._startTime);
+if(step>=1){
+step=1;
+this._percent=100;
+}else{
+this._percent=step*100;
+}
+if((this.easing)&&(dojo.lang.isFunction(this.easing))){
+step=this.easing(step);
+}
+var _3dd=this.curve.getValue(step);
+this.fire("handler",["animate",_3dd]);
+this.fire("onAnimate",[_3dd]);
+if(step<1){
+this._timer=setTimeout(dojo.lang.hitch(this,"_cycle"),this.rate);
+}else{
+this._active=false;
+this.fire("handler",["end"]);
+this.fire("onEnd");
+if(this.repeatCount>0){
+this.repeatCount--;
+this.play(null,true);
+}else{
+if(this.repeatCount==-1){
+this.play(null,true);
+}else{
+if(this._startRepeatCount){
+this.repeatCount=this._startRepeatCount;
+this._startRepeatCount=0;
+}
+}
+}
+}
+}
+return this;
+}});
+dojo.lfx.Combine=function(_3de){
+dojo.lfx.IAnimation.call(this);
+this._anims=[];
+this._animsEnded=0;
+var _3df=arguments;
+if(_3df.length==1&&(dojo.lang.isArray(_3df[0])||dojo.lang.isArrayLike(_3df[0]))){
+_3df=_3df[0];
+}
+dojo.lang.forEach(_3df,function(anim){
+this._anims.push(anim);
+anim.connect("onEnd",dojo.lang.hitch(this,"_onAnimsEnded"));
+},this);
+};
+dojo.inherits(dojo.lfx.Combine,dojo.lfx.IAnimation);
+dojo.lang.extend(dojo.lfx.Combine,{_animsEnded:0,play:function(_3e1,_3e2){
+if(!this._anims.length){
+return this;
+}
+this.fire("beforeBegin");
+if(_3e1>0){
+setTimeout(dojo.lang.hitch(this,function(){
+this.play(null,_3e2);
+}),_3e1);
+return this;
+}
+if(_3e2||this._anims[0].percent==0){
+this.fire("onBegin");
+}
+this.fire("onPlay");
+this._animsCall("play",null,_3e2);
+return this;
+},pause:function(){
+this.fire("onPause");
+this._animsCall("pause");
+return this;
+},stop:function(_3e3){
+this.fire("onStop");
+this._animsCall("stop",_3e3);
+return this;
+},_onAnimsEnded:function(){
+this._animsEnded++;
+if(this._animsEnded>=this._anims.length){
+this.fire("onEnd");
+}
+return this;
+},_animsCall:function(_3e4){
+var args=[];
+if(arguments.length>1){
+for(var i=1;i<arguments.length;i++){
+args.push(arguments[i]);
+}
+}
+var _3e7=this;
+dojo.lang.forEach(this._anims,function(anim){
+anim[_3e4](args);
+},_3e7);
+return this;
+}});
+dojo.lfx.Chain=function(_3e9){
+dojo.lfx.IAnimation.call(this);
+this._anims=[];
+this._currAnim=-1;
+var _3ea=arguments;
+if(_3ea.length==1&&(dojo.lang.isArray(_3ea[0])||dojo.lang.isArrayLike(_3ea[0]))){
+_3ea=_3ea[0];
+}
+var _3eb=this;
+dojo.lang.forEach(_3ea,function(anim,i,_3ee){
+this._anims.push(anim);
+if(i<_3ee.length-1){
+anim.connect("onEnd",dojo.lang.hitch(this,"_playNext"));
+}else{
+anim.connect("onEnd",dojo.lang.hitch(this,function(){
+this.fire("onEnd");
+}));
+}
+},this);
+};
+dojo.inherits(dojo.lfx.Chain,dojo.lfx.IAnimation);
+dojo.lang.extend(dojo.lfx.Chain,{_currAnim:-1,play:function(_3ef,_3f0){
+if(!this._anims.length){
+return this;
+}
+if(_3f0||!this._anims[this._currAnim]){
+this._currAnim=0;
+}
+var _3f1=this._anims[this._currAnim];
+this.fire("beforeBegin");
+if(_3ef>0){
+setTimeout(dojo.lang.hitch(this,function(){
+this.play(null,_3f0);
+}),_3ef);
+return this;
+}
+if(_3f1){
+if(this._currAnim==0){
+this.fire("handler",["begin",this._currAnim]);
+this.fire("onBegin",[this._currAnim]);
+}
+this.fire("onPlay",[this._currAnim]);
+_3f1.play(null,_3f0);
+}
+return this;
+},pause:function(){
+if(this._anims[this._currAnim]){
+this._anims[this._currAnim].pause();
+this.fire("onPause",[this._currAnim]);
+}
+return this;
+},playPause:function(){
+if(this._anims.length==0){
+return this;
+}
+if(this._currAnim==-1){
+this._currAnim=0;
+}
+var _3f2=this._anims[this._currAnim];
+if(_3f2){
+if(!_3f2._active||_3f2._paused){
+this.play();
+}else{
+this.pause();
+}
+}
+return this;
+},stop:function(){
+var _3f3=this._anims[this._currAnim];
+if(_3f3){
+_3f3.stop();
+this.fire("onStop",[this._currAnim]);
+}
+return _3f3;
+},_playNext:function(){
+if(this._currAnim==-1||this._anims.length==0){
+return this;
+}
+this._currAnim++;
+if(this._anims[this._currAnim]){
+this._anims[this._currAnim].play(null,true);
+}
+return this;
+}});
+dojo.lfx.combine=function(_3f4){
+var _3f5=arguments;
+if(dojo.lang.isArray(arguments[0])){
+_3f5=arguments[0];
+}
+if(_3f5.length==1){
+return _3f5[0];
+}
+return new dojo.lfx.Combine(_3f5);
+};
+dojo.lfx.chain=function(_3f6){
+var _3f7=arguments;
+if(dojo.lang.isArray(arguments[0])){
+_3f7=arguments[0];
+}
+if(_3f7.length==1){
+return _3f7[0];
+}
+return new dojo.lfx.Chain(_3f7);
+};
+dojo.provide("dojo.uri.Uri");
+dojo.uri=new function(){
+this.dojoUri=function(uri){
+return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(),uri);
+};
+this.moduleUri=function(_3f9,uri){
+var loc=dojo.hostenv.getModulePrefix(_3f9);
+if(!loc){
+return null;
+}
+if(loc.lastIndexOf("/")!=loc.length-1){
+loc+="/";
+}
+return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri()+loc,uri);
+};
+this.Uri=function(){
+var uri=arguments[0];
+for(var i=1;i<arguments.length;i++){
+if(!arguments[i]){
+continue;
+}
+var _3fe=new dojo.uri.Uri(arguments[i].toString());
+var _3ff=new dojo.uri.Uri(uri.toString());
+if((_3fe.path=="")&&(_3fe.scheme==null)&&(_3fe.authority==null)&&(_3fe.query==null)){
+if(_3fe.fragment!=null){
+_3ff.fragment=_3fe.fragment;
+}
+_3fe=_3ff;
+}else{
+if(_3fe.scheme==null){
+_3fe.scheme=_3ff.scheme;
+if(_3fe.authority==null){
+_3fe.authority=_3ff.authority;
+if(_3fe.path.charAt(0)!="/"){
+var path=_3ff.path.substring(0,_3ff.path.lastIndexOf("/")+1)+_3fe.path;
+var segs=path.split("/");
+for(var j=0;j<segs.length;j++){
+if(segs[j]=="."){
+if(j==segs.length-1){
+segs[j]="";
+}else{
+segs.splice(j,1);
+j--;
+}
+}else{
+if(j>0&&!(j==1&&segs[0]=="")&&segs[j]==".."&&segs[j-1]!=".."){
+if(j==segs.length-1){
+segs.splice(j,1);
+segs[j-1]="";
+}else{
+segs.splice(j-1,2);
+j-=2;
+}
+}
+}
+}
+_3fe.path=segs.join("/");
+}
+}
+}
+}
+uri="";
+if(_3fe.scheme!=null){
+uri+=_3fe.scheme+":";
+}
+if(_3fe.authority!=null){
+uri+="//"+_3fe.authority;
+}
+uri+=_3fe.path;
+if(_3fe.query!=null){
+uri+="?"+_3fe.query;
+}
+if(_3fe.fragment!=null){
+uri+="#"+_3fe.fragment;
+}
+}
+this.uri=uri.toString();
+var _403="^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
+var r=this.uri.match(new RegExp(_403));
+this.scheme=r[2]||(r[1]?"":null);
+this.authority=r[4]||(r[3]?"":null);
+this.path=r[5];
+this.query=r[7]||(r[6]?"":null);
+this.fragment=r[9]||(r[8]?"":null);
+if(this.authority!=null){
+_403="^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$";
+r=this.authority.match(new RegExp(_403));
+this.user=r[3]||null;
+this.password=r[4]||null;
+this.host=r[5];
+this.port=r[7]||null;
+}
+this.toString=function(){
+return this.uri;
+};
+};
+};
+dojo.provide("dojo.html.style");
+dojo.html.getClass=function(node){
+node=dojo.byId(node);
+if(!node){
+return "";
+}
+var cs="";
+if(node.className){
+cs=node.className;
+}else{
+if(dojo.html.hasAttribute(node,"class")){
+cs=dojo.html.getAttribute(node,"class");
+}
+}
+return cs.replace(/^\s+|\s+$/g,"");
+};
+dojo.html.getClasses=function(node){
+var c=dojo.html.getClass(node);
+return (c=="")?[]:c.split(/\s+/g);
+};
+dojo.html.hasClass=function(node,_40a){
+return (new RegExp("(^|\\s+)"+_40a+"(\\s+|$)")).test(dojo.html.getClass(node));
+};
+dojo.html.prependClass=function(node,_40c){
+_40c+=" "+dojo.html.getClass(node);
+return dojo.html.setClass(node,_40c);
+};
+dojo.html.addClass=function(node,_40e){
+if(dojo.html.hasClass(node,_40e)){
+return false;
+}
+_40e=(dojo.html.getClass(node)+" "+_40e).replace(/^\s+|\s+$/g,"");
+return dojo.html.setClass(node,_40e);
+};
+dojo.html.setClass=function(node,_410){
+node=dojo.byId(node);
+var cs=new String(_410);
+try{
+if(typeof node.className=="string"){
+node.className=cs;
+}else{
+if(node.setAttribute){
+node.setAttribute("class",_410);
+node.className=cs;
+}else{
+return false;
+}
+}
+}
+catch(e){
+dojo.debug("dojo.html.setClass() failed",e);
+}
+return true;
+};
+dojo.html.removeClass=function(node,_413,_414){
+try{
+if(!_414){
+var _415=dojo.html.getClass(node).replace(new RegExp("(^|\\s+)"+_413+"(\\s+|$)"),"$1$2");
+}else{
+var _415=dojo.html.getClass(node).replace(_413,"");
+}
+dojo.html.setClass(node,_415);
+}
+catch(e){
+dojo.debug("dojo.html.removeClass() failed",e);
+}
+return true;
+};
+dojo.html.replaceClass=function(node,_417,_418){
+dojo.html.removeClass(node,_418);
+dojo.html.addClass(node,_417);
+};
+dojo.html.classMatchType={ContainsAll:0,ContainsAny:1,IsOnly:2};
+dojo.html.getElementsByClass=function(_419,_41a,_41b,_41c,_41d){
+_41d=false;
+var _41e=dojo.doc();
+_41a=dojo.byId(_41a)||_41e;
+var _41f=_419.split(/\s+/g);
+var _420=[];
+if(_41c!=1&&_41c!=2){
+_41c=0;
+}
+var _421=new RegExp("(\\s|^)(("+_41f.join(")|(")+"))(\\s|$)");
+var _422=_41f.join(" ").length;
+var _423=[];
+if(!_41d&&_41e.evaluate){
+var _424=".//"+(_41b||"*")+"[contains(";
+if(_41c!=dojo.html.classMatchType.ContainsAny){
+_424+="concat(' ', at class,' '), ' "+_41f.join(" ') and contains(concat(' ', at class,' '), ' ")+" ')";
+if(_41c==2){
+_424+=" and string-length(@class)="+_422+"]";
+}else{
+_424+="]";
+}
+}else{
+_424+="concat(' ', at class,' '), ' "+_41f.join(" ') or contains(concat(' ', at class,' '), ' ")+" ')]";
+}
+var _425=_41e.evaluate(_424,_41a,null,XPathResult.ANY_TYPE,null);
+var _426=_425.iterateNext();
+while(_426){
+try{
+_423.push(_426);
+_426=_425.iterateNext();
+}
+catch(e){
+break;
+}
+}
+return _423;
+}else{
+if(!_41b){
+_41b="*";
+}
+_423=_41a.getElementsByTagName(_41b);
+var node,i=0;
+outer:
+while(node=_423[i++]){
+var _429=dojo.html.getClasses(node);
+if(_429.length==0){
+continue outer;
+}
+var _42a=0;
+for(var j=0;j<_429.length;j++){
+if(_421.test(_429[j])){
+if(_41c==dojo.html.classMatchType.ContainsAny){
+_420.push(node);
+continue outer;
+}else{
+_42a++;
+}
+}else{
+if(_41c==dojo.html.classMatchType.IsOnly){
+continue outer;
+}
+}
+}
+if(_42a==_41f.length){
+if((_41c==dojo.html.classMatchType.IsOnly)&&(_42a==_429.length)){
+_420.push(node);
+}else{
+if(_41c==dojo.html.classMatchType.ContainsAll){
+_420.push(node);
+}
+}
+}
+}
+return _420;
+}
+};
+dojo.html.getElementsByClassName=dojo.html.getElementsByClass;
+dojo.html.toCamelCase=function(_42c){
+var arr=_42c.split("-"),cc=arr[0];
+for(var i=1;i<arr.length;i++){
+cc+=arr[i].charAt(0).toUpperCase()+arr[i].substring(1);
+}
+return cc;
+};
+dojo.html.toSelectorCase=function(_430){
+return _430.replace(/([A-Z])/g,"-$1").toLowerCase();
+};
+dojo.html.getComputedStyle=function(node,_432,_433){
+node=dojo.byId(node);
+var _432=dojo.html.toSelectorCase(_432);
+var _434=dojo.html.toCamelCase(_432);
+if(!node||!node.style){
+return _433;
+}else{
+if(document.defaultView&&dojo.html.isDescendantOf(node,node.ownerDocument)){
+try{
+var cs=document.defaultView.getComputedStyle(node,"");
+if(cs){
+return cs.getPropertyValue(_432);
+}
+}
+catch(e){
+if(node.style.getPropertyValue){
+return node.style.getPropertyValue(_432);
+}else{
+return _433;
+}
+}
+}else{
+if(node.currentStyle){
+return node.currentStyle[_434];
+}
+}
+}
+if(node.style.getPropertyValue){
+return node.style.getPropertyValue(_432);
+}else{
+return _433;
+}
+};
+dojo.html.getStyleProperty=function(node,_437){
+node=dojo.byId(node);
+return (node&&node.style?node.style[dojo.html.toCamelCase(_437)]:undefined);
+};
+dojo.html.getStyle=function(node,_439){
+var _43a=dojo.html.getStyleProperty(node,_439);
+return (_43a?_43a:dojo.html.getComputedStyle(node,_439));
+};
+dojo.html.setStyle=function(node,_43c,_43d){
+node=dojo.byId(node);
+if(node&&node.style){
+var _43e=dojo.html.toCamelCase(_43c);
+node.style[_43e]=_43d;
+}
+};
+dojo.html.setStyleText=function(_43f,text){
+try{
+_43f.style.cssText=text;
+}
+catch(e){
+_43f.setAttribute("style",text);
+}
+};
+dojo.html.copyStyle=function(_441,_442){
+if(!_442.style.cssText){
+_441.setAttribute("style",_442.getAttribute("style"));
+}else{
+_441.style.cssText=_442.style.cssText;
+}
+dojo.html.addClass(_441,dojo.html.getClass(_442));
+};
+dojo.html.getUnitValue=function(node,_444,_445){
+var s=dojo.html.getComputedStyle(node,_444);
+if((!s)||((s=="auto")&&(_445))){
+return {value:0,units:"px"};
+}
+var _447=s.match(/(\-?[\d.]+)([a-z%]*)/i);
+if(!_447){
+return dojo.html.getUnitValue.bad;
+}
+return {value:Number(_447[1]),units:_447[2].toLowerCase()};
+};
+dojo.html.getUnitValue.bad={value:NaN,units:""};
+dojo.html.getPixelValue=function(node,_449,_44a){
+var _44b=dojo.html.getUnitValue(node,_449,_44a);
+if(isNaN(_44b.value)){
+return 0;
+}
+if((_44b.value)&&(_44b.units!="px")){
+return NaN;
+}
+return _44b.value;
+};
+dojo.html.setPositivePixelValue=function(node,_44d,_44e){
+if(isNaN(_44e)){
+return false;
+}
+node.style[_44d]=Math.max(0,_44e)+"px";
+return true;
+};
+dojo.html.styleSheet=null;
+dojo.html.insertCssRule=function(_44f,_450,_451){
+if(!dojo.html.styleSheet){
+if(document.createStyleSheet){
+dojo.html.styleSheet=document.createStyleSheet();
+}else{
+if(document.styleSheets[0]){
+dojo.html.styleSheet=document.styleSheets[0];
+}else{
+return null;
+}
+}
+}
+if(arguments.length<3){
+if(dojo.html.styleSheet.cssRules){
+_451=dojo.html.styleSheet.cssRules.length;
+}else{
+if(dojo.html.styleSheet.rules){
+_451=dojo.html.styleSheet.rules.length;
+}else{
+return null;
+}
+}
+}
+if(dojo.html.styleSheet.insertRule){
+var rule=_44f+" { "+_450+" }";
+return dojo.html.styleSheet.insertRule(rule,_451);
+}else{
+if(dojo.html.styleSheet.addRule){
+return dojo.html.styleSheet.addRule(_44f,_450,_451);
+}else{
+return null;
+}
+}
+};
+dojo.html.removeCssRule=function(_453){
+if(!dojo.html.styleSheet){
+dojo.debug("no stylesheet defined for removing rules");
+return false;
+}
+if(dojo.render.html.ie){
+if(!_453){
+_453=dojo.html.styleSheet.rules.length;
+dojo.html.styleSheet.removeRule(_453);
+}
+}else{
+if(document.styleSheets[0]){
+if(!_453){
+_453=dojo.html.styleSheet.cssRules.length;
+}
+dojo.html.styleSheet.deleteRule(_453);
+}
+}
+return true;
+};
+dojo.html._insertedCssFiles=[];
+dojo.html.insertCssFile=function(URI,doc,_456,_457){
+if(!URI){
+return;
+}
+if(!doc){
+doc=document;
+}
+var _458=dojo.hostenv.getText(URI,false,_457);
+if(_458===null){
+return;
+}
+_458=dojo.html.fixPathsInCssText(_458,URI);
+if(_456){
+var idx=-1,node,ent=dojo.html._insertedCssFiles;
+for(var i=0;i<ent.length;i++){
+if((ent[i].doc==doc)&&(ent[i].cssText==_458)){
+idx=i;
+node=ent[i].nodeRef;
+break;
+}
+}
+if(node){
+var _45d=doc.getElementsByTagName("style");
+for(var i=0;i<_45d.length;i++){
+if(_45d[i]==node){
+return;
+}
+}
+dojo.html._insertedCssFiles.shift(idx,1);
+}
+}
+var _45e=dojo.html.insertCssText(_458);
+dojo.html._insertedCssFiles.push({"doc":doc,"cssText":_458,"nodeRef":_45e});
+if(_45e&&djConfig.isDebug){
+_45e.setAttribute("dbgHref",URI);
+}
+return _45e;
+};
+dojo.html.insertCssText=function(_45f,doc,URI){
+if(!_45f){
+return;
+}
+if(!doc){
+doc=document;
+}
+if(URI){
+_45f=dojo.html.fixPathsInCssText(_45f,URI);
+}
+var _462=doc.createElement("style");
+_462.setAttribute("type","text/css");
+var head=doc.getElementsByTagName("head")[0];
+if(!head){
+dojo.debug("No head tag in document, aborting styles");
+return;
+}else{
+head.appendChild(_462);
+}
+if(_462.styleSheet){
+_462.styleSheet.cssText=_45f;
+}else{
+var _464=doc.createTextNode(_45f);
+_462.appendChild(_464);
+}
+return _462;
+};
+dojo.html.fixPathsInCssText=function(_465,URI){
+function iefixPathsInCssText(){
+var _467=/AlphaImageLoader\(src\=['"]([\t\s\w()\/.\\'"-:#=&?~]*)['"]/;
+while(_468=_467.exec(_465)){
+url=_468[1].replace(_46a,"$2");
+if(!_46b.exec(url)){
+url=(new dojo.uri.Uri(URI,url).toString());
+}
+str+=_465.substring(0,_468.index)+"AlphaImageLoader(src='"+url+"'";
+_465=_465.substr(_468.index+_468[0].length);
+}
+return str+_465;
+}
+if(!_465||!URI){
+return;
+}
+var _468,str="",url="";
+var _46d=/url\(\s*([\t\s\w()\/.\\'"-:#=&?]+)\s*\)/;
+var _46b=/(file|https?|ftps?):\/\//;
+var _46a=/^[\s]*(['"]?)([\w()\/.\\'"-:#=&?]*)\1[\s]*?$/;
+if(dojo.render.html.ie55||dojo.render.html.ie60){
+_465=iefixPathsInCssText();
+}
+while(_468=_46d.exec(_465)){
+url=_468[1].replace(_46a,"$2");
+if(!_46b.exec(url)){
+url=(new dojo.uri.Uri(URI,url).toString());
+}
+str+=_465.substring(0,_468.index)+"url("+url+")";
+_465=_465.substr(_468.index+_468[0].length);
+}
+return str+_465;
+};
+dojo.html.setActiveStyleSheet=function(_46e){
+var i=0,a,els=dojo.doc().getElementsByTagName("link");
+while(a=els[i++]){
+if(a.getAttribute("rel").indexOf("style")!=-1&&a.getAttribute("title")){
+a.disabled=true;
+if(a.getAttribute("title")==_46e){
+a.disabled=false;
+}
+}
+}
+};
+dojo.html.getActiveStyleSheet=function(){
+var i=0,a,els=dojo.doc().getElementsByTagName("link");
+while(a=els[i++]){
+if(a.getAttribute("rel").indexOf("style")!=-1&&a.getAttribute("title")&&!a.disabled){
+return a.getAttribute("title");
+}
+}
+return null;
+};
+dojo.html.getPreferredStyleSheet=function(){
+var i=0,a,els=dojo.doc().getElementsByTagName("link");
+while(a=els[i++]){
+if(a.getAttribute("rel").indexOf("style")!=-1&&a.getAttribute("rel").indexOf("alt")==-1&&a.getAttribute("title")){
+return a.getAttribute("title");
+}
+}
+return null;
+};
+dojo.html.applyBrowserClass=function(node){
+var drh=dojo.render.html;
+var _47a={dj_ie:drh.ie,dj_ie55:drh.ie55,dj_ie6:drh.ie60,dj_ie7:drh.ie70,dj_iequirks:drh.ie&&drh.quirks,dj_opera:drh.opera,dj_opera8:drh.opera&&(Math.floor(dojo.render.version)==8),dj_opera9:drh.opera&&(Math.floor(dojo.render.version)==9),dj_khtml:drh.khtml,dj_safari:drh.safari,dj_gecko:drh.mozilla};
+for(var p in _47a){
+if(_47a[p]){
+dojo.html.addClass(node,p);
+}
+}
+};
+dojo.provide("dojo.html.display");
+dojo.html._toggle=function(node,_47d,_47e){
+node=dojo.byId(node);
+_47e(node,!_47d(node));
+return _47d(node);
+};
+dojo.html.show=function(node){
+node=dojo.byId(node);
+if(dojo.html.getStyleProperty(node,"display")=="none"){
+dojo.html.setStyle(node,"display",(node.dojoDisplayCache||""));
+node.dojoDisplayCache=undefined;
+}
+};
+dojo.html.hide=function(node){
+node=dojo.byId(node);
+if(typeof node["dojoDisplayCache"]=="undefined"){
+var d=dojo.html.getStyleProperty(node,"display");
+if(d!="none"){
+node.dojoDisplayCache=d;
+}
+}
+dojo.html.setStyle(node,"display","none");
+};
+dojo.html.setShowing=function(node,_483){
+dojo.html[(_483?"show":"hide")](node);
+};
+dojo.html.isShowing=function(node){
+return (dojo.html.getStyleProperty(node,"display")!="none");
+};
+dojo.html.toggleShowing=function(node){
+return dojo.html._toggle(node,dojo.html.isShowing,dojo.html.setShowing);
+};
+dojo.html.displayMap={tr:"",td:"",th:"",img:"inline",span:"inline",input:"inline",button:"inline"};
+dojo.html.suggestDisplayByTagName=function(node){
+node=dojo.byId(node);
+if(node&&node.tagName){
+var tag=node.tagName.toLowerCase();
+return (tag in dojo.html.displayMap?dojo.html.displayMap[tag]:"block");
+}
+};
+dojo.html.setDisplay=function(node,_489){
+dojo.html.setStyle(node,"display",((_489 instanceof String||typeof _489=="string")?_489:(_489?dojo.html.suggestDisplayByTagName(node):"none")));
+};
+dojo.html.isDisplayed=function(node){
+return (dojo.html.getComputedStyle(node,"display")!="none");
+};
+dojo.html.toggleDisplay=function(node){
+return dojo.html._toggle(node,dojo.html.isDisplayed,dojo.html.setDisplay);
+};
+dojo.html.setVisibility=function(node,_48d){
+dojo.html.setStyle(node,"visibility",((_48d instanceof String||typeof _48d=="string")?_48d:(_48d?"visible":"hidden")));
+};
+dojo.html.isVisible=function(node){
+return (dojo.html.getComputedStyle(node,"visibility")!="hidden");
+};
+dojo.html.toggleVisibility=function(node){
+return dojo.html._toggle(node,dojo.html.isVisible,dojo.html.setVisibility);
+};
+dojo.html.setOpacity=function(node,_491,_492){
+node=dojo.byId(node);
+var h=dojo.render.html;
+if(!_492){
+if(_491>=1){
+if(h.ie){
+dojo.html.clearOpacity(node);
+return;
+}else{
+_491=0.999999;
+}
+}else{
+if(_491<0){
+_491=0;
+}
+}
+}
+if(h.ie){
+if(node.nodeName.toLowerCase()=="tr"){
+var tds=node.getElementsByTagName("td");
+for(var x=0;x<tds.length;x++){
+tds[x].style.filter="Alpha(Opacity="+_491*100+")";
+}
+}
+node.style.filter="Alpha(Opacity="+_491*100+")";
+}else{
+if(h.moz){
+node.style.opacity=_491;
+node.style.MozOpacity=_491;
+}else{
+if(h.safari){
+node.style.opacity=_491;
+node.style.KhtmlOpacity=_491;
+}else{
+node.style.opacity=_491;
+}
+}
+}
+};
+dojo.html.clearOpacity=function(node){
+node=dojo.byId(node);
+var ns=node.style;
+var h=dojo.render.html;
+if(h.ie){
+try{
+if(node.filters&&node.filters.alpha){
+ns.filter="";
+}
+}
+catch(e){
+}
+}else{
+if(h.moz){
+ns.opacity=1;
+ns.MozOpacity=1;
+}else{
+if(h.safari){
+ns.opacity=1;
+ns.KhtmlOpacity=1;
+}else{
+ns.opacity=1;
+}
+}
+}
+};
+dojo.html.getOpacity=function(node){
+node=dojo.byId(node);
+var h=dojo.render.html;
+if(h.ie){
+var opac=(node.filters&&node.filters.alpha&&typeof node.filters.alpha.opacity=="number"?node.filters.alpha.opacity:100)/100;
+}else{
+var opac=node.style.opacity||node.style.MozOpacity||node.style.KhtmlOpacity||1;
+}
+return opac>=0.999999?1:Number(opac);
+};
+dojo.provide("dojo.html.color");
+dojo.html.getBackgroundColor=function(node){
+node=dojo.byId(node);
+var _49d;
+do{
+_49d=dojo.html.getStyle(node,"background-color");
+if(_49d.toLowerCase()=="rgba(0, 0, 0, 0)"){
+_49d="transparent";
+}
+if(node==document.getElementsByTagName("body")[0]){
+node=null;
+break;
+}
+node=node.parentNode;
+}while(node&&dojo.lang.inArray(["transparent",""],_49d));
+if(_49d=="transparent"){
+_49d=[255,255,255,0];
+}else{
+_49d=dojo.gfx.color.extractRGB(_49d);
+}
+return _49d;
+};
+dojo.provide("dojo.html.common");
+dojo.lang.mixin(dojo.html,dojo.dom);
+dojo.html.body=function(){
+dojo.deprecated("dojo.html.body() moved to dojo.body()","0.5");
+return dojo.body();
+};
+dojo.html.getEventTarget=function(evt){
+if(!evt){
+evt=dojo.global().event||{};
+}
+var t=(evt.srcElement?evt.srcElement:(evt.target?evt.target:null));
+while((t)&&(t.nodeType!=1)){
+t=t.parentNode;
+}
+return t;
+};
+dojo.html.getViewport=function(){
+var _4a0=dojo.global();
+var _4a1=dojo.doc();
+var w=0;
+var h=0;
+if(dojo.render.html.mozilla){
+w=_4a1.documentElement.clientWidth;
+h=_4a0.innerHeight;
+}else{
+if(!dojo.render.html.opera&&_4a0.innerWidth){
+w=_4a0.innerWidth;
+h=_4a0.innerHeight;
+}else{
+if(!dojo.render.html.opera&&dojo.exists(_4a1,"documentElement.clientWidth")){
+var w2=_4a1.documentElement.clientWidth;
+if(!w||w2&&w2<w){
+w=w2;
+}
+h=_4a1.documentElement.clientHeight;
+}else{
+if(dojo.body().clientWidth){
+w=dojo.body().clientWidth;
+h=dojo.body().clientHeight;
+}
+}
+}
+}
+return {width:w,height:h};
+};
+dojo.html.getScroll=function(){
+var _4a5=dojo.global();
+var _4a6=dojo.doc();
+var top=_4a5.pageYOffset||_4a6.documentElement.scrollTop||dojo.body().scrollTop||0;
+var left=_4a5.pageXOffset||_4a6.documentElement.scrollLeft||dojo.body().scrollLeft||0;
+return {top:top,left:left,offset:{x:left,y:top}};
+};
+dojo.html.getParentByType=function(node,type){
+var _4ab=dojo.doc();
+var _4ac=dojo.byId(node);
+type=type.toLowerCase();
+while((_4ac)&&(_4ac.nodeName.toLowerCase()!=type)){
+if(_4ac==(_4ab["body"]||_4ab["documentElement"])){
+return null;
+}
+_4ac=_4ac.parentNode;
+}
+return _4ac;
+};
+dojo.html.getAttribute=function(node,attr){
+node=dojo.byId(node);
+if((!node)||(!node.getAttribute)){
+return null;
+}
+var ta=typeof attr=="string"?attr:new String(attr);
+var v=node.getAttribute(ta.toUpperCase());
+if((v)&&(typeof v=="string")&&(v!="")){
+return v;
+}
+if(v&&v.value){
+return v.value;
+}
+if((node.getAttributeNode)&&(node.getAttributeNode(ta))){
+return (node.getAttributeNode(ta)).value;
+}else{
+if(node.getAttribute(ta)){
+return node.getAttribute(ta);
+}else{
+if(node.getAttribute(ta.toLowerCase())){
+return node.getAttribute(ta.toLowerCase());
+}
+}
+}
+return null;
+};
+dojo.html.hasAttribute=function(node,attr){
+return dojo.html.getAttribute(dojo.byId(node),attr)?true:false;
+};
+dojo.html.getCursorPosition=function(e){
+e=e||dojo.global().event;
+var _4b4={x:0,y:0};
+if(e.pageX||e.pageY){
+_4b4.x=e.pageX;
+_4b4.y=e.pageY;
+}else{
+var de=dojo.doc().documentElement;
+var db=dojo.body();
+_4b4.x=e.clientX+((de||db)["scrollLeft"])-((de||db)["clientLeft"]);
+_4b4.y=e.clientY+((de||db)["scrollTop"])-((de||db)["clientTop"]);
+}
+return _4b4;
+};
+dojo.html.isTag=function(node){
+node=dojo.byId(node);
+if(node&&node.tagName){
+for(var i=1;i<arguments.length;i++){
+if(node.tagName.toLowerCase()==String(arguments[i]).toLowerCase()){
+return String(arguments[i]).toLowerCase();
+}
+}
+}
+return "";
+};
+if(dojo.render.html.ie&&!dojo.render.html.ie70){
+if(window.location.href.substr(0,6).toLowerCase()!="https:"){
+(function(){
+var _4b9=dojo.doc().createElement("script");
+_4b9.src="javascript:'dojo.html.createExternalElement=function(doc, tag){ return doc.createElement(tag); }'";
+dojo.doc().getElementsByTagName("head")[0].appendChild(_4b9);
+})();
+}
+}else{
+dojo.html.createExternalElement=function(doc,tag){
+return doc.createElement(tag);
+};
+}
+dojo.html._callDeprecated=function(_4bc,_4bd,args,_4bf,_4c0){
+dojo.deprecated("dojo.html."+_4bc,"replaced by dojo.html."+_4bd+"("+(_4bf?"node, {"+_4bf+": "+_4bf+"}":"")+")"+(_4c0?"."+_4c0:""),"0.5");
+var _4c1=[];
+if(_4bf){
+var _4c2={};
+_4c2[_4bf]=args[1];
+_4c1.push(args[0]);
+_4c1.push(_4c2);
+}else{
+_4c1=args;
+}
+var ret=dojo.html[_4bd].apply(dojo.html,args);
+if(_4c0){
+return ret[_4c0];
+}else{
+return ret;
+}
+};
+dojo.html.getViewportWidth=function(){
+return dojo.html._callDeprecated("getViewportWidth","getViewport",arguments,null,"width");
+};
+dojo.html.getViewportHeight=function(){
+return dojo.html._callDeprecated("getViewportHeight","getViewport",arguments,null,"height");
+};
+dojo.html.getViewportSize=function(){
+return dojo.html._callDeprecated("getViewportSize","getViewport",arguments);
+};
+dojo.html.getScrollTop=function(){
+return dojo.html._callDeprecated("getScrollTop","getScroll",arguments,null,"top");
+};
+dojo.html.getScrollLeft=function(){
+return dojo.html._callDeprecated("getScrollLeft","getScroll",arguments,null,"left");
+};
+dojo.html.getScrollOffset=function(){
+return dojo.html._callDeprecated("getScrollOffset","getScroll",arguments,null,"offset");
+};
+dojo.provide("dojo.html.layout");
+dojo.html.sumAncestorProperties=function(node,prop){
+node=dojo.byId(node);
+if(!node){
+return 0;
+}
+var _4c6=0;
+while(node){
+if(dojo.html.getComputedStyle(node,"position")=="fixed"){
+return 0;
+}
+var val=node[prop];
+if(val){
+_4c6+=val-0;
+if(node==dojo.body()){
+break;
+}
+}
+node=node.parentNode;
+}
+return _4c6;
+};
+dojo.html.setStyleAttributes=function(node,_4c9){
+node=dojo.byId(node);
+var _4ca=_4c9.replace(/(;)?\s*$/,"").split(";");
+for(var i=0;i<_4ca.length;i++){
+var _4cc=_4ca[i].split(":");
+var name=_4cc[0].replace(/\s*$/,"").replace(/^\s*/,"").toLowerCase();
+var _4ce=_4cc[1].replace(/\s*$/,"").replace(/^\s*/,"");
+switch(name){
+case "opacity":
+dojo.html.setOpacity(node,_4ce);
+break;
+case "content-height":
+dojo.html.setContentBox(node,{height:_4ce});
+break;
+case "content-width":
+dojo.html.setContentBox(node,{width:_4ce});
+break;
+case "outer-height":
+dojo.html.setMarginBox(node,{height:_4ce});
+break;
+case "outer-width":
+dojo.html.setMarginBox(node,{width:_4ce});
+break;
+default:
+node.style[dojo.html.toCamelCase(name)]=_4ce;
+}
+}
+};
+dojo.html.boxSizing={MARGIN_BOX:"margin-box",BORDER_BOX:"border-box",PADDING_BOX:"padding-box",CONTENT_BOX:"content-box"};
+dojo.html.getAbsolutePosition=dojo.html.abs=function(node,_4d0,_4d1){
+node=dojo.byId(node,node.ownerDocument);
+var ret={x:0,y:0};
+var bs=dojo.html.boxSizing;
+if(!_4d1){
+_4d1=bs.CONTENT_BOX;
+}
+var _4d4=2;
+var _4d5;
+switch(_4d1){
+case bs.MARGIN_BOX:
+_4d5=3;
+break;
+case bs.BORDER_BOX:
+_4d5=2;
+break;
+case bs.PADDING_BOX:
+default:
+_4d5=1;
+break;
+case bs.CONTENT_BOX:
+_4d5=0;
+break;
+}
+var h=dojo.render.html;
+var db=document["body"]||document["documentElement"];
+if(h.ie){
+with(node.getBoundingClientRect()){
+ret.x=left-2;
+ret.y=top-2;
+}
+}else{
+if(document.getBoxObjectFor){
+_4d4=1;
+try{
+var bo=document.getBoxObjectFor(node);
+ret.x=bo.x-dojo.html.sumAncestorProperties(node,"scrollLeft");
+ret.y=bo.y-dojo.html.sumAncestorProperties(node,"scrollTop");
+}
+catch(e){
+}
+}else{
+if(node["offsetParent"]){
+var _4d9;
+if((h.safari)&&(node.style.getPropertyValue("position")=="absolute")&&(node.parentNode==db)){
+_4d9=db;
+}else{
+_4d9=db.parentNode;
+}
+if(node.parentNode!=db){
+var nd=node;
+if(dojo.render.html.opera){
+nd=db;
+}
+ret.x-=dojo.html.sumAncestorProperties(nd,"scrollLeft");
+ret.y-=dojo.html.sumAncestorProperties(nd,"scrollTop");
+}
+var _4db=node;
+do{
+var n=_4db["offsetLeft"];
+if(!h.opera||n>0){
+ret.x+=isNaN(n)?0:n;
+}
+var m=_4db["offsetTop"];
+ret.y+=isNaN(m)?0:m;
+_4db=_4db.offsetParent;
+}while((_4db!=_4d9)&&(_4db!=null));
+}else{
+if(node["x"]&&node["y"]){
+ret.x+=isNaN(node.x)?0:node.x;
+ret.y+=isNaN(node.y)?0:node.y;
+}
+}
+}
+}
+if(_4d0){
+var _4de=dojo.html.getScroll();
+ret.y+=_4de.top;
+ret.x+=_4de.left;
+}
+var _4df=[dojo.html.getPaddingExtent,dojo.html.getBorderExtent,dojo.html.getMarginExtent];
+if(_4d4>_4d5){
+for(var i=_4d5;i<_4d4;++i){
+ret.y+=_4df[i](node,"top");
+ret.x+=_4df[i](node,"left");
+}
+}else{
+if(_4d4<_4d5){
+for(var i=_4d5;i>_4d4;--i){
+ret.y-=_4df[i-1](node,"top");
+ret.x-=_4df[i-1](node,"left");
+}
+}
+}
+ret.top=ret.y;
+ret.left=ret.x;
+return ret;
+};
+dojo.html.isPositionAbsolute=function(node){
+return (dojo.html.getComputedStyle(node,"position")=="absolute");
+};
+dojo.html._sumPixelValues=function(node,_4e3,_4e4){
+var _4e5=0;
+for(var x=0;x<_4e3.length;x++){
+_4e5+=dojo.html.getPixelValue(node,_4e3[x],_4e4);
+}
+return _4e5;
+};
+dojo.html.getMargin=function(node){
+return {width:dojo.html._sumPixelValues(node,["margin-left","margin-right"],(dojo.html.getComputedStyle(node,"position")=="absolute")),height:dojo.html._sumPixelValues(node,["margin-top","margin-bottom"],(dojo.html.getComputedStyle(node,"position")=="absolute"))};
+};
+dojo.html.getBorder=function(node){
+return {width:dojo.html.getBorderExtent(node,"left")+dojo.html.getBorderExtent(node,"right"),height:dojo.html.getBorderExtent(node,"top")+dojo.html.getBorderExtent(node,"bottom")};
+};
+dojo.html.getBorderExtent=function(node,side){
+return (dojo.html.getStyle(node,"border-"+side+"-style")=="none"?0:dojo.html.getPixelValue(node,"border-"+side+"-width"));
+};
+dojo.html.getMarginExtent=function(node,side){
+return dojo.html._sumPixelValues(node,["margin-"+side],dojo.html.isPositionAbsolute(node));
+};
+dojo.html.getPaddingExtent=function(node,side){
+return dojo.html._sumPixelValues(node,["padding-"+side],true);
+};
+dojo.html.getPadding=function(node){
+return {width:dojo.html._sumPixelValues(node,["padding-left","padding-right"],true),height:dojo.html._sumPixelValues(node,["padding-top","padding-bottom"],true)};
+};
+dojo.html.getPadBorder=function(node){
+var pad=dojo.html.getPadding(node);
+var _4f2=dojo.html.getBorder(node);
+return {width:pad.width+_4f2.width,height:pad.height+_4f2.height};
+};
+dojo.html.getBoxSizing=function(node){
+var h=dojo.render.html;
+var bs=dojo.html.boxSizing;
+if((h.ie)||(h.opera)){
+var cm=document["compatMode"];
+if((cm=="BackCompat")||(cm=="QuirksMode")){
+return bs.BORDER_BOX;
+}else{
+return bs.CONTENT_BOX;
+}
+}else{
+if(arguments.length==0){
+node=document.documentElement;
+}
+var _4f7=dojo.html.getStyle(node,"-moz-box-sizing");
+if(!_4f7){
+_4f7=dojo.html.getStyle(node,"box-sizing");
+}
+return (_4f7?_4f7:bs.CONTENT_BOX);
+}
+};
+dojo.html.isBorderBox=function(node){
+return (dojo.html.getBoxSizing(node)==dojo.html.boxSizing.BORDER_BOX);
+};
+dojo.html.getBorderBox=function(node){
+node=dojo.byId(node);
+return {width:node.offsetWidth,height:node.offsetHeight};
+};
+dojo.html.getPaddingBox=function(node){
+var box=dojo.html.getBorderBox(node);
+var _4fc=dojo.html.getBorder(node);
+return {width:box.width-_4fc.width,height:box.height-_4fc.height};
+};
+dojo.html.getContentBox=function(node){
+node=dojo.byId(node);
+var _4fe=dojo.html.getPadBorder(node);
+return {width:node.offsetWidth-_4fe.width,height:node.offsetHeight-_4fe.height};
+};
+dojo.html.setContentBox=function(node,args){
+node=dojo.byId(node);
+var _501=0;
+var _502=0;
+var isbb=dojo.html.isBorderBox(node);
+var _504=(isbb?dojo.html.getPadBorder(node):{width:0,height:0});
+var ret={};
+if(typeof args.width!="undefined"){
+_501=args.width+_504.width;
+ret.width=dojo.html.setPositivePixelValue(node,"width",_501);
+}
+if(typeof args.height!="undefined"){
+_502=args.height+_504.height;
+ret.height=dojo.html.setPositivePixelValue(node,"height",_502);
+}
+return ret;
+};
+dojo.html.getMarginBox=function(node){
+var _507=dojo.html.getBorderBox(node);
+var _508=dojo.html.getMargin(node);
+return {width:_507.width+_508.width,height:_507.height+_508.height};
+};
+dojo.html.setMarginBox=function(node,args){
+node=dojo.byId(node);
+var _50b=0;
+var _50c=0;
+var isbb=dojo.html.isBorderBox(node);
+var _50e=(!isbb?dojo.html.getPadBorder(node):{width:0,height:0});
+var _50f=dojo.html.getMargin(node);
+var ret={};
+if(typeof args.width!="undefined"){
+_50b=args.width-_50e.width;
+_50b-=_50f.width;
+ret.width=dojo.html.setPositivePixelValue(node,"width",_50b);
+}
+if(typeof args.height!="undefined"){
+_50c=args.height-_50e.height;
+_50c-=_50f.height;
+ret.height=dojo.html.setPositivePixelValue(node,"height",_50c);
+}
+return ret;
+};
+dojo.html.getElementBox=function(node,type){
+var bs=dojo.html.boxSizing;
+switch(type){
+case bs.MARGIN_BOX:
+return dojo.html.getMarginBox(node);
+case bs.BORDER_BOX:
+return dojo.html.getBorderBox(node);
+case bs.PADDING_BOX:
+return dojo.html.getPaddingBox(node);
+case bs.CONTENT_BOX:
+default:
+return dojo.html.getContentBox(node);
+}
+};
+dojo.html.toCoordinateObject=dojo.html.toCoordinateArray=function(_514,_515,_516){
+if(_514 instanceof Array||typeof _514=="array"){
+dojo.deprecated("dojo.html.toCoordinateArray","use dojo.html.toCoordinateObject({left: , top: , width: , height: }) instead","0.5");
+while(_514.length<4){
+_514.push(0);
+}
+while(_514.length>4){
+_514.pop();
+}
+var ret={left:_514[0],top:_514[1],width:_514[2],height:_514[3]};
+}else{
+if(!_514.nodeType&&!(_514 instanceof String||typeof _514=="string")&&("width" in _514||"height" in _514||"left" in _514||"x" in _514||"top" in _514||"y" in _514)){
+var ret={left:_514.left||_514.x||0,top:_514.top||_514.y||0,width:_514.width||0,height:_514.height||0};
+}else{
+var node=dojo.byId(_514);
+var pos=dojo.html.abs(node,_515,_516);
+var _51a=dojo.html.getMarginBox(node);
+var ret={left:pos.left,top:pos.top,width:_51a.width,height:_51a.height};
+}
+}
+ret.x=ret.left;
+ret.y=ret.top;
+return ret;
+};
+dojo.html.setMarginBoxWidth=dojo.html.setOuterWidth=function(node,_51c){
+return dojo.html._callDeprecated("setMarginBoxWidth","setMarginBox",arguments,"width");
+};
+dojo.html.setMarginBoxHeight=dojo.html.setOuterHeight=function(){
+return dojo.html._callDeprecated("setMarginBoxHeight","setMarginBox",arguments,"height");
+};
+dojo.html.getMarginBoxWidth=dojo.html.getOuterWidth=function(){
+return dojo.html._callDeprecated("getMarginBoxWidth","getMarginBox",arguments,null,"width");
+};
+dojo.html.getMarginBoxHeight=dojo.html.getOuterHeight=function(){
+return dojo.html._callDeprecated("getMarginBoxHeight","getMarginBox",arguments,null,"height");
+};
+dojo.html.getTotalOffset=function(node,type,_51f){
+return dojo.html._callDeprecated("getTotalOffset","getAbsolutePosition",arguments,null,type);
+};
+dojo.html.getAbsoluteX=function(node,_521){
+return dojo.html._callDeprecated("getAbsoluteX","getAbsolutePosition",arguments,null,"x");
+};
+dojo.html.getAbsoluteY=function(node,_523){
+return dojo.html._callDeprecated("getAbsoluteY","getAbsolutePosition",arguments,null,"y");
+};
+dojo.html.totalOffsetLeft=function(node,_525){
+return dojo.html._callDeprecated("totalOffsetLeft","getAbsolutePosition",arguments,null,"left");
+};
+dojo.html.totalOffsetTop=function(node,_527){
+return dojo.html._callDeprecated("totalOffsetTop","getAbsolutePosition",arguments,null,"top");
+};
+dojo.html.getMarginWidth=function(node){
+return dojo.html._callDeprecated("getMarginWidth","getMargin",arguments,null,"width");
+};
+dojo.html.getMarginHeight=function(node){
+return dojo.html._callDeprecated("getMarginHeight","getMargin",arguments,null,"height");
+};
+dojo.html.getBorderWidth=function(node){
+return dojo.html._callDeprecated("getBorderWidth","getBorder",arguments,null,"width");
+};
+dojo.html.getBorderHeight=function(node){
+return dojo.html._callDeprecated("getBorderHeight","getBorder",arguments,null,"height");
+};
+dojo.html.getPaddingWidth=function(node){
+return dojo.html._callDeprecated("getPaddingWidth","getPadding",arguments,null,"width");
+};
+dojo.html.getPaddingHeight=function(node){
+return dojo.html._callDeprecated("getPaddingHeight","getPadding",arguments,null,"height");
+};
+dojo.html.getPadBorderWidth=function(node){
+return dojo.html._callDeprecated("getPadBorderWidth","getPadBorder",arguments,null,"width");
+};
+dojo.html.getPadBorderHeight=function(node){
+return dojo.html._callDeprecated("getPadBorderHeight","getPadBorder",arguments,null,"height");
+};
+dojo.html.getBorderBoxWidth=dojo.html.getInnerWidth=function(){
+return dojo.html._callDeprecated("getBorderBoxWidth","getBorderBox",arguments,null,"width");
+};
+dojo.html.getBorderBoxHeight=dojo.html.getInnerHeight=function(){
+return dojo.html._callDeprecated("getBorderBoxHeight","getBorderBox",arguments,null,"height");
+};
+dojo.html.getContentBoxWidth=dojo.html.getContentWidth=function(){
+return dojo.html._callDeprecated("getContentBoxWidth","getContentBox",arguments,null,"width");
+};
+dojo.html.getContentBoxHeight=dojo.html.getContentHeight=function(){
+return dojo.html._callDeprecated("getContentBoxHeight","getContentBox",arguments,null,"height");
+};
+dojo.html.setContentBoxWidth=dojo.html.setContentWidth=function(node,_531){
+return dojo.html._callDeprecated("setContentBoxWidth","setContentBox",arguments,"width");
+};
+dojo.html.setContentBoxHeight=dojo.html.setContentHeight=function(node,_533){
+return dojo.html._callDeprecated("setContentBoxHeight","setContentBox",arguments,"height");
+};
+dojo.provide("dojo.lfx.html");
+dojo.lfx.html._byId=function(_534){
+if(!_534){
+return [];
+}
+if(dojo.lang.isArrayLike(_534)){
+if(!_534.alreadyChecked){
+var n=[];
+dojo.lang.forEach(_534,function(node){
+n.push(dojo.byId(node));
+});
+n.alreadyChecked=true;
+return n;
+}else{
+return _534;
+}
+}else{
+var n=[];
+n.push(dojo.byId(_534));
+n.alreadyChecked=true;
+return n;
+}
+};
+dojo.lfx.html.propertyAnimation=function(_537,_538,_539,_53a,_53b){
+_537=dojo.lfx.html._byId(_537);
+var _53c={"propertyMap":_538,"nodes":_537,"duration":_539,"easing":_53a||dojo.lfx.easeDefault};
+var _53d=function(args){
+if(args.nodes.length==1){
+var pm=args.propertyMap;
+if(!dojo.lang.isArray(args.propertyMap)){
+var parr=[];
+for(var _541 in pm){
+pm[_541].property=_541;
+parr.push(pm[_541]);
+}
+pm=args.propertyMap=parr;
+}
+dojo.lang.forEach(pm,function(prop){
+if(dj_undef("start",prop)){
+if(prop.property!="opacity"){
+prop.start=parseInt(dojo.html.getComputedStyle(args.nodes[0],prop.property));
+}else{
+prop.start=dojo.html.getOpacity(args.nodes[0]);
+}
+}
+});
+}
+};
+var _543=function(_544){
+var _545=[];
+dojo.lang.forEach(_544,function(c){
+_545.push(Math.round(c));
+});
+return _545;
+};
+var _547=function(n,_549){
+n=dojo.byId(n);
+if(!n||!n.style){
+return;
+}
+for(var s in _549){
+if(s=="opacity"){
+dojo.html.setOpacity(n,_549[s]);
+}else{
+n.style[s]=_549[s];
+}
+}
+};
+var _54b=function(_54c){
+this._properties=_54c;
+this.diffs=new Array(_54c.length);
+dojo.lang.forEach(_54c,function(prop,i){
+if(dojo.lang.isFunction(prop.start)){
+prop.start=prop.start(prop,i);
+}
+if(dojo.lang.isFunction(prop.end)){
+prop.end=prop.end(prop,i);
+}
+if(dojo.lang.isArray(prop.start)){
+this.diffs[i]=null;
+}else{
+if(prop.start instanceof dojo.gfx.color.Color){
+prop.startRgb=prop.start.toRgb();
+prop.endRgb=prop.end.toRgb();
+}else{
+this.diffs[i]=prop.end-prop.start;
+}
+}
+},this);
+this.getValue=function(n){
+var ret={};
+dojo.lang.forEach(this._properties,function(prop,i){
+var _553=null;
+if(dojo.lang.isArray(prop.start)){
+}else{
+if(prop.start instanceof dojo.gfx.color.Color){
+_553=(prop.units||"rgb")+"(";
+for(var j=0;j<prop.startRgb.length;j++){
+_553+=Math.round(((prop.endRgb[j]-prop.startRgb[j])*n)+prop.startRgb[j])+(j<prop.startRgb.length-1?",":"");
+}
+_553+=")";
+}else{
+_553=((this.diffs[i])*n)+prop.start+(prop.property!="opacity"?prop.units||"px":"");
+}
+}
+ret[dojo.html.toCamelCase(prop.property)]=_553;
+},this);
+return ret;
+};
+};
+var anim=new dojo.lfx.Animation({beforeBegin:function(){
+_53d(_53c);
+anim.curve=new _54b(_53c.propertyMap);
+},onAnimate:function(_556){
+dojo.lang.forEach(_53c.nodes,function(node){
+_547(node,_556);
+});
+}},_53c.duration,null,_53c.easing);
+if(_53b){
+for(var x in _53b){
+if(dojo.lang.isFunction(_53b[x])){
+anim.connect(x,anim,_53b[x]);
+}
+}
+}
+return anim;
+};
+dojo.lfx.html._makeFadeable=function(_559){
+var _55a=function(node){
+if(dojo.render.html.ie){
+if((node.style.zoom.length==0)&&(dojo.html.getStyle(node,"zoom")=="normal")){
+node.style.zoom="1";
+}
+if((node.style.width.length==0)&&(dojo.html.getStyle(node,"width")=="auto")){
+node.style.width="auto";
+}
+}
+};
+if(dojo.lang.isArrayLike(_559)){
+dojo.lang.forEach(_559,_55a);
+}else{
+_55a(_559);
+}
+};
+dojo.lfx.html.fade=function(_55c,_55d,_55e,_55f,_560){
+_55c=dojo.lfx.html._byId(_55c);
+var _561={property:"opacity"};
+if(!dj_undef("start",_55d)){
+_561.start=_55d.start;
+}else{
+_561.start=function(){
+return dojo.html.getOpacity(_55c[0]);
+};
+}
+if(!dj_undef("end",_55d)){
+_561.end=_55d.end;
+}else{
+dojo.raise("dojo.lfx.html.fade needs an end value");
+}
+var anim=dojo.lfx.propertyAnimation(_55c,[_561],_55e,_55f);
+anim.connect("beforeBegin",function(){
+dojo.lfx.html._makeFadeable(_55c);
+});
+if(_560){
+anim.connect("onEnd",function(){
+_560(_55c,anim);
+});
+}
+return anim;
+};
+dojo.lfx.html.fadeIn=function(_563,_564,_565,_566){
+return dojo.lfx.html.fade(_563,{end:1},_564,_565,_566);
+};
+dojo.lfx.html.fadeOut=function(_567,_568,_569,_56a){
+return dojo.lfx.html.fade(_567,{end:0},_568,_569,_56a);
+};
+dojo.lfx.html.fadeShow=function(_56b,_56c,_56d,_56e){
+_56b=dojo.lfx.html._byId(_56b);
+dojo.lang.forEach(_56b,function(node){
+dojo.html.setOpacity(node,0);
+});
+var anim=dojo.lfx.html.fadeIn(_56b,_56c,_56d,_56e);
+anim.connect("beforeBegin",function(){
+if(dojo.lang.isArrayLike(_56b)){
+dojo.lang.forEach(_56b,dojo.html.show);
+}else{
+dojo.html.show(_56b);
+}
+});
+return anim;
+};
+dojo.lfx.html.fadeHide=function(_571,_572,_573,_574){
+var anim=dojo.lfx.html.fadeOut(_571,_572,_573,function(){
+if(dojo.lang.isArrayLike(_571)){
+dojo.lang.forEach(_571,dojo.html.hide);
+}else{
+dojo.html.hide(_571);
+}
+if(_574){
+_574(_571,anim);
+}
+});
+return anim;
+};
+dojo.lfx.html.wipeIn=function(_576,_577,_578,_579){
+_576=dojo.lfx.html._byId(_576);
+var _57a=[];
+dojo.lang.forEach(_576,function(node){
+var _57c={};
+dojo.html.show(node);
+var _57d=dojo.html.getBorderBox(node).height;
+dojo.html.hide(node);
+var anim=dojo.lfx.propertyAnimation(node,{"height":{start:1,end:function(){
+return _57d;
+}}},_577,_578);
+anim.connect("beforeBegin",function(){
+_57c.overflow=node.style.overflow;
+_57c.height=node.style.height;
+with(node.style){
+overflow="hidden";
+_57d="1px";
+}
+dojo.html.show(node);
+});
+anim.connect("onEnd",function(){
+with(node.style){
+overflow=_57c.overflow;
+_57d=_57c.height;
+}
+if(_579){
+_579(node,anim);
+}
+});
+_57a.push(anim);
+});
+return dojo.lfx.combine(_57a);
+};
+dojo.lfx.html.wipeOut=function(_57f,_580,_581,_582){
+_57f=dojo.lfx.html._byId(_57f);
+var _583=[];
+dojo.lang.forEach(_57f,function(node){
+var _585={};
+var anim=dojo.lfx.propertyAnimation(node,{"height":{start:function(){
+return dojo.html.getContentBox(node).height;
+},end:1}},_580,_581,{"beforeBegin":function(){
+_585.overflow=node.style.overflow;
+_585.height=node.style.height;
+with(node.style){
+overflow="hidden";
+}
+dojo.html.show(node);
+},"onEnd":function(){
+dojo.html.hide(node);
+with(node.style){
+overflow=_585.overflow;
+height=_585.height;
+}
+if(_582){
+_582(node,anim);
+}
+}});
+_583.push(anim);
+});
+return dojo.lfx.combine(_583);
+};
+dojo.lfx.html.slideTo=function(_587,_588,_589,_58a,_58b){
+_587=dojo.lfx.html._byId(_587);
+var _58c=[];
+var _58d=dojo.html.getComputedStyle;
+if(dojo.lang.isArray(_588)){
+dojo.deprecated("dojo.lfx.html.slideTo(node, array)","use dojo.lfx.html.slideTo(node, {top: value, left: value});","0.5");
+_588={top:_588[0],left:_588[1]};
+}
+dojo.lang.forEach(_587,function(node){
+var top=null;
+var left=null;
+var init=(function(){
+var _592=node;
+return function(){
+var pos=_58d(_592,"position");
+top=(pos=="absolute"?node.offsetTop:parseInt(_58d(node,"top"))||0);
+left=(pos=="absolute"?node.offsetLeft:parseInt(_58d(node,"left"))||0);
+if(!dojo.lang.inArray(["absolute","relative"],pos)){
+var ret=dojo.html.abs(_592,true);
+dojo.html.setStyleAttributes(_592,"position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
+top=ret.y;
+left=ret.x;
+}
+};
+})();
+init();
+var anim=dojo.lfx.propertyAnimation(node,{"top":{start:top,end:(_588.top||0)},"left":{start:left,end:(_588.left||0)}},_589,_58a,{"beforeBegin":init});
+if(_58b){
+anim.connect("onEnd",function(){
+_58b(_587,anim);
+});
+}
+_58c.push(anim);
+});
+return dojo.lfx.combine(_58c);
+};
+dojo.lfx.html.slideBy=function(_596,_597,_598,_599,_59a){
+_596=dojo.lfx.html._byId(_596);
+var _59b=[];
+var _59c=dojo.html.getComputedStyle;
+if(dojo.lang.isArray(_597)){
+dojo.deprecated("dojo.lfx.html.slideBy(node, array)","use dojo.lfx.html.slideBy(node, {top: value, left: value});","0.5");
+_597={top:_597[0],left:_597[1]};
+}
+dojo.lang.forEach(_596,function(node){
+var top=null;
+var left=null;
+var init=(function(){
+var _5a1=node;
+return function(){
+var pos=_59c(_5a1,"position");
+top=(pos=="absolute"?node.offsetTop:parseInt(_59c(node,"top"))||0);
+left=(pos=="absolute"?node.offsetLeft:parseInt(_59c(node,"left"))||0);
+if(!dojo.lang.inArray(["absolute","relative"],pos)){
+var ret=dojo.html.abs(_5a1,true);
+dojo.html.setStyleAttributes(_5a1,"position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
+top=ret.y;
+left=ret.x;
+}
+};
+})();
+init();
+var anim=dojo.lfx.propertyAnimation(node,{"top":{start:top,end:top+(_597.top||0)},"left":{start:left,end:left+(_597.left||0)}},_598,_599).connect("beforeBegin",init);
+if(_59a){
+anim.connect("onEnd",function(){
+_59a(_596,anim);
+});
+}
+_59b.push(anim);
+});
+return dojo.lfx.combine(_59b);
+};
+dojo.lfx.html.explode=function(_5a5,_5a6,_5a7,_5a8,_5a9){
+var h=dojo.html;
+_5a5=dojo.byId(_5a5);
+_5a6=dojo.byId(_5a6);
+var _5ab=h.toCoordinateObject(_5a5,true);
+var _5ac=document.createElement("div");
+h.copyStyle(_5ac,_5a6);
+if(_5a6.explodeClassName){
+_5ac.className=_5a6.explodeClassName;
+}
+with(_5ac.style){
+position="absolute";
+display="none";
+}
+dojo.body().appendChild(_5ac);
+with(_5a6.style){
+visibility="hidden";
+display="block";
+}
+var _5ad=h.toCoordinateObject(_5a6,true);
+with(_5a6.style){
+display="none";
+visibility="visible";
+}
+var _5ae={opacity:{start:0.5,end:1}};
+dojo.lang.forEach(["height","width","top","left"],function(type){
+_5ae[type]={start:_5ab[type],end:_5ad[type]};
+});
+var anim=new dojo.lfx.propertyAnimation(_5ac,_5ae,_5a7,_5a8,{"beforeBegin":function(){
+h.setDisplay(_5ac,"block");
+},"onEnd":function(){
+h.setDisplay(_5a6,"block");
+_5ac.parentNode.removeChild(_5ac);
+}});
+if(_5a9){
+anim.connect("onEnd",function(){
+_5a9(_5a6,anim);
+});
+}
+return anim;
+};
+dojo.lfx.html.implode=function(_5b1,end,_5b3,_5b4,_5b5){
+var h=dojo.html;
+_5b1=dojo.byId(_5b1);
+end=dojo.byId(end);
+var _5b7=dojo.html.toCoordinateObject(_5b1,true);
+var _5b8=dojo.html.toCoordinateObject(end,true);
+var _5b9=document.createElement("div");
+dojo.html.copyStyle(_5b9,_5b1);
+if(_5b1.explodeClassName){
+_5b9.className=_5b1.explodeClassName;
+}
+dojo.html.setOpacity(_5b9,0.3);
+with(_5b9.style){
+position="absolute";
+display="none";
+backgroundColor=h.getStyle(_5b1,"background-color").toLowerCase();
+}
+dojo.body().appendChild(_5b9);
+var _5ba={opacity:{start:1,end:0.5}};
+dojo.lang.forEach(["height","width","top","left"],function(type){
+_5ba[type]={start:_5b7[type],end:_5b8[type]};
+});
+var anim=new dojo.lfx.propertyAnimation(_5b9,_5ba,_5b3,_5b4,{"beforeBegin":function(){
+dojo.html.hide(_5b1);
+dojo.html.show(_5b9);
+},"onEnd":function(){
+_5b9.parentNode.removeChild(_5b9);
+}});
+if(_5b5){
+anim.connect("onEnd",function(){
+_5b5(_5b1,anim);
+});
+}
+return anim;
+};
+dojo.lfx.html.highlight=function(_5bd,_5be,_5bf,_5c0,_5c1){
+_5bd=dojo.lfx.html._byId(_5bd);
+var _5c2=[];
+dojo.lang.forEach(_5bd,function(node){
+var _5c4=dojo.html.getBackgroundColor(node);
+var bg=dojo.html.getStyle(node,"background-color").toLowerCase();
+var _5c6=dojo.html.getStyle(node,"background-image");
+var _5c7=(bg=="transparent"||bg=="rgba(0, 0, 0, 0)");
+while(_5c4.length>3){
+_5c4.pop();
+}
+var rgb=new dojo.gfx.color.Color(_5be);
+var _5c9=new dojo.gfx.color.Color(_5c4);
+var anim=dojo.lfx.propertyAnimation(node,{"background-color":{start:rgb,end:_5c9}},_5bf,_5c0,{"beforeBegin":function(){
+if(_5c6){
+node.style.backgroundImage="none";
+}
+node.style.backgroundColor="rgb("+rgb.toRgb().join(",")+")";
+},"onEnd":function(){
+if(_5c6){
+node.style.backgroundImage=_5c6;
+}
+if(_5c7){
+node.style.backgroundColor="transparent";
+}
+if(_5c1){
+_5c1(node,anim);
+}
+}});
+_5c2.push(anim);
+});
+return dojo.lfx.combine(_5c2);
+};
+dojo.lfx.html.unhighlight=function(_5cb,_5cc,_5cd,_5ce,_5cf){
+_5cb=dojo.lfx.html._byId(_5cb);
+var _5d0=[];
+dojo.lang.forEach(_5cb,function(node){
+var _5d2=new dojo.gfx.color.Color(dojo.html.getBackgroundColor(node));
+var rgb=new dojo.gfx.color.Color(_5cc);
+var _5d4=dojo.html.getStyle(node,"background-image");
+var anim=dojo.lfx.propertyAnimation(node,{"background-color":{start:_5d2,end:rgb}},_5cd,_5ce,{"beforeBegin":function(){
+if(_5d4){
+node.style.backgroundImage="none";
+}
+node.style.backgroundColor="rgb("+_5d2.toRgb().join(",")+")";
+},"onEnd":function(){
+if(_5cf){
+_5cf(node,anim);
+}
+}});
+_5d0.push(anim);
+});
+return dojo.lfx.combine(_5d0);
+};
+dojo.lang.mixin(dojo.lfx,dojo.lfx.html);
+dojo.provide("dojo.lfx.*");
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/dojo.js.uncompressed.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/dojo.js.uncompressed.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/dojo.js.uncompressed.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,9197 @@
+if(typeof dojo == "undefined"){
+
+/**
+* @file bootstrap1.js
+*
+* summary: First file that is loaded that 'bootstraps' the entire dojo library suite.
+* note:  Must run before hostenv_*.js file.
+*
+* @author  Copyright 2004 Mark D. Anderson (mda at discerning.com)
+* TODOC: should the copyright be changed to Dojo Foundation?
+* @license Licensed under the Academic Free License 2.1 http://www.opensource.org/licenses/afl-2.1.php
+*
+* $Id: bootstrap1.js 6258 2006-10-20 03:12:36Z jburke $
+*/
+
+// TODOC: HOW TO DOC THE BELOW?
+// @global: djConfig
+// summary:  
+//		Application code can set the global 'djConfig' prior to loading
+//		the library to override certain global settings for how dojo works.  
+// description:  The variables that can be set are as follows:
+//			- isDebug: false
+//			- allowQueryConfig: false
+//			- baseScriptUri: ""
+//			- baseRelativePath: ""
+//			- libraryScriptUri: ""
+//			- iePreventClobber: false
+//			- ieClobberMinimal: true
+//			- locale: undefined
+//			- extraLocale: undefined
+//			- preventBackButtonFix: true
+//			- searchIds: []
+//			- parseWidgets: true
+// TODOC: HOW TO DOC THESE VARIABLES?
+// TODOC: IS THIS A COMPLETE LIST?
+// note:
+//		'djConfig' does not exist under 'dojo.*' so that it can be set before the 
+//		'dojo' variable exists.  
+// note:
+//		Setting any of these variables *after* the library has loaded does nothing at all. 
+// TODOC: is this still true?  Release notes for 0.3 indicated they could be set after load.
+//
+
+
+//TODOC:  HOW TO DOC THIS?
+// @global: dj_global
+// summary: 
+//		an alias for the top-level global object in the host environment
+//		(e.g., the window object in a browser).
+// description:  
+//		Refer to 'dj_global' rather than referring to window to ensure your
+//		code runs correctly in contexts other than web browsers (eg: Rhino on a server).
+var dj_global = this;
+
+//TODOC:  HOW TO DOC THIS?
+// @global: dj_currentContext
+// summary: 
+//		Private global context object. Where 'dj_global' always refers to the boot-time
+//    global context, 'dj_currentContext' can be modified for temporary context shifting.
+//    dojo.global() returns dj_currentContext.
+// description:  
+//		Refer to dojo.global() rather than referring to dj_global to ensure your
+//		code runs correctly in managed contexts.
+var dj_currentContext = this;
+
+
+// ****************************************************************
+// global public utils
+// TODOC: DO WE WANT TO NOTE THAT THESE ARE GLOBAL PUBLIC UTILS?
+// ****************************************************************
+
+function dj_undef(/*String*/ name, /*Object?*/ object){
+	//summary: Returns true if 'name' is defined on 'object' (or globally if 'object' is null).
+	//description: Note that 'defined' and 'exists' are not the same concept.
+	return (typeof (object || dj_currentContext)[name] == "undefined");	// Boolean
+}
+
+// make sure djConfig is defined
+if(dj_undef("djConfig", this)){ 
+	var djConfig = {}; 
+}
+
+//TODOC:  HOW TO DOC THIS?
+// dojo is the root variable of (almost all) our public symbols -- make sure it is defined.
+if(dj_undef("dojo", this)){ 
+	var dojo = {}; 
+}
+
+dojo.global = function(){
+	// summary:
+	//		return the current global context object
+	//		(e.g., the window object in a browser).
+	// description: 
+	//		Refer to 'dojo.global()' rather than referring to window to ensure your
+	//		code runs correctly in contexts other than web browsers (eg: Rhino on a server).
+	return dj_currentContext;
+}
+
+// Override locale setting, if specified
+dojo.locale  = djConfig.locale;
+
+//TODOC:  HOW TO DOC THIS?
+dojo.version = {
+	// summary: version number of this instance of dojo.
+	major: 0, minor: 4, patch: 0, flag: "",
+	revision: Number("$Rev: 6258 $".match(/[0-9]+/)[0]),
+	toString: function(){
+		with(dojo.version){
+			return major + "." + minor + "." + patch + flag + " (" + revision + ")";	// String
+		}
+	}
+}
+
+dojo.evalProp = function(/*String*/ name, /*Object*/ object, /*Boolean?*/ create){
+	// summary: Returns 'object[name]'.  If not defined and 'create' is true, will return a new Object.
+	// description: 
+	//		Returns null if 'object[name]' is not defined and 'create' is not true.
+	// 		Note: 'defined' and 'exists' are not the same concept.	
+	if((!object)||(!name)) return undefined; // undefined
+	if(!dj_undef(name, object)) return object[name]; // mixed
+	return (create ? (object[name]={}) : undefined);	// mixed
+}
+
+dojo.parseObjPath = function(/*String*/ path, /*Object?*/ context, /*Boolean?*/ create){
+	// summary: Parse string path to an object, and return corresponding object reference and property name.
+	// description: 
+	//		Returns an object with two properties, 'obj' and 'prop'.  
+	//		'obj[prop]' is the reference indicated by 'path'.
+	// path: Path to an object, in the form "A.B.C".
+	// context: Object to use as root of path.  Defaults to 'dojo.global()'.
+	// create: If true, Objects will be created at any point along the 'path' that is undefined.
+	var object = (context || dojo.global());
+	var names = path.split('.');
+	var prop = names.pop();
+	for (var i=0,l=names.length;i<l && object;i++){
+		object = dojo.evalProp(names[i], object, create);
+	}
+	return {obj: object, prop: prop};	// Object: {obj: Object, prop: String}
+}
+
+dojo.evalObjPath = function(/*String*/ path, /*Boolean?*/ create){
+	// summary: Return the value of object at 'path' in the global scope, without using 'eval()'.
+	// path: Path to an object, in the form "A.B.C".
+	// create: If true, Objects will be created at any point along the 'path' that is undefined.
+	if(typeof path != "string"){ 
+		return dojo.global(); 
+	}
+	// fast path for no periods
+	if(path.indexOf('.') == -1){
+		return dojo.evalProp(path, dojo.global(), create);		// mixed
+	}
+
+	//MOW: old 'with' syntax was confusing and would throw an error if parseObjPath returned null.
+	var ref = dojo.parseObjPath(path, dojo.global(), create);
+	if(ref){
+		return dojo.evalProp(ref.prop, ref.obj, create);	// mixed
+	}
+	return null;
+}
+
+dojo.errorToString = function(/*Error*/ exception){
+	// summary: Return an exception's 'message', 'description' or text.
+
+	// TODO: overriding Error.prototype.toString won't accomplish this?
+ 	// 		... since natively generated Error objects do not always reflect such things?
+	if(!dj_undef("message", exception)){
+		return exception.message;		// String
+	}else if(!dj_undef("description", exception)){
+		return exception.description;	// String
+	}else{
+		return exception;				// Error
+	}
+}
+
+dojo.raise = function(/*String*/ message, /*Error?*/ exception){
+	// summary: Common point for raising exceptions in Dojo to enable logging.
+	//	Throws an error message with text of 'exception' if provided, or
+	//	rethrows exception object.
+
+	if(exception){
+		message = message + ": "+dojo.errorToString(exception);
+	}
+
+	// print the message to the user if hostenv.println is defined
+	try { if(djConfig.isDebug){ dojo.hostenv.println("FATAL exception raised: "+message); } } catch (e) {}
+
+	throw exception || Error(message);
+}
+
+//Stub functions so things don't break.
+//TODOC:  HOW TO DOC THESE?
+dojo.debug = function(){};
+dojo.debugShallow = function(obj){};
+dojo.profile = { start: function(){}, end: function(){}, stop: function(){}, dump: function(){} };
+
+function dj_eval(/*String*/ scriptFragment){ 
+	// summary: Perform an evaluation in the global scope.  Use this rather than calling 'eval()' directly.
+	// description: Placed in a separate function to minimize size of trapped evaluation context.
+	// note:
+	//	 - JSC eval() takes an optional second argument which can be 'unsafe'.
+	//	 - Mozilla/SpiderMonkey eval() takes an optional second argument which is the
+	//  	 scope object for new symbols.
+	return dj_global.eval ? dj_global.eval(scriptFragment) : eval(scriptFragment); 	// mixed
+}
+
+dojo.unimplemented = function(/*String*/ funcname, /*String?*/ extra){
+	// summary: Throw an exception because some function is not implemented.
+	// extra: Text to append to the exception message.
+	var message = "'" + funcname + "' not implemented";
+	if (extra != null) { message += " " + extra; }
+	dojo.raise(message);
+}
+
+dojo.deprecated = function(/*String*/ behaviour, /*String?*/ extra, /*String?*/ removal){
+	// summary: Log a debug message to indicate that a behavior has been deprecated.
+	// extra: Text to append to the message.
+	// removal: Text to indicate when in the future the behavior will be removed.
+	var message = "DEPRECATED: " + behaviour;
+	if(extra){ message += " " + extra; }
+	if(removal){ message += " -- will be removed in version: " + removal; }
+	dojo.debug(message);
+}
+
+dojo.render = (function(){
+	//TODOC: HOW TO DOC THIS?
+	// summary: Details rendering support, OS and browser of the current environment.
+	// TODOC: is this something many folks will interact with?  If so, we should doc the structure created...
+	function vscaffold(prefs, names){
+		var tmp = {
+			capable: false,
+			support: {
+				builtin: false,
+				plugin: false
+			},
+			prefixes: prefs
+		};
+		for(var i=0; i<names.length; i++){
+			tmp[names[i]] = false;
+		}
+		return tmp;
+	}
+
+	return {
+		name: "",
+		ver: dojo.version,
+		os: { win: false, linux: false, osx: false },
+		html: vscaffold(["html"], ["ie", "opera", "khtml", "safari", "moz"]),
+		svg: vscaffold(["svg"], ["corel", "adobe", "batik"]),
+		vml: vscaffold(["vml"], ["ie"]),
+		swf: vscaffold(["Swf", "Flash", "Mm"], ["mm"]),
+		swt: vscaffold(["Swt"], ["ibm"])
+	};
+})();
+
+// ****************************************************************
+// dojo.hostenv methods that must be defined in hostenv_*.js
+// ****************************************************************
+
+/**
+ * The interface definining the interaction with the EcmaScript host environment.
+*/
+
+/*
+ * None of these methods should ever be called directly by library users.
+ * Instead public methods such as loadModule should be called instead.
+ */
+dojo.hostenv = (function(){
+	// TODOC:  HOW TO DOC THIS?
+	// summary: Provides encapsulation of behavior that changes across different 'host environments' 
+	//			(different browsers, server via Rhino, etc).
+	// description: None of these methods should ever be called directly by library users.
+	//				Use public methods such as 'loadModule' instead.
+	
+	// default configuration options
+	var config = {
+		isDebug: false,
+		allowQueryConfig: false,
+		baseScriptUri: "",
+		baseRelativePath: "",
+		libraryScriptUri: "",
+		iePreventClobber: false,
+		ieClobberMinimal: true,
+		preventBackButtonFix: true,
+		delayMozLoadingFix: false,
+		searchIds: [],
+		parseWidgets: true
+	};
+
+	if (typeof djConfig == "undefined") { djConfig = config; }
+	else {
+		for (var option in config) {
+			if (typeof djConfig[option] == "undefined") {
+				djConfig[option] = config[option];
+			}
+		}
+	}
+
+	return {
+		name_: '(unset)',
+		version_: '(unset)',
+
+
+		getName: function(){ 
+			// sumary: Return the name of the host environment.
+			return this.name_; 	// String
+		},
+
+
+		getVersion: function(){ 
+			// summary: Return the version of the hostenv.
+			return this.version_; // String
+		},
+
+		getText: function(/*String*/ uri){
+			// summary:	Read the plain/text contents at the specified 'uri'.
+			// description: 
+			//			If 'getText()' is not implemented, then it is necessary to override 
+			//			'loadUri()' with an implementation that doesn't rely on it.
+
+			dojo.unimplemented('getText', "uri=" + uri);
+		}
+	};
+})();
+
+
+dojo.hostenv.getBaseScriptUri = function(){
+	// summary: Return the base script uri that other scripts are found relative to.
+	// TODOC: HUH?  This comment means nothing to me.  What other scripts? Is this the path to other dojo libraries?
+	//		MAYBE:  Return the base uri to scripts in the dojo library.	 ???
+	// return: Empty string or a path ending in '/'.
+	if(djConfig.baseScriptUri.length){ 
+		return djConfig.baseScriptUri;
+	}
+
+	// MOW: Why not:
+	//			uri = djConfig.libraryScriptUri || djConfig.baseRelativePath
+	//		??? Why 'new String(...)'
+	var uri = new String(djConfig.libraryScriptUri||djConfig.baseRelativePath);
+	if (!uri) { dojo.raise("Nothing returned by getLibraryScriptUri(): " + uri); }
+
+	// MOW: uri seems to not be actually used.  Seems to be hard-coding to djConfig.baseRelativePath... ???
+	var lastslash = uri.lastIndexOf('/');		// MOW ???
+	djConfig.baseScriptUri = djConfig.baseRelativePath;
+	return djConfig.baseScriptUri;	// String
+}
+
+/*
+ * loader.js - A bootstrap module.  Runs before the hostenv_*.js file. Contains all of the package loading methods.
+ */
+
+//A semi-colon is at the start of the line because after doing a build, this function definition
+//get compressed onto the same line as the last line in bootstrap1.js. That list line is just a
+//curly bracket, and the browser complains about that syntax. The semicolon fixes it. Putting it
+//here instead of at the end of bootstrap1.js, since it is more of an issue for this file, (using
+//the closure), and bootstrap1.js could change in the future.
+;(function(){
+	//Additional properties for dojo.hostenv
+	var _addHostEnv = {
+		pkgFileName: "__package__",
+	
+		// for recursion protection
+		loading_modules_: {},
+		loaded_modules_: {},
+		addedToLoadingCount: [],
+		removedFromLoadingCount: [],
+	
+		inFlightCount: 0,
+	
+		// FIXME: it should be possible to pull module prefixes in from djConfig
+		modulePrefixes_: {
+			dojo: {name: "dojo", value: "src"}
+		},
+
+		setModulePrefix: function(/*String*/module, /*String*/prefix){
+			// summary: establishes module/prefix pair
+			this.modulePrefixes_[module] = {name: module, value: prefix};
+		},
+
+		moduleHasPrefix: function(/*String*/module){
+			// summary: checks to see if module has been established
+			var mp = this.modulePrefixes_;
+			return Boolean(mp[module] && mp[module].value); // Boolean
+		},
+
+		getModulePrefix: function(/*String*/module){
+			// summary: gets the prefix associated with module
+			if(this.moduleHasPrefix(module)){
+				return this.modulePrefixes_[module].value; // String
+			}
+			return module; // String
+		},
+
+		getTextStack: [],
+		loadUriStack: [],
+		loadedUris: [],
+	
+		//WARNING: This variable is referenced by packages outside of bootstrap: FloatingPane.js and undo/browser.js
+		post_load_: false,
+		
+		//Egad! Lots of test files push on this directly instead of using dojo.addOnLoad.
+		modulesLoadedListeners: [],
+		unloadListeners: [],
+		loadNotifying: false
+	};
+	
+	//Add all of these properties to dojo.hostenv
+	for(var param in _addHostEnv){
+		dojo.hostenv[param] = _addHostEnv[param];
+	}
+})();
+
+dojo.hostenv.loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){
+// summary:
+//	Load a Javascript module given a relative path
+//
+// description:
+//	Loads and interprets the script located at relpath, which is relative to the
+//	script root directory.  If the script is found but its interpretation causes
+//	a runtime exception, that exception is not caught by us, so the caller will
+//	see it.  We return a true value if and only if the script is found.
+//
+//	For now, we do not have an implementation of a true search path.  We
+//	consider only the single base script uri, as returned by getBaseScriptUri().
+//
+// relpath: A relative path to a script (no leading '/', and typically
+// 	ending in '.js').
+// module: A module whose existance to check for after loading a path.
+//	Can be used to determine success or failure of the load.
+// cb: a callback function to pass the result of evaluating the script
+
+	var uri;
+	if(relpath.charAt(0) == '/' || relpath.match(/^\w+:/)){
+		// dojo.raise("relpath '" + relpath + "'; must be relative");
+		uri = relpath;
+	}else{
+		uri = this.getBaseScriptUri() + relpath;
+	}
+	if(djConfig.cacheBust && dojo.render.html.capable){
+		uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,"");
+	}
+	try{
+		return !module ? this.loadUri(uri, cb) : this.loadUriAndCheck(uri, module, cb); // Boolean
+	}catch(e){
+		dojo.debug(e);
+		return false; // Boolean
+	}
+}
+
+dojo.hostenv.loadUri = function(/*String (URL)*/uri, /*Function?*/cb){
+// summary:
+//	Loads JavaScript from a URI
+//
+// description:
+//	Reads the contents of the URI, and evaluates the contents.  This is used to load modules as well
+//	as resource bundles.  Returns true if it succeeded. Returns false if the URI reading failed.
+//	Throws if the evaluation throws.
+//
+// uri: a uri which points at the script to be loaded
+// cb: a callback function to process the result of evaluating the script as an expression, typically
+//	used by the resource bundle loader to load JSON-style resources
+
+	if(this.loadedUris[uri]){
+		return true; // Boolean
+	}
+	var contents = this.getText(uri, null, true);
+	if(!contents){ return false; } // Boolean
+	this.loadedUris[uri] = true;
+	if(cb){ contents = '('+contents+')'; }
+	var value = dj_eval(contents);
+	if(cb){ cb(value); }
+	return true; // Boolean
+}
+
+// FIXME: probably need to add logging to this method
+dojo.hostenv.loadUriAndCheck = function(/*String (URL)*/uri, /*String*/moduleName, /*Function?*/cb){
+	// summary: calls loadUri then findModule and returns true if both succeed
+	var ok = true;
+	try{
+		ok = this.loadUri(uri, cb);
+	}catch(e){
+		dojo.debug("failed loading ", uri, " with error: ", e);
+	}
+	return Boolean(ok && this.findModule(moduleName, false)); // Boolean
+}
+
+dojo.loaded = function(){ }
+dojo.unloaded = function(){ }
+
+dojo.hostenv.loaded = function(){
+	this.loadNotifying = true;
+	this.post_load_ = true;
+	var mll = this.modulesLoadedListeners;
+	for(var x=0; x<mll.length; x++){
+		mll[x]();
+	}
+
+	//Clear listeners so new ones can be added
+	//For other xdomain package loads after the initial load.
+	this.modulesLoadedListeners = [];
+	this.loadNotifying = false;
+
+	dojo.loaded();
+}
+
+dojo.hostenv.unloaded = function(){
+	var mll = this.unloadListeners;
+	while(mll.length){
+		(mll.pop())();
+	}
+	dojo.unloaded();
+}
+
+dojo.addOnLoad = function(/*Object?*/obj, /*String|Function*/functionName) {
+// summary:
+//	Registers a function to be triggered after the DOM has finished loading 
+//	and widgets declared in markup have been instantiated.  Images and CSS files
+//	may or may not have finished downloading when the specified function is called.
+//	(Note that widgets' CSS and HTML code is guaranteed to be downloaded before said
+//	widgets are instantiated.)
+//
+// usage:
+//	dojo.addOnLoad(functionPointer)
+//	dojo.addOnLoad(object, "functionName")
+
+	var dh = dojo.hostenv;
+	if(arguments.length == 1) {
+		dh.modulesLoadedListeners.push(obj);
+	} else if(arguments.length > 1) {
+		dh.modulesLoadedListeners.push(function() {
+			obj[functionName]();
+		});
+	}
+
+	//Added for xdomain loading. dojo.addOnLoad is used to
+	//indicate callbacks after doing some dojo.require() statements.
+	//In the xdomain case, if all the requires are loaded (after initial
+	//page load), then immediately call any listeners.
+	if(dh.post_load_ && dh.inFlightCount == 0 && !dh.loadNotifying){
+		dh.callLoaded();
+	}
+}
+
+dojo.addOnUnload = function(/*Object?*/obj, /*String|Function?*/functionName){
+// summary: registers a function to be triggered when the page unloads
+//
+// usage:
+//	dojo.addOnLoad(functionPointer)
+//	dojo.addOnLoad(object, "functionName")
+	var dh = dojo.hostenv;
+	if(arguments.length == 1){
+		dh.unloadListeners.push(obj);
+	} else if(arguments.length > 1) {
+		dh.unloadListeners.push(function() {
+			obj[functionName]();
+		});
+	}
+}
+
+dojo.hostenv.modulesLoaded = function(){
+	if(this.post_load_){ return; }
+	if(this.loadUriStack.length==0 && this.getTextStack.length==0){
+		if(this.inFlightCount > 0){ 
+			dojo.debug("files still in flight!");
+			return;
+		}
+		dojo.hostenv.callLoaded();
+	}
+}
+
+dojo.hostenv.callLoaded = function(){
+	if(typeof setTimeout == "object"){
+		setTimeout("dojo.hostenv.loaded();", 0);
+	}else{
+		dojo.hostenv.loaded();
+	}
+}
+
+dojo.hostenv.getModuleSymbols = function(/*String*/modulename){
+// summary:
+//	Converts a module name in dotted JS notation to an array representing the path in the source tree
+
+	var syms = modulename.split(".");
+	for(var i = syms.length; i>0; i--){
+		var parentModule = syms.slice(0, i).join(".");
+		if ((i==1) && !this.moduleHasPrefix(parentModule)){		
+			//Support default module directory (sibling of dojo)
+			syms[0] = "../" + syms[0];
+		}else{
+			var parentModulePath = this.getModulePrefix(parentModule);
+			if(parentModulePath != parentModule){
+				syms.splice(0, i, parentModulePath);
+				break;
+			}
+		}
+	}
+	return syms; // Array
+}
+
+dojo.hostenv._global_omit_module_check = false;
+dojo.hostenv.loadModule = function(/*String*/moduleName, /*Boolean?*/exactOnly, /*Boolean?*/omitModuleCheck){
+// summary:
+//	loads a Javascript module from the appropriate URI
+//
+// description:
+//	loadModule("A.B") first checks to see if symbol A.B is defined. 
+//	If it is, it is simply returned (nothing to do).
+//	
+//	If it is not defined, it will look for "A/B.js" in the script root directory,
+//	followed by "A.js".
+//	
+//	It throws if it cannot find a file to load, or if the symbol A.B is not
+//	defined after loading.
+//	
+//	It returns the object A.B.
+//	
+//	This does nothing about importing symbols into the current package.
+//	It is presumed that the caller will take care of that. For example, to import
+//	all symbols:
+//	
+//	   with (dojo.hostenv.loadModule("A.B")) {
+//	      ...
+//	   }
+//	
+//	And to import just the leaf symbol:
+//	
+//	   var B = dojo.hostenv.loadModule("A.B");
+//	   ...
+//	
+//	dj_load is an alias for dojo.hostenv.loadModule
+
+	if(!moduleName){ return; }
+	omitModuleCheck = this._global_omit_module_check || omitModuleCheck;
+	var module = this.findModule(moduleName, false);
+	if(module){
+		return module;
+	}
+
+	// protect against infinite recursion from mutual dependencies
+	if(dj_undef(moduleName, this.loading_modules_)){
+		this.addedToLoadingCount.push(moduleName);
+	}
+	this.loading_modules_[moduleName] = 1;
+
+	// convert periods to slashes
+	var relpath = moduleName.replace(/\./g, '/') + '.js';
+
+	var nsyms = moduleName.split(".");
+	
+	// this line allowed loading of a module manifest as if it were a namespace
+	// it's an interesting idea, but shouldn't be combined with 'namespaces' proper
+	// and leads to unwanted dependencies
+	// the effect can be achieved in other (albeit less-flexible) ways now, so I am
+	// removing this pending further design work
+	// perhaps we can explicitly define this idea of a 'module manifest', and subclass
+	// 'namespace manifest' from that
+	//dojo.getNamespace(nsyms[0]);
+
+	var syms = this.getModuleSymbols(moduleName);
+	var startedRelative = ((syms[0].charAt(0) != '/') && !syms[0].match(/^\w+:/));
+	var last = syms[syms.length - 1];
+	var ok;
+	// figure out if we're looking for a full package, if so, we want to do
+	// things slightly diffrently
+	if(last=="*"){
+		moduleName = nsyms.slice(0, -1).join('.');
+		while(syms.length){
+			syms.pop();
+			syms.push(this.pkgFileName);
+			relpath = syms.join("/") + '.js';
+			if(startedRelative && relpath.charAt(0)=="/"){
+				relpath = relpath.slice(1);
+			}
+			ok = this.loadPath(relpath, !omitModuleCheck ? moduleName : null);
+			if(ok){ break; }
+			syms.pop();
+		}
+	}else{
+		relpath = syms.join("/") + '.js';
+		moduleName = nsyms.join('.');
+		var modArg = !omitModuleCheck ? moduleName : null;
+		ok = this.loadPath(relpath, modArg);
+		if(!ok && !exactOnly){
+			syms.pop();
+			while(syms.length){
+				relpath = syms.join('/') + '.js';
+				ok = this.loadPath(relpath, modArg);
+				if(ok){ break; }
+				syms.pop();
+				relpath = syms.join('/') + '/'+this.pkgFileName+'.js';
+				if(startedRelative && relpath.charAt(0)=="/"){
+					relpath = relpath.slice(1);
+				}
+				ok = this.loadPath(relpath, modArg);
+				if(ok){ break; }
+			}
+		}
+
+		if(!ok && !omitModuleCheck){
+			dojo.raise("Could not load '" + moduleName + "'; last tried '" + relpath + "'");
+		}
+	}
+
+	// check that the symbol was defined
+	//Don't bother if we're doing xdomain (asynchronous) loading.
+	if(!omitModuleCheck && !this["isXDomain"]){
+		// pass in false so we can give better error
+		module = this.findModule(moduleName, false);
+		if(!module){
+			dojo.raise("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'"); 
+		}
+	}
+
+	return module;
+}
+
+dojo.hostenv.startPackage = function(/*String*/packageName){
+// summary:
+//	Creates a JavaScript package
+//
+// description:
+//	startPackage("A.B") follows the path, and at each level creates a new empty
+//	object or uses what already exists. It returns the result.
+//
+// packageName: the package to be created as a String in dot notation
+
+	//Make sure we have a string.
+	var fullPkgName = String(packageName);
+	var strippedPkgName = fullPkgName;
+
+	var syms = packageName.split(/\./);
+	if(syms[syms.length-1]=="*"){
+		syms.pop();
+		strippedPkgName = syms.join(".");
+	}
+	var evaledPkg = dojo.evalObjPath(strippedPkgName, true);
+	this.loaded_modules_[fullPkgName] = evaledPkg;
+	this.loaded_modules_[strippedPkgName] = evaledPkg;
+	
+	return evaledPkg; // Object
+}
+
+dojo.hostenv.findModule = function(/*String*/moduleName, /*Boolean?*/mustExist){
+// summary:
+//	Returns the Object representing the module, if it exists, otherwise null.
+//
+// moduleName A fully qualified module including package name, like 'A.B'.
+// mustExist Optional, default false. throw instead of returning null
+//	if the module does not currently exist.
+
+	var lmn = String(moduleName);
+
+	if(this.loaded_modules_[lmn]){
+		return this.loaded_modules_[lmn]; // Object
+	}
+
+	if(mustExist){
+		dojo.raise("no loaded module named '" + moduleName + "'");
+	}
+	return null; // null
+}
+
+//Start of old bootstrap2:
+
+dojo.kwCompoundRequire = function(/*Object containing Arrays*/modMap){
+// description:
+//	This method taks a "map" of arrays which one can use to optionally load dojo
+//	modules. The map is indexed by the possible dojo.hostenv.name_ values, with
+//	two additional values: "default" and "common". The items in the "default"
+//	array will be loaded if none of the other items have been choosen based on
+//	the hostenv.name_ item. The items in the "common" array will _always_ be
+//	loaded, regardless of which list is chosen.  Here's how it's normally
+//	called:
+//	
+//	dojo.kwCompoundRequire({
+//		browser: [
+//			["foo.bar.baz", true, true], // an example that passes multiple args to loadModule()
+//			"foo.sample.*",
+//			"foo.test,
+//		],
+//		default: [ "foo.sample.*" ],
+//		common: [ "really.important.module.*" ]
+//	});
+
+	var common = modMap["common"]||[];
+	var result = modMap[dojo.hostenv.name_] ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]);
+
+	for(var x=0; x<result.length; x++){
+		var curr = result[x];
+		if(curr.constructor == Array){
+			dojo.hostenv.loadModule.apply(dojo.hostenv, curr);
+		}else{
+			dojo.hostenv.loadModule(curr);
+		}
+	}
+}
+
+dojo.require = function(/*String*/ resourceName){
+	// summary
+	//	Ensure that the given resource (ie, javascript
+	//	source file) has been loaded.
+	// description
+	//	dojo.require() is similar to C's #include command or java's "import" command.
+	//	You call dojo.require() to pull in the resources (ie, javascript source files)
+	//	that define the functions you are using. 
+	//
+	//	Note that in the case of a build, many resources have already been included
+	//	into dojo.js (ie, many of the javascript source files have been compressed and
+	//	concatened into dojo.js), so many dojo.require() calls will simply return
+	//	without downloading anything.
+	dojo.hostenv.loadModule.apply(dojo.hostenv, arguments);
+}
+
+dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){
+	// summary
+	//	If the condition is true then call dojo.require() for the specified resource
+	var arg0 = arguments[0];
+	if((arg0 === true)||(arg0=="common")||(arg0 && dojo.render[arg0].capable)){
+		var args = [];
+		for (var i = 1; i < arguments.length; i++) { args.push(arguments[i]); }
+		dojo.require.apply(dojo, args);
+	}
+}
+
+dojo.requireAfterIf = dojo.requireIf;
+
+dojo.provide = function(/*String*/ resourceName){
+	// summary
+	//	Each javascript source file must have (exactly) one dojo.provide()
+	//	call at the top of the file, corresponding to the file name.
+	//	For example, dojo/src/foo.js must have dojo.provide("dojo.foo"); at the top of the file.
+	//
+	// description
+	//	Each javascript source file is called a resource.  When a resource
+	//	is loaded by the browser, dojo.provide() registers that it has
+	//	been loaded.
+	//	
+	//	For backwards compatibility reasons, in addition to registering the resource,
+	//	dojo.provide() also ensures that the javascript object for the module exists.  For
+	//	example, dojo.provide("dojo.html.common"), in addition to registering that common.js
+	//	is a resource for the dojo.html module, will ensure that the dojo.html javascript object
+	//	exists, so that calls like dojo.html.foo = function(){ ... } don't fail.
+	//
+	//	In the case of a build (or in the future, a rollup), where multiple javascript source
+	//	files are combined into one bigger file (similar to a .lib or .jar file), that file
+	//	will contain multiple dojo.provide() calls, to note that it includes
+	//	multiple resources.
+	return dojo.hostenv.startPackage.apply(dojo.hostenv, arguments);
+}
+
+dojo.registerModulePath = function(/*String*/module, /*String*/prefix){
+	// summary: maps a module name to a path
+	// description: An unregistered module is given the default path of ../<module>,
+	//	relative to Dojo root. For example, module acme is mapped to ../acme.
+	//	If you want to use a different module name, use dojo.registerModulePath. 
+	return dojo.hostenv.setModulePrefix(module, prefix);
+}
+
+dojo.setModulePrefix = function(/*String*/module, /*String*/prefix){
+	// summary: maps a module name to a path
+	dojo.deprecated('dojo.setModulePrefix("' + module + '", "' + prefix + '")', "replaced by dojo.registerModulePath", "0.5");
+	return dojo.registerModulePath(module, prefix);
+}
+
+dojo.exists = function(/*Object*/obj, /*String*/name){
+	// summary: determine if an object supports a given method
+	// description: useful for longer api chains where you have to test each object in the chain
+	var p = name.split(".");
+	for(var i = 0; i < p.length; i++){
+		if(!obj[p[i]]){ return false; } // Boolean
+		obj = obj[p[i]];
+	}
+	return true; // Boolean
+}
+
+// Localization routines
+
+dojo.hostenv.normalizeLocale = function(/*String?*/locale){
+//	summary:
+//		Returns canonical form of locale, as used by Dojo.  All variants are case-insensitive and are separated by '-'
+//		as specified in RFC 3066. If no locale is specified, the user agent's default is returned.
+
+	return locale ? locale.toLowerCase() : dojo.locale; // String
+};
+
+dojo.hostenv.searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
+//	summary:
+//		A helper method to assist in searching for locale-based resources.  Will iterate through
+//		the variants of a particular locale, either up or down, executing a callback function.
+//		For example, "en-us" and true will try "en-us" followed by "en" and finally "ROOT".
+
+	locale = dojo.hostenv.normalizeLocale(locale);
+
+	var elements = locale.split('-');
+	var searchlist = [];
+	for(var i = elements.length; i > 0; i--){
+		searchlist.push(elements.slice(0, i).join('-'));
+	}
+	searchlist.push(false);
+	if(down){searchlist.reverse();}
+
+	for(var j = searchlist.length - 1; j >= 0; j--){
+		var loc = searchlist[j] || "ROOT";
+		var stop = searchFunc(loc);
+		if(stop){ break; }
+	}
+}
+
+//These two functions are placed outside of preloadLocalizations
+//So that the xd loading can use/override them.
+dojo.hostenv.localesGenerated /***BUILD:localesGenerated***/; // value will be inserted here at build time, if necessary
+
+dojo.hostenv.registerNlsPrefix = function(){
+// summary:
+//	Register module "nls" to point where Dojo can find pre-built localization files
+	dojo.registerModulePath("nls","nls");	
+}
+
+dojo.hostenv.preloadLocalizations = function(){
+// summary:
+//	Load built, flattened resource bundles, if available for all locales used in the page.
+//	Execute only once.  Note that this is a no-op unless there is a build.
+
+	if(dojo.hostenv.localesGenerated){
+		dojo.hostenv.registerNlsPrefix();
+
+		function preload(locale){
+			locale = dojo.hostenv.normalizeLocale(locale);
+			dojo.hostenv.searchLocalePath(locale, true, function(loc){
+				for(var i=0; i<dojo.hostenv.localesGenerated.length;i++){
+					if(dojo.hostenv.localesGenerated[i] == loc){
+						dojo["require"]("nls.dojo_"+loc);
+						return true; // Boolean
+					}
+				}
+				return false; // Boolean
+			});
+		}
+		preload();
+		var extra = djConfig.extraLocale||[];
+		for(var i=0; i<extra.length; i++){
+			preload(extra[i]);
+		}
+	}
+	dojo.hostenv.preloadLocalizations = function(){};
+}
+
+dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale){
+// summary:
+//	Declares translated resources and loads them if necessary, in the same style as dojo.require.
+//	Contents of the resource bundle are typically strings, but may be any name/value pair,
+//	represented in JSON format.  See also dojo.i18n.getLocalization.
+//
+// moduleName: name of the package containing the "nls" directory in which the bundle is found
+// bundleName: bundle name, i.e. the filename without the '.js' suffix
+// locale: the locale to load (optional)  By default, the browser's user locale as defined by dojo.locale
+//
+// description:
+//	Load translated resource bundles provided underneath the "nls" directory within a package.
+//	Translated resources may be located in different packages throughout the source tree.  For example,
+//	a particular widget may define one or more resource bundles, structured in a program as follows,
+//	where moduleName is mycode.mywidget and bundleNames available include bundleone and bundletwo:
+//	...
+//	mycode/
+//	 mywidget/
+//	  nls/
+//	   bundleone.js (the fallback translation, English in this example)
+//	   bundletwo.js (also a fallback translation)
+//	   de/
+//	    bundleone.js
+//	    bundletwo.js
+//	   de-at/
+//	    bundleone.js
+//	   en/
+//	    (empty; use the fallback translation)
+//	   en-us/
+//	    bundleone.js
+//	   en-gb/
+//	    bundleone.js
+//	   es/
+//	    bundleone.js
+//	    bundletwo.js
+//	  ...etc
+//	...
+//	Each directory is named for a locale as specified by RFC 3066, (http://www.ietf.org/rfc/rfc3066.txt),
+//	normalized in lowercase.  Note that the two bundles in the example do not define all the same variants.
+//	For a given locale, bundles will be loaded for that locale and all more general locales above it, including
+//	a fallback at the root directory.  For example, a declaration for the "de-at" locale will first
+//	load nls/de-at/bundleone.js, then nls/de/bundleone.js and finally nls/bundleone.js.  The data will
+//	be flattened into a single Object so that lookups will follow this cascading pattern.  An optional build
+//	step can preload the bundles to avoid data redundancy and the multiple network hits normally required to
+//	load these resources.
+
+	dojo.hostenv.preloadLocalizations();
+ 	var bundlePackage = [moduleName, "nls", bundleName].join(".");
+//NOTE: When loading these resources, the packaging does not match what is on disk.  This is an
+// implementation detail, as this is just a private data structure to hold the loaded resources.
+// e.g. tests/hello/nls/en-us/salutations.js is loaded as the object tests.hello.nls.salutations.en_us={...}
+// The structure on disk is intended to be most convenient for developers and translators, but in memory
+// it is more logical and efficient to store in a different order.  Locales cannot use dashes, since the
+// resulting path will not evaluate as valid JS, so we translate them to underscores.
+
+	var bundle = dojo.hostenv.findModule(bundlePackage);
+	if(bundle){
+		if(djConfig.localizationComplete && bundle._built){return;}
+		var jsLoc = dojo.hostenv.normalizeLocale(locale).replace('-', '_');
+		var translationPackage = bundlePackage+"."+jsLoc;
+		if(dojo.hostenv.findModule(translationPackage)){return;}
+	}
+
+	bundle = dojo.hostenv.startPackage(bundlePackage);
+	var syms = dojo.hostenv.getModuleSymbols(moduleName);
+	var modpath = syms.concat("nls").join("/");
+	var parent;
+	dojo.hostenv.searchLocalePath(locale, false, function(loc){
+		var jsLoc = loc.replace('-', '_');
+		var translationPackage = bundlePackage + "." + jsLoc;
+		var loaded = false;
+		if(!dojo.hostenv.findModule(translationPackage)){
+			// Mark loaded whether it's found or not, so that further load attempts will not be made
+			dojo.hostenv.startPackage(translationPackage);
+			var module = [modpath];
+			if(loc != "ROOT"){module.push(loc);}
+			module.push(bundleName);
+			var filespec = module.join("/") + '.js';
+			loaded = dojo.hostenv.loadPath(filespec, null, function(hash){
+				// Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
+				var clazz = function(){};
+				clazz.prototype = parent;
+				bundle[jsLoc] = new clazz();
+				for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
+			});
+		}else{
+			loaded = true;
+		}
+		if(loaded && bundle[jsLoc]){
+			parent = bundle[jsLoc];
+		}else{
+			bundle[jsLoc] = parent;
+		}
+	});
+};
+
+(function(){
+	// If other locales are used, dojo.requireLocalization should load them as well, by default.
+	// Override dojo.requireLocalization to do load the default bundle, then iterate through the
+	// extraLocale list and load those translations as well, unless a particular locale was requested.
+
+	var extra = djConfig.extraLocale;
+	if(extra){
+		if(!extra instanceof Array){
+			extra = [extra];
+		}
+
+		var req = dojo.requireLocalization;
+		dojo.requireLocalization = function(m, b, locale){
+			req(m,b,locale);
+			if(locale){return;}
+			for(var i=0; i<extra.length; i++){
+				req(m,b,extra[i]);
+			}
+		};
+	}
+})();
+
+};
+
+if (typeof window != 'undefined') {
+
+// attempt to figure out the path to dojo if it isn't set in the config
+(function() {
+	// before we get any further with the config options, try to pick them out
+	// of the URL. Most of this code is from NW
+	if(djConfig.allowQueryConfig){
+		var baseUrl = document.location.toString(); // FIXME: use location.query instead?
+		var params = baseUrl.split("?", 2);
+		if(params.length > 1){
+			var paramStr = params[1];
+			var pairs = paramStr.split("&");
+			for(var x in pairs){
+				var sp = pairs[x].split("=");
+				// FIXME: is this eval dangerous?
+				if((sp[0].length > 9)&&(sp[0].substr(0, 9) == "djConfig.")){
+					var opt = sp[0].substr(9);
+					try{
+						djConfig[opt]=eval(sp[1]);
+					}catch(e){
+						djConfig[opt]=sp[1];
+					}
+				}
+			}
+		}
+	}
+
+	if(((djConfig["baseScriptUri"] == "")||(djConfig["baseRelativePath"] == "")) &&(document && document.getElementsByTagName)){
+		var scripts = document.getElementsByTagName("script");
+		var rePkg = /(__package__|dojo|bootstrap1)\.js([\?\.]|$)/i;
+		for(var i = 0; i < scripts.length; i++) {
+			var src = scripts[i].getAttribute("src");
+			if(!src) { continue; }
+			var m = src.match(rePkg);
+			if(m) {
+				var root = src.substring(0, m.index);
+				if(src.indexOf("bootstrap1") > -1) { root += "../"; }
+				if(!this["djConfig"]) { djConfig = {}; }
+				if(djConfig["baseScriptUri"] == "") { djConfig["baseScriptUri"] = root; }
+				if(djConfig["baseRelativePath"] == "") { djConfig["baseRelativePath"] = root; }
+				break;
+			}
+		}
+	}
+
+	// fill in the rendering support information in dojo.render.*
+	var dr = dojo.render;
+	var drh = dojo.render.html;
+	var drs = dojo.render.svg;
+	var dua = (drh.UA = navigator.userAgent);
+	var dav = (drh.AV = navigator.appVersion);
+	var t = true;
+	var f = false;
+	drh.capable = t;
+	drh.support.builtin = t;
+
+	dr.ver = parseFloat(drh.AV);
+	dr.os.mac = dav.indexOf("Macintosh") >= 0;
+	dr.os.win = dav.indexOf("Windows") >= 0;
+	// could also be Solaris or something, but it's the same browser
+	dr.os.linux = dav.indexOf("X11") >= 0;
+
+	drh.opera = dua.indexOf("Opera") >= 0;
+	drh.khtml = (dav.indexOf("Konqueror") >= 0)||(dav.indexOf("Safari") >= 0);
+	drh.safari = dav.indexOf("Safari") >= 0;
+	var geckoPos = dua.indexOf("Gecko");
+	drh.mozilla = drh.moz = (geckoPos >= 0)&&(!drh.khtml);
+	if (drh.mozilla) {
+		// gecko version is YYYYMMDD
+		drh.geckoVersion = dua.substring(geckoPos + 6, geckoPos + 14);
+	}
+	drh.ie = (document.all)&&(!drh.opera);
+	drh.ie50 = drh.ie && dav.indexOf("MSIE 5.0")>=0;
+	drh.ie55 = drh.ie && dav.indexOf("MSIE 5.5")>=0;
+	drh.ie60 = drh.ie && dav.indexOf("MSIE 6.0")>=0;
+	drh.ie70 = drh.ie && dav.indexOf("MSIE 7.0")>=0;
+
+	var cm = document["compatMode"];
+	drh.quirks = (cm == "BackCompat")||(cm == "QuirksMode")||drh.ie55||drh.ie50;
+
+	// TODO: is the HTML LANG attribute relevant?
+	dojo.locale = dojo.locale || (drh.ie ? navigator.userLanguage : navigator.language).toLowerCase();
+
+	dr.vml.capable=drh.ie;
+	drs.capable = f;
+	drs.support.plugin = f;
+	drs.support.builtin = f;
+	var tdoc = window["document"];
+	var tdi = tdoc["implementation"];
+
+	if((tdi)&&(tdi["hasFeature"])&&(tdi.hasFeature("org.w3c.dom.svg", "1.0"))){
+		drs.capable = t;
+		drs.support.builtin = t;
+		drs.support.plugin = f;
+	}
+	// webkits after 420 support SVG natively. The test string is "AppleWebKit/420+"
+	if(drh.safari){
+		var tmp = dua.split("AppleWebKit/")[1];
+		var ver = parseFloat(tmp.split(" ")[0]);
+		if(ver >= 420){
+			drs.capable = t;
+			drs.support.builtin = t;
+			drs.support.plugin = f;
+		}
+	}
+})();
+
+dojo.hostenv.startPackage("dojo.hostenv");
+
+dojo.render.name = dojo.hostenv.name_ = 'browser';
+dojo.hostenv.searchIds = [];
+
+// These are in order of decreasing likelihood; this will change in time.
+dojo.hostenv._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
+
+dojo.hostenv.getXmlhttpObject = function(){
+    var http = null;
+	var last_e = null;
+	try{ http = new XMLHttpRequest(); }catch(e){}
+    if(!http){
+		for(var i=0; i<3; ++i){
+			var progid = dojo.hostenv._XMLHTTP_PROGIDS[i];
+			try{
+				http = new ActiveXObject(progid);
+			}catch(e){
+				last_e = e;
+			}
+
+			if(http){
+				dojo.hostenv._XMLHTTP_PROGIDS = [progid];  // so faster next time
+				break;
+			}
+		}
+
+		/*if(http && !http.toString) {
+			http.toString = function() { "[object XMLHttpRequest]"; }
+		}*/
+	}
+
+	if(!http){
+		return dojo.raise("XMLHTTP not available", last_e);
+	}
+
+	return http;
+}
+
+/**
+ * Read the contents of the specified uri and return those contents.
+ *
+ * @param uri A relative or absolute uri. If absolute, it still must be in the
+ * same "domain" as we are.
+ *
+ * @param async_cb If not specified, load synchronously. If specified, load
+ * asynchronously, and use async_cb as the progress handler which takes the
+ * xmlhttp object as its argument. If async_cb, this function returns null.
+ *
+ * @param fail_ok Default false. If fail_ok and !async_cb and loading fails,
+ * return null instead of throwing.
+ */
+dojo.hostenv._blockAsync = false;
+dojo.hostenv.getText = function(uri, async_cb, fail_ok){
+	// need to block async callbacks from snatching this thread as the result
+	// of an async callback might call another sync XHR, this hangs khtml forever
+	// hostenv._blockAsync must also be checked in BrowserIO's watchInFlight()
+	// NOTE: must be declared before scope switches ie. this.getXmlhttpObject()
+	if(!async_cb){ this._blockAsync = true; }
+
+	var http = this.getXmlhttpObject();
+
+	function isDocumentOk(http){
+		var stat = http["status"];
+		// allow a 304 use cache, needed in konq (is this compliant with the http spec?)
+		return Boolean((!stat)||((200 <= stat)&&(300 > stat))||(stat==304));
+	}
+
+	if(async_cb){
+		var _this = this, timer = null, gbl = dojo.global();
+		var xhr = dojo.evalObjPath("dojo.io.XMLHTTPTransport");
+		http.onreadystatechange = function(){
+			if(timer){ gbl.clearTimeout(timer); timer = null; }
+			if(_this._blockAsync || (xhr && xhr._blockAsync)){
+				timer = gbl.setTimeout(function () { http.onreadystatechange.apply(this); }, 10);
+			}else{
+				if(4==http.readyState){
+					if(isDocumentOk(http)){
+						// dojo.debug("LOADED URI: "+uri);
+						async_cb(http.responseText);
+					}
+				}
+			}
+		}
+	}
+
+	http.open('GET', uri, async_cb ? true : false);
+	try{
+		http.send(null);
+		if(async_cb){
+			return null;
+		}
+		if(!isDocumentOk(http)){
+			var err = Error("Unable to load "+uri+" status:"+ http.status);
+			err.status = http.status;
+			err.responseText = http.responseText;
+			throw err;
+		}
+	}catch(e){
+		this._blockAsync = false;
+		if((fail_ok)&&(!async_cb)){
+			return null;
+		}else{
+			throw e;
+		}
+	}
+
+	this._blockAsync = false;
+	return http.responseText;
+}
+
+/*
+ * It turns out that if we check *right now*, as this script file is being loaded,
+ * then the last script element in the window DOM is ourselves.
+ * That is because any subsequent script elements haven't shown up in the document
+ * object yet.
+ */
+ /*
+function dj_last_script_src() {
+    var scripts = window.document.getElementsByTagName('script');
+    if(scripts.length < 1){
+		dojo.raise("No script elements in window.document, so can't figure out my script src");
+	}
+    var script = scripts[scripts.length - 1];
+    var src = script.src;
+    if(!src){
+		dojo.raise("Last script element (out of " + scripts.length + ") has no src");
+	}
+    return src;
+}
+
+if(!dojo.hostenv["library_script_uri_"]){
+	dojo.hostenv.library_script_uri_ = dj_last_script_src();
+}
+*/
+
+dojo.hostenv.defaultDebugContainerId = 'dojoDebug';
+dojo.hostenv._println_buffer = [];
+dojo.hostenv._println_safe = false;
+dojo.hostenv.println = function (line){
+	if(!dojo.hostenv._println_safe){
+		dojo.hostenv._println_buffer.push(line);
+	}else{
+		try {
+			var console = document.getElementById(djConfig.debugContainerId ?
+				djConfig.debugContainerId : dojo.hostenv.defaultDebugContainerId);
+			if(!console) { console = dojo.body(); }
+
+			var div = document.createElement("div");
+			div.appendChild(document.createTextNode(line));
+			console.appendChild(div);
+		} catch (e) {
+			try{
+				// safari needs the output wrapped in an element for some reason
+				document.write("<div>" + line + "</div>");
+			}catch(e2){
+				window.status = line;
+			}
+		}
+	}
+}
+
+dojo.addOnLoad(function(){
+	dojo.hostenv._println_safe = true;
+	while(dojo.hostenv._println_buffer.length > 0){
+		dojo.hostenv.println(dojo.hostenv._println_buffer.shift());
+	}
+});
+
+function dj_addNodeEvtHdlr(node, evtName, fp, capture){
+	var oldHandler = node["on"+evtName] || function(){};
+	node["on"+evtName] = function(){
+		fp.apply(node, arguments);
+		oldHandler.apply(node, arguments);
+	}
+	return true;
+}
+
+//	BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
+function dj_load_init(e){
+	// allow multiple calls, only first one will take effect
+	// A bug in khtml calls events callbacks for document for event which isnt supported
+	// for example a created contextmenu event calls DOMContentLoaded, workaround
+	var type = (e && e.type) ? e.type.toLowerCase() : "load";
+	if(arguments.callee.initialized || (type!="domcontentloaded" && type!="load")){ return; }
+	arguments.callee.initialized = true;
+	if(typeof(_timer) != 'undefined'){
+		clearInterval(_timer);
+		delete _timer;
+	}
+
+	var initFunc = function(){
+		//perform initialization
+		if(dojo.render.html.ie){
+			dojo.hostenv.makeWidgets();
+		}
+	};
+
+	if(dojo.hostenv.inFlightCount == 0){
+		initFunc();
+		dojo.hostenv.modulesLoaded();
+	}else{
+		dojo.addOnLoad(initFunc);
+	}
+}
+
+//	START DOMContentLoaded
+// Mozilla and Opera 9 expose the event we could use
+if(document.addEventListener){
+	if(dojo.render.html.opera || (dojo.render.html.moz && !djConfig.delayMozLoadingFix)){
+		document.addEventListener("DOMContentLoaded", dj_load_init, null);
+	}
+
+	//	mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.
+	//  also used for Mozilla because of trac #1640
+	window.addEventListener("load", dj_load_init, null);
+}
+
+// 	for Internet Explorer. readyState will not be achieved on init call, but dojo doesn't need it
+//	however, we'll include it because we don't know if there are other functions added that might.
+//	Note that this has changed because the build process strips all comments--including conditional
+//		ones.
+if(dojo.render.html.ie && dojo.render.os.win){
+	document.attachEvent("onreadystatechange", function(e){
+		if(document.readyState == "complete"){
+			dj_load_init();
+		}
+	});
+}
+
+if (/(WebKit|khtml)/i.test(navigator.userAgent)) { // sniff
+    var _timer = setInterval(function() {
+        if (/loaded|complete/.test(document.readyState)) {
+            dj_load_init(); // call the onload handler
+        }
+    }, 10);
+}
+//	END DOMContentLoaded
+
+// IE WebControl hosted in an application can fire "beforeunload" and "unload"
+// events when control visibility changes, causing Dojo to unload too soon. The
+// following code fixes the problem
+// Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;199155
+if(dojo.render.html.ie){
+	dj_addNodeEvtHdlr(window, "beforeunload", function(){
+		dojo.hostenv._unloading = true;
+		window.setTimeout(function() {
+			dojo.hostenv._unloading = false;
+		}, 0);
+	});
+}
+
+dj_addNodeEvtHdlr(window, "unload", function(){
+	dojo.hostenv.unloaded();
+	if((!dojo.render.html.ie)||(dojo.render.html.ie && dojo.hostenv._unloading)){
+		dojo.hostenv.unloaded();
+	}
+});
+
+dojo.hostenv.makeWidgets = function(){
+	// you can put searchIds in djConfig and dojo.hostenv at the moment
+	// we should probably eventually move to one or the other
+	var sids = [];
+	if(djConfig.searchIds && djConfig.searchIds.length > 0) {
+		sids = sids.concat(djConfig.searchIds);
+	}
+	if(dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) {
+		sids = sids.concat(dojo.hostenv.searchIds);
+	}
+
+	if((djConfig.parseWidgets)||(sids.length > 0)){
+		if(dojo.evalObjPath("dojo.widget.Parse")){
+			// we must do this on a delay to avoid:
+			//	http://www.shaftek.org/blog/archives/000212.html
+			// (IE bug)
+				var parser = new dojo.xml.Parse();
+				if(sids.length > 0){
+					for(var x=0; x<sids.length; x++){
+						var tmpNode = document.getElementById(sids[x]);
+						if(!tmpNode){ continue; }
+						var frag = parser.parseElement(tmpNode, null, true);
+						dojo.widget.getParser().createComponents(frag);
+					}
+				}else if(djConfig.parseWidgets){
+					var frag  = parser.parseElement(dojo.body(), null, true);
+					dojo.widget.getParser().createComponents(frag);
+				}
+		}
+	}
+}
+
+dojo.addOnLoad(function(){
+	if(!dojo.render.html.ie) {
+		dojo.hostenv.makeWidgets();
+	}
+});
+
+try {
+	if (dojo.render.html.ie) {
+		document.namespaces.add("v","urn:schemas-microsoft-com:vml");
+		document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
+	}
+} catch (e) { }
+
+// stub, over-ridden by debugging code. This will at least keep us from
+// breaking when it's not included
+dojo.hostenv.writeIncludes = function(){}
+
+//TODOC:  HOW TO DOC THIS?
+// @global: dj_currentDocument
+// summary:
+//		Current document object. 'dj_currentDocument' can be modified for temporary context shifting.
+// description:
+//    dojo.doc() returns dojo.currentDocument.
+//		Refer to dojo.doc() rather than referring to 'window.document' to ensure your
+//		code runs correctly in managed contexts.
+if(!dj_undef("document", this)){
+	dj_currentDocument = this.document;
+}
+
+dojo.doc = function(){
+	// summary:
+	//		return the document object associated with the dojo.global()
+	return dj_currentDocument;
+}
+
+dojo.body = function(){
+	// summary:
+	//		return the body object associated with dojo.doc()
+	// Note: document.body is not defined for a strict xhtml document
+	return dojo.doc().body || dojo.doc().getElementsByTagName("body")[0];
+}
+
+dojo.byId = function(id, doc){
+	if((id)&&((typeof id == "string")||(id instanceof String))){
+		if (!doc) { doc = dj_currentDocument; }
+		var ele = doc.getElementById(id);
+		// workaround bug in IE and Opera 8.2 where getElementById returns wrong element
+		if (ele && (ele.id != id) && doc.all) {
+			ele = null;
+			// get all matching elements with this id
+			eles = doc.all[id];
+			if (eles) {
+				// if more than 1, choose first with the correct id
+				if (eles.length) {
+					for (var i=0; i < eles.length; i++) {
+						if (eles[i].id == id) {
+							ele = eles[i];
+							break;
+						}
+					}
+				// return 1 and only element
+				} else { ele = eles; }
+			}
+		}
+		return ele;
+	}
+	return id; // assume it's a node
+}
+
+dojo.setContext = function(/*Object*/globalObject, /*Object*/ globalDocument){
+	dj_currentContext = globalObject;
+	dj_currentDocument = globalDocument;
+};
+
+dojo._fireCallback = function(callback, context, cbArguments) {
+	if((context)&&((typeof callback == "string")||(callback instanceof String))){
+		callback=context[callback];
+	}
+	return (context ? callback.apply(context, cbArguments || [ ]) : callback());
+}
+
+dojo.withGlobal = function(/*Object*/globalObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments){
+	// summary:
+	//		Call callback with globalObject as dojo.global() and globalObject.document
+	//		as dojo.doc(). If provided, globalObject will be executed in the context of
+	//		object thisObject
+	// description:
+	//		When callback() returns or throws an error, the dojo.global() and dojo.doc() will
+	//		be restored to its previous state.
+	var rval;
+	var oldGlob = dj_currentContext;
+	var oldDoc = dj_currentDocument;
+	try{
+		dojo.setContext(globalObject, globalObject.document);
+		rval = dojo._fireCallback(callback, thisObject, cbArguments);
+	}finally{
+		dojo.setContext(oldGlob, oldDoc);
+	}
+	return rval;
+}
+
+dojo.withDoc = function (/*Object*/documentObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments) {
+	// summary:
+	//		Call callback with documentObject as dojo.doc(). If provided, callback will be executed
+	//		in the context of object thisObject
+	// description:
+	//		When callback() returns or throws an error, the dojo.doc() will
+	//		be restored to its previous state.
+	var rval;
+	var oldDoc = dj_currentDocument;
+	try{
+		dj_currentDocument = documentObject;
+		rval = dojo._fireCallback(callback, thisObject, cbArguments);
+	}finally{
+		dj_currentDocument = oldDoc;
+	}
+	return rval;
+}
+
+} //if (typeof window != 'undefined')
+
+//Semicolon is for when this file is integrated with a custom build on one line
+//with some other file's contents. Sometimes that makes things not get defined
+//properly, particularly with the using the closure below to do all the work.
+;(function(){
+	//Don't do this work if dojo.js has already done it.
+	if(typeof dj_usingBootstrap != "undefined"){
+		return;
+	}
+
+	var isRhino = false;
+	var isSpidermonkey = false;
+	var isDashboard = false;
+	if((typeof this["load"] == "function")&&((typeof this["Packages"] == "function")||(typeof this["Packages"] == "object"))){
+		isRhino = true;
+	}else if(typeof this["load"] == "function"){
+		isSpidermonkey  = true;
+	}else if(window.widget){
+		isDashboard = true;
+	}
+
+	var tmps = [];
+	if((this["djConfig"])&&((djConfig["isDebug"])||(djConfig["debugAtAllCosts"]))){
+		tmps.push("debug.js");
+	}
+
+	if((this["djConfig"])&&(djConfig["debugAtAllCosts"])&&(!isRhino)&&(!isDashboard)){
+		tmps.push("browser_debug.js");
+	}
+
+	var loaderRoot = djConfig["baseScriptUri"];
+	if((this["djConfig"])&&(djConfig["baseLoaderUri"])){
+		loaderRoot = djConfig["baseLoaderUri"];
+	}
+
+	for(var x=0; x < tmps.length; x++){
+		var spath = loaderRoot+"src/"+tmps[x];
+		if(isRhino||isSpidermonkey){
+			load(spath);
+		} else {
+			try {
+				document.write("<scr"+"ipt type='text/javascript' src='"+spath+"'></scr"+"ipt>");
+			} catch (e) {
+				var script = document.createElement("script");
+				script.src = spath;
+				document.getElementsByTagName("head")[0].appendChild(script);
+			}
+		}
+	}
+})();
+
+dojo.provide("dojo.string.common");
+
+dojo.string.trim = function(/* string */str, /* integer? */wh){
+	//	summary
+	//	Trim whitespace from str.  If wh > 0, trim from start, if wh < 0, trim from end, else both
+	if(!str.replace){ return str; }
+	if(!str.length){ return str; }
+	var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g);
+	return str.replace(re, "");	//	string
+}
+
+dojo.string.trimStart = function(/* string */str) {
+	//	summary
+	//	Trim whitespace at the beginning of 'str'
+	return dojo.string.trim(str, 1);	//	string
+}
+
+dojo.string.trimEnd = function(/* string */str) {
+	//	summary
+	//	Trim whitespace at the end of 'str'
+	return dojo.string.trim(str, -1);
+}
+
+dojo.string.repeat = function(/* string */str, /* integer */count, /* string? */separator) {
+	//	summary
+	//	Return 'str' repeated 'count' times, optionally placing 'separator' between each rep
+	var out = "";
+	for(var i = 0; i < count; i++) {
+		out += str;
+		if(separator && i < count - 1) {
+			out += separator;
+		}
+	}
+	return out;	//	string
+}
+
+dojo.string.pad = function(/* string */str, /* integer */len/*=2*/, /* string */ c/*='0'*/, /* integer */dir/*=1*/) {
+	//	summary
+	//	Pad 'str' to guarantee that it is at least 'len' length with the character 'c' at either the 
+	//	start (dir=1) or end (dir=-1) of the string
+	var out = String(str);
+	if(!c) {
+		c = '0';
+	}
+	if(!dir) {
+		dir = 1;
+	}
+	while(out.length < len) {
+		if(dir > 0) {
+			out = c + out;
+		} else {
+			out += c;
+		}
+	}
+	return out;	//	string
+}
+
+dojo.string.padLeft = function(/* string */str, /* integer */len, /* string */c) {
+	//	summary
+	//	same as dojo.string.pad(str, len, c, 1)
+	return dojo.string.pad(str, len, c, 1);	//	string
+}
+
+dojo.string.padRight = function(/* string */str, /* integer */len, /* string */c) {
+	//	summary
+	//	same as dojo.string.pad(str, len, c, -1)
+	return dojo.string.pad(str, len, c, -1);	//	string
+}
+
+dojo.provide("dojo.string");
+
+dojo.provide("dojo.lang.common");
+
+dojo.lang.inherits = function(/*Function*/ subclass, /*Function*/ superclass){
+	// summary: Set up inheritance between two classes.
+	if(typeof superclass != 'function'){ 
+		dojo.raise("dojo.inherits: superclass argument ["+superclass+"] must be a function (subclass: ["+subclass+"']");
+	}
+	subclass.prototype = new superclass();
+	subclass.prototype.constructor = subclass;
+	subclass.superclass = superclass.prototype;
+	// DEPRECATED: super is a reserved word, use 'superclass'
+	subclass['super'] = superclass.prototype;
+}
+
+dojo.lang._mixin = function(/*Object*/ obj, /*Object*/ props){
+	// summary:	Adds all properties and methods of props to obj.
+	var tobj = {};
+	for(var x in props){
+		// the "tobj" condition avoid copying properties in "props"
+		// inherited from Object.prototype.  For example, if obj has a custom
+		// toString() method, don't overwrite it with the toString() method
+		// that props inherited from Object.protoype
+		if((typeof tobj[x] == "undefined") || (tobj[x] != props[x])){
+			obj[x] = props[x];
+		}
+	}
+	// IE doesn't recognize custom toStrings in for..in
+	if(dojo.render.html.ie 
+		&& (typeof(props["toString"]) == "function")
+		&& (props["toString"] != obj["toString"])
+		&& (props["toString"] != tobj["toString"]))
+	{
+		obj.toString = props.toString;
+	}
+	return obj; // Object
+}
+
+dojo.lang.mixin = function(/*Object*/ obj, /*Object...*/props){
+	// summary:	Adds all properties and methods of props to obj.
+	for(var i=1, l=arguments.length; i<l; i++){
+		dojo.lang._mixin(obj, arguments[i]);
+	}
+	return obj; // Object
+}
+
+dojo.lang.extend = function(/*Object*/ constructor, /*Object...*/ props){
+	// summary:	Adds all properties and methods of props to constructor's prototype,
+	//			making them available to all instances created with constructor.
+	for(var i=1, l=arguments.length; i<l; i++){
+		dojo.lang._mixin(constructor.prototype, arguments[i]);
+	}
+	return constructor; // Object
+}
+
+// Promote to dojo module
+dojo.inherits = dojo.lang.inherits;
+//dojo.lang._mixin = dojo.lang._mixin;
+dojo.mixin = dojo.lang.mixin;
+dojo.extend = dojo.lang.extend;
+
+dojo.lang.find = function(	/*Array*/		array, 
+							/*Object*/		value,
+							/*Boolean?*/	identity,
+							/*Boolean?*/	findLast){
+	// summary:	Return the index of value in array, returning -1 if not found.
+	// identity: If true, matches with identity comparison (===).  
+	//					 If false, uses normal comparison (==).
+	// findLast: If true, returns index of last instance of value.
+	
+	// examples:
+	//  find(array, value[, identity [findLast]]) // recommended
+ 	//  find(value, array[, identity [findLast]]) // deprecated
+							
+	// support both (array, value) and (value, array)
+	if(!dojo.lang.isArrayLike(array) && dojo.lang.isArrayLike(value)) {
+		dojo.deprecated('dojo.lang.find(value, array)', 'use dojo.lang.find(array, value) instead', "0.5");
+		var temp = array;
+		array = value;
+		value = temp;
+	}
+	var isString = dojo.lang.isString(array);
+	if(isString) { array = array.split(""); }
+
+	if(findLast) {
+		var step = -1;
+		var i = array.length - 1;
+		var end = -1;
+	} else {
+		var step = 1;
+		var i = 0;
+		var end = array.length;
+	}
+	if(identity){
+		while(i != end) {
+			if(array[i] === value){ return i; }
+			i += step;
+		}
+	}else{
+		while(i != end) {
+			if(array[i] == value){ return i; }
+			i += step;
+		}
+	}
+	return -1;	// number
+}
+
+dojo.lang.indexOf = dojo.lang.find;
+
+dojo.lang.findLast = function(/*Array*/ array, /*Object*/ value, /*boolean?*/ identity){
+	// summary:	Return index of last occurance of value in array, returning -1 if not found.
+	// identity: If true, matches with identity comparison (===). If false, uses normal comparison (==).
+	return dojo.lang.find(array, value, identity, true); // number
+}
+
+dojo.lang.lastIndexOf = dojo.lang.findLast;
+
+dojo.lang.inArray = function(array /*Array*/, value /*Object*/){
+	// summary:	Return true if value is present in array.
+	return dojo.lang.find(array, value) > -1; // boolean
+}
+
+/**
+ * Partial implmentation of is* functions from
+ * http://www.crockford.com/javascript/recommend.html
+ * NOTE: some of these may not be the best thing to use in all situations
+ * as they aren't part of core JS and therefore can't work in every case.
+ * See WARNING messages inline for tips.
+ *
+ * The following is* functions are fairly "safe"
+ */
+
+dojo.lang.isObject = function(/*anything*/ it){
+	// summary:	Return true if it is an Object, Array or Function.
+	if(typeof it == "undefined"){ return false; }
+	return (typeof it == "object" || it === null || dojo.lang.isArray(it) || dojo.lang.isFunction(it)); // Boolean
+}
+
+dojo.lang.isArray = function(/*anything*/ it){
+	// summary:	Return true if it is an Array.
+	return (it && it instanceof Array || typeof it == "array"); // Boolean
+}
+
+dojo.lang.isArrayLike = function(/*anything*/ it){
+	// summary:	Return true if it can be used as an array (i.e. is an object with an integer length property).
+	if((!it)||(dojo.lang.isUndefined(it))){ return false; }
+	if(dojo.lang.isString(it)){ return false; }
+	if(dojo.lang.isFunction(it)){ return false; } // keeps out built-in constructors (Number, String, ...) which have length properties
+	if(dojo.lang.isArray(it)){ return true; }
+	// form node itself is ArrayLike, but not always iterable. Use form.elements instead.
+	if((it.tagName)&&(it.tagName.toLowerCase()=='form')){ return false; }
+	if(dojo.lang.isNumber(it.length) && isFinite(it.length)){ return true; }
+	return false; // Boolean
+}
+
+dojo.lang.isFunction = function(/*anything*/ it){
+	// summary:	Return true if it is a Function.
+	if(!it){ return false; }
+	// webkit treats NodeList as a function, which is bad
+	if((typeof(it) == "function") && (it == "[object NodeList]")) { return false; }
+	return (it instanceof Function || typeof it == "function"); // Boolean
+}
+
+dojo.lang.isString = function(/*anything*/ it){
+	// summary:	Return true if it is a String.
+	return (typeof it == "string" || it instanceof String);
+}
+
+dojo.lang.isAlien = function(/*anything*/ it){
+	// summary:	Return true if it is not a built-in function.
+	if(!it){ return false; }
+	return !dojo.lang.isFunction() && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
+}
+
+dojo.lang.isBoolean = function(/*anything*/ it){
+	// summary:	Return true if it is a Boolean.
+	return (it instanceof Boolean || typeof it == "boolean"); // Boolean
+}
+
+/**
+ * The following is***() functions are somewhat "unsafe". Fortunately,
+ * there are workarounds the the language provides and are mentioned
+ * in the WARNING messages.
+ *
+ */
+dojo.lang.isNumber = function(/*anything*/ it){
+	// summary:	Return true if it is a number.
+	// description: 
+	//		WARNING - In most cases, isNaN(it) is sufficient to determine whether or not
+	// 		something is a number or can be used as such. For example, a number or string
+	// 		can be used interchangably when accessing array items (array["1"] is the same as
+	// 		array[1]) and isNaN will return false for both values ("1" and 1). However,
+	// 		isNumber("1")  will return false, which is generally not too useful.
+	// 		Also, isNumber(NaN) returns true, again, this isn't generally useful, but there
+	// 		are corner cases (like when you want to make sure that two things are really
+	// 		the same type of thing). That is really where isNumber "shines".
+	//
+	// Recommendation - Use isNaN(it) when possible
+	
+	return (it instanceof Number || typeof it == "number"); // Boolean
+}
+
+/*
+ * FIXME: Should isUndefined go away since it is error prone?
+ */
+dojo.lang.isUndefined = function(/*anything*/ it){
+	// summary: Return true if it is not defined.
+	// description: 
+	//		WARNING - In some cases, isUndefined will not behave as you
+	// 		might expect. If you do isUndefined(foo) and there is no earlier
+	// 		reference to foo, an error will be thrown before isUndefined is
+	// 		called. It behaves correctly if you scope yor object first, i.e.
+	// 		isUndefined(foo.bar) where foo is an object and bar isn't a
+	// 		property of the object.
+	//
+	// Recommendation - Use typeof foo == "undefined" when possible
+
+	return ((typeof(it) == "undefined")&&(it == undefined)); // Boolean
+}
+
+// end Crockford functions
+
+dojo.provide("dojo.lang.extras");
+
+
+dojo.lang.setTimeout = function(/*Function*/func, /*int*/delay /*, ...*/){
+	// summary:
+	//	Sets a timeout in milliseconds to execute a function in a given context
+	//	with optional arguments.
+	//
+	// usage:
+	//	setTimeout (Object context, function func, number delay[, arg1[, ...]]);
+	//	setTimeout (function func, number delay[, arg1[, ...]]);
+
+	var context = window, argsStart = 2;
+	if(!dojo.lang.isFunction(func)){
+		context = func;
+		func = delay;
+		delay = arguments[2];
+		argsStart++;
+	}
+
+	if(dojo.lang.isString(func)){
+		func = context[func];
+	}
+	
+	var args = [];
+	for (var i = argsStart; i < arguments.length; i++){
+		args.push(arguments[i]);
+	}
+	return dojo.global().setTimeout(function () { func.apply(context, args); }, delay); // int
+}
+
+dojo.lang.clearTimeout = function(/*int*/timer){
+	// summary: clears timer by number from the execution queue
+	dojo.global().clearTimeout(timer);
+}
+
+dojo.lang.getNameInObj = function(/*Object*/ns, /*unknown*/item){
+	// summary: looks for a value in the object ns with a value matching item and returns the property name
+	// ns: if null, dj_global is used
+	// item: value to match
+	if(!ns){ ns = dj_global; }
+
+	for(var x in ns){
+		if(ns[x] === item){
+			return new String(x); // String
+		}
+	}
+	return null; // null
+}
+
+dojo.lang.shallowCopy = function(/*Object*/obj, /*Boolean?*/deep){
+	// summary: copies object obj one level deep, or full depth if deep is true
+	var i, ret;	
+
+	if(obj === null){ /*obj: null*/ return null; } // null
+	
+	if(dojo.lang.isObject(obj)){
+		// obj: Object	
+		ret = new obj.constructor();
+		for(i in obj){
+			if(dojo.lang.isUndefined(ret[i])){
+				ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
+			}
+		}
+	} else if(dojo.lang.isArray(obj)){
+		// obj: Array
+		ret = [];
+		for(i=0; i<obj.length; i++){
+			ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
+		}
+	} else {
+		// obj: unknown
+		ret = obj;
+	}
+
+	return ret; // unknown
+}
+
+dojo.lang.firstValued = function(/* ... */){
+	// summary: Return the first argument that isn't undefined
+
+	for(var i = 0; i < arguments.length; i++){
+		if(typeof arguments[i] != "undefined"){
+			return arguments[i]; // unknown
+		}
+	}
+	return undefined; // undefined
+}
+
+dojo.lang.getObjPathValue = function(/*String*/objpath, /*Object?*/context, /*Boolean?*/create){
+	// summary:
+	//	Gets a value from a reference specified as a string descriptor,
+	//	(e.g. "A.B") in the given context.
+	//
+	// context: if not specified, dj_global is used
+	// create: if true, undefined objects in the path are created.
+
+	with(dojo.parseObjPath(objpath, context, create)){
+		return dojo.evalProp(prop, obj, create); // unknown
+	}
+}
+
+dojo.lang.setObjPathValue = function(/*String*/objpath, /*unknown*/value, /*Object?*/context, /*Boolean?*/create){
+	// summary:
+	//	Sets a value on a reference specified as a string descriptor. 
+	//	(e.g. "A.B") in the given context.
+	//
+	//	context: if not specified, dj_global is used
+	//	create: if true, undefined objects in the path are created.
+
+	if(arguments.length < 4){
+		create = true;
+	}
+	with(dojo.parseObjPath(objpath, context, create)){
+		if(obj && (create || (prop in obj))){
+			obj[prop] = value;
+		}
+	}
+}
+
+dojo.provide("dojo.io.common");
+
+/******************************************************************************
+ *	Notes about dojo.io design:
+ *	
+ *	The dojo.io.* package has the unenviable task of making a lot of different
+ *	types of I/O feel natural, despite a universal lack of good (or even
+ *	reasonable!) I/O capability in the host environment. So lets pin this down
+ *	a little bit further.
+ *
+ *	Rhino:
+ *		perhaps the best situation anywhere. Access to Java classes allows you
+ *		to do anything one might want in terms of I/O, both synchronously and
+ *		async. Can open TCP sockets and perform low-latency client/server
+ *		interactions. HTTP transport is available through Java HTTP client and
+ *		server classes. Wish it were always this easy.
+ *
+ *	xpcshell:
+ *		XPCOM for I/O.
+ *
+ *	spidermonkey:
+ *		S.O.L.
+ *
+ *	Browsers:
+ *		Browsers generally do not provide any useable filesystem access. We are
+ *		therefore limited to HTTP for moving information to and from Dojo
+ *		instances living in a browser.
+ *
+ *		XMLHTTP:
+ *			Sync or async, allows reading of arbitrary text files (including
+ *			JS, which can then be eval()'d), writing requires server
+ *			cooperation and is limited to HTTP mechanisms (POST and GET).
+ *
+ *		<iframe> hacks:
+ *			iframe document hacks allow browsers to communicate asynchronously
+ *			with a server via HTTP POST and GET operations. With significant
+ *			effort and server cooperation, low-latency data transit between
+ *			client and server can be acheived via iframe mechanisms (repubsub).
+ *
+ *		SVG:
+ *			Adobe's SVG viewer implements helpful primitives for XML-based
+ *			requests, but receipt of arbitrary text data seems unlikely w/o
+ *			<![CDATA[]]> sections.
+ *
+ *
+ *	A discussion between Dylan, Mark, Tom, and Alex helped to lay down a lot
+ *	the IO API interface. A transcript of it can be found at:
+ *		http://dojotoolkit.org/viewcvs/viewcvs.py/documents/irc/irc_io_api_log.txt?rev=307&view=auto
+ *	
+ *	Also referenced in the design of the API was the DOM 3 L&S spec:
+ *		http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html
+ ******************************************************************************/
+
+// a map of the available transport options. Transports should add themselves
+// by calling add(name)
+dojo.io.transports = [];
+dojo.io.hdlrFuncNames = [ "load", "error", "timeout" ]; // we're omitting a progress() event for now
+
+dojo.io.Request = function(/*String*/ url, /*String*/ mimetype, /*String*/ transport, /*String or Boolean*/ changeUrl){
+// summary:
+//		Constructs a Request object that is used by dojo.io.bind(). dojo.io.bind() will create one of these for you if
+//		you call dojo.io.bind() with an plain object containing the bind parameters.
+//		This method can either take the arguments specified, or an Object containing all of the parameters that you
+//		want to use to create the dojo.io.Request (similar to how dojo.io.bind() is called.
+//		The named parameters to this constructor represent the minimum set of parameters need
+	if((arguments.length == 1)&&(arguments[0].constructor == Object)){
+		this.fromKwArgs(arguments[0]);
+	}else{
+		this.url = url;
+		if(mimetype){ this.mimetype = mimetype; }
+		if(transport){ this.transport = transport; }
+		if(arguments.length >= 4){ this.changeUrl = changeUrl; }
+	}
+}
+
+dojo.lang.extend(dojo.io.Request, {
+
+	/** The URL to hit */
+	url: "",
+	
+	/** The mime type used to interrpret the response body */
+	mimetype: "text/plain",
+	
+	/** The HTTP method to use */
+	method: "GET",
+	
+	/** An Object containing key-value pairs to be included with the request */
+	content: undefined, // Object
+	
+	/** The transport medium to use */
+	transport: undefined, // String
+	
+	/** If defined the URL of the page is physically changed */
+	changeUrl: undefined, // String
+	
+	/** A form node to use in the request */
+	formNode: undefined, // HTMLFormElement
+	
+	/** Whether the request should be made synchronously */
+	sync: false,
+	
+	bindSuccess: false,
+
+	/** Cache/look for the request in the cache before attempting to request?
+	 *  NOTE: this isn't a browser cache, this is internal and would only cache in-page
+	 */
+	useCache: false,
+
+	/** Prevent the browser from caching this by adding a query string argument to the URL */
+	preventCache: false,
+	
+	// events stuff
+	load: function(/*String*/ type, /*Object*/ data, /*Object*/ transportImplementation, /*Object*/ kwArgs){
+		// summary:
+		//		Called on successful completion of a bind.
+		//		type:
+		//				A string with value "load"
+		//		data:
+		//				The object representing the result of the bind. The actual structure
+		//				of the data object will depend on the mimetype that was given to bind
+		//				in the bind arguments.
+		//		transportImplementation:
+		//				The object that implements a particular transport. Structure is depedent
+		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
+		//				XMLHttpRequest object from the browser.
+		//		kwArgs:
+		//				Object that contains the request parameters that were given to the
+		//				bind call. Useful for storing and retrieving state from when bind
+		//				was called.
+	},
+	error: function(/*String*/ type, /*Object*/ error, /*Object*/ transportImplementation, /*Object*/ kwArgs){
+		// summary:
+		//		Called when there is an error with a bind.
+		//		type:
+		//				A string with value "error"
+		//		error:
+		//				The error object. Should be a dojo.io.Error object, but not guaranteed.
+		//		transportImplementation:
+		//				The object that implements a particular transport. Structure is depedent
+		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
+		//				XMLHttpRequest object from the browser.
+		//		kwArgs:
+		//				Object that contains the request parameters that were given to the
+		//				bind call. Useful for storing and retrieving state from when bind
+		//				was called.
+	},
+	timeout: function(/*String*/ type, /*Object*/ empty, /*Object*/ transportImplementation, /*Object*/ kwArgs){
+		// summary:
+		//		Called when there is an error with a bind. Only implemented in certain transports at this time.
+		//		type:
+		//				A string with value "timeout"
+		//		empty:
+		//				Should be null. Just a spacer argument so that load, error, timeout and handle have the
+		//				same signatures.
+		//		transportImplementation:
+		//				The object that implements a particular transport. Structure is depedent
+		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
+		//				XMLHttpRequest object from the browser. May be null for the timeout case for
+		//				some transports.
+		//		kwArgs:
+		//				Object that contains the request parameters that were given to the
+		//				bind call. Useful for storing and retrieving state from when bind
+		//				was called.
+	},
+	handle: function(/*String*/ type, /*Object*/ data, /*Object*/ transportImplementation, /*Object*/ kwArgs){
+		// summary:
+		//		The handle method can be defined instead of defining separate load, error and timeout
+		//		callbacks.
+		//		type:
+		//				A string with the type of callback: "load", "error", or "timeout".
+		//		data:
+		//				See the above callbacks for what this parameter could be.
+		//		transportImplementation:
+		//				The object that implements a particular transport. Structure is depedent
+		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
+		//				XMLHttpRequest object from the browser.
+		//		kwArgs:
+		//				Object that contains the request parameters that were given to the
+		//				bind call. Useful for storing and retrieving state from when bind
+		//				was called.	
+	},
+
+	//FIXME: change IframeIO.js to use timeouts?
+	// The number of seconds to wait until firing a timeout callback.
+	// If it is zero, that means, don't do a timeout check.
+	timeoutSeconds: 0,
+	
+	// the abort method needs to be filled in by the transport that accepts the
+	// bind() request
+	abort: function(){ },
+	
+	// backButton: function(){ },
+	// forwardButton: function(){ },
+
+	fromKwArgs: function(/*Object*/ kwArgs){
+		// summary:
+		//		Creates a dojo.io.Request from a simple object (kwArgs object).
+
+		// normalize args
+		if(kwArgs["url"]){ kwArgs.url = kwArgs.url.toString(); }
+		if(kwArgs["formNode"]) { kwArgs.formNode = dojo.byId(kwArgs.formNode); }
+		if(!kwArgs["method"] && kwArgs["formNode"] && kwArgs["formNode"].method) {
+			kwArgs.method = kwArgs["formNode"].method;
+		}
+		
+		// backwards compatibility
+		if(!kwArgs["handle"] && kwArgs["handler"]){ kwArgs.handle = kwArgs.handler; }
+		if(!kwArgs["load"] && kwArgs["loaded"]){ kwArgs.load = kwArgs.loaded; }
+		if(!kwArgs["changeUrl"] && kwArgs["changeURL"]) { kwArgs.changeUrl = kwArgs.changeURL; }
+
+		// encoding fun!
+		kwArgs.encoding = dojo.lang.firstValued(kwArgs["encoding"], djConfig["bindEncoding"], "");
+
+		kwArgs.sendTransport = dojo.lang.firstValued(kwArgs["sendTransport"], djConfig["ioSendTransport"], false);
+
+		var isFunction = dojo.lang.isFunction;
+		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
+			var fn = dojo.io.hdlrFuncNames[x];
+			if(kwArgs[fn] && isFunction(kwArgs[fn])){ continue; }
+			if(kwArgs["handle"] && isFunction(kwArgs["handle"])){
+				kwArgs[fn] = kwArgs.handle;
+			}
+			// handler is aliased above, shouldn't need this check
+			/* else if(dojo.lang.isObject(kwArgs.handler)){
+				if(isFunction(kwArgs.handler[fn])){
+					kwArgs[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"]||function(){};
+				}
+			}*/
+		}
+		dojo.lang.mixin(this, kwArgs);
+	}
+
+});
+
+dojo.io.Error = function(/*String*/ msg, /*String*/ type, /*Number*/num){
+	// summary:
+	//		Constructs an object representing a bind error.
+	this.message = msg;
+	this.type =  type || "unknown"; // must be one of "io", "parse", "unknown"
+	this.number = num || 0; // per-substrate error number, not normalized
+}
+
+dojo.io.transports.addTransport = function(name){
+	// summary:
+	//		Used to register transports that can support bind calls.
+	this.push(name);
+	// FIXME: do we need to handle things that aren't direct children of the
+	// dojo.io module? (say, dojo.io.foo.fooTransport?)
+	this[name] = dojo.io[name];
+}
+
+// binding interface, the various implementations register their capabilities
+// and the bind() method dispatches
+dojo.io.bind = function(/*Object*/ request){
+	// summary:
+	//		Binding interface for IO. Loading different IO transports, like
+	//		dojo.io.BrowserIO or dojo.io.IframeIO will register with bind
+	//		to handle particular types of bind calls.
+	//		request:
+	//				Object containing bind arguments. This object is converted to
+	//				a dojo.io.Request object, and that request object is the return
+	//				value for this method.
+	if(!(request instanceof dojo.io.Request)){
+		try{
+			request = new dojo.io.Request(request);
+		}catch(e){ dojo.debug(e); }
+	}
+
+	// if the request asks for a particular implementation, use it
+	var tsName = "";
+	if(request["transport"]){
+		tsName = request["transport"];
+		if(!this[tsName]){
+			dojo.io.sendBindError(request, "No dojo.io.bind() transport with name '"
+				+ request["transport"] + "'.");
+			return request; //dojo.io.Request
+		}
+		if(!this[tsName].canHandle(request)){
+			dojo.io.sendBindError(request, "dojo.io.bind() transport with name '"
+				+ request["transport"] + "' cannot handle this type of request.");
+			return request;	//dojo.io.Request
+		}
+	}else{
+		// otherwise we do our best to auto-detect what available transports
+		// will handle 
+		for(var x=0; x<dojo.io.transports.length; x++){
+			var tmp = dojo.io.transports[x];
+			if((this[tmp])&&(this[tmp].canHandle(request))){
+				tsName = tmp;
+				break;
+			}
+		}
+		if(tsName == ""){
+			dojo.io.sendBindError(request, "None of the loaded transports for dojo.io.bind()"
+				+ " can handle the request.");
+			return request; //dojo.io.Request
+		}
+	}
+	this[tsName].bind(request);
+	request.bindSuccess = true;
+	return request; //dojo.io.Request
+}
+
+dojo.io.sendBindError = function(request /* Object */, message /* String */){
+	// summary:
+	//		Used internally by dojo.io.bind() to return/raise a bind error.
+
+	//Need to be careful since not all hostenvs support setTimeout.
+	if((typeof request.error == "function" || typeof request.handle == "function")
+		&& (typeof setTimeout == "function" || typeof setTimeout == "object")){
+		var errorObject = new dojo.io.Error(message);
+		setTimeout(function(){
+			request[(typeof request.error == "function") ? "error" : "handle"]("error", errorObject, null, request);
+		}, 50);
+	}else{
+		dojo.raise(message);
+	}
+}
+
+dojo.io.queueBind = function(/* Object */ request){
+	// summary:
+	//		queueBind will use dojo.io.bind() but guarantee that only one bind
+	//		call is handled at a time. If queueBind is called while a bind call
+	//		is in process, it will queue up the other calls to bind and call them
+	//		in order as bind calls complete.
+	//		request:
+	//			Same sort of request object as used for dojo.io.bind().
+	if(!(request instanceof dojo.io.Request)){
+		try{
+			request = new dojo.io.Request(request);
+		}catch(e){ dojo.debug(e); }
+	}
+
+	// make sure we get called if/when we get a response
+	var oldLoad = request.load;
+	request.load = function(){
+		dojo.io._queueBindInFlight = false;
+		var ret = oldLoad.apply(this, arguments);
+		dojo.io._dispatchNextQueueBind();
+		return ret;
+	}
+
+	var oldErr = request.error;
+	request.error = function(){
+		dojo.io._queueBindInFlight = false;
+		var ret = oldErr.apply(this, arguments);
+		dojo.io._dispatchNextQueueBind();
+		return ret;
+	}
+
+	dojo.io._bindQueue.push(request);
+	dojo.io._dispatchNextQueueBind();
+	return request; //dojo.io.Request
+}
+
+dojo.io._dispatchNextQueueBind = function(){
+	// summary:
+	//	Private method used by dojo.io.queueBind().
+	if(!dojo.io._queueBindInFlight){
+		dojo.io._queueBindInFlight = true;
+		if(dojo.io._bindQueue.length > 0){
+			dojo.io.bind(dojo.io._bindQueue.shift());
+		}else{
+			dojo.io._queueBindInFlight = false;
+		}
+	}
+}
+dojo.io._bindQueue = [];
+dojo.io._queueBindInFlight = false;
+
+dojo.io.argsFromMap = function(/*Object*/ map, /*String*/ encoding, /*String*/ last){
+	// summary:
+	//		Converts name/values pairs in the map object to an URL-encoded string
+	//		with format of name1=value1&name2=value2...
+	//		map:
+	//			Object that has the contains the names and values.
+	//		encoding:
+	//			String to specify how to encode the name and value. If the encoding string
+	//			contains "utf" (case-insensitive), then encodeURIComponent is used. Otherwise
+	//			dojo.string.encodeAscii is used.
+	//		last:
+	//			The last parameter in the list. Helps with final string formatting?
+	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
+	var mapped = [];
+	var control = new Object();
+	for(var name in map){
+		var domap = function(elt){
+			var val = enc(name)+"="+enc(elt);
+			mapped[(last == name) ? "push" : "unshift"](val);
+		}
+		if(!control[name]){
+			var value = map[name];
+			// FIXME: should be isArrayLike?
+			if (dojo.lang.isArray(value)){
+				dojo.lang.forEach(value, domap);
+			}else{
+				domap(value);
+			}
+		}
+	}
+	return mapped.join("&"); //String
+}
+
+dojo.io.setIFrameSrc = function(/*DOMNode*/ iframe, /*String*/ src, /*Boolean*/ replace){
+	//summary:
+	//		Sets the URL that is loaded in an IFrame. The replace parameter indicates whether
+	//		location.replace() should be used when changing the location of the iframe.
+	try{
+		var r = dojo.render.html;
+		// dojo.debug(iframe);
+		if(!replace){
+			if(r.safari){
+				iframe.location = src;
+			}else{
+				frames[iframe.name].location = src;
+			}
+		}else{
+			// Fun with DOM 0 incompatibilities!
+			var idoc;
+			if(r.ie){
+				idoc = iframe.contentWindow.document;
+			}else if(r.safari){
+				idoc = iframe.document;
+			}else{ //  if(r.moz){
+				idoc = iframe.contentWindow;
+			}
+
+			//For Safari (at least 2.0.3) and Opera, if the iframe
+			//has just been created but it doesn't have content
+			//yet, then iframe.document may be null. In that case,
+			//use iframe.location and return.
+			if(!idoc){
+				iframe.location = src;
+				return;
+			}else{
+				idoc.location.replace(src);
+			}
+		}
+	}catch(e){ 
+		dojo.debug(e); 
+		dojo.debug("setIFrameSrc: "+e); 
+	}
+}
+
+/*
+dojo.io.sampleTranport = new function(){
+	this.canHandle = function(kwArgs){
+		// canHandle just tells dojo.io.bind() if this is a good transport to
+		// use for the particular type of request.
+		if(	
+			(
+				(kwArgs["mimetype"] == "text/plain") ||
+				(kwArgs["mimetype"] == "text/html") ||
+				(kwArgs["mimetype"] == "text/javascript")
+			)&&(
+				(kwArgs["method"] == "get") ||
+				( (kwArgs["method"] == "post") && (!kwArgs["formNode"]) )
+			)
+		){
+			return true;
+		}
+
+		return false;
+	}
+
+	this.bind = function(kwArgs){
+		var hdlrObj = {};
+
+		// set up a handler object
+		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
+			var fn = dojo.io.hdlrFuncNames[x];
+			if(typeof kwArgs.handler == "object"){
+				if(typeof kwArgs.handler[fn] == "function"){
+					hdlrObj[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"];
+				}
+			}else if(typeof kwArgs[fn] == "function"){
+				hdlrObj[fn] = kwArgs[fn];
+			}else{
+				hdlrObj[fn] = kwArgs["handle"]||function(){};
+			}
+		}
+
+		// build a handler function that calls back to the handler obj
+		var hdlrFunc = function(evt){
+			if(evt.type == "onload"){
+				hdlrObj.load("load", evt.data, evt);
+			}else if(evt.type == "onerr"){
+				var errObj = new dojo.io.Error("sampleTransport Error: "+evt.msg);
+				hdlrObj.error("error", errObj);
+			}
+		}
+
+		// the sample transport would attach the hdlrFunc() when sending the
+		// request down the pipe at this point
+		var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
+		// sampleTransport.sendRequest(tgtURL, hdlrFunc);
+	}
+
+	dojo.io.transports.addTransport("sampleTranport");
+}
+*/
+
+dojo.provide("dojo.lang.array");
+
+
+// FIXME: Is this worthless since you can do: if(name in obj)
+// is this the right place for this?
+dojo.lang.has = function(/*Object*/obj, /*String*/name){
+	try{
+		return typeof obj[name] != "undefined";
+	}catch(e){ return false; }
+}
+
+dojo.lang.isEmpty = function(/*Object*/obj){
+	if(dojo.lang.isObject(obj)){
+		var tmp = {};
+		var count = 0;
+		for(var x in obj){
+			if(obj[x] && (!tmp[x])){
+				count++;
+				break;
+			} 
+		}
+		return count == 0;
+	}else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)){
+		return obj.length == 0;
+	}
+}
+
+dojo.lang.map = function(/*Array*/arr, /*Object|Function*/obj, /*Function?*/unary_func){
+	var isString = dojo.lang.isString(arr);
+	if(isString){
+		// arr: String
+		arr = arr.split("");
+	}
+	if(dojo.lang.isFunction(obj)&&(!unary_func)){
+		unary_func = obj;
+		obj = dj_global;
+	}else if(dojo.lang.isFunction(obj) && unary_func){
+		// ff 1.5 compat
+		var tmpObj = obj;
+		obj = unary_func;
+		unary_func = tmpObj;
+	}
+	if(Array.map){
+	 	var outArr = Array.map(arr, unary_func, obj);
+	}else{
+		var outArr = [];
+		for(var i=0;i<arr.length;++i){
+			outArr.push(unary_func.call(obj, arr[i]));
+		}
+	}
+	if(isString) {
+		return outArr.join(""); // String
+	} else {
+		return outArr; // Array
+	}
+}
+
+dojo.lang.reduce = function(/*Array*/arr, initialValue, /*Object|null*/obj, /*Function*/binary_func){
+	var reducedValue = initialValue;
+	var ob = obj ? obj : dj_global;
+	dojo.lang.map(arr, 
+		function(val){
+			reducedValue = binary_func.call(ob, reducedValue, val);
+		}
+	);
+	return reducedValue;
+}
+
+// http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
+dojo.lang.forEach = function(/*Array*/anArray, /*Function*/callback, /*Object?*/thisObject){
+	if(dojo.lang.isString(anArray)){
+		// anArray: String
+		anArray = anArray.split(""); 
+	}
+	if(Array.forEach){
+		Array.forEach(anArray, callback, thisObject);
+	}else{
+		// FIXME: there are several ways of handilng thisObject. Is dj_global always the default context?
+		if(!thisObject){
+			thisObject=dj_global;
+		}
+		for(var i=0,l=anArray.length; i<l; i++){ 
+			callback.call(thisObject, anArray[i], i, anArray);
+		}
+	}
+}
+
+dojo.lang._everyOrSome = function(/*Boolean*/every, /*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
+	if(dojo.lang.isString(arr)){ 
+		//arr: String
+		arr = arr.split(""); 
+	}
+	if(Array.every){
+		return Array[ every ? "every" : "some" ](arr, callback, thisObject);
+	}else{
+		if(!thisObject){
+			thisObject = dj_global;
+		}
+		for(var i=0,l=arr.length; i<l; i++){
+			var result = callback.call(thisObject, arr[i], i, arr);
+			if(every && !result){
+				return false; // Boolean
+			}else if((!every)&&(result)){
+				return true; // Boolean
+			}
+		}
+		return Boolean(every); // Boolean
+	}
+}
+
+dojo.lang.every = function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
+	return this._everyOrSome(true, arr, callback, thisObject); // Boolean
+}
+
+dojo.lang.some = function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
+	return this._everyOrSome(false, arr, callback, thisObject); // Boolean
+}
+
+dojo.lang.filter = function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
+	var isString = dojo.lang.isString(arr);
+	if(isString){ /*arr: String*/arr = arr.split(""); }
+	var outArr;
+	if(Array.filter){
+		outArr = Array.filter(arr, callback, thisObject);
+	} else {
+		if(!thisObject){
+			if(arguments.length >= 3){ dojo.raise("thisObject doesn't exist!"); }
+			thisObject = dj_global;
+		}
+
+		outArr = [];
+		for(var i = 0; i < arr.length; i++){
+			if(callback.call(thisObject, arr[i], i, arr)){
+				outArr.push(arr[i]);
+			}
+		}
+	}
+	if(isString){
+		return outArr.join(""); // String
+	} else {
+		return outArr; // Array
+	}
+}
+
+dojo.lang.unnest = function(/* ... */){
+	// summary:
+	//	Creates a 1-D array out of all the arguments passed,
+	//	unravelling any array-like objects in the process
+	//
+	// usage:
+	//	unnest(1, 2, 3) ==> [1, 2, 3]
+	//	unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4]
+
+	var out = [];
+	for(var i = 0; i < arguments.length; i++){
+		if(dojo.lang.isArrayLike(arguments[i])){
+			var add = dojo.lang.unnest.apply(this, arguments[i]);
+			out = out.concat(add);
+		}else{
+			out.push(arguments[i]);
+		}
+	}
+	return out; // Array
+}
+
+dojo.lang.toArray = function(/*Object*/arrayLike, /*Number*/startOffset){
+	// summary:
+	//	Converts an array-like object (i.e. arguments, DOMCollection)
+	//	to an array
+	var array = [];
+	for(var i = startOffset||0; i < arrayLike.length; i++){
+		array.push(arrayLike[i]);
+	}
+	return array; // Array
+}
+
+dojo.provide("dojo.lang.func");
+
+
+/**
+ * Runs a function in a given scope (thisObject), can
+ * also be used to preserve scope.
+ *
+ * hitch(foo, "bar"); // runs foo.bar() in the scope of foo
+ * hitch(foo, myFunction); // runs myFunction in the scope of foo
+ */
+dojo.lang.hitch = function(thisObject, method){
+	var fcn = (dojo.lang.isString(method) ? thisObject[method] : method) || function(){};
+
+	return function() {
+		return fcn.apply(thisObject, arguments);
+	};
+}
+
+dojo.lang.anonCtr = 0;
+dojo.lang.anon = {};
+dojo.lang.nameAnonFunc = function(anonFuncPtr, namespaceObj, searchForNames){
+	var nso = (namespaceObj || dojo.lang.anon);
+	if( (searchForNames) ||
+		((dj_global["djConfig"])&&(djConfig["slowAnonFuncLookups"] == true)) ){
+		for(var x in nso){
+			try{
+				if(nso[x] === anonFuncPtr){
+					return x;
+				}
+			}catch(e){} // window.external fails in IE embedded in Eclipse (Eclipse bug #151165)
+		}
+	}
+	var ret = "__"+dojo.lang.anonCtr++;
+	while(typeof nso[ret] != "undefined"){
+		ret = "__"+dojo.lang.anonCtr++;
+	}
+	nso[ret] = anonFuncPtr;
+	return ret;
+}
+
+dojo.lang.forward = function(funcName){
+	// Returns a function that forwards a method call to this.func(...)
+	return function(){
+		return this[funcName].apply(this, arguments);
+	};
+}
+
+dojo.lang.curry = function(ns, func /* args ... */){
+	var outerArgs = [];
+	ns = ns||dj_global;
+	if(dojo.lang.isString(func)){
+		func = ns[func];
+	}
+	for(var x=2; x<arguments.length; x++){
+		outerArgs.push(arguments[x]);
+	}
+	// since the event system replaces the original function with a new
+	// join-point runner with an arity of 0, we check to see if it's left us
+	// any clues about the original arity in lieu of the function's actual
+	// length property
+	var ecount = (func["__preJoinArity"]||func.length) - outerArgs.length;
+	// borrowed from svend tofte
+	function gather(nextArgs, innerArgs, expected){
+		var texpected = expected;
+		var totalArgs = innerArgs.slice(0); // copy
+		for(var x=0; x<nextArgs.length; x++){
+			totalArgs.push(nextArgs[x]);
+		}
+		// check the list of provided nextArgs to see if it, plus the
+		// number of innerArgs already supplied, meets the total
+		// expected.
+		expected = expected-nextArgs.length;
+		if(expected<=0){
+			var res = func.apply(ns, totalArgs);
+			expected = texpected;
+			return res;
+		}else{
+			return function(){
+				return gather(arguments,// check to see if we've been run
+										// with enough args
+							totalArgs,	// a copy
+							expected);	// how many more do we need to run?;
+			};
+		}
+	}
+	return gather([], outerArgs, ecount);
+}
+
+dojo.lang.curryArguments = function(ns, func, args, offset){
+	var targs = [];
+	var x = offset||0;
+	for(x=offset; x<args.length; x++){
+		targs.push(args[x]); // ensure that it's an arr
+	}
+	return dojo.lang.curry.apply(dojo.lang, [ns, func].concat(targs));
+}
+
+dojo.lang.tryThese = function(){
+	for(var x=0; x<arguments.length; x++){
+		try{
+			if(typeof arguments[x] == "function"){
+				var ret = (arguments[x]());
+				if(ret){
+					return ret;
+				}
+			}
+		}catch(e){
+			dojo.debug(e);
+		}
+	}
+}
+
+dojo.lang.delayThese = function(farr, cb, delay, onend){
+	/**
+	 * alternate: (array funcArray, function callback, function onend)
+	 * alternate: (array funcArray, function callback)
+	 * alternate: (array funcArray)
+	 */
+	if(!farr.length){ 
+		if(typeof onend == "function"){
+			onend();
+		}
+		return;
+	}
+	if((typeof delay == "undefined")&&(typeof cb == "number")){
+		delay = cb;
+		cb = function(){};
+	}else if(!cb){
+		cb = function(){};
+		if(!delay){ delay = 0; }
+	}
+	setTimeout(function(){
+		(farr.shift())();
+		cb();
+		dojo.lang.delayThese(farr, cb, delay, onend);
+	}, delay);
+}
+
+dojo.provide("dojo.string.extras");
+
+
+//TODO: should we use ${} substitution syntax instead, like widgets do?
+dojo.string.substituteParams = function(/*string*/template, /* object - optional or ... */hash){
+// summary:
+//	Performs parameterized substitutions on a string. Throws an exception if any parameter is unmatched.
+//
+// description:
+//	For example,
+//		dojo.string.substituteParams("File '%{0}' is not found in directory '%{1}'.","foo.html","/temp");
+//	returns
+//		"File 'foo.html' is not found in directory '/temp'."
+//
+// template: the original string template with %{values} to be replaced
+// hash: name/value pairs (type object) to provide substitutions.  Alternatively, substitutions may be
+//	included as arguments 1..n to this function, corresponding to template parameters 0..n-1
+
+	var map = (typeof hash == 'object') ? hash : dojo.lang.toArray(arguments, 1);
+
+	return template.replace(/\%\{(\w+)\}/g, function(match, key){
+		if(typeof(map[key]) != "undefined" && map[key] != null){
+			return map[key];
+		}
+		dojo.raise("Substitution not found: " + key);
+	}); // string
+};
+
+dojo.string.capitalize = function(/*string*/str){
+// summary:
+//	Uppercases the first letter of each word
+
+	if(!dojo.lang.isString(str)){ return ""; }
+	if(arguments.length == 0){ str = this; }
+
+	var words = str.split(' ');
+	for(var i=0; i<words.length; i++){
+		words[i] = words[i].charAt(0).toUpperCase() + words[i].substring(1);
+	}
+	return words.join(" "); // string
+}
+
+dojo.string.isBlank = function(/*string*/str){
+// summary:
+//	Return true if the entire string is whitespace characters
+
+	if(!dojo.lang.isString(str)){ return true; }
+	return (dojo.string.trim(str).length == 0); // boolean
+}
+
+//FIXME: not sure exactly what encodeAscii is trying to do, or if it's working right
+dojo.string.encodeAscii = function(/*string*/str){
+	if(!dojo.lang.isString(str)){ return str; } // unknown
+	var ret = "";
+	var value = escape(str);
+	var match, re = /%u([0-9A-F]{4})/i;
+	while((match = value.match(re))){
+		var num = Number("0x"+match[1]);
+		var newVal = escape("&#" + num + ";");
+		ret += value.substring(0, match.index) + newVal;
+		value = value.substring(match.index+match[0].length);
+	}
+	ret += value.replace(/\+/g, "%2B");
+	return ret; // string
+}
+
+dojo.string.escape = function(/*string*/type, /*string*/str){
+// summary:
+//	Adds escape sequences for special characters according to the convention of 'type'
+//
+// type: one of xml|html|xhtml|sql|regexp|regex|javascript|jscript|js|ascii
+// str: the string to be escaped
+
+	var args = dojo.lang.toArray(arguments, 1);
+	switch(type.toLowerCase()){
+		case "xml":
+		case "html":
+		case "xhtml":
+			return dojo.string.escapeXml.apply(this, args); // string
+		case "sql":
+			return dojo.string.escapeSql.apply(this, args); // string
+		case "regexp":
+		case "regex":
+			return dojo.string.escapeRegExp.apply(this, args); // string
+		case "javascript":
+		case "jscript":
+		case "js":
+			return dojo.string.escapeJavaScript.apply(this, args); // string
+		case "ascii":
+			// so it's encode, but it seems useful
+			return dojo.string.encodeAscii.apply(this, args); // string
+		default:
+			return str; // string
+	}
+}
+
+dojo.string.escapeXml = function(/*string*/str, /*boolean*/noSingleQuotes){
+//summary:
+//	Adds escape sequences for special characters in XML: &<>"'
+//  Optionally skips escapes for single quotes
+
+	str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
+		.replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
+	if(!noSingleQuotes){ str = str.replace(/'/gm, "&#39;"); }
+	return str; // string
+}
+
+dojo.string.escapeSql = function(/*string*/str){
+//summary:
+//	Adds escape sequences for single quotes in SQL expressions
+
+	return str.replace(/'/gm, "''"); //string
+}
+
+dojo.string.escapeRegExp = function(/*string*/str){
+//summary:
+//	Adds escape sequences for special characters in regular expressions
+
+	return str.replace(/\\/gm, "\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1"); // string
+}
+
+//FIXME: should this one also escape backslash?
+dojo.string.escapeJavaScript = function(/*string*/str){
+//summary:
+//	Adds escape sequences for single and double quotes as well
+//	as non-visible characters in JavaScript string literal expressions
+
+	return str.replace(/(["'\f\b\n\t\r])/gm, "\\$1"); // string
+}
+
+//FIXME: looks a lot like escapeJavaScript, just adds quotes? deprecate one?
+dojo.string.escapeString = function(/*string*/str){
+//summary:
+//	Adds escape sequences for non-visual characters, double quote and backslash
+//	and surrounds with double quotes to form a valid string literal.
+	return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'
+		).replace(/[\f]/g, "\\f"
+		).replace(/[\b]/g, "\\b"
+		).replace(/[\n]/g, "\\n"
+		).replace(/[\t]/g, "\\t"
+		).replace(/[\r]/g, "\\r"); // string
+}
+
+// TODO: make an HTML version
+dojo.string.summary = function(/*string*/str, /*number*/len){
+// summary:
+//	Truncates 'str' after 'len' characters and appends periods as necessary so that it ends with "..."
+
+	if(!len || str.length <= len){
+		return str; // string
+	}
+
+	return str.substring(0, len).replace(/\.+$/, "") + "..."; // string
+}
+
+dojo.string.endsWith = function(/*string*/str, /*string*/end, /*boolean*/ignoreCase){
+// summary:
+//	Returns true if 'str' ends with 'end'
+
+	if(ignoreCase){
+		str = str.toLowerCase();
+		end = end.toLowerCase();
+	}
+	if((str.length - end.length) < 0){
+		return false; // boolean
+	}
+	return str.lastIndexOf(end) == str.length - end.length; // boolean
+}
+
+dojo.string.endsWithAny = function(/*string*/str /* , ... */){
+// summary:
+//	Returns true if 'str' ends with any of the arguments[2 -> n]
+
+	for(var i = 1; i < arguments.length; i++) {
+		if(dojo.string.endsWith(str, arguments[i])) {
+			return true; // boolean
+		}
+	}
+	return false; // boolean
+}
+
+dojo.string.startsWith = function(/*string*/str, /*string*/start, /*boolean*/ignoreCase){
+// summary:
+//	Returns true if 'str' starts with 'start'
+
+	if(ignoreCase) {
+		str = str.toLowerCase();
+		start = start.toLowerCase();
+	}
+	return str.indexOf(start) == 0; // boolean
+}
+
+dojo.string.startsWithAny = function(/*string*/str /* , ... */){
+// summary:
+//	Returns true if 'str' starts with any of the arguments[2 -> n]
+
+	for(var i = 1; i < arguments.length; i++) {
+		if(dojo.string.startsWith(str, arguments[i])) {
+			return true; // boolean
+		}
+	}
+	return false; // boolean
+}
+
+dojo.string.has = function(/*string*/str /* , ... */) {
+// summary:
+//	Returns true if 'str' contains any of the arguments 2 -> n
+
+	for(var i = 1; i < arguments.length; i++) {
+		if(str.indexOf(arguments[i]) > -1){
+			return true; // boolean
+		}
+	}
+	return false; // boolean
+}
+
+dojo.string.normalizeNewlines = function(/*string*/text, /*string? (\n or \r)*/newlineChar){
+// summary:
+//	Changes occurences of CR and LF in text to CRLF, or if newlineChar is provided as '\n' or '\r',
+//	substitutes newlineChar for occurrences of CR/LF and CRLF
+
+	if (newlineChar == "\n"){
+		text = text.replace(/\r\n/g, "\n");
+		text = text.replace(/\r/g, "\n");
+	} else if (newlineChar == "\r"){
+		text = text.replace(/\r\n/g, "\r");
+		text = text.replace(/\n/g, "\r");
+	}else{
+		text = text.replace(/([^\r])\n/g, "$1\r\n").replace(/\r([^\n])/g, "\r\n$1");
+	}
+	return text; // string
+}
+
+dojo.string.splitEscaped = function(/*string*/str, /*string of length=1*/charac){
+// summary:
+//	Splits 'str' into an array separated by 'charac', but skips characters escaped with a backslash
+
+	var components = [];
+	for (var i = 0, prevcomma = 0; i < str.length; i++){
+		if (str.charAt(i) == '\\'){ i++; continue; }
+		if (str.charAt(i) == charac){
+			components.push(str.substring(prevcomma, i));
+			prevcomma = i + 1;
+		}
+	}
+	components.push(str.substr(prevcomma));
+	return components; // array
+}
+
+dojo.provide("dojo.dom");
+
+dojo.dom.ELEMENT_NODE                  = 1;
+dojo.dom.ATTRIBUTE_NODE                = 2;
+dojo.dom.TEXT_NODE                     = 3;
+dojo.dom.CDATA_SECTION_NODE            = 4;
+dojo.dom.ENTITY_REFERENCE_NODE         = 5;
+dojo.dom.ENTITY_NODE                   = 6;
+dojo.dom.PROCESSING_INSTRUCTION_NODE   = 7;
+dojo.dom.COMMENT_NODE                  = 8;
+dojo.dom.DOCUMENT_NODE                 = 9;
+dojo.dom.DOCUMENT_TYPE_NODE            = 10;
+dojo.dom.DOCUMENT_FRAGMENT_NODE        = 11;
+dojo.dom.NOTATION_NODE                 = 12;
+	
+dojo.dom.dojoml = "http://www.dojotoolkit.org/2004/dojoml";
+
+/**
+ *	comprehensive list of XML namespaces
+**/
+dojo.dom.xmlns = {
+	//	summary
+	//	aliases for various common XML namespaces
+	svg : "http://www.w3.org/2000/svg",
+	smil : "http://www.w3.org/2001/SMIL20/",
+	mml : "http://www.w3.org/1998/Math/MathML",
+	cml : "http://www.xml-cml.org",
+	xlink : "http://www.w3.org/1999/xlink",
+	xhtml : "http://www.w3.org/1999/xhtml",
+	xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+	xbl : "http://www.mozilla.org/xbl",
+	fo : "http://www.w3.org/1999/XSL/Format",
+	xsl : "http://www.w3.org/1999/XSL/Transform",
+	xslt : "http://www.w3.org/1999/XSL/Transform",
+	xi : "http://www.w3.org/2001/XInclude",
+	xforms : "http://www.w3.org/2002/01/xforms",
+	saxon : "http://icl.com/saxon",
+	xalan : "http://xml.apache.org/xslt",
+	xsd : "http://www.w3.org/2001/XMLSchema",
+	dt: "http://www.w3.org/2001/XMLSchema-datatypes",
+	xsi : "http://www.w3.org/2001/XMLSchema-instance",
+	rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+	rdfs : "http://www.w3.org/2000/01/rdf-schema#",
+	dc : "http://purl.org/dc/elements/1.1/",
+	dcq: "http://purl.org/dc/qualifiers/1.0",
+	"soap-env" : "http://schemas.xmlsoap.org/soap/envelope/",
+	wsdl : "http://schemas.xmlsoap.org/wsdl/",
+	AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
+};
+
+dojo.dom.isNode = function(/* object */wh){
+	//	summary
+	//	checks to see if wh is actually a node.
+	if(typeof Element == "function") {
+		try {
+			return wh instanceof Element;	//	boolean
+		} catch(E) {}
+	} else {
+		// best-guess
+		return wh && !isNaN(wh.nodeType);	//	boolean
+	}
+}
+
+dojo.dom.getUniqueId = function(){
+	//	summary
+	//	returns a unique string for use with any DOM element
+	var _document = dojo.doc();
+	do {
+		var id = "dj_unique_" + (++arguments.callee._idIncrement);
+	}while(_document.getElementById(id));
+	return id;	//	string
+}
+dojo.dom.getUniqueId._idIncrement = 0;
+
+dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(/* Element */parentNode, /* string? */tagName){
+	//	summary
+	//	returns the first child element matching tagName
+	var node = parentNode.firstChild;
+	while(node && node.nodeType != dojo.dom.ELEMENT_NODE){
+		node = node.nextSibling;
+	}
+	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
+		node = dojo.dom.nextElement(node, tagName);
+	}
+	return node;	//	Element
+}
+
+dojo.dom.lastElement = dojo.dom.getLastChildElement = function(/* Element */parentNode, /* string? */tagName){
+	//	summary
+	//	returns the last child element matching tagName
+	var node = parentNode.lastChild;
+	while(node && node.nodeType != dojo.dom.ELEMENT_NODE) {
+		node = node.previousSibling;
+	}
+	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
+		node = dojo.dom.prevElement(node, tagName);
+	}
+	return node;	//	Element
+}
+
+dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(/* Node */node, /* string? */tagName){
+	//	summary
+	//	returns the next sibling element matching tagName
+	if(!node) { return null; }
+	do {
+		node = node.nextSibling;
+	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
+
+	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
+		return dojo.dom.nextElement(node, tagName);
+	}
+	return node;	//	Element
+}
+
+dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(/* Node */node, /* string? */tagName){
+	//	summary
+	//	returns the previous sibling element matching tagName
+	if(!node) { return null; }
+	if(tagName) { tagName = tagName.toLowerCase(); }
+	do {
+		node = node.previousSibling;
+	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
+
+	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
+		return dojo.dom.prevElement(node, tagName);
+	}
+	return node;	//	Element
+}
+
+// TODO: hmph
+/*this.forEachChildTag = function(node, unaryFunc) {
+	var child = this.getFirstChildTag(node);
+	while(child) {
+		if(unaryFunc(child) == "break") { break; }
+		child = this.getNextSiblingTag(child);
+	}
+}*/
+
+dojo.dom.moveChildren = function(/* Element */srcNode, /* Element */destNode, /* boolean? */trim){
+	//	summary
+	//	Moves children from srcNode to destNode and returns the count of children moved; 
+	//		will trim off text nodes if trim == true
+	var count = 0;
+	if(trim) {
+		while(srcNode.hasChildNodes() &&
+			srcNode.firstChild.nodeType == dojo.dom.TEXT_NODE) {
+			srcNode.removeChild(srcNode.firstChild);
+		}
+		while(srcNode.hasChildNodes() &&
+			srcNode.lastChild.nodeType == dojo.dom.TEXT_NODE) {
+			srcNode.removeChild(srcNode.lastChild);
+		}
+	}
+	while(srcNode.hasChildNodes()){
+		destNode.appendChild(srcNode.firstChild);
+		count++;
+	}
+	return count;	//	number
+}
+
+dojo.dom.copyChildren = function(/* Element */srcNode, /* Element */destNode, /* boolean? */trim){
+	//	summary
+	//	Copies children from srcNde to destNode and returns the count of children copied;
+	//		will trim off text nodes if trim == true
+	var clonedNode = srcNode.cloneNode(true);
+	return this.moveChildren(clonedNode, destNode, trim);	//	number
+}
+
+dojo.dom.removeChildren = function(/* Element */node){
+	//	summary
+	//	removes all children from node and returns the count of children removed.
+	var count = node.childNodes.length;
+	while(node.hasChildNodes()){ node.removeChild(node.firstChild); }
+	return count;	//	number
+}
+
+dojo.dom.replaceChildren = function(/* Element */node, /* Node */newChild){
+	//	summary
+	//	Removes all children of node and appends newChild
+	// FIXME: what if newChild is an array-like object?
+	dojo.dom.removeChildren(node);
+	node.appendChild(newChild);
+}
+
+dojo.dom.removeNode = function(/* Node */node){
+	//	summary
+	//	if node has a parent, removes node from parent and returns a reference to the removed child.
+	if(node && node.parentNode){
+		// return a ref to the removed child
+		return node.parentNode.removeChild(node);	//	Node
+	}
+}
+
+dojo.dom.getAncestors = function(/* Node */node, /* function? */filterFunction, /* boolean? */returnFirstHit) {
+	//	summary
+	//	returns all ancestors matching optional filterFunction; will return only the first if returnFirstHit
+	var ancestors = [];
+	var isFunction = (filterFunction && (filterFunction instanceof Function || typeof filterFunction == "function"));
+	while(node) {
+		if (!isFunction || filterFunction(node)) {
+			ancestors.push(node);
+		}
+		if (returnFirstHit && ancestors.length > 0) { 
+			return ancestors[0]; 	//	Node
+		}
+		
+		node = node.parentNode;
+	}
+	if (returnFirstHit) { return null; }
+	return ancestors;	//	array
+}
+
+dojo.dom.getAncestorsByTag = function(/* Node */node, /* string */tag, /* boolean? */returnFirstHit) {
+	//	summary
+	//	returns all ancestors matching tag (as tagName), will only return first one if returnFirstHit
+	tag = tag.toLowerCase();
+	return dojo.dom.getAncestors(node, function(el){
+		return ((el.tagName)&&(el.tagName.toLowerCase() == tag));
+	}, returnFirstHit);	//	Node || array
+}
+
+dojo.dom.getFirstAncestorByTag = function(/* Node */node, /* string */tag) {
+	//	summary
+	//	Returns first ancestor of node with tag tagName
+	return dojo.dom.getAncestorsByTag(node, tag, true);	//	Node
+}
+
+dojo.dom.isDescendantOf = function(/* Node */node, /* Node */ancestor, /* boolean? */guaranteeDescendant){
+	//	summary
+	//	Returns boolean if node is a descendant of ancestor
+	// guaranteeDescendant allows us to be a "true" isDescendantOf function
+	if(guaranteeDescendant && node) { node = node.parentNode; }
+	while(node) {
+		if(node == ancestor){ 
+			return true; 	//	boolean
+		}
+		node = node.parentNode;
+	}
+	return false;	//	boolean
+}
+
+dojo.dom.innerXML = function(/* Node */node){
+	//	summary
+	//	Implementation of MS's innerXML function.
+	if(node.innerXML){
+		return node.innerXML;	//	string
+	}else if (node.xml){
+		return node.xml;		//	string
+	}else if(typeof XMLSerializer != "undefined"){
+		return (new XMLSerializer()).serializeToString(node);	//	string
+	}
+}
+
+dojo.dom.createDocument = function(){
+	//	summary
+	//	cross-browser implementation of creating an XML document object.
+	var doc = null;
+	var _document = dojo.doc();
+
+	if(!dj_undef("ActiveXObject")){
+		var prefixes = [ "MSXML2", "Microsoft", "MSXML", "MSXML3" ];
+		for(var i = 0; i<prefixes.length; i++){
+			try{
+				doc = new ActiveXObject(prefixes[i]+".XMLDOM");
+			}catch(e){ /* squelch */ };
+
+			if(doc){ break; }
+		}
+	}else if((_document.implementation)&&
+		(_document.implementation.createDocument)){
+		doc = _document.implementation.createDocument("", "", null);
+	}
+	
+	return doc;	//	DOMDocument
+}
+
+dojo.dom.createDocumentFromText = function(/* string */str, /* string? */mimetype){
+	//	summary
+	//	attempts to create a Document object based on optional mime-type, using str as the contents of the document
+	if(!mimetype){ mimetype = "text/xml"; }
+	if(!dj_undef("DOMParser")){
+		var parser = new DOMParser();
+		return parser.parseFromString(str, mimetype);	//	DOMDocument
+	}else if(!dj_undef("ActiveXObject")){
+		var domDoc = dojo.dom.createDocument();
+		if(domDoc){
+			domDoc.async = false;
+			domDoc.loadXML(str);
+			return domDoc;	//	DOMDocument
+		}else{
+			dojo.debug("toXml didn't work?");
+		}
+	/*
+	}else if((dojo.render.html.capable)&&(dojo.render.html.safari)){
+		// FIXME: this doesn't appear to work!
+		// from: http://web-graphics.com/mtarchive/001606.php
+		// var xml = '<?xml version="1.0"?>'+str;
+		var mtype = "text/xml";
+		var xml = '<?xml version="1.0"?>'+str;
+		var url = "data:"+mtype+";charset=utf-8,"+encodeURIComponent(xml);
+		var req = new XMLHttpRequest();
+		req.open("GET", url, false);
+		req.overrideMimeType(mtype);
+		req.send(null);
+		return req.responseXML;
+	*/
+	}else{
+		var _document = dojo.doc();
+		if(_document.createElement){
+			// FIXME: this may change all tags to uppercase!
+			var tmp = _document.createElement("xml");
+			tmp.innerHTML = str;
+			if(_document.implementation && _document.implementation.createDocument) {
+				var xmlDoc = _document.implementation.createDocument("foo", "", null);
+				for(var i = 0; i < tmp.childNodes.length; i++) {
+					xmlDoc.importNode(tmp.childNodes.item(i), true);
+				}
+				return xmlDoc;	//	DOMDocument
+			}
+			// FIXME: probably not a good idea to have to return an HTML fragment
+			// FIXME: the tmp.doc.firstChild is as tested from IE, so it may not
+			// work that way across the board
+			return ((tmp.document)&&
+				(tmp.document.firstChild ?  tmp.document.firstChild : tmp));	//	DOMDocument
+		}
+	}
+	return null;
+}
+
+dojo.dom.prependChild = function(/* Element */node, /* Element */parent) {
+	// summary
+	//	prepends node to parent's children nodes
+	if(parent.firstChild) {
+		parent.insertBefore(node, parent.firstChild);
+	} else {
+		parent.appendChild(node);
+	}
+	return true;	//	boolean
+}
+
+dojo.dom.insertBefore = function(/* Node */node, /* Node */ref, /* boolean? */force){
+	//	summary
+	//	Try to insert node before ref
+	if (force != true &&
+		(node === ref || node.nextSibling === ref)){ return false; }
+	var parent = ref.parentNode;
+	parent.insertBefore(node, ref);
+	return true;	//	boolean
+}
+
+dojo.dom.insertAfter = function(/* Node */node, /* Node */ref, /* boolean? */force){
+	//	summary
+	//	Try to insert node after ref
+	var pn = ref.parentNode;
+	if(ref == pn.lastChild){
+		if((force != true)&&(node === ref)){
+			return false;	//	boolean
+		}
+		pn.appendChild(node);
+	}else{
+		return this.insertBefore(node, ref.nextSibling, force);	//	boolean
+	}
+	return true;	//	boolean
+}
+
+dojo.dom.insertAtPosition = function(/* Node */node, /* Node */ref, /* string */position){
+	//	summary
+	//	attempt to insert node in relation to ref based on position
+	if((!node)||(!ref)||(!position)){ 
+		return false;	//	boolean 
+	}
+	switch(position.toLowerCase()){
+		case "before":
+			return dojo.dom.insertBefore(node, ref);	//	boolean
+		case "after":
+			return dojo.dom.insertAfter(node, ref);		//	boolean
+		case "first":
+			if(ref.firstChild){
+				return dojo.dom.insertBefore(node, ref.firstChild);	//	boolean
+			}else{
+				ref.appendChild(node);
+				return true;	//	boolean
+			}
+			break;
+		default: // aka: last
+			ref.appendChild(node);
+			return true;	//	boolean
+	}
+}
+
+dojo.dom.insertAtIndex = function(/* Node */node, /* Element */containingNode, /* number */insertionIndex){
+	//	summary
+	//	insert node into child nodes nodelist of containingNode at insertionIndex.
+	var siblingNodes = containingNode.childNodes;
+
+	// if there aren't any kids yet, just add it to the beginning
+
+	if (!siblingNodes.length){
+		containingNode.appendChild(node);
+		return true;	//	boolean
+	}
+
+	// otherwise we need to walk the childNodes
+	// and find our spot
+
+	var after = null;
+
+	for(var i=0; i<siblingNodes.length; i++){
+
+		var sibling_index = siblingNodes.item(i)["getAttribute"] ? parseInt(siblingNodes.item(i).getAttribute("dojoinsertionindex")) : -1;
+
+		if (sibling_index < insertionIndex){
+			after = siblingNodes.item(i);
+		}
+	}
+
+	if (after){
+		// add it after the node in {after}
+
+		return dojo.dom.insertAfter(node, after);	//	boolean
+	}else{
+		// add it to the start
+
+		return dojo.dom.insertBefore(node, siblingNodes.item(0));	//	boolean
+	}
+}
+	
+dojo.dom.textContent = function(/* Node */node, /* string */text){
+	//	summary
+	//	implementation of the DOM Level 3 attribute; scan node for text
+	if (arguments.length>1) {
+		var _document = dojo.doc();
+		dojo.dom.replaceChildren(node, _document.createTextNode(text));
+		return text;	//	string
+	} else {
+		if(node.textContent != undefined){ //FF 1.5
+			return node.textContent;	//	string
+		}
+		var _result = "";
+		if (node == null) { return _result; }
+		for (var i = 0; i < node.childNodes.length; i++) {
+			switch (node.childNodes[i].nodeType) {
+				case 1: // ELEMENT_NODE
+				case 5: // ENTITY_REFERENCE_NODE
+					_result += dojo.dom.textContent(node.childNodes[i]);
+					break;
+				case 3: // TEXT_NODE
+				case 2: // ATTRIBUTE_NODE
+				case 4: // CDATA_SECTION_NODE
+					_result += node.childNodes[i].nodeValue;
+					break;
+				default:
+					break;
+			}
+		}
+		return _result;	//	string
+	}
+}
+
+dojo.dom.hasParent = function (/* Node */node) {
+	//	summary
+	//	returns whether or not node is a child of another node.
+	return node && node.parentNode && dojo.dom.isNode(node.parentNode);	//	boolean
+}
+
+/**
+ * Examples:
+ *
+ * myFooNode = <foo />
+ * isTag(myFooNode, "foo"); // returns "foo"
+ * isTag(myFooNode, "bar"); // returns ""
+ * isTag(myFooNode, "FOO"); // returns ""
+ * isTag(myFooNode, "hey", "foo", "bar"); // returns "foo"
+**/
+dojo.dom.isTag = function(/* Node */node /* ... */) {
+	//	summary
+	//	determines if node has any of the provided tag names and returns the tag name that matches, empty string otherwise.
+	if(node && node.tagName) {
+		for(var i=1; i<arguments.length; i++){
+			if(node.tagName==String(arguments[i])){
+				return String(arguments[i]);	//	string
+			}
+		}
+	}
+	return "";	//	string
+}
+
+dojo.dom.setAttributeNS = function(/* Element */elem, /* string */namespaceURI, /* string */attrName, /* string */attrValue){
+	//	summary
+	//	implementation of DOM2 setAttributeNS that works cross browser.
+	if(elem == null || ((elem == undefined)&&(typeof elem == "undefined"))){
+		dojo.raise("No element given to dojo.dom.setAttributeNS");
+	}
+	
+	if(!((elem.setAttributeNS == undefined)&&(typeof elem.setAttributeNS == "undefined"))){ // w3c
+		elem.setAttributeNS(namespaceURI, attrName, attrValue);
+	}else{ // IE
+		// get a root XML document
+		var ownerDoc = elem.ownerDocument;
+		var attribute = ownerDoc.createNode(
+			2, // node type
+			attrName,
+			namespaceURI
+		);
+		
+		// set value
+		attribute.nodeValue = attrValue;
+		
+		// attach to element
+		elem.setAttributeNode(attribute);
+	}
+}
+
+dojo.provide("dojo.undo.browser");
+
+try{
+	if((!djConfig["preventBackButtonFix"])&&(!dojo.hostenv.post_load_)){
+		document.write("<iframe style='border: 0px; width: 1px; height: 1px; position: absolute; bottom: 0px; right: 0px; visibility: visible;' name='djhistory' id='djhistory' src='"+(dojo.hostenv.getBaseScriptUri()+'iframe_history.html')+"'></iframe>");
+	}
+}catch(e){/* squelch */}
+
+if(dojo.render.html.opera){
+	dojo.debug("Opera is not supported with dojo.undo.browser, so back/forward detection will not work.");
+}
+
+/* NOTES:
+ *  Safari 1.2: 
+ *	back button "works" fine, however it's not possible to actually
+ *	DETECT that you've moved backwards by inspecting window.location.
+ *	Unless there is some other means of locating.
+ *	FIXME: perhaps we can poll on history.length?
+ *  Safari 2.0.3+ (and probably 1.3.2+):
+ *	works fine, except when changeUrl is used. When changeUrl is used,
+ *	Safari jumps all the way back to whatever page was shown before
+ *	the page that uses dojo.undo.browser support.
+ *  IE 5.5 SP2:
+ *	back button behavior is macro. It does not move back to the
+ *	previous hash value, but to the last full page load. This suggests
+ *	that the iframe is the correct way to capture the back button in
+ *	these cases.
+ *	Don't test this page using local disk for MSIE. MSIE will not create 
+ *	a history list for iframe_history.html if served from a file: URL. 
+ *	The XML served back from the XHR tests will also not be properly 
+ *	created if served from local disk. Serve the test pages from a web 
+ *	server to test in that browser.
+ *  IE 6.0:
+ *	same behavior as IE 5.5 SP2
+ * Firefox 1.0+:
+ *	the back button will return us to the previous hash on the same
+ *	page, thereby not requiring an iframe hack, although we do then
+ *	need to run a timer to detect inter-page movement.
+ */
+
+dojo.undo.browser = {
+	initialHref: window.location.href,
+	initialHash: window.location.hash,
+
+	moveForward: false,
+	historyStack: [],
+	forwardStack: [],
+	historyIframe: null,
+	bookmarkAnchor: null,
+	locationTimer: null,
+
+	/**
+	 * setInitialState sets the state object and back callback for the very first page that is loaded.
+	 * It is recommended that you call this method as part of an event listener that is registered via
+	 * dojo.addOnLoad().
+	 */
+	setInitialState: function(args){
+		this.initialState = this._createState(this.initialHref, args, this.initialHash);
+	},
+
+	//FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things.
+	//FIXME: is there a slight race condition in moz using change URL with the timer check and when
+	//       the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent.
+	/**
+	 * addToHistory takes one argument, and it is an object that defines the following functions:
+	 * - To support getting back button notifications, the object argument should implement a
+	 *   function called either "back", "backButton", or "handle". The string "back" will be
+	 *   passed as the first and only argument to this callback.
+	 * - To support getting forward button notifications, the object argument should implement a
+	 *   function called either "forward", "forwardButton", or "handle". The string "forward" will be
+	 *   passed as the first and only argument to this callback.
+	 * - If you want the browser location string to change, define "changeUrl" on the object. If the
+	 *   value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment
+	 *   identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does
+	 *   not evaluate to false, that value will be used as the fragment identifier. For example,
+	 *   if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1
+	 *   
+	 * Full example:
+	 * 
+	 * dojo.undo.browser.addToHistory({
+	 *   back: function() { alert('back pressed'); },
+	 *   forward: function() { alert('forward pressed'); },
+	 *   changeUrl: true
+	 * });
+	 */
+	addToHistory: function(args){
+		//If addToHistory is called, then that means we prune the
+		//forward stack -- the user went back, then wanted to
+		//start a new forward path.
+		this.forwardStack = []; 
+
+		var hash = null;
+		var url = null;
+		if(!this.historyIframe){
+			this.historyIframe = window.frames["djhistory"];
+		}
+		if(!this.bookmarkAnchor){
+			this.bookmarkAnchor = document.createElement("a");
+			dojo.body().appendChild(this.bookmarkAnchor);
+			this.bookmarkAnchor.style.display = "none";
+		}
+		if(args["changeUrl"]){
+			hash = "#"+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime());
+			
+			//If the current hash matches the new one, just replace the history object with
+			//this new one. It doesn't make sense to track different state objects for the same
+			//logical URL. This matches the browser behavior of only putting in one history
+			//item no matter how many times you click on the same #hash link, at least in Firefox
+			//and Safari, and there is no reliable way in those browsers to know if a #hash link
+			//has been clicked on multiple times. So making this the standard behavior in all browsers
+			//so that dojo.undo.browser's behavior is the same in all browsers.
+			if(this.historyStack.length == 0 && this.initialState.urlHash == hash){
+				this.initialState = this._createState(url, args, hash);
+				return;
+			}else if(this.historyStack.length > 0 && this.historyStack[this.historyStack.length - 1].urlHash == hash){
+				this.historyStack[this.historyStack.length - 1] = this._createState(url, args, hash);
+				return;
+			}
+
+			this.changingUrl = true;
+			setTimeout("window.location.href = '"+hash+"'; dojo.undo.browser.changingUrl = false;", 1);
+			this.bookmarkAnchor.href = hash;
+			
+			if(dojo.render.html.ie){
+				url = this._loadIframeHistory();
+
+				var oldCB = args["back"]||args["backButton"]||args["handle"];
+
+				//The function takes handleName as a parameter, in case the
+				//callback we are overriding was "handle". In that case,
+				//we will need to pass the handle name to handle.
+				var tcb = function(handleName){
+					if(window.location.hash != ""){
+						setTimeout("window.location.href = '"+hash+"';", 1);
+					}
+					//Use apply to set "this" to args, and to try to avoid memory leaks.
+					oldCB.apply(this, [handleName]);
+				}
+		
+				//Set interceptor function in the right place.
+				if(args["back"]){
+					args.back = tcb;
+				}else if(args["backButton"]){
+					args.backButton = tcb;
+				}else if(args["handle"]){
+					args.handle = tcb;
+				}
+		
+				var oldFW = args["forward"]||args["forwardButton"]||args["handle"];
+		
+				//The function takes handleName as a parameter, in case the
+				//callback we are overriding was "handle". In that case,
+				//we will need to pass the handle name to handle.
+				var tfw = function(handleName){
+					if(window.location.hash != ""){
+						window.location.href = hash;
+					}
+					if(oldFW){ // we might not actually have one
+						//Use apply to set "this" to args, and to try to avoid memory leaks.
+						oldFW.apply(this, [handleName]);
+					}
+				}
+
+				//Set interceptor function in the right place.
+				if(args["forward"]){
+					args.forward = tfw;
+				}else if(args["forwardButton"]){
+					args.forwardButton = tfw;
+				}else if(args["handle"]){
+					args.handle = tfw;
+				}
+
+			}else if(dojo.render.html.moz){
+				// start the timer
+				if(!this.locationTimer){
+					this.locationTimer = setInterval("dojo.undo.browser.checkLocation();", 200);
+				}
+			}
+		}else{
+			url = this._loadIframeHistory();
+		}
+
+		this.historyStack.push(this._createState(url, args, hash));
+	},
+
+	checkLocation: function(){
+		if (!this.changingUrl){
+			var hsl = this.historyStack.length;
+
+			if((window.location.hash == this.initialHash||window.location.href == this.initialHref)&&(hsl == 1)){
+				// FIXME: could this ever be a forward button?
+				// we can't clear it because we still need to check for forwards. Ugg.
+				// clearInterval(this.locationTimer);
+				this.handleBackButton();
+				return;
+			}
+			
+			// first check to see if we could have gone forward. We always halt on
+			// a no-hash item.
+			if(this.forwardStack.length > 0){
+				if(this.forwardStack[this.forwardStack.length-1].urlHash == window.location.hash){
+					this.handleForwardButton();
+					return;
+				}
+			}
+	
+			// ok, that didn't work, try someplace back in the history stack
+			if((hsl >= 2)&&(this.historyStack[hsl-2])){
+				if(this.historyStack[hsl-2].urlHash==window.location.hash){
+					this.handleBackButton();
+					return;
+				}
+			}
+		}
+	},
+
+	iframeLoaded: function(evt, ifrLoc){
+		if(!dojo.render.html.opera){
+			var query = this._getUrlQuery(ifrLoc.href);
+			if(query == null){ 
+				// alert("iframeLoaded");
+				// we hit the end of the history, so we should go back
+				if(this.historyStack.length == 1){
+					this.handleBackButton();
+				}
+				return;
+			}
+			if(this.moveForward){
+				// we were expecting it, so it's not either a forward or backward movement
+				this.moveForward = false;
+				return;
+			}
+	
+			//Check the back stack first, since it is more likely.
+			//Note that only one step back or forward is supported.
+			if(this.historyStack.length >= 2 && query == this._getUrlQuery(this.historyStack[this.historyStack.length-2].url)){
+				this.handleBackButton();
+			}
+			else if(this.forwardStack.length > 0 && query == this._getUrlQuery(this.forwardStack[this.forwardStack.length-1].url)){
+				this.handleForwardButton();
+			}
+		}
+	},
+
+	handleBackButton: function(){
+		//The "current" page is always at the top of the history stack.
+		var current = this.historyStack.pop();
+		if(!current){ return; }
+		var last = this.historyStack[this.historyStack.length-1];
+		if(!last && this.historyStack.length == 0){
+			last = this.initialState;
+		}
+		if (last){
+			if(last.kwArgs["back"]){
+				last.kwArgs["back"]();
+			}else if(last.kwArgs["backButton"]){
+				last.kwArgs["backButton"]();
+			}else if(last.kwArgs["handle"]){
+				last.kwArgs.handle("back");
+			}
+		}
+		this.forwardStack.push(current);
+	},
+
+	handleForwardButton: function(){
+		var last = this.forwardStack.pop();
+		if(!last){ return; }
+		if(last.kwArgs["forward"]){
+			last.kwArgs.forward();
+		}else if(last.kwArgs["forwardButton"]){
+			last.kwArgs.forwardButton();
+		}else if(last.kwArgs["handle"]){
+			last.kwArgs.handle("forward");
+		}
+		this.historyStack.push(last);
+	},
+
+	_createState: function(url, args, hash){
+		return {"url": url, "kwArgs": args, "urlHash": hash};	
+	},
+
+	_getUrlQuery: function(url){
+		var segments = url.split("?");
+		if (segments.length < 2){
+			return null;
+		}
+		else{
+			return segments[1];
+		}
+	},
+	
+	_loadIframeHistory: function(){
+		var url = dojo.hostenv.getBaseScriptUri()+"iframe_history.html?"+(new Date()).getTime();
+		this.moveForward = true;
+		dojo.io.setIFrameSrc(this.historyIframe, url, false);	
+		return url;
+	}
+}
+
+dojo.provide("dojo.io.BrowserIO");
+
+
+dojo.io.checkChildrenForFile = function(node){
+	var hasFile = false;
+	var inputs = node.getElementsByTagName("input");
+	dojo.lang.forEach(inputs, function(input){
+		if(hasFile){ return; }
+		if(input.getAttribute("type")=="file"){
+			hasFile = true;
+		}
+	});
+	return hasFile;
+}
+
+dojo.io.formHasFile = function(formNode){
+	return dojo.io.checkChildrenForFile(formNode);
+}
+
+dojo.io.updateNode = function(node, urlOrArgs){
+	node = dojo.byId(node);
+	var args = urlOrArgs;
+	if(dojo.lang.isString(urlOrArgs)){
+		args = { url: urlOrArgs };
+	}
+	args.mimetype = "text/html";
+	args.load = function(t, d, e){
+		while(node.firstChild){
+			if(dojo["event"]){
+				try{
+					dojo.event.browser.clean(node.firstChild);
+				}catch(e){}
+			}
+			node.removeChild(node.firstChild);
+		}
+		node.innerHTML = d;
+	};
+	dojo.io.bind(args);
+}
+
+dojo.io.formFilter = function(node) {
+	var type = (node.type||"").toLowerCase();
+	return !node.disabled && node.name
+		&& !dojo.lang.inArray(["file", "submit", "image", "reset", "button"], type);
+}
+
+// TODO: Move to htmlUtils
+dojo.io.encodeForm = function(formNode, encoding, formFilter){
+	if((!formNode)||(!formNode.tagName)||(!formNode.tagName.toLowerCase() == "form")){
+		dojo.raise("Attempted to encode a non-form element.");
+	}
+	if(!formFilter) { formFilter = dojo.io.formFilter; }
+	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
+	var values = [];
+
+	for(var i = 0; i < formNode.elements.length; i++){
+		var elm = formNode.elements[i];
+		if(!elm || elm.tagName.toLowerCase() == "fieldset" || !formFilter(elm)) { continue; }
+		var name = enc(elm.name);
+		var type = elm.type.toLowerCase();
+
+		if(type == "select-multiple"){
+			for(var j = 0; j < elm.options.length; j++){
+				if(elm.options[j].selected) {
+					values.push(name + "=" + enc(elm.options[j].value));
+				}
+			}
+		}else if(dojo.lang.inArray(["radio", "checkbox"], type)){
+			if(elm.checked){
+				values.push(name + "=" + enc(elm.value));
+			}
+		}else{
+			values.push(name + "=" + enc(elm.value));
+		}
+	}
+
+	// now collect input type="image", which doesn't show up in the elements array
+	var inputs = formNode.getElementsByTagName("input");
+	for(var i = 0; i < inputs.length; i++) {
+		var input = inputs[i];
+		if(input.type.toLowerCase() == "image" && input.form == formNode
+			&& formFilter(input)) {
+			var name = enc(input.name);
+			values.push(name + "=" + enc(input.value));
+			values.push(name + ".x=0");
+			values.push(name + ".y=0");
+		}
+	}
+	return values.join("&") + "&";
+}
+
+dojo.io.FormBind = function(args) {
+	this.bindArgs = {};
+
+	if(args && args.formNode) {
+		this.init(args);
+	} else if(args) {
+		this.init({formNode: args});
+	}
+}
+dojo.lang.extend(dojo.io.FormBind, {
+	form: null,
+
+	bindArgs: null,
+
+	clickedButton: null,
+
+	init: function(args) {
+		var form = dojo.byId(args.formNode);
+
+		if(!form || !form.tagName || form.tagName.toLowerCase() != "form") {
+			throw new Error("FormBind: Couldn't apply, invalid form");
+		} else if(this.form == form) {
+			return;
+		} else if(this.form) {
+			throw new Error("FormBind: Already applied to a form");
+		}
+
+		dojo.lang.mixin(this.bindArgs, args);
+		this.form = form;
+
+		this.connect(form, "onsubmit", "submit");
+
+		for(var i = 0; i < form.elements.length; i++) {
+			var node = form.elements[i];
+			if(node && node.type && dojo.lang.inArray(["submit", "button"], node.type.toLowerCase())) {
+				this.connect(node, "onclick", "click");
+			}
+		}
+
+		var inputs = form.getElementsByTagName("input");
+		for(var i = 0; i < inputs.length; i++) {
+			var input = inputs[i];
+			if(input.type.toLowerCase() == "image" && input.form == form) {
+				this.connect(input, "onclick", "click");
+			}
+		}
+	},
+
+	onSubmit: function(form) {
+		return true;
+	},
+
+	submit: function(e) {
+		e.preventDefault();
+		if(this.onSubmit(this.form)) {
+			dojo.io.bind(dojo.lang.mixin(this.bindArgs, {
+				formFilter: dojo.lang.hitch(this, "formFilter")
+			}));
+		}
+	},
+
+	click: function(e) {
+		var node = e.currentTarget;
+		if(node.disabled) { return; }
+		this.clickedButton = node;
+	},
+
+	formFilter: function(node) {
+		var type = (node.type||"").toLowerCase();
+		var accept = false;
+		if(node.disabled || !node.name) {
+			accept = false;
+		} else if(dojo.lang.inArray(["submit", "button", "image"], type)) {
+			if(!this.clickedButton) { this.clickedButton = node; }
+			accept = node == this.clickedButton;
+		} else {
+			accept = !dojo.lang.inArray(["file", "submit", "reset", "button"], type);
+		}
+		return accept;
+	},
+
+	// in case you don't have dojo.event.* pulled in
+	connect: function(srcObj, srcFcn, targetFcn) {
+		if(dojo.evalObjPath("dojo.event.connect")) {
+			dojo.event.connect(srcObj, srcFcn, this, targetFcn);
+		} else {
+			var fcn = dojo.lang.hitch(this, targetFcn);
+			srcObj[srcFcn] = function(e) {
+				if(!e) { e = window.event; }
+				if(!e.currentTarget) { e.currentTarget = e.srcElement; }
+				if(!e.preventDefault) { e.preventDefault = function() { window.event.returnValue = false; } }
+				fcn(e);
+			}
+		}
+	}
+});
+
+dojo.io.XMLHTTPTransport = new function(){
+	var _this = this;
+
+	var _cache = {}; // FIXME: make this public? do we even need to?
+	this.useCache = false; // if this is true, we'll cache unless kwArgs.useCache = false
+	this.preventCache = false; // if this is true, we'll always force GET requests to cache
+
+	// FIXME: Should this even be a function? or do we just hard code it in the next 2 functions?
+	function getCacheKey(url, query, method) {
+		return url + "|" + query + "|" + method.toLowerCase();
+	}
+
+	function addToCache(url, query, method, http) {
+		_cache[getCacheKey(url, query, method)] = http;
+	}
+
+	function getFromCache(url, query, method) {
+		return _cache[getCacheKey(url, query, method)];
+	}
+
+	this.clearCache = function() {
+		_cache = {};
+	}
+
+	// moved successful load stuff here
+	function doLoad(kwArgs, http, url, query, useCache) {
+		if(	((http.status>=200)&&(http.status<300))|| 	// allow any 2XX response code
+			(http.status==304)|| 						// get it out of the cache
+			(location.protocol=="file:" && (http.status==0 || http.status==undefined))||
+			(location.protocol=="chrome:" && (http.status==0 || http.status==undefined))
+		){
+			var ret;
+			if(kwArgs.method.toLowerCase() == "head"){
+				var headers = http.getAllResponseHeaders();
+				ret = {};
+				ret.toString = function(){ return headers; }
+				var values = headers.split(/[\r\n]+/g);
+				for(var i = 0; i < values.length; i++) {
+					var pair = values[i].match(/^([^:]+)\s*:\s*(.+)$/i);
+					if(pair) {
+						ret[pair[1]] = pair[2];
+					}
+				}
+			}else if(kwArgs.mimetype == "text/javascript"){
+				try{
+					ret = dj_eval(http.responseText);
+				}catch(e){
+					dojo.debug(e);
+					dojo.debug(http.responseText);
+					ret = null;
+				}
+			}else if(kwArgs.mimetype == "text/json" || kwArgs.mimetype == "application/json"){
+				try{
+					ret = dj_eval("("+http.responseText+")");
+				}catch(e){
+					dojo.debug(e);
+					dojo.debug(http.responseText);
+					ret = false;
+				}
+			}else if((kwArgs.mimetype == "application/xml")||
+						(kwArgs.mimetype == "text/xml")){
+				ret = http.responseXML;
+				if(!ret || typeof ret == "string" || !http.getResponseHeader("Content-Type")) {
+					ret = dojo.dom.createDocumentFromText(http.responseText);
+				}
+			}else{
+				ret = http.responseText;
+			}
+
+			if(useCache){ // only cache successful responses
+				addToCache(url, query, kwArgs.method, http);
+			}
+			kwArgs[(typeof kwArgs.load == "function") ? "load" : "handle"]("load", ret, http, kwArgs);
+		}else{
+			var errObj = new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText);
+			kwArgs[(typeof kwArgs.error == "function") ? "error" : "handle"]("error", errObj, http, kwArgs);
+		}
+	}
+
+	// set headers (note: Content-Type will get overriden if kwArgs.contentType is set)
+	function setHeaders(http, kwArgs){
+		if(kwArgs["headers"]) {
+			for(var header in kwArgs["headers"]) {
+				if(header.toLowerCase() == "content-type" && !kwArgs["contentType"]) {
+					kwArgs["contentType"] = kwArgs["headers"][header];
+				} else {
+					http.setRequestHeader(header, kwArgs["headers"][header]);
+				}
+			}
+		}
+	}
+
+	this.inFlight = [];
+	this.inFlightTimer = null;
+
+	this.startWatchingInFlight = function(){
+		if(!this.inFlightTimer){
+			// setInterval broken in mozilla x86_64 in some circumstances, see
+			// https://bugzilla.mozilla.org/show_bug.cgi?id=344439
+			// using setTimeout instead
+			this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
+		}
+	}
+
+	this.watchInFlight = function(){
+		var now = null;
+		// make sure sync calls stay thread safe, if this callback is called during a sync call
+		// and this results in another sync call before the first sync call ends the browser hangs
+		if(!dojo.hostenv._blockAsync && !_this._blockAsync){
+			for(var x=this.inFlight.length-1; x>=0; x--){
+				try{
+					var tif = this.inFlight[x];
+					if(!tif || tif.http._aborted || !tif.http.readyState){
+						this.inFlight.splice(x, 1); continue; 
+					}
+					if(4==tif.http.readyState){
+						// remove it so we can clean refs
+						this.inFlight.splice(x, 1);
+						doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache);
+					}else if (tif.startTime){
+						//See if this is a timeout case.
+						if(!now){
+							now = (new Date()).getTime();
+						}
+						if(tif.startTime + (tif.req.timeoutSeconds * 1000) < now){
+							//Stop the request.
+							if(typeof tif.http.abort == "function"){
+								tif.http.abort();
+							}
+		
+							// remove it so we can clean refs
+							this.inFlight.splice(x, 1);
+							tif.req[(typeof tif.req.timeout == "function") ? "timeout" : "handle"]("timeout", null, tif.http, tif.req);
+						}
+					}
+				}catch(e){
+					try{
+						var errObj = new dojo.io.Error("XMLHttpTransport.watchInFlight Error: " + e);
+						tif.req[(typeof tif.req.error == "function") ? "error" : "handle"]("error", errObj, tif.http, tif.req);
+					}catch(e2){
+						dojo.debug("XMLHttpTransport error callback failed: " + e2);
+					}
+				}
+			}
+		}
+
+		clearTimeout(this.inFlightTimer);
+		if(this.inFlight.length == 0){
+			this.inFlightTimer = null;
+			return;
+		}
+		this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
+	}
+
+	var hasXmlHttp = dojo.hostenv.getXmlhttpObject() ? true : false;
+	this.canHandle = function(kwArgs){
+		// canHandle just tells dojo.io.bind() if this is a good transport to
+		// use for the particular type of request.
+
+		// FIXME: we need to determine when form values need to be
+		// multi-part mime encoded and avoid using this transport for those
+		// requests.
+		return hasXmlHttp
+			&& dojo.lang.inArray(["text/plain", "text/html", "application/xml", "text/xml", "text/javascript", "text/json", "application/json"], (kwArgs["mimetype"].toLowerCase()||""))
+			&& !( kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]) );
+	}
+
+	this.multipartBoundary = "45309FFF-BD65-4d50-99C9-36986896A96F";	// unique guid as a boundary value for multipart posts
+
+	this.bind = function(kwArgs){
+		if(!kwArgs["url"]){
+			// are we performing a history action?
+			if( !kwArgs["formNode"]
+				&& (kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"] || kwArgs["watchForURL"])
+				&& (!djConfig.preventBackButtonFix)) {
+        dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request",
+        				"Use dojo.undo.browser.addToHistory() instead.", "0.4");
+				dojo.undo.browser.addToHistory(kwArgs);
+				return true;
+			}
+		}
+
+		// build this first for cache purposes
+		var url = kwArgs.url;
+		var query = "";
+		if(kwArgs["formNode"]){
+			var ta = kwArgs.formNode.getAttribute("action");
+			if((ta)&&(!kwArgs["url"])){ url = ta; }
+			var tp = kwArgs.formNode.getAttribute("method");
+			if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; }
+			query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]);
+		}
+
+		if(url.indexOf("#") > -1) {
+			dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url);
+			url = url.split("#")[0];
+		}
+
+		if(kwArgs["file"]){
+			// force post for file transfer
+			kwArgs.method = "post";
+		}
+
+		if(!kwArgs["method"]){
+			kwArgs.method = "get";
+		}
+
+		// guess the multipart value
+		if(kwArgs.method.toLowerCase() == "get"){
+			// GET cannot use multipart
+			kwArgs.multipart = false;
+		}else{
+			if(kwArgs["file"]){
+				// enforce multipart when sending files
+				kwArgs.multipart = true;
+			}else if(!kwArgs["multipart"]){
+				// default 
+				kwArgs.multipart = false;
+			}
+		}
+
+		if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
+			dojo.undo.browser.addToHistory(kwArgs);
+		}
+
+		var content = kwArgs["content"] || {};
+
+		if(kwArgs.sendTransport) {
+			content["dojo.transport"] = "xmlhttp";
+		}
+
+		do { // break-block
+			if(kwArgs.postContent){
+				query = kwArgs.postContent;
+				break;
+			}
+
+			if(content) {
+				query += dojo.io.argsFromMap(content, kwArgs.encoding);
+			}
+			
+			if(kwArgs.method.toLowerCase() == "get" || !kwArgs.multipart){
+				break;
+			}
+
+			var	t = [];
+			if(query.length){
+				var q = query.split("&");
+				for(var i = 0; i < q.length; ++i){
+					if(q[i].length){
+						var p = q[i].split("=");
+						t.push(	"--" + this.multipartBoundary,
+								"Content-Disposition: form-data; name=\"" + p[0] + "\"", 
+								"",
+								p[1]);
+					}
+				}
+			}
+
+			if(kwArgs.file){
+				if(dojo.lang.isArray(kwArgs.file)){
+					for(var i = 0; i < kwArgs.file.length; ++i){
+						var o = kwArgs.file[i];
+						t.push(	"--" + this.multipartBoundary,
+								"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
+								"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
+								"",
+								o.content);
+					}
+				}else{
+					var o = kwArgs.file;
+					t.push(	"--" + this.multipartBoundary,
+							"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
+							"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
+							"",
+							o.content);
+				}
+			}
+
+			if(t.length){
+				t.push("--"+this.multipartBoundary+"--", "");
+				query = t.join("\r\n");
+			}
+		}while(false);
+
+		// kwArgs.Connection = "close";
+
+		var async = kwArgs["sync"] ? false : true;
+
+		var preventCache = kwArgs["preventCache"] ||
+			(this.preventCache == true && kwArgs["preventCache"] != false);
+		var useCache = kwArgs["useCache"] == true ||
+			(this.useCache == true && kwArgs["useCache"] != false );
+
+		// preventCache is browser-level (add query string junk), useCache
+		// is for the local cache. If we say preventCache, then don't attempt
+		// to look in the cache, but if useCache is true, we still want to cache
+		// the response
+		if(!preventCache && useCache){
+			var cachedHttp = getFromCache(url, query, kwArgs.method);
+			if(cachedHttp){
+				doLoad(kwArgs, cachedHttp, url, query, false);
+				return;
+			}
+		}
+
+		// much of this is from getText, but reproduced here because we need
+		// more flexibility
+		var http = dojo.hostenv.getXmlhttpObject(kwArgs);	
+		var received = false;
+
+		// build a handler function that calls back to the handler obj
+		if(async){
+			var startTime = 
+			// FIXME: setting up this callback handler leaks on IE!!!
+			this.inFlight.push({
+				"req":		kwArgs,
+				"http":		http,
+				"url":	 	url,
+				"query":	query,
+				"useCache":	useCache,
+				"startTime": kwArgs.timeoutSeconds ? (new Date()).getTime() : 0
+			});
+			this.startWatchingInFlight();
+		}else{
+			// block async callbacks until sync is in, needed in khtml, others?
+			_this._blockAsync = true;
+		}
+
+		if(kwArgs.method.toLowerCase() == "post"){
+			// FIXME: need to hack in more flexible Content-Type setting here!
+			if (!kwArgs.user) {
+				http.open("POST", url, async);
+			}else{
+        http.open("POST", url, async, kwArgs.user, kwArgs.password);
+			}
+			setHeaders(http, kwArgs);
+			http.setRequestHeader("Content-Type", kwArgs.multipart ? ("multipart/form-data; boundary=" + this.multipartBoundary) : 
+				(kwArgs.contentType || "application/x-www-form-urlencoded"));
+			try{
+				http.send(query);
+			}catch(e){
+				if(typeof http.abort == "function"){
+					http.abort();
+				}
+				doLoad(kwArgs, {status: 404}, url, query, useCache);
+			}
+		}else{
+			var tmpUrl = url;
+			if(query != "") {
+				tmpUrl += (tmpUrl.indexOf("?") > -1 ? "&" : "?") + query;
+			}
+			if(preventCache) {
+				tmpUrl += (dojo.string.endsWithAny(tmpUrl, "?", "&")
+					? "" : (tmpUrl.indexOf("?") > -1 ? "&" : "?")) + "dojo.preventCache=" + new Date().valueOf();
+			}
+			if (!kwArgs.user) {
+				http.open(kwArgs.method.toUpperCase(), tmpUrl, async);
+			}else{
+				http.open(kwArgs.method.toUpperCase(), tmpUrl, async, kwArgs.user, kwArgs.password);
+			}
+			setHeaders(http, kwArgs);
+			try {
+				http.send(null);
+			}catch(e)	{
+				if(typeof http.abort == "function"){
+					http.abort();
+				}
+				doLoad(kwArgs, {status: 404}, url, query, useCache);
+			}
+		}
+
+		if( !async ) {
+			doLoad(kwArgs, http, url, query, useCache);
+			_this._blockAsync = false;
+		}
+
+		kwArgs.abort = function(){
+			try{// khtml doesent reset readyState on abort, need this workaround
+				http._aborted = true; 
+			}catch(e){/*squelsh*/}
+			return http.abort();
+		}
+
+		return;
+	}
+	dojo.io.transports.addTransport("XMLHTTPTransport");
+}
+
+dojo.provide("dojo.io.cookie");
+
+dojo.io.cookie.setCookie = function(name, value, days, path, domain, secure) {
+	var expires = -1;
+	if(typeof days == "number" && days >= 0) {
+		var d = new Date();
+		d.setTime(d.getTime()+(days*24*60*60*1000));
+		expires = d.toGMTString();
+	}
+	value = escape(value);
+	document.cookie = name + "=" + value + ";"
+		+ (expires != -1 ? " expires=" + expires + ";" : "")
+		+ (path ? "path=" + path : "")
+		+ (domain ? "; domain=" + domain : "")
+		+ (secure ? "; secure" : "");
+}
+
+dojo.io.cookie.set = dojo.io.cookie.setCookie;
+
+dojo.io.cookie.getCookie = function(name) {
+	// FIXME: Which cookie should we return?
+	//        If there are cookies set for different sub domains in the current
+	//        scope there could be more than one cookie with the same name.
+	//        I think taking the last one in the list takes the one from the
+	//        deepest subdomain, which is what we're doing here.
+	var idx = document.cookie.lastIndexOf(name+'=');
+	if(idx == -1) { return null; }
+	var value = document.cookie.substring(idx+name.length+1);
+	var end = value.indexOf(';');
+	if(end == -1) { end = value.length; }
+	value = value.substring(0, end);
+	value = unescape(value);
+	return value;
+}
+
+dojo.io.cookie.get = dojo.io.cookie.getCookie;
+
+dojo.io.cookie.deleteCookie = function(name) {
+	dojo.io.cookie.setCookie(name, "-", 0);
+}
+
+dojo.io.cookie.setObjectCookie = function(name, obj, days, path, domain, secure, clearCurrent) {
+	if(arguments.length == 5) { // for backwards compat
+		clearCurrent = domain;
+		domain = null;
+		secure = null;
+	}
+	var pairs = [], cookie, value = "";
+	if(!clearCurrent) { cookie = dojo.io.cookie.getObjectCookie(name); }
+	if(days >= 0) {
+		if(!cookie) { cookie = {}; }
+		for(var prop in obj) {
+			if(prop == null) {
+				delete cookie[prop];
+			} else if(typeof obj[prop] == "string" || typeof obj[prop] == "number") {
+				cookie[prop] = obj[prop];
+			}
+		}
+		prop = null;
+		for(var prop in cookie) {
+			pairs.push(escape(prop) + "=" + escape(cookie[prop]));
+		}
+		value = pairs.join("&");
+	}
+	dojo.io.cookie.setCookie(name, value, days, path, domain, secure);
+}
+
+dojo.io.cookie.getObjectCookie = function(name) {
+	var values = null, cookie = dojo.io.cookie.getCookie(name);
+	if(cookie) {
+		values = {};
+		var pairs = cookie.split("&");
+		for(var i = 0; i < pairs.length; i++) {
+			var pair = pairs[i].split("=");
+			var value = pair[1];
+			if( isNaN(value) ) { value = unescape(pair[1]); }
+			values[ unescape(pair[0]) ] = value;
+		}
+	}
+	return values;
+}
+
+dojo.io.cookie.isSupported = function() {
+	if(typeof navigator.cookieEnabled != "boolean") {
+		dojo.io.cookie.setCookie("__TestingYourBrowserForCookieSupport__",
+			"CookiesAllowed", 90, null);
+		var cookieVal = dojo.io.cookie.getCookie("__TestingYourBrowserForCookieSupport__");
+		navigator.cookieEnabled = (cookieVal == "CookiesAllowed");
+		if(navigator.cookieEnabled) {
+			// FIXME: should we leave this around?
+			this.deleteCookie("__TestingYourBrowserForCookieSupport__");
+		}
+	}
+	return navigator.cookieEnabled;
+}
+
+// need to leave this in for backwards-compat from 0.1 for when it gets pulled in by dojo.io.*
+if(!dojo.io.cookies) { dojo.io.cookies = dojo.io.cookie; }
+
+dojo.provide("dojo.io.*");
+
+dojo.provide("dojo.io");
+
+dojo.deprecated("dojo.io", "replaced by dojo.io.*", "0.5");
+
+dojo.provide("dojo.event.common");
+
+
+// TODO: connection filter functions
+//			these are functions that accept a method invocation (like around
+//			advice) and return a boolean based on it. That value determines
+//			whether or not the connection proceeds. It could "feel" like around
+//			advice for those who know what it is (calling proceed() or not),
+//			but I think presenting it as a "filter" and/or calling it with the
+//			function args and not the MethodInvocation might make it more
+//			palletable to "normal" users than around-advice currently is
+// TODO: execution scope mangling
+//			YUI's event facility by default executes listeners in the context
+//			of the source object. This is very odd, but should probably be
+//			supported as an option (both for the source and for the dest). It
+//			can be thought of as a connection-specific hitch().
+// TODO: more resiliency for 4+ arguments to connect()
+
+dojo.event = new function(){
+	this._canTimeout = dojo.lang.isFunction(dj_global["setTimeout"])||dojo.lang.isAlien(dj_global["setTimeout"]);
+
+	// FIXME: where should we put this method (not here!)?
+	function interpolateArgs(args, searchForNames){
+		var dl = dojo.lang;
+		var ao = {
+			srcObj: dj_global,
+			srcFunc: null,
+			adviceObj: dj_global,
+			adviceFunc: null,
+			aroundObj: null,
+			aroundFunc: null,
+			adviceType: (args.length>2) ? args[0] : "after",
+			precedence: "last",
+			once: false,
+			delay: null,
+			rate: 0,
+			adviceMsg: false
+		};
+
+		switch(args.length){
+			case 0: return;
+			case 1: return;
+			case 2:
+				ao.srcFunc = args[0];
+				ao.adviceFunc = args[1];
+				break;
+			case 3:
+				if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){
+					ao.adviceType = "after";
+					ao.srcObj = args[0];
+					ao.srcFunc = args[1];
+					ao.adviceFunc = args[2];
+				}else if((dl.isString(args[1]))&&(dl.isString(args[2]))){
+					ao.srcFunc = args[1];
+					ao.adviceFunc = args[2];
+				}else if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){
+					ao.adviceType = "after";
+					ao.srcObj = args[0];
+					ao.srcFunc = args[1];
+					var tmpName  = dl.nameAnonFunc(args[2], ao.adviceObj, searchForNames);
+					ao.adviceFunc = tmpName;
+				}else if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){
+					ao.adviceType = "after";
+					ao.srcObj = dj_global;
+					var tmpName  = dl.nameAnonFunc(args[0], ao.srcObj, searchForNames);
+					ao.srcFunc = tmpName;
+					ao.adviceObj = args[1];
+					ao.adviceFunc = args[2];
+				}
+				break;
+			case 4:
+				if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){
+					// we can assume that we've got an old-style "connect" from
+					// the sigslot school of event attachment. We therefore
+					// assume after-advice.
+					ao.adviceType = "after";
+					ao.srcObj = args[0];
+					ao.srcFunc = args[1];
+					ao.adviceObj = args[2];
+					ao.adviceFunc = args[3];
+				}else if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){
+					ao.adviceType = args[0];
+					ao.srcObj = dj_global;
+					ao.srcFunc = args[1];
+					ao.adviceObj = args[2];
+					ao.adviceFunc = args[3];
+				}else if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){
+					ao.adviceType = args[0];
+					ao.srcObj = dj_global;
+					var tmpName  = dl.nameAnonFunc(args[1], dj_global, searchForNames);
+					ao.srcFunc = tmpName;
+					ao.adviceObj = args[2];
+					ao.adviceFunc = args[3];
+				}else if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){
+					ao.srcObj = args[1];
+					ao.srcFunc = args[2];
+					var tmpName  = dl.nameAnonFunc(args[3], dj_global, searchForNames);
+					ao.adviceObj = dj_global;
+					ao.adviceFunc = tmpName;
+				}else if(dl.isObject(args[1])){
+					ao.srcObj = args[1];
+					ao.srcFunc = args[2];
+					ao.adviceObj = dj_global;
+					ao.adviceFunc = args[3];
+				}else if(dl.isObject(args[2])){
+					ao.srcObj = dj_global;
+					ao.srcFunc = args[1];
+					ao.adviceObj = args[2];
+					ao.adviceFunc = args[3];
+				}else{
+					ao.srcObj = ao.adviceObj = ao.aroundObj = dj_global;
+					ao.srcFunc = args[1];
+					ao.adviceFunc = args[2];
+					ao.aroundFunc = args[3];
+				}
+				break;
+			case 6:
+				ao.srcObj = args[1];
+				ao.srcFunc = args[2];
+				ao.adviceObj = args[3]
+				ao.adviceFunc = args[4];
+				ao.aroundFunc = args[5];
+				ao.aroundObj = dj_global;
+				break;
+			default:
+				ao.srcObj = args[1];
+				ao.srcFunc = args[2];
+				ao.adviceObj = args[3]
+				ao.adviceFunc = args[4];
+				ao.aroundObj = args[5];
+				ao.aroundFunc = args[6];
+				ao.once = args[7];
+				ao.delay = args[8];
+				ao.rate = args[9];
+				ao.adviceMsg = args[10];
+				break;
+		}
+
+		if(dl.isFunction(ao.aroundFunc)){
+			var tmpName  = dl.nameAnonFunc(ao.aroundFunc, ao.aroundObj, searchForNames);
+			ao.aroundFunc = tmpName;
+		}
+
+		if(dl.isFunction(ao.srcFunc)){
+			ao.srcFunc = dl.getNameInObj(ao.srcObj, ao.srcFunc);
+		}
+
+		if(dl.isFunction(ao.adviceFunc)){
+			ao.adviceFunc = dl.getNameInObj(ao.adviceObj, ao.adviceFunc);
+		}
+
+		if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){
+			ao.aroundFunc = dl.getNameInObj(ao.aroundObj, ao.aroundFunc);
+		}
+
+		if(!ao.srcObj){
+			dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc);
+		}
+		if(!ao.adviceObj){
+			dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc);
+		}
+		
+		if(!ao.adviceFunc){
+			dojo.debug("bad adviceFunc for srcFunc: "+ao.srcFunc);
+			dojo.debugShallow(ao);
+		} 
+		
+		return ao;
+	}
+
+	this.connect = function(/*...*/){
+		// summary:
+		//		dojo.event.connect is the glue that holds most Dojo-based
+		//		applications together. Most combinations of arguments are
+		//		supported, with the connect() method attempting to disambiguate
+		//		the implied types of positional parameters. The following will
+		//		all work:
+		//			dojo.event.connect("globalFunctionName1", "globalFunctionName2");
+		//			dojo.event.connect(functionReference1, functionReference2);
+		//			dojo.event.connect("globalFunctionName1", functionReference2);
+		//			dojo.event.connect(functionReference1, "globalFunctionName2");
+		//			dojo.event.connect(scope1, "functionName1", "globalFunctionName2");
+		//			dojo.event.connect("globalFunctionName1", scope2, "functionName2");
+		//			dojo.event.connect(scope1, "functionName1", scope2, "functionName2");
+		//			dojo.event.connect("after", scope1, "functionName1", scope2, "functionName2");
+		//			dojo.event.connect("before", scope1, "functionName1", scope2, "functionName2");
+		//			dojo.event.connect("around", 	scope1, "functionName1", 
+		//											scope2, "functionName2",
+		//											aroundFunctionReference);
+		//			dojo.event.connect("around", 	scope1, "functionName1", 
+		//											scope2, "functionName2",
+		//											scope3, "aroundFunctionName");
+		//			dojo.event.connect("before-around", 	scope1, "functionName1", 
+		//													scope2, "functionName2",
+		//													aroundFunctionReference);
+		//			dojo.event.connect("after-around", 		scope1, "functionName1", 
+		//													scope2, "functionName2",
+		//													aroundFunctionReference);
+		//			dojo.event.connect("after-around", 		scope1, "functionName1", 
+		//													scope2, "functionName2",
+		//													scope3, "aroundFunctionName");
+		//			dojo.event.connect("around", 	scope1, "functionName1", 
+		//											scope2, "functionName2",
+		//											scope3, "aroundFunctionName", true, 30);
+		//			dojo.event.connect("around", 	scope1, "functionName1", 
+		//											scope2, "functionName2",
+		//											scope3, "aroundFunctionName", null, null, 10);
+		// adviceType: 
+		//		Optional. String. One of "before", "after", "around",
+		//		"before-around", or "after-around". FIXME
+		// srcObj:
+		//		the scope in which to locate/execute the named srcFunc. Along
+		//		with srcFunc, this creates a way to dereference the function to
+		//		call. So if the function in question is "foo.bar", the
+		//		srcObj/srcFunc pair would be foo and "bar", where "bar" is a
+		//		string and foo is an object reference.
+		// srcFunc:
+		//		the name of the function to connect to. When it is executed,
+		//		the listener being registered with this call will be called.
+		//		The adviceType defines the call order between the source and
+		//		the target functions.
+		// adviceObj:
+		//		the scope in which to locate/execute the named adviceFunc.
+		// adviceFunc:
+		//		the name of the function being conected to srcObj.srcFunc
+		// aroundObj:
+		//		the scope in which to locate/execute the named aroundFunc.
+		// aroundFunc:
+		//		the name of, or a reference to, the function that will be used
+		//		to mediate the advice call. Around advice requires a special
+		//		unary function that will be passed a "MethodInvocation" object.
+		//		These objects have several important properties, namely:
+		//			- args
+		//				a mutable array of arguments to be passed into the
+		//				wrapped function
+		//			- proceed
+		//				a function that "continues" the invocation. The result
+		//				of this function is the return of the wrapped function.
+		//				You can then manipulate this return before passing it
+		//				back out (or take further action based on it).
+		// once:
+		//		boolean that determines whether or not this connect() will
+		//		create a new connection if an identical connect() has already
+		//		been made. Defaults to "false".
+		// delay:
+		//		an optional delay (in ms), as an integer, for dispatch of a
+		//		listener after the source has been fired.
+		// rate:
+		//		an optional rate throttling parameter (integer, in ms). When
+		//		specified, this particular connection will not fire more than
+		//		once in the interval specified by the rate
+		// adviceMsg:
+		//		boolean. Should the listener have all the parameters passed in
+		//		as a single argument?
+
+		/*
+				ao.adviceType = args[0];
+				ao.srcObj = args[1];
+				ao.srcFunc = args[2];
+				ao.adviceObj = args[3]
+				ao.adviceFunc = args[4];
+				ao.aroundObj = args[5];
+				ao.aroundFunc = args[6];
+				ao.once = args[7];
+				ao.delay = args[8];
+				ao.rate = args[9];
+				ao.adviceMsg = args[10];
+		*/
+		if(arguments.length == 1){
+			var ao = arguments[0];
+		}else{
+			var ao = interpolateArgs(arguments, true);
+		}
+		if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
+			if(dojo.render.html.ie){
+				ao.srcFunc = "onkeydown";
+				this.connect(ao);
+			}
+			ao.srcFunc = "onkeypress";
+		}
+
+
+		if(dojo.lang.isArray(ao.srcObj) && ao.srcObj!=""){
+			var tmpAO = {};
+			for(var x in ao){
+				tmpAO[x] = ao[x];
+			}
+			var mjps = [];
+			dojo.lang.forEach(ao.srcObj, function(src){
+				if((dojo.render.html.capable)&&(dojo.lang.isString(src))){
+					src = dojo.byId(src);
+					// dojo.debug(src);
+				}
+				tmpAO.srcObj = src;
+				// dojo.debug(tmpAO.srcObj, tmpAO.srcFunc);
+				// dojo.debug(tmpAO.adviceObj, tmpAO.adviceFunc);
+				mjps.push(dojo.event.connect.call(dojo.event, tmpAO));
+			});
+			return mjps;
+		}
+
+		// FIXME: just doing a "getForMethod()" seems to be enough to put this into infinite recursion!!
+		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
+		if(ao.adviceFunc){
+			var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc);
+		}
+
+		mjp.kwAddAdvice(ao);
+
+		// advanced users might want to fsck w/ the join point manually
+		return mjp; // a MethodJoinPoint object
+	}
+
+	this.log = function(/*object or funcName*/ a1, /*funcName*/ a2){
+		// summary:
+		//		a function that will wrap and log all calls to the specified
+		//		a1.a2() function. If only a1 is passed, it'll be used as a
+		//		function or function name on the global context. Logging will
+		//		be sent to dojo.debug
+		// a1:
+		//		if a2 is passed, this should be an object. If not, it can be a
+		//		function or function name.
+		// a2:
+		//		a function name
+		var kwArgs;
+		if((arguments.length == 1)&&(typeof a1 == "object")){
+			kwArgs = a1;
+		}else{
+			kwArgs = {
+				srcObj: a1,
+				srcFunc: a2
+			};
+		}
+		kwArgs.adviceFunc = function(){
+			var argsStr = [];
+			for(var x=0; x<arguments.length; x++){
+				argsStr.push(arguments[x]);
+			}
+			dojo.debug("("+kwArgs.srcObj+")."+kwArgs.srcFunc, ":", argsStr.join(", "));
+		}
+		this.kwConnect(kwArgs);
+	}
+
+	this.connectBefore = function(){
+		// summary:
+		//	 	takes the same parameters as dojo.event.connect(), except that
+		//	 	the advice type will always be "before"
+		var args = ["before"];
+		for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
+		return this.connect.apply(this, args); // a MethodJoinPoint object
+	}
+
+	this.connectAround = function(){
+		// summary:
+		//	 	takes the same parameters as dojo.event.connect(), except that
+		//	 	the advice type will always be "around"
+		var args = ["around"];
+		for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
+		return this.connect.apply(this, args); // a MethodJoinPoint object
+	}
+
+	this.connectOnce = function(){
+		// summary:
+		//	 	takes the same parameters as dojo.event.connect(), except that
+		//	 	the "once" flag will always be set to "true"
+		var ao = interpolateArgs(arguments, true);
+		ao.once = true;
+		return this.connect(ao); // a MethodJoinPoint object
+	}
+
+	this._kwConnectImpl = function(kwArgs, disconnect){
+		var fn = (disconnect) ? "disconnect" : "connect";
+		if(typeof kwArgs["srcFunc"] == "function"){
+			kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
+			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.srcFunc, kwArgs.srcObj, true);
+			kwArgs.srcFunc = tmpName;
+		}
+		if(typeof kwArgs["adviceFunc"] == "function"){
+			kwArgs.adviceObj = kwArgs["adviceObj"]||dj_global;
+			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.adviceFunc, kwArgs.adviceObj, true);
+			kwArgs.adviceFunc = tmpName;
+		}
+		kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
+		kwArgs.adviceObj = kwArgs["adviceObj"]||kwArgs["targetObj"]||dj_global;
+		kwArgs.adviceFunc = kwArgs["adviceFunc"]||kwArgs["targetFunc"];
+		// pass kwargs to avoid unrolling/repacking
+		return dojo.event[fn](kwArgs);
+	}
+
+	this.kwConnect = function(/*Object*/ kwArgs){
+		// summary:
+		//		A version of dojo.event.connect() that takes a map of named
+		//		parameters instead of the positional parameters that
+		//		dojo.event.connect() uses. For many advanced connection types,
+		//		this can be a much more readable (and potentially faster)
+		//		alternative.
+		// kwArgs:
+		// 		An object that can have the following properties:
+		//			- adviceType
+		//			- srcObj
+		//			- srcFunc
+		//			- adviceObj
+		//			- adviceFunc 
+		//			- aroundObj
+		//			- aroundFunc
+		//			- once
+		//			- delay
+		//			- rate
+		//			- adviceMsg
+		//		As with connect, only srcFunc and adviceFunc are generally
+		//		required
+
+		return this._kwConnectImpl(kwArgs, false); // a MethodJoinPoint object
+
+	}
+
+	this.disconnect = function(){
+		// summary:
+		//		Takes the same parameters as dojo.event.connect() but destroys
+		//		an existing connection instead of building a new one. For
+		//		multiple identical connections, multiple disconnect() calls
+		//		will unroll one each time it's called.
+		if(arguments.length == 1){
+			var ao = arguments[0];
+		}else{
+			var ao = interpolateArgs(arguments, true);
+		}
+		if(!ao.adviceFunc){ return; } // nothing to disconnect
+		if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
+			if(dojo.render.html.ie){
+				ao.srcFunc = "onkeydown";
+				this.disconnect(ao);
+			}
+			ao.srcFunc = "onkeypress";
+		}
+		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
+		return mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once); // a MethodJoinPoint object
+	}
+
+	this.kwDisconnect = function(kwArgs){
+		// summary:
+		//		Takes the same parameters as dojo.event.kwConnect() but
+		//		destroys an existing connection instead of building a new one.
+		return this._kwConnectImpl(kwArgs, true);
+	}
+}
+
+// exactly one of these is created whenever a method with a joint point is run,
+// if there is at least one 'around' advice.
+dojo.event.MethodInvocation = function(/*dojo.event.MethodJoinPoint*/join_point, /*Object*/obj, /*Array*/args){
+	// summary:
+	//		a class the models the call into a function. This is used under the
+	//		covers for all method invocations on both ends of a
+	//		connect()-wrapped function dispatch. This allows us to "pickle"
+	//		calls, such as in the case of around advice.
+	// join_point:
+	//		a dojo.event.MethodJoinPoint object that represents a connection
+	// obj:
+	//		the scope the call will execute in
+	// args:
+	//		an array of parameters that will get passed to the callee
+	this.jp_ = join_point;
+	this.object = obj;
+	this.args = [];
+	// make sure we don't lock into a mutable object which can change under us.
+	// It's ok if the individual items change, though.
+	for(var x=0; x<args.length; x++){
+		this.args[x] = args[x];
+	}
+	// the index of the 'around' that is currently being executed.
+	this.around_index = -1;
+}
+
+dojo.event.MethodInvocation.prototype.proceed = function(){
+	// summary:
+	//		proceed with the method call that's represented by this invocation
+	//		object
+	this.around_index++;
+	if(this.around_index >= this.jp_.around.length){
+		return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args);
+		// return this.jp_.run_before_after(this.object, this.args);
+	}else{
+		var ti = this.jp_.around[this.around_index];
+		var mobj = ti[0]||dj_global;
+		var meth = ti[1];
+		return mobj[meth].call(mobj, this);
+	}
+} 
+
+
+dojo.event.MethodJoinPoint = function(/*Object*/obj, /*String*/funcName){
+	this.object = obj||dj_global;
+	this.methodname = funcName;
+	this.methodfunc = this.object[funcName];
+	this.squelch = false;
+	// this.before = [];
+	// this.after = [];
+	// this.around = [];
+}
+
+dojo.event.MethodJoinPoint.getForMethod = function(/*Object*/obj, /*String*/funcName){
+	// summary:
+	//		"static" class function for returning a MethodJoinPoint from a
+	//		scoped function. If one doesn't exist, one is created.
+	// obj:
+	//		the scope to search for the function in
+	// funcName:
+	//		the name of the function to return a MethodJoinPoint for
+	if(!obj){ obj = dj_global; }
+	if(!obj[funcName]){
+		// supply a do-nothing method implementation
+		obj[funcName] = function(){};
+		if(!obj[funcName]){
+			// e.g. cannot add to inbuilt objects in IE6
+			dojo.raise("Cannot set do-nothing method on that object "+funcName);
+		}
+	}else if((!dojo.lang.isFunction(obj[funcName]))&&(!dojo.lang.isAlien(obj[funcName]))){
+		// FIXME: should we throw an exception here instead?
+		return null; 
+	}
+	// we hide our joinpoint instance in obj[funcName + '$joinpoint']
+	var jpname = funcName + "$joinpoint";
+	var jpfuncname = funcName + "$joinpoint$method";
+	var joinpoint = obj[jpname];
+	if(!joinpoint){
+		var isNode = false;
+		if(dojo.event["browser"]){
+			if( (obj["attachEvent"])||
+				(obj["nodeType"])||
+				(obj["addEventListener"]) ){
+				isNode = true;
+				dojo.event.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, funcName]);
+			}
+		}
+		var origArity = obj[funcName].length;
+		obj[jpfuncname] = obj[funcName];
+		// joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, funcName);
+		joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, jpfuncname);
+		obj[funcName] = function(){ 
+			var args = [];
+
+			if((isNode)&&(!arguments.length)){
+				var evt = null;
+				try{
+					if(obj.ownerDocument){
+						evt = obj.ownerDocument.parentWindow.event;
+					}else if(obj.documentElement){
+						evt = obj.documentElement.ownerDocument.parentWindow.event;
+					}else if(obj.event){ //obj is a window
+						evt = obj.event;
+					}else{
+						evt = window.event;
+					}
+				}catch(e){
+					evt = window.event;
+				}
+
+				if(evt){
+					args.push(dojo.event.browser.fixEvent(evt, this));
+				}
+			}else{
+				for(var x=0; x<arguments.length; x++){
+					if((x==0)&&(isNode)&&(dojo.event.browser.isEvent(arguments[x]))){
+						args.push(dojo.event.browser.fixEvent(arguments[x], this));
+					}else{
+						args.push(arguments[x]);
+					}
+				}
+			}
+			// return joinpoint.run.apply(joinpoint, arguments); 
+			return joinpoint.run.apply(joinpoint, args); 
+		}
+		obj[funcName].__preJoinArity = origArity;
+	}
+	return joinpoint; // dojo.event.MethodJoinPoint
+}
+
+dojo.lang.extend(dojo.event.MethodJoinPoint, {
+	unintercept: function(){
+		// summary: 
+		//		destroy the connection to all listeners that may have been
+		//		registered on this joinpoint
+		this.object[this.methodname] = this.methodfunc;
+		this.before = [];
+		this.after = [];
+		this.around = [];
+	},
+
+	disconnect: dojo.lang.forward("unintercept"),
+
+	run: function(){
+		// summary:
+		//		execute the connection represented by this join point. The
+		//		arguments passed to run() will be passed to the function and
+		//		its listeners.
+		var obj = this.object||dj_global;
+		var args = arguments;
+
+		// optimization. We only compute once the array version of the arguments
+		// pseudo-arr in order to prevent building it each time advice is unrolled.
+		var aargs = [];
+		for(var x=0; x<args.length; x++){
+			aargs[x] = args[x];
+		}
+
+		var unrollAdvice  = function(marr){ 
+			if(!marr){
+				dojo.debug("Null argument to unrollAdvice()");
+				return;
+			}
+		  
+			var callObj = marr[0]||dj_global;
+			var callFunc = marr[1];
+			
+			if(!callObj[callFunc]){
+				dojo.raise("function \"" + callFunc + "\" does not exist on \"" + callObj + "\"");
+			}
+			
+			var aroundObj = marr[2]||dj_global;
+			var aroundFunc = marr[3];
+			var msg = marr[6];
+			var undef;
+
+			var to = {
+				args: [],
+				jp_: this,
+				object: obj,
+				proceed: function(){
+					return callObj[callFunc].apply(callObj, to.args);
+				}
+			};
+			to.args = aargs;
+
+			var delay = parseInt(marr[4]);
+			var hasDelay = ((!isNaN(delay))&&(marr[4]!==null)&&(typeof marr[4] != "undefined"));
+			if(marr[5]){
+				var rate = parseInt(marr[5]);
+				var cur = new Date();
+				var timerSet = false;
+				if((marr["last"])&&((cur-marr.last)<=rate)){
+					if(dojo.event._canTimeout){
+						if(marr["delayTimer"]){
+							clearTimeout(marr.delayTimer);
+						}
+						var tod = parseInt(rate*2); // is rate*2 naive?
+						var mcpy = dojo.lang.shallowCopy(marr);
+						marr.delayTimer = setTimeout(function(){
+							// FIXME: on IE at least, event objects from the
+							// browser can go out of scope. How (or should?) we
+							// deal with it?
+							mcpy[5] = 0;
+							unrollAdvice(mcpy);
+						}, tod);
+					}
+					return;
+				}else{
+					marr.last = cur;
+				}
+			}
+
+			// FIXME: need to enforce rates for a connection here!
+
+			if(aroundFunc){
+				// NOTE: around advice can't delay since we might otherwise depend
+				// on execution order!
+				aroundObj[aroundFunc].call(aroundObj, to);
+			}else{
+				// var tmjp = dojo.event.MethodJoinPoint.getForMethod(obj, methname);
+				if((hasDelay)&&((dojo.render.html)||(dojo.render.svg))){  // FIXME: the render checks are grotty!
+					dj_global["setTimeout"](function(){
+						if(msg){
+							callObj[callFunc].call(callObj, to); 
+						}else{
+							callObj[callFunc].apply(callObj, args); 
+						}
+					}, delay);
+				}else{ // many environments can't support delay!
+					if(msg){
+						callObj[callFunc].call(callObj, to); 
+					}else{
+						callObj[callFunc].apply(callObj, args); 
+					}
+				}
+			}
+		}
+
+		var unRollSquelch = function(){
+			if(this.squelch){
+				try{
+					return unrollAdvice.apply(this, arguments);
+				}catch(e){ 
+					dojo.debug(e);
+				}
+			}else{
+				return unrollAdvice.apply(this, arguments);
+			}
+		}
+
+		if((this["before"])&&(this.before.length>0)){
+			// pass a cloned array, if this event disconnects this event forEach on this.before wont work
+			dojo.lang.forEach(this.before.concat(new Array()), unRollSquelch);
+		}
+
+		var result;
+		try{
+			if((this["around"])&&(this.around.length>0)){
+				var mi = new dojo.event.MethodInvocation(this, obj, args);
+				result = mi.proceed();
+			}else if(this.methodfunc){
+				result = this.object[this.methodname].apply(this.object, args);
+			}
+		}catch(e){ if(!this.squelch){ dojo.raise(e); } }
+
+		if((this["after"])&&(this.after.length>0)){
+			// see comment on this.before above
+			dojo.lang.forEach(this.after.concat(new Array()), unRollSquelch);
+		}
+
+		return (this.methodfunc) ? result : null;
+	},
+
+	getArr: function(/*String*/kind){
+		// summary: return a list of listeners of the past "kind"
+		// kind:
+		//		can be one of: "before", "after", "around", "before-around", or
+		//		"after-around"
+		var type = "after";
+		// FIXME: we should be able to do this through props or Array.in()
+		if((typeof kind == "string")&&(kind.indexOf("before")!=-1)){
+			type = "before";
+		}else if(kind=="around"){
+			type = "around";
+		}
+		if(!this[type]){ this[type] = []; }
+		return this[type]; // Array
+	},
+
+	kwAddAdvice: function(/*Object*/args){
+		// summary:
+		//		adds advice to the joinpoint with arguments in a map
+		// args:
+		// 		An object that can have the following properties:
+		//			- adviceType
+		//			- adviceObj
+		//			- adviceFunc 
+		//			- aroundObj
+		//			- aroundFunc
+		//			- once
+		//			- delay
+		//			- rate
+		//			- adviceMsg
+		this.addAdvice(	args["adviceObj"], args["adviceFunc"], 
+						args["aroundObj"], args["aroundFunc"], 
+						args["adviceType"], args["precedence"], 
+						args["once"], args["delay"], args["rate"], 
+						args["adviceMsg"]);
+	},
+
+	addAdvice: function(	thisAdviceObj, thisAdvice, 
+							thisAroundObj, thisAround, 
+							adviceType, precedence, 
+							once, delay, rate, asMessage){
+		// summary:
+		//		add advice to this joinpoint using positional parameters
+		// thisAdviceObj:
+		//		the scope in which to locate/execute the named adviceFunc.
+		// thisAdviceFunc:
+		//		the name of the function being conected
+		// thisAroundObj:
+		//		the scope in which to locate/execute the named aroundFunc.
+		// thisAroundFunc:
+		//		the name of the function that will be used to mediate the
+		//		advice call.
+		// adviceType: 
+		//		Optional. String. One of "before", "after", "around",
+		//		"before-around", or "after-around". FIXME
+		// once:
+		//		boolean that determines whether or not this advice will create
+		//		a new connection if an identical advice set has already been
+		//		provided. Defaults to "false".
+		// delay:
+		//		an optional delay (in ms), as an integer, for dispatch of a
+		//		listener after the source has been fired.
+		// rate:
+		//		an optional rate throttling parameter (integer, in ms). When
+		//		specified, this particular connection will not fire more than
+		//		once in the interval specified by the rate
+		// adviceMsg:
+		//		boolean. Should the listener have all the parameters passed in
+		//		as a single argument?
+		var arr = this.getArr(adviceType);
+		if(!arr){
+			dojo.raise("bad this: " + this);
+		}
+
+		var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage];
+		
+		if(once){
+			if(this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr) >= 0){
+				return;
+			}
+		}
+
+		if(precedence == "first"){
+			arr.unshift(ao);
+		}else{
+			arr.push(ao);
+		}
+	},
+
+	hasAdvice: function(thisAdviceObj, thisAdvice, adviceType, arr){
+		// summary:
+		//		returns the array index of the first existing connection
+		//		betweened the passed advice and this joinpoint. Will be -1 if
+		//		none exists.
+		// thisAdviceObj:
+		//		the scope in which to locate/execute the named adviceFunc.
+		// thisAdviceFunc:
+		//		the name of the function being conected
+		// adviceType: 
+		//		Optional. String. One of "before", "after", "around",
+		//		"before-around", or "after-around". FIXME
+		// arr:
+		//		Optional. The list of advices to search. Will be found via
+		//		adviceType if not passed
+		if(!arr){ arr = this.getArr(adviceType); }
+		var ind = -1;
+		for(var x=0; x<arr.length; x++){
+			var aao = (typeof thisAdvice == "object") ? (new String(thisAdvice)).toString() : thisAdvice;
+			var a1o = (typeof arr[x][1] == "object") ? (new String(arr[x][1])).toString() : arr[x][1];
+			if((arr[x][0] == thisAdviceObj)&&(a1o == aao)){
+				ind = x;
+			}
+		}
+		return ind; // Integer
+	},
+
+	removeAdvice: function(thisAdviceObj, thisAdvice, adviceType, once){
+		// summary:
+		//		returns the array index of the first existing connection
+		//		betweened the passed advice and this joinpoint. Will be -1 if
+		//		none exists.
+		// thisAdviceObj:
+		//		the scope in which to locate/execute the named adviceFunc.
+		// thisAdviceFunc:
+		//		the name of the function being conected
+		// adviceType: 
+		//		Optional. String. One of "before", "after", "around",
+		//		"before-around", or "after-around". FIXME
+		// once:
+		//		Optional. Should this only remove the first occurance of the
+		//		connection?
+		var arr = this.getArr(adviceType);
+		var ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
+		if(ind == -1){
+			return false;
+		}
+		while(ind != -1){
+			arr.splice(ind, 1);
+			if(once){ break; }
+			ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
+		}
+		return true;
+	}
+});
+
+dojo.provide("dojo.event.topic");
+
+dojo.event.topic = new function(){
+	this.topics = {};
+
+	this.getTopic = function(/*String*/topic){
+		// summary:
+		//		returns a topic implementation object of type
+		//		dojo.event.topic.TopicImpl
+		// topic:
+		//		a unique, opaque string that names the topic
+		if(!this.topics[topic]){
+			this.topics[topic] = new this.TopicImpl(topic);
+		}
+		return this.topics[topic]; // a dojo.event.topic.TopicImpl object
+	}
+
+	this.registerPublisher = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
+		// summary:
+		//		registers a function as a publisher on a topic. Subsequent
+		//		calls to the function will cause a publish event on the topic
+		//		with the arguments passed to the function passed to registered
+		//		listeners.
+		// topic: 
+		//		a unique, opaque string that names the topic
+		// obj:
+		//		the scope to locate the function in
+		// funcName:
+		//		the name of the function to register
+		var topic = this.getTopic(topic);
+		topic.registerPublisher(obj, funcName);
+	}
+
+	this.subscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
+		// summary:
+		//		susbscribes the function to the topic. Subsequent events
+		//		dispached to the topic will create a function call for the
+		//		obj.funcName() function.
+		// topic: 
+		//		a unique, opaque string that names the topic
+		// obj:
+		//		the scope to locate the function in
+		// funcName:
+		//		the name of the function to being registered as a listener
+		var topic = this.getTopic(topic);
+		topic.subscribe(obj, funcName);
+	}
+
+	this.unsubscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
+		// summary:
+		//		unsubscribes the obj.funcName() from the topic
+		// topic: 
+		//		a unique, opaque string that names the topic
+		// obj:
+		//		the scope to locate the function in
+		// funcName:
+		//		the name of the function to being unregistered as a listener
+		var topic = this.getTopic(topic);
+		topic.unsubscribe(obj, funcName);
+	}
+
+	this.destroy = function(/*String*/topic){
+		// summary: 
+		//		destroys the topic and unregisters all listeners
+		// topic:
+		//		a unique, opaque string that names the topic
+		this.getTopic(topic).destroy();
+		delete this.topics[topic];
+	}
+
+	this.publishApply = function(/*String*/topic, /*Array*/args){
+		// summary: 
+		//		dispatches an event to the topic using the args array as the
+		//		source for the call arguments to each listener. This is similar
+		//		to JavaScript's built-in Function.apply()
+		// topic:
+		//		a unique, opaque string that names the topic
+		// args:
+		//		the arguments to be passed into listeners of the topic
+		var topic = this.getTopic(topic);
+		topic.sendMessage.apply(topic, args);
+	}
+
+	this.publish = function(/*String*/topic, /*Object*/message){
+		// summary: 
+		//		manually "publish" to the passed topic
+		// topic:
+		//		a unique, opaque string that names the topic
+		// message:
+		//		can be an array of parameters (similar to publishApply), or
+		//		will be treated as one of many arguments to be passed along in
+		//		a "flat" unrolling
+		var topic = this.getTopic(topic);
+		// if message is an array, we treat it as a set of arguments,
+		// otherwise, we just pass on the arguments passed in as-is
+		var args = [];
+		// could we use concat instead here?
+		for(var x=1; x<arguments.length; x++){
+			args.push(arguments[x]);
+		}
+		topic.sendMessage.apply(topic, args);
+	}
+}
+
+dojo.event.topic.TopicImpl = function(topicName){
+	// summary: a class to represent topics
+
+	this.topicName = topicName;
+
+	this.subscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
+		// summary:
+		//		use dojo.event.connect() to attach the passed listener to the
+		//		topic represented by this object
+		// listenerObject:
+		//		if a string and listenerMethod is ommitted, this is treated as
+		//		the name of a function in the global namespace. If
+		//		listenerMethod is provided, this is the scope to find/execute
+		//		the function in.
+		// listenerMethod:
+		//		Optional. The function to register.
+		var tf = listenerMethod||listenerObject;
+		var to = (!listenerMethod) ? dj_global : listenerObject;
+		return dojo.event.kwConnect({ // dojo.event.MethodJoinPoint
+			srcObj:		this, 
+			srcFunc:	"sendMessage", 
+			adviceObj:	to,
+			adviceFunc: tf
+		});
+	}
+
+	this.unsubscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
+		// summary:
+		//		use dojo.event.disconnect() to attach the passed listener to the
+		//		topic represented by this object
+		// listenerObject:
+		//		if a string and listenerMethod is ommitted, this is treated as
+		//		the name of a function in the global namespace. If
+		//		listenerMethod is provided, this is the scope to find the
+		//		function in.
+		// listenerMethod:
+		//		Optional. The function to unregister.
+		var tf = (!listenerMethod) ? listenerObject : listenerMethod;
+		var to = (!listenerMethod) ? null : listenerObject;
+		return dojo.event.kwDisconnect({ // dojo.event.MethodJoinPoint
+			srcObj:		this, 
+			srcFunc:	"sendMessage", 
+			adviceObj:	to,
+			adviceFunc: tf
+		});
+	}
+
+	this._getJoinPoint = function(){
+		return dojo.event.MethodJoinPoint.getForMethod(this, "sendMessage");
+	}
+
+	this.setSquelch = function(/*Boolean*/shouldSquelch){
+		// summary: 
+		//		determine whether or not exceptions in the calling of a
+		//		listener in the chain should stop execution of the chain.
+		this._getJoinPoint().squelch = shouldSquelch;
+	}
+
+	this.destroy = function(){
+		// summary: disconnects all listeners from this topic
+		this._getJoinPoint().disconnect();
+	}
+
+	this.registerPublisher = function(	/*Object*/publisherObject, 
+										/*Function or String*/publisherMethod){
+		// summary:
+		//		registers the passed function as a publisher on this topic.
+		//		Each time the function is called, an event will be published on
+		//		this topic.
+		// publisherObject:
+		//		if a string and listenerMethod is ommitted, this is treated as
+		//		the name of a function in the global namespace. If
+		//		listenerMethod is provided, this is the scope to find the
+		//		function in.
+		// publisherMethod:
+		//		Optional. The function to register.
+		dojo.event.connect(publisherObject, publisherMethod, this, "sendMessage");
+	}
+
+	this.sendMessage = function(message){
+		// summary: a stub to be called when a message is sent to the topic.
+
+		// The message has been propagated
+	}
+}
+
+
+dojo.provide("dojo.event.browser");
+
+// FIXME: any particular reason this is in the global scope?
+dojo._ie_clobber = new function(){
+	this.clobberNodes = [];
+
+	function nukeProp(node, prop){
+		// try{ node.removeAttribute(prop); 	}catch(e){ /* squelch */ }
+		try{ node[prop] = null; 			}catch(e){ /* squelch */ }
+		try{ delete node[prop]; 			}catch(e){ /* squelch */ }
+		// FIXME: JotLive needs this, but I'm not sure if it's too slow or not
+		try{ node.removeAttribute(prop);	}catch(e){ /* squelch */ }
+	}
+
+	this.clobber = function(nodeRef){
+		var na;
+		var tna;
+		if(nodeRef){
+			tna = nodeRef.all || nodeRef.getElementsByTagName("*");
+			na = [nodeRef];
+			for(var x=0; x<tna.length; x++){
+				// if we're gonna be clobbering the thing, at least make sure
+				// we aren't trying to do it twice
+				if(tna[x]["__doClobber__"]){
+					na.push(tna[x]);
+				}
+			}
+		}else{
+			try{ window.onload = null; }catch(e){}
+			na = (this.clobberNodes.length) ? this.clobberNodes : document.all;
+		}
+		tna = null;
+		var basis = {};
+		for(var i = na.length-1; i>=0; i=i-1){
+			var el = na[i];
+			try{
+				if(el && el["__clobberAttrs__"]){
+					for(var j=0; j<el.__clobberAttrs__.length; j++){
+						nukeProp(el, el.__clobberAttrs__[j]);
+					}
+					nukeProp(el, "__clobberAttrs__");
+					nukeProp(el, "__doClobber__");
+				}
+			}catch(e){ /* squelch! */};
+		}
+		na = null;
+	}
+}
+
+if(dojo.render.html.ie){
+	dojo.addOnUnload(function(){
+		dojo._ie_clobber.clobber();
+		try{
+			if((dojo["widget"])&&(dojo.widget["manager"])){
+				dojo.widget.manager.destroyAll();
+			}
+		}catch(e){}
+		try{ window.onload = null; }catch(e){}
+		try{ window.onunload = null; }catch(e){}
+		dojo._ie_clobber.clobberNodes = [];
+		// CollectGarbage();
+	});
+}
+
+dojo.event.browser = new function(){
+
+	var clobberIdx = 0;
+
+	this.normalizedEventName = function(/*String*/eventName){
+		switch(eventName){
+			case "CheckboxStateChange":
+			case "DOMAttrModified":
+			case "DOMMenuItemActive":
+			case "DOMMenuItemInactive":
+			case "DOMMouseScroll":
+			case "DOMNodeInserted":
+			case "DOMNodeRemoved":
+			case "RadioStateChange":
+				return eventName;
+				break;
+			default:
+				return eventName.toLowerCase();
+				break;
+		}
+	}
+	
+	this.clean = function(/*DOMNode*/node){
+		// summary:
+		//		removes native event handlers so that destruction of the node
+		//		will not leak memory. On most browsers this is a no-op, but
+		//		it's critical for manual node removal on IE.
+		// node:
+		//		A DOM node. All of it's children will also be cleaned.
+		if(dojo.render.html.ie){ 
+			dojo._ie_clobber.clobber(node);
+		}
+	}
+
+	this.addClobberNode = function(/*DOMNode*/node){
+		// summary:
+		//		register the passed node to support event stripping
+		// node:
+		//		A DOM node
+		if(!dojo.render.html.ie){ return; }
+		if(!node["__doClobber__"]){
+			node.__doClobber__ = true;
+			dojo._ie_clobber.clobberNodes.push(node);
+			// this might not be the most efficient thing to do, but it's
+			// much less error prone than other approaches which were
+			// previously tried and failed
+			node.__clobberAttrs__ = [];
+		}
+	}
+
+	this.addClobberNodeAttrs = function(/*DOMNode*/node, /*Array*/props){
+		// summary:
+		//		register the passed node to support event stripping
+		// node:
+		//		A DOM node to stip properties from later
+		// props:
+		//		A list of propeties to strip from the node
+		if(!dojo.render.html.ie){ return; }
+		this.addClobberNode(node);
+		for(var x=0; x<props.length; x++){
+			node.__clobberAttrs__.push(props[x]);
+		}
+	}
+
+	this.removeListener = function(	/*DOMNode*/ node, 
+									/*String*/	evtName, 
+									/*Function*/fp, 
+									/*Boolean*/	capture){
+		// summary:
+		//		clobbers the listener from the node
+		// evtName:
+		//		the name of the handler to remove the function from
+		// node:
+		//		DOM node to attach the event to
+		// fp:
+		//		the function to register
+		// capture:
+		//		Optional. should this listener prevent propigation?
+		if(!capture){ var capture = false; }
+		evtName = dojo.event.browser.normalizedEventName(evtName);
+		if( (evtName == "onkey") || (evtName == "key") ){
+			if(dojo.render.html.ie){
+				this.removeListener(node, "onkeydown", fp, capture);
+			}
+			evtName = "onkeypress";
+		}
+		if(evtName.substr(0,2)=="on"){ evtName = evtName.substr(2); }
+		// FIXME: this is mostly a punt, we aren't actually doing anything on IE
+		if(node.removeEventListener){
+			node.removeEventListener(evtName, fp, capture);
+		}
+	}
+
+	this.addListener = function(/*DOMNode*/node, /*String*/evtName, /*Function*/fp, /*Boolean*/capture, /*Boolean*/dontFix){
+		// summary:
+		//		adds a listener to the node
+		// evtName:
+		//		the name of the handler to add the listener to can be either of
+		//		the form "onclick" or "click"
+		// node:
+		//		DOM node to attach the event to
+		// fp:
+		//		the function to register
+		// capture:
+		//		Optional. Should this listener prevent propigation?
+		// dontFix:
+		//		Optional. Should we avoid registering a new closure around the
+		//		listener to enable fixEvent for dispatch of the registered
+		//		function?
+		if(!node){ return; } // FIXME: log and/or bail?
+		if(!capture){ var capture = false; }
+		evtName = dojo.event.browser.normalizedEventName(evtName);
+		if( (evtName == "onkey") || (evtName == "key") ){
+			if(dojo.render.html.ie){
+				this.addListener(node, "onkeydown", fp, capture, dontFix);
+			}
+			evtName = "onkeypress";
+		}
+		if(evtName.substr(0,2)!="on"){ evtName = "on"+evtName; }
+
+		if(!dontFix){
+			// build yet another closure around fp in order to inject fixEvent
+			// around the resulting event
+			var newfp = function(evt){
+				if(!evt){ evt = window.event; }
+				var ret = fp(dojo.event.browser.fixEvent(evt, this));
+				if(capture){
+					dojo.event.browser.stopEvent(evt);
+				}
+				return ret;
+			}
+		}else{
+			newfp = fp;
+		}
+
+		if(node.addEventListener){ 
+			node.addEventListener(evtName.substr(2), newfp, capture);
+			return newfp;
+		}else{
+			if(typeof node[evtName] == "function" ){
+				var oldEvt = node[evtName];
+				node[evtName] = function(e){
+					oldEvt(e);
+					return newfp(e);
+				}
+			}else{
+				node[evtName]=newfp;
+			}
+			if(dojo.render.html.ie){
+				this.addClobberNodeAttrs(node, [evtName]);
+			}
+			return newfp;
+		}
+	}
+
+	this.isEvent = function(/*Object*/obj){
+		// summary: 
+		//		Tries to determine whether or not the object is a DOM event.
+
+		// FIXME: event detection hack ... could test for additional attributes
+		// if necessary
+		return (typeof obj != "undefined")&&(typeof Event != "undefined")&&(obj.eventPhase); // Boolean
+		// Event does not support instanceof in Opera, otherwise:
+		//return (typeof Event != "undefined")&&(obj instanceof Event);
+	}
+
+	this.currentEvent = null;
+	
+	this.callListener = function(/*Function*/listener, /*DOMNode*/curTarget){
+		// summary:
+		//		calls the specified listener in the context of the passed node
+		//		with the current DOM event object as the only parameter
+		// listener:
+		//		the function to call
+		// curTarget:
+		//		the Node to call the function in the scope of
+		if(typeof listener != 'function'){
+			dojo.raise("listener not a function: " + listener);
+		}
+		dojo.event.browser.currentEvent.currentTarget = curTarget;
+		return listener.call(curTarget, dojo.event.browser.currentEvent);
+	}
+
+	this._stopPropagation = function(){
+		dojo.event.browser.currentEvent.cancelBubble = true; 
+	}
+
+	this._preventDefault = function(){
+		dojo.event.browser.currentEvent.returnValue = false;
+	}
+
+	this.keys = {
+		KEY_BACKSPACE: 8,
+		KEY_TAB: 9,
+		KEY_CLEAR: 12,
+		KEY_ENTER: 13,
+		KEY_SHIFT: 16,
+		KEY_CTRL: 17,
+		KEY_ALT: 18,
+		KEY_PAUSE: 19,
+		KEY_CAPS_LOCK: 20,
+		KEY_ESCAPE: 27,
+		KEY_SPACE: 32,
+		KEY_PAGE_UP: 33,
+		KEY_PAGE_DOWN: 34,
+		KEY_END: 35,
+		KEY_HOME: 36,
+		KEY_LEFT_ARROW: 37,
+		KEY_UP_ARROW: 38,
+		KEY_RIGHT_ARROW: 39,
+		KEY_DOWN_ARROW: 40,
+		KEY_INSERT: 45,
+		KEY_DELETE: 46,
+		KEY_HELP: 47,
+		KEY_LEFT_WINDOW: 91,
+		KEY_RIGHT_WINDOW: 92,
+		KEY_SELECT: 93,
+		KEY_NUMPAD_0: 96,
+		KEY_NUMPAD_1: 97,
+		KEY_NUMPAD_2: 98,
+		KEY_NUMPAD_3: 99,
+		KEY_NUMPAD_4: 100,
+		KEY_NUMPAD_5: 101,
+		KEY_NUMPAD_6: 102,
+		KEY_NUMPAD_7: 103,
+		KEY_NUMPAD_8: 104,
+		KEY_NUMPAD_9: 105,
+		KEY_NUMPAD_MULTIPLY: 106,
+		KEY_NUMPAD_PLUS: 107,
+		KEY_NUMPAD_ENTER: 108,
+		KEY_NUMPAD_MINUS: 109,
+		KEY_NUMPAD_PERIOD: 110,
+		KEY_NUMPAD_DIVIDE: 111,
+		KEY_F1: 112,
+		KEY_F2: 113,
+		KEY_F3: 114,
+		KEY_F4: 115,
+		KEY_F5: 116,
+		KEY_F6: 117,
+		KEY_F7: 118,
+		KEY_F8: 119,
+		KEY_F9: 120,
+		KEY_F10: 121,
+		KEY_F11: 122,
+		KEY_F12: 123,
+		KEY_F13: 124,
+		KEY_F14: 125,
+		KEY_F15: 126,
+		KEY_NUM_LOCK: 144,
+		KEY_SCROLL_LOCK: 145
+	};
+
+	// reverse lookup
+	this.revKeys = [];
+	for(var key in this.keys){
+		this.revKeys[this.keys[key]] = key;
+	}
+
+	this.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
+		// summary:
+		//		normalizes properties on the event object including event
+		//		bubbling methods, keystroke normalization, and x/y positions
+		// evt: the native event object
+		// sender: the node to treat as "currentTarget"
+		if(!evt){
+			if(window["event"]){
+				evt = window.event;
+			}
+		}
+		
+		if((evt["type"])&&(evt["type"].indexOf("key") == 0)){ // key events
+			evt.keys = this.revKeys;
+			// FIXME: how can we eliminate this iteration?
+			for(var key in this.keys){
+				evt[key] = this.keys[key];
+			}
+			if(evt["type"] == "keydown" && dojo.render.html.ie){
+				switch(evt.keyCode){
+					case evt.KEY_SHIFT:
+					case evt.KEY_CTRL:
+					case evt.KEY_ALT:
+					case evt.KEY_CAPS_LOCK:
+					case evt.KEY_LEFT_WINDOW:
+					case evt.KEY_RIGHT_WINDOW:
+					case evt.KEY_SELECT:
+					case evt.KEY_NUM_LOCK:
+					case evt.KEY_SCROLL_LOCK:
+					// I'll get these in keypress after the OS munges them based on numlock
+					case evt.KEY_NUMPAD_0:
+					case evt.KEY_NUMPAD_1:
+					case evt.KEY_NUMPAD_2:
+					case evt.KEY_NUMPAD_3:
+					case evt.KEY_NUMPAD_4:
+					case evt.KEY_NUMPAD_5:
+					case evt.KEY_NUMPAD_6:
+					case evt.KEY_NUMPAD_7:
+					case evt.KEY_NUMPAD_8:
+					case evt.KEY_NUMPAD_9:
+					case evt.KEY_NUMPAD_PERIOD:
+						break; // just ignore the keys that can morph
+					case evt.KEY_NUMPAD_MULTIPLY:
+					case evt.KEY_NUMPAD_PLUS:
+					case evt.KEY_NUMPAD_ENTER:
+					case evt.KEY_NUMPAD_MINUS:
+					case evt.KEY_NUMPAD_DIVIDE:
+						break; // I could handle these but just pick them up in keypress
+					case evt.KEY_PAUSE:
+					case evt.KEY_TAB:
+					case evt.KEY_BACKSPACE:
+					case evt.KEY_ENTER:
+					case evt.KEY_ESCAPE:
+					case evt.KEY_PAGE_UP:
+					case evt.KEY_PAGE_DOWN:
+					case evt.KEY_END:
+					case evt.KEY_HOME:
+					case evt.KEY_LEFT_ARROW:
+					case evt.KEY_UP_ARROW:
+					case evt.KEY_RIGHT_ARROW:
+					case evt.KEY_DOWN_ARROW:
+					case evt.KEY_INSERT:
+					case evt.KEY_DELETE:
+					case evt.KEY_F1:
+					case evt.KEY_F2:
+					case evt.KEY_F3:
+					case evt.KEY_F4:
+					case evt.KEY_F5:
+					case evt.KEY_F6:
+					case evt.KEY_F7:
+					case evt.KEY_F8:
+					case evt.KEY_F9:
+					case evt.KEY_F10:
+					case evt.KEY_F11:
+					case evt.KEY_F12:
+					case evt.KEY_F12:
+					case evt.KEY_F13:
+					case evt.KEY_F14:
+					case evt.KEY_F15:
+					case evt.KEY_CLEAR:
+					case evt.KEY_HELP:
+						evt.key = evt.keyCode;
+						break;
+					default:
+						if(evt.ctrlKey || evt.altKey){
+							var unifiedCharCode = evt.keyCode;
+							// if lower case but keycode is uppercase, convert it
+							if(unifiedCharCode >= 65 && unifiedCharCode <= 90 && evt.shiftKey == false){
+								unifiedCharCode += 32;
+							}
+							if(unifiedCharCode >= 1 && unifiedCharCode <= 26 && evt.ctrlKey){
+								unifiedCharCode += 96; // 001-032 = ctrl+[a-z]
+							}
+							evt.key = String.fromCharCode(unifiedCharCode);
+						}
+				}
+			} else if(evt["type"] == "keypress"){
+				if(dojo.render.html.opera){
+					if(evt.which == 0){
+						evt.key = evt.keyCode;
+					}else if(evt.which > 0){
+						switch(evt.which){
+							case evt.KEY_SHIFT:
+							case evt.KEY_CTRL:
+							case evt.KEY_ALT:
+							case evt.KEY_CAPS_LOCK:
+							case evt.KEY_NUM_LOCK:
+							case evt.KEY_SCROLL_LOCK:
+								break;
+							case evt.KEY_PAUSE:
+							case evt.KEY_TAB:
+							case evt.KEY_BACKSPACE:
+							case evt.KEY_ENTER:
+							case evt.KEY_ESCAPE:
+								evt.key = evt.which;
+								break;
+							default:
+								var unifiedCharCode = evt.which;
+								if((evt.ctrlKey || evt.altKey || evt.metaKey) && (evt.which >= 65 && evt.which <= 90 && evt.shiftKey == false)){
+									unifiedCharCode += 32;
+								}
+								evt.key = String.fromCharCode(unifiedCharCode);
+						}
+					}
+				}else if(dojo.render.html.ie){ // catch some IE keys that are hard to get in keyDown
+					// key combinations were handled in onKeyDown
+					if(!evt.ctrlKey && !evt.altKey && evt.keyCode >= evt.KEY_SPACE){
+						evt.key = String.fromCharCode(evt.keyCode);
+					}
+				}else if(dojo.render.html.safari){
+					switch(evt.keyCode){
+						case 63232: evt.key = evt.KEY_UP_ARROW; break;
+						case 63233: evt.key = evt.KEY_DOWN_ARROW; break;
+						case 63234: evt.key = evt.KEY_LEFT_ARROW; break;
+						case 63235: evt.key = evt.KEY_RIGHT_ARROW; break;
+						default: 
+							evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode;
+					}
+				}else{
+					evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode;
+				}
+			}
+		}
+		if(dojo.render.html.ie){
+			if(!evt.target){ evt.target = evt.srcElement; }
+			if(!evt.currentTarget){ evt.currentTarget = (sender ? sender : evt.srcElement); }
+			if(!evt.layerX){ evt.layerX = evt.offsetX; }
+			if(!evt.layerY){ evt.layerY = evt.offsetY; }
+			// FIXME: scroll position query is duped from dojo.html to avoid dependency on that entire module
+			// DONOT replace the following to use dojo.body(), in IE, document.documentElement should be used
+			// here rather than document.body
+			var doc = (evt.srcElement && evt.srcElement.ownerDocument) ? evt.srcElement.ownerDocument : document;
+			var docBody = ((dojo.render.html.ie55)||(doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement;
+			if(!evt.pageX){ evt.pageX = evt.clientX + (docBody.scrollLeft || 0) }
+			if(!evt.pageY){ evt.pageY = evt.clientY + (docBody.scrollTop || 0) }
+			// mouseover
+			if(evt.type == "mouseover"){ evt.relatedTarget = evt.fromElement; }
+			// mouseout
+			if(evt.type == "mouseout"){ evt.relatedTarget = evt.toElement; }
+			this.currentEvent = evt;
+			evt.callListener = this.callListener;
+			evt.stopPropagation = this._stopPropagation;
+			evt.preventDefault = this._preventDefault;
+		}
+		return evt; // Event
+	}
+
+	this.stopEvent = function(/*Event*/evt){
+		// summary:
+		//		prevents propigation and clobbers the default action of the
+		//		passed event
+		// evt: Optional for IE. The native event object.
+		if(window.event){
+			evt.returnValue = false;
+			evt.cancelBubble = true;
+		}else{
+			evt.preventDefault();
+			evt.stopPropagation();
+		}
+	}
+}
+
+dojo.provide("dojo.event.*");
+
+dojo.provide("dojo.gfx.color");
+
+// TODO: rewrite the "x2y" methods to take advantage of the parsing
+//       abilities of the Color object. Also, beef up the Color
+//       object (as possible) to parse most common formats
+
+// takes an r, g, b, a(lpha) value, [r, g, b, a] array, "rgb(...)" string, hex string (#aaa, #aaaaaa, aaaaaaa)
+dojo.gfx.color.Color = function(r, g, b, a) {
+	// dojo.debug("r:", r[0], "g:", r[1], "b:", r[2]);
+	if(dojo.lang.isArray(r)){
+		this.r = r[0];
+		this.g = r[1];
+		this.b = r[2];
+		this.a = r[3]||1.0;
+	}else if(dojo.lang.isString(r)){
+		var rgb = dojo.gfx.color.extractRGB(r);
+		this.r = rgb[0];
+		this.g = rgb[1];
+		this.b = rgb[2];
+		this.a = g||1.0;
+	}else if(r instanceof dojo.gfx.color.Color){
+		// why does this create a new instance if we were passed one?
+		this.r = r.r;
+		this.b = r.b;
+		this.g = r.g;
+		this.a = r.a;
+	}else{
+		this.r = r;
+		this.g = g;
+		this.b = b;
+		this.a = a;
+	}
+}
+
+dojo.gfx.color.Color.fromArray = function(arr) {
+	return new dojo.gfx.color.Color(arr[0], arr[1], arr[2], arr[3]);
+}
+
+dojo.extend(dojo.gfx.color.Color, {
+	toRgb: function(includeAlpha) {
+		if(includeAlpha) {
+			return this.toRgba();
+		} else {
+			return [this.r, this.g, this.b];
+		}
+	},
+	toRgba: function() {
+		return [this.r, this.g, this.b, this.a];
+	},
+	toHex: function() {
+		return dojo.gfx.color.rgb2hex(this.toRgb());
+	},
+	toCss: function() {
+		return "rgb(" + this.toRgb().join() + ")";
+	},
+	toString: function() {
+		return this.toHex(); // decent default?
+	},
+	blend: function(color, weight){
+		var rgb = null;
+		if(dojo.lang.isArray(color)){
+			rgb = color;
+		}else if(color instanceof dojo.gfx.color.Color){
+			rgb = color.toRgb();
+		}else{
+			rgb = new dojo.gfx.color.Color(color).toRgb();
+		}
+		return dojo.gfx.color.blend(this.toRgb(), rgb, weight);
+	}
+});
+
+dojo.gfx.color.named = {
+	white:      [255,255,255],
+	black:      [0,0,0],
+	red:        [255,0,0],
+	green:	    [0,255,0],
+	lime:	    [0,255,0],
+	blue:       [0,0,255],
+	navy:       [0,0,128],
+	gray:       [128,128,128],
+	silver:     [192,192,192]
+};
+
+dojo.gfx.color.blend = function(a, b, weight){
+	// summary: 
+	//		blend colors a and b (both as RGB array or hex strings) with weight
+	//		from -1 to +1, 0 being a 50/50 blend
+	if(typeof a == "string"){
+		return dojo.gfx.color.blendHex(a, b, weight);
+	}
+	if(!weight){
+		weight = 0;
+	}
+	weight = Math.min(Math.max(-1, weight), 1);
+
+	// alex: this interface blows.
+	// map -1 to 1 to the range 0 to 1
+	weight = ((weight + 1)/2);
+	
+	var c = [];
+
+	// var stop = (1000*weight);
+	for(var x = 0; x < 3; x++){
+		c[x] = parseInt( b[x] + ( (a[x] - b[x]) * weight) );
+	}
+	return c;
+}
+
+// very convenient blend that takes and returns hex values
+// (will get called automatically by blend when blend gets strings)
+dojo.gfx.color.blendHex = function(a, b, weight) {
+	return dojo.gfx.color.rgb2hex(dojo.gfx.color.blend(dojo.gfx.color.hex2rgb(a), dojo.gfx.color.hex2rgb(b), weight));
+}
+
+// get RGB array from css-style color declarations
+dojo.gfx.color.extractRGB = function(color) {
+	var hex = "0123456789abcdef";
+	color = color.toLowerCase();
+	if( color.indexOf("rgb") == 0 ) {
+		var matches = color.match(/rgba*\((\d+), *(\d+), *(\d+)/i);
+		var ret = matches.splice(1, 3);
+		return ret;
+	} else {
+		var colors = dojo.gfx.color.hex2rgb(color);
+		if(colors) {
+			return colors;
+		} else {
+			// named color (how many do we support?)
+			return dojo.gfx.color.named[color] || [255, 255, 255];
+		}
+	}
+}
+
+dojo.gfx.color.hex2rgb = function(hex) {
+	var hexNum = "0123456789ABCDEF";
+	var rgb = new Array(3);
+	if( hex.indexOf("#") == 0 ) { hex = hex.substring(1); }
+	hex = hex.toUpperCase();
+	if(hex.replace(new RegExp("["+hexNum+"]", "g"), "") != "") {
+		return null;
+	}
+	if( hex.length == 3 ) {
+		rgb[0] = hex.charAt(0) + hex.charAt(0)
+		rgb[1] = hex.charAt(1) + hex.charAt(1)
+		rgb[2] = hex.charAt(2) + hex.charAt(2);
+	} else {
+		rgb[0] = hex.substring(0, 2);
+		rgb[1] = hex.substring(2, 4);
+		rgb[2] = hex.substring(4);
+	}
+	for(var i = 0; i < rgb.length; i++) {
+		rgb[i] = hexNum.indexOf(rgb[i].charAt(0)) * 16 + hexNum.indexOf(rgb[i].charAt(1));
+	}
+	return rgb;
+}
+
+dojo.gfx.color.rgb2hex = function(r, g, b) {
+	if(dojo.lang.isArray(r)) {
+		g = r[1] || 0;
+		b = r[2] || 0;
+		r = r[0] || 0;
+	}
+	var ret = dojo.lang.map([r, g, b], function(x) {
+		x = new Number(x);
+		var s = x.toString(16);
+		while(s.length < 2) { s = "0" + s; }
+		return s;
+	});
+	ret.unshift("#");
+	return ret.join("");
+}
+
+dojo.provide("dojo.lfx.Animation");
+
+
+/*
+	Animation package based on Dan Pupius' work: http://pupius.co.uk/js/Toolkit.Drawing.js
+*/
+dojo.lfx.Line = function(/*int*/ start, /*int*/ end){
+	// summary: dojo.lfx.Line is the object used to generate values
+	//			from a start value to an end value
+	this.start = start;
+	this.end = end;
+	if(dojo.lang.isArray(start)){
+		/* start: Array
+		   end: Array
+		   pId: a */
+		var diff = [];
+		dojo.lang.forEach(this.start, function(s,i){
+			diff[i] = this.end[i] - s;
+		}, this);
+		
+		this.getValue = function(/*float*/ n){
+			var res = [];
+			dojo.lang.forEach(this.start, function(s, i){
+				res[i] = (diff[i] * n) + s;
+			}, this);
+			return res; // Array
+		}
+	}else{
+		var diff = end - start;
+			
+		this.getValue = function(/*float*/ n){
+			//	summary: returns the point on the line
+			//	n: a floating point number greater than 0 and less than 1
+			return (diff * n) + this.start; // Decimal
+		}
+	}
+}
+
+dojo.lfx.easeDefault = function(/*Decimal?*/ n){
+	//	summary: Returns the point for point n on a sin wave.
+	if(dojo.render.html.khtml){
+		// the cool kids are obviously not using konqueror...
+		// found a very wierd bug in floats constants, 1.5 evals as 1
+		// seems somebody mixed up ints and floats in 3.5.4 ??
+		// FIXME: investigate more and post a KDE bug (Fredrik)
+		return (parseFloat("0.5")+((Math.sin( (n+parseFloat("1.5")) * Math.PI))/2));
+	}else{
+		return (0.5+((Math.sin( (n+1.5) * Math.PI))/2));
+	}
+}
+
+dojo.lfx.easeIn = function(/*Decimal?*/ n){
+	//	summary: returns the point on an easing curve
+	//	n: a floating point number greater than 0 and less than 1
+	return Math.pow(n, 3);
+}
+
+dojo.lfx.easeOut = function(/*Decimal?*/ n){
+	//	summary: returns the point on the line
+	//	n: a floating point number greater than 0 and less than 1
+	return ( 1 - Math.pow(1 - n, 3) );
+}
+
+dojo.lfx.easeInOut = function(/*Decimal?*/ n){
+	//	summary: returns the point on the line
+	//	n: a floating point number greater than 0 and less than 1
+	return ( (3 * Math.pow(n, 2)) - (2 * Math.pow(n, 3)) );
+}
+
+dojo.lfx.IAnimation = function(){
+	// summary: dojo.lfx.IAnimation is an interface that implements
+	//			commonly used functions of animation objects
+}
+dojo.lang.extend(dojo.lfx.IAnimation, {
+	// public properties
+	curve: null,
+	duration: 1000,
+	easing: null,
+	repeatCount: 0,
+	rate: 25,
+	
+	// events
+	handler: null,
+	beforeBegin: null,
+	onBegin: null,
+	onAnimate: null,
+	onEnd: null,
+	onPlay: null,
+	onPause: null,
+	onStop: null,
+	
+	// public methods
+	play: null,
+	pause: null,
+	stop: null,
+	
+	connect: function(/*Event*/ evt, /*Object*/ scope, /*Function*/ newFunc){
+		// summary: Convenience function.  Quickly connect to an event
+		//			of this object and save the old functions connected to it.
+		// evt: The name of the event to connect to.
+		// scope: the scope in which to run newFunc.
+		// newFunc: the function to run when evt is fired.
+		if(!newFunc){
+			/* scope: Function
+			   newFunc: null
+			   pId: f */
+			newFunc = scope;
+			scope = this;
+		}
+		newFunc = dojo.lang.hitch(scope, newFunc);
+		var oldFunc = this[evt]||function(){};
+		this[evt] = function(){
+			var ret = oldFunc.apply(this, arguments);
+			newFunc.apply(this, arguments);
+			return ret;
+		}
+		return this; // dojo.lfx.IAnimation
+	},
+
+	fire: function(/*Event*/ evt, /*Array*/ args){
+		// summary: Convenience function.  Fire event "evt" and pass it
+		//			the arguments specified in "args".
+		// evt: The event to fire.
+		// args: The arguments to pass to the event.
+		if(this[evt]){
+			this[evt].apply(this, (args||[]));
+		}
+		return this; // dojo.lfx.IAnimation
+	},
+	
+	repeat: function(/*int*/ count){
+		// summary: Set the repeat count of this object.
+		// count: How many times to repeat the animation.
+		this.repeatCount = count;
+		return this; // dojo.lfx.IAnimation
+	},
+
+	// private properties
+	_active: false,
+	_paused: false
+});
+
+dojo.lfx.Animation = function(	/*Object*/ handlers, 
+								/*int*/ duration, 
+								/*dojo.lfx.Line*/ curve, 
+								/*function*/ easing, 
+								/*int*/ repeatCount, 
+								/*int*/ rate){
+	//	summary
+	//		a generic animation object that fires callbacks into it's handlers
+	//		object at various states
+	//	handlers: { handler: Function?, onstart: Function?, onstop: Function?, onanimate: Function? }
+	dojo.lfx.IAnimation.call(this);
+	if(dojo.lang.isNumber(handlers)||(!handlers && duration.getValue)){
+		// no handlers argument:
+		rate = repeatCount;
+		repeatCount = easing;
+		easing = curve;
+		curve = duration;
+		duration = handlers;
+		handlers = null;
+	}else if(handlers.getValue||dojo.lang.isArray(handlers)){
+		// no handlers or duration:
+		rate = easing;
+		repeatCount = curve;
+		easing = duration;
+		curve = handlers;
+		duration = null;
+		handlers = null;
+	}
+	if(dojo.lang.isArray(curve)){
+		/* curve: Array
+		   pId: a */
+		this.curve = new dojo.lfx.Line(curve[0], curve[1]);
+	}else{
+		this.curve = curve;
+	}
+	if(duration != null && duration > 0){ this.duration = duration; }
+	if(repeatCount){ this.repeatCount = repeatCount; }
+	if(rate){ this.rate = rate; }
+	if(handlers){
+		dojo.lang.forEach([
+				"handler", "beforeBegin", "onBegin", 
+				"onEnd", "onPlay", "onStop", "onAnimate"
+			], function(item){
+				if(handlers[item]){
+					this.connect(item, handlers[item]);
+				}
+			}, this);
+	}
+	if(easing && dojo.lang.isFunction(easing)){
+		this.easing=easing;
+	}
+}
+dojo.inherits(dojo.lfx.Animation, dojo.lfx.IAnimation);
+dojo.lang.extend(dojo.lfx.Animation, {
+	// "private" properties
+	_startTime: null,
+	_endTime: null,
+	_timer: null,
+	_percent: 0,
+	_startRepeatCount: 0,
+
+	// public methods
+	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
+		// summary: Start the animation.
+		// delay: How many milliseconds to delay before starting.
+		// gotoStart: If true, starts the animation from the beginning; otherwise,
+		//            starts it from its current position.
+		if(gotoStart){
+			clearTimeout(this._timer);
+			this._active = false;
+			this._paused = false;
+			this._percent = 0;
+		}else if(this._active && !this._paused){
+			return this; // dojo.lfx.Animation
+		}
+		
+		this.fire("handler", ["beforeBegin"]);
+		this.fire("beforeBegin");
+
+		if(delay > 0){
+			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
+			return this; // dojo.lfx.Animation
+		}
+		
+		this._startTime = new Date().valueOf();
+		if(this._paused){
+			this._startTime -= (this.duration * this._percent / 100);
+		}
+		this._endTime = this._startTime + this.duration;
+
+		this._active = true;
+		this._paused = false;
+		
+		var step = this._percent / 100;
+		var value = this.curve.getValue(step);
+		if(this._percent == 0 ){
+			if(!this._startRepeatCount){
+				this._startRepeatCount = this.repeatCount;
+			}
+			this.fire("handler", ["begin", value]);
+			this.fire("onBegin", [value]);
+		}
+
+		this.fire("handler", ["play", value]);
+		this.fire("onPlay", [value]);
+
+		this._cycle();
+		return this; // dojo.lfx.Animation
+	},
+
+	pause: function(){
+		// summary: Pauses a running animation.
+		clearTimeout(this._timer);
+		if(!this._active){ return this; /*dojo.lfx.Animation*/}
+		this._paused = true;
+		var value = this.curve.getValue(this._percent / 100);
+		this.fire("handler", ["pause", value]);
+		this.fire("onPause", [value]);
+		return this; // dojo.lfx.Animation
+	},
+
+	gotoPercent: function(/*Decimal*/ pct, /*bool?*/ andPlay){
+		// summary: Sets the progress of the animation.
+		// pct: A percentage in decimal notation (between and including 0.0 and 1.0).
+		// andPlay: If true, play the animation after setting the progress.
+		clearTimeout(this._timer);
+		this._active = true;
+		this._paused = true;
+		this._percent = pct;
+		if(andPlay){ this.play(); }
+		return this; // dojo.lfx.Animation
+	},
+
+	stop: function(/*bool?*/ gotoEnd){
+		// summary: Stops a running animation.
+		// gotoEnd: If true, the animation will end.
+		clearTimeout(this._timer);
+		var step = this._percent / 100;
+		if(gotoEnd){
+			step = 1;
+		}
+		var value = this.curve.getValue(step);
+		this.fire("handler", ["stop", value]);
+		this.fire("onStop", [value]);
+		this._active = false;
+		this._paused = false;
+		return this; // dojo.lfx.Animation
+	},
+
+	status: function(){
+		// summary: Returns a string representation of the status of
+		//			the animation.
+		if(this._active){
+			return this._paused ? "paused" : "playing"; // String
+		}else{
+			return "stopped"; // String
+		}
+		return this;
+	},
+
+	// "private" methods
+	_cycle: function(){
+		clearTimeout(this._timer);
+		if(this._active){
+			var curr = new Date().valueOf();
+			var step = (curr - this._startTime) / (this._endTime - this._startTime);
+
+			if(step >= 1){
+				step = 1;
+				this._percent = 100;
+			}else{
+				this._percent = step * 100;
+			}
+			
+			// Perform easing
+			if((this.easing)&&(dojo.lang.isFunction(this.easing))){
+				step = this.easing(step);
+			}
+
+			var value = this.curve.getValue(step);
+			this.fire("handler", ["animate", value]);
+			this.fire("onAnimate", [value]);
+
+			if( step < 1 ){
+				this._timer = setTimeout(dojo.lang.hitch(this, "_cycle"), this.rate);
+			}else{
+				this._active = false;
+				this.fire("handler", ["end"]);
+				this.fire("onEnd");
+
+				if(this.repeatCount > 0){
+					this.repeatCount--;
+					this.play(null, true);
+				}else if(this.repeatCount == -1){
+					this.play(null, true);
+				}else{
+					if(this._startRepeatCount){
+						this.repeatCount = this._startRepeatCount;
+						this._startRepeatCount = 0;
+					}
+				}
+			}
+		}
+		return this; // dojo.lfx.Animation
+	}
+});
+
+dojo.lfx.Combine = function(/*dojo.lfx.IAnimation...*/ animations){
+	// summary: An animation object to play animations passed to it at the same time.
+	dojo.lfx.IAnimation.call(this);
+	this._anims = [];
+	this._animsEnded = 0;
+	
+	var anims = arguments;
+	if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
+		/* animations: dojo.lfx.IAnimation[]
+		   pId: a */
+		anims = anims[0];
+	}
+	
+	dojo.lang.forEach(anims, function(anim){
+		this._anims.push(anim);
+		anim.connect("onEnd", dojo.lang.hitch(this, "_onAnimsEnded"));
+	}, this);
+}
+dojo.inherits(dojo.lfx.Combine, dojo.lfx.IAnimation);
+dojo.lang.extend(dojo.lfx.Combine, {
+	// private members
+	_animsEnded: 0,
+	
+	// public methods
+	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
+		// summary: Start the animations.
+		// delay: How many milliseconds to delay before starting.
+		// gotoStart: If true, starts the animations from the beginning; otherwise,
+		//            starts them from their current position.
+		if( !this._anims.length ){ return this; /*dojo.lfx.Combine*/}
+
+		this.fire("beforeBegin");
+
+		if(delay > 0){
+			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
+			return this; // dojo.lfx.Combine
+		}
+		
+		if(gotoStart || this._anims[0].percent == 0){
+			this.fire("onBegin");
+		}
+		this.fire("onPlay");
+		this._animsCall("play", null, gotoStart);
+		return this; // dojo.lfx.Combine
+	},
+	
+	pause: function(){
+		// summary: Pauses the running animations.
+		this.fire("onPause");
+		this._animsCall("pause"); 
+		return this; // dojo.lfx.Combine
+	},
+	
+	stop: function(/*bool?*/ gotoEnd){
+		// summary: Stops the running animations.
+		// gotoEnd: If true, the animations will end.
+		this.fire("onStop");
+		this._animsCall("stop", gotoEnd);
+		return this; // dojo.lfx.Combine
+	},
+	
+	// private methods
+	_onAnimsEnded: function(){
+		this._animsEnded++;
+		if(this._animsEnded >= this._anims.length){
+			this.fire("onEnd");
+		}
+		return this; // dojo.lfx.Combine
+	},
+	
+	_animsCall: function(/*String*/ funcName){
+		var args = [];
+		if(arguments.length > 1){
+			for(var i = 1 ; i < arguments.length ; i++){
+				args.push(arguments[i]);
+			}
+		}
+		var _this = this;
+		dojo.lang.forEach(this._anims, function(anim){
+			anim[funcName](args);
+		}, _this);
+		return this; // dojo.lfx.Combine
+	}
+});
+
+dojo.lfx.Chain = function(/*dojo.lfx.IAnimation...*/ animations) {
+	// summary: An animation object to play animations passed to it
+	//			one after another.
+	dojo.lfx.IAnimation.call(this);
+	this._anims = [];
+	this._currAnim = -1;
+	
+	var anims = arguments;
+	if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
+		/* animations: dojo.lfx.IAnimation[]
+		   pId: a */
+		anims = anims[0];
+	}
+	
+	var _this = this;
+	dojo.lang.forEach(anims, function(anim, i, anims_arr){
+		this._anims.push(anim);
+		if(i < anims_arr.length - 1){
+			anim.connect("onEnd", dojo.lang.hitch(this, "_playNext") );
+		}else{
+			anim.connect("onEnd", dojo.lang.hitch(this, function(){ this.fire("onEnd"); }) );
+		}
+	}, this);
+}
+dojo.inherits(dojo.lfx.Chain, dojo.lfx.IAnimation);
+dojo.lang.extend(dojo.lfx.Chain, {
+	// private members
+	_currAnim: -1,
+	
+	// public methods
+	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
+		// summary: Start the animation sequence.
+		// delay: How many milliseconds to delay before starting.
+		// gotoStart: If true, starts the sequence from the beginning; otherwise,
+		//            starts it from its current position.
+		if( !this._anims.length ) { return this; /*dojo.lfx.Chain*/}
+		if( gotoStart || !this._anims[this._currAnim] ) {
+			this._currAnim = 0;
+		}
+
+		var currentAnimation = this._anims[this._currAnim];
+
+		this.fire("beforeBegin");
+		if(delay > 0){
+			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
+			return this; // dojo.lfx.Chain
+		}
+		
+		if(currentAnimation){
+			if(this._currAnim == 0){
+				this.fire("handler", ["begin", this._currAnim]);
+				this.fire("onBegin", [this._currAnim]);
+			}
+			this.fire("onPlay", [this._currAnim]);
+			currentAnimation.play(null, gotoStart);
+		}
+		return this; // dojo.lfx.Chain
+	},
+	
+	pause: function(){
+		// summary: Pauses the running animation sequence.
+		if( this._anims[this._currAnim] ) {
+			this._anims[this._currAnim].pause();
+			this.fire("onPause", [this._currAnim]);
+		}
+		return this; // dojo.lfx.Chain
+	},
+	
+	playPause: function(){
+		// summary: If the animation sequence is playing, pause it; otherwise,
+		//			play it.
+		if(this._anims.length == 0){ return this; }
+		if(this._currAnim == -1){ this._currAnim = 0; }
+		var currAnim = this._anims[this._currAnim];
+		if( currAnim ) {
+			if( !currAnim._active || currAnim._paused ) {
+				this.play();
+			} else {
+				this.pause();
+			}
+		}
+		return this; // dojo.lfx.Chain
+	},
+	
+	stop: function(){
+		// summary: Stops the running animations.
+		var currAnim = this._anims[this._currAnim];
+		if(currAnim){
+			currAnim.stop();
+			this.fire("onStop", [this._currAnim]);
+		}
+		return currAnim; // dojo.lfx.IAnimation
+	},
+	
+	// private methods
+	_playNext: function(){
+		if( this._currAnim == -1 || this._anims.length == 0 ) { return this; }
+		this._currAnim++;
+		if( this._anims[this._currAnim] ){
+			this._anims[this._currAnim].play(null, true);
+		}
+		return this; // dojo.lfx.Chain
+	}
+});
+
+dojo.lfx.combine = function(/*dojo.lfx.IAnimation...*/ animations){
+	// summary: Convenience function.  Returns a dojo.lfx.Combine created
+	//			using the animations passed in.
+	var anims = arguments;
+	if(dojo.lang.isArray(arguments[0])){
+		/* animations: dojo.lfx.IAnimation[]
+		   pId: a */
+		anims = arguments[0];
+	}
+	if(anims.length == 1){ return anims[0]; }
+	return new dojo.lfx.Combine(anims); // dojo.lfx.Combine
+}
+
+dojo.lfx.chain = function(/*dojo.lfx.IAnimation...*/ animations){
+	// summary: Convenience function.  Returns a dojo.lfx.Chain created
+	//			using the animations passed in.
+	var anims = arguments;
+	if(dojo.lang.isArray(arguments[0])){
+		/* animations: dojo.lfx.IAnimation[]
+		   pId: a */
+		anims = arguments[0];
+	}
+	if(anims.length == 1){ return anims[0]; }
+	return new dojo.lfx.Chain(anims); // dojo.lfx.Combine
+}
+
+dojo.provide("dojo.uri.Uri");
+
+dojo.uri = new function() {
+	this.dojoUri = function (/*dojo.uri.Uri||String*/uri) {
+		// summary: returns a Uri object resolved relative to the dojo root
+		return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(), uri);
+	}
+	
+	this.moduleUri = function(/*String*/module, /*dojo.uri.Uri||String*/uri){
+		// summary: returns a Uri object relative to a (top-level) module
+		// description: Examples: dojo.uri.moduleUri("dojo","Editor"), or dojo.uri.moduleUri("acme","someWidget")
+		var loc = dojo.hostenv.getModulePrefix(module);
+		if(!loc){return null;}
+		if(loc.lastIndexOf("/") != loc.length-1){loc += "/";}
+		return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri()+loc,uri);
+	}
+
+	this.Uri = function (/*dojo.uri.Uri||String...*/) {
+		// summary: Constructor to create an object representing a URI.
+		// description: 
+		//  Each argument is evaluated in order relative to the next until
+		//  a canonical uri is produced. To get an absolute Uri relative
+		//  to the current document use
+		//      new dojo.uri.Uri(document.baseURI, uri)
+
+		// TODO: support for IPv6, see RFC 2732
+
+		// resolve uri components relative to each other
+		var uri = arguments[0];
+		for (var i = 1; i < arguments.length; i++) {
+			if(!arguments[i]) { continue; }
+
+			// Safari doesn't support this.constructor so we have to be explicit
+			var relobj = new dojo.uri.Uri(arguments[i].toString());
+			var uriobj = new dojo.uri.Uri(uri.toString());
+
+			if ((relobj.path=="")&&(relobj.scheme==null)&&(relobj.authority==null)&&(relobj.query==null)) {
+				if (relobj.fragment != null) { uriobj.fragment = relobj.fragment; }
+				relobj = uriobj;
+			} else if (relobj.scheme == null) {
+				relobj.scheme = uriobj.scheme;
+			
+				if (relobj.authority == null) {
+					relobj.authority = uriobj.authority;
+					
+					if (relobj.path.charAt(0) != "/") {
+						var path = uriobj.path.substring(0,
+							uriobj.path.lastIndexOf("/") + 1) + relobj.path;
+
+						var segs = path.split("/");
+						for (var j = 0; j < segs.length; j++) {
+							if (segs[j] == ".") {
+								if (j == segs.length - 1) { segs[j] = ""; }
+								else { segs.splice(j, 1); j--; }
+							} else if (j > 0 && !(j == 1 && segs[0] == "") &&
+								segs[j] == ".." && segs[j-1] != "..") {
+
+								if (j == segs.length - 1) { segs.splice(j, 1); segs[j - 1] = ""; }
+								else { segs.splice(j - 1, 2); j -= 2; }
+							}
+						}
+						relobj.path = segs.join("/");
+					}
+				}
+			}
+
+			uri = "";
+			if (relobj.scheme != null) { uri += relobj.scheme + ":"; }
+			if (relobj.authority != null) { uri += "//" + relobj.authority; }
+			uri += relobj.path;
+			if (relobj.query != null) { uri += "?" + relobj.query; }
+			if (relobj.fragment != null) { uri += "#" + relobj.fragment; }
+		}
+
+		this.uri = uri.toString();
+
+		// break the uri into its main components
+		var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
+	  var r = this.uri.match(new RegExp(regexp));
+
+		this.scheme = r[2] || (r[1] ? "" : null);
+		this.authority = r[4] || (r[3] ? "" : null);
+		this.path = r[5]; // can never be undefined
+		this.query = r[7] || (r[6] ? "" : null);
+		this.fragment  = r[9] || (r[8] ? "" : null);
+		
+		if (this.authority != null) {
+			// server based naming authority
+			regexp = "^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$";
+			r = this.authority.match(new RegExp(regexp));
+			
+			this.user = r[3] || null;
+			this.password = r[4] || null;
+			this.host = r[5];
+			this.port = r[7] || null;
+		}
+	
+		this.toString = function(){ return this.uri; }
+	}
+};
+
+dojo.provide("dojo.html.style");
+
+dojo.html.getClass = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the string value of the list of CSS classes currently assigned directly 
+	//	to the node in question. Returns an empty string if no class attribute is found;
+	node = dojo.byId(node);
+	if(!node){ return ""; }
+	var cs = "";
+	if(node.className){
+		cs = node.className;
+	}else if(dojo.html.hasAttribute(node, "class")){
+		cs = dojo.html.getAttribute(node, "class");
+	}
+	return cs.replace(/^\s+|\s+$/g, "");	//	string
+}
+
+dojo.html.getClasses = function(/* HTMLElement */node) {
+	//	summary
+	//	Returns an array of CSS classes currently assigned directly to the node in question. 
+	//	Returns an empty array if no classes are found;
+	var c = dojo.html.getClass(node);
+	return (c == "") ? [] : c.split(/\s+/g);	//	array
+}
+
+dojo.html.hasClass = function(/* HTMLElement */node, /* string */classname){
+	//	summary
+	//	Returns whether or not the specified classname is a portion of the
+	//	class list currently applied to the node. Does not cover cascaded
+	//	styles, only classes directly applied to the node.
+	return (new RegExp('(^|\\s+)'+classname+'(\\s+|$)')).test(dojo.html.getClass(node))	//	boolean
+}
+
+dojo.html.prependClass = function(/* HTMLElement */node, /* string */classStr){
+	//	summary
+	//	Adds the specified class to the beginning of the class list on the
+	//	passed node. This gives the specified class the highest precidence
+	//	when style cascading is calculated for the node. Returns true or
+	//	false; indicating success or failure of the operation, respectively.
+	classStr += " " + dojo.html.getClass(node);
+	return dojo.html.setClass(node, classStr);	//	boolean
+}
+
+dojo.html.addClass = function(/* HTMLElement */node, /* string */classStr){
+	//	summary
+	//	Adds the specified class to the end of the class list on the
+	//	passed &node;. Returns &true; or &false; indicating success or failure.
+	if (dojo.html.hasClass(node, classStr)) {
+	  return false;
+	}
+	classStr = (dojo.html.getClass(node) + " " + classStr).replace(/^\s+|\s+$/g,"");
+	return dojo.html.setClass(node, classStr);	//	boolean
+}
+
+dojo.html.setClass = function(/* HTMLElement */node, /* string */classStr){
+	//	summary
+	//	Clobbers the existing list of classes for the node, replacing it with
+	//	the list given in the 2nd argument. Returns true or false
+	//	indicating success or failure.
+	node = dojo.byId(node);
+	var cs = new String(classStr);
+	try{
+		if(typeof node.className == "string"){
+			node.className = cs;
+		}else if(node.setAttribute){
+			node.setAttribute("class", classStr);
+			node.className = cs;
+		}else{
+			return false;
+		}
+	}catch(e){
+		dojo.debug("dojo.html.setClass() failed", e);
+	}
+	return true;
+}
+
+dojo.html.removeClass = function(/* HTMLElement */node, /* string */classStr, /* boolean? */allowPartialMatches){
+	//	summary
+	//	Removes the className from the node;. Returns true or false indicating success or failure.
+	try{
+		if (!allowPartialMatches) {
+			var newcs = dojo.html.getClass(node).replace(new RegExp('(^|\\s+)'+classStr+'(\\s+|$)'), "$1$2");
+		} else {
+			var newcs = dojo.html.getClass(node).replace(classStr,'');
+		}
+		dojo.html.setClass(node, newcs);
+	}catch(e){
+		dojo.debug("dojo.html.removeClass() failed", e);
+	}
+	return true;	//	boolean
+}
+
+dojo.html.replaceClass = function(/* HTMLElement */node, /* string */newClass, /* string */oldClass) {
+	//	summary
+	//	Replaces 'oldClass' and adds 'newClass' to node
+	dojo.html.removeClass(node, oldClass);
+	dojo.html.addClass(node, newClass);
+}
+
+// Enum type for getElementsByClass classMatchType arg:
+dojo.html.classMatchType = {
+	ContainsAll : 0, // all of the classes are part of the node's class (default)
+	ContainsAny : 1, // any of the classes are part of the node's class
+	IsOnly : 2 // only all of the classes are part of the node's class
+}
+
+
+dojo.html.getElementsByClass = function(
+	/* string */classStr, 
+	/* HTMLElement? */parent, 
+	/* string? */nodeType, 
+	/* integer? */classMatchType, 
+	/* boolean? */useNonXpath
+){
+	//	summary
+	//	Returns an array of nodes for the given classStr, children of a
+	//	parent, and optionally of a certain nodeType
+	// FIXME: temporarily set to false because of several dojo tickets related
+	// to the xpath version not working consistently in firefox.
+	useNonXpath = false;
+	var _document = dojo.doc();
+	parent = dojo.byId(parent) || _document;
+	var classes = classStr.split(/\s+/g);
+	var nodes = [];
+	if( classMatchType != 1 && classMatchType != 2 ) classMatchType = 0; // make it enum
+	var reClass = new RegExp("(\\s|^)((" + classes.join(")|(") + "))(\\s|$)");
+	var srtLength = classes.join(" ").length;
+	var candidateNodes = [];
+	
+	if(!useNonXpath && _document.evaluate) { // supports dom 3 xpath
+		var xpath = ".//" + (nodeType || "*") + "[contains(";
+		if(classMatchType != dojo.html.classMatchType.ContainsAny){
+			xpath += "concat(' ', at class,' '), ' " +
+			classes.join(" ') and contains(concat(' ', at class,' '), ' ") +
+			" ')";
+			if (classMatchType == 2) {
+				xpath += " and string-length(@class)="+srtLength+"]";
+			}else{
+				xpath += "]";
+			}
+		}else{
+			xpath += "concat(' ', at class,' '), ' " +
+			classes.join(" ') or contains(concat(' ', at class,' '), ' ") +
+			" ')]";
+		}
+		var xpathResult = _document.evaluate(xpath, parent, null, XPathResult.ANY_TYPE, null);
+		var result = xpathResult.iterateNext();
+		while(result){
+			try{
+				candidateNodes.push(result);
+				result = xpathResult.iterateNext();
+			}catch(e){ break; }
+		}
+		return candidateNodes;	//	NodeList
+	}else{
+		if(!nodeType){
+			nodeType = "*";
+		}
+		candidateNodes = parent.getElementsByTagName(nodeType);
+
+		var node, i = 0;
+		outer:
+		while(node = candidateNodes[i++]){
+			var nodeClasses = dojo.html.getClasses(node);
+			if(nodeClasses.length == 0){ continue outer; }
+			var matches = 0;
+	
+			for(var j = 0; j < nodeClasses.length; j++){
+				if(reClass.test(nodeClasses[j])){
+					if(classMatchType == dojo.html.classMatchType.ContainsAny){
+						nodes.push(node);
+						continue outer;
+					}else{
+						matches++;
+					}
+				}else{
+					if(classMatchType == dojo.html.classMatchType.IsOnly){
+						continue outer;
+					}
+				}
+			}
+	
+			if(matches == classes.length){
+				if(	(classMatchType == dojo.html.classMatchType.IsOnly)&&
+					(matches == nodeClasses.length)){
+					nodes.push(node);
+				}else if(classMatchType == dojo.html.classMatchType.ContainsAll){
+					nodes.push(node);
+				}
+			}
+		}
+		return nodes;	//	NodeList
+	}
+}
+dojo.html.getElementsByClassName = dojo.html.getElementsByClass;
+
+dojo.html.toCamelCase = function(/* string */selector){
+	//	summary
+	//	Translates a CSS selector string to a camel-cased one.
+	var arr = selector.split('-'), cc = arr[0];
+	for(var i = 1; i < arr.length; i++) {
+		cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
+	}
+	return cc;	//	string
+}
+
+dojo.html.toSelectorCase = function(/* string */selector){
+	//	summary
+	//	Translates a camel cased string to a selector cased one.
+	return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase();	//	string
+}
+
+dojo.html.getComputedStyle = function(/* HTMLElement */node, /* string */cssSelector, /* integer? */inValue){
+	//	summary
+	//	Returns the computed style of cssSelector on node.
+	node = dojo.byId(node);
+	// cssSelector may actually be in camel case, so force selector version
+	var cssSelector = dojo.html.toSelectorCase(cssSelector);
+	var property = dojo.html.toCamelCase(cssSelector);
+	if(!node || !node.style){
+		return inValue;			
+	} else if (document.defaultView && dojo.html.isDescendantOf(node, node.ownerDocument)){ // W3, gecko, KHTML
+		try{
+			// mozilla segfaults when margin-* and node is removed from doc
+			// FIXME: need to figure out a if there is quicker workaround
+			var cs = document.defaultView.getComputedStyle(node, "");
+			if(cs){
+				return cs.getPropertyValue(cssSelector);	//	integer
+			} 
+		}catch(e){ // reports are that Safari can throw an exception above
+			if(node.style.getPropertyValue){ // W3
+				return node.style.getPropertyValue(cssSelector);	//	integer
+			} else {
+				return inValue;	//	integer
+			}
+		}
+	} else if(node.currentStyle){ // IE
+		return node.currentStyle[property];	//	integer
+	}
+	
+	if(node.style.getPropertyValue){ // W3
+		return node.style.getPropertyValue(cssSelector);	//	integer
+	}else{
+		return inValue;	//	integer
+	}
+}
+
+dojo.html.getStyleProperty = function(/* HTMLElement */node, /* string */cssSelector){
+	//	summary
+	//	Returns the value of the passed style
+	node = dojo.byId(node);
+	return (node && node.style ? node.style[dojo.html.toCamelCase(cssSelector)] : undefined);	//	string
+}
+
+dojo.html.getStyle = function(/* HTMLElement */node, /* string */cssSelector){
+	//	summary
+	//	Returns the computed value of the passed style
+	var value = dojo.html.getStyleProperty(node, cssSelector);
+	return (value ? value : dojo.html.getComputedStyle(node, cssSelector));	//	string || integer
+}
+
+dojo.html.setStyle = function(/* HTMLElement */node, /* string */cssSelector, /* string */value){
+	//	summary
+	//	Set the value of passed style on node
+	node = dojo.byId(node);
+	if(node && node.style){
+		var camelCased = dojo.html.toCamelCase(cssSelector);
+		node.style[camelCased] = value;
+	}
+}
+
+dojo.html.setStyleText = function (/* HTMLElement */target, /* string */text) {
+	//	summary
+	//	Try to set the entire cssText property of the passed target; equiv of setting style attribute.
+	try {
+	 	target.style.cssText = text;
+	} catch (e) {
+		target.setAttribute("style", text);
+	}
+}
+
+dojo.html.copyStyle = function(/* HTMLElement */target, /* HTMLElement */source){
+	//	summary
+	// work around for opera which doesn't have cssText, and for IE which fails on setAttribute 
+	if(!source.style.cssText){ 
+		target.setAttribute("style", source.getAttribute("style")); 
+	}else{
+		target.style.cssText = source.style.cssText; 
+	}
+	dojo.html.addClass(target, dojo.html.getClass(source));
+}
+
+dojo.html.getUnitValue = function(/* HTMLElement */node, /* string */cssSelector, /* boolean? */autoIsZero){
+	//	summary
+	//	Get the value of passed selector, with the specific units used
+	var s = dojo.html.getComputedStyle(node, cssSelector);
+	if((!s)||((s == 'auto')&&(autoIsZero))){ 
+		return { value: 0, units: 'px' };	//	object 
+	}
+	// FIXME: is regex inefficient vs. parseInt or some manual test? 
+	var match = s.match(/(\-?[\d.]+)([a-z%]*)/i);
+	if (!match){return dojo.html.getUnitValue.bad;}
+	return { value: Number(match[1]), units: match[2].toLowerCase() };	//	object
+}
+dojo.html.getUnitValue.bad = { value: NaN, units: '' };
+
+dojo.html.getPixelValue = function(/* HTMLElement */node, /* string */cssSelector, /* boolean? */autoIsZero){
+	//	summary
+	//	Get the value of passed selector in pixels.
+	var result = dojo.html.getUnitValue(node, cssSelector, autoIsZero);
+	// FIXME: there is serious debate as to whether or not this is the right solution
+	if(isNaN(result.value)){ 
+		return 0; //	integer 
+	}	
+	// FIXME: code exists for converting other units to px (see Dean Edward's IE7) 
+	// but there are cross-browser complexities
+	if((result.value)&&(result.units != 'px')){ 
+		return NaN;	//	integer 
+	}
+	return result.value;	//	integer
+}
+
+dojo.html.setPositivePixelValue = function(/* HTMLElement */node, /* string */selector, /* integer */value){
+	//	summary
+	//	Attempt to set the value of selector on node as a positive pixel value.
+	if(isNaN(value)){return false;}
+	node.style[selector] = Math.max(0, value) + 'px'; 
+	return true;	//	boolean
+}
+
+dojo.html.styleSheet = null;
+
+// FIXME: this is a really basic stub for adding and removing cssRules, but
+// it assumes that you know the index of the cssRule that you want to add 
+// or remove, making it less than useful.  So we need something that can 
+// search for the selector that you you want to remove.
+dojo.html.insertCssRule = function(/* string */selector, /* string */declaration, /* integer? */index) {
+	//	summary
+	//	Attempt to insert declaration as selector on the internal stylesheet; if index try to set it there.
+	if (!dojo.html.styleSheet) {
+		if (document.createStyleSheet) { // IE
+			dojo.html.styleSheet = document.createStyleSheet();
+		} else if (document.styleSheets[0]) { // rest
+			// FIXME: should create a new style sheet here
+			// fall back on an exsiting style sheet
+			dojo.html.styleSheet = document.styleSheets[0];
+		} else { 
+			return null;	//	integer 
+		} // fail
+	}
+
+	if (arguments.length < 3) { // index may == 0
+		if (dojo.html.styleSheet.cssRules) { // W3
+			index = dojo.html.styleSheet.cssRules.length;
+		} else if (dojo.html.styleSheet.rules) { // IE
+			index = dojo.html.styleSheet.rules.length;
+		} else { 
+			return null;	//	integer 
+		} // fail
+	}
+
+	if (dojo.html.styleSheet.insertRule) { // W3
+		var rule = selector + " { " + declaration + " }";
+		return dojo.html.styleSheet.insertRule(rule, index);	//	integer
+	} else if (dojo.html.styleSheet.addRule) { // IE
+		return dojo.html.styleSheet.addRule(selector, declaration, index);	//	integer
+	} else { 
+		return null; // integer
+	} // fail
+}
+
+dojo.html.removeCssRule = function(/* integer? */index){
+	//	summary
+	//	Attempt to remove the rule at index.
+	if(!dojo.html.styleSheet){
+		dojo.debug("no stylesheet defined for removing rules");
+		return false;
+	}
+	if(dojo.render.html.ie){
+		if(!index){
+			index = dojo.html.styleSheet.rules.length;
+			dojo.html.styleSheet.removeRule(index);
+		}
+	}else if(document.styleSheets[0]){
+		if(!index){
+			index = dojo.html.styleSheet.cssRules.length;
+		}
+		dojo.html.styleSheet.deleteRule(index);
+	}
+	return true;	//	boolean
+}
+
+dojo.html._insertedCssFiles = []; // cache container needed because IE reformats cssText when added to DOM
+dojo.html.insertCssFile = function(/* string */URI, /* HTMLDocument? */doc, /* boolean? */checkDuplicates, /* boolean */fail_ok){
+	//	summary
+	// calls css by XmlHTTP and inserts it into DOM as <style [widgetType="widgetType"]> *downloaded cssText*</style>
+	if(!URI){ return; }
+	if(!doc){ doc = document; }
+	var cssStr = dojo.hostenv.getText(URI, false, fail_ok);
+  if(cssStr===null){ return; } 
+	cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
+
+	if(checkDuplicates){
+		var idx = -1, node, ent = dojo.html._insertedCssFiles;
+		for(var i = 0; i < ent.length; i++){
+			if((ent[i].doc == doc) && (ent[i].cssText == cssStr)){
+				idx = i; node = ent[i].nodeRef;
+				break;
+			}
+		}
+		// make sure we havent deleted our node
+		if(node){
+			var styles = doc.getElementsByTagName("style");
+			for(var i = 0; i < styles.length; i++){
+				if(styles[i] == node){
+					return;
+				}
+			}
+			// delete this entry
+			dojo.html._insertedCssFiles.shift(idx, 1);
+		}
+	}
+
+	var style = dojo.html.insertCssText(cssStr);
+	dojo.html._insertedCssFiles.push({'doc': doc, 'cssText': cssStr, 'nodeRef': style});
+
+	// insert custom attribute ex dbgHref="../foo.css" usefull when debugging in DOM inspectors, no?
+	if(style && djConfig.isDebug){
+		style.setAttribute("dbgHref", URI);
+	}
+	return style;	//	HTMLStyleElement
+}
+
+dojo.html.insertCssText = function(/* string */cssStr, /* HTMLDocument? */doc, /* string? */URI){
+	//	summary
+	//	Attempt to insert CSS rules into the document through inserting a style element
+	// DomNode Style  = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ])
+	if(!cssStr){ 
+		return; //	HTMLStyleElement
+	}
+	if(!doc){ doc = document; }
+	if(URI){// fix paths in cssStr
+		cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
+	}
+	var style = doc.createElement("style");
+	style.setAttribute("type", "text/css");
+	// IE is b0rken enough to require that we add the element to the doc
+	// before changing it's properties
+	var head = doc.getElementsByTagName("head")[0];
+	if(!head){ // must have a head tag 
+		dojo.debug("No head tag in document, aborting styles");
+		return;	//	HTMLStyleElement
+	}else{
+		head.appendChild(style);
+	}
+	if(style.styleSheet){// IE
+		style.styleSheet.cssText = cssStr;
+	}else{ // w3c
+		var cssText = doc.createTextNode(cssStr);
+		style.appendChild(cssText);
+	}
+	return style;	//	HTMLStyleElement
+}
+
+dojo.html.fixPathsInCssText = function(/* string */cssStr, /* string */URI){
+	//	summary
+	// usage: cssText comes from dojoroot/src/widget/templates/Foobar.css
+	// 	it has .dojoFoo { background-image: url(images/bar.png);} then uri should point to dojoroot/src/widget/templates/
+	function iefixPathsInCssText() {
+		var regexIe = /AlphaImageLoader\(src\=['"]([\t\s\w()\/.\\'"-:#=&?~]*)['"]/;
+		while(match = regexIe.exec(cssStr)){
+			url = match[1].replace(regexTrim, "$2");
+			if(!regexProtocol.exec(url)){
+				url = (new dojo.uri.Uri(URI, url).toString());
+			}
+			str += cssStr.substring(0, match.index) + "AlphaImageLoader(src='" + url + "'";
+			cssStr = cssStr.substr(match.index + match[0].length);
+		}
+		return str + cssStr;
+	}
+
+	if(!cssStr || !URI){ return; }
+	var match, str = "", url = "";
+	var regex = /url\(\s*([\t\s\w()\/.\\'"-:#=&?]+)\s*\)/;
+	var regexProtocol = /(file|https?|ftps?):\/\//;
+	var regexTrim = /^[\s]*(['"]?)([\w()\/.\\'"-:#=&?]*)\1[\s]*?$/;
+	if (dojo.render.html.ie55 || dojo.render.html.ie60) {
+		cssStr = iefixPathsInCssText();
+	}
+	while(match = regex.exec(cssStr)){
+		url = match[1].replace(regexTrim, "$2");
+		if(!regexProtocol.exec(url)){
+			url = (new dojo.uri.Uri(URI, url).toString());
+		}
+		str += cssStr.substring(0, match.index) + "url(" + url + ")";
+		cssStr = cssStr.substr(match.index + match[0].length);
+	}
+	return str + cssStr;	//	string
+}
+
+dojo.html.setActiveStyleSheet = function(/* string */title){
+	//	summary
+	//	Activate style sheet with specified title.
+	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
+	while (a = els[i++]) {
+		if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")){
+			a.disabled = true;
+			if (a.getAttribute("title") == title) { a.disabled = false; }
+		}
+	}
+}
+
+dojo.html.getActiveStyleSheet = function(){
+	//	summary
+	//	return the title of the currently active stylesheet
+	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
+	while (a = els[i++]) {
+		if (a.getAttribute("rel").indexOf("style") != -1 
+			&& a.getAttribute("title") 
+			&& !a.disabled
+		){
+			return a.getAttribute("title");	//	string 
+		}
+	}
+	return null;	//	string
+}
+
+dojo.html.getPreferredStyleSheet = function(){
+	//	summary
+	//	Return the preferred stylesheet title (i.e. link without alt attribute)
+	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
+	while (a = els[i++]) {
+		if(a.getAttribute("rel").indexOf("style") != -1
+			&& a.getAttribute("rel").indexOf("alt") == -1
+			&& a.getAttribute("title")
+		){ 
+			return a.getAttribute("title"); 	//	string
+		}
+	}
+	return null;	//	string
+}
+
+dojo.html.applyBrowserClass = function(/* HTMLElement */node){
+	//	summary
+	//	Applies pre-set class names based on browser & version to the passed node.
+	//	Modified version of Morris' CSS hack.
+	var drh=dojo.render.html;
+	var classes = {
+		dj_ie: drh.ie,
+		dj_ie55: drh.ie55,
+		dj_ie6: drh.ie60,
+		dj_ie7: drh.ie70,
+		dj_iequirks: drh.ie && drh.quirks,
+		dj_opera: drh.opera,
+		dj_opera8: drh.opera && (Math.floor(dojo.render.version)==8),
+		dj_opera9: drh.opera && (Math.floor(dojo.render.version)==9),
+		dj_khtml: drh.khtml,
+		dj_safari: drh.safari,
+		dj_gecko: drh.mozilla
+	}; // no dojo unsupported browsers
+	for(var p in classes){
+		if(classes[p]){
+			dojo.html.addClass(node, p);
+		}
+	}
+};
+
+dojo.provide("dojo.html.display");
+
+dojo.html._toggle = function(node, tester, setter){
+	node = dojo.byId(node);
+	setter(node, !tester(node));
+	return tester(node);
+}
+
+dojo.html.show = function(/* HTMLElement */node){
+	//	summary
+	//	Show the passed element by reverting display property set by dojo.html.hide
+	node = dojo.byId(node);
+	if(dojo.html.getStyleProperty(node, 'display')=='none'){
+		dojo.html.setStyle(node, 'display', (node.dojoDisplayCache||''));
+		node.dojoDisplayCache = undefined;	// cannot use delete on a node in IE6
+	}
+}
+
+dojo.html.hide = function(/* HTMLElement */node){
+	//	summary
+	//	Hide the passed element by setting display:none
+	node = dojo.byId(node);
+	if(typeof node["dojoDisplayCache"] == "undefined"){ // it could == '', so we cannot say !node.dojoDisplayCount
+		var d = dojo.html.getStyleProperty(node, 'display')
+		if(d!='none'){
+			node.dojoDisplayCache = d;
+		}
+	}
+	dojo.html.setStyle(node, 'display', 'none');
+}
+
+dojo.html.setShowing = function(/* HTMLElement */node, /* boolean? */showing){
+	//	summary
+	// Calls show() if showing is true, hide() otherwise
+	dojo.html[(showing ? 'show' : 'hide')](node);
+}
+
+dojo.html.isShowing = function(/* HTMLElement */node){
+	//	summary
+	//	Returns whether the element is displayed or not.
+	// FIXME: returns true if node is bad, isHidden would be easier to make correct
+	return (dojo.html.getStyleProperty(node, 'display') != 'none');	//	boolean
+}
+
+dojo.html.toggleShowing = function(/* HTMLElement */node){
+	//	summary
+	// Call setShowing() on node with the complement of isShowing(), then return the new value of isShowing()
+	return dojo.html._toggle(node, dojo.html.isShowing, dojo.html.setShowing);	//	boolean
+}
+
+// Simple mapping of tag names to display values
+// FIXME: simplistic 
+dojo.html.displayMap = { tr: '', td: '', th: '', img: 'inline', span: 'inline', input: 'inline', button: 'inline' };
+
+dojo.html.suggestDisplayByTagName = function(/* HTMLElement */node){
+	//	summary
+	// Suggest a value for the display property that will show 'node' based on it's tag
+	node = dojo.byId(node);
+	if(node && node.tagName){
+		var tag = node.tagName.toLowerCase();
+		return (tag in dojo.html.displayMap ? dojo.html.displayMap[tag] : 'block');	//	string
+	}
+}
+
+dojo.html.setDisplay = function(/* HTMLElement */node, /* string */display){
+	//	summary
+	// 	Sets the value of style.display to value of 'display' parameter if it is a string.
+	// 	Otherwise, if 'display' is false, set style.display to 'none'.
+	// 	Finally, set 'display' to a suggested display value based on the node's tag
+	dojo.html.setStyle(node, 'display', ((display instanceof String || typeof display == "string") ? display : (display ? dojo.html.suggestDisplayByTagName(node) : 'none')));
+}
+
+dojo.html.isDisplayed = function(/* HTMLElement */node){
+	//	summary
+	// 	Is true if the the computed display style for node is not 'none'
+	// 	FIXME: returns true if node is bad, isNotDisplayed would be easier to make correct
+	return (dojo.html.getComputedStyle(node, 'display') != 'none');	//	boolean
+}
+
+dojo.html.toggleDisplay = function(/* HTMLElement */node){
+	//	summary
+	// 	Call setDisplay() on node with the complement of isDisplayed(), then
+	// 	return the new value of isDisplayed()
+	return dojo.html._toggle(node, dojo.html.isDisplayed, dojo.html.setDisplay);	//	boolean
+}
+
+dojo.html.setVisibility = function(/* HTMLElement */node, /* string */visibility){
+	//	summary
+	// 	Sets the value of style.visibility to value of 'visibility' parameter if it is a string.
+	// 	Otherwise, if 'visibility' is false, set style.visibility to 'hidden'. Finally, set style.visibility to 'visible'.
+	dojo.html.setStyle(node, 'visibility', ((visibility instanceof String || typeof visibility == "string") ? visibility : (visibility ? 'visible' : 'hidden')));
+}
+
+dojo.html.isVisible = function(/* HTMLElement */node){
+	//	summary
+	// 	Returns true if the the computed visibility style for node is not 'hidden'
+	// 	FIXME: returns true if node is bad, isInvisible would be easier to make correct
+	return (dojo.html.getComputedStyle(node, 'visibility') != 'hidden');	//	boolean
+}
+
+dojo.html.toggleVisibility = function(node){
+	//	summary
+	// Call setVisibility() on node with the complement of isVisible(), then return the new value of isVisible()
+	return dojo.html._toggle(node, dojo.html.isVisible, dojo.html.setVisibility);	//	boolean
+}
+
+dojo.html.setOpacity = function(/* HTMLElement */node, /* float */opacity, /* boolean? */dontFixOpacity){
+	//	summary
+	//	Sets the opacity of node in a cross-browser way.
+	//	float between 0.0 (transparent) and 1.0 (opaque)
+	node = dojo.byId(node);
+	var h = dojo.render.html;
+	if(!dontFixOpacity){
+		if( opacity >= 1.0){
+			if(h.ie){
+				dojo.html.clearOpacity(node);
+				return;
+			}else{
+				opacity = 0.999999;
+			}
+		}else if( opacity < 0.0){ opacity = 0; }
+	}
+	if(h.ie){
+		if(node.nodeName.toLowerCase() == "tr"){
+			// FIXME: is this too naive? will we get more than we want?
+			var tds = node.getElementsByTagName("td");
+			for(var x=0; x<tds.length; x++){
+				tds[x].style.filter = "Alpha(Opacity="+opacity*100+")";
+			}
+		}
+		node.style.filter = "Alpha(Opacity="+opacity*100+")";
+	}else if(h.moz){
+		node.style.opacity = opacity; // ffox 1.0 directly supports "opacity"
+		node.style.MozOpacity = opacity;
+	}else if(h.safari){
+		node.style.opacity = opacity; // 1.3 directly supports "opacity"
+		node.style.KhtmlOpacity = opacity;
+	}else{
+		node.style.opacity = opacity;
+	}
+}
+
+dojo.html.clearOpacity = function(/* HTMLElement */node){
+	//	summary
+	//	Clears any opacity setting on the passed element.
+	node = dojo.byId(node);
+	var ns = node.style;
+	var h = dojo.render.html;
+	if(h.ie){
+		try {
+			if( node.filters && node.filters.alpha ){
+				ns.filter = ""; // FIXME: may get rid of other filter effects
+			}
+		} catch(e) {
+			/*
+			 * IE7 gives error if node.filters not set;
+			 * don't know why or how to workaround (other than this)
+			 */
+		}
+	}else if(h.moz){
+		ns.opacity = 1;
+		ns.MozOpacity = 1;
+	}else if(h.safari){
+		ns.opacity = 1;
+		ns.KhtmlOpacity = 1;
+	}else{
+		ns.opacity = 1;
+	}
+}
+
+dojo.html.getOpacity = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the opacity of the passed element
+	node = dojo.byId(node);
+	var h = dojo.render.html;
+	if(h.ie){
+		var opac = (node.filters && node.filters.alpha &&
+			typeof node.filters.alpha.opacity == "number"
+			? node.filters.alpha.opacity : 100) / 100;
+	}else{
+		var opac = node.style.opacity || node.style.MozOpacity ||
+			node.style.KhtmlOpacity || 1;
+	}
+	return opac >= 0.999999 ? 1.0 : Number(opac);	//	float
+}
+
+dojo.provide("dojo.html.color");
+
+
+dojo.html.getBackgroundColor = function(/* HTMLElement */node){
+	//	summary
+	//	returns the background color of the passed node as a 32-bit color (RGBA)
+	node = dojo.byId(node);
+	var color;
+	do{
+		color = dojo.html.getStyle(node, "background-color");
+		// Safari doesn't say "transparent"
+		if(color.toLowerCase() == "rgba(0, 0, 0, 0)") { color = "transparent"; }
+		if(node == document.getElementsByTagName("body")[0]) { node = null; break; }
+		node = node.parentNode;
+	}while(node && dojo.lang.inArray(["transparent", ""], color));
+	if(color == "transparent"){
+		color = [255, 255, 255, 0];
+	}else{
+		color = dojo.gfx.color.extractRGB(color);
+	}
+	return color;	//	array
+}
+
+dojo.provide("dojo.html.common");
+
+dojo.lang.mixin(dojo.html, dojo.dom);
+
+dojo.html.body = function(){
+	dojo.deprecated("dojo.html.body() moved to dojo.body()", "0.5");
+	return dojo.body();
+}
+
+// FIXME: we are going to assume that we can throw any and every rendering
+// engine into the IE 5.x box model. In Mozilla, we do this w/ CSS.
+// Need to investigate for KHTML and Opera
+
+dojo.html.getEventTarget = function(/* DOMEvent */evt){
+	//	summary
+	//	Returns the target of an event
+	if(!evt) { evt = dojo.global().event || {} };
+	var t = (evt.srcElement ? evt.srcElement : (evt.target ? evt.target : null));
+	while((t)&&(t.nodeType!=1)){ t = t.parentNode; }
+	return t;	//	HTMLElement
+}
+
+dojo.html.getViewport = function(){
+	//	summary
+	//	Returns the dimensions of the viewable area of a browser window
+	var _window = dojo.global();
+	var _document = dojo.doc();
+	var w = 0;
+	var h = 0;
+
+	if(dojo.render.html.mozilla){
+		// mozilla
+		w = _document.documentElement.clientWidth;
+		h = _window.innerHeight;
+	}else if(!dojo.render.html.opera && _window.innerWidth){
+		//in opera9, dojo.body().clientWidth should be used, instead
+		//of window.innerWidth/document.documentElement.clientWidth
+		//so we have to check whether it is opera
+		w = _window.innerWidth;
+		h = _window.innerHeight;
+	} else if (!dojo.render.html.opera && dojo.exists(_document, "documentElement.clientWidth")){
+		// IE6 Strict
+		var w2 = _document.documentElement.clientWidth;
+		// this lets us account for scrollbars
+		if(!w || w2 && w2 < w) {
+			w = w2;
+		}
+		h = _document.documentElement.clientHeight;
+	} else if (dojo.body().clientWidth){
+		// IE, Opera
+		w = dojo.body().clientWidth;
+		h = dojo.body().clientHeight;
+	}
+	return { width: w, height: h };	//	object
+}
+
+dojo.html.getScroll = function(){
+	//	summary
+	//	Returns the scroll position of the document
+	var _window = dojo.global();
+	var _document = dojo.doc();
+	var top = _window.pageYOffset || _document.documentElement.scrollTop || dojo.body().scrollTop || 0;
+	var left = _window.pageXOffset || _document.documentElement.scrollLeft || dojo.body().scrollLeft || 0;
+	return { 
+		top: top, 
+		left: left, 
+		offset:{ x: left, y: top }	//	note the change, NOT an Array with added properties. 
+	};	//	object
+}
+
+dojo.html.getParentByType = function(/* HTMLElement */node, /* string */type) {
+	//	summary
+	//	Returns the first ancestor of node with tagName type.
+	var _document = dojo.doc();
+	var parent = dojo.byId(node);
+	type = type.toLowerCase();
+	while((parent)&&(parent.nodeName.toLowerCase()!=type)){
+		if(parent==(_document["body"]||_document["documentElement"])){
+			return null;
+		}
+		parent = parent.parentNode;
+	}
+	return parent;	//	HTMLElement
+}
+
+dojo.html.getAttribute = function(/* HTMLElement */node, /* string */attr){
+	//	summary
+	//	Returns the value of attribute attr from node.
+	node = dojo.byId(node);
+	// FIXME: need to add support for attr-specific accessors
+	if((!node)||(!node.getAttribute)){
+		// if(attr !== 'nwType'){
+		//	alert("getAttr of '" + attr + "' with bad node"); 
+		// }
+		return null;
+	}
+	var ta = typeof attr == 'string' ? attr : new String(attr);
+
+	// first try the approach most likely to succeed
+	var v = node.getAttribute(ta.toUpperCase());
+	if((v)&&(typeof v == 'string')&&(v!="")){ 
+		return v;	//	string 
+	}
+
+	// try returning the attributes value, if we couldn't get it as a string
+	if(v && v.value){ 
+		return v.value;	//	string 
+	}
+
+	// this should work on Opera 7, but it's a little on the crashy side
+	if((node.getAttributeNode)&&(node.getAttributeNode(ta))){
+		return (node.getAttributeNode(ta)).value;	//	string
+	}else if(node.getAttribute(ta)){
+		return node.getAttribute(ta);	//	string
+	}else if(node.getAttribute(ta.toLowerCase())){
+		return node.getAttribute(ta.toLowerCase());	//	string
+	}
+	return null;	//	string
+}
+	
+dojo.html.hasAttribute = function(/* HTMLElement */node, /* string */attr){
+	//	summary
+	//	Determines whether or not the specified node carries a value for the attribute in question.
+	return dojo.html.getAttribute(dojo.byId(node), attr) ? true : false;	//	boolean
+}
+	
+dojo.html.getCursorPosition = function(/* DOMEvent */e){
+	//	summary
+	//	Returns the mouse position relative to the document (not the viewport).
+	//	For example, if you have a document that is 10000px tall,
+	//	but your browser window is only 100px tall,
+	//	if you scroll to the bottom of the document and call this function it
+	//	will return {x: 0, y: 10000}
+	//	NOTE: for events delivered via dojo.event.connect() and/or dojoAttachEvent (for widgets),
+	//	you can just access evt.pageX and evt.pageY, rather than calling this function.
+	e = e || dojo.global().event;
+	var cursor = {x:0, y:0};
+	if(e.pageX || e.pageY){
+		cursor.x = e.pageX;
+		cursor.y = e.pageY;
+	}else{
+		var de = dojo.doc().documentElement;
+		var db = dojo.body();
+		cursor.x = e.clientX + ((de||db)["scrollLeft"]) - ((de||db)["clientLeft"]);
+		cursor.y = e.clientY + ((de||db)["scrollTop"]) - ((de||db)["clientTop"]);
+	}
+	return cursor;	//	object
+}
+
+dojo.html.isTag = function(/* HTMLElement */node) {
+	//	summary
+	//	Like dojo.dom.isTag, except case-insensitive
+	node = dojo.byId(node);
+	if(node && node.tagName) {
+		for (var i=1; i<arguments.length; i++){
+			if (node.tagName.toLowerCase()==String(arguments[i]).toLowerCase()){
+				return String(arguments[i]).toLowerCase();	//	string
+			}
+		}
+	}
+	return "";	//	string
+}
+
+//define dojo.html.createExternalElement for IE to workaround the annoying activation "feature" in new IE
+//details: http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/activating_activex.asp
+if(dojo.render.html.ie && !dojo.render.html.ie70){
+	//only define createExternalElement for IE in none https to avoid "mixed content" warning dialog
+	if(window.location.href.substr(0,6).toLowerCase() != "https:"){
+		(function(){
+			// FIXME: this seems not to work correctly on IE 7!!
+
+			//The trick is to define a function in a script.src property:
+			// <script src="javascript:'function createExternalElement(){...}'"></script>,
+			//which will be treated as an external javascript file in IE
+			var xscript = dojo.doc().createElement('script');
+			xscript.src = "javascript:'dojo.html.createExternalElement=function(doc, tag){ return doc.createElement(tag); }'";
+			dojo.doc().getElementsByTagName("head")[0].appendChild(xscript);
+		})();
+	}
+}else{
+	//for other browsers, simply use document.createElement
+	//is enough
+	dojo.html.createExternalElement = function(/* HTMLDocument */doc, /* string */tag){
+		//	summary
+		//	Creates an element in the HTML document, here for ActiveX activation workaround.
+		return doc.createElement(tag);	//	HTMLElement
+	}
+}
+
+dojo.html._callDeprecated = function(inFunc, replFunc, args, argName, retValue){
+	dojo.deprecated("dojo.html." + inFunc,
+					"replaced by dojo.html." + replFunc + "(" + (argName ? "node, {"+ argName + ": " + argName + "}" : "" ) + ")" + (retValue ? "." + retValue : ""), "0.5");
+	var newArgs = [];
+	if(argName){ var argsIn = {}; argsIn[argName] = args[1]; newArgs.push(args[0]); newArgs.push(argsIn); }
+	else { newArgs = args }
+	var ret = dojo.html[replFunc].apply(dojo.html, args);
+	if(retValue){ return ret[retValue]; }
+	else { return ret; }
+}
+
+dojo.html.getViewportWidth = function(){
+	return dojo.html._callDeprecated("getViewportWidth", "getViewport", arguments, null, "width");
+}
+dojo.html.getViewportHeight = function(){
+	return dojo.html._callDeprecated("getViewportHeight", "getViewport", arguments, null, "height");
+}
+dojo.html.getViewportSize = function(){
+	return dojo.html._callDeprecated("getViewportSize", "getViewport", arguments);
+}
+dojo.html.getScrollTop = function(){
+	return dojo.html._callDeprecated("getScrollTop", "getScroll", arguments, null, "top");
+}
+dojo.html.getScrollLeft = function(){
+	return dojo.html._callDeprecated("getScrollLeft", "getScroll", arguments, null, "left");
+}
+dojo.html.getScrollOffset = function(){
+	return dojo.html._callDeprecated("getScrollOffset", "getScroll", arguments, null, "offset");
+}
+
+dojo.provide("dojo.html.layout");
+
+
+dojo.html.sumAncestorProperties = function(/* HTMLElement */node, /* string */prop){
+	//	summary
+	//	Returns the sum of the passed property on all ancestors of node.
+	node = dojo.byId(node);
+	if(!node){ return 0; } // FIXME: throw an error?
+	
+	var retVal = 0;
+	while(node){
+		if(dojo.html.getComputedStyle(node, 'position') == 'fixed'){
+			return 0;
+		}
+		var val = node[prop];
+		if(val){
+			retVal += val - 0;
+			if(node==dojo.body()){ break; }// opera and khtml #body & #html has the same values, we only need one value
+		}
+		node = node.parentNode;
+	}
+	return retVal;	//	integer
+}
+
+dojo.html.setStyleAttributes = function(/* HTMLElement */node, /* string */attributes) { 
+	//	summary
+	//	allows a dev to pass a string similar to what you'd pass in style="", and apply it to a node.
+	node = dojo.byId(node);
+	var splittedAttribs=attributes.replace(/(;)?\s*$/, "").split(";"); 
+	for(var i=0; i<splittedAttribs.length; i++){ 
+		var nameValue=splittedAttribs[i].split(":"); 
+		var name=nameValue[0].replace(/\s*$/, "").replace(/^\s*/, "").toLowerCase();
+		var value=nameValue[1].replace(/\s*$/, "").replace(/^\s*/, "");
+		switch(name){
+			case "opacity":
+				dojo.html.setOpacity(node, value); 
+				break; 
+			case "content-height":
+				dojo.html.setContentBox(node, {height: value}); 
+				break; 
+			case "content-width":
+				dojo.html.setContentBox(node, {width: value}); 
+				break; 
+			case "outer-height":
+				dojo.html.setMarginBox(node, {height: value}); 
+				break; 
+			case "outer-width":
+				dojo.html.setMarginBox(node, {width: value}); 
+				break; 
+			default:
+				node.style[dojo.html.toCamelCase(name)]=value; 
+		}
+	} 
+}
+
+dojo.html.boxSizing = {
+	MARGIN_BOX: "margin-box",
+	BORDER_BOX: "border-box",
+	PADDING_BOX: "padding-box",
+	CONTENT_BOX: "content-box"
+};
+
+dojo.html.getAbsolutePosition = dojo.html.abs = function(/* HTMLElement */node, /* boolean? */includeScroll, /* string? */boxType){
+	//	summary
+	//	Gets the absolute position of the passed element based on the document itself.
+	node = dojo.byId(node, node.ownerDocument);
+	var ret = {
+		x: 0,
+		y: 0
+	};
+
+	var bs = dojo.html.boxSizing;
+	if(!boxType) { boxType = bs.CONTENT_BOX; }
+	var nativeBoxType = 2; //BORDER box
+	var targetBoxType;
+	switch(boxType){
+		case bs.MARGIN_BOX:
+			targetBoxType = 3;
+			break;
+		case bs.BORDER_BOX:
+			targetBoxType = 2;
+			break;
+		case bs.PADDING_BOX:
+		default:
+			targetBoxType = 1;
+			break;
+		case bs.CONTENT_BOX:
+			targetBoxType = 0;
+			break;
+	}
+
+	var h = dojo.render.html;
+	var db = document["body"]||document["documentElement"];
+
+	if(h.ie){
+		with(node.getBoundingClientRect()){
+			ret.x = left-2;
+			ret.y = top-2;
+		}
+	}else if(document.getBoxObjectFor){
+		// mozilla
+		nativeBoxType = 1; //getBoxObjectFor return padding box coordinate
+		try{
+			var bo = document.getBoxObjectFor(node);
+			ret.x = bo.x - dojo.html.sumAncestorProperties(node, "scrollLeft");
+			ret.y = bo.y - dojo.html.sumAncestorProperties(node, "scrollTop");
+		}catch(e){
+			// squelch
+		}
+	}else{
+		if(node["offsetParent"]){
+			var endNode;
+			// in Safari, if the node is an absolutely positioned child of
+			// the body and the body has a margin the offset of the child
+			// and the body contain the body's margins, so we need to end
+			// at the body
+			if(	(h.safari)&&
+				(node.style.getPropertyValue("position") == "absolute")&&
+				(node.parentNode == db)){
+				endNode = db;
+			}else{
+				endNode = db.parentNode;
+			}
+
+			//TODO: set correct nativeBoxType for safari/konqueror
+
+			if(node.parentNode != db){
+				var nd = node;
+				if(dojo.render.html.opera){ nd = db; }
+				ret.x -= dojo.html.sumAncestorProperties(nd, "scrollLeft");
+				ret.y -= dojo.html.sumAncestorProperties(nd, "scrollTop");
+			}
+			var curnode = node;
+			do{
+				var n = curnode["offsetLeft"];
+				//FIXME: ugly hack to workaround the submenu in 
+				//popupmenu2 does not shown up correctly in opera. 
+				//Someone have a better workaround?
+				if(!h.opera || n>0){
+					ret.x += isNaN(n) ? 0 : n;
+				}
+				var m = curnode["offsetTop"];
+				ret.y += isNaN(m) ? 0 : m;
+				curnode = curnode.offsetParent;
+			}while((curnode != endNode)&&(curnode != null));
+		}else if(node["x"]&&node["y"]){
+			ret.x += isNaN(node.x) ? 0 : node.x;
+			ret.y += isNaN(node.y) ? 0 : node.y;
+		}
+	}
+
+	// account for document scrolling!
+	if(includeScroll){
+		var scroll = dojo.html.getScroll();
+		ret.y += scroll.top;
+		ret.x += scroll.left;
+	}
+
+	var extentFuncArray=[dojo.html.getPaddingExtent, dojo.html.getBorderExtent, dojo.html.getMarginExtent];
+	if(nativeBoxType > targetBoxType){
+		for(var i=targetBoxType;i<nativeBoxType;++i){
+			ret.y += extentFuncArray[i](node, 'top');
+			ret.x += extentFuncArray[i](node, 'left');
+		}
+	}else if(nativeBoxType < targetBoxType){
+		for(var i=targetBoxType;i>nativeBoxType;--i){
+			ret.y -= extentFuncArray[i-1](node, 'top');
+			ret.x -= extentFuncArray[i-1](node, 'left');
+		}
+	}
+	ret.top = ret.y;
+	ret.left = ret.x;
+	return ret;	//	object
+}
+
+dojo.html.isPositionAbsolute = function(/* HTMLElement */node){
+	//	summary
+	//	Returns true if the element is absolutely positioned.
+	return (dojo.html.getComputedStyle(node, 'position') == 'absolute');	//	boolean
+}
+
+dojo.html._sumPixelValues = function(/* HTMLElement */node, selectors, autoIsZero){
+	var total = 0;
+	for(var x=0; x<selectors.length; x++){
+		total += dojo.html.getPixelValue(node, selectors[x], autoIsZero);
+	}
+	return total;
+}
+
+dojo.html.getMargin = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the width and height of the passed node's margin
+	return {
+		width: dojo.html._sumPixelValues(node, ["margin-left", "margin-right"], (dojo.html.getComputedStyle(node, 'position') == 'absolute')),
+		height: dojo.html._sumPixelValues(node, ["margin-top", "margin-bottom"], (dojo.html.getComputedStyle(node, 'position') == 'absolute'))
+	};	//	object
+}
+
+dojo.html.getBorder = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the width and height of the passed node's border
+	return {
+		width: dojo.html.getBorderExtent(node, 'left') + dojo.html.getBorderExtent(node, 'right'),
+		height: dojo.html.getBorderExtent(node, 'top') + dojo.html.getBorderExtent(node, 'bottom')
+	};	//	object
+}
+
+dojo.html.getBorderExtent = function(/* HTMLElement */node, /* string */side){
+	//	summary
+	//	returns the width of the requested border
+	return (dojo.html.getStyle(node, 'border-' + side + '-style') == 'none' ? 0 : dojo.html.getPixelValue(node, 'border-' + side + '-width'));	// integer
+}
+
+dojo.html.getMarginExtent = function(/* HTMLElement */node, /* string */side){
+	//	summary
+	//	returns the width of the requested margin
+	return dojo.html._sumPixelValues(node, ["margin-" + side], dojo.html.isPositionAbsolute(node));	//	integer
+}
+
+dojo.html.getPaddingExtent = function(/* HTMLElement */node, /* string */side){
+	//	summary
+	//	Returns the width of the requested padding 
+	return dojo.html._sumPixelValues(node, ["padding-" + side], true);	//	integer
+}
+
+dojo.html.getPadding = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the width and height of the passed node's padding
+	return {
+		width: dojo.html._sumPixelValues(node, ["padding-left", "padding-right"], true),
+		height: dojo.html._sumPixelValues(node, ["padding-top", "padding-bottom"], true)
+	};	//	object
+}
+
+dojo.html.getPadBorder = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the width and height of the passed node's padding and border
+	var pad = dojo.html.getPadding(node);
+	var border = dojo.html.getBorder(node);
+	return { width: pad.width + border.width, height: pad.height + border.height };	//	object
+}
+
+dojo.html.getBoxSizing = function(/* HTMLElement */node){
+	//	summary
+	//	Returns which box model the passed element is working with
+	var h = dojo.render.html;
+	var bs = dojo.html.boxSizing;
+	if((h.ie)||(h.opera)){ 
+		var cm = document["compatMode"];
+		if((cm == "BackCompat")||(cm == "QuirksMode")){ 
+			return bs.BORDER_BOX; 	//	string
+		}else{
+			return bs.CONTENT_BOX; 	//	string
+		}
+	}else{
+		if(arguments.length == 0){ node = document.documentElement; }
+		var sizing = dojo.html.getStyle(node, "-moz-box-sizing");
+		if(!sizing){ sizing = dojo.html.getStyle(node, "box-sizing"); }
+		return (sizing ? sizing : bs.CONTENT_BOX);	//	string
+	}
+}
+
+dojo.html.isBorderBox = function(/* HTMLElement */node){
+	//	summary
+	//	returns whether the passed element is using border box sizing or not.
+	return (dojo.html.getBoxSizing(node) == dojo.html.boxSizing.BORDER_BOX);	//	boolean
+}
+
+dojo.html.getBorderBox = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the dimensions of the passed element based on border-box sizing.
+	node = dojo.byId(node);
+	return { width: node.offsetWidth, height: node.offsetHeight };	//	object
+}
+
+dojo.html.getPaddingBox = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the dimensions of the padding box (see http://www.w3.org/TR/CSS21/box.html)
+	var box = dojo.html.getBorderBox(node);
+	var border = dojo.html.getBorder(node);
+	return {
+		width: box.width - border.width,
+		height:box.height - border.height
+	};	//	object
+}
+
+dojo.html.getContentBox = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the dimensions of the content box (see http://www.w3.org/TR/CSS21/box.html)
+	node = dojo.byId(node);
+	var padborder = dojo.html.getPadBorder(node);
+	return {
+		width: node.offsetWidth - padborder.width,
+		height: node.offsetHeight - padborder.height
+	};	//	object
+}
+
+dojo.html.setContentBox = function(/* HTMLElement */node, /* object */args){
+	//	summary
+	//	Sets the dimensions of the passed node according to content sizing.
+	node = dojo.byId(node);
+	var width = 0; var height = 0;
+	var isbb = dojo.html.isBorderBox(node);
+	var padborder = (isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0});
+	var ret = {};
+	if(typeof args.width != "undefined"){
+		width = args.width + padborder.width;
+		ret.width = dojo.html.setPositivePixelValue(node, "width", width);
+	}
+	if(typeof args.height != "undefined"){
+		height = args.height + padborder.height;
+		ret.height = dojo.html.setPositivePixelValue(node, "height", height);
+	}
+	return ret;	//	object
+}
+
+dojo.html.getMarginBox = function(/* HTMLElement */node){
+	//	summary
+	//	returns the dimensions of the passed node including any margins.
+	var borderbox = dojo.html.getBorderBox(node);
+	var margin = dojo.html.getMargin(node);
+	return { width: borderbox.width + margin.width, height: borderbox.height + margin.height };	//	object
+}
+
+dojo.html.setMarginBox = function(/* HTMLElement */node, /* object */args){
+	//	summary
+	//	Sets the dimensions of the passed node using margin box calcs.
+	node = dojo.byId(node);
+	var width = 0; var height = 0;
+	var isbb = dojo.html.isBorderBox(node);
+	var padborder = (!isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0 });
+	var margin = dojo.html.getMargin(node);
+	var ret = {};
+	if(typeof args.width != "undefined"){
+		width = args.width - padborder.width;
+		width -= margin.width;
+		ret.width = dojo.html.setPositivePixelValue(node, "width", width);
+	}
+	if(typeof args.height != "undefined"){
+		height = args.height - padborder.height;
+		height -= margin.height;
+		ret.height = dojo.html.setPositivePixelValue(node, "height", height);
+	}
+	return ret;	//	object
+}
+
+dojo.html.getElementBox = function(/* HTMLElement */node, /* string */type){
+	//	summary
+	//	return dimesions of a node based on the passed box model type.
+	var bs = dojo.html.boxSizing;
+	switch(type){
+		case bs.MARGIN_BOX:
+			return dojo.html.getMarginBox(node);	//	object
+		case bs.BORDER_BOX:
+			return dojo.html.getBorderBox(node);	//	object
+		case bs.PADDING_BOX:
+			return dojo.html.getPaddingBox(node);	//	object
+		case bs.CONTENT_BOX:
+		default:
+			return dojo.html.getContentBox(node);	//	object
+	}
+}
+// in: coordinate array [x,y,w,h] or dom node
+// return: coordinate object
+dojo.html.toCoordinateObject = dojo.html.toCoordinateArray = function(/* array */coords, /* boolean? */includeScroll, /* string? */boxtype) {
+	//	summary
+	//	Converts an array of coordinates into an object of named arguments.
+	if(coords instanceof Array || typeof coords == "array"){
+		dojo.deprecated("dojo.html.toCoordinateArray", "use dojo.html.toCoordinateObject({left: , top: , width: , height: }) instead", "0.5");
+		// coords is already an array (of format [x,y,w,h]), just return it
+		while ( coords.length < 4 ) { coords.push(0); }
+		while ( coords.length > 4 ) { coords.pop(); }
+		var ret = {
+			left: coords[0],
+			top: coords[1],
+			width: coords[2],
+			height: coords[3]
+		};
+	}else if(!coords.nodeType && !(coords instanceof String || typeof coords == "string") &&
+			 ('width' in coords || 'height' in coords || 'left' in coords ||
+			  'x' in coords || 'top' in coords || 'y' in coords)){
+		// coords is a coordinate object or at least part of one
+		var ret = {
+			left: coords.left||coords.x||0,
+			top: coords.top||coords.y||0,
+			width: coords.width||0,
+			height: coords.height||0
+		};
+	}else{
+		// coords is an dom object (or dom object id); return it's coordinates
+		var node = dojo.byId(coords);
+		var pos = dojo.html.abs(node, includeScroll, boxtype);
+		var marginbox = dojo.html.getMarginBox(node);
+		var ret = {
+			left: pos.left,
+			top: pos.top,
+			width: marginbox.width,
+			height: marginbox.height
+		};
+	}
+	ret.x = ret.left;
+	ret.y = ret.top;
+	return ret;	//	object
+}
+
+dojo.html.setMarginBoxWidth = dojo.html.setOuterWidth = function(node, width){
+	return dojo.html._callDeprecated("setMarginBoxWidth", "setMarginBox", arguments, "width");
+}
+dojo.html.setMarginBoxHeight = dojo.html.setOuterHeight = function(){
+	return dojo.html._callDeprecated("setMarginBoxHeight", "setMarginBox", arguments, "height");
+}
+dojo.html.getMarginBoxWidth = dojo.html.getOuterWidth = function(){
+	return dojo.html._callDeprecated("getMarginBoxWidth", "getMarginBox", arguments, null, "width");
+}
+dojo.html.getMarginBoxHeight = dojo.html.getOuterHeight = function(){
+	return dojo.html._callDeprecated("getMarginBoxHeight", "getMarginBox", arguments, null, "height");
+}
+dojo.html.getTotalOffset = function(node, type, includeScroll){
+	return dojo.html._callDeprecated("getTotalOffset", "getAbsolutePosition", arguments, null, type);
+}
+dojo.html.getAbsoluteX = function(node, includeScroll){
+	return dojo.html._callDeprecated("getAbsoluteX", "getAbsolutePosition", arguments, null, "x");
+}
+dojo.html.getAbsoluteY = function(node, includeScroll){
+	return dojo.html._callDeprecated("getAbsoluteY", "getAbsolutePosition", arguments, null, "y");
+}
+dojo.html.totalOffsetLeft = function(node, includeScroll){
+	return dojo.html._callDeprecated("totalOffsetLeft", "getAbsolutePosition", arguments, null, "left");
+}
+dojo.html.totalOffsetTop = function(node, includeScroll){
+	return dojo.html._callDeprecated("totalOffsetTop", "getAbsolutePosition", arguments, null, "top");
+}
+dojo.html.getMarginWidth = function(node){
+	return dojo.html._callDeprecated("getMarginWidth", "getMargin", arguments, null, "width");
+}
+dojo.html.getMarginHeight = function(node){
+	return dojo.html._callDeprecated("getMarginHeight", "getMargin", arguments, null, "height");
+}
+dojo.html.getBorderWidth = function(node){
+	return dojo.html._callDeprecated("getBorderWidth", "getBorder", arguments, null, "width");
+}
+dojo.html.getBorderHeight = function(node){
+	return dojo.html._callDeprecated("getBorderHeight", "getBorder", arguments, null, "height");
+}
+dojo.html.getPaddingWidth = function(node){
+	return dojo.html._callDeprecated("getPaddingWidth", "getPadding", arguments, null, "width");
+}
+dojo.html.getPaddingHeight = function(node){
+	return dojo.html._callDeprecated("getPaddingHeight", "getPadding", arguments, null, "height");
+}
+dojo.html.getPadBorderWidth = function(node){
+	return dojo.html._callDeprecated("getPadBorderWidth", "getPadBorder", arguments, null, "width");
+}
+dojo.html.getPadBorderHeight = function(node){
+	return dojo.html._callDeprecated("getPadBorderHeight", "getPadBorder", arguments, null, "height");
+}
+dojo.html.getBorderBoxWidth = dojo.html.getInnerWidth = function(){
+	return dojo.html._callDeprecated("getBorderBoxWidth", "getBorderBox", arguments, null, "width");
+}
+dojo.html.getBorderBoxHeight = dojo.html.getInnerHeight = function(){
+	return dojo.html._callDeprecated("getBorderBoxHeight", "getBorderBox", arguments, null, "height");
+}
+dojo.html.getContentBoxWidth = dojo.html.getContentWidth = function(){
+	return dojo.html._callDeprecated("getContentBoxWidth", "getContentBox", arguments, null, "width");
+}
+dojo.html.getContentBoxHeight = dojo.html.getContentHeight = function(){
+	return dojo.html._callDeprecated("getContentBoxHeight", "getContentBox", arguments, null, "height");
+}
+dojo.html.setContentBoxWidth = dojo.html.setContentWidth = function(node, width){
+	return dojo.html._callDeprecated("setContentBoxWidth", "setContentBox", arguments, "width");
+}
+dojo.html.setContentBoxHeight = dojo.html.setContentHeight = function(node, height){
+	return dojo.html._callDeprecated("setContentBoxHeight", "setContentBox", arguments, "height");
+}
+
+dojo.provide("dojo.lfx.html");
+
+
+dojo.lfx.html._byId = function(nodes){
+	if(!nodes){ return []; }
+	if(dojo.lang.isArrayLike(nodes)){
+		if(!nodes.alreadyChecked){
+			var n = [];
+			dojo.lang.forEach(nodes, function(node){
+				n.push(dojo.byId(node));
+			});
+			n.alreadyChecked = true;
+			return n;
+		}else{
+			return nodes;
+		}
+	}else{
+		var n = [];
+		n.push(dojo.byId(nodes));
+		n.alreadyChecked = true;
+		return n;
+	}
+}
+
+dojo.lfx.html.propertyAnimation = function(	/*DOMNode[]*/ nodes, 
+											/*Object[]*/ propertyMap, 
+											/*int*/ duration,
+											/*function*/ easing,
+											/*Object*/ handlers){
+	// summary: Returns an animation that will transition the properties of "nodes"
+	//			depending how they are defined in "propertyMap".
+	// nodes: An array of DOMNodes or one DOMNode.
+	// propertyMap: { property: String, start: Decimal?, end: Decimal?, units: String? }
+	//				An array of objects defining properties to change.
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// handlers: { handler: Function?, onstart: Function?, onstop: Function?, onanimate: Function? }
+	nodes = dojo.lfx.html._byId(nodes);
+
+	var targs = {
+		"propertyMap": propertyMap,
+		"nodes": nodes,
+		"duration": duration,
+		"easing": easing||dojo.lfx.easeDefault
+	};
+	
+	var setEmUp = function(args){
+		if(args.nodes.length==1){
+			// FIXME: we're only supporting start-value filling when one node is
+			// passed
+			
+			var pm = args.propertyMap;
+			if(!dojo.lang.isArray(args.propertyMap)){
+				// it's stupid to have to pack an array with a set of objects
+				// when you can just pass in an object list
+				var parr = [];
+				for(var pname in pm){
+					pm[pname].property = pname;
+					parr.push(pm[pname]);
+				}
+				pm = args.propertyMap = parr;
+			}
+			dojo.lang.forEach(pm, function(prop){
+				if(dj_undef("start", prop)){
+					if(prop.property != "opacity"){
+						prop.start = parseInt(dojo.html.getComputedStyle(args.nodes[0], prop.property));
+					}else{
+						prop.start = dojo.html.getOpacity(args.nodes[0]);
+					}
+				}
+			});
+		}
+	}
+
+	var coordsAsInts = function(coords){
+		var cints = [];
+		dojo.lang.forEach(coords, function(c){ 
+			cints.push(Math.round(c));
+		});
+		return cints;
+	}
+
+	var setStyle = function(n, style){
+		n = dojo.byId(n);
+		if(!n || !n.style){ return; }
+		for(var s in style){
+			if(s == "opacity"){
+				dojo.html.setOpacity(n, style[s]);
+			}else{
+				n.style[s] = style[s];
+			}
+		}
+	}
+
+	var propLine = function(properties){
+		this._properties = properties;
+		this.diffs = new Array(properties.length);
+		dojo.lang.forEach(properties, function(prop, i){
+			// calculate the end - start to optimize a bit
+			if(dojo.lang.isFunction(prop.start)){
+				prop.start = prop.start(prop, i);
+			}
+			if(dojo.lang.isFunction(prop.end)){
+				prop.end = prop.end(prop, i);
+			}
+			if(dojo.lang.isArray(prop.start)){
+				// don't loop through the arrays
+				this.diffs[i] = null;
+			}else if(prop.start instanceof dojo.gfx.color.Color){
+				// save these so we don't have to call toRgb() every getValue() call
+				prop.startRgb = prop.start.toRgb();
+				prop.endRgb = prop.end.toRgb();
+			}else{
+				this.diffs[i] = prop.end - prop.start;
+			}
+		}, this);
+
+		this.getValue = function(n){
+			var ret = {};
+			dojo.lang.forEach(this._properties, function(prop, i){
+				var value = null;
+				if(dojo.lang.isArray(prop.start)){
+					// FIXME: what to do here?
+				}else if(prop.start instanceof dojo.gfx.color.Color){
+					value = (prop.units||"rgb") + "(";
+					for(var j = 0 ; j < prop.startRgb.length ; j++){
+						value += Math.round(((prop.endRgb[j] - prop.startRgb[j]) * n) + prop.startRgb[j]) + (j < prop.startRgb.length - 1 ? "," : "");
+					}
+					value += ")";
+				}else{
+					value = ((this.diffs[i]) * n) + prop.start + (prop.property != "opacity" ? prop.units||"px" : "");
+				}
+				ret[dojo.html.toCamelCase(prop.property)] = value;
+			}, this);
+			return ret;
+		}
+	}
+	
+	var anim = new dojo.lfx.Animation({
+			beforeBegin: function(){ 
+				setEmUp(targs); 
+				anim.curve = new propLine(targs.propertyMap);
+			},
+			onAnimate: function(propValues){
+				dojo.lang.forEach(targs.nodes, function(node){
+					setStyle(node, propValues);
+				});
+			}
+		},
+		targs.duration, 
+		null,
+		targs.easing
+	);
+	if(handlers){
+		for(var x in handlers){
+			if(dojo.lang.isFunction(handlers[x])){
+				anim.connect(x, anim, handlers[x]);
+			}
+		}
+	}
+	
+	return anim; // dojo.lfx.Animation
+}
+
+dojo.lfx.html._makeFadeable = function(nodes){
+	var makeFade = function(node){
+		if(dojo.render.html.ie){
+			// only set the zoom if the "tickle" value would be the same as the
+			// default
+			if( (node.style.zoom.length == 0) &&
+				(dojo.html.getStyle(node, "zoom") == "normal") ){
+				// make sure the node "hasLayout"
+				// NOTE: this has been tested with larger and smaller user-set text
+				// sizes and works fine
+				node.style.zoom = "1";
+				// node.style.zoom = "normal";
+			}
+			// don't set the width to auto if it didn't already cascade that way.
+			// We don't want to f anyones designs
+			if(	(node.style.width.length == 0) &&
+				(dojo.html.getStyle(node, "width") == "auto") ){
+				node.style.width = "auto";
+			}
+		}
+	}
+	if(dojo.lang.isArrayLike(nodes)){
+		dojo.lang.forEach(nodes, makeFade);
+	}else{
+		makeFade(nodes);
+	}
+}
+
+dojo.lfx.html.fade = function(/*DOMNode[]*/ nodes,
+							  /*Object*/values,
+							  /*int?*/ duration,
+							  /*Function?*/ easing,
+							  /*Function?*/ callback){
+	// summary:Returns an animation that will fade the "nodes" from the start to end values passed.
+	// nodes: An array of DOMNodes or one DOMNode.
+	// values: { start: Decimal?, end: Decimal? }
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	nodes = dojo.lfx.html._byId(nodes);
+	var props = { property: "opacity" };
+	if(!dj_undef("start", values)){
+		props.start = values.start;
+	}else{
+		props.start = function(){ return dojo.html.getOpacity(nodes[0]); };
+	}
+
+	if(!dj_undef("end", values)){
+		props.end = values.end;
+	}else{
+		dojo.raise("dojo.lfx.html.fade needs an end value");
+	}
+
+	var anim = dojo.lfx.propertyAnimation(nodes, [ props ], duration, easing);
+	anim.connect("beforeBegin", function(){
+		dojo.lfx.html._makeFadeable(nodes);
+	});
+	if(callback){
+		anim.connect("onEnd", function(){ callback(nodes, anim); });
+	}
+
+	return anim; // dojo.lfx.Animation
+}
+
+dojo.lfx.html.fadeIn = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
+	// summary: Returns an animation that will fade "nodes" from its current opacity to fully opaque.
+	// nodes: An array of DOMNodes or one DOMNode.
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	return dojo.lfx.html.fade(nodes, { end: 1 }, duration, easing, callback); // dojo.lfx.Animation
+}
+
+dojo.lfx.html.fadeOut = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
+	// summary: Returns an animation that will fade "nodes" from its current opacity to fully transparent.
+	// nodes: An array of DOMNodes or one DOMNode.
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.	
+	return dojo.lfx.html.fade(nodes, { end: 0 }, duration, easing, callback); // dojo.lfx.Animation
+}
+
+dojo.lfx.html.fadeShow = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
+	// summary: Returns an animation that will fade "nodes" from transparent to opaque and shows
+	//			"nodes" at the end if it is hidden.
+	// nodes: An array of DOMNodes or one DOMNode.
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.	
+	nodes=dojo.lfx.html._byId(nodes);
+	dojo.lang.forEach(nodes, function(node){
+		dojo.html.setOpacity(node, 0.0);
+	});
+
+	var anim = dojo.lfx.html.fadeIn(nodes, duration, easing, callback);
+	anim.connect("beforeBegin", function(){ 
+		if(dojo.lang.isArrayLike(nodes)){
+			dojo.lang.forEach(nodes, dojo.html.show);
+		}else{
+			dojo.html.show(nodes);
+		}
+	});
+
+	return anim; // dojo.lfx.Animation
+}
+
+dojo.lfx.html.fadeHide = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
+	// summary: Returns an animation that will fade "nodes" from its current opacity to opaque and hides
+	//			"nodes" at the end.
+	// nodes: An array of DOMNodes or one DOMNode.
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	var anim = dojo.lfx.html.fadeOut(nodes, duration, easing, function(){
+		if(dojo.lang.isArrayLike(nodes)){
+			dojo.lang.forEach(nodes, dojo.html.hide);
+		}else{
+			dojo.html.hide(nodes);
+		}
+		if(callback){ callback(nodes, anim); }
+	});
+	
+	return anim; // dojo.lfx.Animation
+}
+
+dojo.lfx.html.wipeIn = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
+	// summary: Returns an animation that will show and wipe in "nodes".
+	// nodes: An array of DOMNodes or one DOMNode.
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	nodes = dojo.lfx.html._byId(nodes);
+	var anims = [];
+
+	dojo.lang.forEach(nodes, function(node){
+		var oprop = {  };	// old properties of node (before we mucked w/them)
+		
+		// get node height, either it's natural height or it's height specified via style or class attributes
+		// (for FF, the node has to be (temporarily) rendered to measure height)
+		dojo.html.show(node);
+		var height = dojo.html.getBorderBox(node).height;
+		dojo.html.hide(node);
+
+		var anim = dojo.lfx.propertyAnimation(node,
+			{	"height": {
+					start: 1, // 0 causes IE to display the whole panel
+					end: function(){ return height; } 
+				}
+			}, 
+			duration, 
+			easing);
+	
+		anim.connect("beforeBegin", function(){
+			oprop.overflow = node.style.overflow;
+			oprop.height = node.style.height;
+			with(node.style){
+				overflow = "hidden";
+				height = "1px"; // 0 causes IE to display the whole panel
+			}
+			dojo.html.show(node);
+		});
+		
+		anim.connect("onEnd", function(){ 
+			with(node.style){
+				overflow = oprop.overflow;
+				height = oprop.height;
+			}
+			if(callback){ callback(node, anim); }
+		});
+		anims.push(anim);
+	});
+	
+	return dojo.lfx.combine(anims); // dojo.lfx.Combine
+}
+
+dojo.lfx.html.wipeOut = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
+	// summary: Returns an animation that will wipe out and hide "nodes".
+	// nodes: An array of DOMNodes or one DOMNode.
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	nodes = dojo.lfx.html._byId(nodes);
+	var anims = [];
+	
+	dojo.lang.forEach(nodes, function(node){
+		var oprop = {  };	// old properties of node (before we mucked w/them)
+		var anim = dojo.lfx.propertyAnimation(node,
+			{	"height": {
+					start: function(){ return dojo.html.getContentBox(node).height; },
+					end: 1 // 0 causes IE to display the whole panel
+				} 
+			},
+			duration,
+			easing,
+			{
+				"beforeBegin": function(){
+					oprop.overflow = node.style.overflow;
+					oprop.height = node.style.height;
+					with(node.style){
+						overflow = "hidden";
+					}
+					dojo.html.show(node);
+				},
+				
+				"onEnd": function(){ 
+					dojo.html.hide(node);
+					with(node.style){
+						overflow = oprop.overflow;
+						height = oprop.height;
+					}
+					if(callback){ callback(node, anim); }
+				}
+			}
+		);
+		anims.push(anim);
+	});
+
+	return dojo.lfx.combine(anims); // dojo.lfx.Combine
+}
+
+dojo.lfx.html.slideTo = function(/*DOMNode*/ nodes,
+								 /*Object*/ coords,
+								 /*int?*/ duration,
+								 /*Function?*/ easing,
+								 /*Function?*/ callback){
+	// summary: Returns an animation that will slide "nodes" from its current position to
+	//			the position defined in "coords".
+	// nodes: An array of DOMNodes or one DOMNode.
+	// coords: { top: Decimal?, left: Decimal? }
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	nodes = dojo.lfx.html._byId(nodes);
+	var anims = [];
+	var compute = dojo.html.getComputedStyle;
+	
+	if(dojo.lang.isArray(coords)){
+		/* coords: Array
+		   pId: a */
+		dojo.deprecated('dojo.lfx.html.slideTo(node, array)', 'use dojo.lfx.html.slideTo(node, {top: value, left: value});', '0.5');
+		coords = { top: coords[0], left: coords[1] };
+	}
+	dojo.lang.forEach(nodes, function(node){
+		var top = null;
+		var left = null;
+		
+		var init = (function(){
+			var innerNode = node;
+			return function(){
+				var pos = compute(innerNode, 'position');
+				top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0);
+				left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0);
+
+				if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
+					var ret = dojo.html.abs(innerNode, true);
+					dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
+					top = ret.y;
+					left = ret.x;
+				}
+			}
+		})();
+		init();
+		
+		var anim = dojo.lfx.propertyAnimation(node,
+			{	"top": { start: top, end: (coords.top||0) },
+				"left": { start: left, end: (coords.left||0)  }
+			},
+			duration,
+			easing,
+			{ "beforeBegin": init }
+		);
+
+		if(callback){
+			anim.connect("onEnd", function(){ callback(nodes, anim); });
+		}
+
+		anims.push(anim);
+	});
+	
+	return dojo.lfx.combine(anims); // dojo.lfx.Combine
+}
+
+dojo.lfx.html.slideBy = function(/*DOMNode*/ nodes, /*Object*/ coords, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
+	// summary: Returns an animation that will slide "nodes" from its current position
+	//			to its current position plus the numbers defined in "coords".
+	// nodes: An array of DOMNodes or one DOMNode.
+	// coords: { top: Decimal?, left: Decimal? }
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	nodes = dojo.lfx.html._byId(nodes);
+	var anims = [];
+	var compute = dojo.html.getComputedStyle;
+
+	if(dojo.lang.isArray(coords)){
+		/* coords: Array
+		   pId: a */
+		dojo.deprecated('dojo.lfx.html.slideBy(node, array)', 'use dojo.lfx.html.slideBy(node, {top: value, left: value});', '0.5');
+		coords = { top: coords[0], left: coords[1] };
+	}
+
+	dojo.lang.forEach(nodes, function(node){
+		var top = null;
+		var left = null;
+		
+		var init = (function(){
+			var innerNode = node;
+			return function(){
+				var pos = compute(innerNode, 'position');
+				top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0);
+				left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0);
+
+				if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
+					var ret = dojo.html.abs(innerNode, true);
+					dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
+					top = ret.y;
+					left = ret.x;
+				}
+			}
+		})();
+		init();
+		
+		var anim = dojo.lfx.propertyAnimation(node,
+			{
+				"top": { start: top, end: top+(coords.top||0) },
+				"left": { start: left, end: left+(coords.left||0) }
+			},
+			duration,
+			easing).connect("beforeBegin", init);
+
+		if(callback){
+			anim.connect("onEnd", function(){ callback(nodes, anim); });
+		}
+
+		anims.push(anim);
+	});
+
+	return dojo.lfx.combine(anims); // dojo.lfx.Combine
+}
+
+dojo.lfx.html.explode = function(/*DOMNode*/ start,
+								 /*DOMNode*/ endNode,
+								 /*int?*/ duration,
+								 /*Function?*/ easing,
+								 /*Function?*/ callback){
+	// summary: Returns an animation that will 
+	// start:
+	// endNode:
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	var h = dojo.html;
+	start = dojo.byId(start);
+	endNode = dojo.byId(endNode);
+	var startCoords = h.toCoordinateObject(start, true);
+	var outline = document.createElement("div");
+	h.copyStyle(outline, endNode);
+	if (endNode.explodeClassName) { outline.className = endNode.explodeClassName; }
+	with(outline.style){
+		position = "absolute";
+		display = "none";
+		// border = "1px solid black";
+	}
+	dojo.body().appendChild(outline);
+
+	with(endNode.style){
+		visibility = "hidden";
+		display = "block";
+	}
+	var endCoords = h.toCoordinateObject(endNode, true);
+	with(endNode.style){
+		display = "none";
+		visibility = "visible";
+	}
+
+	var props = { opacity: { start: 0.5, end: 1.0 } };
+	dojo.lang.forEach(["height", "width", "top", "left"], function(type){
+		props[type] = { start: startCoords[type], end: endCoords[type] }
+	});
+	
+	var anim = new dojo.lfx.propertyAnimation(outline, 
+		props,
+		duration,
+		easing,
+		{
+			"beforeBegin": function(){
+				h.setDisplay(outline, "block");
+			},
+			"onEnd": function(){
+				h.setDisplay(endNode, "block");
+				outline.parentNode.removeChild(outline);
+			}
+		}
+	);
+
+	if(callback){
+		anim.connect("onEnd", function(){ callback(endNode, anim); });
+	}
+	return anim; // dojo.lfx.Animation
+}
+
+dojo.lfx.html.implode = function(/*DOMNode*/ startNode,
+								 /*DOMNode*/ end,
+								 /*int?*/ duration,
+								 /*Function?*/ easing,
+								 /*Function?*/ callback){
+	// summary: Returns an animation that will 
+	// startNode:
+	// end:
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	var h = dojo.html;
+	startNode = dojo.byId(startNode);
+	end = dojo.byId(end);
+	var startCoords = dojo.html.toCoordinateObject(startNode, true);
+	var endCoords = dojo.html.toCoordinateObject(end, true);
+
+	var outline = document.createElement("div");
+	dojo.html.copyStyle(outline, startNode);
+	if (startNode.explodeClassName) { outline.className = startNode.explodeClassName; }
+	dojo.html.setOpacity(outline, 0.3);
+	with(outline.style){
+		position = "absolute";
+		display = "none";
+		backgroundColor = h.getStyle(startNode, "background-color").toLowerCase();
+	}
+	dojo.body().appendChild(outline);
+
+	var props = { opacity: { start: 1.0, end: 0.5 } };
+	dojo.lang.forEach(["height", "width", "top", "left"], function(type){
+		props[type] = { start: startCoords[type], end: endCoords[type] }
+	});
+	
+	var anim = new dojo.lfx.propertyAnimation(outline,
+		props,
+		duration,
+		easing,
+		{
+			"beforeBegin": function(){
+				dojo.html.hide(startNode);
+				dojo.html.show(outline);
+			},
+			"onEnd": function(){
+				outline.parentNode.removeChild(outline);
+			}
+		}
+	);
+
+	if(callback){
+		anim.connect("onEnd", function(){ callback(startNode, anim); });
+	}
+	return anim; // dojo.lfx.Animation
+}
+
+dojo.lfx.html.highlight = function(/*DOMNode[]*/ nodes,
+								   /*dojo.gfx.color.Color*/ startColor,
+								   /*int?*/ duration,
+								   /*Function?*/ easing,
+								   /*Function?*/ callback){
+	// summary: Returns an animation that will set the background color
+	//			of "nodes" to startColor and transition it to "nodes"
+	//			original color.
+	// startColor: Color to transition from.
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	nodes = dojo.lfx.html._byId(nodes);
+	var anims = [];
+
+	dojo.lang.forEach(nodes, function(node){
+		var color = dojo.html.getBackgroundColor(node);
+		var bg = dojo.html.getStyle(node, "background-color").toLowerCase();
+		var bgImage = dojo.html.getStyle(node, "background-image");
+		var wasTransparent = (bg == "transparent" || bg == "rgba(0, 0, 0, 0)");
+		while(color.length > 3) { color.pop(); }
+
+		var rgb = new dojo.gfx.color.Color(startColor);
+		var endRgb = new dojo.gfx.color.Color(color);
+
+		var anim = dojo.lfx.propertyAnimation(node, 
+			{ "background-color": { start: rgb, end: endRgb } }, 
+			duration, 
+			easing,
+			{
+				"beforeBegin": function(){ 
+					if(bgImage){
+						node.style.backgroundImage = "none";
+					}
+					node.style.backgroundColor = "rgb(" + rgb.toRgb().join(",") + ")";
+				},
+				"onEnd": function(){ 
+					if(bgImage){
+						node.style.backgroundImage = bgImage;
+					}
+					if(wasTransparent){
+						node.style.backgroundColor = "transparent";
+					}
+					if(callback){
+						callback(node, anim);
+					}
+				}
+			}
+		);
+
+		anims.push(anim);
+	});
+	return dojo.lfx.combine(anims); // dojo.lfx.Combine
+}
+
+dojo.lfx.html.unhighlight = function(/*DOMNode[]*/ nodes,
+									 /*dojo.gfx.color.Color*/ endColor,
+									 /*int?*/ duration,
+									 /*Function?*/ easing,
+									 /*Function?*/ callback){
+	// summary: Returns an animation that will transition "nodes" background color
+	//			from its current color to "endColor".
+	// endColor: Color to transition to.
+	// duration: Duration of the animation in milliseconds.
+	// easing: An easing function.
+	// callback: Function to run at the end of the animation.
+	nodes = dojo.lfx.html._byId(nodes);
+	var anims = [];
+
+	dojo.lang.forEach(nodes, function(node){
+		var color = new dojo.gfx.color.Color(dojo.html.getBackgroundColor(node));
+		var rgb = new dojo.gfx.color.Color(endColor);
+
+		var bgImage = dojo.html.getStyle(node, "background-image");
+		
+		var anim = dojo.lfx.propertyAnimation(node, 
+			{ "background-color": { start: color, end: rgb } },
+			duration, 
+			easing,
+			{
+				"beforeBegin": function(){ 
+					if(bgImage){
+						node.style.backgroundImage = "none";
+					}
+					node.style.backgroundColor = "rgb(" + color.toRgb().join(",") + ")";
+				},
+				"onEnd": function(){ 
+					if(callback){
+						callback(node, anim);
+					}
+				}
+			}
+		);
+		anims.push(anim);
+	});
+	return dojo.lfx.combine(anims); // dojo.lfx.Combine
+}
+
+dojo.lang.mixin(dojo.lfx, dojo.lfx.html);
+
+dojo.provide("dojo.lfx.*");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/iframe_history.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/iframe_history.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/iframe_history.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,54 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
+	<script type="text/javascript">
+	// <!--
+	var noInit = false;
+	var domain = "";
+	// document.domain = "localhost";
+	function init(){
+		// parse the query string if there is one to try to get args that
+		// we can act on
+		var sparams = document.location.search;
+		if(sparams.length >= 0){
+			if(sparams.charAt(0) == "?"){
+				sparams = sparams.substring(1);
+			}
+			var ss = (sparams.indexOf("&amp;") >= 0) ? "&amp;" : "&";
+			sparams = sparams.split(ss);
+			for(var x=0; x<sparams.length; x++){
+				var tp = sparams[x].split("=");
+				if(typeof window[tp[0]] != "undefined"){
+					window[tp[0]] = ((tp[1]=="true")||(tp[1]=="false")) ? eval(tp[1]) : tp[1];
+				}
+			}
+		}
+
+		if(noInit){ return; }
+		if(domain.length > 0){
+			document.domain = domain;
+		}
+		if((window.parent != window)&&(window.parent["dojo"])){
+			//Set the page title so IE history shows up with a somewhat correct name.
+			document.title = window.parent.document.title;
+			
+			//Notify parent that we are loaded.
+			var pdj = window.parent.dojo;
+			if(pdj["undo"] && pdj["undo"]["browser"]){
+				pdj.undo.browser.iframeLoaded(null, window.location);
+			}
+		}
+	}
+	// -->
+	</script>
+</head>
+<body onload="try{ init(); }catch(e){ alert(e); }">
+	<h4>The Dojo Toolkit -- iframe_history.html</h4>
+
+	<p>This file is used in Dojo's back/fwd button management.</p>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/AdapterRegistry.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/AdapterRegistry.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/AdapterRegistry.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,89 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.AdapterRegistry");
+dojo.require("dojo.lang.func");
+
+dojo.AdapterRegistry = function(/*boolean, optional*/returnWrappers){
+	// summary:
+	//		A registry to make contextual calling/searching easier.
+	// description:
+	//		Objects of this class keep list of arrays in the form [name, check,
+	//		wrap, directReturn] that are used to determine what the contextual
+	//		result of a set of checked arguments is. All check/wrap functions
+	//		in this registry should be of the same arity.
+	this.pairs = [];
+	this.returnWrappers = returnWrappers || false;
+}
+
+dojo.lang.extend(dojo.AdapterRegistry, {
+	register: function(	/*string*/ name, /*function*/ check, /*function*/ wrap, 
+						/*boolean, optional*/ directReturn, 
+						/*boolean, optional*/ override){
+		// summary: 
+		//		register a check function to determine if the wrap function or
+		//		object gets selected
+		// name: a way to identify this matcher.
+		// check:
+		//		a function that arguments are passed to from the adapter's
+		//		match() function.  The check function should return true if the
+		//		given arguments are appropriate for the wrap function.
+		// directReturn:
+		//		If directReturn is true, the value passed in for wrap will be
+		//		returned instead of being called. Alternately, the
+		//		AdapterRegistry can be set globally to "return not call" using
+		//		the returnWrappers property. Either way, this behavior allows
+		//		the registry to act as a "search" function instead of a
+		//		function interception library.
+		// override:
+		//		If override is given and true, the check function will be given
+		//		highest priority. Otherwise, it will be the lowest priority
+		//		adapter.
+
+		var type = (override) ? "unshift" : "push";
+		this.pairs[type]([name, check, wrap, directReturn]);
+	},
+
+	match: function(/* ... */){
+        // summary:
+		//		Find an adapter for the given arguments. If no suitable adapter
+		//		is found, throws an exception. match() accepts any number of
+		//		arguments, all of which are passed to all matching functions
+		//		from the registered pairs.
+		for(var i = 0; i < this.pairs.length; i++){
+			var pair = this.pairs[i];
+			if(pair[1].apply(this, arguments)){
+				if((pair[3])||(this.returnWrappers)){
+					return pair[2];
+				}else{
+					return pair[2].apply(this, arguments);
+				}
+			}
+		}
+		throw new Error("No match found");
+		// dojo.raise("No match found");
+	},
+
+	unregister: function(name){
+		// summary: Remove a named adapter from the registry
+
+		// FIXME: this is kind of a dumb way to handle this. On a large
+		// registry this will be slow-ish and we can use the name as a lookup
+		// should we choose to trade memory for speed.
+		for(var i = 0; i < this.pairs.length; i++){
+			var pair = this.pairs[i];
+			if(pair[0] == name){
+				this.pairs.splice(i, 1);
+				return true;
+			}
+		}
+		return false;
+	}
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/Deferred.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/Deferred.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/Deferred.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,315 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.Deferred");
+dojo.require("dojo.lang.func");
+
+dojo.Deferred = function(/* optional */ canceller){
+	/*
+	NOTE: this namespace and documentation are imported wholesale 
+		from MochiKit
+
+	Encapsulates a sequence of callbacks in response to a value that
+	may not yet be available.  This is modeled after the Deferred class
+	from Twisted <http://twistedmatrix.com>.
+
+	Why do we want this?  JavaScript has no threads, and even if it did,
+	threads are hard.  Deferreds are a way of abstracting non-blocking
+	events, such as the final response to an XMLHttpRequest.
+
+	The sequence of callbacks is internally represented as a list
+	of 2-tuples containing the callback/errback pair.  For example,
+	the following call sequence::
+
+		var d = new Deferred();
+		d.addCallback(myCallback);
+		d.addErrback(myErrback);
+		d.addBoth(myBoth);
+		d.addCallbacks(myCallback, myErrback);
+
+	is translated into a Deferred with the following internal
+	representation::
+
+		[
+			[myCallback, null],
+			[null, myErrback],
+			[myBoth, myBoth],
+			[myCallback, myErrback]
+		]
+
+	The Deferred also keeps track of its current status (fired).
+	Its status may be one of three things:
+
+		-1: no value yet (initial condition)
+		0: success
+		1: error
+
+	A Deferred will be in the error state if one of the following
+	three conditions are met:
+
+		1. The result given to callback or errback is "instanceof" Error
+		2. The previous callback or errback raised an exception while
+		   executing
+		3. The previous callback or errback returned a value "instanceof"
+			Error
+
+	Otherwise, the Deferred will be in the success state.  The state of
+	the Deferred determines the next element in the callback sequence to
+	run.
+
+	When a callback or errback occurs with the example deferred chain,
+	something equivalent to the following will happen (imagine that
+	exceptions are caught and returned)::
+
+		// d.callback(result) or d.errback(result)
+		if(!(result instanceof Error)){
+			result = myCallback(result);
+		}
+		if(result instanceof Error){
+			result = myErrback(result);
+		}
+		result = myBoth(result);
+		if(result instanceof Error){
+			result = myErrback(result);
+		}else{
+			result = myCallback(result);
+		}
+
+	The result is then stored away in case another step is added to the
+	callback sequence.	Since the Deferred already has a value available,
+	any new callbacks added will be called immediately.
+
+	There are two other "advanced" details about this implementation that
+	are useful:
+
+	Callbacks are allowed to return Deferred instances themselves, so you
+	can build complicated sequences of events with ease.
+
+	The creator of the Deferred may specify a canceller.  The canceller
+	is a function that will be called if Deferred.cancel is called before
+	the Deferred fires.	 You can use this to implement clean aborting of
+	an XMLHttpRequest, etc.	 Note that cancel will fire the deferred with
+	a CancelledError (unless your canceller returns another kind of
+	error), so the errbacks should be prepared to handle that error for
+	cancellable Deferreds.
+
+	*/
+	
+	this.chain = [];
+	this.id = this._nextId();
+	this.fired = -1;
+	this.paused = 0;
+	this.results = [null, null];
+	this.canceller = canceller;
+	this.silentlyCancelled = false;
+};
+
+dojo.lang.extend(dojo.Deferred, {
+	getFunctionFromArgs: function(){
+		var a = arguments;
+		if((a[0])&&(!a[1])){
+			if(dojo.lang.isFunction(a[0])){
+				return a[0];
+			}else if(dojo.lang.isString(a[0])){
+				return dj_global[a[0]];
+			}
+		}else if((a[0])&&(a[1])){
+			return dojo.lang.hitch(a[0], a[1]);
+		}
+		return null;
+	},
+
+	makeCalled: function() {
+		var deferred = new dojo.Deferred();
+		deferred.callback();
+		return deferred;
+	},
+
+	repr: function(){
+		var state;
+		if(this.fired == -1){
+			state = 'unfired';
+		}else if(this.fired == 0){
+			state = 'success';
+		} else {
+			state = 'error';
+		}
+		return 'Deferred(' + this.id + ', ' + state + ')';
+	},
+
+	toString: dojo.lang.forward("repr"),
+
+	_nextId: (function(){
+		var n = 1;
+		return function(){ return n++; };
+	})(),
+
+	cancel: function(){
+		/***
+		Cancels a Deferred that has not yet received a value, or is
+		waiting on another Deferred as its value.
+
+		If a canceller is defined, the canceller is called. If the
+		canceller did not return an error, or there was no canceller,
+		then the errback chain is started with CancelledError.
+		***/
+		if(this.fired == -1){
+			if (this.canceller){
+				this.canceller(this);
+			}else{
+				this.silentlyCancelled = true;
+			}
+			if(this.fired == -1){
+				this.errback(new Error(this.repr()));
+			}
+		}else if(	(this.fired == 0)&&
+					(this.results[0] instanceof dojo.Deferred)){
+			this.results[0].cancel();
+		}
+	},
+			
+
+	_pause: function(){
+		// Used internally to signal that it's waiting on another Deferred
+		this.paused++;
+	},
+
+	_unpause: function(){
+		// Used internally to signal that it's no longer waiting on
+		// another Deferred.
+		this.paused--;
+		if ((this.paused == 0) && (this.fired >= 0)) {
+			this._fire();
+		}
+	},
+
+	_continue: function(res){
+		// Used internally when a dependent deferred fires.
+		this._resback(res);
+		this._unpause();
+	},
+
+	_resback: function(res){
+		// The primitive that means either callback or errback
+		this.fired = ((res instanceof Error) ? 1 : 0);
+		this.results[this.fired] = res;
+		this._fire();
+	},
+
+	_check: function(){
+		if(this.fired != -1){
+			if(!this.silentlyCancelled){
+				dojo.raise("already called!");
+			}
+			this.silentlyCancelled = false;
+			return;
+		}
+	},
+
+	callback: function(res){
+		/*
+		Begin the callback sequence with a non-error value.
+		
+		callback or errback should only be called once on a given
+		Deferred.
+		*/
+		this._check();
+		this._resback(res);
+	},
+
+	errback: function(res){
+		// Begin the callback sequence with an error result.
+		this._check();
+		if(!(res instanceof Error)){
+			res = new Error(res);
+		}
+		this._resback(res);
+	},
+
+	addBoth: function(cb, cbfn){
+		/*
+		Add the same function as both a callback and an errback as the
+		next element on the callback sequence.	This is useful for code
+		that you want to guarantee to run, e.g. a finalizer.
+		*/
+		var enclosed = this.getFunctionFromArgs(cb, cbfn);
+		if(arguments.length > 2){
+			enclosed = dojo.lang.curryArguments(null, enclosed, arguments, 2);
+		}
+		return this.addCallbacks(enclosed, enclosed);
+	},
+
+	addCallback: function(cb, cbfn){
+		// Add a single callback to the end of the callback sequence.
+		var enclosed = this.getFunctionFromArgs(cb, cbfn);
+		if(arguments.length > 2){
+			enclosed = dojo.lang.curryArguments(null, enclosed, arguments, 2);
+		}
+		return this.addCallbacks(enclosed, null);
+	},
+
+	addErrback: function(cb, cbfn){
+		// Add a single callback to the end of the callback sequence.
+		var enclosed = this.getFunctionFromArgs(cb, cbfn);
+		if(arguments.length > 2){
+			enclosed = dojo.lang.curryArguments(null, enclosed, arguments, 2);
+		}
+		return this.addCallbacks(null, enclosed);
+		return this.addCallbacks(null, cbfn);
+	},
+
+	addCallbacks: function (cb, eb) {
+		// Add separate callback and errback to the end of the callback
+		// sequence.
+		this.chain.push([cb, eb])
+		if (this.fired >= 0) {
+			this._fire();
+		}
+		return this;
+	},
+
+	_fire: function(){
+		// Used internally to exhaust the callback sequence when a result
+		// is available.
+		var chain = this.chain;
+		var fired = this.fired;
+		var res = this.results[fired];
+		var self = this;
+		var cb = null;
+		while (chain.length > 0 && this.paused == 0) {
+			// Array
+			var pair = chain.shift();
+			var f = pair[fired];
+			if (f == null) {
+				continue;
+			}
+			try {
+				res = f(res);
+				fired = ((res instanceof Error) ? 1 : 0);
+				if(res instanceof dojo.Deferred) {
+					cb = function(res){
+						self._continue(res);
+					}
+					this._pause();
+				}
+			}catch(err){
+				fired = 1;
+				res = err;
+			}
+		}
+		this.fired = fired;
+		this.results[fired] = res;
+		if((cb)&&(this.paused)){
+			// this is for "tail recursion" in case the dependent
+			// deferred is already fired
+			res.addBoth(cb);
+		}
+	}
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/DeferredList.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/DeferredList.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/DeferredList.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,88 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.require("dojo.Deferred");
+
+dojo.provide("dojo.DeferredList");
+
+
+dojo.DeferredList = function (list, /*bool?*/ fireOnOneCallback, /*bool?*/ fireOnOneErrback, /*bool?*/ consumeErrors, /*Function?*/ canceller) {
+    this.list = list;
+    this.resultList = new Array(this.list.length);
+
+    // Deferred init
+    this.chain = [];
+    this.id = this._nextId();
+    this.fired = -1;
+    this.paused = 0;
+    this.results = [null, null];
+    this.canceller = canceller;
+    this.silentlyCancelled = false;
+    
+    if (this.list.length === 0 && !fireOnOneCallback) {
+        this.callback(this.resultList);
+    }
+    
+    this.finishedCount = 0;
+    this.fireOnOneCallback = fireOnOneCallback;
+    this.fireOnOneErrback = fireOnOneErrback;
+    this.consumeErrors = consumeErrors;
+
+    var index = 0;
+    
+    var _this = this;
+    
+    dojo.lang.forEach(this.list, function(d) {
+        var _index = index;
+        //dojo.debug("add cb/errb index "+_index);
+        d.addCallback(function(r) { _this._cbDeferred(_index, true, r) });
+        d.addErrback(function(r) { _this._cbDeferred(_index, false, r) });
+        index++;
+    });
+                      
+};
+
+
+dojo.inherits(dojo.DeferredList, dojo.Deferred);
+
+dojo.lang.extend(dojo.DeferredList, {
+
+    _cbDeferred: function (index, succeeded, result) {
+        //dojo.debug("Fire "+index+" succ "+succeeded+" res "+result);
+        this.resultList[index] = [succeeded, result];
+        this.finishedCount += 1;
+        if (this.fired !== 0) {
+            if (succeeded && this.fireOnOneCallback) {
+                this.callback([index, result]);
+            } else if (!succeeded && this.fireOnOneErrback) {
+                this.errback(result);
+            } else if (this.finishedCount == this.list.length) {
+                this.callback(this.resultList);
+            }
+        }
+        if (!succeeded && this.consumeErrors) {
+            result = null;
+        }
+        return result;
+    },
+    
+    gatherResults: function (deferredList) {
+        var d = new dojo.DeferredList(deferredList, false, true, false);
+        d.addCallback(function (results) {
+            var ret = [];
+            for (var i = 0; i < results.length; i++) {
+                ret.push(results[i][1]);
+            }
+            return ret;
+        });
+        return d;
+    }
+});
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/a11y.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/a11y.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/a11y.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,103 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.a11y");
+
+dojo.require("dojo.uri.*");
+dojo.require("dojo.html.common");
+
+dojo.a11y = {
+	// imgPath: String path to the test image for determining if images are displayed or not
+	// doAccessibleCheck: Boolean if true will perform check for need to create accessible widgets
+	// accessible: Boolean uninitialized when null (accessible check has not been performed)
+	//   if true generate accessible widgets
+	imgPath:dojo.uri.dojoUri("src/widget/templates/images"),
+	doAccessibleCheck: true,
+	accessible: null,		
+
+	checkAccessible: function(){ 
+	// summary: 
+	//		perform check for accessibility if accessibility checking is turned
+	//		on and the accessibility test has not been performed yet
+		if(this.accessible === null){ 
+			this.accessible = false; //default
+			if(this.doAccessibleCheck == true){ 
+				this.accessible = this.testAccessible();
+			}
+		}
+		return this.accessible; /* Boolean */
+	},
+	
+	testAccessible: function(){
+	// summary: 
+	//		Always perform the accessibility check to determine if high 
+	//		contrast mode is on or display of images are turned off. Currently only checks 
+	//		in IE and Mozilla. 
+		this.accessible = false; //default
+		if (dojo.render.html.ie || dojo.render.html.mozilla){
+			var div = document.createElement("div");
+			//div.style.color="rgb(153,204,204)";
+			div.style.backgroundImage = "url(\"" + this.imgPath + "/tab_close.gif\")";
+			// must add to hierarchy before can view currentStyle below
+			dojo.body().appendChild(div);
+			// in FF and IE the value for the current background style of the added div
+			// will be "none" in high contrast mode
+			// in FF the return value will be url(invalid-url:) when running over http 
+			var bkImg = null;
+			if (window.getComputedStyle  ) {
+				var cStyle = getComputedStyle(div, ""); 
+				bkImg = cStyle.getPropertyValue("background-image");
+			}else{
+				bkImg = div.currentStyle.backgroundImage;
+			}
+			var bUseImgElem = false;
+			if (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" )) {
+				this.accessible = true;
+			}
+			/*
+			if(this.accessible == false && document.images){
+				// test if images are off in IE
+				var testImg = new Image();
+				if(testImg.fileSize) {
+					testImg.src = this.imgPath + "/tab_close.gif";
+					if(testImg.fileSize < 0){ 
+						this.accessible = true;
+					}
+				}	
+			}*/
+			dojo.body().removeChild(div);
+		}
+		return this.accessible; /* Boolean */
+	},
+	
+	setCheckAccessible: function(/* Boolean */ bTest){ 
+	// summary: 
+	//		Set whether or not to check for accessibility mode.  Default value
+	//		of module is true - perform check for accessibility modes. 
+	//		bTest: Boolean - true to check; false to turn off checking
+		this.doAccessibleCheck = bTest;
+	},
+
+	setAccessibleMode: function(){
+	// summary:
+	//		perform the accessibility check and sets the correct mode to load 
+	//		a11y widgets. Only runs if test for accessiiblity has not been performed yet. 
+	//		Call testAccessible() to force the test.
+		if (this.accessible === null){
+			if (this.checkAccessible()){
+				dojo.render.html.prefixes.unshift("a11y");
+			}
+		}
+		return this.accessible; /* Boolean */
+	}
+};
+
+//dojo.hostenv.modulesLoadedListeners.unshift(function() { dojo.a11y.setAccessibleMode(); });
+//dojo.event.connect("before", dojo.hostenv, "makeWidgets", dojo.a11y, "setAccessibleMode");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/animation.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/animation.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/animation.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,14 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.animation");
+dojo.require("dojo.animation.Animation");
+
+dojo.deprecated("dojo.animation is slated for removal in 0.5; use dojo.lfx instead.", "0.5");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/behavior.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/behavior.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/behavior.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,248 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.behavior");
+dojo.require("dojo.event.*");
+
+dojo.require("dojo.experimental");
+dojo.experimental("dojo.behavior");
+
+dojo.behavior = new function(){
+	function arrIn(obj, name){
+		if(!obj[name]){ obj[name] = []; }
+		return obj[name];
+	}
+
+	function forIn(obj, scope, func){
+		var tmpObj = {};
+		for(var x in obj){
+			if(typeof tmpObj[x] == "undefined"){
+				if(!func){
+					scope(obj[x], x);
+				}else{
+					func.call(scope, obj[x], x);
+				}
+			}
+		}
+	}
+
+	// FIXME: need a better test so we don't exclude nightly Safari's!
+	this.behaviors = {};
+	this.add = function(behaviorObj){
+		/*	behavior objects are specified in the following format:
+		 *
+		 *	{ 
+		 *	 	"#id": {
+		 *			"found": function(element){
+		 *				// ...
+		 *			},
+		 *
+		 *			"onblah": {targetObj: foo, targetFunc: "bar"},
+		 *
+		 *			"onblarg": "/foo/bar/baz/blarg",
+		 *
+		 *			"onevent": function(evt){
+		 *			},
+		 *
+		 *			"onotherevent: function(evt){
+		 *				// ...
+		 *			}
+		 *		},
+		 *
+		 *		"#id2": {
+		 *			// ...
+		 *		},
+		 *
+		 *		"#id3": function(element){
+		 *			// ...
+		 *		},
+		 *
+		 *		// publish the match on a topic
+		 *		"#id4": "/found/topic/name",
+		 *
+		 *		// match all direct descendants
+		 *		"#id4 > *": function(element){
+		 *			// ...
+		 *		},
+		 *
+		 *		// match the first child node that's an element
+		 *		"#id4 > @firstElement": { ... },
+		 *
+		 *		// match the last child node that's an element
+		 *		"#id4 > @lastElement":  { ... },
+		 *
+		 *		// all elements of type tagname
+		 *		"tagname": {
+		 *			// ...
+		 *		},
+		 *
+		 *		// maps to roughly:
+		 *		//	dojo.lang.forEach(body.getElementsByTagName("tagname1"), function(node){
+		 *		//		dojo.lang.forEach(node.getElementsByTagName("tagname2"), function(node2){
+		 *		//			dojo.lang.forEach(node2.getElementsByTagName("tagname3", function(node3){
+		 *		//				// apply rules
+		 *		//			});
+		 *		//		});
+		 *		//	});
+		 *		"tagname1 tagname2 tagname3": {
+		 *			// ...
+		 *		},
+		 *
+		 *		".classname": {
+		 *			// ...
+		 *		},
+		 *
+		 *		"tagname.classname": {
+		 *			// ...
+		 *		},
+		 *	}
+		 *
+		 *	The "found" method is a generalized handler that's called as soon
+		 *	as the node matches the selector. Rules for values that follow also
+		 *	apply to the "found" key.
+		 *	
+		 *	The "on*" handlers are attached with dojo.event.connect(). If the
+		 *	value is not a function but is rather an object, it's assumed to be
+		 *	the "other half" of a dojo.event.kwConnect() argument object. It
+		 *	may contain any/all properties of such a connection modifier save
+		 *	for the sourceObj and sourceFunc properties which are filled in by
+		 *	the system automatically. If a string is instead encountered, the
+		 *	node publishes the specified event on the topic contained in the
+		 *	string value.
+		 *
+		 *	If the value corresponding to the ID key is a function and not a
+		 *	list, it's treated as though it was the value of "found".
+		 *
+		 */
+
+		var tmpObj = {};
+		forIn(behaviorObj, this, function(behavior, name){
+			var tBehavior = arrIn(this.behaviors, name);
+			if((dojo.lang.isString(behavior))||(dojo.lang.isFunction(behavior))){
+				behavior = { found: behavior };
+			}
+			forIn(behavior, function(rule, ruleName){
+				arrIn(tBehavior, ruleName).push(rule);
+			});
+		});
+	}
+
+	this.apply = function(){
+		dojo.profile.start("dojo.behavior.apply");
+		var r = dojo.render.html;
+		// note, we apply one way for fast queries and one way for slow
+		// iteration. So be it.
+		var safariGoodEnough = (!r.safari);
+		if(r.safari){
+			// Anything over release #420 should work the fast way
+			var uas = r.UA.split("AppleWebKit/")[1];
+			if(parseInt(uas.match(/[0-9.]{3,}/)) >= 420){
+				safariGoodEnough = true;
+			}
+		}
+		if((dj_undef("behaviorFastParse", djConfig) ? (safariGoodEnough) : djConfig["behaviorFastParse"])){
+			this.applyFast();
+		}else{
+			this.applySlow();
+		}
+		dojo.profile.end("dojo.behavior.apply");
+	}
+
+	this.matchCache = {};
+
+	this.elementsById = function(id, handleRemoved){
+		var removed = [];
+		var added = [];
+		arrIn(this.matchCache, id);
+		if(handleRemoved){
+			var nodes = this.matchCache[id];
+			for(var x=0; x<nodes.length; x++){
+				if(nodes[x].id != ""){
+					removed.push(nodes[x]);
+					nodes.splice(x, 1);
+					x--;
+				}
+			}
+		}
+		var tElem = dojo.byId(id);
+		while(tElem){
+			if(!tElem["idcached"]){
+				added.push(tElem);
+			}
+			tElem.id = "";
+			tElem = dojo.byId(id);
+		}
+		this.matchCache[id] = this.matchCache[id].concat(added);
+		dojo.lang.forEach(this.matchCache[id], function(node){
+			node.id = id;
+			node.idcached = true;
+		});
+		return { "removed": removed, "added": added, "match": this.matchCache[id] };
+	}
+
+	this.applyToNode = function(node, action, ruleSetName){
+		if(typeof action == "string"){
+			dojo.event.topic.registerPublisher(action, node, ruleSetName);
+		}else if(typeof action == "function"){
+			if(ruleSetName == "found"){
+				action(node);
+			}else{
+				dojo.event.connect(node, ruleSetName, action);
+			}
+		}else{
+			action.srcObj = node;
+			action.srcFunc = ruleSetName;
+			dojo.event.kwConnect(action);
+		}
+	}
+
+	this.applyFast = function(){
+		dojo.profile.start("dojo.behavior.applyFast");
+		// fast DOM queries...wheeee!
+		forIn(this.behaviors, function(tBehavior, id){
+			var elems = dojo.behavior.elementsById(id);
+			dojo.lang.forEach(elems.added, 
+				function(elem){
+					forIn(tBehavior, function(ruleSet, ruleSetName){
+						if(dojo.lang.isArray(ruleSet)){
+							dojo.lang.forEach(ruleSet, function(action){
+								dojo.behavior.applyToNode(elem, action, ruleSetName);
+							});
+						}
+					});
+				}
+			);
+		});
+		dojo.profile.end("dojo.behavior.applyFast");
+	}
+	
+	this.applySlow = function(){
+		// iterate. Ugg.
+		dojo.profile.start("dojo.behavior.applySlow");
+		var all = document.getElementsByTagName("*");
+		var allLen = all.length;
+		for(var x=0; x<allLen; x++){
+			var elem = all[x];
+			if((elem.id)&&(!elem["behaviorAdded"])&&(this.behaviors[elem.id])){
+				elem["behaviorAdded"] = true;
+				forIn(this.behaviors[elem.id], function(ruleSet, ruleSetName){
+					if(dojo.lang.isArray(ruleSet)){
+						dojo.lang.forEach(ruleSet, function(action){
+							dojo.behavior.applyToNode(elem, action, ruleSetName);
+						});
+					}
+				});
+			}
+		}
+		dojo.profile.end("dojo.behavior.applySlow");
+	}
+}
+
+dojo.addOnLoad(dojo.behavior, "apply");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/bootstrap1.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/bootstrap1.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/bootstrap1.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,343 @@
+/**
+* @file bootstrap1.js
+*
+* summary: First file that is loaded that 'bootstraps' the entire dojo library suite.
+* note:  Must run before hostenv_*.js file.
+*
+* @author  Copyright 2004 Mark D. Anderson (mda at discerning.com)
+* TODOC: should the copyright be changed to Dojo Foundation?
+* @license Licensed under the Academic Free License 2.1 http://www.opensource.org/licenses/afl-2.1.php
+*
+* $Id: bootstrap1.js 6258 2006-10-20 03:12:36Z jburke $
+*/
+
+// TODOC: HOW TO DOC THE BELOW?
+// @global: djConfig
+// summary:  
+//		Application code can set the global 'djConfig' prior to loading
+//		the library to override certain global settings for how dojo works.  
+// description:  The variables that can be set are as follows:
+//			- isDebug: false
+//			- allowQueryConfig: false
+//			- baseScriptUri: ""
+//			- baseRelativePath: ""
+//			- libraryScriptUri: ""
+//			- iePreventClobber: false
+//			- ieClobberMinimal: true
+//			- locale: undefined
+//			- extraLocale: undefined
+//			- preventBackButtonFix: true
+//			- searchIds: []
+//			- parseWidgets: true
+// TODOC: HOW TO DOC THESE VARIABLES?
+// TODOC: IS THIS A COMPLETE LIST?
+// note:
+//		'djConfig' does not exist under 'dojo.*' so that it can be set before the 
+//		'dojo' variable exists.  
+// note:
+//		Setting any of these variables *after* the library has loaded does nothing at all. 
+// TODOC: is this still true?  Release notes for 0.3 indicated they could be set after load.
+//
+
+
+//TODOC:  HOW TO DOC THIS?
+// @global: dj_global
+// summary: 
+//		an alias for the top-level global object in the host environment
+//		(e.g., the window object in a browser).
+// description:  
+//		Refer to 'dj_global' rather than referring to window to ensure your
+//		code runs correctly in contexts other than web browsers (eg: Rhino on a server).
+var dj_global = this;
+
+//TODOC:  HOW TO DOC THIS?
+// @global: dj_currentContext
+// summary: 
+//		Private global context object. Where 'dj_global' always refers to the boot-time
+//    global context, 'dj_currentContext' can be modified for temporary context shifting.
+//    dojo.global() returns dj_currentContext.
+// description:  
+//		Refer to dojo.global() rather than referring to dj_global to ensure your
+//		code runs correctly in managed contexts.
+var dj_currentContext = this;
+
+
+// ****************************************************************
+// global public utils
+// TODOC: DO WE WANT TO NOTE THAT THESE ARE GLOBAL PUBLIC UTILS?
+// ****************************************************************
+
+function dj_undef(/*String*/ name, /*Object?*/ object){
+	//summary: Returns true if 'name' is defined on 'object' (or globally if 'object' is null).
+	//description: Note that 'defined' and 'exists' are not the same concept.
+	return (typeof (object || dj_currentContext)[name] == "undefined");	// Boolean
+}
+
+// make sure djConfig is defined
+if(dj_undef("djConfig", this)){ 
+	var djConfig = {}; 
+}
+
+//TODOC:  HOW TO DOC THIS?
+// dojo is the root variable of (almost all) our public symbols -- make sure it is defined.
+if(dj_undef("dojo", this)){ 
+	var dojo = {}; 
+}
+
+dojo.global = function(){
+	// summary:
+	//		return the current global context object
+	//		(e.g., the window object in a browser).
+	// description: 
+	//		Refer to 'dojo.global()' rather than referring to window to ensure your
+	//		code runs correctly in contexts other than web browsers (eg: Rhino on a server).
+	return dj_currentContext;
+}
+
+// Override locale setting, if specified
+dojo.locale  = djConfig.locale;
+
+//TODOC:  HOW TO DOC THIS?
+dojo.version = {
+	// summary: version number of this instance of dojo.
+	major: 0, minor: 3, patch: 1, flag: "+",
+	revision: Number("$Rev: 6258 $".match(/[0-9]+/)[0]),
+	toString: function(){
+		with(dojo.version){
+			return major + "." + minor + "." + patch + flag + " (" + revision + ")";	// String
+		}
+	}
+}
+
+dojo.evalProp = function(/*String*/ name, /*Object*/ object, /*Boolean?*/ create){
+	// summary: Returns 'object[name]'.  If not defined and 'create' is true, will return a new Object.
+	// description: 
+	//		Returns null if 'object[name]' is not defined and 'create' is not true.
+	// 		Note: 'defined' and 'exists' are not the same concept.	
+	if((!object)||(!name)) return undefined; // undefined
+	if(!dj_undef(name, object)) return object[name]; // mixed
+	return (create ? (object[name]={}) : undefined);	// mixed
+}
+
+dojo.parseObjPath = function(/*String*/ path, /*Object?*/ context, /*Boolean?*/ create){
+	// summary: Parse string path to an object, and return corresponding object reference and property name.
+	// description: 
+	//		Returns an object with two properties, 'obj' and 'prop'.  
+	//		'obj[prop]' is the reference indicated by 'path'.
+	// path: Path to an object, in the form "A.B.C".
+	// context: Object to use as root of path.  Defaults to 'dojo.global()'.
+	// create: If true, Objects will be created at any point along the 'path' that is undefined.
+	var object = (context || dojo.global());
+	var names = path.split('.');
+	var prop = names.pop();
+	for (var i=0,l=names.length;i<l && object;i++){
+		object = dojo.evalProp(names[i], object, create);
+	}
+	return {obj: object, prop: prop};	// Object: {obj: Object, prop: String}
+}
+
+dojo.evalObjPath = function(/*String*/ path, /*Boolean?*/ create){
+	// summary: Return the value of object at 'path' in the global scope, without using 'eval()'.
+	// path: Path to an object, in the form "A.B.C".
+	// create: If true, Objects will be created at any point along the 'path' that is undefined.
+	if(typeof path != "string"){ 
+		return dojo.global(); 
+	}
+	// fast path for no periods
+	if(path.indexOf('.') == -1){
+		return dojo.evalProp(path, dojo.global(), create);		// mixed
+	}
+
+	//MOW: old 'with' syntax was confusing and would throw an error if parseObjPath returned null.
+	var ref = dojo.parseObjPath(path, dojo.global(), create);
+	if(ref){
+		return dojo.evalProp(ref.prop, ref.obj, create);	// mixed
+	}
+	return null;
+}
+
+dojo.errorToString = function(/*Error*/ exception){
+	// summary: Return an exception's 'message', 'description' or text.
+
+	// TODO: overriding Error.prototype.toString won't accomplish this?
+ 	// 		... since natively generated Error objects do not always reflect such things?
+	if(!dj_undef("message", exception)){
+		return exception.message;		// String
+	}else if(!dj_undef("description", exception)){
+		return exception.description;	// String
+	}else{
+		return exception;				// Error
+	}
+}
+
+dojo.raise = function(/*String*/ message, /*Error?*/ exception){
+	// summary: Common point for raising exceptions in Dojo to enable logging.
+	//	Throws an error message with text of 'exception' if provided, or
+	//	rethrows exception object.
+
+	if(exception){
+		message = message + ": "+dojo.errorToString(exception);
+	}
+
+	// print the message to the user if hostenv.println is defined
+	try { if(djConfig.isDebug){ dojo.hostenv.println("FATAL exception raised: "+message); } } catch (e) {}
+
+	throw exception || Error(message);
+}
+
+//Stub functions so things don't break.
+//TODOC:  HOW TO DOC THESE?
+dojo.debug = function(){};
+dojo.debugShallow = function(obj){};
+dojo.profile = { start: function(){}, end: function(){}, stop: function(){}, dump: function(){} };
+
+function dj_eval(/*String*/ scriptFragment){ 
+	// summary: Perform an evaluation in the global scope.  Use this rather than calling 'eval()' directly.
+	// description: Placed in a separate function to minimize size of trapped evaluation context.
+	// note:
+	//	 - JSC eval() takes an optional second argument which can be 'unsafe'.
+	//	 - Mozilla/SpiderMonkey eval() takes an optional second argument which is the
+	//  	 scope object for new symbols.
+	return dj_global.eval ? dj_global.eval(scriptFragment) : eval(scriptFragment); 	// mixed
+}
+
+dojo.unimplemented = function(/*String*/ funcname, /*String?*/ extra){
+	// summary: Throw an exception because some function is not implemented.
+	// extra: Text to append to the exception message.
+	var message = "'" + funcname + "' not implemented";
+	if (extra != null) { message += " " + extra; }
+	dojo.raise(message);
+}
+
+dojo.deprecated = function(/*String*/ behaviour, /*String?*/ extra, /*String?*/ removal){
+	// summary: Log a debug message to indicate that a behavior has been deprecated.
+	// extra: Text to append to the message.
+	// removal: Text to indicate when in the future the behavior will be removed.
+	var message = "DEPRECATED: " + behaviour;
+	if(extra){ message += " " + extra; }
+	if(removal){ message += " -- will be removed in version: " + removal; }
+	dojo.debug(message);
+}
+
+dojo.render = (function(){
+	//TODOC: HOW TO DOC THIS?
+	// summary: Details rendering support, OS and browser of the current environment.
+	// TODOC: is this something many folks will interact with?  If so, we should doc the structure created...
+	function vscaffold(prefs, names){
+		var tmp = {
+			capable: false,
+			support: {
+				builtin: false,
+				plugin: false
+			},
+			prefixes: prefs
+		};
+		for(var i=0; i<names.length; i++){
+			tmp[names[i]] = false;
+		}
+		return tmp;
+	}
+
+	return {
+		name: "",
+		ver: dojo.version,
+		os: { win: false, linux: false, osx: false },
+		html: vscaffold(["html"], ["ie", "opera", "khtml", "safari", "moz"]),
+		svg: vscaffold(["svg"], ["corel", "adobe", "batik"]),
+		vml: vscaffold(["vml"], ["ie"]),
+		swf: vscaffold(["Swf", "Flash", "Mm"], ["mm"]),
+		swt: vscaffold(["Swt"], ["ibm"])
+	};
+})();
+
+// ****************************************************************
+// dojo.hostenv methods that must be defined in hostenv_*.js
+// ****************************************************************
+
+/**
+ * The interface definining the interaction with the EcmaScript host environment.
+*/
+
+/*
+ * None of these methods should ever be called directly by library users.
+ * Instead public methods such as loadModule should be called instead.
+ */
+dojo.hostenv = (function(){
+	// TODOC:  HOW TO DOC THIS?
+	// summary: Provides encapsulation of behavior that changes across different 'host environments' 
+	//			(different browsers, server via Rhino, etc).
+	// description: None of these methods should ever be called directly by library users.
+	//				Use public methods such as 'loadModule' instead.
+	
+	// default configuration options
+	var config = {
+		isDebug: false,
+		allowQueryConfig: false,
+		baseScriptUri: "",
+		baseRelativePath: "",
+		libraryScriptUri: "",
+		iePreventClobber: false,
+		ieClobberMinimal: true,
+		preventBackButtonFix: true,
+		delayMozLoadingFix: false,
+		searchIds: [],
+		parseWidgets: true
+	};
+
+	if (typeof djConfig == "undefined") { djConfig = config; }
+	else {
+		for (var option in config) {
+			if (typeof djConfig[option] == "undefined") {
+				djConfig[option] = config[option];
+			}
+		}
+	}
+
+	return {
+		name_: '(unset)',
+		version_: '(unset)',
+
+
+		getName: function(){ 
+			// sumary: Return the name of the host environment.
+			return this.name_; 	// String
+		},
+
+
+		getVersion: function(){ 
+			// summary: Return the version of the hostenv.
+			return this.version_; // String
+		},
+
+		getText: function(/*String*/ uri){
+			// summary:	Read the plain/text contents at the specified 'uri'.
+			// description: 
+			//			If 'getText()' is not implemented, then it is necessary to override 
+			//			'loadUri()' with an implementation that doesn't rely on it.
+
+			dojo.unimplemented('getText', "uri=" + uri);
+		}
+	};
+})();
+
+
+dojo.hostenv.getBaseScriptUri = function(){
+	// summary: Return the base script uri that other scripts are found relative to.
+	// TODOC: HUH?  This comment means nothing to me.  What other scripts? Is this the path to other dojo libraries?
+	//		MAYBE:  Return the base uri to scripts in the dojo library.	 ???
+	// return: Empty string or a path ending in '/'.
+	if(djConfig.baseScriptUri.length){ 
+		return djConfig.baseScriptUri;
+	}
+
+	// MOW: Why not:
+	//			uri = djConfig.libraryScriptUri || djConfig.baseRelativePath
+	//		??? Why 'new String(...)'
+	var uri = new String(djConfig.libraryScriptUri||djConfig.baseRelativePath);
+	if (!uri) { dojo.raise("Nothing returned by getLibraryScriptUri(): " + uri); }
+
+	// MOW: uri seems to not be actually used.  Seems to be hard-coding to djConfig.baseRelativePath... ???
+	var lastslash = uri.lastIndexOf('/');		// MOW ???
+	djConfig.baseScriptUri = djConfig.baseRelativePath;
+	return djConfig.baseScriptUri;	// String
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/bootstrap2.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/bootstrap2.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/bootstrap2.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,59 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+//Semicolon is for when this file is integrated with a custom build on one line
+//with some other file's contents. Sometimes that makes things not get defined
+//properly, particularly with the using the closure below to do all the work.
+;(function(){
+	//Don't do this work if dojo.js has already done it.
+	if(typeof dj_usingBootstrap != "undefined"){
+		return;
+	}
+
+	var isRhino = false;
+	var isSpidermonkey = false;
+	var isDashboard = false;
+	if((typeof this["load"] == "function")&&((typeof this["Packages"] == "function")||(typeof this["Packages"] == "object"))){
+		isRhino = true;
+	}else if(typeof this["load"] == "function"){
+		isSpidermonkey  = true;
+	}else if(window.widget){
+		isDashboard = true;
+	}
+
+	var tmps = [];
+	if((this["djConfig"])&&((djConfig["isDebug"])||(djConfig["debugAtAllCosts"]))){
+		tmps.push("debug.js");
+	}
+
+	if((this["djConfig"])&&(djConfig["debugAtAllCosts"])&&(!isRhino)&&(!isDashboard)){
+		tmps.push("browser_debug.js");
+	}
+
+	var loaderRoot = djConfig["baseScriptUri"];
+	if((this["djConfig"])&&(djConfig["baseLoaderUri"])){
+		loaderRoot = djConfig["baseLoaderUri"];
+	}
+
+	for(var x=0; x < tmps.length; x++){
+		var spath = loaderRoot+"src/"+tmps[x];
+		if(isRhino||isSpidermonkey){
+			load(spath);
+		} else {
+			try {
+				document.write("<scr"+"ipt type='text/javascript' src='"+spath+"'></scr"+"ipt>");
+			} catch (e) {
+				var script = document.createElement("script");
+				script.src = spath;
+				document.getElementsByTagName("head")[0].appendChild(script);
+			}
+		}
+	}
+})();

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/browser_debug.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/browser_debug.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/browser_debug.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,176 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.hostenv.loadedUris.push("../src/bootstrap1.js");
+dojo.hostenv.loadedUris.push("../src/loader.js");
+dojo.hostenv.loadedUris.push("../src/hostenv_browser.js");
+dojo.hostenv.loadedUris.push("../src/bootstrap2.js");
+dojo.hostenv._loadedUrisListStart = dojo.hostenv.loadedUris.length;
+
+function removeComments(contents){
+	contents = new String((!contents) ? "" : contents);
+	// clobber all comments
+	// FIXME broken if // or /* inside quotes or regexp
+	contents = contents.replace( /^(.*?)\/\/(.*)$/mg , "$1");
+	contents = contents.replace( /(\n)/mg , "__DOJONEWLINE");
+	contents = contents.replace( /\/\*(.*?)\*\//g , "");
+	return contents.replace( /__DOJONEWLINE/mg , "\n");
+}
+
+dojo.hostenv.getRequiresAndProvides = function(contents){
+	// FIXME: should probably memoize this!
+	if(!contents){ return []; }
+	
+
+	// check to see if we need to load anything else first. Ugg.
+	var deps = [];
+	var tmp;
+	RegExp.lastIndex = 0;
+	var testExp = /dojo.(hostenv.loadModule|hostenv.require|require|requireIf|kwCompoundRequire|hostenv.conditionalLoadModule|hostenv.startPackage|provide)\([\w\W]*?\)/mg;
+	while((tmp = testExp.exec(contents)) != null){
+		deps.push(tmp[0]);
+	}
+	return deps;
+}
+
+dojo.hostenv.getDelayRequiresAndProvides = function(contents){
+	// FIXME: should probably memoize this!
+	if(!contents){ return []; }
+
+	// check to see if we need to load anything else first. Ugg.
+	var deps = [];
+	var tmp;
+	RegExp.lastIndex = 0;
+	var testExp = /dojo.(requireAfterIf)\([\w\W]*?\)/mg;
+	while((tmp = testExp.exec(contents)) != null){
+		deps.push(tmp[0]);
+	}
+	return deps;
+}
+
+/*
+dojo.getNonExistantDescendants = function(objpath){
+	var ret = [];
+	// fast path for no periods
+	if(typeof objpath != "string"){ return dj_global; }
+	if(objpath.indexOf('.') == -1){
+		if(dj_undef(objpath, dj_global)){
+			ret.push[objpath];
+		}
+		return ret;
+	}
+
+	var syms = objpath.split(/\./);
+	var obj = dj_global;
+	for(var i=0;i<syms.length;++i){
+		if(dj_undef(syms[i], obj)){
+			for(var j=i; j<syms.length; j++){
+				ret.push(syms.slice(0, j+1).join("."));
+			}
+			break;
+		}
+	}
+	return ret;
+}
+*/
+
+dojo.clobberLastObject = function(objpath){
+	if(objpath.indexOf('.') == -1){
+		if(!dj_undef(objpath, dj_global)){
+			delete dj_global[objpath];
+		}
+		return true;
+	}
+
+	var syms = objpath.split(/\./);
+	var base = dojo.evalObjPath(syms.slice(0, -1).join("."), false);
+	var child = syms[syms.length-1];
+	if(!dj_undef(child, base)){
+		// alert(objpath);
+		delete base[child];
+		return true;
+	}
+	return false;
+}
+
+var removals = [];
+
+function zip(arr){
+	var ret = [];
+	var seen = {};
+	for(var x=0; x<arr.length; x++){
+		if(!seen[arr[x]]){
+			ret.push(arr[x]);
+			seen[arr[x]] = true;
+		}
+	}
+	return ret;
+}
+
+// over-write dj_eval to prevent actual loading of subsequent files
+var old_dj_eval = dj_eval;
+dj_eval = function(){ return true; }
+dojo.hostenv.oldLoadUri = dojo.hostenv.loadUri;
+dojo.hostenv.loadUri = function(uri, cb /*optional*/){
+	if(dojo.hostenv.loadedUris[uri]){
+		return true; // fixes endless recursion opera trac 471
+	}
+	try{
+		var text = this.getText(uri, null, true);
+		if(!text) { return false; }
+		if(cb){
+			// No way to load i18n bundles but to eval them, and they usually
+			// don't have script needing to be debugged anyway
+			var expr = old_dj_eval('('+text+')');
+			cb(expr);
+		}else {
+			var requires = dojo.hostenv.getRequiresAndProvides(text);
+			eval(requires.join(";"));
+			dojo.hostenv.loadedUris.push(uri);
+			dojo.hostenv.loadedUris[uri] = true;
+			var delayRequires = dojo.hostenv.getDelayRequiresAndProvides(text);
+			eval(delayRequires.join(";"));
+		}
+	}catch(e){ 
+		alert(e);
+	}
+	return true;
+}
+
+dojo.hostenv._writtenIncludes = {};
+dojo.hostenv.writeIncludes = function(willCallAgain){
+	for(var x=removals.length-1; x>=0; x--){
+		dojo.clobberLastObject(removals[x]);
+	}
+	var depList = [];
+	var seen = dojo.hostenv._writtenIncludes;
+	for(var x=0; x<dojo.hostenv.loadedUris.length; x++){
+		var curi = dojo.hostenv.loadedUris[x];
+		// dojo.debug(curi);
+		if(!seen[curi]){
+			seen[curi] = true;
+			depList.push(curi);
+		}
+	}
+
+	dojo.hostenv._global_omit_module_check = true;
+	
+	for(var x= dojo.hostenv._loadedUrisListStart; x<depList.length; x++){
+		document.write("<script type='text/javascript' src='"+depList[x]+"'></script>");
+	}
+	document.write("<script type='text/javascript'>dojo.hostenv._global_omit_module_check = false;</script>");
+	dojo.hostenv._loadedUrisListStart = 0;
+	if (!willCallAgain) {
+		// turn off debugAtAllCosts, so that dojo.require() calls inside of ContentPane hrefs
+		// work correctly
+		dj_eval = old_dj_eval;
+		dojo.hostenv.loadUri = dojo.hostenv.oldLoadUri;
+	}
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/crypto.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/crypto.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/crypto.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,31 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.crypto");
+
+dojo.crypto.cipherModes={ 
+	//	summary
+	//	Enumeration for various cipher modes.
+	ECB:0, 
+	CBC:1, 
+	PCBC:2, 
+	CFB:3, 
+	OFB:4, 
+	CTR:5 
+};
+
+dojo.crypto.outputTypes={ 
+	//	summary
+	//	Enumeration for input and output encodings.
+	Base64:0,
+	Hex:1,
+	String:2,
+	Raw:3 
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/data.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/data.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/data.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,15 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.data");
+
+// currently a stub for dojo.data
+
+dojo.data = {};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/date.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/date.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/date.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,13 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.date");
+
+dojo.deprecated("dojo.date", "use one of the modules in dojo.date.* instead", "0.5");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/debug.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/debug.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/debug.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,91 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.debug = function(/*...*/){
+	// summary:
+	//		Produce a line of debug output. Does nothing unless
+	//		djConfig.isDebug is true. Accepts any nubmer of args, joined with
+	//		'' to produce a single line of debugging output.  Caller should not
+	//		supply a trailing "\n".
+	if (!djConfig.isDebug) { return; }
+	var args = arguments;
+	if(dj_undef("println", dojo.hostenv)){
+		dojo.raise("dojo.debug not available (yet?)");
+	}
+	var isJUM = dj_global["jum"] && !dj_global["jum"].isBrowser;
+	var s = [(isJUM ? "": "DEBUG: ")];
+	for(var i=0;i<args.length;++i){
+		if(!false && args[i] && args[i] instanceof Error){
+			var msg = "[" + args[i].name + ": " + dojo.errorToString(args[i]) +
+				(args[i].fileName ? ", file: " + args[i].fileName : "") +
+				(args[i].lineNumber ? ", line: " + args[i].lineNumber : "") + "]";
+		} else {
+			try {
+				var msg = String(args[i]);
+			} catch(e) {
+				if(dojo.render.html.ie) {
+					var msg = "[ActiveXObject]";
+				} else {
+					var msg = "[unknown]";
+				}
+			}
+		}
+		s.push(msg);
+	}
+	
+	dojo.hostenv.println(s.join(" "));
+}
+
+/**
+ * this is really hacky for now - just 
+ * display the properties of the object
+**/
+
+dojo.debugShallow = function(/*Object*/obj){
+	// summary:
+	//		outputs a "name: value" style listing of all enumerable properties
+	//		in obj. Does nothing if djConfig.isDebug == false.
+	// obj: the object to be enumerated
+	if (!djConfig.isDebug) { return; }
+	dojo.debug('------------------------------------------------------------');
+	dojo.debug('Object: '+obj);
+	var props = [];
+	for(var prop in obj){
+		try {
+			props.push(prop + ': ' + obj[prop]);
+		} catch(E) {
+			props.push(prop + ': ERROR - ' + E.message);
+		}
+	}
+	props.sort();
+	for(var i = 0; i < props.length; i++) {
+		dojo.debug(props[i]);
+	}
+	dojo.debug('------------------------------------------------------------');
+}
+
+dojo.debugDeep = function(/*Object*/obj){
+	// summary:
+	//		provides an "object explorer" view of the passed obj in a popup
+	//		window.
+	// obj: the object to be examined
+	if (!djConfig.isDebug) { return; }
+	if (!dojo.uri || !dojo.uri.dojoUri){ return dojo.debug("You'll need to load dojo.uri.* for deep debugging - sorry!"); }
+	if (!window.open){ return dojo.debug('Deep debugging is only supported in host environments with window.open'); }
+	var idx = dojo.debugDeep.debugVars.length;
+	dojo.debugDeep.debugVars.push(obj);
+	// dojo.undo.browser back and forward breaks relpaths
+	var url = new dojo.uri.Uri(location, dojo.uri.dojoUri("src/debug/deep.html?var="+idx)).toString();
+	var win = window.open(url, '_blank', 'width=600, height=400, resizable=yes, scrollbars=yes, status=yes');
+	try{
+		win.debugVar = obj;
+	}catch(e){}
+}
+dojo.debugDeep.debugVars = [];

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/DragAndDrop.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/DragAndDrop.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/DragAndDrop.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,173 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.require("dojo.lang.common");
+dojo.require("dojo.lang.declare");
+dojo.provide("dojo.dnd.DragAndDrop");
+
+dojo.declare("dojo.dnd.DragSource", null, {
+	type: "",
+
+	onDragEnd: function(){
+	},
+
+	onDragStart: function(){
+	},
+
+	/*
+	 * This function gets called when the DOM element was 
+	 * selected for dragging by the HtmlDragAndDropManager.
+	 */
+	onSelected: function(){
+	},
+
+	unregister: function(){
+		dojo.dnd.dragManager.unregisterDragSource(this);
+	},
+
+	reregister: function(){
+		dojo.dnd.dragManager.registerDragSource(this);
+	}
+}, function(){
+
+	//dojo.profile.start("DragSource");
+
+	var dm = dojo.dnd.dragManager;
+	if(dm["registerDragSource"]){ // side-effect prevention
+		dm.registerDragSource(this);
+	}
+
+	//dojo.profile.end("DragSource");
+
+});
+
+dojo.declare("dojo.dnd.DragObject", null, {
+	type: "",
+
+	onDragStart: function(){
+		// gets called directly after being created by the DragSource
+		// default action is to clone self as icon
+	},
+
+	onDragMove: function(){
+		// this changes the UI for the drag icon
+		//	"it moves itself"
+	},
+
+	onDragOver: function(){
+	},
+
+	onDragOut: function(){
+	},
+
+	onDragEnd: function(){
+	},
+
+	// normal aliases
+	onDragLeave: this.onDragOut,
+	onDragEnter: this.onDragOver,
+
+	// non-camel aliases
+	ondragout: this.onDragOut,
+	ondragover: this.onDragOver
+}, function(){
+	var dm = dojo.dnd.dragManager;
+	if(dm["registerDragObject"]){ // side-effect prevention
+		dm.registerDragObject(this);
+	}
+});
+
+dojo.declare("dojo.dnd.DropTarget", null, {
+
+	acceptsType: function(type){
+		if(!dojo.lang.inArray(this.acceptedTypes, "*")){ // wildcard
+			if(!dojo.lang.inArray(this.acceptedTypes, type)) { return false; }
+		}
+		return true;
+	},
+
+	accepts: function(dragObjects){
+		if(!dojo.lang.inArray(this.acceptedTypes, "*")){ // wildcard
+			for (var i = 0; i < dragObjects.length; i++) {
+				if (!dojo.lang.inArray(this.acceptedTypes,
+					dragObjects[i].type)) { return false; }
+			}
+		}
+		return true;
+	},
+
+	unregister: function(){
+		dojo.dnd.dragManager.unregisterDropTarget(this);
+	},
+
+	onDragOver: function(){
+	},
+
+	onDragOut: function(){
+	},
+
+	onDragMove: function(){
+	},
+
+	onDropStart: function(){
+	},
+
+	onDrop: function(){
+	},
+
+	onDropEnd: function(){
+	}
+}, function(){
+	if (this.constructor == dojo.dnd.DropTarget) { return; } // need to be subclassed
+	this.acceptedTypes = [];
+	dojo.dnd.dragManager.registerDropTarget(this);
+});
+
+// NOTE: this interface is defined here for the convenience of the DragManager
+// implementor. It is expected that in most cases it will be satisfied by
+// extending a native event (DOM event in HTML and SVG).
+dojo.dnd.DragEvent = function(){
+	this.dragSource = null;
+	this.dragObject = null;
+	this.target = null;
+	this.eventStatus = "success";
+	//
+	// can be one of:
+	//	[	"dropSuccess", "dropFailure", "dragMove",
+	//		"dragStart", "dragEnter", "dragLeave"]
+	//
+}
+/*
+ *	The DragManager handles listening for low-level events and dispatching
+ *	them to higher-level primitives like drag sources and drop targets. In
+ *	order to do this, it must keep a list of the items.
+ */
+dojo.declare("dojo.dnd.DragManager", null, {
+	selectedSources: [],
+	dragObjects: [],
+	dragSources: [],
+	registerDragSource: function(){},
+	dropTargets: [],
+	registerDropTarget: function(){},
+	lastDragTarget: null,
+	currentDragTarget: null,
+	onKeyDown: function(){},
+	onMouseOut: function(){},
+	onMouseMove: function(){},
+	onMouseUp: function(){}
+});
+
+// NOTE: despite the existance of the DragManager class, there will be a
+// singleton drag manager provided by the renderer-specific D&D support code.
+// It is therefore sane for us to assign instance variables to the DragManager
+// prototype
+
+// The renderer-specific file will define the following object:
+// dojo.dnd.dragManager = null;

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragAndDrop.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragAndDrop.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragAndDrop.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,508 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.dnd.HtmlDragAndDrop");
+
+dojo.require("dojo.dnd.HtmlDragManager");
+dojo.require("dojo.dnd.DragAndDrop");
+
+dojo.require("dojo.html.*");
+dojo.require("dojo.html.display");
+dojo.require("dojo.html.util");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.html.iframe");
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.event.*");
+
+dojo.declare("dojo.dnd.HtmlDragSource", dojo.dnd.DragSource, {
+	dragClass: "", // CSS classname(s) applied to node when it is being dragged
+
+	onDragStart: function(){
+		var dragObj = new dojo.dnd.HtmlDragObject(this.dragObject, this.type);
+		if(this.dragClass) { dragObj.dragClass = this.dragClass; }
+
+		if (this.constrainToContainer) {
+			dragObj.constrainTo(this.constrainingContainer || this.domNode.parentNode);
+		}
+
+		return dragObj;
+	},
+
+	setDragHandle: function(node){
+		node = dojo.byId(node);
+		dojo.dnd.dragManager.unregisterDragSource(this);
+		this.domNode = node;
+		dojo.dnd.dragManager.registerDragSource(this);
+	},
+
+	setDragTarget: function(node){
+		this.dragObject = node;
+	},
+
+	constrainTo: function(container) {
+		this.constrainToContainer = true;
+		if (container) {
+			this.constrainingContainer = container;
+		}
+	},
+	
+	/*
+	*
+	* see dojo.dnd.DragSource.onSelected
+	*/
+	onSelected: function() {
+		for (var i=0; i<this.dragObjects.length; i++) {
+			dojo.dnd.dragManager.selectedSources.push(new dojo.dnd.HtmlDragSource(this.dragObjects[i]));
+		}
+	},
+
+	/**
+	* Register elements that should be dragged along with
+	* the actual DragSource.
+	*
+	* Example usage:
+	* 	var dragSource = new dojo.dnd.HtmlDragSource(...);
+	*	// add a single element
+	*	dragSource.addDragObjects(dojo.byId('id1'));
+	*	// add multiple elements to drag along
+	*	dragSource.addDragObjects(dojo.byId('id2'), dojo.byId('id3'));
+	*
+	* el A dom node to add to the drag list.
+	*/
+	addDragObjects: function(/*DOMNode*/ el) {
+		for (var i=0; i<arguments.length; i++) {
+			this.dragObjects.push(arguments[i]);
+		}
+	}
+}, function(node, type){
+	node = dojo.byId(node);
+	this.dragObjects = [];
+	this.constrainToContainer = false;
+	if(node){
+		this.domNode = node;
+		this.dragObject = node;
+		// register us
+		dojo.dnd.DragSource.call(this);
+		// set properties that might have been clobbered by the mixin
+		this.type = (type)||(this.domNode.nodeName.toLowerCase());
+	}
+
+});
+
+dojo.declare("dojo.dnd.HtmlDragObject", dojo.dnd.DragObject, {
+	dragClass: "",
+	opacity: 0.5,
+	createIframe: true,		// workaround IE6 bug
+
+	// if true, node will not move in X and/or Y direction
+	disableX: false,
+	disableY: false,
+
+	createDragNode: function() {
+		var node = this.domNode.cloneNode(true);
+		if(this.dragClass) { dojo.html.addClass(node, this.dragClass); }
+		if(this.opacity < 1) { dojo.html.setOpacity(node, this.opacity); }
+		if(node.tagName.toLowerCase() == "tr"){
+			// dojo.debug("Dragging table row")
+			// Create a table for the cloned row
+			var doc = this.domNode.ownerDocument;
+			var table = doc.createElement("table");
+			var tbody = doc.createElement("tbody");
+			table.appendChild(tbody);
+			tbody.appendChild(node);
+
+			// Set a fixed width to the cloned TDs
+			var domTds = this.domNode.childNodes;
+			var cloneTds = node.childNodes;
+			for(var i = 0; i < domTds.length; i++){
+			    if((cloneTds[i])&&(cloneTds[i].style)){
+				    cloneTds[i].style.width = dojo.html.getContentBox(domTds[i]).width + "px";
+			    }
+			}
+			node = table;
+		}
+
+		if((dojo.render.html.ie55||dojo.render.html.ie60) && this.createIframe){
+			with(node.style) {
+				top="0px";
+				left="0px";
+			}
+			var outer = document.createElement("div");
+			outer.appendChild(node);
+			this.bgIframe = new dojo.html.BackgroundIframe(outer);
+			outer.appendChild(this.bgIframe.iframe);
+			node = outer;
+		}
+		node.style.zIndex = 999;
+
+		return node;
+	},
+
+	onDragStart: function(e){
+		dojo.html.clearSelection();
+
+		this.scrollOffset = dojo.html.getScroll().offset;
+		this.dragStartPosition = dojo.html.getAbsolutePosition(this.domNode, true);
+
+		this.dragOffset = {y: this.dragStartPosition.y - e.pageY,
+			x: this.dragStartPosition.x - e.pageX};
+
+		this.dragClone = this.createDragNode();
+
+		this.containingBlockPosition = this.domNode.offsetParent ? 
+			dojo.html.getAbsolutePosition(this.domNode.offsetParent, true) : {x:0, y:0};
+
+		if (this.constrainToContainer) {
+			this.constraints = this.getConstraints();
+		}
+
+		// set up for dragging
+		with(this.dragClone.style){
+			position = "absolute";
+			top = this.dragOffset.y + e.pageY + "px";
+			left = this.dragOffset.x + e.pageX + "px";
+		}
+
+		dojo.body().appendChild(this.dragClone);
+
+		// shortly the browser will fire an onClick() event,
+		// but since this was really a drag, just squelch it
+		dojo.event.connect(this.domNode, "onclick", this, "squelchOnClick");
+
+		dojo.event.topic.publish('dragStart', { source: this } );
+	},
+
+	/** Return min/max x/y (relative to document.body) for this object) **/
+	getConstraints: function() {
+		if (this.constrainingContainer.nodeName.toLowerCase() == 'body') {
+			var viewport = dojo.html.getViewport();
+			var width = viewport.width;
+			var height = viewport.height;
+			var x = 0;
+			var y = 0;
+		} else {
+			var content = dojo.html.getContentBox(this.constrainingContainer);
+			width = content.width;
+			height = content.height;
+			x =
+				this.containingBlockPosition.x +
+				dojo.html.getPixelValue(this.constrainingContainer, "padding-left", true) +
+				dojo.html.getBorderExtent(this.constrainingContainer, "left");
+			y =
+				this.containingBlockPosition.y +
+				dojo.html.getPixelValue(this.constrainingContainer, "padding-top", true) +
+				dojo.html.getBorderExtent(this.constrainingContainer, "top");
+		}
+		
+		var mb = dojo.html.getMarginBox(this.domNode);
+		return {
+			minX: x,
+			minY: y,
+			maxX: x + width - mb.width,
+			maxY: y + height - mb.height
+		}
+	},
+
+	updateDragOffset: function() {
+		var scroll = dojo.html.getScroll().offset;
+		if(scroll.y != this.scrollOffset.y) {
+			var diff = scroll.y - this.scrollOffset.y;
+			this.dragOffset.y += diff;
+			this.scrollOffset.y = scroll.y;
+		}
+		if(scroll.x != this.scrollOffset.x) {
+			var diff = scroll.x - this.scrollOffset.x;
+			this.dragOffset.x += diff;
+			this.scrollOffset.x = scroll.x;
+		}
+	},
+
+	/** Moves the node to follow the mouse */
+	onDragMove: function(e){
+		this.updateDragOffset();
+		var x = this.dragOffset.x + e.pageX;
+		var y = this.dragOffset.y + e.pageY;
+
+		if (this.constrainToContainer) {
+			if (x < this.constraints.minX) { x = this.constraints.minX; }
+			if (y < this.constraints.minY) { y = this.constraints.minY; }
+			if (x > this.constraints.maxX) { x = this.constraints.maxX; }
+			if (y > this.constraints.maxY) { y = this.constraints.maxY; }
+		}
+
+		this.setAbsolutePosition(x, y);
+
+		dojo.event.topic.publish('dragMove', { source: this } );
+	},
+
+	/**
+	 * Set the position of the drag clone.  (x,y) is relative to <body>.
+	 */
+	setAbsolutePosition: function(x, y){
+		// The drag clone is attached to document.body so this is trivial
+		if(!this.disableY) { this.dragClone.style.top = y + "px"; }
+		if(!this.disableX) { this.dragClone.style.left = x + "px"; }
+	},
+
+
+	/**
+	 * If the drag operation returned a success we reomve the clone of
+	 * ourself from the original position. If the drag operation returned
+	 * failure we slide back over to where we came from and end the operation
+	 * with a little grace.
+	 */
+	onDragEnd: function(e){
+		switch(e.dragStatus){
+
+			case "dropSuccess":
+				dojo.html.removeNode(this.dragClone);
+				this.dragClone = null;
+				break;
+
+			case "dropFailure": // slide back to the start
+				var startCoords = dojo.html.getAbsolutePosition(this.dragClone, true);
+				// offset the end so the effect can be seen
+				var endCoords = { left: this.dragStartPosition.x + 1,
+					top: this.dragStartPosition.y + 1};
+
+				// animate
+				var anim = dojo.lfx.slideTo(this.dragClone, endCoords, 500, dojo.lfx.easeOut);
+				var dragObject = this;
+				dojo.event.connect(anim, "onEnd", function (e) {
+					// pause for a second (not literally) and disappear
+					dojo.lang.setTimeout(function() {
+							dojo.html.removeNode(dragObject.dragClone);
+							// Allow drag clone to be gc'ed
+							dragObject.dragClone = null;
+						},
+						200);
+				});
+				anim.play();
+				break;
+		}
+
+		dojo.event.topic.publish('dragEnd', { source: this } );
+	},
+
+	squelchOnClick: function(e){
+		// squelch this onClick() event because it's the result of a drag (it's not a real click)
+		dojo.event.browser.stopEvent(e);
+
+		// disconnect after a short delay to prevent "Null argument to unrollAdvice()" warning
+		dojo.lang.setTimeout(function() {
+				dojo.event.disconnect(this.domNode, "onclick", this, "squelchOnClick");
+			},50);
+	},
+
+	constrainTo: function(container) {
+		this.constrainToContainer=true;
+		if (container) {
+			this.constrainingContainer = container;
+		} else {
+			this.constrainingContainer = this.domNode.parentNode;
+		}
+	}
+}, function(node, type){
+	this.domNode = dojo.byId(node);
+	this.type = type;
+	this.constrainToContainer = false;
+	this.dragSource = null;
+});
+
+dojo.declare("dojo.dnd.HtmlDropTarget", dojo.dnd.DropTarget, {
+	vertical: false,
+	onDragOver: function(e){
+		if(!this.accepts(e.dragObjects)){ return false; }
+
+		// cache the positions of the child nodes
+		this.childBoxes = [];
+		for (var i = 0, child; i < this.domNode.childNodes.length; i++) {
+			child = this.domNode.childNodes[i];
+			if (child.nodeType != dojo.html.ELEMENT_NODE) { continue; }
+			var pos = dojo.html.getAbsolutePosition(child, true);
+			var inner = dojo.html.getBorderBox(child);
+			this.childBoxes.push({top: pos.y, bottom: pos.y+inner.height,
+				left: pos.x, right: pos.x+inner.width, height: inner.height, 
+				width: inner.width, node: child});
+		}
+
+		// TODO: use dummy node
+
+		return true;
+	},
+
+	_getNodeUnderMouse: function(e){
+		// find the child
+		for (var i = 0, child; i < this.childBoxes.length; i++) {
+			with (this.childBoxes[i]) {
+				if (e.pageX >= left && e.pageX <= right &&
+					e.pageY >= top && e.pageY <= bottom) { return i; }
+			}
+		}
+
+		return -1;
+	},
+
+	createDropIndicator: function() {
+		this.dropIndicator = document.createElement("div");
+		with (this.dropIndicator.style) {
+			position = "absolute";
+			zIndex = 999;
+			if(this.vertical){
+				borderLeftWidth = "1px";
+				borderLeftColor = "black";
+				borderLeftStyle = "solid";
+				height = dojo.html.getBorderBox(this.domNode).height + "px";
+				top = dojo.html.getAbsolutePosition(this.domNode, true).y + "px";
+			}else{
+				borderTopWidth = "1px";
+				borderTopColor = "black";
+				borderTopStyle = "solid";
+				width = dojo.html.getBorderBox(this.domNode).width + "px";
+				left = dojo.html.getAbsolutePosition(this.domNode, true).x + "px";
+			}
+		}
+	},
+
+	onDragMove: function(e, dragObjects){
+		var i = this._getNodeUnderMouse(e);
+
+		if(!this.dropIndicator){
+			this.createDropIndicator();
+		}
+
+		var gravity = this.vertical ? dojo.html.gravity.WEST : dojo.html.gravity.NORTH;
+		var hide = false;
+		if(i < 0) {
+			if(this.childBoxes.length) {
+				var before = (dojo.html.gravity(this.childBoxes[0].node, e) & gravity);
+				if(before){ hide = true; }
+			} else {
+				var before = true;
+			}
+		} else {
+			var child = this.childBoxes[i];
+			var before = (dojo.html.gravity(child.node, e) & gravity);
+			if(child.node === dragObjects[0].dragSource.domNode){
+				hide = true;
+			}else{
+				var currentPosChild = before ? 
+						(i>0?this.childBoxes[i-1]:child) : 
+						(i<this.childBoxes.length-1?this.childBoxes[i+1]:child);
+				if(currentPosChild.node === dragObjects[0].dragSource.domNode){
+					hide = true;
+				}
+			}
+		}
+
+		if(hide){
+			this.dropIndicator.style.display="none";
+			return;
+		}else{
+			this.dropIndicator.style.display="";
+		}
+
+		this.placeIndicator(e, dragObjects, i, before);
+
+		if(!dojo.html.hasParent(this.dropIndicator)) {
+			dojo.body().appendChild(this.dropIndicator);
+		}
+	},
+
+	/**
+	 * Position the horizontal line that indicates "insert between these two items"
+	 */
+	placeIndicator: function(e, dragObjects, boxIndex, before) {
+		var targetProperty = this.vertical ? "left" : "top";
+		var child;
+		if (boxIndex < 0) {
+			if (this.childBoxes.length) {
+				child = before ? this.childBoxes[0]
+					: this.childBoxes[this.childBoxes.length - 1];
+			} else {
+				this.dropIndicator.style[targetProperty] = dojo.html.getAbsolutePosition(this.domNode, true)[this.vertical?"x":"y"] + "px";
+			}
+		} else {
+			child = this.childBoxes[boxIndex];
+		}
+		if(child){
+			this.dropIndicator.style[targetProperty] = (before ? child[targetProperty] : child[this.vertical?"right":"bottom"]) + "px";
+			if(this.vertical){
+				this.dropIndicator.style.height = child.height + "px";
+				this.dropIndicator.style.top = child.top + "px";
+			}else{
+				this.dropIndicator.style.width = child.width + "px";
+				this.dropIndicator.style.left = child.left + "px";
+			}
+		}
+	},
+
+	onDragOut: function(e) {
+		if(this.dropIndicator) {
+			dojo.html.removeNode(this.dropIndicator);
+			delete this.dropIndicator;
+		}
+	},
+
+	/**
+	 * Inserts the DragObject as a child of this node relative to the
+	 * position of the mouse.
+	 *
+	 * @return true if the DragObject was inserted, false otherwise
+	 */
+	onDrop: function(e){
+		this.onDragOut(e);
+
+		var i = this._getNodeUnderMouse(e);
+
+		var gravity = this.vertical ? dojo.html.gravity.WEST : dojo.html.gravity.NORTH;
+		if (i < 0) {
+			if (this.childBoxes.length) {
+				if (dojo.html.gravity(this.childBoxes[0].node, e) & gravity) {
+					return this.insert(e, this.childBoxes[0].node, "before");
+				} else {
+					return this.insert(e, this.childBoxes[this.childBoxes.length-1].node, "after");
+				}
+			}
+			return this.insert(e, this.domNode, "append");
+		}
+
+		var child = this.childBoxes[i];
+		if (dojo.html.gravity(child.node, e) & gravity) {
+			return this.insert(e, child.node, "before");
+		} else {
+			return this.insert(e, child.node, "after");
+		}
+	},
+
+	insert: function(e, refNode, position) {
+		var node = e.dragObject.domNode;
+
+		if(position == "before") {
+			return dojo.html.insertBefore(node, refNode);
+		} else if(position == "after") {
+			return dojo.html.insertAfter(node, refNode);
+		} else if(position == "append") {
+			refNode.appendChild(node);
+			return true;
+		}
+
+		return false;
+	}
+}, function(node, types){
+	if (arguments.length == 0) { return; }
+	this.domNode = dojo.byId(node);
+	dojo.dnd.DropTarget.call(this);
+	if(types && dojo.lang.isString(types)) {
+		types = [types];
+	}
+	this.acceptedTypes = types || [];
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragCopy.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragCopy.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragCopy.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,85 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.dnd.HtmlDragCopy");
+dojo.require("dojo.dnd.*");
+
+dojo.declare("dojo.dnd.HtmlDragCopySource", dojo.dnd.HtmlDragSource,
+function(node, type, copyOnce){
+		this.copyOnce = copyOnce;
+		this.makeCopy = true;
+},
+{
+	onDragStart: function(){
+		var dragObj = new dojo.dnd.HtmlDragCopyObject(this.dragObject, this.type, this);
+		if(this.dragClass) { dragObj.dragClass = this.dragClass; }
+
+		if (this.constrainToContainer) {
+			dragObj.constrainTo(this.constrainingContainer || this.domNode.parentNode);
+		}
+
+		return dragObj;
+	},
+	onSelected: function() {
+		for (var i=0; i<this.dragObjects.length; i++) {
+			dojo.dnd.dragManager.selectedSources.push(new dojo.dnd.HtmlDragCopySource(this.dragObjects[i]));
+		}
+	}
+});
+
+dojo.declare("dojo.dnd.HtmlDragCopyObject", dojo.dnd.HtmlDragObject,
+function(dragObject, type, source){
+		this.copySource = source;
+},
+{
+	onDragStart: function(e) {
+		dojo.dnd.HtmlDragCopyObject.superclass.onDragStart.apply(this, arguments);
+		if(this.copySource.makeCopy) {
+			this.sourceNode = this.domNode;
+			this.domNode    = this.domNode.cloneNode(true);
+		}
+	},
+	onDragEnd: function(e){
+		switch(e.dragStatus){
+			case "dropFailure": // slide back to the start
+				var startCoords = dojo.html.getAbsolutePosition(this.dragClone, true);
+				// offset the end so the effect can be seen
+				var endCoords = { left: this.dragStartPosition.x + 1,
+					top: this.dragStartPosition.y + 1};
+
+				// animate
+				var anim = dojo.lfx.slideTo(this.dragClone, endCoords, 500, dojo.lfx.easeOut);
+				var dragObject = this;
+				dojo.event.connect(anim, "onEnd", function (e) {
+					// pause for a second (not literally) and disappear
+					dojo.lang.setTimeout(function() {
+							dojo.html.removeNode(dragObject.dragClone);
+							dragObject.dragClone = null;
+							if(dragObject.copySource.makeCopy) {
+								dojo.html.removeNode(dragObject.domNode);
+								dragObject.domNode = dragObject.sourceNode;
+								dragObject.sourceNode = null;
+							}
+						},
+						200);
+				});
+				anim.play();
+				dojo.event.topic.publish('dragEnd', { source: this } );
+				return;
+		}
+		dojo.dnd.HtmlDragCopyObject.superclass.onDragEnd.apply(this, arguments);
+		this.copySource.dragObject = this.domNode;
+		if(this.copySource.copyOnce){
+			this.copySource.makeCopy = false;
+		}
+		new dojo.dnd.HtmlDragCopySource(this.sourceNode, this.type, this.copySource.copyOnce);
+		this.sourceNode = null;
+	}
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragManager.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragManager.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragManager.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,505 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.dnd.HtmlDragManager");
+dojo.require("dojo.dnd.DragAndDrop");
+dojo.require("dojo.event.*");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.html.common");
+dojo.require("dojo.html.layout");
+
+// NOTE: there will only ever be a single instance of HTMLDragManager, so it's
+// safe to use prototype properties for book-keeping.
+dojo.declare("dojo.dnd.HtmlDragManager", dojo.dnd.DragManager, {
+	/**
+	 * There are several sets of actions that the DnD code cares about in the
+	 * HTML context:
+	 *	1.) mouse-down ->
+	 *			(draggable selection)
+	 *			(dragObject generation)
+	 *		mouse-move ->
+	 *			(draggable movement)
+	 *			(droppable detection)
+	 *			(inform droppable)
+	 *			(inform dragObject)
+	 *		mouse-up
+	 *			(inform/destroy dragObject)
+	 *			(inform draggable)
+	 *			(inform droppable)
+	 *	2.) mouse-down -> mouse-down
+	 *			(click-hold context menu)
+	 *	3.) mouse-click ->
+	 *			(draggable selection)
+	 *		shift-mouse-click ->
+	 *			(augment draggable selection)
+	 *		mouse-down ->
+	 *			(dragObject generation)
+	 *		mouse-move ->
+	 *			(draggable movement)
+	 *			(droppable detection)
+	 *			(inform droppable)
+	 *			(inform dragObject)
+	 *		mouse-up
+	 *			(inform draggable)
+	 *			(inform droppable)
+	 *	4.) mouse-up
+	 *			(clobber draggable selection)
+	 */
+	disabled: false, // to kill all dragging!
+	nestedTargets: false,
+	mouseDownTimer: null, // used for click-hold operations
+	dsCounter: 0,
+	dsPrefix: "dojoDragSource",
+
+	// dimension calculation cache for use durring drag
+	dropTargetDimensions: [],
+
+	currentDropTarget: null,
+	// currentDropTargetPoints: null,
+	previousDropTarget: null,
+	_dragTriggered: false,
+
+	selectedSources: [],
+	dragObjects: [],
+
+	// mouse position properties
+	currentX: null,
+	currentY: null,
+	lastX: null,
+	lastY: null,
+	mouseDownX: null,
+	mouseDownY: null,
+	threshold: 7,
+
+	dropAcceptable: false,
+
+	cancelEvent: function(e){ e.stopPropagation(); e.preventDefault();},
+
+	// method over-rides
+	registerDragSource: function(ds){
+		//dojo.profile.start("register DragSource");
+
+		if(ds["domNode"]){
+			// FIXME: dragSource objects SHOULD have some sort of property that
+			// references their DOM node, we shouldn't just be passing nodes and
+			// expecting it to work.
+			//dojo.profile.start("register DragSource 1");
+			var dp = this.dsPrefix;
+			var dpIdx = dp+"Idx_"+(this.dsCounter++);
+			ds.dragSourceId = dpIdx;
+			this.dragSources[dpIdx] = ds;
+			ds.domNode.setAttribute(dp, dpIdx);
+			//dojo.profile.end("register DragSource 1");
+
+			//dojo.profile.start("register DragSource 2");
+
+			// so we can drag links
+			if(dojo.render.html.ie){
+				//dojo.profile.start("register DragSource IE");
+				
+				dojo.event.browser.addListener(ds.domNode, "ondragstart", this.cancelEvent);
+				// terribly slow
+				//dojo.event.connect(ds.domNode, "ondragstart", this.cancelEvent);
+				//dojo.profile.end("register DragSource IE");
+
+			}
+			//dojo.profile.end("register DragSource 2");
+
+		}
+		//dojo.profile.end("register DragSource");
+	},
+
+	unregisterDragSource: function(ds){
+		if (ds["domNode"]){
+			var dp = this.dsPrefix;
+			var dpIdx = ds.dragSourceId;
+			delete ds.dragSourceId;
+			delete this.dragSources[dpIdx];
+			ds.domNode.setAttribute(dp, null);
+			if(dojo.render.html.ie){
+				dojo.event.browser.removeListener(ds.domNode, "ondragstart", this.cancelEvent);			
+			}
+		}
+	},
+
+	registerDropTarget: function(dt){
+		this.dropTargets.push(dt);
+	},
+
+	unregisterDropTarget: function(dt){
+		var index = dojo.lang.find(this.dropTargets, dt, true);
+		if (index>=0) {
+			this.dropTargets.splice(index, 1);
+		}
+	},
+
+	/**
+	* Get the DOM element that is meant to drag.
+	* Loop through the parent nodes of the event target until
+	* the element is found that was created as a DragSource and 
+	* return it.
+	*
+	* @param event object The event for which to get the drag source.
+	*/
+	getDragSource: function(e){
+		var tn = e.target;
+		if(tn === dojo.body()){ return; }
+		var ta = dojo.html.getAttribute(tn, this.dsPrefix);
+		while((!ta)&&(tn)){
+			tn = tn.parentNode;
+			if((!tn)||(tn === dojo.body())){ return; }
+			ta = dojo.html.getAttribute(tn, this.dsPrefix);
+		}
+		return this.dragSources[ta];
+	},
+
+	onKeyDown: function(e){
+	},
+
+	onMouseDown: function(e){
+		if(this.disabled) { return; }
+
+		// only begin on left click
+		if(dojo.render.html.ie) {
+			if(e.button != 1) { return; }
+		} else if(e.which != 1) {
+			return;
+		}
+
+		var target = e.target.nodeType == dojo.html.TEXT_NODE ?
+			e.target.parentNode : e.target;
+
+		// do not start drag involvement if the user is interacting with
+		// a form element.
+		if(dojo.html.isTag(target, "button", "textarea", "input", "select", "option")) {
+			return;
+		}
+
+		// find a selection object, if one is a parent of the source node
+		var ds = this.getDragSource(e);
+		
+		// this line is important.  if we aren't selecting anything then
+		// we need to return now, so preventDefault() isn't called, and thus
+		// the event is propogated to other handling code
+		if(!ds){ return; }
+
+		if(!dojo.lang.inArray(this.selectedSources, ds)){
+			this.selectedSources.push(ds);
+			ds.onSelected();
+		}
+
+ 		this.mouseDownX = e.pageX;
+ 		this.mouseDownY = e.pageY;
+
+		// Must stop the mouse down from being propogated, or otherwise can't
+		// drag links in firefox.
+		// WARNING: preventing the default action on all mousedown events
+		// prevents user interaction with the contents.
+		e.preventDefault();
+
+		dojo.event.connect(document, "onmousemove", this, "onMouseMove");
+	},
+
+	onMouseUp: function(e, cancel){
+		// if we aren't dragging then ignore the mouse-up
+		// (in particular, don't call preventDefault(), because other
+		// code may need to process this event)
+		if(this.selectedSources.length==0){
+			return;
+		}
+
+		this.mouseDownX = null;
+		this.mouseDownY = null;
+		this._dragTriggered = false;
+ 		// e.preventDefault();
+		e.dragSource = this.dragSource;
+		// let ctrl be used for multiselect or another action
+		// if I use same key to trigger treeV3 node selection and here,
+		// I have bugs with drag'n'drop. why ?? no idea..
+		if((!e.shiftKey)&&(!e.ctrlKey)){ 
+		//if(!e.shiftKey){
+			if(this.currentDropTarget) {
+				this.currentDropTarget.onDropStart();
+			}
+			dojo.lang.forEach(this.dragObjects, function(tempDragObj){
+				var ret = null;
+				if(!tempDragObj){ return; }
+				if(this.currentDropTarget) {
+					e.dragObject = tempDragObj;
+
+					// NOTE: we can't get anything but the current drop target
+					// here since the drag shadow blocks mouse-over events.
+					// This is probelematic for dropping "in" something
+					var ce = this.currentDropTarget.domNode.childNodes;
+					if(ce.length > 0){
+						e.dropTarget = ce[0];
+						while(e.dropTarget == tempDragObj.domNode){
+							e.dropTarget = e.dropTarget.nextSibling;
+						}
+					}else{
+						e.dropTarget = this.currentDropTarget.domNode;
+					}
+					if(this.dropAcceptable){
+						ret = this.currentDropTarget.onDrop(e);
+					}else{
+						 this.currentDropTarget.onDragOut(e);
+					}
+				}
+
+				e.dragStatus = this.dropAcceptable && ret ? "dropSuccess" : "dropFailure";
+				// decouple the calls for onDragEnd, so they don't block the execution here
+				// ie. if the onDragEnd would call an alert, the execution here is blocked until the
+				// user has confirmed the alert box and then the rest of the dnd code is executed
+				// while the mouse doesnt "hold" the dragged object anymore ... and so on
+				dojo.lang.delayThese([
+					function() {
+						// in FF1.5 this throws an exception, see 
+						// http://dojotoolkit.org/pipermail/dojo-interest/2006-April/006751.html
+						try{
+							tempDragObj.dragSource.onDragEnd(e)
+						} catch(err) {
+							// since the problem seems passing e, we just copy all 
+							// properties and try the copy ...
+							var ecopy = {};
+							for (var i in e) {
+								if (i=="type") { // the type property contains the exception, no idea why...
+									ecopy.type = "mouseup";
+									continue;
+								}
+								ecopy[i] = e[i];
+							}
+							tempDragObj.dragSource.onDragEnd(ecopy);
+						}
+					}
+					, function() {tempDragObj.onDragEnd(e)}]);
+			}, this);
+
+			this.selectedSources = [];
+			this.dragObjects = [];
+			this.dragSource = null;
+			if(this.currentDropTarget) {
+				this.currentDropTarget.onDropEnd();
+			}
+		} else {
+			//dojo.debug("special click");
+		}
+
+		dojo.event.disconnect(document, "onmousemove", this, "onMouseMove");
+		this.currentDropTarget = null;
+	},
+
+	onScroll: function(){
+		//dojo.profile.start("DNDManager updateoffset");
+		for(var i = 0; i < this.dragObjects.length; i++) {
+			if(this.dragObjects[i].updateDragOffset) {
+				this.dragObjects[i].updateDragOffset();
+			}
+		}
+		//dojo.profile.end("DNDManager updateoffset");
+
+		// TODO: do not recalculate, only adjust coordinates
+		if (this.dragObjects.length) {
+			this.cacheTargetLocations();
+		}
+	},
+
+	_dragStartDistance: function(x, y){
+		if((!this.mouseDownX)||(!this.mouseDownX)){
+			return;
+		}
+		var dx = Math.abs(x-this.mouseDownX);
+		var dx2 = dx*dx;
+		var dy = Math.abs(y-this.mouseDownY);
+		var dy2 = dy*dy;
+		return parseInt(Math.sqrt(dx2+dy2), 10);
+	},
+
+	cacheTargetLocations: function(){
+		dojo.profile.start("cacheTargetLocations");
+
+		this.dropTargetDimensions = [];
+		dojo.lang.forEach(this.dropTargets, function(tempTarget){
+			var tn = tempTarget.domNode;
+			//only cache dropTarget which can accept current dragSource
+			if(!tn || dojo.lang.find(tempTarget.acceptedTypes, this.dragSource.type) < 0){ return; }
+			var abs = dojo.html.getAbsolutePosition(tn, true);
+			var bb = dojo.html.getBorderBox(tn);
+			this.dropTargetDimensions.push([
+				[abs.x, abs.y],	// upper-left
+				// lower-right
+				[ abs.x+bb.width, abs.y+bb.height ],
+				tempTarget
+			]);
+			//dojo.debug("Cached for "+tempTarget)
+		}, this);
+
+		dojo.profile.end("cacheTargetLocations");
+
+		//dojo.debug("Cache locations")
+	},
+
+	onMouseMove: function(e){
+		if((dojo.render.html.ie)&&(e.button != 1)){
+			// Oooops - mouse up occurred - e.g. when mouse was not over the
+			// window. I don't think we can detect this for FF - but at least
+			// we can be nice in IE.
+			this.currentDropTarget = null;
+			this.onMouseUp(e, true);
+			return;
+		}
+
+		// if we've got some sources, but no drag objects, we need to send
+		// onDragStart to all the right parties and get things lined up for
+		// drop target detection
+
+		if(	(this.selectedSources.length)&&
+			(!this.dragObjects.length) ){
+			var dx;
+			var dy;
+			if(!this._dragTriggered){
+				this._dragTriggered = (this._dragStartDistance(e.pageX, e.pageY) > this.threshold);
+				if(!this._dragTriggered){ return; }
+				dx = e.pageX - this.mouseDownX;
+				dy = e.pageY - this.mouseDownY;
+			}
+
+			// the first element is always our dragSource, if there are multiple
+			// selectedSources (elements that move along) then the first one is the master
+			// and for it the events will be fired etc.
+			this.dragSource = this.selectedSources[0];
+			
+			dojo.lang.forEach(this.selectedSources, function(tempSource){
+				if(!tempSource){ return; }
+				var tdo = tempSource.onDragStart(e);
+				if(tdo){
+					tdo.onDragStart(e);
+
+					// "bump" the drag object to account for the drag threshold
+					tdo.dragOffset.y += dy;
+					tdo.dragOffset.x += dx;
+					tdo.dragSource = tempSource;
+
+					this.dragObjects.push(tdo);
+				}
+			}, this);
+
+			/* clean previous drop target in dragStart */
+			this.previousDropTarget = null;
+
+			this.cacheTargetLocations();
+		}
+
+		// FIXME: we need to add dragSources and dragObjects to e
+		dojo.lang.forEach(this.dragObjects, function(dragObj){
+			if(dragObj){ dragObj.onDragMove(e); }
+		});
+
+		// if we have a current drop target, check to see if we're outside of
+		// it. If so, do all the actions that need doing.
+		if(this.currentDropTarget){
+			//dojo.debug(dojo.html.hasParent(this.currentDropTarget.domNode))
+			var c = dojo.html.toCoordinateObject(this.currentDropTarget.domNode, true);
+			//		var dtp = this.currentDropTargetPoints;
+			var dtp = [
+				[c.x,c.y], [c.x+c.width, c.y+c.height]
+			];
+		}
+
+		if((!this.nestedTargets)&&(dtp)&&(this.isInsideBox(e, dtp))){
+			if(this.dropAcceptable){
+				this.currentDropTarget.onDragMove(e, this.dragObjects);
+			}
+		}else{
+			// FIXME: need to fix the event object!
+			// see if we can find a better drop target
+			var bestBox = this.findBestTarget(e);
+
+			if(bestBox.target === null){
+				if(this.currentDropTarget){
+					this.currentDropTarget.onDragOut(e);
+					this.previousDropTarget = this.currentDropTarget;
+					this.currentDropTarget = null;
+					// this.currentDropTargetPoints = null;
+				}
+				this.dropAcceptable = false;
+				return;
+			}
+
+			if(this.currentDropTarget !== bestBox.target){
+				if(this.currentDropTarget){
+					this.previousDropTarget = this.currentDropTarget;
+					this.currentDropTarget.onDragOut(e);
+				}
+				this.currentDropTarget = bestBox.target;
+				// this.currentDropTargetPoints = bestBox.points;
+				e.dragObjects = this.dragObjects;
+				this.dropAcceptable = this.currentDropTarget.onDragOver(e);
+
+			}else{
+				if(this.dropAcceptable){
+					this.currentDropTarget.onDragMove(e, this.dragObjects);
+				}
+			}
+		}
+	},
+
+	findBestTarget: function(e) {
+		var _this = this;
+		var bestBox = new Object();
+		bestBox.target = null;
+		bestBox.points = null;
+		dojo.lang.every(this.dropTargetDimensions, function(tmpDA) {
+			if(!_this.isInsideBox(e, tmpDA)){
+				return true;
+			}
+
+			bestBox.target = tmpDA[2];
+			bestBox.points = tmpDA;
+			// continue iterating only if _this.nestedTargets == true
+			return Boolean(_this.nestedTargets);
+		});
+
+		return bestBox;
+	},
+
+	isInsideBox: function(e, coords){
+		if(	(e.pageX > coords[0][0])&&
+			(e.pageX < coords[1][0])&&
+			(e.pageY > coords[0][1])&&
+			(e.pageY < coords[1][1]) ){
+			return true;
+		}
+		return false;
+	},
+
+	onMouseOver: function(e){
+	},
+
+	onMouseOut: function(e){
+	}
+});
+
+dojo.dnd.dragManager = new dojo.dnd.HtmlDragManager();
+
+// global namespace protection closure
+(function(){
+	var d = document;
+	var dm = dojo.dnd.dragManager;
+	//TODO: when focus manager is ready, dragManager should be rewritten to use it
+	// set up event handlers on the document (or no?)
+	dojo.event.connect(d, "onkeydown", dm, "onKeyDown");
+	dojo.event.connect(d, "onmouseover", dm, "onMouseOver");
+	dojo.event.connect(d, "onmouseout", dm, "onMouseOut");
+	dojo.event.connect(d, "onmousedown", dm, "onMouseDown");
+	dojo.event.connect(d, "onmouseup", dm, "onMouseUp");
+	// TODO: process scrolling of elements, not only window (focus manager would 
+	// probably come to rescue here as well)
+	dojo.event.connect(window, "onscroll", dm, "onScroll");
+})();

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragMove.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragMove.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/HtmlDragMove.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,66 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.dnd.HtmlDragMove");
+dojo.require("dojo.dnd.*");
+
+dojo.declare("dojo.dnd.HtmlDragMoveSource", dojo.dnd.HtmlDragSource, {
+	onDragStart: function(){
+		var dragObj =  new dojo.dnd.HtmlDragMoveObject(this.dragObject, this.type);
+		if (this.constrainToContainer) {
+			dragObj.constrainTo(this.constrainingContainer);
+		}
+		return dragObj;
+	},
+	/*
+	 * see dojo.dnd.HtmlDragSource.onSelected
+	 */
+	onSelected: function() {
+		for (var i=0; i<this.dragObjects.length; i++) {
+			dojo.dnd.dragManager.selectedSources.push(new dojo.dnd.HtmlDragMoveSource(this.dragObjects[i]));
+		}
+	}
+});
+
+dojo.declare("dojo.dnd.HtmlDragMoveObject", dojo.dnd.HtmlDragObject, {
+	onDragEnd: function(e){
+		// shortly the browser will fire an onClick() event,
+		// but since this was really a drag, just squelch it
+		dojo.event.connect(this.domNode, "onclick", this, "squelchOnClick");
+	},
+	onDragStart: function(e){
+		dojo.html.clearSelection();
+
+		this.dragClone = this.domNode;
+
+		this.scrollOffset = dojo.html.getScroll().offset;
+		this.dragStartPosition = dojo.html.abs(this.domNode, true);
+		
+		this.dragOffset = {y: this.dragStartPosition.y - e.pageY,
+			x: this.dragStartPosition.x - e.pageX};
+
+		this.containingBlockPosition = this.domNode.offsetParent ? 
+			dojo.html.abs(this.domNode.offsetParent, true) : {x:0, y:0};
+
+		this.dragClone.style.position = "absolute";
+
+		if (this.constrainToContainer) {
+			this.constraints = this.getConstraints();
+		}
+	},
+	/**
+	 * Set the position of the drag node.  (x,y) is relative to <body>.
+	 */
+	setAbsolutePosition: function(x, y){
+		// The drag clone is attached to it's constraining container so offset for that
+		if(!this.disableY) { this.domNode.style.top = (y-this.containingBlockPosition.y) + "px"; }
+		if(!this.disableX) { this.domNode.style.left = (x-this.containingBlockPosition.x) + "px"; }
+	}
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/Sortable.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/Sortable.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/Sortable.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,28 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.dnd.Sortable");
+dojo.require("dojo.dnd.*");
+
+dojo.dnd.Sortable = function () {}
+
+dojo.lang.extend(dojo.dnd.Sortable, {
+
+	ondragstart: function (e) {
+		var dragObject = e.target;
+		while (dragObject.parentNode && dragObject.parentNode != this) {
+			dragObject = dragObject.parentNode;
+		}
+		// TODO: should apply HtmlDropTarget interface to self
+		// TODO: should apply HtmlDragObject interface?
+		return dragObject;
+	}
+
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/TreeDragAndDrop.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/TreeDragAndDrop.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/TreeDragAndDrop.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,475 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/**
+ * TreeDrag* specialized on managing subtree drags
+ * It selects nodes and visualises what's going on,
+ * but delegates real actions upon tree to the controller
+ *
+ * This code is considered a part of controller
+*/
+
+dojo.provide("dojo.dnd.TreeDragAndDrop");
+
+dojo.require("dojo.dnd.HtmlDragAndDrop");
+dojo.require("dojo.lang.func");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.html.layout");
+
+dojo.dnd.TreeDragSource = function(node, syncController, type, treeNode){
+	this.controller = syncController;
+	this.treeNode = treeNode;
+
+	dojo.dnd.HtmlDragSource.call(this, node, type);
+}
+
+dojo.inherits(dojo.dnd.TreeDragSource, dojo.dnd.HtmlDragSource);
+
+dojo.lang.extend(dojo.dnd.TreeDragSource, {
+	onDragStart: function(){
+		/* extend adds functions to prototype */
+		var dragObject = dojo.dnd.HtmlDragSource.prototype.onDragStart.call(this);
+		//dojo.debugShallow(dragObject)
+
+		dragObject.treeNode = this.treeNode;
+
+		dragObject.onDragStart = dojo.lang.hitch(dragObject, function(e) {
+
+			/* save selection */
+			this.savedSelectedNode = this.treeNode.tree.selector.selectedNode;
+			if (this.savedSelectedNode) {
+				this.savedSelectedNode.unMarkSelected();
+			}
+
+			var result = dojo.dnd.HtmlDragObject.prototype.onDragStart.apply(this, arguments);
+
+
+			/* remove background grid from cloned object */
+			var cloneGrid = this.dragClone.getElementsByTagName('img');
+			for(var i=0; i<cloneGrid.length; i++) {
+				cloneGrid.item(i).style.backgroundImage='url()';
+			}
+
+			return result;
+
+
+		});
+
+		dragObject.onDragEnd = function(e) {
+
+			/* restore selection */
+			if (this.savedSelectedNode) {
+				this.savedSelectedNode.markSelected();
+			}
+			//dojo.debug(e.dragStatus);
+
+			return dojo.dnd.HtmlDragObject.prototype.onDragEnd.apply(this, arguments);
+		}
+		//dojo.debug(dragObject.domNode.outerHTML)
+
+
+		return dragObject;
+	},
+
+	onDragEnd: function(e){
+
+
+		 var res = dojo.dnd.HtmlDragSource.prototype.onDragEnd.call(this, e);
+
+
+		 return res;
+	}
+});
+
+// .......................................
+
+dojo.dnd.TreeDropTarget = function(domNode, controller, type, treeNode){
+
+	this.treeNode = treeNode;
+	this.controller = controller; // I will sync-ly process drops
+	
+	dojo.dnd.HtmlDropTarget.apply(this, [domNode, type]);
+}
+
+dojo.inherits(dojo.dnd.TreeDropTarget, dojo.dnd.HtmlDropTarget);
+
+dojo.lang.extend(dojo.dnd.TreeDropTarget, {
+
+	autoExpandDelay: 1500,
+	autoExpandTimer: null,
+
+
+	position: null,
+
+	indicatorStyle: "2px black solid",
+
+	showIndicator: function(position) {
+
+		// do not change style too often, cause of blinking possible
+		if (this.position == position) {
+			return;
+		}
+
+		//dojo.debug(position)
+
+		this.hideIndicator();
+
+		this.position = position;
+
+		if (position == "before") {
+			this.treeNode.labelNode.style.borderTop = this.indicatorStyle;
+		} else if (position == "after") {
+			this.treeNode.labelNode.style.borderBottom = this.indicatorStyle;
+		} else if (position == "onto") {
+			this.treeNode.markSelected();
+		}
+
+
+	},
+
+	hideIndicator: function() {
+		this.treeNode.labelNode.style.borderBottom="";
+		this.treeNode.labelNode.style.borderTop="";
+		this.treeNode.unMarkSelected();
+		this.position = null;
+	},
+
+
+
+	// is the target possibly ok ?
+	// This function is run on dragOver, but drop possibility is also determined by position over node
+	// that's why acceptsWithPosition is called
+	// doesnt take index into account ( can change while moving mouse w/o changing target )
+
+
+	/**
+	 * Coarse (tree-level) access check.
+	 * We can't determine real accepts status w/o position
+	*/
+	onDragOver: function(e){
+//dojo.debug("onDragOver for "+e);
+
+
+		var accepts = dojo.dnd.HtmlDropTarget.prototype.onDragOver.apply(this, arguments);
+
+		//dojo.debug("TreeDropTarget.onDragOver accepts:"+accepts)
+
+		if (accepts && this.treeNode.isFolder && !this.treeNode.isExpanded) {
+			this.setAutoExpandTimer();
+		}
+
+		return accepts;
+	},
+
+	/* Parent.onDragOver calls this function to get accepts status */
+	accepts: function(dragObjects) {
+
+		var accepts = dojo.dnd.HtmlDropTarget.prototype.accepts.apply(this, arguments);
+
+		if (!accepts) return false;
+
+		var sourceTreeNode = dragObjects[0].treeNode;
+
+		if (dojo.lang.isUndefined(sourceTreeNode) || !sourceTreeNode || !sourceTreeNode.isTreeNode) {
+			dojo.raise("Source is not TreeNode or not found");
+		}
+
+		if (sourceTreeNode === this.treeNode) return false;
+
+		return true;
+	},
+
+
+
+	setAutoExpandTimer: function() {
+		// set up autoexpand timer
+		var _this = this;
+
+		var autoExpand = function () {
+			if (dojo.dnd.dragManager.currentDropTarget === _this) {
+				_this.controller.expand(_this.treeNode);
+			}
+		}
+
+		this.autoExpandTimer = dojo.lang.setTimeout(autoExpand, _this.autoExpandDelay);
+	},
+
+
+	getDNDMode: function() {
+		return this.treeNode.tree.DNDMode;
+	},
+		
+
+	getAcceptPosition: function(e, sourceTreeNode) {
+
+		var DNDMode = this.getDNDMode();
+
+		if (DNDMode & dojo.widget.Tree.prototype.DNDModes.ONTO &&
+			// check if ONTO is allowed localy
+			!(
+			  !this.treeNode.actionIsDisabled(dojo.widget.TreeNode.prototype.actions.ADDCHILD) // check dynamically cause may change w/o regeneration of dropTarget
+			  && sourceTreeNode.parent !== this.treeNode
+			  && this.controller.canMove(sourceTreeNode, this.treeNode)
+			 )
+		) {
+			// disable ONTO if can't move
+			DNDMode &= ~dojo.widget.Tree.prototype.DNDModes.ONTO;
+		}
+
+
+		var position = this.getPosition(e, DNDMode);
+
+		//dojo.debug(DNDMode & +" : "+position);
+
+
+		// if onto is here => it was allowed before, no accept check is needed
+		if (position=="onto" ||
+			(!this.isAdjacentNode(sourceTreeNode, position)
+			 && this.controller.canMove(sourceTreeNode, this.treeNode.parent)
+			)
+		) {
+			return position;
+		} else {
+			return false;
+		}
+
+	},
+
+	onDragOut: function(e) {
+		this.clearAutoExpandTimer();
+
+		this.hideIndicator();
+	},
+
+
+	clearAutoExpandTimer: function() {
+		if (this.autoExpandTimer) {
+			clearTimeout(this.autoExpandTimer);
+			this.autoExpandTimer = null;
+		}
+	},
+
+
+
+	onDragMove: function(e, dragObjects){
+
+		var sourceTreeNode = dragObjects[0].treeNode;
+
+		var position = this.getAcceptPosition(e, sourceTreeNode);
+
+		if (position) {
+			this.showIndicator(position);
+		}
+
+	},
+
+	isAdjacentNode: function(sourceNode, position) {
+
+		if (sourceNode === this.treeNode) return true;
+		if (sourceNode.getNextSibling() === this.treeNode && position=="before") return true;
+		if (sourceNode.getPreviousSibling() === this.treeNode && position=="after") return true;
+
+		return false;
+	},
+
+
+	/* get DNDMode and see which position e fits */
+	getPosition: function(e, DNDMode) {
+		var node = dojo.byId(this.treeNode.labelNode);
+		var mousey = e.pageY || e.clientY + dojo.body().scrollTop;
+		var nodey = dojo.html.getAbsolutePosition(node).y;
+		var height = dojo.html.getBorderBox(node).height;
+
+		var relY = mousey - nodey;
+		var p = relY / height;
+
+		var position = ""; // "" <=> forbidden
+		if (DNDMode & dojo.widget.Tree.prototype.DNDModes.ONTO
+		  && DNDMode & dojo.widget.Tree.prototype.DNDModes.BETWEEN) {
+			if (p<=0.3) {
+				position = "before";
+			} else if (p<=0.7) {
+				position = "onto";
+			} else {
+				position = "after";
+			}
+		} else if (DNDMode & dojo.widget.Tree.prototype.DNDModes.BETWEEN) {
+			if (p<=0.5) {
+				position = "before";
+			} else {
+				position = "after";
+			}
+		}
+		else if (DNDMode & dojo.widget.Tree.prototype.DNDModes.ONTO) {
+			position = "onto";
+		}
+
+
+		return position;
+	},
+
+
+
+	getTargetParentIndex: function(sourceTreeNode, position) {
+
+		var index = position == "before" ? this.treeNode.getParentIndex() : this.treeNode.getParentIndex()+1;
+		if (this.treeNode.parent === sourceTreeNode.parent
+		  && this.treeNode.getParentIndex() > sourceTreeNode.getParentIndex()) {
+		  	index--;  // dragging a node is different for simple move bacause of before-after issues
+		}
+
+		return index;
+	},
+
+
+	onDrop: function(e){
+		// onDragOut will clean position
+
+
+		var position = this.position;
+
+//dojo.debug(position);
+
+		this.onDragOut(e);
+
+		var sourceTreeNode = e.dragObject.treeNode;
+
+		if (!dojo.lang.isObject(sourceTreeNode)) {
+			dojo.raise("TreeNode not found in dragObject")
+		}
+
+		if (position == "onto") {
+			return this.controller.move(sourceTreeNode, this.treeNode, 0);
+		} else {
+			var index = this.getTargetParentIndex(sourceTreeNode, position);
+			return this.controller.move(sourceTreeNode, this.treeNode.parent, index);
+		}
+
+		//dojo.debug('drop2');
+
+
+
+	}
+
+
+});
+
+
+
+dojo.dnd.TreeDNDController = function(treeController) {
+
+	// I use this controller to perform actions
+	this.treeController = treeController;
+
+	this.dragSources = {};
+
+	this.dropTargets = {};
+
+}
+
+dojo.lang.extend(dojo.dnd.TreeDNDController, {
+
+
+	listenTree: function(tree) {
+		//dojo.debug("Listen tree "+tree);
+		dojo.event.topic.subscribe(tree.eventNames.createDOMNode, this, "onCreateDOMNode");
+		dojo.event.topic.subscribe(tree.eventNames.moveFrom, this, "onMoveFrom");
+		dojo.event.topic.subscribe(tree.eventNames.moveTo, this, "onMoveTo");
+		dojo.event.topic.subscribe(tree.eventNames.addChild, this, "onAddChild");
+		dojo.event.topic.subscribe(tree.eventNames.removeNode, this, "onRemoveNode");
+		dojo.event.topic.subscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy");
+	},
+
+
+	unlistenTree: function(tree) {
+		//dojo.debug("Listen tree "+tree);
+		dojo.event.topic.unsubscribe(tree.eventNames.createDOMNode, this, "onCreateDOMNode");
+		dojo.event.topic.unsubscribe(tree.eventNames.moveFrom, this, "onMoveFrom");
+		dojo.event.topic.unsubscribe(tree.eventNames.moveTo, this, "onMoveTo");
+		dojo.event.topic.unsubscribe(tree.eventNames.addChild, this, "onAddChild");
+		dojo.event.topic.unsubscribe(tree.eventNames.removeNode, this, "onRemoveNode");
+		dojo.event.topic.unsubscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy");
+	},
+
+	onTreeDestroy: function(message) {
+		this.unlistenTree(message.source);
+		// I'm not widget so don't use destroy() call and dieWithTree
+	},
+
+	onCreateDOMNode: function(message) {
+		this.registerDNDNode(message.source);
+	},
+
+	onAddChild: function(message) {
+		this.registerDNDNode(message.child);
+	},
+
+	onMoveFrom: function(message) {
+		var _this = this;
+		dojo.lang.forEach(
+			message.child.getDescendants(),
+			function(node) { _this.unregisterDNDNode(node); }
+		);
+	},
+
+	onMoveTo: function(message) {
+		var _this = this;
+		dojo.lang.forEach(
+			message.child.getDescendants(),
+			function(node) { _this.registerDNDNode(node); }
+		);
+	},
+
+	/**
+	 * Controller(node model) creates DNDNodes because it passes itself to node for synchroneous drops processing
+	 * I can't process DnD with events cause an event can't return result success/false
+	*/
+	registerDNDNode: function(node) {
+		if (!node.tree.DNDMode) return;
+
+//dojo.debug("registerDNDNode "+node);
+
+		/* I drag label, not domNode, because large domNodes are very slow to copy and large to drag */
+
+		var source = null;
+		var target = null;
+
+		if (!node.actionIsDisabled(node.actions.MOVE)) {
+			//dojo.debug("reg source")
+			var source = new dojo.dnd.TreeDragSource(node.labelNode, this, node.tree.widgetId, node);
+			this.dragSources[node.widgetId] = source;
+		}
+
+		var target = new dojo.dnd.TreeDropTarget(node.labelNode, this.treeController, node.tree.DNDAcceptTypes, node);
+
+		this.dropTargets[node.widgetId] = target;
+
+	},
+
+
+	unregisterDNDNode: function(node) {
+
+		if (this.dragSources[node.widgetId]) {
+			dojo.dnd.dragManager.unregisterDragSource(this.dragSources[node.widgetId]);
+			delete this.dragSources[node.widgetId];
+		}
+
+		if (this.dropTargets[node.widgetId]) {
+			dojo.dnd.dragManager.unregisterDropTarget(this.dropTargets[node.widgetId]);
+			delete this.dropTargets[node.widgetId];
+		}
+	}
+
+
+
+
+
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/TreeDragAndDropV3.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/TreeDragAndDropV3.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/TreeDragAndDropV3.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,404 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/**
+ * TreeDrag* specialized on managing subtree drags
+ * It selects nodes and visualises what's going on,
+ * but delegates real actions upon tree to the controller
+ *
+ * This code is considered a part of controller
+*/
+
+dojo.provide("dojo.dnd.TreeDragAndDropV3");
+
+dojo.require("dojo.dnd.HtmlDragAndDrop");
+dojo.require("dojo.lang.func");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.Deferred");
+dojo.require("dojo.html.layout");
+
+// FIXME: if controller can't move then skip node on move start
+dojo.dnd.TreeDragSourceV3 = function(node, syncController, type, treeNode){
+	//dojo.profile.start("TreeDragSourceV3 "+treeNode);
+	this.controller = syncController;
+	this.treeNode = treeNode;
+
+	dojo.dnd.HtmlDragSource.call(this, node, type);
+	//dojo.profile.end("TreeDragSourceV3 "+treeNode);
+
+}
+
+dojo.inherits(dojo.dnd.TreeDragSourceV3, dojo.dnd.HtmlDragSource);
+
+
+// .......................................
+
+dojo.dnd.TreeDropTargetV3 = function(domNode, controller, type, treeNode){
+
+	this.treeNode = treeNode;
+	this.controller = controller; // I will sync-ly process drops
+	
+	dojo.dnd.HtmlDropTarget.call(this, domNode, type);
+}
+
+dojo.inherits(dojo.dnd.TreeDropTargetV3, dojo.dnd.HtmlDropTarget);
+
+dojo.lang.extend(dojo.dnd.TreeDropTargetV3, {
+
+	autoExpandDelay: 1500,
+	autoExpandTimer: null,
+
+
+	position: null,
+
+	indicatorStyle: "2px black groove",
+
+	showIndicator: function(position) {
+
+		// do not change style too often, cause of blinking possible
+		if (this.position == position) {
+			return;
+		}
+
+		//dojo.debug("set position for "+this.treeNode)
+
+		this.hideIndicator();
+
+		this.position = position;
+		
+		var node = this.treeNode;
+			
+		
+		node.contentNode.style.width = dojo.html.getBorderBox(node.labelNode).width + "px";
+
+		if (position == "onto") {					
+			node.contentNode.style.border = this.indicatorStyle;
+		} else {
+			// FIXME: bottom-top or highlight should cover ONLY top/bottom or div itself,
+			// not span whole line (try Dnd)
+			// FAILURE: Can't put span inside div: multiline bottom-top will span multiple lines
+			if (position == "before") {
+				node.contentNode.style.borderTop = this.indicatorStyle;
+			} else if (position == "after") {
+				node.contentNode.style.borderBottom = this.indicatorStyle;
+			}									
+		}  
+	},
+
+	hideIndicator: function() {
+		this.treeNode.contentNode.style.borderBottom = "";
+		this.treeNode.contentNode.style.borderTop = "";
+		this.treeNode.contentNode.style.border = "";
+		this.treeNode.contentNode.style.width=""
+		this.position = null;
+	},
+
+
+
+	// is the target possibly ok ?
+	// This function is run on dragOver, but drop possibility is also determined by position over node
+	// that's why acceptsWithPosition is called
+	// doesnt take index into account ( can change while moving mouse w/o changing target )
+	/**
+	 * Coarse (tree-level) access check.
+	 * We can't determine real accepts status w/o position
+	*/
+	onDragOver: function(e){
+		//dojo.debug("onDragOver for "+e);
+
+		var accepts = dojo.dnd.HtmlDropTarget.prototype.onDragOver.apply(this, arguments);
+
+		//dojo.debug("TreeDropTarget.onDragOver accepts:"+accepts)
+
+		if (accepts && this.treeNode.isFolder && !this.treeNode.isExpanded) {
+			this.setAutoExpandTimer();
+		}
+		
+		if (accepts) {
+			this.cacheNodeCoords();
+		}
+
+
+		return accepts;
+	},
+
+	/* Parent.onDragOver calls this function to get accepts status */
+	accepts: function(dragObjects) {
+
+		var accepts = dojo.dnd.HtmlDropTarget.prototype.accepts.apply(this, arguments);
+
+		//dojo.debug("accepts "+accepts);
+
+		if (!accepts) return false;
+
+		for(var i=0; i<dragObjects.length; i++) {
+			// there may be NO treeNode
+			var sourceTreeNode = dragObjects[i].dragSource.treeNode;
+			
+			if (sourceTreeNode === this.treeNode) return false;
+		}
+
+		return true;
+	},
+
+
+
+	setAutoExpandTimer: function() {
+		// set up autoexpand timer
+		var _this = this;
+
+		var autoExpand = function () {
+			if (dojo.dnd.dragManager.currentDropTarget === _this) {
+				_this.controller.expand(_this.treeNode);
+				// SLOW. Coordinates will not be recalculated if collapse occurs, or
+				// other (generic) resize. So that's a kind of hack.
+				dojo.dnd.dragManager.cacheTargetLocations();
+			}
+		}
+
+		this.autoExpandTimer = dojo.lang.setTimeout(autoExpand, _this.autoExpandDelay);
+	},
+
+		
+
+	getAcceptPosition: function(e, dragObjects) {
+
+
+		var DndMode = this.treeNode.tree.DndMode;
+
+		// disable ONTO mode possibility if impossible 
+		if (DndMode & dojo.widget.TreeV3.prototype.DndModes.ONTO &&
+			// check if ONTO is allowed localy
+			// check dynamically cause may change w/o regeneration of dropTarget
+			this.treeNode.actionIsDisabledNow(this.treeNode.actions.ADDCHILD) 
+		) {
+			// disable ONTO if can't move
+			DndMode &= ~dojo.widget.TreeV3.prototype.DndModes.ONTO;
+		}
+		
+
+		var position = this.getPosition(e, DndMode);
+
+		//dojo.debug(DndMode & +" : "+position);
+
+
+		// if onto is here => it was allowed before, no accept check is needed
+		if (position=="onto") {
+			return position;
+		}
+		
+		for(var i=0; i<dragObjects.length; i++) {
+			var source = dragObjects[i].dragSource;
+			if (source.treeNode && this.isAdjacentNode(source.treeNode, position)) { // skip check if same parent
+				continue;
+			}		
+						
+			if (!this.controller.canMove(source.treeNode ? source.treeNode : source, this.treeNode.parent)) {
+				return false;
+			}
+		}
+		
+		return position;
+	
+	},
+
+	
+
+	onDropEnd: function(e) {
+		this.clearAutoExpandTimer();
+
+		this.hideIndicator();
+	},
+
+
+	onDragOut: function(e) {
+		this.clearAutoExpandTimer();
+
+		this.hideIndicator();
+	},
+
+	clearAutoExpandTimer: function() {
+		if (this.autoExpandTimer) {
+			clearTimeout(this.autoExpandTimer);
+			this.autoExpandTimer = null;
+		}
+	},
+
+
+
+	onDragMove: function(e, dragObjects){
+		
+		var position = this.getAcceptPosition(e, dragObjects);
+
+		if (position) {
+			this.showIndicator(position);
+		}
+
+	},
+
+	isAdjacentNode: function(sourceNode, position) {
+
+		if (sourceNode === this.treeNode) return true;
+		if (sourceNode.getNextSibling() === this.treeNode && position=="before") return true;
+		if (sourceNode.getPreviousSibling() === this.treeNode && position=="after") return true;
+
+		return false;
+	},
+
+
+	/**
+	 * cache node coordinates to speed up onDragMove
+	 */
+	cacheNodeCoords: function() {
+		var node = this.treeNode.contentNode;
+		
+		this.cachedNodeY = dojo.html.getAbsolutePosition(node).y;
+		this.cachedNodeHeight = dojo.html.getBorderBox(node).height;
+	},
+	
+	
+
+	/* get DndMode and see which position e fits */
+	getPosition: function(e, DndMode) {
+		var mousey = e.pageY || e.clientY + dojo.body().scrollTop;
+		
+		var relY = mousey - this.cachedNodeY;
+		var p = relY / this.cachedNodeHeight;
+
+		var position = ""; // "" <=> forbidden
+		if (DndMode & dojo.widget.TreeV3.prototype.DndModes.ONTO
+		  && DndMode & dojo.widget.TreeV3.prototype.DndModes.BETWEEN) {
+			//dojo.debug("BOTH");
+			if (p<=0.33) {
+				position = "before";
+				// if children are expanded then I ignore understrike, cause it is confusing with firstChild
+				// but for last nodes I put understrike there
+			} else if (p<=0.66 || this.treeNode.isExpanded && this.treeNode.children.length && !this.treeNode.isLastChild()) {
+				position = "onto";
+			} else {
+				position = "after";
+			}
+		} else if (DndMode & dojo.widget.TreeV3.prototype.DndModes.BETWEEN) {
+			//dojo.debug("BETWEEN");
+			if (p<=0.5 || this.treeNode.isExpanded && this.treeNode.children.length && !this.treeNode.isLastChild()) {
+				position = "before";
+			} else {
+				position = "after";
+			}
+		}
+		else if (DndMode & dojo.widget.TreeV3.prototype.DndModes.ONTO) {
+			//dojo.debug("ONTO");
+			position = "onto";
+		}
+
+		//dojo.debug(position);
+
+		return position;
+	},
+
+
+
+	getTargetParentIndex: function(source, position) {
+
+		var index = position == "before" ? this.treeNode.getParentIndex() : this.treeNode.getParentIndex()+1;
+		if (source.treeNode
+		  && this.treeNode.parent === source.treeNode.parent
+		  && this.treeNode.getParentIndex() > source.treeNode.getParentIndex()) {
+		  	index--;  // dragging a node is different for simple move bacause of before-after issues
+		}
+
+		return index;
+	},
+
+
+	onDrop: function(e) {
+		// onDropEnd will clean position
+
+		
+		var position = this.position;
+
+//dojo.debug(position);
+		var source = e.dragObject.dragSource;
+		
+		//dojo.debug("onDrop "+source.treeNode+" " + position + " "+this.treeNode);
+
+
+		var targetParent, targetIndex;
+		if (position == "onto") {
+			targetParent = this.treeNode;
+			targetIndex = 0;
+		} else {
+			targetIndex = this.getTargetParentIndex(source, position);
+			targetParent = this.treeNode.parent;
+		}
+		
+		//dojo.profile.start("onDrop "+sourceTreeNode);
+		var r = this.getDropHandler(e, source, targetParent, targetIndex)();
+		
+		//dojo.profile.end("onDrop "+sourceTreeNode);
+			
+		return r;
+
+	},
+	
+	/**
+	 * determine, which action I should perform with nodes
+	 * e.g move, clone..
+	 */
+	getDropHandler: function(e, source, targetParent, targetIndex) {
+		var handler;
+		var _this = this;
+		handler = function () {
+			var result;
+			
+			//dojo.debug("Move "+source.treeNode+" to parent "+targetParent+":"+targetIndex);
+			if (source.treeNode) {
+				result = _this.controller.move(source.treeNode, targetParent, targetIndex, true);
+				//dojo.debug("moved "+result);
+			} else {
+				if (dojo.lang.isFunction(source.onDrop)) {
+					source.onDrop(targetParent, targetIndex);
+				}
+				
+				var treeNode = source.getTreeNode();
+				if (treeNode) {
+					result = _this.controller.createChild(targetParent, targetIndex, treeNode, true);
+				} else {
+					result = true;
+				}
+			}
+			
+			if (result instanceof dojo.Deferred) {
+				// this Deferred is always sync
+				var isSuccess = result.fired == 0;
+				if (!isSuccess) {
+					_this.handleDropError(source, targetParent, targetIndex, result);
+				}
+				
+				return isSuccess;				
+				
+			} else {
+				return result;
+			}
+		}
+		
+		return handler;
+	},
+	
+	
+	handleDropError: function(source, parent, index, result) {
+		dojo.debug("TreeDropTargetV3.handleDropError: DND error occured");
+		dojo.debugShallow(result);
+	}
+
+
+});
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/__package__.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/__package__.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dnd/__package__.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,16 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.kwCompoundRequire({
+	common: ["dojo.dnd.DragAndDrop"],
+	browser: ["dojo.dnd.HtmlDragAndDrop"],
+	dashboard: ["dojo.dnd.HtmlDragAndDrop"]
+});
+dojo.provide("dojo.dnd.*");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/docs.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/docs.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/docs.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,1048 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.docs");
+dojo.require("dojo.io.*");
+dojo.require("dojo.event.topic");
+dojo.require("dojo.rpc.JotService");
+dojo.require("dojo.dom");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.Deferred");
+dojo.require("dojo.DeferredList");
+
+/*
+ * TODO:
+ *
+ * Package summary needs to compensate for "is"
+ * Handle host environments
+ * Deal with dojo.widget weirdness
+ * Parse parameters
+ * Limit function parameters to only the valid ones (Involves packing parameters onto meta during rewriting)
+ *
+ */
+
+dojo.docs = new function() {
+	this._url = dojo.uri.dojoUri("docscripts");
+	this._rpc = new dojo.rpc.JotService;
+	this._rpc.serviceUrl = dojo.uri.dojoUri("docscripts/jsonrpc.php");
+};
+dojo.lang.mixin(dojo.docs, {
+	_count: 0,
+	_callbacks: {function_names: []},
+	_cache: {}, // Saves the JSON objects in cache
+	require: function(/*String*/ require, /*bool*/ sync) {
+		dojo.debug("require(): " + require);
+		var parts = require.split("/");
+		var size = parts.length;
+		var deferred = new dojo.Deferred;
+		var args = {
+			mimetype: "text/json",
+			load: function(type, data){
+				dojo.debug("require(): loaded");
+				
+				if(parts[0] != "function_names") {
+					for(var i = 0, part; part = parts[i]; i++){
+						data = data[part];
+					}
+				}
+				deferred.callback(data);
+			},
+			error: function(){
+				deferred.errback();
+			}
+		};
+		
+		if (sync) {
+			args.sync = true;
+		}
+
+		if(location.protocol == "file:"){
+			if(size){
+				if(parts[0] == "function_names"){
+					args.url = [this._url, "local_json", "function_names"].join("/");
+				}else{
+					var dirs = parts[0].split(".");
+					args.url = [this._url, "local_json", dirs[0]].join("/");
+					if(dirs.length > 1){
+						args.url = [args.url, dirs[1]].join(".");
+					}
+				}
+			}
+		}
+		
+		dojo.io.bind(args);
+		return deferred;
+	},
+	getFunctionNames: function(){
+		return this.require("function_names"); // dojo.Deferred
+	},
+	unFormat: function(/*String*/ string){
+		var fString = string;
+		if(string.charAt(string.length - 1) == "_"){
+			fString = [string.substring(0, string.length - 1), "*"].join("");
+		}
+		return fString;
+	},
+	getMeta: function(/*mixed*/ selectKey, /*String*/ pkg, /*String*/ name, /*Function*/ callback, /*String?*/ id){
+		// summary: Gets information about a function in regards to its meta data
+		if(typeof name == "function"){
+			// pId: a
+			// pkg: ignore
+			id = callback;
+			callback = name;
+			name = pkg;
+			pkg = null;
+			dojo.debug("getMeta(" + name + ")");
+		}else{
+			dojo.debug("getMeta(" + pkg + "/" + name + ")");
+		}
+		
+		if(!id){
+			id = "_";
+		}
+
+		if(!selectKey){
+			selectKey = ++dojo.docs._count;
+		}
+
+		var input;
+		if(typeof selectKey == "object" && selectKey.selectKey){
+			input = selectKey;
+			selectKey = selectKey.selectKey;
+		}else{
+			input = {};
+		}
+
+		dojo.docs._buildCache({
+			type: "meta",
+			callbacks: [dojo.docs._gotMeta, callback],
+			pkg: pkg,
+			name: name,
+			id: id,
+			selectKey: selectKey,
+			input: input
+		});
+	},
+	_withPkg: function(/*String*/ type, /*Object*/ data, /*Object*/ evt, /*Object*/ input, /*String*/ newType){
+		dojo.debug("_withPkg(" + evt.name + ") has package: " + data[0]);
+		evt.pkg = data[0];
+		if("load" == type && evt.pkg){
+			evt.type = newType;
+			dojo.docs._buildCache(evt);
+		}else{
+			if(evt.callbacks && evt.callbacks.length){
+				evt.callbacks.shift()("error", {}, evt, evt.input);
+			}
+		}
+	},
+	_gotMeta: function(/*String*/ type, /*Object*/ data, /*Object*/ evt){
+		dojo.debug("_gotMeta(" + evt.name + ")");
+
+		var cached = dojo.docs._getCache(evt.pkg, evt.name, "meta", "functions", evt.id);
+		if(cached.summary){
+			data.summary = cached.summary;
+		}
+		if(evt.callbacks && evt.callbacks.length){
+			evt.callbacks.shift()(type, data, evt, evt.input);
+		}
+	},
+	getSrc: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback, /*String?*/ id){
+		// summary: Gets src file (created by the doc parser)
+		dojo.debug("getSrc(" + name + ")");
+		if(!id){
+			id = "_";
+		}
+		if(!selectKey){
+			selectKey = ++dojo.docs._count;
+		}
+		
+		var input;
+		if(typeof selectKey == "object" && selectKey.selectKey){
+			input = selectKey;
+			selectKey = selectKey.selectKey;
+		}else{
+			input = {};
+		}
+		
+		dojo.docs._buildCache({
+			type: "src",
+			callbacks: [callback],
+			name: name,
+			id: id,
+			input: input,
+			selectKey: selectKey
+		});
+	},
+	getDoc: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback, /*String?*/ id){
+		// summary: Gets external documentation stored on Jot for a given function
+		dojo.debug("getDoc(" + name  + ")");
+
+		if(!id){
+			id = "_";
+		}
+
+		if(!selectKey){
+			selectKey = ++dojo.docs._count;
+		}
+
+		var input = {};
+		if(typeof selectKey == "object" && selectKey.selectKey){
+			input.input = selectKey;
+			selectKey = selectKey.selectKey;
+		}
+
+		input.type = "doc";
+		input.name = name;
+		input.selectKey = selectKey;
+		input.callbacks = [callback];
+		input.selectKey = selectKey;
+
+		dojo.docs._buildCache(input);
+	},
+	_gotDoc: function(/*String*/ type, /*Array*/ data, /*Object*/ evt, /*Object*/ input){
+		dojo.debug("_gotDoc(" + evt.type + ")");
+		
+		evt[evt.type] = data;
+		if(evt.expects && evt.expects.doc){
+			for(var i = 0, expect; expect = evt.expects.doc[i]; i++){
+				if(!(expect in evt)){
+					dojo.debug("_gotDoc() waiting for more data");
+					return;
+				}
+			}
+		}
+		
+		var cache = dojo.docs._getCache(evt.pkg, "meta", "functions", evt.name, evt.id, "meta");
+
+		var description = evt.fn.description;
+		cache.description = description;
+		data = {
+			returns: evt.fn.returns,
+			id: evt.id,
+			variables: [],
+			selectKey: evt.selectKey
+		}
+		if(!cache.parameters){
+			cache.parameters = {};
+		}
+		for(var i = 0, param; param = evt.param[i]; i++){
+			var fName = param["DocParamForm/name"];
+			if(!cache.parameters[fName]){
+				cache.parameters[fName] = {};
+			}
+			cache.parameters[fName].description = param["DocParamForm/desc"]
+		}
+
+		data.description = cache.description;
+		data.parameters = cache.parameters;
+		
+		evt.type = "doc";
+	
+		if(evt.callbacks && evt.callbacks.length){
+			evt.callbacks.shift()("load", data, evt, input);
+		}
+	},
+	getPkgDoc: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback){
+		// summary: Gets external documentation stored on Jot for a given package
+		dojo.debug("getPkgDoc(" + name + ")");
+		var input = {};
+		if(typeof selectKey == "object" && selectKey.selectKey){
+			input = selectKey;
+			selectKey = selectKey.selectKey;
+		}
+		if(!selectKey){
+			selectKey = ++dojo.docs._count;
+		}
+		dojo.docs._buildCache({
+			type: "pkgdoc",
+			callbacks: [callback],
+			name: name,
+			selectKey: selectKey,
+			input: input
+		});
+	},
+	getPkgInfo: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback){
+		// summary: Gets a combination of the metadata and external documentation for a given package
+		dojo.debug("getPkgInfo(" + name + ")");
+		if(!selectKey){
+			selectKey = ++dojo.docs._count;
+		}
+
+		var input = {
+			selectKey: selectKey,
+			expects: {
+				pkginfo: ["pkgmeta", "pkgdoc"]
+			},
+			callback: callback
+		};
+		dojo.docs.getPkgMeta(input, name, dojo.docs._getPkgInfo);
+		dojo.docs.getPkgDoc(input, name, dojo.docs._getPkgInfo);
+	},
+	_getPkgInfo: function(/*String*/ type, /*Object*/ data, /*Object*/ evt){
+		dojo.debug("_getPkgInfo() for " + evt.type);
+		var key = evt.selectKey;
+		var input = {};
+		var results = {};
+		if(typeof key == "object"){
+			input = key;
+			key = key.selectKey;
+			input[evt.type] = data;
+			if(input.expects && input.expects.pkginfo){
+				for(var i = 0, expect; expect = input.expects.pkginfo[i]; i++){
+					if(!(expect in input)){
+						dojo.debug("_getPkgInfo() waiting for more data");
+						return;
+					}
+				}
+			}
+			results = input.pkgmeta;
+			results.description = input.pkgdoc;
+		}
+
+		if(input.callback){
+			input.callback("load", results, evt);
+		}
+	},
+	getInfo: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback){
+		dojo.debug("getInfo(" + name + ")");
+		var input = {
+			expects: {
+				"info": ["meta", "doc"]
+			},
+			selectKey: selectKey,
+			callback: callback
+		}
+		dojo.docs.getMeta(input, name, dojo.docs._getInfo);
+		dojo.docs.getDoc(input, name, dojo.docs._getInfo);
+	},
+	_getInfo: function(/*String*/ type, /*String*/ data, /*Object*/ evt, /*Object*/ input){
+		dojo.debug("_getInfo(" + evt.type + ")");
+		if(input && input.expects && input.expects.info){
+			input[evt.type] = data;
+			for(var i = 0, expect; expect = input.expects.info[i]; i++){
+				if(!(expect in input)){
+					dojo.debug("_getInfo() waiting for more data");
+					return;
+				}
+			}
+		}
+
+		if(input.callback){
+			input.callback("load", dojo.docs._getCache(evt.pkg, "meta", "functions", evt.name, evt.id, "meta"), evt, input);
+		}
+	},
+	_getMainText: function(/*String*/ text){
+		// summary: Grabs the innerHTML from a Jot Rech Text node
+		dojo.debug("_getMainText()");
+		return text.replace(/^<html[^<]*>/, "").replace(/<\/html>$/, "").replace(/<\w+\s*\/>/g, "");
+	},
+	getPackageMeta: function(/*Object*/ input){
+		dojo.debug("getPackageMeta(): " + input.pkg);
+		return this.require(input.pkg + "/meta", input.sync);
+	},
+	OLDgetPkgMeta: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback){
+		dojo.debug("getPkgMeta(" + name + ")");
+		var input = {};
+		if(typeof selectKey == "object" && selectKey.selectKey){
+			input = selectKey;
+			selectKey = selectKey.selectKey;
+		}else if(!selectKey){
+			selectKey = ++dojo.docs._count;
+		}
+		dojo.docs._buildCache({
+			type: "pkgmeta",
+			callbacks: [callback],
+			name: name,
+			selectKey: selectKey,
+			input: input
+		});
+	},
+	OLD_getPkgMeta: function(/*Object*/ input){
+		dojo.debug("_getPkgMeta(" + input.name + ")");
+		input.type = "pkgmeta";
+		dojo.docs._buildCache(input);
+	},
+	_onDocSearch: function(/*Object*/ input){
+		var _this = this;
+		var name = input.name.toLowerCase();
+		if(!name) return;
+
+		this.getFunctionNames().addCallback(function(data){
+			dojo.debug("_onDocSearch(): function names loaded for " + name);
+
+			var output = [];
+			var list = [];
+			var closure = function(pkg, fn) {
+				return function(data){
+					dojo.debug("_onDocSearch(): package meta loaded for: " + pkg);
+					if(data.functions){
+						var functions = data.functions;
+						for(var key in functions){
+							if(fn == key){
+								var ids = functions[key];
+								for(var id in ids){
+									var fnMeta = ids[id];
+									output.push({
+										package: pkg,
+										name: fn,
+										id: id,
+										summary: fnMeta.summary
+									});
+								}
+							}
+						}
+					}
+					return output;
+				}
+			}
+
+			pkgLoop:
+			for(var pkg in data){
+				if(pkg.toLowerCase() == name){
+					name = pkg;
+					dojo.debug("_onDocSearch found a package");
+					//dojo.docs._onDocSelectPackage(input);
+					return;
+				}
+				for(var i = 0, fn; fn = data[pkg][i]; i++){
+					if(fn.toLowerCase().indexOf(name) != -1){
+						dojo.debug("_onDocSearch(): Search matched " + fn);
+						var meta = _this.getPackageMeta({pkg: pkg});
+						meta.addCallback(closure(pkg, fn));
+						list.push(meta);
+
+						// Build a list of all packages that need to be loaded and their loaded state.
+						continue pkgLoop;
+					}
+				}
+			}
+			
+			list = new dojo.DeferredList(list);
+			list.addCallback(function(results){
+				dojo.debug("_onDocSearch(): All packages loaded");
+				_this._printFunctionResults(results[0][1]);
+			});
+		});
+	},
+	_onDocSearchFn: function(/*String*/ type, /*Array*/ data, /*Object*/ evt){
+		dojo.debug("_onDocSearchFn(" + evt.name + ")");
+
+		var name = evt.name || evt.pkg;
+
+		dojo.debug("_onDocSearchFn found a function");
+
+		evt.pkgs = packages;
+		evt.pkg = name;
+		evt.loaded = 0;
+		for(var i = 0, pkg; pkg = packages[i]; i++){
+			dojo.docs.getPkgMeta(evt, pkg, dojo.docs._onDocResults);
+		}
+	},
+	_onPkgResults: function(/*String*/ type, /*Object*/ data, /*Object*/ evt, /*Object*/ input){
+		dojo.debug("_onPkgResults(" + evt.type + ")");
+		var description = "";
+		var path = "";
+		var methods = {};
+		var requires = {};
+		if(input){
+			input[evt.type] = data;
+			if(input.expects && input.expects.pkgresults){
+				for(var i = 0, expect; expect = input.expects.pkgresults[i]; i++){
+					if(!(expect in input)){
+						dojo.debug("_onPkgResults() waiting for more data");
+						return;
+					}
+				}
+			}
+			path = input.pkgdoc.path;
+			description = input.pkgdoc.description;
+			methods = input.pkgmeta.methods;
+			requires = input.pkgmeta.requires;
+		}
+		var pkg = evt.name.replace("_", "*");
+		var results = {
+			path: path,
+			description: description,
+			size: 0,
+			methods: [],
+			pkg: pkg,
+			selectKey: evt.selectKey,
+			requires: requires
+		}
+		var rePrivate = /_[^.]+$/;
+		for(var method in methods){
+			if(!rePrivate.test(method)){
+				for(var pId in methods[method]){
+					results.methods.push({
+						pkg: pkg,
+						name: method,
+						id: pId,
+						summary: methods[method][pId].summary
+					})
+				}
+			}
+		}
+		results.size = results.methods.length;
+		dojo.docs._printPkgResult(results);
+	},
+	_onDocResults: function(/*String*/ type, /*Object*/ data, /*Object*/ evt, /*Object*/ input){
+		dojo.debug("_onDocResults(" + evt.name + "/" + input.pkg + ") " + type);
+		++input.loaded;
+
+		if(input.loaded == input.pkgs.length){
+			var pkgs = input.pkgs;
+			var name = input.pkg;
+			var results = {selectKey: evt.selectKey, methods: []};
+			var rePrivate = /_[^.]+$/;
+			data = dojo.docs._cache;
+
+			for(var i = 0, pkg; pkg = pkgs[i]; i++){
+				var methods = dojo.docs._getCache(pkg, "meta", "methods");
+				for(var fn in methods){
+					if(fn.toLowerCase().indexOf(name) == -1){
+						continue;
+					}
+					if(fn != "requires" && !rePrivate.test(fn)){
+						for(var pId in methods[fn]){
+							var result = {
+								pkg: pkg,
+								name: fn,
+								id: "_",
+								summary: ""
+							}
+							if(methods[fn][pId].summary){
+								result.summary = methods[fn][pId].summary;
+							}
+							results.methods.push(result);
+						}
+					}
+				}
+			}
+
+			dojo.debug("Publishing docResults");
+			dojo.docs._printFnResults(results);
+		}
+	},
+	_printFunctionResults: function(results){
+		dojo.debug("_printFnResults(): called");
+		// summary: Call this function to send the /docs/function/results topic
+	},
+	_printPkgResult: function(results){
+		dojo.debug("_printPkgResult(): called");
+	},
+	_onDocSelectFunction: function(/*Object*/ input){
+		// summary: Get doc, meta, and src
+		var name = input.name;
+		var pkg = input.pkg;
+		dojo.debug("_onDocSelectFunction(" + name + ")");
+		if(!name){
+			return false;
+		}
+		if(!input.selectKey){
+			input.selectKey = ++dojo.docs._count;
+		}
+		input.expects = {
+			"docresults": ["meta", "doc", "pkgmeta"]
+		}
+		dojo.docs.getMeta(input, pkg, name, dojo.docs._onDocSelectResults);
+		dojo.docs.getDoc(input, pkg, name, dojo.docs._onDocSelectResults);
+	},
+	_onDocSelectPackage: function(/*Object*/ input){
+		dojo.debug("_onDocSelectPackage(" + input.name + ")")
+		input.expects = {
+			"pkgresults": ["pkgmeta", "pkgdoc"]
+		};
+		if(!input.selectKey){
+			input.selectKey = ++dojo.docs._count;
+		}
+		dojo.docs.getPkgMeta(input, input.name, dojo.docs._onPkgResults);
+		dojo.docs.getPkgDoc(input, input.name, dojo.docs._onPkgResults);
+	},
+	_onDocSelectResults: function(/*String*/ type, /*Object*/ data, /*Object*/ evt, /*Object*/ input){
+		dojo.debug("_onDocSelectResults(" + evt.type + ", " + evt.name + ")");
+		if(evt.type == "meta"){
+			dojo.docs.getPkgMeta(input, evt.pkg, dojo.docs._onDocSelectResults);
+		}
+		if(input){
+			input[evt.type] = data;
+			if(input.expects && input.expects.docresults){
+				for(var i = 0, expect; expect = input.expects.docresults[i]; i++){
+					if(!(expect in input)){
+						dojo.debug("_onDocSelectResults() waiting for more data");
+						return;
+					}
+				}
+			}
+		}
+
+		dojo.docs._printFunctionDetail(input);
+	},
+	
+	_printFunctionDetail: function(results) {
+		// summary: Call this function to send the /docs/function/detail topic event
+	},
+
+	_buildCache: function(/*Object*/ input){
+		dojo.debug("_buildCache(" + input.type + ", " + input.name + ")");
+		// Get stuff from the input object
+		var type = input.type;
+		var pkg = input.pkg;
+		var callbacks = input.callbacks;
+		var id = input.id;
+		if(!id){
+			id = input.id = "_";
+		}
+		var name = input.name;
+		var selectKey = input.selectKey;
+
+		var META = "meta";
+		var METHODS = "methods";
+		var SRC = "src";
+		var DESCRIPTION = "description";
+		var INPUT = "input";
+		var LOAD = "load";
+		var ERROR = "error";
+		
+		var docs = dojo.docs;
+		var getCache = docs._getCache;
+		
+		// Stuff to pass to RPC
+		var search = [];
+	
+		if(type == "doc"){
+			if(!pkg){
+				docs.functionPackages(selectKey, name, function(){ var a = arguments; docs._withPkg.call(this, a[0], a[1], a[2], a[3], "doc"); }, input);
+				return;
+			}else{
+				var cached = getCache(pkg, META, METHODS, name, id, META);
+			
+				if(cached[DESCRIPTION]){
+					callbacks.shift()(LOAD, cached[DESCRIPTION], input, input[INPUT]);
+					return;
+				}
+
+				var obj = {};
+				obj.forFormName = "DocFnForm";
+				obj.limit = 1;
+
+				obj.filter = "it/DocFnForm/require = '" + pkg + "' and it/DocFnForm/name = '" + name + "' and ";
+				if(id == "_"){
+					obj.filter += " not(it/DocFnForm/id)";
+				}else{
+					obj.filter += " it/DocFnForm/id = '" + id + "'";
+				}
+
+				obj.load = function(data){
+					var cached = getCache(pkg, META, METHODS, name, id, META);
+
+					var description = "";
+					var returns = "";
+					if(data.list && data.list.length){
+						description = docs._getMainText(data.list[0]["main/text"]);
+						returns = data.list[0]["DocFnForm/returns"];
+					}
+
+					cached[DESCRIPTION]  = description;
+					if(!cached.returns){
+						cached.returns = {};
+					}
+					cached.returns.summary = returns;
+
+					input.type = "fn";
+					docs._gotDoc(LOAD, cached, input, input[INPUT]);				
+				}
+				obj.error = function(data){
+					input.type = "fn";
+					docs._gotDoc(ERROR, {}, input, input[INPUT]);
+				}
+				search.push(obj);
+
+				obj = {};
+				obj.forFormName = "DocParamForm";
+
+				obj.filter = "it/DocParamForm/fns = '" + pkg + "=>" + name;
+				if(id != "_"){
+					obj.filter += "=>" + id;
+				}
+				obj.filter += "'";
+			
+				obj.load = function(data){
+					var cache = getCache(pkg, META, METHODS, name, id, META);
+					for(var i = 0, param; param = data.list[i]; i++){
+						var pName = param["DocParamForm/name"];
+						if(!cache.parameters[pName]){
+							cache.parameters[pName] = {};
+						}
+						cache.parameters[pName].summary = param["DocParamForm/desc"];
+					}
+					input.type = "param";
+					docs._gotDoc(LOAD, cache.parameters, input);
+				}
+				obj.error = function(data){
+					input.type = "param";
+					docs._gotDoc(ERROR, {}, input);
+				}
+				search.push(obj);
+			}
+		}else if(type == "pkgdoc"){
+			var cached = getCache(name, META);
+
+			if(cached[DESCRIPTION]){
+				callbacks.shift()(LOAD, {description: cached[DESCRIPTION], path: cached.path}, input, input.input);
+				return;
+			}
+
+			var obj = {};
+			obj.forFormName = "DocPkgForm";
+			obj.limit = 1;
+			obj.filter = "it/DocPkgForm/require = '" + name + "'";
+			
+			obj.load = function(data){
+				var description = "";
+				var list = data.list;
+				if(list && list.length && list[0]["main/text"]){
+					description = docs._getMainText(list[0]["main/text"]);
+					cached[DESCRIPTION] = description;
+					cached.path = list[0].name;
+				}
+
+				if(callbacks && callbacks.length){
+					callbacks.shift()(LOAD, {description: description, path: cached.path}, input, input.input);
+				}
+			}
+			obj.error = function(data){
+				if(callbacks && callbacks.length){
+					callbacks.shift()(ERROR, "", input, input.input);
+				}
+			}
+			search.push(obj);
+		}else if(type == "function_names"){
+			var cached = getCache();
+			if(!cached.function_names){
+				dojo.debug("_buildCache() new cache");
+				if(callbacks && callbacks.length){
+					docs._callbacks.function_names.push([input, callbacks.shift()]);
+				}
+				cached.function_names = {loading: true};
+				
+				var obj = {};
+				obj.url = "function_names";
+				obj.load = function(type, data, evt){
+					cached.function_names = data;
+					while(docs._callbacks.function_names.length){
+						var parts = docs._callbacks.function_names.pop();
+						parts[1](LOAD, data, parts[0]);
+					}
+				}
+				obj.error = function(type, data, evt){
+					while(docs._callbacks.function_names.length){
+						var parts = docs._callbacks.function_names.pop();
+						parts[1](LOAD, {}, parts[0]);
+					}
+				}
+				search.push(obj);
+			}else if(cached.function_names.loading){
+				dojo.debug("_buildCache() loading cache, adding to callback list");
+				if(callbacks && callbacks.length){
+					docs._callbacks.function_names.push([input, callbacks.shift()]);
+				}
+				return;
+			}else{
+				dojo.debug("_buildCache() loading from cache");
+				if(callbacks && callbacks.length){
+					callbacks.shift()(LOAD, cached.function_names, input);
+				}
+				return;
+			}
+		}else if(type == META || type == SRC){
+			if(!pkg){
+				if(type == META){
+					docs.functionPackages(selectKey, name, function(){ var a = arguments; docs._withPkg.call(this, a[0], a[1], a[2], a[3], META); }, input);
+					return;
+				}else{
+					docs.functionPackages(selectKey, name, function(){ var a = arguments; docs._withPkg.call(this, a[0], a[1], a[2], a[3], SRC); }, input);
+					return;
+				}
+			}else{
+				var cached = getCache(pkg, META, METHODS, name, id);
+
+				if(cached[type] && cached[type].returns){
+					if(callbacks && callbacks.length){
+						callbacks.shift()(LOAD, cached[type], input);
+						return;
+					}
+				}
+
+				dojo.debug("Finding " + type + " for: " + pkg + ", function: " + name + ", id: " + id);
+
+				var obj = {};
+
+				if(type == SRC){
+					obj.mimetype = "text/plain"
+				}
+				obj.url = pkg + "/" + name + "/" + id + "/" + type;
+				obj.load = function(type, data, evt){
+					dojo.debug("_buildCache() loaded " + input.type);
+
+					if(input.type == SRC){
+						getCache(pkg, META, METHODS, name, id).src = data;
+						if(callbacks && callbacks.length){
+							callbacks.shift()(LOAD, data, input, input[INPUT]);
+						}
+					}else{
+						var cache = getCache(pkg, META, METHODS, name, id, META);
+						if(!cache.parameters){
+							cache.parameters = {};
+						}
+						for(var i = 0, param; param = data.parameters[i]; i++){
+							if(!cache.parameters[param[1]]){
+								cache.parameters[param[1]] = {};
+							}
+							cache.parameters[param[1]].type = param[0];
+						}
+						if(!cache.returns){
+							cache.returns = {};
+						}
+						cache.returns.type = data.returns;
+					}
+
+					if(callbacks && callbacks.length){
+						callbacks.shift()(LOAD, cache, input, input[INPUT]);
+					}
+				}
+				obj.error = function(type, data, evt){
+					if(callbacks && callbacks.length){
+						callbacks.shift()(ERROR, {}, input, input[INPUT]);
+					}
+				}
+			}
+
+			search.push(obj);
+		}else if(type == "pkgmeta"){
+			var cached = getCache(name, "meta");
+
+			if(cached.requires){
+				if(callbacks && callbacks.length){
+					callbacks.shift()(LOAD, cached, input, input[INPUT]);
+					return;
+				}
+			}
+
+			dojo.debug("Finding package meta for: " + name);
+
+			var obj = {};
+
+			obj.url = name + "/meta";
+			obj.load = function(type, data, evt){
+				dojo.debug("_buildCache() loaded for: " + name);
+		
+				var methods = data.methods;
+				if(methods){
+					for(var method in methods){
+						if (method == "is") {
+							continue;
+						}
+						for(var pId in methods[method]){
+							getCache(name, META, METHODS, method, pId, META).summary = methods[method][pId];
+						}
+					}
+				}
+
+				var requires = data.requires;
+				var cache = getCache(name, META);
+				if(requires){
+					cache.requires = requires;
+				}
+				if(callbacks && callbacks.length){
+					callbacks.shift()(LOAD, cache, input, input[INPUT]);
+				}
+			}
+			obj.error = function(type, data, evt){
+				if(callbacks && callbacks.length){
+					callbacks.shift()(ERROR, {}, input, input[INPUT]);
+				}
+			}
+			search.push(obj);
+		}
+		
+		for(var i = 0, obj; obj = search[i]; i++){
+			var load = obj.load;
+			var error = obj.error;
+			delete obj.load;
+			delete obj.error;
+			var mimetype = obj.mimetype;
+			if(!mimetype){
+				mimetype = "text/json"
+			}
+			if(obj.url){
+				dojo.io.bind({
+					url: new dojo.uri.Uri(docs._url, obj.url),
+					input: input,
+					mimetype: mimetype,
+					error: error,
+					load: load
+				});
+			}else{
+				docs._rpc.callRemote("search", obj).addCallbacks(load, error);
+			}
+		}
+	},
+	selectFunction: function(/*String*/ name, /*String?*/ id){
+		// summary: The combined information
+	},
+	savePackage: function(/*Object*/ callbackObject, /*String*/ callback, /*Object*/ parameters){
+		dojo.event.kwConnect({
+			srcObj: dojo.docs,
+			srcFunc: "_savedPkgRpc",
+			targetObj: callbackObject,
+			targetFunc: callback,
+			once: true
+		});
+		
+		var props = {};
+		var cache = dojo.docs._getCache(parameters.pkg, "meta");
+
+		var i = 1;
+
+		if(!cache.path){
+			var path = "id";
+			props[["pname", i].join("")] = "DocPkgForm/require";
+			props[["pvalue", i++].join("")] = parameters.pkg;
+		}else{
+			var path = cache.path;
+		}
+
+		props.form = "//DocPkgForm";
+		props.path = ["/WikiHome/DojoDotDoc/", path].join("");
+
+		if(parameters.description){
+			props[["pname", i].join("")] = "main/text";
+			props[["pvalue", i++].join("")] = parameters.description;
+		}
+		
+		dojo.docs._rpc.callRemote("saveForm",	props).addCallbacks(dojo.docs._pkgRpc, dojo.docs._pkgRpc);
+	},
+	_pkgRpc: function(data){
+		if(data.name){
+			dojo.docs._getCache(data["DocPkgForm/require"], "meta").path = data.name;
+			dojo.docs._savedPkgRpc("load");
+		}else{
+			dojo.docs._savedPkgRpc("error");
+		}
+	},
+	_savedPkgRpc: function(type){
+	},
+	functionPackages: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback, /*Object*/ input){
+		// summary: Gets the package associated with a function and stores it in the .pkg value of input
+		dojo.debug("functionPackages() name: " + name);
+
+		if(!input){
+			input = {};
+		}
+		if(!input.callbacks){
+			input.callbacks = [];
+		}
+
+		input.type = "function_names";
+		input.name = name;
+		input.callbacks.unshift(callback);
+		input.callbacks.unshift(dojo.docs._functionPackages);
+		dojo.docs._buildCache(input);
+	},
+	_functionPackages: function(/*String*/ type, /*Array*/ data, /*Object*/ evt){
+		dojo.debug("_functionPackages() name: " + evt.name);
+		evt.pkg = '';
+
+		var results = [];
+		var data = dojo.docs._cache['function_names'];
+		for(var key in data){
+			if(dojo.lang.inArray(data[key], evt.name)){
+				dojo.debug("_functionPackages() package: " + key);
+				results.push(key);
+			}
+		}
+
+		if(evt.callbacks && evt.callbacks.length){
+			evt.callbacks.shift()(type, results, evt, evt.input);
+		}
+	},
+	setUserName: function(/*String*/ name){
+		dojo.docs._userName = name;
+		if(name && dojo.docs._password){
+			dojo.docs._logIn();
+		}
+	},
+	setPassword: function(/*String*/ password){
+		dojo.docs._password = password;
+		if(password && dojo.docs._userName){
+			dojo.docs._logIn();
+		}
+	},
+	_logIn: function(){
+		dojo.io.bind({
+			url: dojo.docs._rpc.serviceUrl.toString(),
+			method: "post",
+			mimetype: "text/json",
+			content: {
+				username: dojo.docs._userName,
+				password: dojo.docs._password
+			},
+			load: function(type, data){
+				if(data.error){
+					dojo.docs.logInSuccess();
+				}else{
+					dojo.docs.logInFailure();
+				}
+			},
+			error: function(){
+				dojo.docs.logInFailure();
+			}
+		});
+	},
+	logInSuccess: function(){},
+	logInFailure: function(){},
+	_set: function(/*Object*/ base, /*String...*/ keys, /*String*/ value){
+		var args = [];
+		for(var i = 0, arg; arg = arguments[i]; i++){
+			args.push(arg);
+		}
+
+		if(args.length < 3) return;
+		base = args.shift();
+		value = args.pop();
+		var key = args.pop();
+		for(var i = 0, arg; arg = args[i]; i++){
+			if(typeof base[arg] != "object"){
+				base[arg] = {};
+			}
+			base = base[arg];
+		}
+		base[key] = value;
+	},
+	_getCache: function(/*String...*/ keys){
+		var obj = dojo.docs._cache;
+		for(var i = 0; i < arguments.length; i++){
+			var arg = arguments[i];
+			if(!obj[arg]){
+				obj[arg] = {};
+			}
+			obj = obj[arg];
+		}
+		return obj;
+	}
+});
+
+dojo.event.topic.subscribe("/docs/search", dojo.docs, "_onDocSearch");
+dojo.event.topic.subscribe("/docs/function/select", dojo.docs, "_onDocSelectFunction");
+dojo.event.topic.subscribe("/docs/package/select", dojo.docs, "_onDocSelectPackage");
+
+dojo.event.topic.registerPublisher("/docs/function/results", dojo.docs, "_printFunctionResults");
+dojo.event.topic.registerPublisher("/docs/function/detail", dojo.docs, "_printFunctionDetail");
+dojo.event.topic.registerPublisher("/docs/package/detail", dojo.docs, "_printPkgResult");
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dom.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dom.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/dom.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,522 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.dom");
+
+dojo.dom.ELEMENT_NODE                  = 1;
+dojo.dom.ATTRIBUTE_NODE                = 2;
+dojo.dom.TEXT_NODE                     = 3;
+dojo.dom.CDATA_SECTION_NODE            = 4;
+dojo.dom.ENTITY_REFERENCE_NODE         = 5;
+dojo.dom.ENTITY_NODE                   = 6;
+dojo.dom.PROCESSING_INSTRUCTION_NODE   = 7;
+dojo.dom.COMMENT_NODE                  = 8;
+dojo.dom.DOCUMENT_NODE                 = 9;
+dojo.dom.DOCUMENT_TYPE_NODE            = 10;
+dojo.dom.DOCUMENT_FRAGMENT_NODE        = 11;
+dojo.dom.NOTATION_NODE                 = 12;
+	
+dojo.dom.dojoml = "http://www.dojotoolkit.org/2004/dojoml";
+
+/**
+ *	comprehensive list of XML namespaces
+**/
+dojo.dom.xmlns = {
+	//	summary
+	//	aliases for various common XML namespaces
+	svg : "http://www.w3.org/2000/svg",
+	smil : "http://www.w3.org/2001/SMIL20/",
+	mml : "http://www.w3.org/1998/Math/MathML",
+	cml : "http://www.xml-cml.org",
+	xlink : "http://www.w3.org/1999/xlink",
+	xhtml : "http://www.w3.org/1999/xhtml",
+	xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+	xbl : "http://www.mozilla.org/xbl",
+	fo : "http://www.w3.org/1999/XSL/Format",
+	xsl : "http://www.w3.org/1999/XSL/Transform",
+	xslt : "http://www.w3.org/1999/XSL/Transform",
+	xi : "http://www.w3.org/2001/XInclude",
+	xforms : "http://www.w3.org/2002/01/xforms",
+	saxon : "http://icl.com/saxon",
+	xalan : "http://xml.apache.org/xslt",
+	xsd : "http://www.w3.org/2001/XMLSchema",
+	dt: "http://www.w3.org/2001/XMLSchema-datatypes",
+	xsi : "http://www.w3.org/2001/XMLSchema-instance",
+	rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+	rdfs : "http://www.w3.org/2000/01/rdf-schema#",
+	dc : "http://purl.org/dc/elements/1.1/",
+	dcq: "http://purl.org/dc/qualifiers/1.0",
+	"soap-env" : "http://schemas.xmlsoap.org/soap/envelope/",
+	wsdl : "http://schemas.xmlsoap.org/wsdl/",
+	AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
+};
+
+dojo.dom.isNode = function(/* object */wh){
+	//	summary
+	//	checks to see if wh is actually a node.
+	if(typeof Element == "function") {
+		try {
+			return wh instanceof Element;	//	boolean
+		} catch(E) {}
+	} else {
+		// best-guess
+		return wh && !isNaN(wh.nodeType);	//	boolean
+	}
+}
+
+dojo.dom.getUniqueId = function(){
+	//	summary
+	//	returns a unique string for use with any DOM element
+	var _document = dojo.doc();
+	do {
+		var id = "dj_unique_" + (++arguments.callee._idIncrement);
+	}while(_document.getElementById(id));
+	return id;	//	string
+}
+dojo.dom.getUniqueId._idIncrement = 0;
+
+dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(/* Element */parentNode, /* string? */tagName){
+	//	summary
+	//	returns the first child element matching tagName
+	var node = parentNode.firstChild;
+	while(node && node.nodeType != dojo.dom.ELEMENT_NODE){
+		node = node.nextSibling;
+	}
+	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
+		node = dojo.dom.nextElement(node, tagName);
+	}
+	return node;	//	Element
+}
+
+dojo.dom.lastElement = dojo.dom.getLastChildElement = function(/* Element */parentNode, /* string? */tagName){
+	//	summary
+	//	returns the last child element matching tagName
+	var node = parentNode.lastChild;
+	while(node && node.nodeType != dojo.dom.ELEMENT_NODE) {
+		node = node.previousSibling;
+	}
+	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
+		node = dojo.dom.prevElement(node, tagName);
+	}
+	return node;	//	Element
+}
+
+dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(/* Node */node, /* string? */tagName){
+	//	summary
+	//	returns the next sibling element matching tagName
+	if(!node) { return null; }
+	do {
+		node = node.nextSibling;
+	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
+
+	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
+		return dojo.dom.nextElement(node, tagName);
+	}
+	return node;	//	Element
+}
+
+dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(/* Node */node, /* string? */tagName){
+	//	summary
+	//	returns the previous sibling element matching tagName
+	if(!node) { return null; }
+	if(tagName) { tagName = tagName.toLowerCase(); }
+	do {
+		node = node.previousSibling;
+	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
+
+	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
+		return dojo.dom.prevElement(node, tagName);
+	}
+	return node;	//	Element
+}
+
+// TODO: hmph
+/*this.forEachChildTag = function(node, unaryFunc) {
+	var child = this.getFirstChildTag(node);
+	while(child) {
+		if(unaryFunc(child) == "break") { break; }
+		child = this.getNextSiblingTag(child);
+	}
+}*/
+
+dojo.dom.moveChildren = function(/* Element */srcNode, /* Element */destNode, /* boolean? */trim){
+	//	summary
+	//	Moves children from srcNode to destNode and returns the count of children moved; 
+	//		will trim off text nodes if trim == true
+	var count = 0;
+	if(trim) {
+		while(srcNode.hasChildNodes() &&
+			srcNode.firstChild.nodeType == dojo.dom.TEXT_NODE) {
+			srcNode.removeChild(srcNode.firstChild);
+		}
+		while(srcNode.hasChildNodes() &&
+			srcNode.lastChild.nodeType == dojo.dom.TEXT_NODE) {
+			srcNode.removeChild(srcNode.lastChild);
+		}
+	}
+	while(srcNode.hasChildNodes()){
+		destNode.appendChild(srcNode.firstChild);
+		count++;
+	}
+	return count;	//	number
+}
+
+dojo.dom.copyChildren = function(/* Element */srcNode, /* Element */destNode, /* boolean? */trim){
+	//	summary
+	//	Copies children from srcNde to destNode and returns the count of children copied;
+	//		will trim off text nodes if trim == true
+	var clonedNode = srcNode.cloneNode(true);
+	return this.moveChildren(clonedNode, destNode, trim);	//	number
+}
+
+dojo.dom.removeChildren = function(/* Element */node){
+	//	summary
+	//	removes all children from node and returns the count of children removed.
+	var count = node.childNodes.length;
+	while(node.hasChildNodes()){ node.removeChild(node.firstChild); }
+	return count;	//	number
+}
+
+dojo.dom.replaceChildren = function(/* Element */node, /* Node */newChild){
+	//	summary
+	//	Removes all children of node and appends newChild
+	// FIXME: what if newChild is an array-like object?
+	dojo.dom.removeChildren(node);
+	node.appendChild(newChild);
+}
+
+dojo.dom.removeNode = function(/* Node */node){
+	//	summary
+	//	if node has a parent, removes node from parent and returns a reference to the removed child.
+	if(node && node.parentNode){
+		// return a ref to the removed child
+		return node.parentNode.removeChild(node);	//	Node
+	}
+}
+
+dojo.dom.getAncestors = function(/* Node */node, /* function? */filterFunction, /* boolean? */returnFirstHit) {
+	//	summary
+	//	returns all ancestors matching optional filterFunction; will return only the first if returnFirstHit
+	var ancestors = [];
+	var isFunction = (filterFunction && (filterFunction instanceof Function || typeof filterFunction == "function"));
+	while(node) {
+		if (!isFunction || filterFunction(node)) {
+			ancestors.push(node);
+		}
+		if (returnFirstHit && ancestors.length > 0) { 
+			return ancestors[0]; 	//	Node
+		}
+		
+		node = node.parentNode;
+	}
+	if (returnFirstHit) { return null; }
+	return ancestors;	//	array
+}
+
+dojo.dom.getAncestorsByTag = function(/* Node */node, /* string */tag, /* boolean? */returnFirstHit) {
+	//	summary
+	//	returns all ancestors matching tag (as tagName), will only return first one if returnFirstHit
+	tag = tag.toLowerCase();
+	return dojo.dom.getAncestors(node, function(el){
+		return ((el.tagName)&&(el.tagName.toLowerCase() == tag));
+	}, returnFirstHit);	//	Node || array
+}
+
+dojo.dom.getFirstAncestorByTag = function(/* Node */node, /* string */tag) {
+	//	summary
+	//	Returns first ancestor of node with tag tagName
+	return dojo.dom.getAncestorsByTag(node, tag, true);	//	Node
+}
+
+dojo.dom.isDescendantOf = function(/* Node */node, /* Node */ancestor, /* boolean? */guaranteeDescendant){
+	//	summary
+	//	Returns boolean if node is a descendant of ancestor
+	// guaranteeDescendant allows us to be a "true" isDescendantOf function
+	if(guaranteeDescendant && node) { node = node.parentNode; }
+	while(node) {
+		if(node == ancestor){ 
+			return true; 	//	boolean
+		}
+		node = node.parentNode;
+	}
+	return false;	//	boolean
+}
+
+dojo.dom.innerXML = function(/* Node */node){
+	//	summary
+	//	Implementation of MS's innerXML function.
+	if(node.innerXML){
+		return node.innerXML;	//	string
+	}else if (node.xml){
+		return node.xml;		//	string
+	}else if(typeof XMLSerializer != "undefined"){
+		return (new XMLSerializer()).serializeToString(node);	//	string
+	}
+}
+
+dojo.dom.createDocument = function(){
+	//	summary
+	//	cross-browser implementation of creating an XML document object.
+	var doc = null;
+	var _document = dojo.doc();
+
+	if(!dj_undef("ActiveXObject")){
+		var prefixes = [ "MSXML2", "Microsoft", "MSXML", "MSXML3" ];
+		for(var i = 0; i<prefixes.length; i++){
+			try{
+				doc = new ActiveXObject(prefixes[i]+".XMLDOM");
+			}catch(e){ /* squelch */ };
+
+			if(doc){ break; }
+		}
+	}else if((_document.implementation)&&
+		(_document.implementation.createDocument)){
+		doc = _document.implementation.createDocument("", "", null);
+	}
+	
+	return doc;	//	DOMDocument
+}
+
+dojo.dom.createDocumentFromText = function(/* string */str, /* string? */mimetype){
+	//	summary
+	//	attempts to create a Document object based on optional mime-type, using str as the contents of the document
+	if(!mimetype){ mimetype = "text/xml"; }
+	if(!dj_undef("DOMParser")){
+		var parser = new DOMParser();
+		return parser.parseFromString(str, mimetype);	//	DOMDocument
+	}else if(!dj_undef("ActiveXObject")){
+		var domDoc = dojo.dom.createDocument();
+		if(domDoc){
+			domDoc.async = false;
+			domDoc.loadXML(str);
+			return domDoc;	//	DOMDocument
+		}else{
+			dojo.debug("toXml didn't work?");
+		}
+	/*
+	}else if((dojo.render.html.capable)&&(dojo.render.html.safari)){
+		// FIXME: this doesn't appear to work!
+		// from: http://web-graphics.com/mtarchive/001606.php
+		// var xml = '<?xml version="1.0"?>'+str;
+		var mtype = "text/xml";
+		var xml = '<?xml version="1.0"?>'+str;
+		var url = "data:"+mtype+";charset=utf-8,"+encodeURIComponent(xml);
+		var req = new XMLHttpRequest();
+		req.open("GET", url, false);
+		req.overrideMimeType(mtype);
+		req.send(null);
+		return req.responseXML;
+	*/
+	}else{
+		var _document = dojo.doc();
+		if(_document.createElement){
+			// FIXME: this may change all tags to uppercase!
+			var tmp = _document.createElement("xml");
+			tmp.innerHTML = str;
+			if(_document.implementation && _document.implementation.createDocument) {
+				var xmlDoc = _document.implementation.createDocument("foo", "", null);
+				for(var i = 0; i < tmp.childNodes.length; i++) {
+					xmlDoc.importNode(tmp.childNodes.item(i), true);
+				}
+				return xmlDoc;	//	DOMDocument
+			}
+			// FIXME: probably not a good idea to have to return an HTML fragment
+			// FIXME: the tmp.doc.firstChild is as tested from IE, so it may not
+			// work that way across the board
+			return ((tmp.document)&&
+				(tmp.document.firstChild ?  tmp.document.firstChild : tmp));	//	DOMDocument
+		}
+	}
+	return null;
+}
+
+dojo.dom.prependChild = function(/* Element */node, /* Element */parent) {
+	// summary
+	//	prepends node to parent's children nodes
+	if(parent.firstChild) {
+		parent.insertBefore(node, parent.firstChild);
+	} else {
+		parent.appendChild(node);
+	}
+	return true;	//	boolean
+}
+
+dojo.dom.insertBefore = function(/* Node */node, /* Node */ref, /* boolean? */force){
+	//	summary
+	//	Try to insert node before ref
+	if (force != true &&
+		(node === ref || node.nextSibling === ref)){ return false; }
+	var parent = ref.parentNode;
+	parent.insertBefore(node, ref);
+	return true;	//	boolean
+}
+
+dojo.dom.insertAfter = function(/* Node */node, /* Node */ref, /* boolean? */force){
+	//	summary
+	//	Try to insert node after ref
+	var pn = ref.parentNode;
+	if(ref == pn.lastChild){
+		if((force != true)&&(node === ref)){
+			return false;	//	boolean
+		}
+		pn.appendChild(node);
+	}else{
+		return this.insertBefore(node, ref.nextSibling, force);	//	boolean
+	}
+	return true;	//	boolean
+}
+
+dojo.dom.insertAtPosition = function(/* Node */node, /* Node */ref, /* string */position){
+	//	summary
+	//	attempt to insert node in relation to ref based on position
+	if((!node)||(!ref)||(!position)){ 
+		return false;	//	boolean 
+	}
+	switch(position.toLowerCase()){
+		case "before":
+			return dojo.dom.insertBefore(node, ref);	//	boolean
+		case "after":
+			return dojo.dom.insertAfter(node, ref);		//	boolean
+		case "first":
+			if(ref.firstChild){
+				return dojo.dom.insertBefore(node, ref.firstChild);	//	boolean
+			}else{
+				ref.appendChild(node);
+				return true;	//	boolean
+			}
+			break;
+		default: // aka: last
+			ref.appendChild(node);
+			return true;	//	boolean
+	}
+}
+
+dojo.dom.insertAtIndex = function(/* Node */node, /* Element */containingNode, /* number */insertionIndex){
+	//	summary
+	//	insert node into child nodes nodelist of containingNode at insertionIndex.
+	var siblingNodes = containingNode.childNodes;
+
+	// if there aren't any kids yet, just add it to the beginning
+
+	if (!siblingNodes.length){
+		containingNode.appendChild(node);
+		return true;	//	boolean
+	}
+
+	// otherwise we need to walk the childNodes
+	// and find our spot
+
+	var after = null;
+
+	for(var i=0; i<siblingNodes.length; i++){
+
+		var sibling_index = siblingNodes.item(i)["getAttribute"] ? parseInt(siblingNodes.item(i).getAttribute("dojoinsertionindex")) : -1;
+
+		if (sibling_index < insertionIndex){
+			after = siblingNodes.item(i);
+		}
+	}
+
+	if (after){
+		// add it after the node in {after}
+
+		return dojo.dom.insertAfter(node, after);	//	boolean
+	}else{
+		// add it to the start
+
+		return dojo.dom.insertBefore(node, siblingNodes.item(0));	//	boolean
+	}
+}
+	
+dojo.dom.textContent = function(/* Node */node, /* string */text){
+	//	summary
+	//	implementation of the DOM Level 3 attribute; scan node for text
+	if (arguments.length>1) {
+		var _document = dojo.doc();
+		dojo.dom.replaceChildren(node, _document.createTextNode(text));
+		return text;	//	string
+	} else {
+		if(node.textContent != undefined){ //FF 1.5
+			return node.textContent;	//	string
+		}
+		var _result = "";
+		if (node == null) { return _result; }
+		for (var i = 0; i < node.childNodes.length; i++) {
+			switch (node.childNodes[i].nodeType) {
+				case 1: // ELEMENT_NODE
+				case 5: // ENTITY_REFERENCE_NODE
+					_result += dojo.dom.textContent(node.childNodes[i]);
+					break;
+				case 3: // TEXT_NODE
+				case 2: // ATTRIBUTE_NODE
+				case 4: // CDATA_SECTION_NODE
+					_result += node.childNodes[i].nodeValue;
+					break;
+				default:
+					break;
+			}
+		}
+		return _result;	//	string
+	}
+}
+
+dojo.dom.hasParent = function (/* Node */node) {
+	//	summary
+	//	returns whether or not node is a child of another node.
+	return node && node.parentNode && dojo.dom.isNode(node.parentNode);	//	boolean
+}
+
+/**
+ * Examples:
+ *
+ * myFooNode = <foo />
+ * isTag(myFooNode, "foo"); // returns "foo"
+ * isTag(myFooNode, "bar"); // returns ""
+ * isTag(myFooNode, "FOO"); // returns ""
+ * isTag(myFooNode, "hey", "foo", "bar"); // returns "foo"
+**/
+dojo.dom.isTag = function(/* Node */node /* ... */) {
+	//	summary
+	//	determines if node has any of the provided tag names and returns the tag name that matches, empty string otherwise.
+	if(node && node.tagName) {
+		for(var i=1; i<arguments.length; i++){
+			if(node.tagName==String(arguments[i])){
+				return String(arguments[i]);	//	string
+			}
+		}
+	}
+	return "";	//	string
+}
+
+dojo.dom.setAttributeNS = function(/* Element */elem, /* string */namespaceURI, /* string */attrName, /* string */attrValue){
+	//	summary
+	//	implementation of DOM2 setAttributeNS that works cross browser.
+	if(elem == null || ((elem == undefined)&&(typeof elem == "undefined"))){
+		dojo.raise("No element given to dojo.dom.setAttributeNS");
+	}
+	
+	if(!((elem.setAttributeNS == undefined)&&(typeof elem.setAttributeNS == "undefined"))){ // w3c
+		elem.setAttributeNS(namespaceURI, attrName, attrValue);
+	}else{ // IE
+		// get a root XML document
+		var ownerDoc = elem.ownerDocument;
+		var attribute = ownerDoc.createNode(
+			2, // node type
+			attrName,
+			namespaceURI
+		);
+		
+		// set value
+		attribute.nodeValue = attrValue;
+		
+		// attach to element
+		elem.setAttributeNode(attribute);
+	}
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/__package__.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/__package__.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/__package__.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,16 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.kwCompoundRequire({
+	common: ["dojo.event.common", "dojo.event.topic"],
+	browser: ["dojo.event.browser"],
+	dashboard: ["dojo.event.browser"]
+});
+dojo.provide("dojo.event.*");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/browser.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/browser.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/browser.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,514 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.event.browser");
+dojo.require("dojo.event.common");
+
+// FIXME: any particular reason this is in the global scope?
+dojo._ie_clobber = new function(){
+	this.clobberNodes = [];
+
+	function nukeProp(node, prop){
+		// try{ node.removeAttribute(prop); 	}catch(e){ /* squelch */ }
+		try{ node[prop] = null; 			}catch(e){ /* squelch */ }
+		try{ delete node[prop]; 			}catch(e){ /* squelch */ }
+		// FIXME: JotLive needs this, but I'm not sure if it's too slow or not
+		try{ node.removeAttribute(prop);	}catch(e){ /* squelch */ }
+	}
+
+	this.clobber = function(nodeRef){
+		var na;
+		var tna;
+		if(nodeRef){
+			tna = nodeRef.all || nodeRef.getElementsByTagName("*");
+			na = [nodeRef];
+			for(var x=0; x<tna.length; x++){
+				// if we're gonna be clobbering the thing, at least make sure
+				// we aren't trying to do it twice
+				if(tna[x]["__doClobber__"]){
+					na.push(tna[x]);
+				}
+			}
+		}else{
+			try{ window.onload = null; }catch(e){}
+			na = (this.clobberNodes.length) ? this.clobberNodes : document.all;
+		}
+		tna = null;
+		var basis = {};
+		for(var i = na.length-1; i>=0; i=i-1){
+			var el = na[i];
+			try{
+				if(el && el["__clobberAttrs__"]){
+					for(var j=0; j<el.__clobberAttrs__.length; j++){
+						nukeProp(el, el.__clobberAttrs__[j]);
+					}
+					nukeProp(el, "__clobberAttrs__");
+					nukeProp(el, "__doClobber__");
+				}
+			}catch(e){ /* squelch! */};
+		}
+		na = null;
+	}
+}
+
+if(dojo.render.html.ie){
+	dojo.addOnUnload(function(){
+		dojo._ie_clobber.clobber();
+		try{
+			if((dojo["widget"])&&(dojo.widget["manager"])){
+				dojo.widget.manager.destroyAll();
+			}
+		}catch(e){}
+		try{ window.onload = null; }catch(e){}
+		try{ window.onunload = null; }catch(e){}
+		dojo._ie_clobber.clobberNodes = [];
+		// CollectGarbage();
+	});
+}
+
+dojo.event.browser = new function(){
+
+	var clobberIdx = 0;
+
+	this.normalizedEventName = function(/*String*/eventName){
+		switch(eventName){
+			case "CheckboxStateChange":
+			case "DOMAttrModified":
+			case "DOMMenuItemActive":
+			case "DOMMenuItemInactive":
+			case "DOMMouseScroll":
+			case "DOMNodeInserted":
+			case "DOMNodeRemoved":
+			case "RadioStateChange":
+				return eventName;
+				break;
+			default:
+				return eventName.toLowerCase();
+				break;
+		}
+	}
+	
+	this.clean = function(/*DOMNode*/node){
+		// summary:
+		//		removes native event handlers so that destruction of the node
+		//		will not leak memory. On most browsers this is a no-op, but
+		//		it's critical for manual node removal on IE.
+		// node:
+		//		A DOM node. All of it's children will also be cleaned.
+		if(dojo.render.html.ie){ 
+			dojo._ie_clobber.clobber(node);
+		}
+	}
+
+	this.addClobberNode = function(/*DOMNode*/node){
+		// summary:
+		//		register the passed node to support event stripping
+		// node:
+		//		A DOM node
+		if(!dojo.render.html.ie){ return; }
+		if(!node["__doClobber__"]){
+			node.__doClobber__ = true;
+			dojo._ie_clobber.clobberNodes.push(node);
+			// this might not be the most efficient thing to do, but it's
+			// much less error prone than other approaches which were
+			// previously tried and failed
+			node.__clobberAttrs__ = [];
+		}
+	}
+
+	this.addClobberNodeAttrs = function(/*DOMNode*/node, /*Array*/props){
+		// summary:
+		//		register the passed node to support event stripping
+		// node:
+		//		A DOM node to stip properties from later
+		// props:
+		//		A list of propeties to strip from the node
+		if(!dojo.render.html.ie){ return; }
+		this.addClobberNode(node);
+		for(var x=0; x<props.length; x++){
+			node.__clobberAttrs__.push(props[x]);
+		}
+	}
+
+	this.removeListener = function(	/*DOMNode*/ node, 
+									/*String*/	evtName, 
+									/*Function*/fp, 
+									/*Boolean*/	capture){
+		// summary:
+		//		clobbers the listener from the node
+		// evtName:
+		//		the name of the handler to remove the function from
+		// node:
+		//		DOM node to attach the event to
+		// fp:
+		//		the function to register
+		// capture:
+		//		Optional. should this listener prevent propigation?
+		if(!capture){ var capture = false; }
+		evtName = dojo.event.browser.normalizedEventName(evtName);
+		if( (evtName == "onkey") || (evtName == "key") ){
+			if(dojo.render.html.ie){
+				this.removeListener(node, "onkeydown", fp, capture);
+			}
+			evtName = "onkeypress";
+		}
+		if(evtName.substr(0,2)=="on"){ evtName = evtName.substr(2); }
+		// FIXME: this is mostly a punt, we aren't actually doing anything on IE
+		if(node.removeEventListener){
+			node.removeEventListener(evtName, fp, capture);
+		}
+	}
+
+	this.addListener = function(/*DOMNode*/node, /*String*/evtName, /*Function*/fp, /*Boolean*/capture, /*Boolean*/dontFix){
+		// summary:
+		//		adds a listener to the node
+		// evtName:
+		//		the name of the handler to add the listener to can be either of
+		//		the form "onclick" or "click"
+		// node:
+		//		DOM node to attach the event to
+		// fp:
+		//		the function to register
+		// capture:
+		//		Optional. Should this listener prevent propigation?
+		// dontFix:
+		//		Optional. Should we avoid registering a new closure around the
+		//		listener to enable fixEvent for dispatch of the registered
+		//		function?
+		if(!node){ return; } // FIXME: log and/or bail?
+		if(!capture){ var capture = false; }
+		evtName = dojo.event.browser.normalizedEventName(evtName);
+		if( (evtName == "onkey") || (evtName == "key") ){
+			if(dojo.render.html.ie){
+				this.addListener(node, "onkeydown", fp, capture, dontFix);
+			}
+			evtName = "onkeypress";
+		}
+		if(evtName.substr(0,2)!="on"){ evtName = "on"+evtName; }
+
+		if(!dontFix){
+			// build yet another closure around fp in order to inject fixEvent
+			// around the resulting event
+			var newfp = function(evt){
+				if(!evt){ evt = window.event; }
+				var ret = fp(dojo.event.browser.fixEvent(evt, this));
+				if(capture){
+					dojo.event.browser.stopEvent(evt);
+				}
+				return ret;
+			}
+		}else{
+			newfp = fp;
+		}
+
+		if(node.addEventListener){ 
+			node.addEventListener(evtName.substr(2), newfp, capture);
+			return newfp;
+		}else{
+			if(typeof node[evtName] == "function" ){
+				var oldEvt = node[evtName];
+				node[evtName] = function(e){
+					oldEvt(e);
+					return newfp(e);
+				}
+			}else{
+				node[evtName]=newfp;
+			}
+			if(dojo.render.html.ie){
+				this.addClobberNodeAttrs(node, [evtName]);
+			}
+			return newfp;
+		}
+	}
+
+	this.isEvent = function(/*Object*/obj){
+		// summary: 
+		//		Tries to determine whether or not the object is a DOM event.
+
+		// FIXME: event detection hack ... could test for additional attributes
+		// if necessary
+		return (typeof obj != "undefined")&&(typeof Event != "undefined")&&(obj.eventPhase); // Boolean
+		// Event does not support instanceof in Opera, otherwise:
+		//return (typeof Event != "undefined")&&(obj instanceof Event);
+	}
+
+	this.currentEvent = null;
+	
+	this.callListener = function(/*Function*/listener, /*DOMNode*/curTarget){
+		// summary:
+		//		calls the specified listener in the context of the passed node
+		//		with the current DOM event object as the only parameter
+		// listener:
+		//		the function to call
+		// curTarget:
+		//		the Node to call the function in the scope of
+		if(typeof listener != 'function'){
+			dojo.raise("listener not a function: " + listener);
+		}
+		dojo.event.browser.currentEvent.currentTarget = curTarget;
+		return listener.call(curTarget, dojo.event.browser.currentEvent);
+	}
+
+	this._stopPropagation = function(){
+		dojo.event.browser.currentEvent.cancelBubble = true; 
+	}
+
+	this._preventDefault = function(){
+		dojo.event.browser.currentEvent.returnValue = false;
+	}
+
+	this.keys = {
+		KEY_BACKSPACE: 8,
+		KEY_TAB: 9,
+		KEY_CLEAR: 12,
+		KEY_ENTER: 13,
+		KEY_SHIFT: 16,
+		KEY_CTRL: 17,
+		KEY_ALT: 18,
+		KEY_PAUSE: 19,
+		KEY_CAPS_LOCK: 20,
+		KEY_ESCAPE: 27,
+		KEY_SPACE: 32,
+		KEY_PAGE_UP: 33,
+		KEY_PAGE_DOWN: 34,
+		KEY_END: 35,
+		KEY_HOME: 36,
+		KEY_LEFT_ARROW: 37,
+		KEY_UP_ARROW: 38,
+		KEY_RIGHT_ARROW: 39,
+		KEY_DOWN_ARROW: 40,
+		KEY_INSERT: 45,
+		KEY_DELETE: 46,
+		KEY_HELP: 47,
+		KEY_LEFT_WINDOW: 91,
+		KEY_RIGHT_WINDOW: 92,
+		KEY_SELECT: 93,
+		KEY_NUMPAD_0: 96,
+		KEY_NUMPAD_1: 97,
+		KEY_NUMPAD_2: 98,
+		KEY_NUMPAD_3: 99,
+		KEY_NUMPAD_4: 100,
+		KEY_NUMPAD_5: 101,
+		KEY_NUMPAD_6: 102,
+		KEY_NUMPAD_7: 103,
+		KEY_NUMPAD_8: 104,
+		KEY_NUMPAD_9: 105,
+		KEY_NUMPAD_MULTIPLY: 106,
+		KEY_NUMPAD_PLUS: 107,
+		KEY_NUMPAD_ENTER: 108,
+		KEY_NUMPAD_MINUS: 109,
+		KEY_NUMPAD_PERIOD: 110,
+		KEY_NUMPAD_DIVIDE: 111,
+		KEY_F1: 112,
+		KEY_F2: 113,
+		KEY_F3: 114,
+		KEY_F4: 115,
+		KEY_F5: 116,
+		KEY_F6: 117,
+		KEY_F7: 118,
+		KEY_F8: 119,
+		KEY_F9: 120,
+		KEY_F10: 121,
+		KEY_F11: 122,
+		KEY_F12: 123,
+		KEY_F13: 124,
+		KEY_F14: 125,
+		KEY_F15: 126,
+		KEY_NUM_LOCK: 144,
+		KEY_SCROLL_LOCK: 145
+	};
+
+	// reverse lookup
+	this.revKeys = [];
+	for(var key in this.keys){
+		this.revKeys[this.keys[key]] = key;
+	}
+
+	this.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
+		// summary:
+		//		normalizes properties on the event object including event
+		//		bubbling methods, keystroke normalization, and x/y positions
+		// evt: the native event object
+		// sender: the node to treat as "currentTarget"
+		if(!evt){
+			if(window["event"]){
+				evt = window.event;
+			}
+		}
+		
+		if((evt["type"])&&(evt["type"].indexOf("key") == 0)){ // key events
+			evt.keys = this.revKeys;
+			// FIXME: how can we eliminate this iteration?
+			for(var key in this.keys){
+				evt[key] = this.keys[key];
+			}
+			if(evt["type"] == "keydown" && dojo.render.html.ie){
+				switch(evt.keyCode){
+					case evt.KEY_SHIFT:
+					case evt.KEY_CTRL:
+					case evt.KEY_ALT:
+					case evt.KEY_CAPS_LOCK:
+					case evt.KEY_LEFT_WINDOW:
+					case evt.KEY_RIGHT_WINDOW:
+					case evt.KEY_SELECT:
+					case evt.KEY_NUM_LOCK:
+					case evt.KEY_SCROLL_LOCK:
+					// I'll get these in keypress after the OS munges them based on numlock
+					case evt.KEY_NUMPAD_0:
+					case evt.KEY_NUMPAD_1:
+					case evt.KEY_NUMPAD_2:
+					case evt.KEY_NUMPAD_3:
+					case evt.KEY_NUMPAD_4:
+					case evt.KEY_NUMPAD_5:
+					case evt.KEY_NUMPAD_6:
+					case evt.KEY_NUMPAD_7:
+					case evt.KEY_NUMPAD_8:
+					case evt.KEY_NUMPAD_9:
+					case evt.KEY_NUMPAD_PERIOD:
+						break; // just ignore the keys that can morph
+					case evt.KEY_NUMPAD_MULTIPLY:
+					case evt.KEY_NUMPAD_PLUS:
+					case evt.KEY_NUMPAD_ENTER:
+					case evt.KEY_NUMPAD_MINUS:
+					case evt.KEY_NUMPAD_DIVIDE:
+						break; // I could handle these but just pick them up in keypress
+					case evt.KEY_PAUSE:
+					case evt.KEY_TAB:
+					case evt.KEY_BACKSPACE:
+					case evt.KEY_ENTER:
+					case evt.KEY_ESCAPE:
+					case evt.KEY_PAGE_UP:
+					case evt.KEY_PAGE_DOWN:
+					case evt.KEY_END:
+					case evt.KEY_HOME:
+					case evt.KEY_LEFT_ARROW:
+					case evt.KEY_UP_ARROW:
+					case evt.KEY_RIGHT_ARROW:
+					case evt.KEY_DOWN_ARROW:
+					case evt.KEY_INSERT:
+					case evt.KEY_DELETE:
+					case evt.KEY_F1:
+					case evt.KEY_F2:
+					case evt.KEY_F3:
+					case evt.KEY_F4:
+					case evt.KEY_F5:
+					case evt.KEY_F6:
+					case evt.KEY_F7:
+					case evt.KEY_F8:
+					case evt.KEY_F9:
+					case evt.KEY_F10:
+					case evt.KEY_F11:
+					case evt.KEY_F12:
+					case evt.KEY_F12:
+					case evt.KEY_F13:
+					case evt.KEY_F14:
+					case evt.KEY_F15:
+					case evt.KEY_CLEAR:
+					case evt.KEY_HELP:
+						evt.key = evt.keyCode;
+						break;
+					default:
+						if(evt.ctrlKey || evt.altKey){
+							var unifiedCharCode = evt.keyCode;
+							// if lower case but keycode is uppercase, convert it
+							if(unifiedCharCode >= 65 && unifiedCharCode <= 90 && evt.shiftKey == false){
+								unifiedCharCode += 32;
+							}
+							if(unifiedCharCode >= 1 && unifiedCharCode <= 26 && evt.ctrlKey){
+								unifiedCharCode += 96; // 001-032 = ctrl+[a-z]
+							}
+							evt.key = String.fromCharCode(unifiedCharCode);
+						}
+				}
+			} else if(evt["type"] == "keypress"){
+				if(dojo.render.html.opera){
+					if(evt.which == 0){
+						evt.key = evt.keyCode;
+					}else if(evt.which > 0){
+						switch(evt.which){
+							case evt.KEY_SHIFT:
+							case evt.KEY_CTRL:
+							case evt.KEY_ALT:
+							case evt.KEY_CAPS_LOCK:
+							case evt.KEY_NUM_LOCK:
+							case evt.KEY_SCROLL_LOCK:
+								break;
+							case evt.KEY_PAUSE:
+							case evt.KEY_TAB:
+							case evt.KEY_BACKSPACE:
+							case evt.KEY_ENTER:
+							case evt.KEY_ESCAPE:
+								evt.key = evt.which;
+								break;
+							default:
+								var unifiedCharCode = evt.which;
+								if((evt.ctrlKey || evt.altKey || evt.metaKey) && (evt.which >= 65 && evt.which <= 90 && evt.shiftKey == false)){
+									unifiedCharCode += 32;
+								}
+								evt.key = String.fromCharCode(unifiedCharCode);
+						}
+					}
+				}else if(dojo.render.html.ie){ // catch some IE keys that are hard to get in keyDown
+					// key combinations were handled in onKeyDown
+					if(!evt.ctrlKey && !evt.altKey && evt.keyCode >= evt.KEY_SPACE){
+						evt.key = String.fromCharCode(evt.keyCode);
+					}
+				}else if(dojo.render.html.safari){
+					switch(evt.keyCode){
+						case 63232: evt.key = evt.KEY_UP_ARROW; break;
+						case 63233: evt.key = evt.KEY_DOWN_ARROW; break;
+						case 63234: evt.key = evt.KEY_LEFT_ARROW; break;
+						case 63235: evt.key = evt.KEY_RIGHT_ARROW; break;
+						default: 
+							evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode;
+					}
+				}else{
+					evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode;
+				}
+			}
+		}
+		if(dojo.render.html.ie){
+			if(!evt.target){ evt.target = evt.srcElement; }
+			if(!evt.currentTarget){ evt.currentTarget = (sender ? sender : evt.srcElement); }
+			if(!evt.layerX){ evt.layerX = evt.offsetX; }
+			if(!evt.layerY){ evt.layerY = evt.offsetY; }
+			// FIXME: scroll position query is duped from dojo.html to avoid dependency on that entire module
+			// DONOT replace the following to use dojo.body(), in IE, document.documentElement should be used
+			// here rather than document.body
+			var doc = (evt.srcElement && evt.srcElement.ownerDocument) ? evt.srcElement.ownerDocument : document;
+			var docBody = ((dojo.render.html.ie55)||(doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement;
+			if(!evt.pageX){ evt.pageX = evt.clientX + (docBody.scrollLeft || 0) }
+			if(!evt.pageY){ evt.pageY = evt.clientY + (docBody.scrollTop || 0) }
+			// mouseover
+			if(evt.type == "mouseover"){ evt.relatedTarget = evt.fromElement; }
+			// mouseout
+			if(evt.type == "mouseout"){ evt.relatedTarget = evt.toElement; }
+			this.currentEvent = evt;
+			evt.callListener = this.callListener;
+			evt.stopPropagation = this._stopPropagation;
+			evt.preventDefault = this._preventDefault;
+		}
+		return evt; // Event
+	}
+
+	this.stopEvent = function(/*Event*/evt){
+		// summary:
+		//		prevents propigation and clobbers the default action of the
+		//		passed event
+		// evt: Optional for IE. The native event object.
+		if(window.event){
+			evt.returnValue = false;
+			evt.cancelBubble = true;
+		}else{
+			evt.preventDefault();
+			evt.stopPropagation();
+		}
+	}
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/common.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/common.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/common.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,878 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.event.common");
+
+dojo.require("dojo.lang.array");
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.lang.func");
+
+// TODO: connection filter functions
+//			these are functions that accept a method invocation (like around
+//			advice) and return a boolean based on it. That value determines
+//			whether or not the connection proceeds. It could "feel" like around
+//			advice for those who know what it is (calling proceed() or not),
+//			but I think presenting it as a "filter" and/or calling it with the
+//			function args and not the MethodInvocation might make it more
+//			palletable to "normal" users than around-advice currently is
+// TODO: execution scope mangling
+//			YUI's event facility by default executes listeners in the context
+//			of the source object. This is very odd, but should probably be
+//			supported as an option (both for the source and for the dest). It
+//			can be thought of as a connection-specific hitch().
+// TODO: more resiliency for 4+ arguments to connect()
+
+dojo.event = new function(){
+	this._canTimeout = dojo.lang.isFunction(dj_global["setTimeout"])||dojo.lang.isAlien(dj_global["setTimeout"]);
+
+	// FIXME: where should we put this method (not here!)?
+	function interpolateArgs(args, searchForNames){
+		var dl = dojo.lang;
+		var ao = {
+			srcObj: dj_global,
+			srcFunc: null,
+			adviceObj: dj_global,
+			adviceFunc: null,
+			aroundObj: null,
+			aroundFunc: null,
+			adviceType: (args.length>2) ? args[0] : "after",
+			precedence: "last",
+			once: false,
+			delay: null,
+			rate: 0,
+			adviceMsg: false
+		};
+
+		switch(args.length){
+			case 0: return;
+			case 1: return;
+			case 2:
+				ao.srcFunc = args[0];
+				ao.adviceFunc = args[1];
+				break;
+			case 3:
+				if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){
+					ao.adviceType = "after";
+					ao.srcObj = args[0];
+					ao.srcFunc = args[1];
+					ao.adviceFunc = args[2];
+				}else if((dl.isString(args[1]))&&(dl.isString(args[2]))){
+					ao.srcFunc = args[1];
+					ao.adviceFunc = args[2];
+				}else if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){
+					ao.adviceType = "after";
+					ao.srcObj = args[0];
+					ao.srcFunc = args[1];
+					var tmpName  = dl.nameAnonFunc(args[2], ao.adviceObj, searchForNames);
+					ao.adviceFunc = tmpName;
+				}else if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){
+					ao.adviceType = "after";
+					ao.srcObj = dj_global;
+					var tmpName  = dl.nameAnonFunc(args[0], ao.srcObj, searchForNames);
+					ao.srcFunc = tmpName;
+					ao.adviceObj = args[1];
+					ao.adviceFunc = args[2];
+				}
+				break;
+			case 4:
+				if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){
+					// we can assume that we've got an old-style "connect" from
+					// the sigslot school of event attachment. We therefore
+					// assume after-advice.
+					ao.adviceType = "after";
+					ao.srcObj = args[0];
+					ao.srcFunc = args[1];
+					ao.adviceObj = args[2];
+					ao.adviceFunc = args[3];
+				}else if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){
+					ao.adviceType = args[0];
+					ao.srcObj = dj_global;
+					ao.srcFunc = args[1];
+					ao.adviceObj = args[2];
+					ao.adviceFunc = args[3];
+				}else if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){
+					ao.adviceType = args[0];
+					ao.srcObj = dj_global;
+					var tmpName  = dl.nameAnonFunc(args[1], dj_global, searchForNames);
+					ao.srcFunc = tmpName;
+					ao.adviceObj = args[2];
+					ao.adviceFunc = args[3];
+				}else if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){
+					ao.srcObj = args[1];
+					ao.srcFunc = args[2];
+					var tmpName  = dl.nameAnonFunc(args[3], dj_global, searchForNames);
+					ao.adviceObj = dj_global;
+					ao.adviceFunc = tmpName;
+				}else if(dl.isObject(args[1])){
+					ao.srcObj = args[1];
+					ao.srcFunc = args[2];
+					ao.adviceObj = dj_global;
+					ao.adviceFunc = args[3];
+				}else if(dl.isObject(args[2])){
+					ao.srcObj = dj_global;
+					ao.srcFunc = args[1];
+					ao.adviceObj = args[2];
+					ao.adviceFunc = args[3];
+				}else{
+					ao.srcObj = ao.adviceObj = ao.aroundObj = dj_global;
+					ao.srcFunc = args[1];
+					ao.adviceFunc = args[2];
+					ao.aroundFunc = args[3];
+				}
+				break;
+			case 6:
+				ao.srcObj = args[1];
+				ao.srcFunc = args[2];
+				ao.adviceObj = args[3]
+				ao.adviceFunc = args[4];
+				ao.aroundFunc = args[5];
+				ao.aroundObj = dj_global;
+				break;
+			default:
+				ao.srcObj = args[1];
+				ao.srcFunc = args[2];
+				ao.adviceObj = args[3]
+				ao.adviceFunc = args[4];
+				ao.aroundObj = args[5];
+				ao.aroundFunc = args[6];
+				ao.once = args[7];
+				ao.delay = args[8];
+				ao.rate = args[9];
+				ao.adviceMsg = args[10];
+				break;
+		}
+
+		if(dl.isFunction(ao.aroundFunc)){
+			var tmpName  = dl.nameAnonFunc(ao.aroundFunc, ao.aroundObj, searchForNames);
+			ao.aroundFunc = tmpName;
+		}
+
+		if(dl.isFunction(ao.srcFunc)){
+			ao.srcFunc = dl.getNameInObj(ao.srcObj, ao.srcFunc);
+		}
+
+		if(dl.isFunction(ao.adviceFunc)){
+			ao.adviceFunc = dl.getNameInObj(ao.adviceObj, ao.adviceFunc);
+		}
+
+		if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){
+			ao.aroundFunc = dl.getNameInObj(ao.aroundObj, ao.aroundFunc);
+		}
+
+		if(!ao.srcObj){
+			dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc);
+		}
+		if(!ao.adviceObj){
+			dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc);
+		}
+		
+		if(!ao.adviceFunc){
+			dojo.debug("bad adviceFunc for srcFunc: "+ao.srcFunc);
+			dojo.debugShallow(ao);
+		} 
+		
+		return ao;
+	}
+
+	this.connect = function(/*...*/){
+		// summary:
+		//		dojo.event.connect is the glue that holds most Dojo-based
+		//		applications together. Most combinations of arguments are
+		//		supported, with the connect() method attempting to disambiguate
+		//		the implied types of positional parameters. The following will
+		//		all work:
+		//			dojo.event.connect("globalFunctionName1", "globalFunctionName2");
+		//			dojo.event.connect(functionReference1, functionReference2);
+		//			dojo.event.connect("globalFunctionName1", functionReference2);
+		//			dojo.event.connect(functionReference1, "globalFunctionName2");
+		//			dojo.event.connect(scope1, "functionName1", "globalFunctionName2");
+		//			dojo.event.connect("globalFunctionName1", scope2, "functionName2");
+		//			dojo.event.connect(scope1, "functionName1", scope2, "functionName2");
+		//			dojo.event.connect("after", scope1, "functionName1", scope2, "functionName2");
+		//			dojo.event.connect("before", scope1, "functionName1", scope2, "functionName2");
+		//			dojo.event.connect("around", 	scope1, "functionName1", 
+		//											scope2, "functionName2",
+		//											aroundFunctionReference);
+		//			dojo.event.connect("around", 	scope1, "functionName1", 
+		//											scope2, "functionName2",
+		//											scope3, "aroundFunctionName");
+		//			dojo.event.connect("before-around", 	scope1, "functionName1", 
+		//													scope2, "functionName2",
+		//													aroundFunctionReference);
+		//			dojo.event.connect("after-around", 		scope1, "functionName1", 
+		//													scope2, "functionName2",
+		//													aroundFunctionReference);
+		//			dojo.event.connect("after-around", 		scope1, "functionName1", 
+		//													scope2, "functionName2",
+		//													scope3, "aroundFunctionName");
+		//			dojo.event.connect("around", 	scope1, "functionName1", 
+		//											scope2, "functionName2",
+		//											scope3, "aroundFunctionName", true, 30);
+		//			dojo.event.connect("around", 	scope1, "functionName1", 
+		//											scope2, "functionName2",
+		//											scope3, "aroundFunctionName", null, null, 10);
+		// adviceType: 
+		//		Optional. String. One of "before", "after", "around",
+		//		"before-around", or "after-around". FIXME
+		// srcObj:
+		//		the scope in which to locate/execute the named srcFunc. Along
+		//		with srcFunc, this creates a way to dereference the function to
+		//		call. So if the function in question is "foo.bar", the
+		//		srcObj/srcFunc pair would be foo and "bar", where "bar" is a
+		//		string and foo is an object reference.
+		// srcFunc:
+		//		the name of the function to connect to. When it is executed,
+		//		the listener being registered with this call will be called.
+		//		The adviceType defines the call order between the source and
+		//		the target functions.
+		// adviceObj:
+		//		the scope in which to locate/execute the named adviceFunc.
+		// adviceFunc:
+		//		the name of the function being conected to srcObj.srcFunc
+		// aroundObj:
+		//		the scope in which to locate/execute the named aroundFunc.
+		// aroundFunc:
+		//		the name of, or a reference to, the function that will be used
+		//		to mediate the advice call. Around advice requires a special
+		//		unary function that will be passed a "MethodInvocation" object.
+		//		These objects have several important properties, namely:
+		//			- args
+		//				a mutable array of arguments to be passed into the
+		//				wrapped function
+		//			- proceed
+		//				a function that "continues" the invocation. The result
+		//				of this function is the return of the wrapped function.
+		//				You can then manipulate this return before passing it
+		//				back out (or take further action based on it).
+		// once:
+		//		boolean that determines whether or not this connect() will
+		//		create a new connection if an identical connect() has already
+		//		been made. Defaults to "false".
+		// delay:
+		//		an optional delay (in ms), as an integer, for dispatch of a
+		//		listener after the source has been fired.
+		// rate:
+		//		an optional rate throttling parameter (integer, in ms). When
+		//		specified, this particular connection will not fire more than
+		//		once in the interval specified by the rate
+		// adviceMsg:
+		//		boolean. Should the listener have all the parameters passed in
+		//		as a single argument?
+
+		/*
+				ao.adviceType = args[0];
+				ao.srcObj = args[1];
+				ao.srcFunc = args[2];
+				ao.adviceObj = args[3]
+				ao.adviceFunc = args[4];
+				ao.aroundObj = args[5];
+				ao.aroundFunc = args[6];
+				ao.once = args[7];
+				ao.delay = args[8];
+				ao.rate = args[9];
+				ao.adviceMsg = args[10];
+		*/
+		if(arguments.length == 1){
+			var ao = arguments[0];
+		}else{
+			var ao = interpolateArgs(arguments, true);
+		}
+		if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
+			if(dojo.render.html.ie){
+				ao.srcFunc = "onkeydown";
+				this.connect(ao);
+			}
+			ao.srcFunc = "onkeypress";
+		}
+
+
+		if(dojo.lang.isArray(ao.srcObj) && ao.srcObj!=""){
+			var tmpAO = {};
+			for(var x in ao){
+				tmpAO[x] = ao[x];
+			}
+			var mjps = [];
+			dojo.lang.forEach(ao.srcObj, function(src){
+				if((dojo.render.html.capable)&&(dojo.lang.isString(src))){
+					src = dojo.byId(src);
+					// dojo.debug(src);
+				}
+				tmpAO.srcObj = src;
+				// dojo.debug(tmpAO.srcObj, tmpAO.srcFunc);
+				// dojo.debug(tmpAO.adviceObj, tmpAO.adviceFunc);
+				mjps.push(dojo.event.connect.call(dojo.event, tmpAO));
+			});
+			return mjps;
+		}
+
+		// FIXME: just doing a "getForMethod()" seems to be enough to put this into infinite recursion!!
+		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
+		if(ao.adviceFunc){
+			var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc);
+		}
+
+		mjp.kwAddAdvice(ao);
+
+		// advanced users might want to fsck w/ the join point manually
+		return mjp; // a MethodJoinPoint object
+	}
+
+	this.log = function(/*object or funcName*/ a1, /*funcName*/ a2){
+		// summary:
+		//		a function that will wrap and log all calls to the specified
+		//		a1.a2() function. If only a1 is passed, it'll be used as a
+		//		function or function name on the global context. Logging will
+		//		be sent to dojo.debug
+		// a1:
+		//		if a2 is passed, this should be an object. If not, it can be a
+		//		function or function name.
+		// a2:
+		//		a function name
+		var kwArgs;
+		if((arguments.length == 1)&&(typeof a1 == "object")){
+			kwArgs = a1;
+		}else{
+			kwArgs = {
+				srcObj: a1,
+				srcFunc: a2
+			};
+		}
+		kwArgs.adviceFunc = function(){
+			var argsStr = [];
+			for(var x=0; x<arguments.length; x++){
+				argsStr.push(arguments[x]);
+			}
+			dojo.debug("("+kwArgs.srcObj+")."+kwArgs.srcFunc, ":", argsStr.join(", "));
+		}
+		this.kwConnect(kwArgs);
+	}
+
+	this.connectBefore = function(){
+		// summary:
+		//	 	takes the same parameters as dojo.event.connect(), except that
+		//	 	the advice type will always be "before"
+		var args = ["before"];
+		for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
+		return this.connect.apply(this, args); // a MethodJoinPoint object
+	}
+
+	this.connectAround = function(){
+		// summary:
+		//	 	takes the same parameters as dojo.event.connect(), except that
+		//	 	the advice type will always be "around"
+		var args = ["around"];
+		for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
+		return this.connect.apply(this, args); // a MethodJoinPoint object
+	}
+
+	this.connectOnce = function(){
+		// summary:
+		//	 	takes the same parameters as dojo.event.connect(), except that
+		//	 	the "once" flag will always be set to "true"
+		var ao = interpolateArgs(arguments, true);
+		ao.once = true;
+		return this.connect(ao); // a MethodJoinPoint object
+	}
+
+	this._kwConnectImpl = function(kwArgs, disconnect){
+		var fn = (disconnect) ? "disconnect" : "connect";
+		if(typeof kwArgs["srcFunc"] == "function"){
+			kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
+			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.srcFunc, kwArgs.srcObj, true);
+			kwArgs.srcFunc = tmpName;
+		}
+		if(typeof kwArgs["adviceFunc"] == "function"){
+			kwArgs.adviceObj = kwArgs["adviceObj"]||dj_global;
+			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.adviceFunc, kwArgs.adviceObj, true);
+			kwArgs.adviceFunc = tmpName;
+		}
+		kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
+		kwArgs.adviceObj = kwArgs["adviceObj"]||kwArgs["targetObj"]||dj_global;
+		kwArgs.adviceFunc = kwArgs["adviceFunc"]||kwArgs["targetFunc"];
+		// pass kwargs to avoid unrolling/repacking
+		return dojo.event[fn](kwArgs);
+	}
+
+	this.kwConnect = function(/*Object*/ kwArgs){
+		// summary:
+		//		A version of dojo.event.connect() that takes a map of named
+		//		parameters instead of the positional parameters that
+		//		dojo.event.connect() uses. For many advanced connection types,
+		//		this can be a much more readable (and potentially faster)
+		//		alternative.
+		// kwArgs:
+		// 		An object that can have the following properties:
+		//			- adviceType
+		//			- srcObj
+		//			- srcFunc
+		//			- adviceObj
+		//			- adviceFunc 
+		//			- aroundObj
+		//			- aroundFunc
+		//			- once
+		//			- delay
+		//			- rate
+		//			- adviceMsg
+		//		As with connect, only srcFunc and adviceFunc are generally
+		//		required
+
+		return this._kwConnectImpl(kwArgs, false); // a MethodJoinPoint object
+
+	}
+
+	this.disconnect = function(){
+		// summary:
+		//		Takes the same parameters as dojo.event.connect() but destroys
+		//		an existing connection instead of building a new one. For
+		//		multiple identical connections, multiple disconnect() calls
+		//		will unroll one each time it's called.
+		if(arguments.length == 1){
+			var ao = arguments[0];
+		}else{
+			var ao = interpolateArgs(arguments, true);
+		}
+		if(!ao.adviceFunc){ return; } // nothing to disconnect
+		if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
+			if(dojo.render.html.ie){
+				ao.srcFunc = "onkeydown";
+				this.disconnect(ao);
+			}
+			ao.srcFunc = "onkeypress";
+		}
+		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
+		return mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once); // a MethodJoinPoint object
+	}
+
+	this.kwDisconnect = function(kwArgs){
+		// summary:
+		//		Takes the same parameters as dojo.event.kwConnect() but
+		//		destroys an existing connection instead of building a new one.
+		return this._kwConnectImpl(kwArgs, true);
+	}
+}
+
+// exactly one of these is created whenever a method with a joint point is run,
+// if there is at least one 'around' advice.
+dojo.event.MethodInvocation = function(/*dojo.event.MethodJoinPoint*/join_point, /*Object*/obj, /*Array*/args){
+	// summary:
+	//		a class the models the call into a function. This is used under the
+	//		covers for all method invocations on both ends of a
+	//		connect()-wrapped function dispatch. This allows us to "pickle"
+	//		calls, such as in the case of around advice.
+	// join_point:
+	//		a dojo.event.MethodJoinPoint object that represents a connection
+	// obj:
+	//		the scope the call will execute in
+	// args:
+	//		an array of parameters that will get passed to the callee
+	this.jp_ = join_point;
+	this.object = obj;
+	this.args = [];
+	// make sure we don't lock into a mutable object which can change under us.
+	// It's ok if the individual items change, though.
+	for(var x=0; x<args.length; x++){
+		this.args[x] = args[x];
+	}
+	// the index of the 'around' that is currently being executed.
+	this.around_index = -1;
+}
+
+dojo.event.MethodInvocation.prototype.proceed = function(){
+	// summary:
+	//		proceed with the method call that's represented by this invocation
+	//		object
+	this.around_index++;
+	if(this.around_index >= this.jp_.around.length){
+		return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args);
+		// return this.jp_.run_before_after(this.object, this.args);
+	}else{
+		var ti = this.jp_.around[this.around_index];
+		var mobj = ti[0]||dj_global;
+		var meth = ti[1];
+		return mobj[meth].call(mobj, this);
+	}
+} 
+
+
+dojo.event.MethodJoinPoint = function(/*Object*/obj, /*String*/funcName){
+	this.object = obj||dj_global;
+	this.methodname = funcName;
+	this.methodfunc = this.object[funcName];
+	this.squelch = false;
+	// this.before = [];
+	// this.after = [];
+	// this.around = [];
+}
+
+dojo.event.MethodJoinPoint.getForMethod = function(/*Object*/obj, /*String*/funcName){
+	// summary:
+	//		"static" class function for returning a MethodJoinPoint from a
+	//		scoped function. If one doesn't exist, one is created.
+	// obj:
+	//		the scope to search for the function in
+	// funcName:
+	//		the name of the function to return a MethodJoinPoint for
+	if(!obj){ obj = dj_global; }
+	if(!obj[funcName]){
+		// supply a do-nothing method implementation
+		obj[funcName] = function(){};
+		if(!obj[funcName]){
+			// e.g. cannot add to inbuilt objects in IE6
+			dojo.raise("Cannot set do-nothing method on that object "+funcName);
+		}
+	}else if((!dojo.lang.isFunction(obj[funcName]))&&(!dojo.lang.isAlien(obj[funcName]))){
+		// FIXME: should we throw an exception here instead?
+		return null; 
+	}
+	// we hide our joinpoint instance in obj[funcName + '$joinpoint']
+	var jpname = funcName + "$joinpoint";
+	var jpfuncname = funcName + "$joinpoint$method";
+	var joinpoint = obj[jpname];
+	if(!joinpoint){
+		var isNode = false;
+		if(dojo.event["browser"]){
+			if( (obj["attachEvent"])||
+				(obj["nodeType"])||
+				(obj["addEventListener"]) ){
+				isNode = true;
+				dojo.event.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, funcName]);
+			}
+		}
+		var origArity = obj[funcName].length;
+		obj[jpfuncname] = obj[funcName];
+		// joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, funcName);
+		joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, jpfuncname);
+		obj[funcName] = function(){ 
+			var args = [];
+
+			if((isNode)&&(!arguments.length)){
+				var evt = null;
+				try{
+					if(obj.ownerDocument){
+						evt = obj.ownerDocument.parentWindow.event;
+					}else if(obj.documentElement){
+						evt = obj.documentElement.ownerDocument.parentWindow.event;
+					}else if(obj.event){ //obj is a window
+						evt = obj.event;
+					}else{
+						evt = window.event;
+					}
+				}catch(e){
+					evt = window.event;
+				}
+
+				if(evt){
+					args.push(dojo.event.browser.fixEvent(evt, this));
+				}
+			}else{
+				for(var x=0; x<arguments.length; x++){
+					if((x==0)&&(isNode)&&(dojo.event.browser.isEvent(arguments[x]))){
+						args.push(dojo.event.browser.fixEvent(arguments[x], this));
+					}else{
+						args.push(arguments[x]);
+					}
+				}
+			}
+			// return joinpoint.run.apply(joinpoint, arguments); 
+			return joinpoint.run.apply(joinpoint, args); 
+		}
+		obj[funcName].__preJoinArity = origArity;
+	}
+	return joinpoint; // dojo.event.MethodJoinPoint
+}
+
+dojo.lang.extend(dojo.event.MethodJoinPoint, {
+	unintercept: function(){
+		// summary: 
+		//		destroy the connection to all listeners that may have been
+		//		registered on this joinpoint
+		this.object[this.methodname] = this.methodfunc;
+		this.before = [];
+		this.after = [];
+		this.around = [];
+	},
+
+	disconnect: dojo.lang.forward("unintercept"),
+
+	run: function(){
+		// summary:
+		//		execute the connection represented by this join point. The
+		//		arguments passed to run() will be passed to the function and
+		//		its listeners.
+		var obj = this.object||dj_global;
+		var args = arguments;
+
+		// optimization. We only compute once the array version of the arguments
+		// pseudo-arr in order to prevent building it each time advice is unrolled.
+		var aargs = [];
+		for(var x=0; x<args.length; x++){
+			aargs[x] = args[x];
+		}
+
+		var unrollAdvice  = function(marr){ 
+			if(!marr){
+				dojo.debug("Null argument to unrollAdvice()");
+				return;
+			}
+		  
+			var callObj = marr[0]||dj_global;
+			var callFunc = marr[1];
+			
+			if(!callObj[callFunc]){
+				dojo.raise("function \"" + callFunc + "\" does not exist on \"" + callObj + "\"");
+			}
+			
+			var aroundObj = marr[2]||dj_global;
+			var aroundFunc = marr[3];
+			var msg = marr[6];
+			var undef;
+
+			var to = {
+				args: [],
+				jp_: this,
+				object: obj,
+				proceed: function(){
+					return callObj[callFunc].apply(callObj, to.args);
+				}
+			};
+			to.args = aargs;
+
+			var delay = parseInt(marr[4]);
+			var hasDelay = ((!isNaN(delay))&&(marr[4]!==null)&&(typeof marr[4] != "undefined"));
+			if(marr[5]){
+				var rate = parseInt(marr[5]);
+				var cur = new Date();
+				var timerSet = false;
+				if((marr["last"])&&((cur-marr.last)<=rate)){
+					if(dojo.event._canTimeout){
+						if(marr["delayTimer"]){
+							clearTimeout(marr.delayTimer);
+						}
+						var tod = parseInt(rate*2); // is rate*2 naive?
+						var mcpy = dojo.lang.shallowCopy(marr);
+						marr.delayTimer = setTimeout(function(){
+							// FIXME: on IE at least, event objects from the
+							// browser can go out of scope. How (or should?) we
+							// deal with it?
+							mcpy[5] = 0;
+							unrollAdvice(mcpy);
+						}, tod);
+					}
+					return;
+				}else{
+					marr.last = cur;
+				}
+			}
+
+			// FIXME: need to enforce rates for a connection here!
+
+			if(aroundFunc){
+				// NOTE: around advice can't delay since we might otherwise depend
+				// on execution order!
+				aroundObj[aroundFunc].call(aroundObj, to);
+			}else{
+				// var tmjp = dojo.event.MethodJoinPoint.getForMethod(obj, methname);
+				if((hasDelay)&&((dojo.render.html)||(dojo.render.svg))){  // FIXME: the render checks are grotty!
+					dj_global["setTimeout"](function(){
+						if(msg){
+							callObj[callFunc].call(callObj, to); 
+						}else{
+							callObj[callFunc].apply(callObj, args); 
+						}
+					}, delay);
+				}else{ // many environments can't support delay!
+					if(msg){
+						callObj[callFunc].call(callObj, to); 
+					}else{
+						callObj[callFunc].apply(callObj, args); 
+					}
+				}
+			}
+		}
+
+		var unRollSquelch = function(){
+			if(this.squelch){
+				try{
+					return unrollAdvice.apply(this, arguments);
+				}catch(e){ 
+					dojo.debug(e);
+				}
+			}else{
+				return unrollAdvice.apply(this, arguments);
+			}
+		}
+
+		if((this["before"])&&(this.before.length>0)){
+			// pass a cloned array, if this event disconnects this event forEach on this.before wont work
+			dojo.lang.forEach(this.before.concat(new Array()), unRollSquelch);
+		}
+
+		var result;
+		try{
+			if((this["around"])&&(this.around.length>0)){
+				var mi = new dojo.event.MethodInvocation(this, obj, args);
+				result = mi.proceed();
+			}else if(this.methodfunc){
+				result = this.object[this.methodname].apply(this.object, args);
+			}
+		}catch(e){ if(!this.squelch){ dojo.raise(e); } }
+
+		if((this["after"])&&(this.after.length>0)){
+			// see comment on this.before above
+			dojo.lang.forEach(this.after.concat(new Array()), unRollSquelch);
+		}
+
+		return (this.methodfunc) ? result : null;
+	},
+
+	getArr: function(/*String*/kind){
+		// summary: return a list of listeners of the past "kind"
+		// kind:
+		//		can be one of: "before", "after", "around", "before-around", or
+		//		"after-around"
+		var type = "after";
+		// FIXME: we should be able to do this through props or Array.in()
+		if((typeof kind == "string")&&(kind.indexOf("before")!=-1)){
+			type = "before";
+		}else if(kind=="around"){
+			type = "around";
+		}
+		if(!this[type]){ this[type] = []; }
+		return this[type]; // Array
+	},
+
+	kwAddAdvice: function(/*Object*/args){
+		// summary:
+		//		adds advice to the joinpoint with arguments in a map
+		// args:
+		// 		An object that can have the following properties:
+		//			- adviceType
+		//			- adviceObj
+		//			- adviceFunc 
+		//			- aroundObj
+		//			- aroundFunc
+		//			- once
+		//			- delay
+		//			- rate
+		//			- adviceMsg
+		this.addAdvice(	args["adviceObj"], args["adviceFunc"], 
+						args["aroundObj"], args["aroundFunc"], 
+						args["adviceType"], args["precedence"], 
+						args["once"], args["delay"], args["rate"], 
+						args["adviceMsg"]);
+	},
+
+	addAdvice: function(	thisAdviceObj, thisAdvice, 
+							thisAroundObj, thisAround, 
+							adviceType, precedence, 
+							once, delay, rate, asMessage){
+		// summary:
+		//		add advice to this joinpoint using positional parameters
+		// thisAdviceObj:
+		//		the scope in which to locate/execute the named adviceFunc.
+		// thisAdviceFunc:
+		//		the name of the function being conected
+		// thisAroundObj:
+		//		the scope in which to locate/execute the named aroundFunc.
+		// thisAroundFunc:
+		//		the name of the function that will be used to mediate the
+		//		advice call.
+		// adviceType: 
+		//		Optional. String. One of "before", "after", "around",
+		//		"before-around", or "after-around". FIXME
+		// once:
+		//		boolean that determines whether or not this advice will create
+		//		a new connection if an identical advice set has already been
+		//		provided. Defaults to "false".
+		// delay:
+		//		an optional delay (in ms), as an integer, for dispatch of a
+		//		listener after the source has been fired.
+		// rate:
+		//		an optional rate throttling parameter (integer, in ms). When
+		//		specified, this particular connection will not fire more than
+		//		once in the interval specified by the rate
+		// adviceMsg:
+		//		boolean. Should the listener have all the parameters passed in
+		//		as a single argument?
+		var arr = this.getArr(adviceType);
+		if(!arr){
+			dojo.raise("bad this: " + this);
+		}
+
+		var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage];
+		
+		if(once){
+			if(this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr) >= 0){
+				return;
+			}
+		}
+
+		if(precedence == "first"){
+			arr.unshift(ao);
+		}else{
+			arr.push(ao);
+		}
+	},
+
+	hasAdvice: function(thisAdviceObj, thisAdvice, adviceType, arr){
+		// summary:
+		//		returns the array index of the first existing connection
+		//		betweened the passed advice and this joinpoint. Will be -1 if
+		//		none exists.
+		// thisAdviceObj:
+		//		the scope in which to locate/execute the named adviceFunc.
+		// thisAdviceFunc:
+		//		the name of the function being conected
+		// adviceType: 
+		//		Optional. String. One of "before", "after", "around",
+		//		"before-around", or "after-around". FIXME
+		// arr:
+		//		Optional. The list of advices to search. Will be found via
+		//		adviceType if not passed
+		if(!arr){ arr = this.getArr(adviceType); }
+		var ind = -1;
+		for(var x=0; x<arr.length; x++){
+			var aao = (typeof thisAdvice == "object") ? (new String(thisAdvice)).toString() : thisAdvice;
+			var a1o = (typeof arr[x][1] == "object") ? (new String(arr[x][1])).toString() : arr[x][1];
+			if((arr[x][0] == thisAdviceObj)&&(a1o == aao)){
+				ind = x;
+			}
+		}
+		return ind; // Integer
+	},
+
+	removeAdvice: function(thisAdviceObj, thisAdvice, adviceType, once){
+		// summary:
+		//		returns the array index of the first existing connection
+		//		betweened the passed advice and this joinpoint. Will be -1 if
+		//		none exists.
+		// thisAdviceObj:
+		//		the scope in which to locate/execute the named adviceFunc.
+		// thisAdviceFunc:
+		//		the name of the function being conected
+		// adviceType: 
+		//		Optional. String. One of "before", "after", "around",
+		//		"before-around", or "after-around". FIXME
+		// once:
+		//		Optional. Should this only remove the first occurance of the
+		//		connection?
+		var arr = this.getArr(adviceType);
+		var ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
+		if(ind == -1){
+			return false;
+		}
+		while(ind != -1){
+			arr.splice(ind, 1);
+			if(once){ break; }
+			ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
+		}
+		return true;
+	}
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/topic.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/topic.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event/topic.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,201 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.require("dojo.event.common");
+dojo.provide("dojo.event.topic");
+
+dojo.event.topic = new function(){
+	this.topics = {};
+
+	this.getTopic = function(/*String*/topic){
+		// summary:
+		//		returns a topic implementation object of type
+		//		dojo.event.topic.TopicImpl
+		// topic:
+		//		a unique, opaque string that names the topic
+		if(!this.topics[topic]){
+			this.topics[topic] = new this.TopicImpl(topic);
+		}
+		return this.topics[topic]; // a dojo.event.topic.TopicImpl object
+	}
+
+	this.registerPublisher = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
+		// summary:
+		//		registers a function as a publisher on a topic. Subsequent
+		//		calls to the function will cause a publish event on the topic
+		//		with the arguments passed to the function passed to registered
+		//		listeners.
+		// topic: 
+		//		a unique, opaque string that names the topic
+		// obj:
+		//		the scope to locate the function in
+		// funcName:
+		//		the name of the function to register
+		var topic = this.getTopic(topic);
+		topic.registerPublisher(obj, funcName);
+	}
+
+	this.subscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
+		// summary:
+		//		susbscribes the function to the topic. Subsequent events
+		//		dispached to the topic will create a function call for the
+		//		obj.funcName() function.
+		// topic: 
+		//		a unique, opaque string that names the topic
+		// obj:
+		//		the scope to locate the function in
+		// funcName:
+		//		the name of the function to being registered as a listener
+		var topic = this.getTopic(topic);
+		topic.subscribe(obj, funcName);
+	}
+
+	this.unsubscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
+		// summary:
+		//		unsubscribes the obj.funcName() from the topic
+		// topic: 
+		//		a unique, opaque string that names the topic
+		// obj:
+		//		the scope to locate the function in
+		// funcName:
+		//		the name of the function to being unregistered as a listener
+		var topic = this.getTopic(topic);
+		topic.unsubscribe(obj, funcName);
+	}
+
+	this.destroy = function(/*String*/topic){
+		// summary: 
+		//		destroys the topic and unregisters all listeners
+		// topic:
+		//		a unique, opaque string that names the topic
+		this.getTopic(topic).destroy();
+		delete this.topics[topic];
+	}
+
+	this.publishApply = function(/*String*/topic, /*Array*/args){
+		// summary: 
+		//		dispatches an event to the topic using the args array as the
+		//		source for the call arguments to each listener. This is similar
+		//		to JavaScript's built-in Function.apply()
+		// topic:
+		//		a unique, opaque string that names the topic
+		// args:
+		//		the arguments to be passed into listeners of the topic
+		var topic = this.getTopic(topic);
+		topic.sendMessage.apply(topic, args);
+	}
+
+	this.publish = function(/*String*/topic, /*Object*/message){
+		// summary: 
+		//		manually "publish" to the passed topic
+		// topic:
+		//		a unique, opaque string that names the topic
+		// message:
+		//		can be an array of parameters (similar to publishApply), or
+		//		will be treated as one of many arguments to be passed along in
+		//		a "flat" unrolling
+		var topic = this.getTopic(topic);
+		// if message is an array, we treat it as a set of arguments,
+		// otherwise, we just pass on the arguments passed in as-is
+		var args = [];
+		// could we use concat instead here?
+		for(var x=1; x<arguments.length; x++){
+			args.push(arguments[x]);
+		}
+		topic.sendMessage.apply(topic, args);
+	}
+}
+
+dojo.event.topic.TopicImpl = function(topicName){
+	// summary: a class to represent topics
+
+	this.topicName = topicName;
+
+	this.subscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
+		// summary:
+		//		use dojo.event.connect() to attach the passed listener to the
+		//		topic represented by this object
+		// listenerObject:
+		//		if a string and listenerMethod is ommitted, this is treated as
+		//		the name of a function in the global namespace. If
+		//		listenerMethod is provided, this is the scope to find/execute
+		//		the function in.
+		// listenerMethod:
+		//		Optional. The function to register.
+		var tf = listenerMethod||listenerObject;
+		var to = (!listenerMethod) ? dj_global : listenerObject;
+		return dojo.event.kwConnect({ // dojo.event.MethodJoinPoint
+			srcObj:		this, 
+			srcFunc:	"sendMessage", 
+			adviceObj:	to,
+			adviceFunc: tf
+		});
+	}
+
+	this.unsubscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
+		// summary:
+		//		use dojo.event.disconnect() to attach the passed listener to the
+		//		topic represented by this object
+		// listenerObject:
+		//		if a string and listenerMethod is ommitted, this is treated as
+		//		the name of a function in the global namespace. If
+		//		listenerMethod is provided, this is the scope to find the
+		//		function in.
+		// listenerMethod:
+		//		Optional. The function to unregister.
+		var tf = (!listenerMethod) ? listenerObject : listenerMethod;
+		var to = (!listenerMethod) ? null : listenerObject;
+		return dojo.event.kwDisconnect({ // dojo.event.MethodJoinPoint
+			srcObj:		this, 
+			srcFunc:	"sendMessage", 
+			adviceObj:	to,
+			adviceFunc: tf
+		});
+	}
+
+	this._getJoinPoint = function(){
+		return dojo.event.MethodJoinPoint.getForMethod(this, "sendMessage");
+	}
+
+	this.setSquelch = function(/*Boolean*/shouldSquelch){
+		// summary: 
+		//		determine whether or not exceptions in the calling of a
+		//		listener in the chain should stop execution of the chain.
+		this._getJoinPoint().squelch = shouldSquelch;
+	}
+
+	this.destroy = function(){
+		// summary: disconnects all listeners from this topic
+		this._getJoinPoint().disconnect();
+	}
+
+	this.registerPublisher = function(	/*Object*/publisherObject, 
+										/*Function or String*/publisherMethod){
+		// summary:
+		//		registers the passed function as a publisher on this topic.
+		//		Each time the function is called, an event will be published on
+		//		this topic.
+		// publisherObject:
+		//		if a string and listenerMethod is ommitted, this is treated as
+		//		the name of a function in the global namespace. If
+		//		listenerMethod is provided, this is the scope to find the
+		//		function in.
+		// publisherMethod:
+		//		Optional. The function to register.
+		dojo.event.connect(publisherObject, publisherMethod, this, "sendMessage");
+	}
+
+	this.sendMessage = function(message){
+		// summary: a stub to be called when a message is sent to the topic.
+
+		// The message has been propagated
+	}
+}
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/event.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,14 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.event");
+
+dojo.require("dojo.event.*");
+dojo.deprecated("dojo.event", "replaced by dojo.event.*", "0.5");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/experimental.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/experimental.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/experimental.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,30 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.experimental");
+
+dojo.experimental = function(/* String */ moduleName, /* String? */ extra){
+	// summary: Marks code as experimental.
+	// description: 
+	//    This can be used to mark a function, file, or module as experimental.
+	//    Experimental code is not ready to be used, and the APIs are subject
+	//    to change without notice.  Experimental code may be completed deleted
+	//    without going through the normal deprecation process.
+	// moduleName: The name of a module, or the name of a module file or a specific function
+	// extra: some additional message for the user
+	
+	// examples:
+	//    dojo.experimental("dojo.data.Result");
+	//    dojo.experimental("dojo.weather.toKelvin()", "PENDING approval from NOAA");
+	var message = "EXPERIMENTAL: " + moduleName;
+	message += " -- Not yet ready for use.  APIs subject to change without notice.";
+	if(extra){ message += " " + extra; }
+	dojo.debug(message);
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/flash.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/flash.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/flash.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,1230 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.flash");
+
+dojo.require("dojo.string.*");
+dojo.require("dojo.uri.*");
+dojo.require("dojo.html.common");
+
+/** 
+		The goal of dojo.flash is to make it easy to extend Flash's capabilities
+		into an AJAX/DHTML environment. Robust, performant, reliable 
+		JavaScript/Flash communication is harder than most realize when they
+		delve into the topic, especially if you want it
+		to work on Internet Explorer, Firefox, and Safari, and to be able to
+		push around hundreds of K of information quickly. Dojo.flash makes it
+		possible to support these platforms; you have to jump through a few
+		hoops to get its capabilites, but if you are a library writer 
+		who wants to bring Flash's storage or streaming sockets ability into
+		DHTML, for example, then dojo.flash is perfect for you.
+  
+		Dojo.flash provides an easy object for interacting with the Flash plugin. 
+		This object provides methods to determine the current version of the Flash
+		plugin (dojo.flash.info); execute Flash instance methods 
+		independent of the Flash version
+		being used (dojo.flash.comm); write out the necessary markup to 
+		dynamically insert a Flash object into the page (dojo.flash.Embed; and 
+		do dynamic installation and upgrading of the current Flash plugin in 
+		use (dojo.flash.Install).
+		
+		To use dojo.flash, you must first wait until Flash is finished loading 
+		and initializing before you attempt communication or interaction. 
+		To know when Flash is finished use dojo.event.connect:
+		
+		dojo.event.connect(dojo.flash, "loaded", myInstance, "myCallback");
+		
+		Then, while the page is still loading provide the file name
+		and the major version of Flash that will be used for Flash/JavaScript
+		communication (see "Flash Communication" below for information on the 
+		different kinds of Flash/JavaScript communication supported and how they 
+		depend on the version of Flash installed):
+		
+		dojo.flash.setSwf({flash6: "src/storage/storage_flash6.swf",
+											 flash8: "src/storage/storage_flash8.swf"});
+		
+		This will cause dojo.flash to pick the best way of communicating
+		between Flash and JavaScript based on the platform.
+		
+		If no SWF files are specified, then Flash is not initialized.
+		
+		Your Flash must use DojoExternalInterface to expose Flash methods and
+		to call JavaScript; see "Flash Communication" below for details.
+		
+		setSwf can take an optional 'visible' attribute to control whether
+		the Flash object is visible or not on the page; the default is visible:
+		
+		dojo.flash.setSwf({flash6: "src/storage/storage_flash6.swf",
+											 flash8: "src/storage/storage_flash8.swf",
+											 visible: false});
+		
+		Once finished, you can query Flash version information:
+		
+		dojo.flash.info.version
+		
+		Or can communicate with Flash methods that were exposed:
+		
+		var results = dojo.flash.comm.sayHello("Some Message");
+		
+		Only string values are currently supported for both arguments and
+		for return results. Everything will be cast to a string on both
+		the JavaScript and Flash sides.
+		
+		-------------------
+		Flash Communication
+		-------------------
+		
+		dojo.flash allows Flash/JavaScript communication in 
+		a way that can pass large amounts of data back and forth reliably and
+		very fast. The dojo.flash
+		framework encapsulates the specific way in which this communication occurs,
+		presenting a common interface to JavaScript irrespective of the underlying
+		Flash version.
+		
+		There are currently three major ways to do Flash/JavaScript communication
+		in the Flash community:
+		
+		1) Flash 6+ - Uses Flash methods, such as SetVariable and TCallLabel,
+		and the fscommand handler to do communication. Strengths: Very fast,
+		mature, and can send extremely large amounts of data; can do
+		synchronous method calls. Problems: Does not work on Safari; works on 
+		Firefox/Mac OS X only if Flash 8 plugin is installed; cryptic to work with.
+		
+		2) Flash 8+ - Uses ExternalInterface, which provides a way for Flash
+		methods to register themselves for callbacks from JavaScript, and a way
+		for Flash to call JavaScript. Strengths: Works on Safari; elegant to
+		work with; can do synchronous method calls. Problems: Extremely buggy 
+		(fails if there are new lines in the data, for example); performance
+		degrades drastically in O(n^2) time as data grows; locks up the browser while
+		it is communicating; does not work in Internet Explorer if Flash
+		object is dynamically added to page with document.writeln, DOM methods,
+		or innerHTML.
+		
+		3) Flash 6+ - Uses two seperate Flash applets, one that we 
+		create over and over, passing input data into it using the PARAM tag, 
+		which then uses a Flash LocalConnection to pass the data to the main Flash
+		applet; communication back to Flash is accomplished using a getURL
+		call with a javascript protocol handler, such as "javascript:myMethod()".
+		Strengths: the most cross browser, cross platform pre-Flash 8 method
+		of Flash communication known; works on Safari. Problems: Timing issues;
+		clunky and complicated; slow; can only send very small amounts of
+		data (several K); all method calls are asynchronous.
+		
+		dojo.flash.comm uses only the first two methods. This framework
+		was created primarily for dojo.storage, which needs to pass very large
+		amounts of data synchronously and reliably across the Flash/JavaScript
+		boundary. We use the first method, the Flash 6 method, on all platforms
+		that support it, while using the Flash 8 ExternalInterface method
+		only on Safari with some special code to help correct ExternalInterface's
+		bugs.
+		
+		Since dojo.flash needs to have two versions of the Flash
+		file it wants to generate, a Flash 6 and a Flash 8 version to gain
+		true cross-browser compatibility, several tools are provided to ease
+		development on the Flash side.
+		
+		In your Flash file, if you want to expose Flash methods that can be
+		called, use the DojoExternalInterface class to register methods. This
+		class is an exact API clone of the standard ExternalInterface class, but
+		can work in Flash 6+ browsers. Under the covers it uses the best
+		mechanism to do communication:
+		
+		class HelloWorld{
+			function HelloWorld(){
+				// Initialize the DojoExternalInterface class
+				DojoExternalInterface.initialize();
+				
+				// Expose your methods
+				DojoExternalInterface.addCallback("sayHello", this, this.sayHello);
+				
+				// Tell JavaScript that you are ready to have method calls
+				DojoExternalInterface.loaded();
+				
+				// Call some JavaScript
+				var resultsReady = function(results){
+					trace("Received the following results from JavaScript: " + results);
+				}
+				DojoExternalInterface.call("someJavaScriptMethod", resultsReady, 
+																	 someParameter);
+			}
+			
+			function sayHello(){ ... }
+			
+			static main(){ ... }
+		}
+		
+		DojoExternalInterface adds two new functions to the ExternalInterface
+		API: initialize() and loaded(). initialize() must be called before
+		any addCallback() or call() methods are run, and loaded() must be
+		called after you are finished adding your callbacks. Calling loaded()
+		will fire the dojo.flash.loaded() event, so that JavaScript can know that
+		Flash has finished loading and adding its callbacks, and can begin to
+		interact with the Flash file.
+		
+		To generate your SWF files, use the ant task
+		"buildFlash". You must have the open source Motion Twin ActionScript 
+		compiler (mtasc) installed and in your path to use the "buildFlash"
+		ant task; download and install mtasc from http://www.mtasc.org/.
+		
+		
+		
+		buildFlash usage:
+		
+		ant buildFlash -Ddojo.flash.file=../tests/flash/HelloWorld.as
+		
+		where "dojo.flash.file" is the relative path to your Flash 
+		ActionScript file.
+		
+		This will generate two SWF files, one ending in _flash6.swf and the other
+		ending in _flash8.swf in the same directory as your ActionScript method:
+		
+		HelloWorld_flash6.swf
+		HelloWorld_flash8.swf
+		
+		Initialize dojo.flash with the filename and Flash communication version to
+		use during page load; see the documentation for dojo.flash for details:
+		
+		dojo.flash.setSwf({flash6: "tests/flash/HelloWorld_flash6.swf",
+											 flash8: "tests/flash/HelloWorld_flash8.swf"});
+		
+		Now, your Flash methods can be called from JavaScript as if they are native
+		Flash methods, mirrored exactly on the JavaScript side:
+		
+		dojo.flash.comm.sayHello();
+		
+		Only Strings are supported being passed back and forth currently.
+		
+		JavaScript to Flash communication is synchronous; i.e., results are returned
+		directly from the method call:
+		
+		var results = dojo.flash.comm.sayHello();
+		
+		Flash to JavaScript communication is asynchronous due to limitations in
+		the underlying technologies; you must use a results callback to handle
+		results returned by JavaScript in your Flash AS files:
+		
+		var resultsReady = function(results){
+			trace("Received the following results from JavaScript: " + results);
+		}
+		DojoExternalInterface.call("someJavaScriptMethod", resultsReady);
+		
+		
+		
+		-------------------
+		Notes
+		-------------------
+		
+		If you have both Flash 6 and Flash 8 versions of your file:
+		
+		dojo.flash.setSwf({flash6: "tests/flash/HelloWorld_flash6.swf",
+											 flash8: "tests/flash/HelloWorld_flash8.swf"});
+											 
+		but want to force the browser to use a certain version of Flash for
+		all platforms (for testing, for example), use the djConfig
+		variable 'forceFlashComm' with the version number to force:
+		
+		var djConfig = { forceFlashComm: 6 };
+		
+		Two values are currently supported, 6 and 8, for the two styles of
+		communication described above. Just because you force dojo.flash
+		to use a particular communication style is no guarantee that it will
+		work; for example, Flash 8 communication doesn't work in Internet
+		Explorer due to bugs in Flash, and Flash 6 communication does not work
+		in Safari. It is best to let dojo.flash determine the best communication
+		mechanism, and to use the value above only for debugging the dojo.flash
+		framework itself.
+		
+		Also note that dojo.flash can currently only work with one Flash object
+		on the page; it and the API do not yet support multiple Flash objects on
+		the same page.
+		
+		We use some special tricks to get decent, linear performance
+		out of Flash 8's ExternalInterface on Safari; see the blog
+		post 
+		http://codinginparadise.org/weblog/2006/02/how-to-speed-up-flash-8s.html
+		for details.
+		
+		Your code can detect whether the Flash player is installing or having
+		its version revved in two ways. First, if dojo.flash detects that
+		Flash installation needs to occur, it sets dojo.flash.info.installing
+		to true. Second, you can detect if installation is necessary with the
+		following callback:
+		
+		dojo.event.connect(dojo.flash, "installing", myInstance, "myCallback");
+		
+		You can use this callback to delay further actions that might need Flash;
+		when installation is finished the full page will be refreshed and the
+		user will be placed back on your page with Flash installed.
+		
+		Two utility methods exist if you want to add loading and installing
+		listeners without creating dependencies on dojo.event; these are
+		'addLoadingListener' and 'addInstallingListener'.
+		
+		-------------------
+		Todo/Known Issues
+		-------------------
+
+		There are several tasks I was not able to do, or did not need to fix
+		to get dojo.storage out:		
+		
+		* When using Flash 8 communication, Flash method calls to JavaScript
+		are not working properly; serialization might also be broken for certain
+		invalid characters when it is Flash invoking JavaScript methods.
+		The Flash side needs to have more sophisticated serialization/
+		deserialization mechanisms like JavaScript currently has. The
+		test_flash2.html unit tests should also be updated to have much more
+		sophisticated Flash to JavaScript unit tests, including large
+		amounts of data.
+		
+		* On Internet Explorer, after doing a basic install, the page is
+		not refreshed or does not detect that Flash is now available. The way
+		to fix this is to create a custom small Flash file that is pointed to
+		during installation; when it is finished loading, it does a callback
+		that says that Flash installation is complete on IE, and we can proceed
+		to initialize the dojo.flash subsystem.
+		
+		@author Brad Neuberg, bkn3 at columbia.edu
+*/
+
+dojo.flash = {
+	flash6_version: null,
+	flash8_version: null,
+	ready: false,
+	_visible: true,
+	_loadedListeners: new Array(),
+	_installingListeners: new Array(),
+	
+	/** Sets the SWF files and versions we are using. */
+	setSwf: function(fileInfo){
+		//dojo.debug("setSwf");
+		if(fileInfo == null || dojo.lang.isUndefined(fileInfo)){
+			return;
+		}
+		
+		if(fileInfo.flash6 != null && !dojo.lang.isUndefined(fileInfo.flash6)){
+			this.flash6_version = fileInfo.flash6;
+		}
+		
+		if(fileInfo.flash8 != null && !dojo.lang.isUndefined(fileInfo.flash8)){
+			this.flash8_version = fileInfo.flash8;
+		}
+		
+		if(!dojo.lang.isUndefined(fileInfo.visible)){
+			this._visible = fileInfo.visible;
+		}
+		
+		// initialize ourselves		
+		this._initialize();
+	},
+	
+	/** Returns whether we are using Flash 6 for communication on this platform. */
+	useFlash6: function(){
+		if(this.flash6_version == null){
+			return false;
+		}else if (this.flash6_version != null && dojo.flash.info.commVersion == 6){
+			// if we have a flash 6 version of this SWF, and this browser supports 
+			// communicating using Flash 6 features...
+			return true;
+		}else{
+			return false;
+		}
+	},
+	
+	/** Returns whether we are using Flash 8 for communication on this platform. */
+	useFlash8: function(){
+		if(this.flash8_version == null){
+			return false;
+		}else if (this.flash8_version != null && dojo.flash.info.commVersion == 8){
+			// if we have a flash 8 version of this SWF, and this browser supports
+			// communicating using Flash 8 features...
+			return true;
+		}else{
+			return false;
+		}
+	},
+	
+	/** Adds a listener to know when Flash is finished loading. 
+			Useful if you don't want a dependency on dojo.event. */
+	addLoadedListener: function(listener){
+		this._loadedListeners.push(listener);
+	},
+
+	/** Adds a listener to know if Flash is being installed. 
+			Useful if you don't want a dependency on dojo.event. */
+	addInstallingListener: function(listener){
+		this._installingListeners.push(listener);
+	},	
+	
+	/** 
+			A callback when the Flash subsystem is finished loading and can be
+			worked with. To be notified when Flash is finished loading, connect
+			your callback to this method using the following:
+			
+			dojo.event.connect(dojo.flash, "loaded", myInstance, "myCallback");
+	*/
+	loaded: function(){
+		//dojo.debug("dojo.flash.loaded");
+		dojo.flash.ready = true;
+		if(dojo.flash._loadedListeners.length > 0){
+			for(var i = 0;i < dojo.flash._loadedListeners.length; i++){
+				dojo.flash._loadedListeners[i].call(null);
+			}
+		}
+	},
+	
+	/** 
+			A callback to know if Flash is currently being installed or
+			having its version revved. To be notified if Flash is installing, connect
+			your callback to this method using the following:
+			
+			dojo.event.connect(dojo.flash, "installing", myInstance, "myCallback");
+	*/
+	installing: function(){
+	 //dojo.debug("installing");
+	 if(dojo.flash._installingListeners.length > 0){
+			for(var i = 0; i < dojo.flash._installingListeners.length; i++){
+				dojo.flash._installingListeners[i].call(null);
+			}
+		}
+	},
+	
+	/** Initializes dojo.flash. */
+	_initialize: function(){
+		//dojo.debug("dojo.flash._initialize");
+		// see if we need to rev or install Flash on this platform
+		var installer = new dojo.flash.Install();
+		dojo.flash.installer = installer;
+
+		if(installer.needed() == true){		
+			installer.install();
+		}else{
+			//dojo.debug("Writing object out");
+			// write the flash object into the page
+			dojo.flash.obj = new dojo.flash.Embed(this._visible);
+			dojo.flash.obj.write(dojo.flash.info.commVersion);
+			
+			// initialize the way we do Flash/JavaScript communication
+			dojo.flash.comm = new dojo.flash.Communicator();
+		}
+	}
+};
+
+
+/** 
+		A class that helps us determine whether Flash is available,
+		it's major and minor versions, and what Flash version features should
+		be used for Flash/JavaScript communication. Parts of this code
+		are adapted from the automatic Flash plugin detection code autogenerated 
+		by the Macromedia Flash 8 authoring environment. 
+		
+		An instance of this class can be accessed on dojo.flash.info after
+		the page is finished loading.
+		
+		This constructor must be called before the page is finished loading. 
+*/
+dojo.flash.Info = function(){
+	// Visual basic helper required to detect Flash Player ActiveX control 
+	// version information on Internet Explorer
+	if(dojo.render.html.ie){
+		document.writeln('<script language="VBScript" type="text/vbscript"\>');
+		document.writeln('Function VBGetSwfVer(i)');
+		document.writeln('  on error resume next');
+		document.writeln('  Dim swControl, swVersion');
+		document.writeln('  swVersion = 0');
+		document.writeln('  set swControl = CreateObject("ShockwaveFlash.ShockwaveFlash." + CStr(i))');
+		document.writeln('  if (IsObject(swControl)) then');
+		document.writeln('    swVersion = swControl.GetVariable("$version")');
+		document.writeln('  end if');
+		document.writeln('  VBGetSwfVer = swVersion');
+		document.writeln('End Function');
+		document.writeln('</script\>');
+	}
+	
+	this._detectVersion();
+	this._detectCommunicationVersion();
+}
+
+dojo.flash.Info.prototype = {
+	/** The full version string, such as "8r22". */
+	version: -1,
+	
+	/** 
+			The major, minor, and revisions of the plugin. For example, if the
+			plugin is 8r22, then the major version is 8, the minor version is 0,
+			and the revision is 22. 
+	*/
+	versionMajor: -1,
+	versionMinor: -1,
+	versionRevision: -1,
+	
+	/** Whether this platform has Flash already installed. */
+	capable: false,
+	
+	/** 
+			The major version number for how our Flash and JavaScript communicate.
+			This can currently be the following values:
+			6 - We use a combination of the Flash plugin methods, such as SetVariable
+			and TCallLabel, along with fscommands, to do communication.
+			8 - We use the ExternalInterface API. 
+			-1 - For some reason neither method is supported, and no communication
+			is possible. 
+	*/
+	commVersion: 6,
+	
+	/** Set if we are in the middle of a Flash installation session. */
+	installing: false,
+	
+	/** 
+			Asserts that this environment has the given major, minor, and revision
+			numbers for the Flash player. Returns true if the player is equal
+			or above the given version, false otherwise.
+			
+			Example: To test for Flash Player 7r14:
+			
+			dojo.flash.info.isVersionOrAbove(7, 0, 14)
+	*/
+	isVersionOrAbove: function(reqMajorVer, reqMinorVer, reqVer){
+		// make the revision a decimal (i.e. transform revision 14 into
+		// 0.14
+		reqVer = parseFloat("." + reqVer);
+		
+		if(this.versionMajor >= reqMajorVer && this.versionMinor >= reqMinorVer
+			 && this.versionRevision >= reqVer){
+			return true;
+		}else{
+			return false;
+		}
+	},
+	
+	_detectVersion: function(){
+		var versionStr;
+		
+		// loop backwards through the versions until we find the newest version	
+		for(var testVersion = 25; testVersion > 0; testVersion--){
+			if(dojo.render.html.ie){
+				versionStr = VBGetSwfVer(testVersion);
+			}else{
+				versionStr = this._JSFlashInfo(testVersion);		
+			}
+				
+			if(versionStr == -1 ){
+				this.capable = false; 
+				return;
+			}else if(versionStr != 0){
+				var versionArray;
+				if(dojo.render.html.ie){
+					var tempArray = versionStr.split(" ");
+					var tempString = tempArray[1];
+					versionArray = tempString.split(",");
+				}else{
+					versionArray = versionStr.split(".");
+				}
+					
+				this.versionMajor = versionArray[0];
+				this.versionMinor = versionArray[1];
+				this.versionRevision = versionArray[2];
+				
+				// 7.0r24 == 7.24
+				var versionString = this.versionMajor + "." + this.versionRevision;
+				this.version = parseFloat(versionString);
+				
+				this.capable = true;
+				
+				break;
+			}
+		}
+	},
+	
+	/** 
+			JavaScript helper required to detect Flash Player PlugIn version 
+			information. Internet Explorer uses a corresponding Visual Basic
+			version to interact with the Flash ActiveX control. 
+	*/
+	_JSFlashInfo: function(testVersion){
+		// NS/Opera version >= 3 check for Flash plugin in plugin array
+		if(navigator.plugins != null && navigator.plugins.length > 0){
+			if(navigator.plugins["Shockwave Flash 2.0"] || 
+				 navigator.plugins["Shockwave Flash"]){
+				var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
+				var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
+				var descArray = flashDescription.split(" ");
+				var tempArrayMajor = descArray[2].split(".");
+				var versionMajor = tempArrayMajor[0];
+				var versionMinor = tempArrayMajor[1];
+				if(descArray[3] != ""){
+					var tempArrayMinor = descArray[3].split("r");
+				}else{
+					var tempArrayMinor = descArray[4].split("r");
+				}
+				var versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
+				var version = versionMajor + "." + versionMinor + "." 
+											+ versionRevision;
+											
+				return version;
+			}
+		}
+		
+		return -1;
+	},
+	
+	/** 
+			Detects the mechanisms that should be used for Flash/JavaScript 
+			communication, setting 'commVersion' to either 6 or 8. If the value is
+			6, we use Flash Plugin 6+ features, such as GetVariable, TCallLabel,
+			and fscommand, to do Flash/JavaScript communication; if the value is
+			8, we use the ExternalInterface API for communication. 
+	*/
+	_detectCommunicationVersion: function(){
+		if(this.capable == false){
+			this.commVersion = null;
+			return;
+		}
+		
+		// detect if the user has over-ridden the default flash version
+		if (typeof djConfig["forceFlashComm"] != "undefined" &&
+				typeof djConfig["forceFlashComm"] != null){
+			this.commVersion = djConfig["forceFlashComm"];
+			return;
+		}
+		
+		// we prefer Flash 6 features over Flash 8, because they are much faster
+		// and much less buggy
+		
+		// at this point, we don't have a flash file to detect features on,
+		// so we need to instead look at the browser environment we are in
+		if(dojo.render.html.safari == true || dojo.render.html.opera == true){
+			this.commVersion = 8;
+		}else{
+			this.commVersion = 6;
+		}
+	}
+};
+
+/** A class that is used to write out the Flash object into the page. */
+dojo.flash.Embed = function(visible){
+	this._visible = visible;
+}
+
+dojo.flash.Embed.prototype = {
+	/** 
+			The width of this Flash applet. The default is the minimal width
+			necessary to show the Flash settings dialog. 
+	*/
+	width: 215,
+	
+	/** 
+			The height of this Flash applet. The default is the minimal height
+			necessary to show the Flash settings dialog. 
+	*/
+	height: 138,
+	
+	/** The id of the Flash object. */
+	id: "flashObject",
+	
+	/** Controls whether this is a visible Flash applet or not. */
+	_visible: true,
+
+	protocol: function(){
+		switch(window.location.protocol){
+			case "https:":
+				return "https";
+				break;
+			default:
+				return "http";
+				break;
+		}
+	},
+	
+	/** 
+			Writes the Flash into the page. This must be called before the page
+			is finished loading. 
+			@param flashVer The Flash version to write.
+			@param doExpressInstall Whether to write out Express Install
+			information. Optional value; defaults to false.
+	*/
+	
+	write: function(flashVer, doExpressInstall){
+		//dojo.debug("write");
+		if(dojo.lang.isUndefined(doExpressInstall)){
+			doExpressInstall = false;
+		}
+		
+		// determine our container div's styling
+		var containerStyle = new dojo.string.Builder();
+		containerStyle.append("width: " + this.width + "px; ");
+		containerStyle.append("height: " + this.height + "px; ");
+		if(this._visible == false){
+			containerStyle.append("position: absolute; ");
+			containerStyle.append("z-index: 10000; ");
+			containerStyle.append("top: -1000px; ");
+			containerStyle.append("left: -1000px; ");
+		}
+		containerStyle = containerStyle.toString();
+
+		// figure out the SWF file to get and how to write out the correct HTML
+		// for this Flash version
+		var objectHTML;
+		var swfloc;
+		// Flash 6
+		if(flashVer == 6){
+			swfloc = dojo.flash.flash6_version;
+			var dojoPath = djConfig.baseRelativePath;
+			swfloc = swfloc + "?baseRelativePath=" + escape(dojoPath);
+			objectHTML = 
+						  '<embed id="' + this.id + '" src="' + swfloc + '" '
+						+ '    quality="high" bgcolor="#ffffff" '
+						+ '    width="' + this.width + '" height="' + this.height + '" '
+						+ '    name="' + this.id + '" '
+						+ '    align="middle" allowScriptAccess="sameDomain" '
+						+ '    type="application/x-shockwave-flash" swLiveConnect="true" '
+						+ '    pluginspage="'
+						+ this.protocol()
+						+ '://www.macromedia.com/go/getflashplayer">';
+		}else{ // Flash 8
+			swfloc = dojo.flash.flash8_version;
+			var swflocObject = swfloc;
+			var swflocEmbed = swfloc;
+			var dojoPath = djConfig.baseRelativePath;
+			if(doExpressInstall){
+				// the location to redirect to after installing
+				var redirectURL = escape(window.location);
+				document.title = document.title.slice(0, 47) + " - Flash Player Installation";
+				var docTitle = escape(document.title);
+				swflocObject += "?MMredirectURL=" + redirectURL
+				                + "&MMplayerType=ActiveX"
+				                + "&MMdoctitle=" + docTitle
+								+ "&baseRelativePath=" + escape(dojoPath);
+				swflocEmbed += "?MMredirectURL=" + redirectURL 
+								+ "&MMplayerType=PlugIn"
+								+ "&baseRelativePath=" + escape(dojoPath);
+			}
+
+			if(swflocEmbed.indexOf("?") == -1){
+				swflocEmbed +=  "?baseRelativePath="+escape(dojoPath)+"' ";
+			}
+			
+			objectHTML =
+				'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '
+				  + 'codebase="'
+					+ this.protocol()
+					+ '://fpdownload.macromedia.com/pub/shockwave/cabs/flash/'
+					+ 'swflash.cab#version=8,0,0,0" '
+				  + 'width="' + this.width + '" '
+				  + 'height="' + this.height + '" '
+				  + 'id="' + this.id + '" '
+				  + 'align="middle"> '
+				  + '<param name="allowScriptAccess" value="sameDomain" /> '
+				  + '<param name="movie" value="' + swflocObject + '" /> '
+				  + '<param name="quality" value="high" /> '
+				  + '<param name="bgcolor" value="#ffffff" /> '
+				  + '<embed src="' + swflocEmbed + "' "
+				  + 'quality="high" '
+				  + 'bgcolor="#ffffff" '
+				  + 'width="' + this.width + '" '
+				  + 'height="' + this.height + '" '
+				  + 'id="' + this.id + '" '
+				  + 'name="' + this.id + '" '
+				  + 'swLiveConnect="true" '
+				  + 'align="middle" '
+				  + 'allowScriptAccess="sameDomain" '
+				  + 'type="application/x-shockwave-flash" '
+				  + 'pluginspage="'
+					+ this.protocol()
+					+'://www.macromedia.com/go/getflashplayer" />'
+				+ '</object>';
+		}
+
+		// now write everything out
+		objectHTML = '<div id="' + this.id + 'Container" style="' + containerStyle + '"> '
+						+ objectHTML
+					 + '</div>';
+		document.writeln(objectHTML);
+	},  
+	
+	/** Gets the Flash object DOM node. */
+	get: function(){
+		//return (dojo.render.html.ie) ? window[this.id] : document[this.id];
+		
+		// more robust way to get Flash object; version above can break
+		// communication on IE sometimes
+		return document.getElementById(this.id);
+	},
+	
+	/** Sets the visibility of this Flash object. */
+	setVisible: function(visible){
+		var container = dojo.byId(this.id + "Container");
+		if(visible == true){
+			container.style.visibility = "visible";
+		}else{
+			container.style.position = "absolute";
+			container.style.x = "-1000px";
+			container.style.y = "-1000px";
+			container.style.visibility = "hidden";
+		}
+	},
+	
+	/** Centers the flash applet on the page. */
+	center: function(){
+		var elementWidth = this.width;
+		var elementHeight = this.height;
+
+		var scroll_offset = dojo.html.getScroll().offset;
+		var viewport_size = dojo.html.getViewport();
+
+		// compute the centered position    
+		var x = scroll_offset.x + (viewport_size.width - elementWidth) / 2;
+		var y = scroll_offset.y + (viewport_size.height - elementHeight) / 2; 
+
+		// set the centered position
+		var container = dojo.byId(this.id + "Container");
+		container.style.top = y + "px";
+		container.style.left = x + "px";
+	}
+};
+
+
+/** 
+		A class that is used to communicate between Flash and JavaScript in 
+		a way that can pass large amounts of data back and forth reliably,
+		very fast, and with synchronous method calls. This class encapsulates the 
+		specific way in which this communication occurs,
+		presenting a common interface to JavaScript irrespective of the underlying
+		Flash version.
+*/
+dojo.flash.Communicator = function(){
+	if(dojo.flash.useFlash6()){
+		this._writeFlash6();
+	}else if (dojo.flash.useFlash8()){
+		this._writeFlash8();
+	}
+}
+
+dojo.flash.Communicator.prototype = {
+	_writeFlash6: function(){
+		var id = dojo.flash.obj.id;
+		
+		// global function needed for Flash 6 callback;
+		// we write it out as a script tag because the VBScript hook for IE
+		// callbacks does not work properly if this function is evalled() from
+		// within the Dojo system
+		document.writeln('<script language="JavaScript">');
+		document.writeln('  function ' + id + '_DoFSCommand(command, args){ ');
+		document.writeln('    dojo.flash.comm._handleFSCommand(command, args); ');
+		document.writeln('}');
+		document.writeln('</script>');
+		
+		// hook for Internet Explorer to receive FSCommands from Flash
+		if(dojo.render.html.ie){
+			document.writeln('<SCRIPT LANGUAGE=VBScript\> ');
+			document.writeln('on error resume next ');
+			document.writeln('Sub ' + id + '_FSCommand(ByVal command, ByVal args)');
+			document.writeln(' call ' + id + '_DoFSCommand(command, args)');
+			document.writeln('end sub');
+			document.writeln('</SCRIPT\> ');
+		}
+	},
+	
+	_writeFlash8: function(){
+		// nothing needs to be written out for Flash 8 communication; 
+		// happens automatically
+	},
+	
+	/** Flash 6 communication. */
+	
+	/** Handles fscommand's from Flash to JavaScript. Flash 6 communication. */
+	_handleFSCommand: function(command, args){
+		//dojo.debug("fscommand, command="+command+", args="+args);
+		// Flash 8 on Mac/Firefox precedes all commands with the string "FSCommand:";
+		// strip it off if it is present
+		if(command != null && !dojo.lang.isUndefined(command)
+			&& /^FSCommand:(.*)/.test(command) == true){
+			command = command.match(/^FSCommand:(.*)/)[1];
+		}
+		 
+		if(command == "addCallback"){ // add Flash method for JavaScript callback
+			this._fscommandAddCallback(command, args);
+		}else if(command == "call"){ // Flash to JavaScript method call
+			this._fscommandCall(command, args);
+		}else if(command == "fscommandReady"){ // see if fscommands are ready
+			this._fscommandReady();
+		}
+	},
+	
+	/** Handles registering a callable Flash function. Flash 6 communication. */
+	_fscommandAddCallback: function(command, args){
+		var functionName = args;
+			
+		// do a trick, where we link this function name to our wrapper
+		// function, _call, that does the actual JavaScript to Flash call
+		var callFunc = function(){
+			return dojo.flash.comm._call(functionName, arguments);
+		};			
+		dojo.flash.comm[functionName] = callFunc;
+		
+		// indicate that the call was successful
+		dojo.flash.obj.get().SetVariable("_succeeded", true);
+	},
+	
+	/** Handles Flash calling a JavaScript function. Flash 6 communication. */
+	_fscommandCall: function(command, args){
+		var plugin = dojo.flash.obj.get();
+		var functionName = args;
+		
+		// get the number of arguments to this method call and build them up
+		var numArgs = parseInt(plugin.GetVariable("_numArgs"));
+		var flashArgs = new Array();
+		for(var i = 0; i < numArgs; i++){
+			var currentArg = plugin.GetVariable("_" + i);
+			flashArgs.push(currentArg);
+		}
+		
+		// get the function instance; we technically support more capabilities
+		// than ExternalInterface, which can only call global functions; if
+		// the method name has a dot in it, such as "dojo.flash.loaded", we
+		// eval it so that the method gets run against an instance
+		var runMe;
+		if(functionName.indexOf(".") == -1){ // global function
+			runMe = window[functionName];
+		}else{
+			// instance function
+			runMe = eval(functionName);
+		}
+		
+		// make the call and get the results
+		var results = null;
+		if(!dojo.lang.isUndefined(runMe) && runMe != null){
+			results = runMe.apply(null, flashArgs);
+		}
+		
+		// return the results to flash
+		plugin.SetVariable("_returnResult", results);
+	},
+	
+	/** Reports that fscommands are ready to run if executed from Flash. */
+	_fscommandReady: function(){
+		var plugin = dojo.flash.obj.get();
+		plugin.SetVariable("fscommandReady", "true");
+	},
+	
+	/** 
+			The actual function that will execute a JavaScript to Flash call; used
+			by the Flash 6 communication method. 
+	*/
+	_call: function(functionName, args){
+		// we do JavaScript to Flash method calls by setting a Flash variable
+		// "_functionName" with the function name; "_numArgs" with the number
+		// of arguments; and "_0", "_1", etc for each numbered argument. Flash
+		// reads these, executes the function call, and returns the result
+		// in "_returnResult"
+		var plugin = dojo.flash.obj.get();
+		plugin.SetVariable("_functionName", functionName);
+		plugin.SetVariable("_numArgs", args.length);
+		for(var i = 0; i < args.length; i++){
+			// unlike Flash 8's ExternalInterface, Flash 6 has no problem with
+			// any special characters _except_ for the null character \0; double
+			// encode this so the Flash side never sees it, but we can get it 
+			// back if the value comes back to JavaScript
+			var value = args[i];
+			value = value.replace(/\0/g, "\\0");
+			
+			plugin.SetVariable("_" + i, value);
+		}
+		
+		// now tell Flash to execute this method using the Flash Runner
+		plugin.TCallLabel("/_flashRunner", "execute");
+		
+		// get the results
+		var results = plugin.GetVariable("_returnResult");
+		
+		// we double encoded all null characters as //0 because Flash breaks
+		// if they are present; turn the //0 back into /0
+		results = results.replace(/\\0/g, "\0");
+		
+		return results;
+	},
+	
+	/** Flash 8 communication. */
+	
+	/** 
+			Registers the existence of a Flash method that we can call with
+			JavaScript, using Flash 8's ExternalInterface. 
+	*/
+	_addExternalInterfaceCallback: function(methodName){
+		var wrapperCall = function(){
+			// some browsers don't like us changing values in the 'arguments' array, so
+			// make a fresh copy of it
+			var methodArgs = new Array(arguments.length);
+			for(var i = 0; i < arguments.length; i++){
+				methodArgs[i] = arguments[i];
+			}
+			return dojo.flash.comm._execFlash(methodName, methodArgs);
+		};
+		
+		dojo.flash.comm[methodName] = wrapperCall;
+	},
+	
+	/** 
+			Encodes our data to get around ExternalInterface bugs.
+			Flash 8 communication.
+	*/
+	_encodeData: function(data){
+		// double encode all entity values, or they will be mis-decoded
+		// by Flash when returned
+		var entityRE = /\&([^;]*)\;/g;
+		data = data.replace(entityRE, "&amp;$1;");
+		
+		// entity encode XML-ish characters, or Flash's broken XML serializer
+		// breaks
+		data = data.replace(/</g, "&lt;");
+		data = data.replace(/>/g, "&gt;");
+		
+		// transforming \ into \\ doesn't work; just use a custom encoding
+		data = data.replace("\\", "&custom_backslash;&custom_backslash;");
+		
+		data = data.replace(/\n/g, "\\n");
+		data = data.replace(/\r/g, "\\r");
+		data = data.replace(/\f/g, "\\f");
+		data = data.replace(/\0/g, "\\0"); // null character
+		data = data.replace(/\'/g, "\\\'");
+		data = data.replace(/\"/g, '\\\"');
+		
+		return data;
+	},
+	
+	/** 
+			Decodes our data to get around ExternalInterface bugs.
+			Flash 8 communication.
+	*/
+	_decodeData: function(data){
+		if(data == null || typeof data == "undefined"){
+			return data;
+		}
+		
+		// certain XMLish characters break Flash's wire serialization for
+		// ExternalInterface; these are encoded on the 
+		// DojoExternalInterface side into a custom encoding, rather than
+		// the standard entity encoding, because otherwise we won't be able to
+		// differentiate between our own encoding and any entity characters
+		// that are being used in the string itself
+		data = data.replace(/\&custom_lt\;/g, "<");
+		data = data.replace(/\&custom_gt\;/g, ">");
+		
+		// Unfortunately, Flash returns us our String with special characters
+		// like newlines broken into seperate characters. So if \n represents
+		// a new line, Flash returns it as "\" and "n". This means the character
+		// is _not_ a newline. This forces us to eval() the string to cause
+		// escaped characters to turn into their real special character values.
+		data = eval('"' + data + '"');
+		
+		return data;
+	},
+	
+	/** 
+			Sends our method arguments over to Flash in chunks in order to
+			have ExternalInterface's performance not be O(n^2).
+			Flash 8 communication.
+	*/
+	_chunkArgumentData: function(value, argIndex){
+		var plugin = dojo.flash.obj.get();
+		
+		// cut up the string into pieces, and push over each piece one
+		// at a time
+		var numSegments = Math.ceil(value.length / 1024);
+		for(var i = 0; i < numSegments; i++){
+			var startCut = i * 1024;
+			var endCut = i * 1024 + 1024;
+			if(i == (numSegments - 1)){
+				endCut = i * 1024 + value.length;
+			}
+			
+			var piece = value.substring(startCut, endCut);
+			
+			// encode each piece seperately, rather than the entire
+			// argument data, because ocassionally a special 
+			// character, such as an entity like &foobar;, will fall between
+			// piece boundaries, and we _don't_ want to encode that value if
+			// it falls between boundaries, or else we will end up with incorrect
+			// data when we patch the pieces back together on the other side
+			piece = this._encodeData(piece);
+			
+			// directly use the underlying CallFunction method used by
+			// ExternalInterface, which is vastly faster for large strings
+			// and lets us bypass some Flash serialization bugs
+			plugin.CallFunction('<invoke name="chunkArgumentData" '
+														+ 'returntype="javascript">'
+														+ '<arguments>'
+														+ '<string>' + piece + '</string>'
+														+ '<number>' + argIndex + '</number>'
+														+ '</arguments>'
+														+ '</invoke>');
+		}
+	},
+	
+	/** 
+			Gets our method return data in chunks for better performance.
+			Flash 8 communication.
+	*/
+	_chunkReturnData: function(){
+		var plugin = dojo.flash.obj.get();
+		
+		var numSegments = plugin.getReturnLength();
+		var resultsArray = new Array();
+		for(var i = 0; i < numSegments; i++){
+			// directly use the underlying CallFunction method used by
+			// ExternalInterface, which is vastly faster for large strings
+			var piece = 
+					plugin.CallFunction('<invoke name="chunkReturnData" '
+															+ 'returntype="javascript">'
+															+ '<arguments>'
+															+ '<number>' + i + '</number>'
+															+ '</arguments>'
+															+ '</invoke>');
+															
+			// remove any leading or trailing JavaScript delimiters, which surround
+			// our String when it comes back from Flash since we bypass Flash's
+			// deserialization routines by directly calling CallFunction on the
+			// plugin
+			if(piece == '""' || piece == "''"){
+				piece = "";
+			}else{
+				piece = piece.substring(1, piece.length-1);
+			}
+		
+			resultsArray.push(piece);
+		}
+		var results = resultsArray.join("");
+		
+		return results;
+	},
+	
+	/** 
+			Executes a Flash method; called from the JavaScript wrapper proxy we
+			create on dojo.flash.comm.
+			Flash 8 communication.
+	*/
+	_execFlash: function(methodName, methodArgs){
+		var plugin = dojo.flash.obj.get();
+				
+		// begin Flash method execution
+		plugin.startExec();
+		
+		// set the number of arguments
+		plugin.setNumberArguments(methodArgs.length);
+		
+		// chunk and send over each argument
+		for(var i = 0; i < methodArgs.length; i++){
+			this._chunkArgumentData(methodArgs[i], i);
+		}
+		
+		// execute the method
+		plugin.exec(methodName);
+														
+		// get the return result
+		var results = this._chunkReturnData();
+		
+		// decode the results
+		results = this._decodeData(results);
+		
+		// reset everything
+		plugin.endExec();
+		
+		return results;
+
+	}
+}
+
+/** 
+		Figures out the best way to automatically install the Flash plugin
+		for this browser and platform. Also determines if installation or
+		revving of the current plugin is needed on this platform.
+*/
+dojo.flash.Install = function(){
+}
+
+dojo.flash.Install.prototype = {
+	/** 
+			Determines if installation or revving of the current plugin is 
+			needed. 
+	*/
+	needed: function(){
+		// do we even have flash?
+		if(dojo.flash.info.capable == false){
+			return true;
+		}
+
+		// are we on the Mac? Safari needs Flash version 8 to do Flash 8
+		// communication, while Firefox/Mac needs Flash 8 to fix bugs it has
+		// with Flash 6 communication
+		if(dojo.render.os.mac == true && !dojo.flash.info.isVersionOrAbove(8, 0, 0)){
+			return true;
+		}
+
+		// other platforms need at least Flash 6 or above
+		if(!dojo.flash.info.isVersionOrAbove(6, 0, 0)){
+			return true;
+		}
+
+		// otherwise we don't need installation
+		return false;
+	},
+
+	/** Performs installation or revving of the Flash plugin. */
+	install: function(){
+		//dojo.debug("install");
+		// indicate that we are installing
+		dojo.flash.info.installing = true;
+		dojo.flash.installing();
+		
+		if(dojo.flash.info.capable == false){ // we have no Flash at all
+			//dojo.debug("Completely new install");
+			// write out a simple Flash object to force the browser to prompt
+			// the user to install things
+			var installObj = new dojo.flash.Embed(false);
+			installObj.write(8); // write out HTML for Flash 8 version+
+		}else if(dojo.flash.info.isVersionOrAbove(6, 0, 65)){ // Express Install
+			//dojo.debug("Express install");
+			var installObj = new dojo.flash.Embed(false);
+			installObj.write(8, true); // write out HTML for Flash 8 version+
+			installObj.setVisible(true);
+			installObj.center();
+		}else{ // older Flash install than version 6r65
+			alert("This content requires a more recent version of the Macromedia "
+						+" Flash Player.");
+			window.location.href = + dojo.flash.Embed.protocol() +
+						"://www.macromedia.com/go/getflashplayer";
+		}
+	},
+	
+	/** 
+			Called when the Express Install is either finished, failed, or was
+			rejected by the user.
+	*/
+	_onInstallStatus: function(msg){
+		if (msg == "Download.Complete"){
+			// Installation is complete.
+			dojo.flash._initialize();
+		}else if(msg == "Download.Cancelled"){
+			alert("This content requires a more recent version of the Macromedia "
+						+" Flash Player.");
+			window.location.href = dojo.flash.Embed.protocol() +
+						"://www.macromedia.com/go/getflashplayer";
+		}else if (msg == "Download.Failed"){
+			// The end user failed to download the installer due to a network failure
+			alert("There was an error downloading the Flash Player update. "
+						+ "Please try again later, or visit macromedia.com to download "
+						+ "the latest version of the Flash plugin.");
+		}	
+	}
+}
+
+// find out if Flash is installed
+dojo.flash.info = new dojo.flash.Info();
+
+// vim:ts=4:noet:tw=0:

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_adobesvg.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_adobesvg.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_adobesvg.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,571 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/*
+ * Adobe SVG Viewer host environment
+ */
+if(typeof window == 'undefined'){
+	dojo.raise("attempt to use adobe svg hostenv when no window object");
+}
+
+with(dojo.render){
+	name = navigator.appName;
+	ver = parseFloat(navigator.appVersion, 10);
+	switch(navigator.platform){
+		case "MacOS":
+			os.osx =  true;
+			break;
+		case "Linux":
+			os.linux =  true;
+			break;
+		case "Windows":
+			os.win =  true;
+			break;
+		default:
+			os.linux = true;
+			break;
+	};
+	svg.capable = true;
+	svg.support.builtin = true;
+	svg.adobe = true;
+};
+
+// browserEval("alert(window.location);");
+
+dojo.hostenv.println = function(s){
+	try{
+    // FIXME: this may not work with adobe's viewer, as we may first need a 
+		// reference to the svgDocument
+		// FIXME: need a way to determine where to position the text for this
+    var ti = document.createElement("text");
+    ti.setAttribute("x","50");
+		var yPos = 25 + 15*document.getElementsByTagName("text").length;
+    ti.setAttribute("y",yPos);
+		var tn = document.createTextNode(s);
+		ti.appendChild(tn);
+		document.documentElement.appendChild(ti);
+	}catch(e){
+
+	}
+}
+
+dojo.debug = function() {
+	if (!djConfig.isDebug) { return; }
+	var args = arguments;
+	if(typeof dojo.hostenv.println != 'function'){
+		dojo.raise("attempt to call dojo.debug when there is no dojo.hostenv println implementation (yet?)");
+	}
+	var isJUM = dj_global["jum"];
+	var s = isJUM ? "": "DEBUG: ";
+	for(var i=0;i<args.length;++i){ s += args[i]; }
+	if(isJUM){ // this seems to be the only way to get JUM to "play nice"
+		jum.debug(s);
+	}else{
+		dojo.hostenv.println(s);
+	}
+}
+
+dojo.hostenv.startPackage("dojo.hostenv");
+
+dojo.hostenv.name_ = 'adobesvg';
+
+dojo.hostenv.anonCtr = 0;
+dojo.hostenv.anon = {};
+
+dojo.hostenv.nameAnonFunc = function(anonFuncPtr, namespaceObj){
+	var ret = "_"+this.anonCtr++;
+	var nso = (namespaceObj || this.anon);
+	while(typeof nso[ret] != "undefined"){
+		ret = "_"+this.anonCtr++;
+	}
+	nso[ret] = anonFuncPtr;
+	return ret;
+}
+
+dojo.hostenv.modulesLoadedFired = false;
+dojo.hostenv.modulesLoadedListeners = [];
+dojo.hostenv.getTextStack = [];
+dojo.hostenv.loadUriStack = [];
+dojo.hostenv.loadedUris = [];
+
+
+dojo.hostenv.modulesLoaded = function(){
+	if(this.modulesLoadedFired){ return; }
+	if((this.loadUriStack.length==0)&&(this.getTextStack.length==0)){
+		if(this.inFlightCount > 0){ 
+			dojo.debug("couldn't initialize, there are files still in flight");
+			return;
+		}
+		this.modulesLoadedFired = true;
+		var mll = this.modulesLoadedListeners;
+		for(var x=0; x<mll.length; x++){
+			mll[x]();
+		}
+	}
+}
+
+dojo.hostenv.getNewAnonFunc = function(){
+	var ret = "_"+this.anonCtr++;
+	while(typeof this.anon[ret] != "undefined"){
+		ret = "_"+this.anonCtr++;
+	}
+	// this.anon[ret] = function(){};
+	eval("dojo.nostenv.anon."+ret+" = function(){};");
+	return [ret, this.anon[ret]];
+}
+
+dojo.hostenv.displayStack = function(){
+	var oa = [];
+	var stack = this.loadUriStack;
+	for(var x=0; x<stack.length; x++){
+		oa.unshift([stack[x][0], (typeof stack[x][2])]);
+	}
+	dojo.debug("<pre>"+oa.join("\n")+"</pre>");
+}
+
+dojo.hostenv.unwindUriStack = function(){
+	var stack = this.loadUriStack;
+	for(var x in dojo.hostenv.loadedUris){
+		for(var y=stack.length-1; y>=0; y--){
+			if(stack[y][0]==x){
+				stack.splice(y, 1);
+			}
+		}
+	}
+	var next = stack.pop();
+	if((!next)&&(stack.length==0)){ 
+		return;
+	}
+	for(var x=0; x<stack.length; x++){
+		if((stack[x][0]==next[0])&&(stack[x][2])){
+			next[2] == stack[x][2]
+		}
+	}
+	var last = next;
+	while(dojo.hostenv.loadedUris[next[0]]){
+		last = next;
+		next = stack.pop();
+	}
+	while(typeof next[2] == "string"){ // unwind as far as we can
+		try{
+			// dojo.debug("<pre><![CDATA["+next[2]+"]]></pre>");
+			dj_eval(next[2]);
+			next[1](true);
+		}catch(e){
+			dojo.debug("we got an error when loading "+next[0]);
+			dojo.debug("error: "+e);
+			// for(var x in e){ alert(x+" "+e[x]); }
+		}
+		dojo.hostenv.loadedUris[next[0]] = true;
+		dojo.hostenv.loadedUris.push(next[0]);
+		last = next;
+		next = stack.pop();
+		if((!next)&&(stack.length==0)){ break; }
+		while(dojo.hostenv.loadedUris[next[0]]){
+			last = next;
+			next = stack.pop();
+		}
+	}
+	if(next){
+		stack.push(next);
+		dojo.debug("### CHOKED ON: "+next[0]);
+	}
+}
+
+/**
+ * Reads the contents of the URI, and evaluates the contents.
+ * Returns true if it succeeded. Returns false if the URI reading failed. Throws if the evaluation throws.
+ * The result of the eval is not available to the caller.
+ */
+dojo.hostenv.loadUri = function(uri, cb){
+	if(dojo.hostenv.loadedUris[uri]){
+		return;
+	}
+	var stack = this.loadUriStack;
+	stack.push([uri, cb, null]);
+	var tcb = function(contents){
+		// gratuitous hack for Adobe SVG 3
+		if(contents.content){
+			contents = contents.content;
+		}
+
+		// stack management
+		var next = stack.pop();
+		if((!next)&&(stack.length==0)){ 
+			dojo.hostenv.modulesLoaded();
+			return;
+		}
+		if(typeof contents == "string"){
+			stack.push(next);
+			for(var x=0; x<stack.length; x++){
+				if(stack[x][0]==uri){
+					stack[x][2] = contents;
+				}
+			}
+			next = stack.pop();
+		}
+		if(dojo.hostenv.loadedUris[next[0]]){ 
+			// dojo.debug("WE ALREADY HAD: "+next[0]);
+			dojo.hostenv.unwindUriStack();
+			return;
+		}
+		// push back onto stack
+		stack.push(next);
+		if(next[0]!=uri){
+			//  and then unwind as far as we can
+			if(typeof next[2] == "string"){
+				dojo.hostenv.unwindUriStack();
+			}
+
+		}else{
+			if(!contents){ 
+				next[1](false);
+			}else{
+				var deps = dojo.hostenv.getDepsForEval(next[2]);
+				if(deps.length>0){
+					eval(deps.join(";"));
+				}else{
+					dojo.hostenv.unwindUriStack();
+				}
+			}
+		}
+	}
+	this.getText(uri, tcb, true);
+}
+
+/**
+ * Reads the contents of the URI, and evaluates the contents.
+ * Returns true if it succeeded. Returns false if the URI reading failed. Throws if the evaluation throws.
+ * The result of the eval is not available to the caller.
+ */
+dojo.hostenv.loadUri = function(uri, cb){
+	if(dojo.hostenv.loadedUris[uri]){
+		return;
+	}
+	var stack = this.loadUriStack;
+	stack.push([uri, cb, null]);
+	var tcb = function(contents){
+		// gratuitous hack for Adobe SVG 3
+		if(contents.content){
+			contents = contents.content;
+		}
+
+		// stack management
+		var next = stack.pop();
+		if((!next)&&(stack.length==0)){ 
+			dojo.hostenv.modulesLoaded();
+			return;
+		}
+		if(typeof contents == "string"){
+			stack.push(next);
+			for(var x=0; x<stack.length; x++){
+				if(stack[x][0]==uri){
+					stack[x][2] = contents;
+				}
+			}
+			next = stack.pop();
+		}
+		if(dojo.hostenv.loadedUris[next[0]]){ 
+			// dojo.debug("WE ALREADY HAD: "+next[0]);
+			dojo.hostenv.unwindUriStack();
+			return;
+		}
+		// push back onto stack
+		stack.push(next);
+		if(next[0]!=uri){
+			//  and then unwind as far as we can
+			if(typeof next[2] == "string"){
+				dojo.hostenv.unwindUriStack();
+			}
+
+		}else{
+			if(!contents){ 
+				next[1](false);
+			}else{
+				var deps = dojo.hostenv.getDepsForEval(next[2]);
+				if(deps.length>0){
+					eval(deps.join(";"));
+				}else{
+					dojo.hostenv.unwindUriStack();
+				}
+			}
+		}
+	}
+	this.getText(uri, tcb, true);
+}
+
+/**
+* loadModule("A.B") first checks to see if symbol A.B is defined. 
+* If it is, it is simply returned (nothing to do).
+* If it is not defined, it will look for "A/B.js" in the script root directory, followed
+* by "A.js".
+* It throws if it cannot find a file to load, or if the symbol A.B is not defined after loading.
+* It returns the object A.B.
+*
+* This does nothing about importing symbols into the current package.
+* It is presumed that the caller will take care of that. For example, to import
+* all symbols:
+*
+*    with (dojo.hostenv.loadModule("A.B")) {
+*       ...
+*    }
+*
+* And to import just the leaf symbol:
+*
+*    var B = dojo.hostenv.loadModule("A.B");
+*    ...
+*
+* dj_load is an alias for dojo.hostenv.loadModule
+*/
+dojo.hostenv.loadModule = function(modulename, exact_only, omit_module_check){
+	// alert("dojo.hostenv.loadModule('"+modulename+"');");
+	var module = this.findModule(modulename, 0);
+	if(module){
+		return module;
+	}
+
+	// dojo.debug("dojo.hostenv.loadModule('"+modulename+"');");
+
+	// protect against infinite recursion from mutual dependencies
+	if (typeof this.loading_modules_[modulename] !== 'undefined'){
+		// NOTE: this should never throw an exception!! "recursive" includes
+		// are normal in the course of app and module building, so blow out of
+		// it gracefully, but log it in debug mode
+
+		// dojo.raise("recursive attempt to load module '" + modulename + "'");
+		dojo.debug("recursive attempt to load module '" + modulename + "'");
+	}else{
+		this.addedToLoadingCount.push(modulename);
+	}
+	this.loading_modules_[modulename] = 1;
+
+
+	// convert periods to slashes
+	var relpath = modulename.replace(/\./g, '/') + '.js';
+
+	var syms = modulename.split(".");
+	var nsyms = modulename.split(".");
+	if(syms[0]=="dojo"){ // FIXME: need a smarter way to do this!
+		syms[0] = "src"; 
+	}
+	var last = syms.pop();
+	syms.push(last);
+	// figure out if we're looking for a full package, if so, we want to do
+	// things slightly diffrently
+	var _this = this;
+	var pfn = this.pkgFileName;
+	if(last=="*"){
+		modulename = (nsyms.slice(0, -1)).join('.');
+
+		var module = this.findModule(modulename, 0);
+		// dojo.debug("found: "+modulename+"="+module);
+		if(module){
+			_this.removedFromLoadingCount.push(modulename);
+			return module;
+		}
+
+		var nextTry = function(lastStatus){
+			if(lastStatus){ 
+				module = _this.findModule(modulename, false); // pass in false so we can give better error
+				if((!module)&&(syms[syms.length-1]!=pfn)){
+					dojo.raise("Module symbol '" + modulename + "' is not defined after loading '" + relpath + "'"); 
+				}
+				if(module){
+					_this.removedFromLoadingCount.push(modulename);
+					dojo.hostenv.modulesLoaded();
+					return;
+				}
+			}
+			syms.pop();
+			syms.push(pfn);
+			// dojo.debug("syms: "+syms);
+			relpath = syms.join("/") + '.js';
+			if(relpath.charAt(0)=="/"){
+				relpath = relpath.slice(1);
+			}
+			// dojo.debug("relpath: "+relpath);
+			_this.loadPath(relpath, ((!omit_module_check) ? modulename : null), nextTry);
+		}
+
+		nextTry();
+	}else{
+		relpath = syms.join("/") + '.js';
+		modulename = nsyms.join('.');
+
+		var nextTry = function(lastStatus){
+			// dojo.debug("lastStatus: "+lastStatus);
+			if(lastStatus){ 
+				// dojo.debug("inital relpath: "+relpath);
+				module = _this.findModule(modulename, false); // pass in false so we can give better error
+				// if(!module){
+				if((!module)&&(syms[syms.length-1]!=pfn)){
+					dojo.raise("Module symbol '" + modulename + "' is not defined after loading '" + relpath + "'"); 
+				}
+				if(module){
+					_this.removedFromLoadingCount.push(modulename);
+					dojo.hostenv.modulesLoaded();
+					return;
+				}
+			}
+			var setPKG = (syms[syms.length-1]==pfn) ? false : true;
+			syms.pop();
+			if(setPKG){
+				syms.push(pfn);
+			}
+			relpath = syms.join("/") + '.js';
+			if(relpath.charAt(0)=="/"){
+				relpath = relpath.slice(1);
+			}
+			// dojo.debug("relpath: "+relpath);
+			_this.loadPath(relpath, ((!omit_module_check) ? modulename : null), nextTry);
+		}
+
+		this.loadPath(relpath, ((!omit_module_check) ? modulename : null), nextTry);
+	}
+	return;
+}
+
+/**
+ * Read the contents of the specified uri and return those contents.
+ *
+ * FIXME: Make sure this is consistent with other implementations of getText
+ * @param uri A relative or absolute uri. If absolute, it still must be in the same "domain" as we are.
+ * @param async_cb If not specified, returns false as synchronous is not
+ * supported. If specified, load asynchronously, and use async_cb as the handler which receives the result of the request.
+ * @param fail_ok Default false. If fail_ok and !async_cb and loading fails, return null instead of throwing.
+ */ 
+dojo.hostenv.async_cb = null;
+
+dojo.hostenv.unWindGetTextStack = function(){
+	if(dojo.hostenv.inFlightCount>0){
+		setTimeout("dojo.hostenv.unWindGetTextStack()", 100);
+		return;
+	}
+	// we serialize because this environment is too messed up
+	// to know how to do anything else
+	dojo.hostenv.inFlightCount++;
+	var next = dojo.hostenv.getTextStack.pop();
+	if((!next)&&(dojo.hostenv.getTextStack.length==0)){ 
+		dojo.hostenv.inFlightCount--;
+		dojo.hostenv.async_cb = function(){};
+		return;
+	}
+	dojo.hostenv.async_cb = next[1];
+	// http = window.getURL(uri, dojo.hostenv.anon[cbn]);
+	window.getURL(next[0], function(result){ 
+		dojo.hostenv.inFlightCount--;
+		dojo.hostenv.async_cb(result.content);
+		dojo.hostenv.unWindGetTextStack();
+	});
+}
+
+dojo.hostenv.getText = function(uri, async_cb, fail_ok){
+	// dojo.debug("Calling getText()");
+	try{
+		if(async_cb){
+			dojo.hostenv.getTextStack.push([uri, async_cb, fail_ok]);
+			dojo.hostenv.unWindGetTextStack();
+		}else{
+			return dojo.raise("No synchronous XMLHTTP implementation available, for uri " + uri);
+		}
+	}catch(e){
+		return dojo.raise("No XMLHTTP implementation available, for uri " + uri);
+	}
+}
+
+
+/**
+ * Makes an async post to the specified uri.
+ *
+ * FIXME: Not sure that we need this, but adding for completeness.
+ * More details about the implementation of this are available at 
+ * http://wiki.svg.org/index.php/PostUrl
+ * @param uri A relative or absolute uri. If absolute, it still must be in the same "domain" as we are.
+ * @param async_cb If not specified, returns false as synchronous is not
+ * supported. If specified, load asynchronously, and use async_cb as the progress handler which takes the xmlhttp object as its argument. If async_cb, this function returns null.
+ * @param text Data to post
+ * @param fail_ok Default false. If fail_ok and !async_cb and loading fails, return null instead of throwing.
+ * @param mime_type optional MIME type of the posted data (such as "text/plain")
+ * @param encoding optional encoding for data. null, 'gzip' and 'deflate' are possible values. If browser does not support binary post this parameter is ignored.
+ */ 
+dojo.hostenv.postText = function(uri, async_cb, text, fail_ok, mime_type, encoding){
+	var http = null;
+	
+	var async_callback = function(httpResponse){
+		if (!httpResponse.success) {
+			dojo.raise("Request for uri '" + uri + "' resulted in " + httpResponse.status);
+		}
+		
+		if(!httpResponse.content) {
+			if (!fail_ok) dojo.raise("Request for uri '" + uri + "' resulted in no content");
+			return null;
+		}
+		// FIXME: wtf, I'm losing a reference to async_cb
+		async_cb(httpResponse.content);
+	}
+	
+	try {
+		if(async_cb) {
+			http = window.postURL(uri, text, async_callback, mimeType, encoding);
+		} else {
+		return dojo.raise("No synchronous XMLHTTP post implementation available, for uri " + uri);
+		}
+	} catch(e) {
+		return dojo.raise("No XMLHTTP post implementation available, for uri " + uri);
+	}
+}
+
+/*
+ * It turns out that if we check *right now*, as this script file is being loaded,
+ * then the last script element in the window DOM is ourselves.
+ * That is because any subsequent script elements haven't shown up in the document
+ * object yet.
+ */
+function dj_last_script_src() {
+	var scripts = window.document.getElementsByTagName('script');
+	if(scripts.length < 1){ 
+		dojo.raise("No script elements in window.document, so can't figure out my script src"); 
+	}
+	var li = scripts.length-1;
+	var xlinkNS = "http://www.w3.org/1999/xlink";
+	var src = null;
+	var script = null;
+	while(!src){
+		script = scripts.item(li);
+		src = script.getAttributeNS(xlinkNS,"href");
+		li--;
+		if(li<0){ break; }
+		// break;
+	}
+	if(!src){
+		dojo.raise("Last script element (out of " + scripts.length + ") has no src");
+	}
+	return src;
+}
+
+if(!dojo.hostenv["library_script_uri_"]){
+	dojo.hostenv.library_script_uri_ = dj_last_script_src();
+}
+
+// dojo.hostenv.loadUri = function(uri){
+	/* FIXME: adding a script element doesn't seem to be synchronous, and so
+	 * checking for namespace or object existance after loadUri using this
+	 * method will error out. Need to figure out some other way of handling
+	 * this!
+	 */
+	/*
+	var se = document.createElement("script");
+	se.src = uri;
+	var head = document.getElementsByTagName("head")[0];
+	head.appendChild(se);
+	// document.write("<script type='text/javascript' src='"+uri+"' />");
+	return 1;
+}
+*/

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_browser.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_browser.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_browser.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,528 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+if (typeof window != 'undefined') {
+
+// attempt to figure out the path to dojo if it isn't set in the config
+(function() {
+	// before we get any further with the config options, try to pick them out
+	// of the URL. Most of this code is from NW
+	if(djConfig.allowQueryConfig){
+		var baseUrl = document.location.toString(); // FIXME: use location.query instead?
+		var params = baseUrl.split("?", 2);
+		if(params.length > 1){
+			var paramStr = params[1];
+			var pairs = paramStr.split("&");
+			for(var x in pairs){
+				var sp = pairs[x].split("=");
+				// FIXME: is this eval dangerous?
+				if((sp[0].length > 9)&&(sp[0].substr(0, 9) == "djConfig.")){
+					var opt = sp[0].substr(9);
+					try{
+						djConfig[opt]=eval(sp[1]);
+					}catch(e){
+						djConfig[opt]=sp[1];
+					}
+				}
+			}
+		}
+	}
+
+	if(((djConfig["baseScriptUri"] == "")||(djConfig["baseRelativePath"] == "")) &&(document && document.getElementsByTagName)){
+		var scripts = document.getElementsByTagName("script");
+		var rePkg = /(__package__|dojo|bootstrap1)\.js([\?\.]|$)/i;
+		for(var i = 0; i < scripts.length; i++) {
+			var src = scripts[i].getAttribute("src");
+			if(!src) { continue; }
+			var m = src.match(rePkg);
+			if(m) {
+				var root = src.substring(0, m.index);
+				if(src.indexOf("bootstrap1") > -1) { root += "../"; }
+				if(!this["djConfig"]) { djConfig = {}; }
+				if(djConfig["baseScriptUri"] == "") { djConfig["baseScriptUri"] = root; }
+				if(djConfig["baseRelativePath"] == "") { djConfig["baseRelativePath"] = root; }
+				break;
+			}
+		}
+	}
+
+	// fill in the rendering support information in dojo.render.*
+	var dr = dojo.render;
+	var drh = dojo.render.html;
+	var drs = dojo.render.svg;
+	var dua = (drh.UA = navigator.userAgent);
+	var dav = (drh.AV = navigator.appVersion);
+	var t = true;
+	var f = false;
+	drh.capable = t;
+	drh.support.builtin = t;
+
+	dr.ver = parseFloat(drh.AV);
+	dr.os.mac = dav.indexOf("Macintosh") >= 0;
+	dr.os.win = dav.indexOf("Windows") >= 0;
+	// could also be Solaris or something, but it's the same browser
+	dr.os.linux = dav.indexOf("X11") >= 0;
+
+	drh.opera = dua.indexOf("Opera") >= 0;
+	drh.khtml = (dav.indexOf("Konqueror") >= 0)||(dav.indexOf("Safari") >= 0);
+	drh.safari = dav.indexOf("Safari") >= 0;
+	var geckoPos = dua.indexOf("Gecko");
+	drh.mozilla = drh.moz = (geckoPos >= 0)&&(!drh.khtml);
+	if (drh.mozilla) {
+		// gecko version is YYYYMMDD
+		drh.geckoVersion = dua.substring(geckoPos + 6, geckoPos + 14);
+	}
+	drh.ie = (document.all)&&(!drh.opera);
+	drh.ie50 = drh.ie && dav.indexOf("MSIE 5.0")>=0;
+	drh.ie55 = drh.ie && dav.indexOf("MSIE 5.5")>=0;
+	drh.ie60 = drh.ie && dav.indexOf("MSIE 6.0")>=0;
+	drh.ie70 = drh.ie && dav.indexOf("MSIE 7.0")>=0;
+
+	var cm = document["compatMode"];
+	drh.quirks = (cm == "BackCompat")||(cm == "QuirksMode")||drh.ie55||drh.ie50;
+
+	// TODO: is the HTML LANG attribute relevant?
+	dojo.locale = dojo.locale || (drh.ie ? navigator.userLanguage : navigator.language).toLowerCase();
+
+	dr.vml.capable=drh.ie;
+	drs.capable = f;
+	drs.support.plugin = f;
+	drs.support.builtin = f;
+	var tdoc = window["document"];
+	var tdi = tdoc["implementation"];
+
+	if((tdi)&&(tdi["hasFeature"])&&(tdi.hasFeature("org.w3c.dom.svg", "1.0"))){
+		drs.capable = t;
+		drs.support.builtin = t;
+		drs.support.plugin = f;
+	}
+	// webkits after 420 support SVG natively. The test string is "AppleWebKit/420+"
+	if(drh.safari){
+		var tmp = dua.split("AppleWebKit/")[1];
+		var ver = parseFloat(tmp.split(" ")[0]);
+		if(ver >= 420){
+			drs.capable = t;
+			drs.support.builtin = t;
+			drs.support.plugin = f;
+		}
+	}
+})();
+
+dojo.hostenv.startPackage("dojo.hostenv");
+
+dojo.render.name = dojo.hostenv.name_ = 'browser';
+dojo.hostenv.searchIds = [];
+
+// These are in order of decreasing likelihood; this will change in time.
+dojo.hostenv._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
+
+dojo.hostenv.getXmlhttpObject = function(){
+    var http = null;
+	var last_e = null;
+	try{ http = new XMLHttpRequest(); }catch(e){}
+    if(!http){
+		for(var i=0; i<3; ++i){
+			var progid = dojo.hostenv._XMLHTTP_PROGIDS[i];
+			try{
+				http = new ActiveXObject(progid);
+			}catch(e){
+				last_e = e;
+			}
+
+			if(http){
+				dojo.hostenv._XMLHTTP_PROGIDS = [progid];  // so faster next time
+				break;
+			}
+		}
+
+		/*if(http && !http.toString) {
+			http.toString = function() { "[object XMLHttpRequest]"; }
+		}*/
+	}
+
+	if(!http){
+		return dojo.raise("XMLHTTP not available", last_e);
+	}
+
+	return http;
+}
+
+/**
+ * Read the contents of the specified uri and return those contents.
+ *
+ * @param uri A relative or absolute uri. If absolute, it still must be in the
+ * same "domain" as we are.
+ *
+ * @param async_cb If not specified, load synchronously. If specified, load
+ * asynchronously, and use async_cb as the progress handler which takes the
+ * xmlhttp object as its argument. If async_cb, this function returns null.
+ *
+ * @param fail_ok Default false. If fail_ok and !async_cb and loading fails,
+ * return null instead of throwing.
+ */
+dojo.hostenv._blockAsync = false;
+dojo.hostenv.getText = function(uri, async_cb, fail_ok){
+	// need to block async callbacks from snatching this thread as the result
+	// of an async callback might call another sync XHR, this hangs khtml forever
+	// hostenv._blockAsync must also be checked in BrowserIO's watchInFlight()
+	// NOTE: must be declared before scope switches ie. this.getXmlhttpObject()
+	if(!async_cb){ this._blockAsync = true; }
+
+	var http = this.getXmlhttpObject();
+
+	function isDocumentOk(http){
+		var stat = http["status"];
+		// allow a 304 use cache, needed in konq (is this compliant with the http spec?)
+		return Boolean((!stat)||((200 <= stat)&&(300 > stat))||(stat==304));
+	}
+
+	if(async_cb){
+		var _this = this, timer = null, gbl = dojo.global();
+		var xhr = dojo.evalObjPath("dojo.io.XMLHTTPTransport");
+		http.onreadystatechange = function(){
+			if(timer){ gbl.clearTimeout(timer); timer = null; }
+			if(_this._blockAsync || (xhr && xhr._blockAsync)){
+				timer = gbl.setTimeout(function () { http.onreadystatechange.apply(this); }, 10);
+			}else{
+				if(4==http.readyState){
+					if(isDocumentOk(http)){
+						// dojo.debug("LOADED URI: "+uri);
+						async_cb(http.responseText);
+					}
+				}
+			}
+		}
+	}
+
+	http.open('GET', uri, async_cb ? true : false);
+	try{
+		http.send(null);
+		if(async_cb){
+			return null;
+		}
+		if(!isDocumentOk(http)){
+			var err = Error("Unable to load "+uri+" status:"+ http.status);
+			err.status = http.status;
+			err.responseText = http.responseText;
+			throw err;
+		}
+	}catch(e){
+		this._blockAsync = false;
+		if((fail_ok)&&(!async_cb)){
+			return null;
+		}else{
+			throw e;
+		}
+	}
+
+	this._blockAsync = false;
+	return http.responseText;
+}
+
+/*
+ * It turns out that if we check *right now*, as this script file is being loaded,
+ * then the last script element in the window DOM is ourselves.
+ * That is because any subsequent script elements haven't shown up in the document
+ * object yet.
+ */
+ /*
+function dj_last_script_src() {
+    var scripts = window.document.getElementsByTagName('script');
+    if(scripts.length < 1){
+		dojo.raise("No script elements in window.document, so can't figure out my script src");
+	}
+    var script = scripts[scripts.length - 1];
+    var src = script.src;
+    if(!src){
+		dojo.raise("Last script element (out of " + scripts.length + ") has no src");
+	}
+    return src;
+}
+
+if(!dojo.hostenv["library_script_uri_"]){
+	dojo.hostenv.library_script_uri_ = dj_last_script_src();
+}
+*/
+
+dojo.hostenv.defaultDebugContainerId = 'dojoDebug';
+dojo.hostenv._println_buffer = [];
+dojo.hostenv._println_safe = false;
+dojo.hostenv.println = function (line){
+	if(!dojo.hostenv._println_safe){
+		dojo.hostenv._println_buffer.push(line);
+	}else{
+		try {
+			var console = document.getElementById(djConfig.debugContainerId ?
+				djConfig.debugContainerId : dojo.hostenv.defaultDebugContainerId);
+			if(!console) { console = dojo.body(); }
+
+			var div = document.createElement("div");
+			div.appendChild(document.createTextNode(line));
+			console.appendChild(div);
+		} catch (e) {
+			try{
+				// safari needs the output wrapped in an element for some reason
+				document.write("<div>" + line + "</div>");
+			}catch(e2){
+				window.status = line;
+			}
+		}
+	}
+}
+
+dojo.addOnLoad(function(){
+	dojo.hostenv._println_safe = true;
+	while(dojo.hostenv._println_buffer.length > 0){
+		dojo.hostenv.println(dojo.hostenv._println_buffer.shift());
+	}
+});
+
+function dj_addNodeEvtHdlr(node, evtName, fp, capture){
+	var oldHandler = node["on"+evtName] || function(){};
+	node["on"+evtName] = function(){
+		fp.apply(node, arguments);
+		oldHandler.apply(node, arguments);
+	}
+	return true;
+}
+
+//	BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
+function dj_load_init(e){
+	// allow multiple calls, only first one will take effect
+	// A bug in khtml calls events callbacks for document for event which isnt supported
+	// for example a created contextmenu event calls DOMContentLoaded, workaround
+	var type = (e && e.type) ? e.type.toLowerCase() : "load";
+	if(arguments.callee.initialized || (type!="domcontentloaded" && type!="load")){ return; }
+	arguments.callee.initialized = true;
+	if(typeof(_timer) != 'undefined'){
+		clearInterval(_timer);
+		delete _timer;
+	}
+
+	var initFunc = function(){
+		//perform initialization
+		if(dojo.render.html.ie){
+			dojo.hostenv.makeWidgets();
+		}
+	};
+
+	if(dojo.hostenv.inFlightCount == 0){
+		initFunc();
+		dojo.hostenv.modulesLoaded();
+	}else{
+		dojo.addOnLoad(initFunc);
+	}
+}
+
+//	START DOMContentLoaded
+// Mozilla and Opera 9 expose the event we could use
+if(document.addEventListener){
+	if(dojo.render.html.opera || (dojo.render.html.moz && !djConfig.delayMozLoadingFix)){
+		document.addEventListener("DOMContentLoaded", dj_load_init, null);
+	}
+
+	//	mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.
+	//  also used for Mozilla because of trac #1640
+	window.addEventListener("load", dj_load_init, null);
+}
+
+// 	for Internet Explorer. readyState will not be achieved on init call, but dojo doesn't need it
+//	however, we'll include it because we don't know if there are other functions added that might.
+//	Note that this has changed because the build process strips all comments--including conditional
+//		ones.
+if(dojo.render.html.ie && dojo.render.os.win){
+	document.attachEvent("onreadystatechange", function(e){
+		if(document.readyState == "complete"){
+			dj_load_init();
+		}
+	});
+}
+
+if (/(WebKit|khtml)/i.test(navigator.userAgent)) { // sniff
+    var _timer = setInterval(function() {
+        if (/loaded|complete/.test(document.readyState)) {
+            dj_load_init(); // call the onload handler
+        }
+    }, 10);
+}
+//	END DOMContentLoaded
+
+// IE WebControl hosted in an application can fire "beforeunload" and "unload"
+// events when control visibility changes, causing Dojo to unload too soon. The
+// following code fixes the problem
+// Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;199155
+if(dojo.render.html.ie){
+	dj_addNodeEvtHdlr(window, "beforeunload", function(){
+		dojo.hostenv._unloading = true;
+		window.setTimeout(function() {
+			dojo.hostenv._unloading = false;
+		}, 0);
+	});
+}
+
+dj_addNodeEvtHdlr(window, "unload", function(){
+	dojo.hostenv.unloaded();
+	if((!dojo.render.html.ie)||(dojo.render.html.ie && dojo.hostenv._unloading)){
+		dojo.hostenv.unloaded();
+	}
+});
+
+dojo.hostenv.makeWidgets = function(){
+	// you can put searchIds in djConfig and dojo.hostenv at the moment
+	// we should probably eventually move to one or the other
+	var sids = [];
+	if(djConfig.searchIds && djConfig.searchIds.length > 0) {
+		sids = sids.concat(djConfig.searchIds);
+	}
+	if(dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) {
+		sids = sids.concat(dojo.hostenv.searchIds);
+	}
+
+	if((djConfig.parseWidgets)||(sids.length > 0)){
+		if(dojo.evalObjPath("dojo.widget.Parse")){
+			// we must do this on a delay to avoid:
+			//	http://www.shaftek.org/blog/archives/000212.html
+			// (IE bug)
+				var parser = new dojo.xml.Parse();
+				if(sids.length > 0){
+					for(var x=0; x<sids.length; x++){
+						var tmpNode = document.getElementById(sids[x]);
+						if(!tmpNode){ continue; }
+						var frag = parser.parseElement(tmpNode, null, true);
+						dojo.widget.getParser().createComponents(frag);
+					}
+				}else if(djConfig.parseWidgets){
+					var frag  = parser.parseElement(dojo.body(), null, true);
+					dojo.widget.getParser().createComponents(frag);
+				}
+		}
+	}
+}
+
+dojo.addOnLoad(function(){
+	if(!dojo.render.html.ie) {
+		dojo.hostenv.makeWidgets();
+	}
+});
+
+try {
+	if (dojo.render.html.ie) {
+		document.namespaces.add("v","urn:schemas-microsoft-com:vml");
+		document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
+	}
+} catch (e) { }
+
+// stub, over-ridden by debugging code. This will at least keep us from
+// breaking when it's not included
+dojo.hostenv.writeIncludes = function(){}
+
+//TODOC:  HOW TO DOC THIS?
+// @global: dj_currentDocument
+// summary:
+//		Current document object. 'dj_currentDocument' can be modified for temporary context shifting.
+// description:
+//    dojo.doc() returns dojo.currentDocument.
+//		Refer to dojo.doc() rather than referring to 'window.document' to ensure your
+//		code runs correctly in managed contexts.
+if(!dj_undef("document", this)){
+	dj_currentDocument = this.document;
+}
+
+dojo.doc = function(){
+	// summary:
+	//		return the document object associated with the dojo.global()
+	return dj_currentDocument;
+}
+
+dojo.body = function(){
+	// summary:
+	//		return the body object associated with dojo.doc()
+	// Note: document.body is not defined for a strict xhtml document
+	return dojo.doc().body || dojo.doc().getElementsByTagName("body")[0];
+}
+
+dojo.byId = function(id, doc){
+	if((id)&&((typeof id == "string")||(id instanceof String))){
+		if (!doc) { doc = dj_currentDocument; }
+		var ele = doc.getElementById(id);
+		// workaround bug in IE and Opera 8.2 where getElementById returns wrong element
+		if (ele && (ele.id != id) && doc.all) {
+			ele = null;
+			// get all matching elements with this id
+			eles = doc.all[id];
+			if (eles) {
+				// if more than 1, choose first with the correct id
+				if (eles.length) {
+					for (var i=0; i < eles.length; i++) {
+						if (eles[i].id == id) {
+							ele = eles[i];
+							break;
+						}
+					}
+				// return 1 and only element
+				} else { ele = eles; }
+			}
+		}
+		return ele;
+	}
+	return id; // assume it's a node
+}
+
+dojo.setContext = function(/*Object*/globalObject, /*Object*/ globalDocument){
+	dj_currentContext = globalObject;
+	dj_currentDocument = globalDocument;
+};
+
+dojo._fireCallback = function(callback, context, cbArguments) {
+	if((context)&&((typeof callback == "string")||(callback instanceof String))){
+		callback=context[callback];
+	}
+	return (context ? callback.apply(context, cbArguments || [ ]) : callback());
+}
+
+dojo.withGlobal = function(/*Object*/globalObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments){
+	// summary:
+	//		Call callback with globalObject as dojo.global() and globalObject.document
+	//		as dojo.doc(). If provided, globalObject will be executed in the context of
+	//		object thisObject
+	// description:
+	//		When callback() returns or throws an error, the dojo.global() and dojo.doc() will
+	//		be restored to its previous state.
+	var rval;
+	var oldGlob = dj_currentContext;
+	var oldDoc = dj_currentDocument;
+	try{
+		dojo.setContext(globalObject, globalObject.document);
+		rval = dojo._fireCallback(callback, thisObject, cbArguments);
+	}finally{
+		dojo.setContext(oldGlob, oldDoc);
+	}
+	return rval;
+}
+
+dojo.withDoc = function (/*Object*/documentObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments) {
+	// summary:
+	//		Call callback with documentObject as dojo.doc(). If provided, callback will be executed
+	//		in the context of object thisObject
+	// description:
+	//		When callback() returns or throws an error, the dojo.doc() will
+	//		be restored to its previous state.
+	var rval;
+	var oldDoc = dj_currentDocument;
+	try{
+		dj_currentDocument = documentObject;
+		rval = dojo._fireCallback(callback, thisObject, cbArguments);
+	}finally{
+		dj_currentDocument = oldDoc;
+	}
+	return rval;
+}
+
+} //if (typeof window != 'undefined')

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_dashboard.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_dashboard.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_dashboard.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,197 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.render.name = dojo.hostenv.name_ = "dashboard";
+
+dojo.hostenv.println = function(/*String*/ message){
+	// summary: Prints a message to the OS X console
+	return alert(message); // null
+}
+
+dojo.hostenv.getXmlhttpObject = function(/*Object*/ kwArgs){
+	// summary: Returns the appropriate transfer object for the call type
+	if(widget.system && kwArgs){
+		if((kwArgs.contentType && kwArgs.contentType.indexOf("text/") != 0) || (kwArgs.headers && kwArgs.headers["content-type"] && kwArgs.headers["content-type"].indexOf("text/") != 0)){
+			var curl = new dojo.hostenv.CurlRequest;
+			curl._save = true;
+			return curl;
+		}else if(kwArgs.method && kwArgs.method.toUpperCase() == "HEAD"){
+			return new dojo.hostenv.CurlRequest;
+		}else if(kwArgs.headers && kwArgs.header.referer){
+			return new dojo.hostenv.CurlRequest; 
+		}
+	}
+	return new XMLHttpRequest; // XMLHttpRequest
+}
+
+dojo.hostenv.CurlRequest = function(){
+	// summary: Emulates the XMLHttpRequest Object
+	this.onreadystatechange = null;
+	this.readyState = 0;
+	this.responseText = "";
+	this.responseXML = null;
+	this.status = 0;
+	this.statusText = "";
+	this._method = "";
+	this._url = "";
+	this._async = true;
+	this._referrer = "";
+	this._headers = [];
+	this._save = false;
+	this._responseHeader = "";
+	this._responseHeaders = {};
+	this._fileName = "";
+	this._username = "";
+	this._password = "";
+}
+
+dojo.hostenv.CurlRequest.prototype.open = function(/*String*/ method, /*URL*/ url, /*Boolean?*/ async, /*String?*/ username, /*String?*/ password){
+	this._method = method;
+	this._url = url;
+	if(async){
+		this._async = async;
+	}
+	if(username){
+		this._username = username;
+	}
+	if(password){
+		this._password = password;
+	}
+}
+
+dojo.hostenv.CurlRequest.prototype.setRequestHeader = function(/*String*/ label, /*String*/ value){
+	switch(label){
+		case "Referer":
+			this._referrer = value;
+			break;
+		case "content-type":
+			break;
+		default:
+			this._headers.push(label + "=" + value);
+			break;
+	}
+}
+
+dojo.hostenv.CurlRequest.prototype.getAllResponseHeaders = function(){
+	return this._responseHeader; // String
+}
+
+dojo.hostenv.CurlRequest.prototype.getResponseHeader = function(/*String*/ headerLabel){
+	return this._responseHeaders[headerLabel]; // String
+}
+
+// -sS = Show only errors in errorString
+// -i = Display headers with return
+// -e = Referrer URI
+// -H = Headers
+// -d = data to be sent (forces POST)
+// -G = forces GET
+// -o = Writes to file (in the cache directory)
+// -I = Only load headers
+// -u = user:password
+dojo.hostenv.CurlRequest.prototype.send = function(/*String*/ content){
+	this.readyState = 1;
+	if(this.onreadystatechange){
+		this.onreadystatechange.call(this);
+	}
+	var query = {sS: ""};
+	if(this._referrer){
+		query.e = this._referrer;
+	}
+	if(this._headers.length){
+		query.H = this._headers.join("&");
+	}
+	if(this._username){
+		if(this._password){
+			query.u = this._username + ":" + this._password;
+		}else{
+			query.u = this._username;
+		}
+	}
+	if(content){
+		query.d = this.content;
+		if(this._method != "POST"){
+			query.G = "";
+		}
+	}
+	if(this._method == "HEAD"){
+		query.I = "";
+	}else{
+		if(this._save){
+			query.I = ""; // Get the headers in the initial query
+		}else{
+			query.i = "";
+		}
+	}
+
+	var system = widget.system(dojo.hostenv.CurlRequest._formatCall(query, this._url), null);
+	this.readyState = 2;
+	if(this.onreadystatechange){
+		this.onreadystatechange.call(this);
+	}
+	if(system.errorString){
+		this.responseText = system.errorString;
+		this.status = 0;
+	}else{
+		if(this._save){
+			this._responseHeader = system.outputString;
+		}else{
+			var split = system.outputString.replace(/\r/g, "").split("\n\n", 2);
+			this._responseHeader = split[0];
+			this.responseText = split[1];
+		}
+		split = this._responseHeader.split("\n");
+		this.statusText = split.shift();
+		this.status = this.statusText.split(" ")[1];
+		for(var i = 0, header; header = split[i]; i++){
+			var header_split = header.split(": ", 2);
+			this._responseHeaders[header_split[0]] = header_split[1];
+		}
+		if(this._save){
+			widget.system("/bin/mkdir cache", null);
+			// First, make a file name
+			this._fileName = this._url.split("/").pop().replace(/\W/g, "");
+			// Then, get its extension
+			this._fileName += "." + this._responseHeaders["Content-Type"].replace(/[\r\n]/g, "").split("/").pop()
+			delete query.I;
+			query.o = "cache/" + this._fileName; // Tell it where to be saved.
+			system = widget.system(dojo.hostenv.CurlRequest._formatCall(query, this._url), null);
+			if(!system.errorString){
+				this.responseText = "cache/" + this._fileName;
+			}
+		}else if(this._method == "HEAD"){
+			this.responseText = this._responseHeader;
+		}
+	}
+
+	this.readyState = 4;
+	if(this.onreadystatechange){
+		this.onreadystatechange.call(this);
+	}
+}
+
+dojo.hostenv.CurlRequest._formatCall = function(query, url){
+	var call = ["/usr/bin/curl"];
+	for(var key in query){
+		if(query[key] != ""){
+			call.push("-" + key + " '" + query[key].replace(/'/g, "\'") + "'");
+		}else{
+			call.push("-" + key);
+		}
+	}
+	call.push("'" + url.replace(/'/g, "\'") + "'");
+	return call.join(" ");
+}
+
+dojo.hostenv.exit = function(){
+	if(widget.system){
+		widget.system("/bin/rm -rf cache/*", null);
+	}
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_jsc.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_jsc.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_jsc.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,76 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/*
+ * JScript .NET jsc
+ *
+ */
+
+dojo.hostenv.name_ = 'jsc';
+
+// Sanity check this is the right hostenv.
+// See the Rotor source code jscript/engine/globalobject.cs for what globals
+// are available.
+if((typeof ScriptEngineMajorVersion != 'function')||(ScriptEngineMajorVersion() < 7)){
+	dojo.raise("attempt to use JScript .NET host environment with inappropriate ScriptEngine"); 
+}
+
+// for more than you wanted to know about why this import is required even if
+// we fully qualify all symbols, see
+// http://groups.google.com/groups?th=f050c7aeefdcbde2&rnum=12
+import System;
+
+dojo.hostenv.getText = function(uri){
+	if(!System.IO.File.Exists(uri)){
+		// dojo.raise("No such file '" + uri + "'");
+		return 0;
+	}
+	var reader = new System.IO.StreamReader(uri);
+	var contents : String = reader.ReadToEnd();
+	return contents;
+}
+
+dojo.hostenv.loadUri = function(uri){
+	var contents = this.getText(uri);
+	if(!contents){
+		dojo.raise("got no back contents from uri '" + uri + "': " + contents);
+	}
+	// TODO: in JScript .NET, eval will not affect the symbol table of the current code?
+	var value = dj_eval(contents);
+	dojo.debug("jsc eval of contents returned: ", value);
+	return 1;
+
+	// for an example doing runtime code compilation, see:
+	// http://groups.google.com/groups?selm=eQ1aeciCBHA.1644%40tkmsftngp05&rnum=6
+	// Microsoft.JScript or System.CodeDom.Compiler ?
+	// var engine = new Microsoft.JScript.Vsa.VsaEngine()
+	// what about loading a js file vs. a dll?
+	// GetObject("script:" . uri);
+}
+
+/* The System.Environment object is useful:
+    print ("CommandLine='" + System.Environment.CommandLine + "' " +
+	   "program name='" + System.Environment.GetCommandLineArgs()[0] + "' " +
+	   "CurrentDirectory='" + System.Environment.CurrentDirectory + "' " +
+	   "StackTrace='" + System.Environment.StackTrace + "'");
+*/
+
+// same as System.Console.WriteLine
+// sigh; Rotor treats symbol "print" at parse time without actually putting it
+// in the builtin symbol table.
+// Note that the print symbol is not available if jsc is run with the "/print-"
+// option.
+dojo.hostenv.println = function(s){
+	print(s); // = print
+}
+
+dojo.hostenv.getLibraryScriptUri = function(){
+	return System.Environment.GetCommandLineArgs()[0];
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_rhino.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_rhino.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_rhino.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,246 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/*
+* Rhino host environment
+*/
+// make jsc shut up (so we can use jsc for sanity checking) 
+/*@cc_on
+ at if (@_jscript_version >= 7)
+var loadClass; var print; var load; var quit; var version; var Packages; var java;
+ at end
+@*/
+
+dojo.hostenv.println=function(line){
+	print(line);
+}
+
+dojo.locale = dojo.locale || java.util.Locale.getDefault().toString().replace('_','-').toLowerCase();
+dojo.render.name = dojo.hostenv.name_ = 'rhino';
+dojo.hostenv.getVersion = function() {return version();};
+
+if (dj_undef("byId")) {
+	dojo.byId = function(id, doc){
+		if(id && (typeof id == "string" || id instanceof String)){
+			if(!doc){ doc = document; }
+			return doc.getElementById(id);
+		}
+		return id; // assume it's a node
+	}
+}
+
+// see comments in spidermonkey loadUri
+dojo.hostenv.loadUri = function(uri, cb){
+	try{
+		var local = (new java.io.File(uri)).exists();
+		if(!local){
+			try{
+				// try it as a file first, URL second
+				var stream = (new java.net.URL(uri)).openStream();
+				// close the stream so we don't leak resources
+				stream.close();
+			}catch(e){
+				// no debug output; this failure just means the uri was not found.
+				return false;
+			}
+		}
+//FIXME: Use Rhino 1.6 native readFile/readUrl if available?
+		if(cb){
+			var contents = (local ? readText : readUri)(uri, "UTF-8");
+			cb(eval('('+contents+')'));
+		}else{
+			load(uri);
+		}
+		return true;
+	}catch(e){
+		dojo.debug("rhino load('" + uri + "') failed. Exception: " + e);
+		return false;
+	}
+}
+
+dojo.hostenv.exit = function(exitcode){ 
+	quit(exitcode);
+}
+
+// Hack to determine current script...
+//
+// These initial attempts failed:
+//   1. get an EcmaError and look at e.getSourceName(): try {eval ("static in return")} catch(e) { ...
+//   Won't work because NativeGlobal.java only does a put of "name" and "message", not a wrapped reflecting object.
+//   Even if the EcmaError object had the sourceName set.
+//  
+//   2. var e = Packages.org.mozilla.javascript.Context.getCurrentContext().reportError('');
+//   Won't work because it goes directly to the errorReporter, not the return value.
+//   We want context.interpreterSourceFile and context.interpreterLine, which are used in static Context.getSourcePositionFromStack
+//   (set by Interpreter.java at interpretation time, if in interpreter mode).
+//
+//   3. var e = Packages.org.mozilla.javascript.Context.getCurrentContext().reportRuntimeError('');
+//   This returns an object, but e.message still does not have source info.
+//   In compiler mode, perhaps not set; in interpreter mode, perhaps not used by errorReporter?
+//
+// What we found works is to do basically the same hack as is done in getSourcePositionFromStack,
+// making a new java.lang.Exception() and then calling printStackTrace on a string stream.
+// We have to parse the string for the .js files (different from the java files).
+// This only works however in compiled mode (-opt 0 or higher).
+// In interpreter mode, entire stack is java.
+// When compiled, printStackTrace is like:
+// java.lang.Exception
+//	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
+//	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
+//	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
+//	at java.lang.reflect.Constructor.newInstance(Constructor.java:274)
+//	at org.mozilla.javascript.NativeJavaClass.constructSpecific(NativeJavaClass.java:228)
+//	at org.mozilla.javascript.NativeJavaClass.construct(NativeJavaClass.java:185)
+//	at org.mozilla.javascript.ScriptRuntime.newObject(ScriptRuntime.java:1269)
+//	at org.mozilla.javascript.gen.c2.call(/Users/mda/Sites/burstproject/testrhino.js:27)
+//    ...
+//	at org.mozilla.javascript.tools.shell.Main.main(Main.java:76)
+//
+// Note may get different answers based on:
+//    Context.setOptimizationLevel(-1)
+//    Context.setGeneratingDebug(true)
+//    Context.setGeneratingSource(true) 
+//
+// Some somewhat helpful posts:
+//    http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=9v9n0g%246gr1%40ripley.netscape.com
+//    http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=3BAA2DC4.6010702%40atg.com
+//
+// Note that Rhino1.5R5 added source name information in some exceptions.
+// But this seems not to help in command-line Rhino, because Context.java has an error reporter
+// so no EvaluationException is thrown.
+
+// do it by using java java.lang.Exception
+function dj_rhino_current_script_via_java(depth) {
+    var optLevel = Packages.org.mozilla.javascript.Context.getCurrentContext().getOptimizationLevel();  
+   // if (optLevel == -1){ dojo.unimplemented("getCurrentScriptURI (determine current script path for rhino when interpreter mode)", ''); }
+    var caw = new java.io.CharArrayWriter();
+    var pw = new java.io.PrintWriter(caw);
+    var exc = new java.lang.Exception();
+    var s = caw.toString();
+    // we have to exclude the ones with or without line numbers because they put double entries in:
+    //   at org.mozilla.javascript.gen.c3._c4(/Users/mda/Sites/burstproject/burst/Runtime.js:56)
+    //   at org.mozilla.javascript.gen.c3.call(/Users/mda/Sites/burstproject/burst/Runtime.js)
+    var matches = s.match(/[^\(]*\.js\)/gi);
+    if(!matches){
+		throw Error("cannot parse printStackTrace output: " + s);
+	}
+
+    // matches[0] is entire string, matches[1] is this function, matches[2] is caller, ...
+    var fname = ((typeof depth != 'undefined')&&(depth)) ? matches[depth + 1] : matches[matches.length - 1];
+    var fname = matches[3];
+	if(!fname){ fname = matches[1]; }
+    // print("got fname '" + fname + "' from stack string '" + s + "'");
+    if (!fname){ throw Error("could not find js file in printStackTrace output: " + s); }
+    //print("Rhino getCurrentScriptURI returning '" + fname + "' from: " + s); 
+    return fname;
+}
+
+// UNUSED: leverage new support in native exception for getSourceName
+/*
+function dj_rhino_current_script_via_eval_exception() {
+    var exc;
+    // 'ReferenceError: "undefinedsymbol" is not defined.'
+    try {eval ("undefinedsymbol()") } catch(e) {exc = e;}
+    // 'Error: whatever'
+    // try{throw Error("whatever");} catch(e) {exc = e;}
+    // 'SyntaxError: identifier is a reserved word'
+    // try {eval ("static in return")} catch(e) { exc = e; }
+   // print("got exception: '" + exc + "' type=" + (typeof exc));
+    // print("exc.stack=" + (typeof exc.stack));
+    var sn = exc.rhinoException.getSourceName();
+    print("SourceName=" + sn);
+    return sn;
+}*/
+
+// reading a file from disk in Java is a humiliating experience by any measure.
+// Lets avoid that and just get the freaking text
+function readText(path, encoding){
+	encoding = encoding || "utf-8";
+	// NOTE: we intentionally avoid handling exceptions, since the caller will
+	// want to know
+	var jf = new java.io.File(path);
+	var is = new java.io.FileInputStream(jf);
+	return dj_readInputStream(is, encoding);
+}
+
+function readUri(uri, encoding){
+	var conn = (new java.net.URL(uri)).openConnection();
+	encoding = encoding || conn.getContentEncoding() || "utf-8";
+	var is = conn.getInputStream();
+	return dj_readInputStream(is, encoding);
+}
+
+function dj_readInputStream(is, encoding){
+	var input = new java.io.BufferedReader(new java.io.InputStreamReader(is, encoding));
+	try {
+		var sb = new java.lang.StringBuffer();
+		var line = "";
+		while((line = input.readLine()) !== null){
+			sb.append(line);
+			sb.append(java.lang.System.getProperty("line.separator"));
+		}
+		return sb.toString();
+	} finally {
+		input.close();
+	}
+}
+
+// call this now because later we may not be on the top of the stack
+if(!djConfig.libraryScriptUri.length){
+	try{
+		djConfig.libraryScriptUri = dj_rhino_current_script_via_java(1);
+	}catch(e){
+		// otherwise just fake it
+		if(djConfig["isDebug"]){
+			print("\n");
+			print("we have no idea where Dojo is located.");
+			print("Please try loading rhino in a non-interpreted mode or set a");
+			print("\n\tdjConfig.libraryScriptUri\n");
+			print("Setting the dojo path to './'");
+			print("This is probably wrong!");
+			print("\n");
+			print("Dojo will try to load anyway");
+		}
+		djConfig.libraryScriptUri = "./";
+	}
+}
+
+dojo.doc = function(){
+	// summary:
+	//		return the document object associated with the dojo.global()
+	return document;
+}
+
+dojo.body = function(){
+	return document.body;	
+}
+
+function setTimeout(func, delay){
+	// summary: provides timed callbacks using Java threads
+
+	var def={
+		sleepTime:delay,
+		hasSlept:false,
+		
+		run:function(){
+			if (!this.hasSlept){
+				this.hasSlept=true;
+				java.lang.Thread.currentThread().sleep(this.sleepTime);
+			}
+			try {
+				func();
+			} catch(e){dojo.debug("Error running setTimeout thread:" + e);}
+		}
+	};
+	
+	var runnable=new java.lang.Runnable(def);
+	var thread=new java.lang.Thread(runnable);
+	thread.start();
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_spidermonkey.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_spidermonkey.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_spidermonkey.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,79 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/*
+ * SpiderMonkey host environment
+ */
+
+dojo.hostenv.name_ = 'spidermonkey';
+
+dojo.hostenv.println = print;
+dojo.hostenv.exit = function(exitcode){ 
+	quit(exitcode); 
+}
+
+// version() returns 0, sigh. and build() returns nothing but just prints.
+dojo.hostenv.getVersion = function(){ return version(); }
+
+// make jsc shut up (so we can use jsc for sanity checking) 
+/*@cc_on
+ at if (@_jscript_version >= 7)
+var line2pc; var print; var load; var quit;
+ at end
+@*/
+
+if(typeof line2pc == 'undefined'){
+	dojo.raise("attempt to use SpiderMonkey host environment when no 'line2pc' global");
+}
+
+/*
+ * This is a hack that determines the current script file by parsing a generated
+ * stack trace (relying on the non-standard "stack" member variable of the
+ * SpiderMonkey Error object).
+ * If param depth is passed in, it'll return the script file which is that far down
+ * the stack, but that does require that you know how deep your stack is when you are
+ * calling.
+ */
+function dj_spidermonkey_current_file(depth){
+    var s = '';
+    try{
+		throw Error("whatever");
+	}catch(e){
+		s = e.stack;
+	}
+    // lines are like: bu_getCurrentScriptURI_spidermonkey("ScriptLoader.js")@burst/Runtime.js:101
+    var matches = s.match(/[^@]*\.js/gi);
+    if(!matches){ 
+		dojo.raise("could not parse stack string: '" + s + "'");
+	}
+    var fname = (typeof depth != 'undefined' && depth) ? matches[depth + 1] : matches[matches.length - 1];
+    if(!fname){ 
+		dojo.raise("could not find file name in stack string '" + s + "'");
+	}
+    //print("SpiderMonkeyRuntime got fname '" + fname + "' from stack string '" + s + "'");
+    return fname;
+}
+
+// call this now because later we may not be on the top of the stack
+if(!dojo.hostenv.library_script_uri_){ 
+	dojo.hostenv.library_script_uri_ = dj_spidermonkey_current_file(0); 
+}
+
+dojo.hostenv.loadUri = function(uri){
+	// spidermonkey load() evaluates the contents into the global scope (which
+	// is what we want).
+	// TODO: sigh, load() does not return a useful value. 
+	// Perhaps it is returning the value of the last thing evaluated?
+	var ok = load(uri);
+	// dojo.debug("spidermonkey load(", uri, ") returned ", ok);
+	return 1;
+}
+
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_svg.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_svg.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_svg.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,223 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+//	hostenv_svg
+if(typeof window == 'undefined'){
+	dojo.raise("attempt to use adobe svg hostenv when no window object");
+}
+dojo.debug = function(){ 
+	if (!djConfig.isDebug) { return; }
+	var args = arguments;
+	var isJUM = dj_global["jum"];
+	var s = isJUM ? "": "DEBUG: ";
+	for (var i = 0; i < args.length; ++i){ s += args[i]; }
+	if (isJUM){ // this seems to be the only way to get JUM to "play nice"
+		jum.debug(s);
+	} else{ 
+		dojo.hostenv.println(s);
+	}
+};
+
+//	set up dojo.render.
+dojo.render.name = navigator.appName;
+dojo.render.ver = parseFloat(navigator.appVersion, 10);
+switch(navigator.platform){
+	case "MacOS":
+		dojo.render.os.osx =  true;
+		break;
+	case "Linux":
+		dojo.render.os.linux =  true;
+		break;
+	case "Windows":
+		dojo.render.os.win =  true;
+		break;
+	default:
+		dojo.render.os.linux = true;
+		break;
+};
+dojo.render.svg.capable = true;
+dojo.render.svg.support.builtin = true;
+//	FIXME the following two is a big-ass hack for now.
+dojo.render.svg.moz = ((navigator.userAgent.indexOf("Gecko") >= 0) && (!((navigator.appVersion.indexOf("Konqueror") >= 0) || (navigator.appVersion.indexOf("Safari") >= 0))));
+dojo.render.svg.adobe = (window.parseXML != null);
+
+//	agent-specific implementations.
+
+//	from old hostenv_adobesvg.
+dojo.hostenv.startPackage("dojo.hostenv");
+dojo.hostenv.println = function(s){ 
+	try {
+		var ti = document.createElement("text");
+		ti.setAttribute("x","50");
+		ti.setAttribute("y", (25 + 15 * document.getElementsByTagName("text").length));
+		ti.appendChild(document.createTextNode(s));
+		document.documentElement.appendChild(ti);
+	} catch(e){ }
+};
+dojo.hostenv.name_ = "svg";
+
+//	expected/defined by bootstrap1.js
+dojo.hostenv.setModulePrefix = function(module, prefix){ };
+dojo.hostenv.getModulePrefix = function(module){ };
+dojo.hostenv.getTextStack = [];
+dojo.hostenv.loadUriStack = [];
+dojo.hostenv.loadedUris = [];
+dojo.hostenv.modules_ = {};
+dojo.hostenv.modulesLoadedFired = false;
+dojo.hostenv.modulesLoadedListeners = [];
+dojo.hostenv.getText = function(uri, cb, data){ 
+	if (!cb) var cb = function(result){ window.alert(result); };
+	if (!data) {
+		window.getUrl(uri, cb);
+	} else {
+		window.postUrl(uri, data, cb);
+	}
+};
+dojo.hostenv.getLibaryScriptUri = function(){ };
+
+dojo.hostenv.loadUri = function(uri){ };
+dojo.hostenv.loadUriAndCheck = function(uri, module){ };
+
+//	aliased in loader.js, don't ignore
+//	we are going to kill loadModule for the first round of SVG stuff, and include stuff manually.
+dojo.hostenv.loadModule = function(moduleName){
+	//	just like startPackage, but this time we're just checking to make sure it exists already.
+	var a = moduleName.split(".");
+	var currentObj = window;
+	var s = [];
+	for (var i = 0; i < a.length; i++){
+		if (a[i] == "*") continue;
+		s.push(a[i]);
+		if (!currentObj[a[i]]){
+			dojo.raise("dojo.require('" + moduleName + "'): module does not exist.");
+		} else currentObj = currentObj[a[i]];
+	}
+	return; 
+};
+dojo.hostenv.startPackage = function(moduleName){
+	var a = moduleName.split(".");
+	var currentObj = window;
+	var s = [];
+	for (var i = 0; i < a.length; i++){
+		if (a[i] == "*") continue;
+		s.push(a[i]);
+		if (!currentObj[a[i]]) currentObj[a[i]] = {};
+		currentObj = currentObj[a[i]];
+	}
+	return; 
+};
+
+//	wrapper objects for ASVG
+if (window.parseXML){
+	window.XMLSerialzer = function(){
+		//	based on WebFX RichTextControl getXHTML() function.
+		function nodeToString(n, a) {
+			function fixText(s) { return String(s).replace(/\&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;"); }
+			function fixAttribute(s) { return fixText(s).replace(/\"/g, "&quot;"); }
+			switch (n.nodeType) {
+				case 1:	{	//	ELEMENT
+					var name = n.nodeName;
+					a.push("<" + name);
+					for (var i = 0; i < n.attributes.length; i++) {
+						if (n.attributes.item(i).specified) {
+							a.push(" " + n.attributes.item(i).nodeName.toLowerCase() + "=\"" + fixAttribute(n.attributes.item(i).nodeValue) + "\"");
+						}
+					}
+					if (n.canHaveChildren || n.hasChildNodes()) {
+						a.push(">");
+						for (var i = 0; i < n.childNodes.length; i++) nodeToString(n.childNodes.item(i), a);
+						a.push("</" + name + ">\n");
+					} else a.push(" />\n");
+					break;
+				}
+				case 3: {	//	TEXT
+					a.push(fixText(n.nodeValue));
+					break;
+				}
+				case 4: {	//	CDATA
+					a.push("<![CDA" + "TA[\n" + n.nodeValue + "\n]" + "]>");
+					break;
+				}
+				case 7:{	//	PROCESSING INSTRUCTION
+					a.push(n.nodeValue);
+					if (/(^<\?xml)|(^<\!DOCTYPE)/.test(n.nodeValue)) a.push("\n");
+					break;
+				}
+				case 8:{	//	COMMENT
+					a.push("<!-- " + n.nodeValue + " -->\n");
+					break;
+				}
+				case 9:		//	DOCUMENT
+				case 11:{	//	DOCUMENT FRAGMENT
+					for (var i = 0; i < n.childNodes.length; i++) nodeToString(n.childNodes.item(i), a);
+					break;
+				}
+				default:{
+					a.push("<!--\nNot Supported:\n\n" + "nodeType: " + n.nodeType + "\nnodeName: " + n.nodeName + "\n-->");
+				}
+			}
+		}
+		this.serializeToString = function(node){
+			var a = [];
+			nodeToString(node, a);
+			return a.join("");
+		};
+	};
+
+	window.DOMParser = function(){
+		//	mimetype is basically ignored
+		this.parseFromString = function(s){
+			return parseXML(s, window.document);
+		}
+	};
+
+	window.XMLHttpRequest = function(){
+		//	we ignore the setting and getting of content-type.
+		var uri = null;
+		var method = "POST";
+		var isAsync = true;	
+		var cb = function(d){
+			this.responseText = d.content;
+			try {
+				this.responseXML = parseXML(this.responseText, window.document);
+			} catch(e){}
+			this.status = "200";
+			this.statusText = "OK";
+			if (!d.success) {
+				this.status = "500";
+				this.statusText = "Internal Server Error";
+			}
+			this.onload();
+			this.onreadystatechange();
+		};
+		this.onload = function(){};
+		this.readyState = 4;
+		this.onreadystatechange = function(){};
+		this.status = 0;
+		this.statusText = "";
+		this.responseBody = null;
+		this.responseStream = null;
+		this.responseXML = null;
+		this.responseText = null;
+		this.abort = function(){ return; };
+		this.getAllResponseHeaders = function(){ return []; };
+		this.getResponseHeader = function(n){ return null; };
+		this.setRequestHeader = function(nm, val){ };
+		this.open = function(meth, url, async){ 
+			method = meth;
+			uri = url;
+		};
+		this.send = function(data){
+			var d = data || null;
+			if (method == "GET") getURL(uri, cb);
+			else postURL(uri, data, cb);
+		};
+	};
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_wsh.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_wsh.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/hostenv_wsh.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,46 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/*
+ * WSH
+ */
+
+dojo.hostenv.name_ = 'wsh';
+
+// make jsc shut up (so can sanity check)
+/*@cc_on
+ at if (@_jscript_version >= 7)
+var WScript;
+ at end
+@*/
+
+// make sure we are in right environment
+if(typeof WScript == 'undefined'){
+	dojo.raise("attempt to use WSH host environment when no WScript global");
+}
+
+dojo.hostenv.println = WScript.Echo;
+
+dojo.hostenv.getCurrentScriptUri = function(){
+	return WScript.ScriptFullName();
+}
+
+dojo.hostenv.getText = function(fpath){
+	var fso = new ActiveXObject("Scripting.FileSystemObject");
+	var istream = fso.OpenTextFile(fpath, 1); // iomode==1 means read only
+	if(!istream){
+		return null;
+	}
+	var contents = istream.ReadAll();
+	istream.Close();
+	return contents;
+}
+
+dojo.hostenv.exit = function(exitcode){ WScript.Quit(exitcode); }

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/__package__.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/__package__.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/__package__.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,15 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.kwCompoundRequire({
+	common: [ "dojo.html.common",
+			  "dojo.html.style" ]
+});
+dojo.provide("dojo.html.*");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/color.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/color.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/color.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,35 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.require("dojo.html.style");
+dojo.provide("dojo.html.color");
+
+dojo.require("dojo.gfx.color");
+dojo.require("dojo.lang.common");
+
+dojo.html.getBackgroundColor = function(/* HTMLElement */node){
+	//	summary
+	//	returns the background color of the passed node as a 32-bit color (RGBA)
+	node = dojo.byId(node);
+	var color;
+	do{
+		color = dojo.html.getStyle(node, "background-color");
+		// Safari doesn't say "transparent"
+		if(color.toLowerCase() == "rgba(0, 0, 0, 0)") { color = "transparent"; }
+		if(node == document.getElementsByTagName("body")[0]) { node = null; break; }
+		node = node.parentNode;
+	}while(node && dojo.lang.inArray(["transparent", ""], color));
+	if(color == "transparent"){
+		color = [255, 255, 255, 0];
+	}else{
+		color = dojo.gfx.color.extractRGB(color);
+	}
+	return color;	//	array
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/common.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/common.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/common.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,230 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.html.common");
+dojo.require("dojo.lang.common");
+dojo.require("dojo.dom");
+
+dojo.lang.mixin(dojo.html, dojo.dom);
+
+dojo.html.body = function(){
+	dojo.deprecated("dojo.html.body() moved to dojo.body()", "0.5");
+	return dojo.body();
+}
+
+// FIXME: we are going to assume that we can throw any and every rendering
+// engine into the IE 5.x box model. In Mozilla, we do this w/ CSS.
+// Need to investigate for KHTML and Opera
+
+dojo.html.getEventTarget = function(/* DOMEvent */evt){
+	//	summary
+	//	Returns the target of an event
+	if(!evt) { evt = dojo.global().event || {} };
+	var t = (evt.srcElement ? evt.srcElement : (evt.target ? evt.target : null));
+	while((t)&&(t.nodeType!=1)){ t = t.parentNode; }
+	return t;	//	HTMLElement
+}
+
+dojo.html.getViewport = function(){
+	//	summary
+	//	Returns the dimensions of the viewable area of a browser window
+	var _window = dojo.global();
+	var _document = dojo.doc();
+	var w = 0;
+	var h = 0;
+
+	if(dojo.render.html.mozilla){
+		// mozilla
+		w = _document.documentElement.clientWidth;
+		h = _window.innerHeight;
+	}else if(!dojo.render.html.opera && _window.innerWidth){
+		//in opera9, dojo.body().clientWidth should be used, instead
+		//of window.innerWidth/document.documentElement.clientWidth
+		//so we have to check whether it is opera
+		w = _window.innerWidth;
+		h = _window.innerHeight;
+	} else if (!dojo.render.html.opera && dojo.exists(_document, "documentElement.clientWidth")){
+		// IE6 Strict
+		var w2 = _document.documentElement.clientWidth;
+		// this lets us account for scrollbars
+		if(!w || w2 && w2 < w) {
+			w = w2;
+		}
+		h = _document.documentElement.clientHeight;
+	} else if (dojo.body().clientWidth){
+		// IE, Opera
+		w = dojo.body().clientWidth;
+		h = dojo.body().clientHeight;
+	}
+	return { width: w, height: h };	//	object
+}
+
+dojo.html.getScroll = function(){
+	//	summary
+	//	Returns the scroll position of the document
+	var _window = dojo.global();
+	var _document = dojo.doc();
+	var top = _window.pageYOffset || _document.documentElement.scrollTop || dojo.body().scrollTop || 0;
+	var left = _window.pageXOffset || _document.documentElement.scrollLeft || dojo.body().scrollLeft || 0;
+	return { 
+		top: top, 
+		left: left, 
+		offset:{ x: left, y: top }	//	note the change, NOT an Array with added properties. 
+	};	//	object
+}
+
+dojo.html.getParentByType = function(/* HTMLElement */node, /* string */type) {
+	//	summary
+	//	Returns the first ancestor of node with tagName type.
+	var _document = dojo.doc();
+	var parent = dojo.byId(node);
+	type = type.toLowerCase();
+	while((parent)&&(parent.nodeName.toLowerCase()!=type)){
+		if(parent==(_document["body"]||_document["documentElement"])){
+			return null;
+		}
+		parent = parent.parentNode;
+	}
+	return parent;	//	HTMLElement
+}
+
+dojo.html.getAttribute = function(/* HTMLElement */node, /* string */attr){
+	//	summary
+	//	Returns the value of attribute attr from node.
+	node = dojo.byId(node);
+	// FIXME: need to add support for attr-specific accessors
+	if((!node)||(!node.getAttribute)){
+		// if(attr !== 'nwType'){
+		//	alert("getAttr of '" + attr + "' with bad node"); 
+		// }
+		return null;
+	}
+	var ta = typeof attr == 'string' ? attr : new String(attr);
+
+	// first try the approach most likely to succeed
+	var v = node.getAttribute(ta.toUpperCase());
+	if((v)&&(typeof v == 'string')&&(v!="")){ 
+		return v;	//	string 
+	}
+
+	// try returning the attributes value, if we couldn't get it as a string
+	if(v && v.value){ 
+		return v.value;	//	string 
+	}
+
+	// this should work on Opera 7, but it's a little on the crashy side
+	if((node.getAttributeNode)&&(node.getAttributeNode(ta))){
+		return (node.getAttributeNode(ta)).value;	//	string
+	}else if(node.getAttribute(ta)){
+		return node.getAttribute(ta);	//	string
+	}else if(node.getAttribute(ta.toLowerCase())){
+		return node.getAttribute(ta.toLowerCase());	//	string
+	}
+	return null;	//	string
+}
+	
+dojo.html.hasAttribute = function(/* HTMLElement */node, /* string */attr){
+	//	summary
+	//	Determines whether or not the specified node carries a value for the attribute in question.
+	return dojo.html.getAttribute(dojo.byId(node), attr) ? true : false;	//	boolean
+}
+	
+dojo.html.getCursorPosition = function(/* DOMEvent */e){
+	//	summary
+	//	Returns the mouse position relative to the document (not the viewport).
+	//	For example, if you have a document that is 10000px tall,
+	//	but your browser window is only 100px tall,
+	//	if you scroll to the bottom of the document and call this function it
+	//	will return {x: 0, y: 10000}
+	//	NOTE: for events delivered via dojo.event.connect() and/or dojoAttachEvent (for widgets),
+	//	you can just access evt.pageX and evt.pageY, rather than calling this function.
+	e = e || dojo.global().event;
+	var cursor = {x:0, y:0};
+	if(e.pageX || e.pageY){
+		cursor.x = e.pageX;
+		cursor.y = e.pageY;
+	}else{
+		var de = dojo.doc().documentElement;
+		var db = dojo.body();
+		cursor.x = e.clientX + ((de||db)["scrollLeft"]) - ((de||db)["clientLeft"]);
+		cursor.y = e.clientY + ((de||db)["scrollTop"]) - ((de||db)["clientTop"]);
+	}
+	return cursor;	//	object
+}
+
+dojo.html.isTag = function(/* HTMLElement */node) {
+	//	summary
+	//	Like dojo.dom.isTag, except case-insensitive
+	node = dojo.byId(node);
+	if(node && node.tagName) {
+		for (var i=1; i<arguments.length; i++){
+			if (node.tagName.toLowerCase()==String(arguments[i]).toLowerCase()){
+				return String(arguments[i]).toLowerCase();	//	string
+			}
+		}
+	}
+	return "";	//	string
+}
+
+//define dojo.html.createExternalElement for IE to workaround the annoying activation "feature" in new IE
+//details: http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/activating_activex.asp
+if(dojo.render.html.ie && !dojo.render.html.ie70){
+	//only define createExternalElement for IE in none https to avoid "mixed content" warning dialog
+	if(window.location.href.substr(0,6).toLowerCase() != "https:"){
+		(function(){
+			// FIXME: this seems not to work correctly on IE 7!!
+
+			//The trick is to define a function in a script.src property:
+			// <script src="javascript:'function createExternalElement(){...}'"></script>,
+			//which will be treated as an external javascript file in IE
+			var xscript = dojo.doc().createElement('script');
+			xscript.src = "javascript:'dojo.html.createExternalElement=function(doc, tag){ return doc.createElement(tag); }'";
+			dojo.doc().getElementsByTagName("head")[0].appendChild(xscript);
+		})();
+	}
+}else{
+	//for other browsers, simply use document.createElement
+	//is enough
+	dojo.html.createExternalElement = function(/* HTMLDocument */doc, /* string */tag){
+		//	summary
+		//	Creates an element in the HTML document, here for ActiveX activation workaround.
+		return doc.createElement(tag);	//	HTMLElement
+	}
+}
+
+dojo.html._callDeprecated = function(inFunc, replFunc, args, argName, retValue){
+	dojo.deprecated("dojo.html." + inFunc,
+					"replaced by dojo.html." + replFunc + "(" + (argName ? "node, {"+ argName + ": " + argName + "}" : "" ) + ")" + (retValue ? "." + retValue : ""), "0.5");
+	var newArgs = [];
+	if(argName){ var argsIn = {}; argsIn[argName] = args[1]; newArgs.push(args[0]); newArgs.push(argsIn); }
+	else { newArgs = args }
+	var ret = dojo.html[replFunc].apply(dojo.html, args);
+	if(retValue){ return ret[retValue]; }
+	else { return ret; }
+}
+
+dojo.html.getViewportWidth = function(){
+	return dojo.html._callDeprecated("getViewportWidth", "getViewport", arguments, null, "width");
+}
+dojo.html.getViewportHeight = function(){
+	return dojo.html._callDeprecated("getViewportHeight", "getViewport", arguments, null, "height");
+}
+dojo.html.getViewportSize = function(){
+	return dojo.html._callDeprecated("getViewportSize", "getViewport", arguments);
+}
+dojo.html.getScrollTop = function(){
+	return dojo.html._callDeprecated("getScrollTop", "getScroll", arguments, null, "top");
+}
+dojo.html.getScrollLeft = function(){
+	return dojo.html._callDeprecated("getScrollLeft", "getScroll", arguments, null, "left");
+}
+dojo.html.getScrollOffset = function(){
+	return dojo.html._callDeprecated("getScrollOffset", "getScroll", arguments, null, "offset");
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/display.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/display.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/display.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,196 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.html.display");
+dojo.require("dojo.html.style");
+
+dojo.html._toggle = function(node, tester, setter){
+	node = dojo.byId(node);
+	setter(node, !tester(node));
+	return tester(node);
+}
+
+dojo.html.show = function(/* HTMLElement */node){
+	//	summary
+	//	Show the passed element by reverting display property set by dojo.html.hide
+	node = dojo.byId(node);
+	if(dojo.html.getStyleProperty(node, 'display')=='none'){
+		dojo.html.setStyle(node, 'display', (node.dojoDisplayCache||''));
+		node.dojoDisplayCache = undefined;	// cannot use delete on a node in IE6
+	}
+}
+
+dojo.html.hide = function(/* HTMLElement */node){
+	//	summary
+	//	Hide the passed element by setting display:none
+	node = dojo.byId(node);
+	if(typeof node["dojoDisplayCache"] == "undefined"){ // it could == '', so we cannot say !node.dojoDisplayCount
+		var d = dojo.html.getStyleProperty(node, 'display')
+		if(d!='none'){
+			node.dojoDisplayCache = d;
+		}
+	}
+	dojo.html.setStyle(node, 'display', 'none');
+}
+
+dojo.html.setShowing = function(/* HTMLElement */node, /* boolean? */showing){
+	//	summary
+	// Calls show() if showing is true, hide() otherwise
+	dojo.html[(showing ? 'show' : 'hide')](node);
+}
+
+dojo.html.isShowing = function(/* HTMLElement */node){
+	//	summary
+	//	Returns whether the element is displayed or not.
+	// FIXME: returns true if node is bad, isHidden would be easier to make correct
+	return (dojo.html.getStyleProperty(node, 'display') != 'none');	//	boolean
+}
+
+dojo.html.toggleShowing = function(/* HTMLElement */node){
+	//	summary
+	// Call setShowing() on node with the complement of isShowing(), then return the new value of isShowing()
+	return dojo.html._toggle(node, dojo.html.isShowing, dojo.html.setShowing);	//	boolean
+}
+
+// Simple mapping of tag names to display values
+// FIXME: simplistic 
+dojo.html.displayMap = { tr: '', td: '', th: '', img: 'inline', span: 'inline', input: 'inline', button: 'inline' };
+
+dojo.html.suggestDisplayByTagName = function(/* HTMLElement */node){
+	//	summary
+	// Suggest a value for the display property that will show 'node' based on it's tag
+	node = dojo.byId(node);
+	if(node && node.tagName){
+		var tag = node.tagName.toLowerCase();
+		return (tag in dojo.html.displayMap ? dojo.html.displayMap[tag] : 'block');	//	string
+	}
+}
+
+dojo.html.setDisplay = function(/* HTMLElement */node, /* string */display){
+	//	summary
+	// 	Sets the value of style.display to value of 'display' parameter if it is a string.
+	// 	Otherwise, if 'display' is false, set style.display to 'none'.
+	// 	Finally, set 'display' to a suggested display value based on the node's tag
+	dojo.html.setStyle(node, 'display', ((display instanceof String || typeof display == "string") ? display : (display ? dojo.html.suggestDisplayByTagName(node) : 'none')));
+}
+
+dojo.html.isDisplayed = function(/* HTMLElement */node){
+	//	summary
+	// 	Is true if the the computed display style for node is not 'none'
+	// 	FIXME: returns true if node is bad, isNotDisplayed would be easier to make correct
+	return (dojo.html.getComputedStyle(node, 'display') != 'none');	//	boolean
+}
+
+dojo.html.toggleDisplay = function(/* HTMLElement */node){
+	//	summary
+	// 	Call setDisplay() on node with the complement of isDisplayed(), then
+	// 	return the new value of isDisplayed()
+	return dojo.html._toggle(node, dojo.html.isDisplayed, dojo.html.setDisplay);	//	boolean
+}
+
+dojo.html.setVisibility = function(/* HTMLElement */node, /* string */visibility){
+	//	summary
+	// 	Sets the value of style.visibility to value of 'visibility' parameter if it is a string.
+	// 	Otherwise, if 'visibility' is false, set style.visibility to 'hidden'. Finally, set style.visibility to 'visible'.
+	dojo.html.setStyle(node, 'visibility', ((visibility instanceof String || typeof visibility == "string") ? visibility : (visibility ? 'visible' : 'hidden')));
+}
+
+dojo.html.isVisible = function(/* HTMLElement */node){
+	//	summary
+	// 	Returns true if the the computed visibility style for node is not 'hidden'
+	// 	FIXME: returns true if node is bad, isInvisible would be easier to make correct
+	return (dojo.html.getComputedStyle(node, 'visibility') != 'hidden');	//	boolean
+}
+
+dojo.html.toggleVisibility = function(node){
+	//	summary
+	// Call setVisibility() on node with the complement of isVisible(), then return the new value of isVisible()
+	return dojo.html._toggle(node, dojo.html.isVisible, dojo.html.setVisibility);	//	boolean
+}
+
+dojo.html.setOpacity = function(/* HTMLElement */node, /* float */opacity, /* boolean? */dontFixOpacity){
+	//	summary
+	//	Sets the opacity of node in a cross-browser way.
+	//	float between 0.0 (transparent) and 1.0 (opaque)
+	node = dojo.byId(node);
+	var h = dojo.render.html;
+	if(!dontFixOpacity){
+		if( opacity >= 1.0){
+			if(h.ie){
+				dojo.html.clearOpacity(node);
+				return;
+			}else{
+				opacity = 0.999999;
+			}
+		}else if( opacity < 0.0){ opacity = 0; }
+	}
+	if(h.ie){
+		if(node.nodeName.toLowerCase() == "tr"){
+			// FIXME: is this too naive? will we get more than we want?
+			var tds = node.getElementsByTagName("td");
+			for(var x=0; x<tds.length; x++){
+				tds[x].style.filter = "Alpha(Opacity="+opacity*100+")";
+			}
+		}
+		node.style.filter = "Alpha(Opacity="+opacity*100+")";
+	}else if(h.moz){
+		node.style.opacity = opacity; // ffox 1.0 directly supports "opacity"
+		node.style.MozOpacity = opacity;
+	}else if(h.safari){
+		node.style.opacity = opacity; // 1.3 directly supports "opacity"
+		node.style.KhtmlOpacity = opacity;
+	}else{
+		node.style.opacity = opacity;
+	}
+}
+
+dojo.html.clearOpacity = function(/* HTMLElement */node){
+	//	summary
+	//	Clears any opacity setting on the passed element.
+	node = dojo.byId(node);
+	var ns = node.style;
+	var h = dojo.render.html;
+	if(h.ie){
+		try {
+			if( node.filters && node.filters.alpha ){
+				ns.filter = ""; // FIXME: may get rid of other filter effects
+			}
+		} catch(e) {
+			/*
+			 * IE7 gives error if node.filters not set;
+			 * don't know why or how to workaround (other than this)
+			 */
+		}
+	}else if(h.moz){
+		ns.opacity = 1;
+		ns.MozOpacity = 1;
+	}else if(h.safari){
+		ns.opacity = 1;
+		ns.KhtmlOpacity = 1;
+	}else{
+		ns.opacity = 1;
+	}
+}
+
+dojo.html.getOpacity = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the opacity of the passed element
+	node = dojo.byId(node);
+	var h = dojo.render.html;
+	if(h.ie){
+		var opac = (node.filters && node.filters.alpha &&
+			typeof node.filters.alpha.opacity == "number"
+			? node.filters.alpha.opacity : 100) / 100;
+	}else{
+		var opac = node.style.opacity || node.style.MozOpacity ||
+			node.style.KhtmlOpacity || 1;
+	}
+	return opac >= 0.999999 ? 1.0 : Number(opac);	//	float
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/iframe.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/iframe.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/iframe.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,117 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.html.iframe");
+dojo.require("dojo.html.util");
+
+// thanks burstlib!
+dojo.html.iframeContentWindow = function(/* HTMLIFrameElement */iframe_el) {
+	//	summary
+	//	returns the window reference of the passed iframe
+	var win = dojo.html.getDocumentWindow(dojo.html.iframeContentDocument(iframe_el)) ||
+		// Moz. TODO: is this available when defaultView isn't?
+		dojo.html.iframeContentDocument(iframe_el).__parent__ ||
+		(iframe_el.name && document.frames[iframe_el.name]) || null;
+	return win;	//	Window
+}
+
+dojo.html.iframeContentDocument = function(/* HTMLIFrameElement */iframe_el){
+	//	summary
+	//	returns a reference to the document object inside iframe_el
+	var doc = iframe_el.contentDocument // W3
+		|| ((iframe_el.contentWindow)&&(iframe_el.contentWindow.document))	// IE
+		|| ((iframe_el.name)&&(document.frames[iframe_el.name])&&(document.frames[iframe_el.name].document)) 
+		|| null;
+	return doc;	//	HTMLDocument
+}
+
+dojo.html.BackgroundIframe = function(/* HTMLElement */node) {
+	//	summary
+	//	For IE z-index schenanigans
+	//	Two possible uses:
+	//	1. new dojo.html.BackgroundIframe(node)
+	//		Makes a background iframe as a child of node, that fills area (and position) of node
+	//	2. new dojo.html.BackgroundIframe()
+	//		Attaches frame to dojo.body().  User must call size() to set size.
+	if(dojo.render.html.ie55 || dojo.render.html.ie60) {
+		var html="<iframe src='javascript:false'"
+			+ "' style='position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;"
+			+ "z-index: -1; filter:Alpha(Opacity=\"0\");' "
+			+ ">";
+		this.iframe = dojo.doc().createElement(html);
+		this.iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didnt work.
+		if(node){
+			node.appendChild(this.iframe);
+			this.domNode=node;
+		}else{
+			dojo.body().appendChild(this.iframe);
+			this.iframe.style.display="none";
+		}
+	}
+}
+dojo.lang.extend(dojo.html.BackgroundIframe, {
+	iframe: null,
+	onResized: function(){
+		//	summary
+		//	Resize event handler.
+		// TODO: this function shouldn't be necessary but setting width=height=100% doesn't work!
+		if(this.iframe && this.domNode && this.domNode.parentNode){ // No parentElement if onResized() timeout event occurs on a removed domnode
+			var outer = dojo.html.getMarginBox(this.domNode);
+			if (outer.width  == 0 || outer.height == 0 ){
+				dojo.lang.setTimeout(this, this.onResized, 100);
+				return;
+			}
+			this.iframe.style.width = outer.width + "px";
+			this.iframe.style.height = outer.height + "px";
+		}
+	},
+
+	size: function(/* HTMLElement */node) {
+		// 	Call this function if the iframe is connected to dojo.body() rather than the node being shadowed 
+		//	(TODO: erase)
+		if(!this.iframe) { return; }
+		var coords = dojo.html.toCoordinateObject(node, true, dojo.html.boxSizing.BORDER_BOX);
+		this.iframe.style.width = coords.width + "px";
+		this.iframe.style.height = coords.height + "px";
+		this.iframe.style.left = coords.left + "px";
+		this.iframe.style.top = coords.top + "px";
+	},
+
+	setZIndex: function(/* HTMLElement */node) {
+		//	summary
+		//	Sets the z-index of the background iframe.
+		if(!this.iframe) { return; }
+		if(dojo.dom.isNode(node)) {
+			this.iframe.style.zIndex = dojo.html.getStyle(node, "z-index") - 1;
+		} else if(!isNaN(node)) {
+			this.iframe.style.zIndex = node;
+		}
+	},
+
+	show: function() {
+		//	summary
+		//	show the iframe
+		if(!this.iframe) { return; }
+		this.iframe.style.display = "block";
+	},
+
+	hide: function() {
+		//	summary
+		//	hide the iframe
+		if(!this.iframe) { return; }
+		this.iframe.style.display = "none";
+	},
+
+	remove: function() {
+		//	summary
+		//	remove the iframe
+		dojo.html.removeNode(this.iframe);
+	}
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowB.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowB.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowBL.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowBL.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowBR.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowBR.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowL.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowL.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowR.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowR.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowT.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowT.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowTL.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowTL.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowTR.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/images/shadowTR.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/layout.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/layout.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/layout.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,487 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.html.layout");
+
+dojo.require("dojo.html.common");
+dojo.require("dojo.html.style");
+dojo.require("dojo.html.display");
+
+dojo.html.sumAncestorProperties = function(/* HTMLElement */node, /* string */prop){
+	//	summary
+	//	Returns the sum of the passed property on all ancestors of node.
+	node = dojo.byId(node);
+	if(!node){ return 0; } // FIXME: throw an error?
+	
+	var retVal = 0;
+	while(node){
+		if(dojo.html.getComputedStyle(node, 'position') == 'fixed'){
+			return 0;
+		}
+		var val = node[prop];
+		if(val){
+			retVal += val - 0;
+			if(node==dojo.body()){ break; }// opera and khtml #body & #html has the same values, we only need one value
+		}
+		node = node.parentNode;
+	}
+	return retVal;	//	integer
+}
+
+dojo.html.setStyleAttributes = function(/* HTMLElement */node, /* string */attributes) { 
+	//	summary
+	//	allows a dev to pass a string similar to what you'd pass in style="", and apply it to a node.
+	node = dojo.byId(node);
+	var splittedAttribs=attributes.replace(/(;)?\s*$/, "").split(";"); 
+	for(var i=0; i<splittedAttribs.length; i++){ 
+		var nameValue=splittedAttribs[i].split(":"); 
+		var name=nameValue[0].replace(/\s*$/, "").replace(/^\s*/, "").toLowerCase();
+		var value=nameValue[1].replace(/\s*$/, "").replace(/^\s*/, "");
+		switch(name){
+			case "opacity":
+				dojo.html.setOpacity(node, value); 
+				break; 
+			case "content-height":
+				dojo.html.setContentBox(node, {height: value}); 
+				break; 
+			case "content-width":
+				dojo.html.setContentBox(node, {width: value}); 
+				break; 
+			case "outer-height":
+				dojo.html.setMarginBox(node, {height: value}); 
+				break; 
+			case "outer-width":
+				dojo.html.setMarginBox(node, {width: value}); 
+				break; 
+			default:
+				node.style[dojo.html.toCamelCase(name)]=value; 
+		}
+	} 
+}
+
+dojo.html.boxSizing = {
+	MARGIN_BOX: "margin-box",
+	BORDER_BOX: "border-box",
+	PADDING_BOX: "padding-box",
+	CONTENT_BOX: "content-box"
+};
+
+dojo.html.getAbsolutePosition = dojo.html.abs = function(/* HTMLElement */node, /* boolean? */includeScroll, /* string? */boxType){
+	//	summary
+	//	Gets the absolute position of the passed element based on the document itself.
+	node = dojo.byId(node, node.ownerDocument);
+	var ret = {
+		x: 0,
+		y: 0
+	};
+
+	var bs = dojo.html.boxSizing;
+	if(!boxType) { boxType = bs.CONTENT_BOX; }
+	var nativeBoxType = 2; //BORDER box
+	var targetBoxType;
+	switch(boxType){
+		case bs.MARGIN_BOX:
+			targetBoxType = 3;
+			break;
+		case bs.BORDER_BOX:
+			targetBoxType = 2;
+			break;
+		case bs.PADDING_BOX:
+		default:
+			targetBoxType = 1;
+			break;
+		case bs.CONTENT_BOX:
+			targetBoxType = 0;
+			break;
+	}
+
+	var h = dojo.render.html;
+	var db = document["body"]||document["documentElement"];
+
+	if(h.ie){
+		with(node.getBoundingClientRect()){
+			ret.x = left-2;
+			ret.y = top-2;
+		}
+	}else if(document.getBoxObjectFor){
+		// mozilla
+		nativeBoxType = 1; //getBoxObjectFor return padding box coordinate
+		try{
+			var bo = document.getBoxObjectFor(node);
+			ret.x = bo.x - dojo.html.sumAncestorProperties(node, "scrollLeft");
+			ret.y = bo.y - dojo.html.sumAncestorProperties(node, "scrollTop");
+		}catch(e){
+			// squelch
+		}
+	}else{
+		if(node["offsetParent"]){
+			var endNode;
+			// in Safari, if the node is an absolutely positioned child of
+			// the body and the body has a margin the offset of the child
+			// and the body contain the body's margins, so we need to end
+			// at the body
+			if(	(h.safari)&&
+				(node.style.getPropertyValue("position") == "absolute")&&
+				(node.parentNode == db)){
+				endNode = db;
+			}else{
+				endNode = db.parentNode;
+			}
+
+			//TODO: set correct nativeBoxType for safari/konqueror
+
+			if(node.parentNode != db){
+				var nd = node;
+				if(dojo.render.html.opera){ nd = db; }
+				ret.x -= dojo.html.sumAncestorProperties(nd, "scrollLeft");
+				ret.y -= dojo.html.sumAncestorProperties(nd, "scrollTop");
+			}
+			var curnode = node;
+			do{
+				var n = curnode["offsetLeft"];
+				//FIXME: ugly hack to workaround the submenu in 
+				//popupmenu2 does not shown up correctly in opera. 
+				//Someone have a better workaround?
+				if(!h.opera || n>0){
+					ret.x += isNaN(n) ? 0 : n;
+				}
+				var m = curnode["offsetTop"];
+				ret.y += isNaN(m) ? 0 : m;
+				curnode = curnode.offsetParent;
+			}while((curnode != endNode)&&(curnode != null));
+		}else if(node["x"]&&node["y"]){
+			ret.x += isNaN(node.x) ? 0 : node.x;
+			ret.y += isNaN(node.y) ? 0 : node.y;
+		}
+	}
+
+	// account for document scrolling!
+	if(includeScroll){
+		var scroll = dojo.html.getScroll();
+		ret.y += scroll.top;
+		ret.x += scroll.left;
+	}
+
+	var extentFuncArray=[dojo.html.getPaddingExtent, dojo.html.getBorderExtent, dojo.html.getMarginExtent];
+	if(nativeBoxType > targetBoxType){
+		for(var i=targetBoxType;i<nativeBoxType;++i){
+			ret.y += extentFuncArray[i](node, 'top');
+			ret.x += extentFuncArray[i](node, 'left');
+		}
+	}else if(nativeBoxType < targetBoxType){
+		for(var i=targetBoxType;i>nativeBoxType;--i){
+			ret.y -= extentFuncArray[i-1](node, 'top');
+			ret.x -= extentFuncArray[i-1](node, 'left');
+		}
+	}
+	ret.top = ret.y;
+	ret.left = ret.x;
+	return ret;	//	object
+}
+
+dojo.html.isPositionAbsolute = function(/* HTMLElement */node){
+	//	summary
+	//	Returns true if the element is absolutely positioned.
+	return (dojo.html.getComputedStyle(node, 'position') == 'absolute');	//	boolean
+}
+
+dojo.html._sumPixelValues = function(/* HTMLElement */node, selectors, autoIsZero){
+	var total = 0;
+	for(var x=0; x<selectors.length; x++){
+		total += dojo.html.getPixelValue(node, selectors[x], autoIsZero);
+	}
+	return total;
+}
+
+dojo.html.getMargin = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the width and height of the passed node's margin
+	return {
+		width: dojo.html._sumPixelValues(node, ["margin-left", "margin-right"], (dojo.html.getComputedStyle(node, 'position') == 'absolute')),
+		height: dojo.html._sumPixelValues(node, ["margin-top", "margin-bottom"], (dojo.html.getComputedStyle(node, 'position') == 'absolute'))
+	};	//	object
+}
+
+dojo.html.getBorder = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the width and height of the passed node's border
+	return {
+		width: dojo.html.getBorderExtent(node, 'left') + dojo.html.getBorderExtent(node, 'right'),
+		height: dojo.html.getBorderExtent(node, 'top') + dojo.html.getBorderExtent(node, 'bottom')
+	};	//	object
+}
+
+dojo.html.getBorderExtent = function(/* HTMLElement */node, /* string */side){
+	//	summary
+	//	returns the width of the requested border
+	return (dojo.html.getStyle(node, 'border-' + side + '-style') == 'none' ? 0 : dojo.html.getPixelValue(node, 'border-' + side + '-width'));	// integer
+}
+
+dojo.html.getMarginExtent = function(/* HTMLElement */node, /* string */side){
+	//	summary
+	//	returns the width of the requested margin
+	return dojo.html._sumPixelValues(node, ["margin-" + side], dojo.html.isPositionAbsolute(node));	//	integer
+}
+
+dojo.html.getPaddingExtent = function(/* HTMLElement */node, /* string */side){
+	//	summary
+	//	Returns the width of the requested padding 
+	return dojo.html._sumPixelValues(node, ["padding-" + side], true);	//	integer
+}
+
+dojo.html.getPadding = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the width and height of the passed node's padding
+	return {
+		width: dojo.html._sumPixelValues(node, ["padding-left", "padding-right"], true),
+		height: dojo.html._sumPixelValues(node, ["padding-top", "padding-bottom"], true)
+	};	//	object
+}
+
+dojo.html.getPadBorder = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the width and height of the passed node's padding and border
+	var pad = dojo.html.getPadding(node);
+	var border = dojo.html.getBorder(node);
+	return { width: pad.width + border.width, height: pad.height + border.height };	//	object
+}
+
+dojo.html.getBoxSizing = function(/* HTMLElement */node){
+	//	summary
+	//	Returns which box model the passed element is working with
+	var h = dojo.render.html;
+	var bs = dojo.html.boxSizing;
+	if((h.ie)||(h.opera)){ 
+		var cm = document["compatMode"];
+		if((cm == "BackCompat")||(cm == "QuirksMode")){ 
+			return bs.BORDER_BOX; 	//	string
+		}else{
+			return bs.CONTENT_BOX; 	//	string
+		}
+	}else{
+		if(arguments.length == 0){ node = document.documentElement; }
+		var sizing = dojo.html.getStyle(node, "-moz-box-sizing");
+		if(!sizing){ sizing = dojo.html.getStyle(node, "box-sizing"); }
+		return (sizing ? sizing : bs.CONTENT_BOX);	//	string
+	}
+}
+
+dojo.html.isBorderBox = function(/* HTMLElement */node){
+	//	summary
+	//	returns whether the passed element is using border box sizing or not.
+	return (dojo.html.getBoxSizing(node) == dojo.html.boxSizing.BORDER_BOX);	//	boolean
+}
+
+dojo.html.getBorderBox = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the dimensions of the passed element based on border-box sizing.
+	node = dojo.byId(node);
+	return { width: node.offsetWidth, height: node.offsetHeight };	//	object
+}
+
+dojo.html.getPaddingBox = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the dimensions of the padding box (see http://www.w3.org/TR/CSS21/box.html)
+	var box = dojo.html.getBorderBox(node);
+	var border = dojo.html.getBorder(node);
+	return {
+		width: box.width - border.width,
+		height:box.height - border.height
+	};	//	object
+}
+
+dojo.html.getContentBox = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the dimensions of the content box (see http://www.w3.org/TR/CSS21/box.html)
+	node = dojo.byId(node);
+	var padborder = dojo.html.getPadBorder(node);
+	return {
+		width: node.offsetWidth - padborder.width,
+		height: node.offsetHeight - padborder.height
+	};	//	object
+}
+
+dojo.html.setContentBox = function(/* HTMLElement */node, /* object */args){
+	//	summary
+	//	Sets the dimensions of the passed node according to content sizing.
+	node = dojo.byId(node);
+	var width = 0; var height = 0;
+	var isbb = dojo.html.isBorderBox(node);
+	var padborder = (isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0});
+	var ret = {};
+	if(typeof args.width != "undefined"){
+		width = args.width + padborder.width;
+		ret.width = dojo.html.setPositivePixelValue(node, "width", width);
+	}
+	if(typeof args.height != "undefined"){
+		height = args.height + padborder.height;
+		ret.height = dojo.html.setPositivePixelValue(node, "height", height);
+	}
+	return ret;	//	object
+}
+
+dojo.html.getMarginBox = function(/* HTMLElement */node){
+	//	summary
+	//	returns the dimensions of the passed node including any margins.
+	var borderbox = dojo.html.getBorderBox(node);
+	var margin = dojo.html.getMargin(node);
+	return { width: borderbox.width + margin.width, height: borderbox.height + margin.height };	//	object
+}
+
+dojo.html.setMarginBox = function(/* HTMLElement */node, /* object */args){
+	//	summary
+	//	Sets the dimensions of the passed node using margin box calcs.
+	node = dojo.byId(node);
+	var width = 0; var height = 0;
+	var isbb = dojo.html.isBorderBox(node);
+	var padborder = (!isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0 });
+	var margin = dojo.html.getMargin(node);
+	var ret = {};
+	if(typeof args.width != "undefined"){
+		width = args.width - padborder.width;
+		width -= margin.width;
+		ret.width = dojo.html.setPositivePixelValue(node, "width", width);
+	}
+	if(typeof args.height != "undefined"){
+		height = args.height - padborder.height;
+		height -= margin.height;
+		ret.height = dojo.html.setPositivePixelValue(node, "height", height);
+	}
+	return ret;	//	object
+}
+
+dojo.html.getElementBox = function(/* HTMLElement */node, /* string */type){
+	//	summary
+	//	return dimesions of a node based on the passed box model type.
+	var bs = dojo.html.boxSizing;
+	switch(type){
+		case bs.MARGIN_BOX:
+			return dojo.html.getMarginBox(node);	//	object
+		case bs.BORDER_BOX:
+			return dojo.html.getBorderBox(node);	//	object
+		case bs.PADDING_BOX:
+			return dojo.html.getPaddingBox(node);	//	object
+		case bs.CONTENT_BOX:
+		default:
+			return dojo.html.getContentBox(node);	//	object
+	}
+}
+// in: coordinate array [x,y,w,h] or dom node
+// return: coordinate object
+dojo.html.toCoordinateObject = dojo.html.toCoordinateArray = function(/* array */coords, /* boolean? */includeScroll, /* string? */boxtype) {
+	//	summary
+	//	Converts an array of coordinates into an object of named arguments.
+	if(coords instanceof Array || typeof coords == "array"){
+		dojo.deprecated("dojo.html.toCoordinateArray", "use dojo.html.toCoordinateObject({left: , top: , width: , height: }) instead", "0.5");
+		// coords is already an array (of format [x,y,w,h]), just return it
+		while ( coords.length < 4 ) { coords.push(0); }
+		while ( coords.length > 4 ) { coords.pop(); }
+		var ret = {
+			left: coords[0],
+			top: coords[1],
+			width: coords[2],
+			height: coords[3]
+		};
+	}else if(!coords.nodeType && !(coords instanceof String || typeof coords == "string") &&
+			 ('width' in coords || 'height' in coords || 'left' in coords ||
+			  'x' in coords || 'top' in coords || 'y' in coords)){
+		// coords is a coordinate object or at least part of one
+		var ret = {
+			left: coords.left||coords.x||0,
+			top: coords.top||coords.y||0,
+			width: coords.width||0,
+			height: coords.height||0
+		};
+	}else{
+		// coords is an dom object (or dom object id); return it's coordinates
+		var node = dojo.byId(coords);
+		var pos = dojo.html.abs(node, includeScroll, boxtype);
+		var marginbox = dojo.html.getMarginBox(node);
+		var ret = {
+			left: pos.left,
+			top: pos.top,
+			width: marginbox.width,
+			height: marginbox.height
+		};
+	}
+	ret.x = ret.left;
+	ret.y = ret.top;
+	return ret;	//	object
+}
+
+dojo.html.setMarginBoxWidth = dojo.html.setOuterWidth = function(node, width){
+	return dojo.html._callDeprecated("setMarginBoxWidth", "setMarginBox", arguments, "width");
+}
+dojo.html.setMarginBoxHeight = dojo.html.setOuterHeight = function(){
+	return dojo.html._callDeprecated("setMarginBoxHeight", "setMarginBox", arguments, "height");
+}
+dojo.html.getMarginBoxWidth = dojo.html.getOuterWidth = function(){
+	return dojo.html._callDeprecated("getMarginBoxWidth", "getMarginBox", arguments, null, "width");
+}
+dojo.html.getMarginBoxHeight = dojo.html.getOuterHeight = function(){
+	return dojo.html._callDeprecated("getMarginBoxHeight", "getMarginBox", arguments, null, "height");
+}
+dojo.html.getTotalOffset = function(node, type, includeScroll){
+	return dojo.html._callDeprecated("getTotalOffset", "getAbsolutePosition", arguments, null, type);
+}
+dojo.html.getAbsoluteX = function(node, includeScroll){
+	return dojo.html._callDeprecated("getAbsoluteX", "getAbsolutePosition", arguments, null, "x");
+}
+dojo.html.getAbsoluteY = function(node, includeScroll){
+	return dojo.html._callDeprecated("getAbsoluteY", "getAbsolutePosition", arguments, null, "y");
+}
+dojo.html.totalOffsetLeft = function(node, includeScroll){
+	return dojo.html._callDeprecated("totalOffsetLeft", "getAbsolutePosition", arguments, null, "left");
+}
+dojo.html.totalOffsetTop = function(node, includeScroll){
+	return dojo.html._callDeprecated("totalOffsetTop", "getAbsolutePosition", arguments, null, "top");
+}
+dojo.html.getMarginWidth = function(node){
+	return dojo.html._callDeprecated("getMarginWidth", "getMargin", arguments, null, "width");
+}
+dojo.html.getMarginHeight = function(node){
+	return dojo.html._callDeprecated("getMarginHeight", "getMargin", arguments, null, "height");
+}
+dojo.html.getBorderWidth = function(node){
+	return dojo.html._callDeprecated("getBorderWidth", "getBorder", arguments, null, "width");
+}
+dojo.html.getBorderHeight = function(node){
+	return dojo.html._callDeprecated("getBorderHeight", "getBorder", arguments, null, "height");
+}
+dojo.html.getPaddingWidth = function(node){
+	return dojo.html._callDeprecated("getPaddingWidth", "getPadding", arguments, null, "width");
+}
+dojo.html.getPaddingHeight = function(node){
+	return dojo.html._callDeprecated("getPaddingHeight", "getPadding", arguments, null, "height");
+}
+dojo.html.getPadBorderWidth = function(node){
+	return dojo.html._callDeprecated("getPadBorderWidth", "getPadBorder", arguments, null, "width");
+}
+dojo.html.getPadBorderHeight = function(node){
+	return dojo.html._callDeprecated("getPadBorderHeight", "getPadBorder", arguments, null, "height");
+}
+dojo.html.getBorderBoxWidth = dojo.html.getInnerWidth = function(){
+	return dojo.html._callDeprecated("getBorderBoxWidth", "getBorderBox", arguments, null, "width");
+}
+dojo.html.getBorderBoxHeight = dojo.html.getInnerHeight = function(){
+	return dojo.html._callDeprecated("getBorderBoxHeight", "getBorderBox", arguments, null, "height");
+}
+dojo.html.getContentBoxWidth = dojo.html.getContentWidth = function(){
+	return dojo.html._callDeprecated("getContentBoxWidth", "getContentBox", arguments, null, "width");
+}
+dojo.html.getContentBoxHeight = dojo.html.getContentHeight = function(){
+	return dojo.html._callDeprecated("getContentBoxHeight", "getContentBox", arguments, null, "height");
+}
+dojo.html.setContentBoxWidth = dojo.html.setContentWidth = function(node, width){
+	return dojo.html._callDeprecated("setContentBoxWidth", "setContentBox", arguments, "width");
+}
+dojo.html.setContentBoxHeight = dojo.html.setContentHeight = function(node, height){
+	return dojo.html._callDeprecated("setContentBoxHeight", "setContentBox", arguments, "height");
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/metrics.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/metrics.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/metrics.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,266 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.html.metrics");
+dojo.require("dojo.html.layout");
+
+/*	dojo.html.metrics
+ *	Methods to help determine font metrics, including things like
+ *	how much of a string will fit inside a certain width, what size
+ *	something might be if you were to place it in a certain node, etc.
+ *
+ *	Based partially on a submitted patch by Morris Johns, and work
+ *	done with 13th Parallel and f( m ) (the 13th columns and the
+ *	unreleased f( m ) layout manager.
+ */
+
+//	derived from Morris John's scrollbar measurer.
+dojo.html.getScrollbar=function(){
+	//	summary
+	//	returns the width of a scrollbar.
+	
+	//	set up the test nodes.
+	var scroll = document.createElement("div");
+	scroll.style.width="100px";
+	scroll.style.height="100px";
+	scroll.style.overflow="scroll";
+	scroll.style.position="absolute";
+	scroll.style.top="-300px";
+	scroll.style.left="0px"
+	
+	var test = document.createElement("div");
+	test.style.width="400px";
+	test.style.height="400px";
+	scroll.appendChild(test);
+	dojo.body().appendChild(scroll);
+
+	var width=scroll.offsetWidth - scroll.clientWidth;
+
+	dojo.body().removeChild(scroll);
+	scroll.removeChild(test);
+	scroll=test=null;
+
+	//	we return an object because we may add additional info in the future.
+	return { width: width };	//	object
+};
+
+//	derived from Morris John's emResized measurer
+dojo.html.getFontMeasurements = function(){
+	//	summary
+	//	Returns an object that has pixel equivilents of standard font size values.
+	var heights = {
+		'1em':0, '1ex':0, '100%':0, '12pt':0, '16px':0, 'xx-small':0, 'x-small':0,
+		'small':0, 'medium':0, 'large':0, 'x-large':0, 'xx-large':0
+	};
+
+	if(dojo.render.html.ie){
+		//	we do a font-size fix if and only if one isn't applied already.
+		//	NOTE: If someone set the fontSize on the HTML Element, this will kill it.
+		document.documentElement.style.fontSize="100%";
+	}
+
+	//	set up the measuring node.
+	var div=document.createElement("div");
+	div.style.position="absolute";
+	div.style.left="-100px";
+	div.style.top="0";
+	div.style.width="30px";
+	div.style.height="1000em";
+	div.style.border="0";
+	div.style.margin="0";
+	div.style.padding="0";
+	div.style.outline="0";
+	div.style.lineHeight="1";
+	div.style.overflow="hidden";
+	dojo.body().appendChild(div);
+
+	//	do the measurements.
+	for(var p in heights){
+		div.style.fontSize = p;
+		heights[p] = Math.round(div.offsetHeight * 12/16) * 16/12 / 1000;
+	}
+	
+	dojo.body().removeChild(div);
+	div = null;
+	return heights; 	//	object
+};
+
+dojo.html._fontMeasurements = null;
+
+dojo.html.getCachedFontMeasurements = function(recalculate){
+	if(recalculate || !dojo.html._fontMeasurements){
+		dojo.html._fontMeasurements = dojo.html.getFontMeasurements();
+	}
+	return dojo.html._fontMeasurements;
+};
+
+dojo.html.measureFragment = function(/* HTMLElement */node, /* string */html, /* string? */boxType){
+	//	summary
+	//	get the dimensions of passed node if it were populated with passed html.
+	var clone = node.cloneNode(true);
+	clone.innerHTML = html;
+	node.parentNode.appendChild(clone);
+	var ret = dojo.html.getElementBox(clone, boxType);
+	node.parentNode.removeChild(clone);
+	clone=null;
+	return ret; // object
+};
+
+//	the following are derived from the 13th Parallel Column script, as
+//		reinterpreted by trt.  http://www.13thparallel.org/archive/column-script
+//	Original by Dan Pupius and Michael van Ouwerkerk.
+dojo.html.getFittedFragment = function(/* HTMLElement */node, /* string */html){
+	//	summary
+	//	Given html, return the fragment that will fit on one line of passed node.
+	function cl(node){
+		var element = document.createElement(node.tagName);
+		element.id = node.id + "-clone";
+		element.className = node.className;
+		for (var j = 0; j < node.attributes.length; j++) {
+			if (node.attributes[j].specified) {
+				if (node.attributes[j].nodeName.toLowerCase() != "style" 
+					&& node.attributes[j].nodeName.toLowerCase() != "edited" 
+					&& node.attributes[j].nodeName.toLowerCase() != "contenteditable"
+					&& node.attributes[j].nodeName.toLowerCase() != "id"
+					&& node.attributes[j].nodeName.toLowerCase() != "class"
+				){
+					element.setAttribute(node.attributes[j].nodeName.toLowerCase(), node.attributes[j].nodeValue);
+				}
+			}
+		}
+		return element;
+	}
+	var height = dojo.html.getFontMeasurements()["16px"];
+	var n=cl(node);
+	n.style.width=dojo.html.getBorderBox(node).width+"px";
+	n.style.height=(height+4)+"px";
+	node.parentNode.appendChild(n);
+	var rem = dojo.html.fitToElement(n, html);
+	var ret = n.innerHTML;
+	n.parentNode.removeChild(n);
+	return ret;
+};
+
+dojo.html.fitToElement = function(/* HTMLElement */node, /* string */html){
+	//	summary
+	//	will fit as much html as possible into node, and return the unused
+	//	portion, with tag corrections.
+	function cl(node){
+		var element = document.createElement(node.tagName);
+		element.id = node.id + "-clone";
+		element.className = node.className;
+		for (var j = 0; j < node.attributes.length; j++) {
+			if (node.attributes[j].specified) {
+				if (node.attributes[j].nodeName.toLowerCase() != "style" 
+					&& node.attributes[j].nodeName.toLowerCase() != "edited" 
+					&& node.attributes[j].nodeName.toLowerCase() != "contenteditable"
+					&& node.attributes[j].nodeName.toLowerCase() != "id"
+					&& node.attributes[j].nodeName.toLowerCase() != "class"
+				){
+					element.setAttribute(node.attributes[j].nodeName.toLowerCase(), node.attributes[j].nodeValue);
+				}
+			}
+		}
+		return element;
+	}
+
+	var clone = cl(node);
+	node.parentNode.appendChild(clone);
+	var t=dojo.html.getBorderBox(node);
+	clone.style.width = t.width+"px";
+
+	var singletons = ["br","img", "hr", "input", "!--"];
+	var chop = ["<BR>","<br>","<br/>","<br />","<p></p>","<P></P>"];
+	var openTags = [];
+
+	var str = html;
+	var i = 0;
+	var limit = str.length;
+	var add = 0;
+	var doLoop = true;
+	clone.innerHTML = str;
+	while (doLoop) {
+		add = Math.round((limit - i) / 2);
+		if (add <= 1) doLoop = false;
+		i += add;
+		clone.innerHTML = str.substr(0, i);
+		if (clone.offsetHeight > t.height) {
+			limit = i;
+			i -= add;
+		}
+	}
+	if (str.substr(0, i) != str) {
+		var lastSpace = str.substr(0, i).lastIndexOf(" ");
+		var lastNewLine = str.substr(0, i).lastIndexOf("\n");
+		var lastGreater = str.substr(0, i).lastIndexOf(">");
+		var lastLess = str.substr(0, i).lastIndexOf("<");
+		if (lastLess <= lastGreater && lastNewLine == i - 1) i = i;
+		else if (lastSpace != -1 && lastSpace > lastGreater && lastGreater > lastLess) i = lastSpace + 1;
+		else if (lastLess > lastGreater) i = lastLess;
+		else if (lastGreater != -1) i = lastGreater + 1;
+	}
+
+	str = str.substr(0, i);
+	var ret = html.substr(str.length);	//	get the rest of the passed text.
+
+	var doPush = true;
+	var tags = str.split("<");
+	tags.shift();
+	for (var j = 0; j < tags.length; j++) {
+		tags[j] = tags[j].split(">")[0];
+		if (tags[j].charAt(tags[j].length - 1) == "/"){ continue; }
+		if (tags[j].charAt(0) != "/") {
+			for (var k = 0; k < singletons.length; k++) {
+				if (tags[j].split(" ")[0].toLowerCase() == singletons[k]){
+					doPush = false;
+				}
+			}
+			if (doPush){
+				openTags.push(tags[j]);
+			}
+			doPush = true;
+		} else {
+			openTags.pop();
+		}
+	}
+
+	//	close any open tags and prepend them to ret as well.
+	for(var j=0; j<chop.length; j++){
+		if(ret.charAt(0) == "\n"){ ret = ret.substr(1); }
+		while(ret.indexOf(chop[j]) == 0){
+			ret = ret.substr(chop[j].length);
+		}
+	}
+
+	for(var j=openTags.length-1; j>=0; j--){
+		if(str.lastIndexOf(openTags[j]) == (str.length-openTags[j].length-1)){
+			str = str.substring(0, str.lastIndexOf(openTags[j]));
+		} else {
+			str += "</"+openTags[j]+">";
+		}
+		if(ret.length > 0){
+			ret = "<"+openTags[j]+">"+ret;
+		}
+	}
+	
+	for(var j=0; j<chop.length; j++){
+		if(ret.charAt(0) == "\n"){ ret = ret.substr(1); }
+		while(ret.indexOf(chop[j]) == 0){
+			ret = ret.substr(chop[j].length);
+		}
+	}
+	//	push it into the node and pull the temp one.
+	node.innerHTML = str;
+	clone.parentNode.removeChild(clone);
+	clone = null;
+	
+	//	return the remainder.
+	return ret;	//	string
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/selection.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/selection.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/selection.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,376 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.require("dojo.html.common");
+dojo.provide("dojo.html.selection");
+
+dojo.require("dojo.dom");
+dojo.require("dojo.lang.common");
+
+/**
+ * type of selection
+**/
+dojo.html.selectionType = {
+	NONE : 0, //selection is empty
+	TEXT : 1, //selection contains text (may also contains CONTROL objects)
+	CONTROL : 2 //only one element is selected (such as img, table etc)
+};
+
+dojo.html.clearSelection = function(){
+	// summary: deselect the current selection to make it empty
+	var _window = dojo.global();
+	var _document = dojo.doc();
+	try{
+		if(_window["getSelection"]){ 
+			if(dojo.render.html.safari){
+				// pulled from WebCore/ecma/kjs_window.cpp, line 2536
+				_window.getSelection().collapse();
+			}else{
+				_window.getSelection().removeAllRanges();
+			}
+		}else if(_document.selection){
+			if(_document.selection.empty){
+				_document.selection.empty();
+			}else if(_document.selection.clear){
+				_document.selection.clear();
+			}
+		}
+		return true;
+	}catch(e){
+		dojo.debug(e);
+		return false;
+	}
+}
+
+dojo.html.disableSelection = function(/*DomNode*/element){
+	// summary: disable selection on a node
+	element = dojo.byId(element)||dojo.body();
+	var h = dojo.render.html;
+	
+	if(h.mozilla){
+		element.style.MozUserSelect = "none";
+	}else if(h.safari){
+		element.style.KhtmlUserSelect = "none"; 
+	}else if(h.ie){
+		element.unselectable = "on";
+	}else{
+		return false;
+	}
+	return true;
+}
+
+dojo.html.enableSelection = function(/*DomNode*/element){
+	// summary: enable selection on a node
+	element = dojo.byId(element)||dojo.body();
+	
+	var h = dojo.render.html;
+	if(h.mozilla){ 
+		element.style.MozUserSelect = ""; 
+	}else if(h.safari){
+		element.style.KhtmlUserSelect = "";
+	}else if(h.ie){
+		element.unselectable = "off";
+	}else{
+		return false;
+	}
+	return true;
+}
+
+dojo.html.selectElement = function(/*DomNode*/element){
+	dojo.deprecated("dojo.html.selectElement", "replaced by dojo.html.selection.selectElementChildren", 0.5);
+}
+
+dojo.html.selectInputText = function(/*DomNode*/element){
+	// summary: select all the text in an input element
+	var _window = dojo.global();
+	var _document = dojo.doc();
+	element = dojo.byId(element);
+	if(_document["selection"] && dojo.body()["createTextRange"]){ // IE
+		var range = element.createTextRange();
+		range.moveStart("character", 0);
+		range.moveEnd("character", element.value.length);
+		range.select();
+	}else if(_window["getSelection"]){
+		var selection = _window.getSelection();
+		// FIXME: does this work on Safari?
+		element.setSelectionRange(0, element.value.length);
+	}
+	element.focus();
+}
+
+
+dojo.html.isSelectionCollapsed = function(){
+	dojo.deprecated("dojo.html.isSelectionCollapsed", "replaced by dojo.html.selection.isCollapsed", 0.5);
+	return dojo.html.selection.isCollapsed();
+}
+
+dojo.lang.mixin(dojo.html.selection, {
+	getType: function() {
+		// summary: Get the selection type (like document.select.type in IE).
+		if(dojo.doc()["selection"]){ //IE
+			return dojo.html.selectionType[dojo.doc().selection.type.toUpperCase()];
+		}else{
+			var stype = dojo.html.selectionType.TEXT;
+	
+			// Check if the actual selection is a CONTROL (IMG, TABLE, HR, etc...).
+			var oSel;
+			try {oSel = dojo.global().getSelection();}
+			catch (e) {}
+			
+			if(oSel && oSel.rangeCount==1){
+				var oRange = oSel.getRangeAt(0);
+				if (oRange.startContainer == oRange.endContainer && (oRange.endOffset - oRange.startOffset) == 1
+					&& oRange.startContainer.nodeType != dojo.dom.TEXT_NODE) {
+					stype = dojo.html.selectionType.CONTROL;
+				}
+			}
+			return stype;
+		}
+	},
+	isCollapsed: function() {
+		// summary: return whether the current selection is empty
+		var _window = dojo.global();
+		var _document = dojo.doc();
+		if(_document["selection"]){ // IE
+			return _document.selection.createRange().text == "";
+		}else if(_window["getSelection"]){
+			var selection = _window.getSelection();
+			if(dojo.lang.isString(selection)){ // Safari
+				return selection == "";
+			}else{ // Mozilla/W3
+				return selection.isCollapsed || selection.toString() == "";
+			}
+		}
+	},
+	getSelectedElement: function() {
+		// summary: 
+		//		Retrieves the selected element (if any), just in the case that a single
+		//		element (object like and image or a table) is selected.
+		if ( dojo.html.selection.getType() == dojo.html.selectionType.CONTROL ){
+			if(dojo.doc()["selection"]){ //IE
+				var range = dojo.doc().selection.createRange();
+		
+				if ( range && range.item ){
+					return dojo.doc().selection.createRange().item(0);
+				}
+			}else{
+				var selection = dojo.global().getSelection();
+				return selection.anchorNode.childNodes[ selection.anchorOffset ];
+			}
+		}
+	},
+	getParentElement: function() {
+		// summary: 
+		//		Get the parent element of the current selection
+		if(dojo.html.selection.getType() == dojo.html.selectionType.CONTROL){
+			var p = dojo.html.selection.getSelectedElement();
+			if(p){ return p.parentNode; }
+		}else{
+			if(dojo.doc()["selection"]){ //IE
+				return dojo.doc().selection.createRange().parentElement();
+			}else{
+				var selection = dojo.global().getSelection();
+				if(selection){
+					var node = selection.anchorNode;
+		
+					while ( node && node.nodeType != dojo.dom.ELEMENT_NODE ){
+						node = node.parentNode;
+					}
+		
+					return node;
+				}
+			}
+		}
+	},
+	getSelectedText: function(){
+		// summary:
+		//		Return the text (no html tags) included in the current selection or null if no text is selected
+		if(dojo.doc()["selection"]){ //IE
+			if(dojo.html.selection.getType() == dojo.html.selectionType.CONTROL){
+				return null;
+			}
+			return dojo.doc().selection.createRange().text;
+		}else{
+			var selection = dojo.global().getSelection();
+			if(selection){
+				return selection.toString();
+			}
+		}
+	},
+	getSelectedHtml: function(){
+		// summary:
+		//		Return the html of the current selection or null if unavailable
+		if(dojo.doc()["selection"]){ //IE
+			if(dojo.html.selection.getType() == dojo.html.selectionType.CONTROL){
+				return null;
+			}
+			return dojo.doc().selection.createRange().htmlText;
+		}else{
+			var selection = dojo.global().getSelection();
+			if(selection && selection.rangeCount){
+				var frag = selection.getRangeAt(0).cloneContents();
+				var div = document.createElement("div");
+				div.appendChild(frag);
+				return div.innerHTML;
+			}
+			return null;
+		}
+	},
+	hasAncestorElement: function(/*String*/tagName /* ... */){
+		// summary: 
+		// 		Check whether current selection has a  parent element which is of type tagName (or one of the other specified tagName)
+		return (dojo.html.selection.getAncestorElement.apply(this, arguments) != null);
+	},
+	getAncestorElement: function(/*String*/tagName /* ... */){
+		// summary:
+		//		Return the parent element of the current selection which is of type tagName (or one of the other specified tagName)
+		var node = dojo.html.selection.getSelectedElement() || dojo.html.selection.getParentElement();
+		while(node /*&& node.tagName.toLowerCase() != 'body'*/){
+			if(dojo.html.selection.isTag(node, arguments).length>0){
+				return node;
+			}
+			node = node.parentNode;
+		}
+		return null;
+	},
+	//modified from dojo.html.isTag to take an array as second parameter
+	isTag: function(/*DomNode*/node, /*Array*/tags) {
+		if(node && node.tagName) {
+			for (var i=0; i<tags.length; i++){
+				if (node.tagName.toLowerCase()==String(tags[i]).toLowerCase()){
+					return String(tags[i]).toLowerCase();
+				}
+			}
+		}
+		return "";
+	},
+	selectElement: function(/*DomNode*/element) {
+		// summary: clear previous selection and select element (including all its children)
+		var _window = dojo.global();
+		var _document = dojo.doc();
+		element = dojo.byId(element);
+		if(_document.selection && dojo.body().createTextRange){ // IE
+			try{
+				var range = dojo.body().createControlRange();
+				range.addElement(element);
+				range.select();
+			}catch(e){
+				dojo.html.selection.selectElementChildren(element);
+			}
+		}else if(_window["getSelection"]){
+			var selection = _window.getSelection();
+			// FIXME: does this work on Safari?
+			if(selection["removeAllRanges"]){ // Mozilla
+				var range = _document.createRange() ;
+				range.selectNode(element) ;
+				selection.removeAllRanges() ;
+				selection.addRange(range) ;
+			}
+		}
+	},
+	selectElementChildren: function(/*DomNode*/element){
+		// summary: clear previous selection and select the content of the node (excluding the node itself)
+		var _window = dojo.global();
+		var _document = dojo.doc();
+		element = dojo.byId(element);
+		if(_document.selection && dojo.body().createTextRange){ // IE
+			var range = dojo.body().createTextRange();
+			range.moveToElementText(element);
+			range.select();
+		}else if(_window["getSelection"]){
+			var selection = _window.getSelection();
+			if(selection["setBaseAndExtent"]){ // Safari
+				selection.setBaseAndExtent(element, 0, element, element.innerText.length - 1);
+			} else if(selection["selectAllChildren"]){ // Mozilla
+				selection.selectAllChildren(element);
+			}
+		}
+	},
+	getBookmark: function(){
+		// summary: Retrieves a bookmark that can be used with moveToBookmark to return to the same range
+		var bookmark;
+		var _document = dojo.doc();
+		if(_document["selection"]){ // IE
+			var range = _document.selection.createRange();
+			bookmark = range.getBookmark();
+		}else{
+			var selection;
+			try {selection = dojo.global().getSelection();}
+			catch (e) {}
+			if(selection){
+				var range = selection.getRangeAt(0);
+				bookmark = range.cloneRange();
+			}else{
+				dojo.debug("No idea how to store the current selection for this browser!");
+			}
+		}
+		return bookmark;
+	},
+	moveToBookmark: function(/*Object*/bookmark){
+		// summary: Moves current selection to a bookmark
+		// bookmark: this should be a returned object from dojo.html.selection.getBookmark()
+		var _document = dojo.doc();
+		if(_document["selection"]){ // IE
+			var range = _document.selection.createRange();
+			 range.moveToBookmark(bookmark);
+			 range.select();
+		}else{ //Moz/W3C
+			var selection;
+			try {selection = dojo.global().getSelection();}
+			catch (e) {}
+			if(selection && selection['removeAllRanges']){
+				selection.removeAllRanges() ;
+				selection.addRange(bookmark) ;
+			}else{
+				dojo.debug("No idea how to restore selection for this browser!");
+			}
+		}
+	},
+	collapse: function(/*Boolean*/beginning) {
+		// summary: clear current selection
+		if(dojo.global()['getSelection']){
+			var selection = dojo.global().getSelection();
+			if(selection.removeAllRanges){ // Mozilla
+				if(beginning){
+					selection.collapseToStart();
+				}else{
+					selection.collapseToEnd();
+				}
+			}else{ // Safari
+				// pulled from WebCore/ecma/kjs_window.cpp, line 2536
+				 dojo.global().getSelection().collapse(beginning);
+			}
+		}else if(dojo.doc().selection){ // IE
+			var range = dojo.doc().selection.createRange();
+			range.collapse(beginning);
+			range.select();
+		}
+	},
+	remove: function() {
+		// summary: delete current selection
+		if(dojo.doc().selection) { //IE
+			var selection = dojo.doc().selection;
+
+			if ( selection.type.toUpperCase() != "NONE" ){
+				selection.clear();
+			}
+		
+			return selection;
+		}else{
+			var selection = dojo.global().getSelection();
+
+			for ( var i = 0; i < selection.rangeCount; i++ ){
+				selection.getRangeAt(i).deleteContents();
+			}
+		
+			return selection;
+		}
+	}
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/shadow.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/shadow.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/shadow.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,16 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.html.shadow");
+
+dojo.require("dojo.lfx.shadow");
+dojo.deprecated("dojo.html.shadow has been moved to dojo.lfx.", "0.5");
+
+dojo.html.shadow = dojo.lfx.shadow;

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/style.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/style.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/style.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,577 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.html.style");
+dojo.require("dojo.uri.Uri");
+
+dojo.html.getClass = function(/* HTMLElement */node){
+	//	summary
+	//	Returns the string value of the list of CSS classes currently assigned directly 
+	//	to the node in question. Returns an empty string if no class attribute is found;
+	node = dojo.byId(node);
+	if(!node){ return ""; }
+	var cs = "";
+	if(node.className){
+		cs = node.className;
+	}else if(dojo.html.hasAttribute(node, "class")){
+		cs = dojo.html.getAttribute(node, "class");
+	}
+	return cs.replace(/^\s+|\s+$/g, "");	//	string
+}
+
+dojo.html.getClasses = function(/* HTMLElement */node) {
+	//	summary
+	//	Returns an array of CSS classes currently assigned directly to the node in question. 
+	//	Returns an empty array if no classes are found;
+	var c = dojo.html.getClass(node);
+	return (c == "") ? [] : c.split(/\s+/g);	//	array
+}
+
+dojo.html.hasClass = function(/* HTMLElement */node, /* string */classname){
+	//	summary
+	//	Returns whether or not the specified classname is a portion of the
+	//	class list currently applied to the node. Does not cover cascaded
+	//	styles, only classes directly applied to the node.
+	return (new RegExp('(^|\\s+)'+classname+'(\\s+|$)')).test(dojo.html.getClass(node))	//	boolean
+}
+
+dojo.html.prependClass = function(/* HTMLElement */node, /* string */classStr){
+	//	summary
+	//	Adds the specified class to the beginning of the class list on the
+	//	passed node. This gives the specified class the highest precidence
+	//	when style cascading is calculated for the node. Returns true or
+	//	false; indicating success or failure of the operation, respectively.
+	classStr += " " + dojo.html.getClass(node);
+	return dojo.html.setClass(node, classStr);	//	boolean
+}
+
+dojo.html.addClass = function(/* HTMLElement */node, /* string */classStr){
+	//	summary
+	//	Adds the specified class to the end of the class list on the
+	//	passed &node;. Returns &true; or &false; indicating success or failure.
+	if (dojo.html.hasClass(node, classStr)) {
+	  return false;
+	}
+	classStr = (dojo.html.getClass(node) + " " + classStr).replace(/^\s+|\s+$/g,"");
+	return dojo.html.setClass(node, classStr);	//	boolean
+}
+
+dojo.html.setClass = function(/* HTMLElement */node, /* string */classStr){
+	//	summary
+	//	Clobbers the existing list of classes for the node, replacing it with
+	//	the list given in the 2nd argument. Returns true or false
+	//	indicating success or failure.
+	node = dojo.byId(node);
+	var cs = new String(classStr);
+	try{
+		if(typeof node.className == "string"){
+			node.className = cs;
+		}else if(node.setAttribute){
+			node.setAttribute("class", classStr);
+			node.className = cs;
+		}else{
+			return false;
+		}
+	}catch(e){
+		dojo.debug("dojo.html.setClass() failed", e);
+	}
+	return true;
+}
+
+dojo.html.removeClass = function(/* HTMLElement */node, /* string */classStr, /* boolean? */allowPartialMatches){
+	//	summary
+	//	Removes the className from the node;. Returns true or false indicating success or failure.
+	try{
+		if (!allowPartialMatches) {
+			var newcs = dojo.html.getClass(node).replace(new RegExp('(^|\\s+)'+classStr+'(\\s+|$)'), "$1$2");
+		} else {
+			var newcs = dojo.html.getClass(node).replace(classStr,'');
+		}
+		dojo.html.setClass(node, newcs);
+	}catch(e){
+		dojo.debug("dojo.html.removeClass() failed", e);
+	}
+	return true;	//	boolean
+}
+
+dojo.html.replaceClass = function(/* HTMLElement */node, /* string */newClass, /* string */oldClass) {
+	//	summary
+	//	Replaces 'oldClass' and adds 'newClass' to node
+	dojo.html.removeClass(node, oldClass);
+	dojo.html.addClass(node, newClass);
+}
+
+// Enum type for getElementsByClass classMatchType arg:
+dojo.html.classMatchType = {
+	ContainsAll : 0, // all of the classes are part of the node's class (default)
+	ContainsAny : 1, // any of the classes are part of the node's class
+	IsOnly : 2 // only all of the classes are part of the node's class
+}
+
+
+dojo.html.getElementsByClass = function(
+	/* string */classStr, 
+	/* HTMLElement? */parent, 
+	/* string? */nodeType, 
+	/* integer? */classMatchType, 
+	/* boolean? */useNonXpath
+){
+	//	summary
+	//	Returns an array of nodes for the given classStr, children of a
+	//	parent, and optionally of a certain nodeType
+	// FIXME: temporarily set to false because of several dojo tickets related
+	// to the xpath version not working consistently in firefox.
+	useNonXpath = false;
+	var _document = dojo.doc();
+	parent = dojo.byId(parent) || _document;
+	var classes = classStr.split(/\s+/g);
+	var nodes = [];
+	if( classMatchType != 1 && classMatchType != 2 ) classMatchType = 0; // make it enum
+	var reClass = new RegExp("(\\s|^)((" + classes.join(")|(") + "))(\\s|$)");
+	var srtLength = classes.join(" ").length;
+	var candidateNodes = [];
+	
+	if(!useNonXpath && _document.evaluate) { // supports dom 3 xpath
+		var xpath = ".//" + (nodeType || "*") + "[contains(";
+		if(classMatchType != dojo.html.classMatchType.ContainsAny){
+			xpath += "concat(' ', at class,' '), ' " +
+			classes.join(" ') and contains(concat(' ', at class,' '), ' ") +
+			" ')";
+			if (classMatchType == 2) {
+				xpath += " and string-length(@class)="+srtLength+"]";
+			}else{
+				xpath += "]";
+			}
+		}else{
+			xpath += "concat(' ', at class,' '), ' " +
+			classes.join(" ') or contains(concat(' ', at class,' '), ' ") +
+			" ')]";
+		}
+		var xpathResult = _document.evaluate(xpath, parent, null, XPathResult.ANY_TYPE, null);
+		var result = xpathResult.iterateNext();
+		while(result){
+			try{
+				candidateNodes.push(result);
+				result = xpathResult.iterateNext();
+			}catch(e){ break; }
+		}
+		return candidateNodes;	//	NodeList
+	}else{
+		if(!nodeType){
+			nodeType = "*";
+		}
+		candidateNodes = parent.getElementsByTagName(nodeType);
+
+		var node, i = 0;
+		outer:
+		while(node = candidateNodes[i++]){
+			var nodeClasses = dojo.html.getClasses(node);
+			if(nodeClasses.length == 0){ continue outer; }
+			var matches = 0;
+	
+			for(var j = 0; j < nodeClasses.length; j++){
+				if(reClass.test(nodeClasses[j])){
+					if(classMatchType == dojo.html.classMatchType.ContainsAny){
+						nodes.push(node);
+						continue outer;
+					}else{
+						matches++;
+					}
+				}else{
+					if(classMatchType == dojo.html.classMatchType.IsOnly){
+						continue outer;
+					}
+				}
+			}
+	
+			if(matches == classes.length){
+				if(	(classMatchType == dojo.html.classMatchType.IsOnly)&&
+					(matches == nodeClasses.length)){
+					nodes.push(node);
+				}else if(classMatchType == dojo.html.classMatchType.ContainsAll){
+					nodes.push(node);
+				}
+			}
+		}
+		return nodes;	//	NodeList
+	}
+}
+dojo.html.getElementsByClassName = dojo.html.getElementsByClass;
+
+dojo.html.toCamelCase = function(/* string */selector){
+	//	summary
+	//	Translates a CSS selector string to a camel-cased one.
+	var arr = selector.split('-'), cc = arr[0];
+	for(var i = 1; i < arr.length; i++) {
+		cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
+	}
+	return cc;	//	string
+}
+
+dojo.html.toSelectorCase = function(/* string */selector){
+	//	summary
+	//	Translates a camel cased string to a selector cased one.
+	return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase();	//	string
+}
+
+dojo.html.getComputedStyle = function(/* HTMLElement */node, /* string */cssSelector, /* integer? */inValue){
+	//	summary
+	//	Returns the computed style of cssSelector on node.
+	node = dojo.byId(node);
+	// cssSelector may actually be in camel case, so force selector version
+	var cssSelector = dojo.html.toSelectorCase(cssSelector);
+	var property = dojo.html.toCamelCase(cssSelector);
+	if(!node || !node.style){
+		return inValue;			
+	} else if (document.defaultView && dojo.html.isDescendantOf(node, node.ownerDocument)){ // W3, gecko, KHTML
+		try{
+			// mozilla segfaults when margin-* and node is removed from doc
+			// FIXME: need to figure out a if there is quicker workaround
+			var cs = document.defaultView.getComputedStyle(node, "");
+			if(cs){
+				return cs.getPropertyValue(cssSelector);	//	integer
+			} 
+		}catch(e){ // reports are that Safari can throw an exception above
+			if(node.style.getPropertyValue){ // W3
+				return node.style.getPropertyValue(cssSelector);	//	integer
+			} else {
+				return inValue;	//	integer
+			}
+		}
+	} else if(node.currentStyle){ // IE
+		return node.currentStyle[property];	//	integer
+	}
+	
+	if(node.style.getPropertyValue){ // W3
+		return node.style.getPropertyValue(cssSelector);	//	integer
+	}else{
+		return inValue;	//	integer
+	}
+}
+
+dojo.html.getStyleProperty = function(/* HTMLElement */node, /* string */cssSelector){
+	//	summary
+	//	Returns the value of the passed style
+	node = dojo.byId(node);
+	return (node && node.style ? node.style[dojo.html.toCamelCase(cssSelector)] : undefined);	//	string
+}
+
+dojo.html.getStyle = function(/* HTMLElement */node, /* string */cssSelector){
+	//	summary
+	//	Returns the computed value of the passed style
+	var value = dojo.html.getStyleProperty(node, cssSelector);
+	return (value ? value : dojo.html.getComputedStyle(node, cssSelector));	//	string || integer
+}
+
+dojo.html.setStyle = function(/* HTMLElement */node, /* string */cssSelector, /* string */value){
+	//	summary
+	//	Set the value of passed style on node
+	node = dojo.byId(node);
+	if(node && node.style){
+		var camelCased = dojo.html.toCamelCase(cssSelector);
+		node.style[camelCased] = value;
+	}
+}
+
+dojo.html.setStyleText = function (/* HTMLElement */target, /* string */text) {
+	//	summary
+	//	Try to set the entire cssText property of the passed target; equiv of setting style attribute.
+	try {
+	 	target.style.cssText = text;
+	} catch (e) {
+		target.setAttribute("style", text);
+	}
+}
+
+dojo.html.copyStyle = function(/* HTMLElement */target, /* HTMLElement */source){
+	//	summary
+	// work around for opera which doesn't have cssText, and for IE which fails on setAttribute 
+	if(!source.style.cssText){ 
+		target.setAttribute("style", source.getAttribute("style")); 
+	}else{
+		target.style.cssText = source.style.cssText; 
+	}
+	dojo.html.addClass(target, dojo.html.getClass(source));
+}
+
+dojo.html.getUnitValue = function(/* HTMLElement */node, /* string */cssSelector, /* boolean? */autoIsZero){
+	//	summary
+	//	Get the value of passed selector, with the specific units used
+	var s = dojo.html.getComputedStyle(node, cssSelector);
+	if((!s)||((s == 'auto')&&(autoIsZero))){ 
+		return { value: 0, units: 'px' };	//	object 
+	}
+	// FIXME: is regex inefficient vs. parseInt or some manual test? 
+	var match = s.match(/(\-?[\d.]+)([a-z%]*)/i);
+	if (!match){return dojo.html.getUnitValue.bad;}
+	return { value: Number(match[1]), units: match[2].toLowerCase() };	//	object
+}
+dojo.html.getUnitValue.bad = { value: NaN, units: '' };
+
+dojo.html.getPixelValue = function(/* HTMLElement */node, /* string */cssSelector, /* boolean? */autoIsZero){
+	//	summary
+	//	Get the value of passed selector in pixels.
+	var result = dojo.html.getUnitValue(node, cssSelector, autoIsZero);
+	// FIXME: there is serious debate as to whether or not this is the right solution
+	if(isNaN(result.value)){ 
+		return 0; //	integer 
+	}	
+	// FIXME: code exists for converting other units to px (see Dean Edward's IE7) 
+	// but there are cross-browser complexities
+	if((result.value)&&(result.units != 'px')){ 
+		return NaN;	//	integer 
+	}
+	return result.value;	//	integer
+}
+
+dojo.html.setPositivePixelValue = function(/* HTMLElement */node, /* string */selector, /* integer */value){
+	//	summary
+	//	Attempt to set the value of selector on node as a positive pixel value.
+	if(isNaN(value)){return false;}
+	node.style[selector] = Math.max(0, value) + 'px'; 
+	return true;	//	boolean
+}
+
+dojo.html.styleSheet = null;
+
+// FIXME: this is a really basic stub for adding and removing cssRules, but
+// it assumes that you know the index of the cssRule that you want to add 
+// or remove, making it less than useful.  So we need something that can 
+// search for the selector that you you want to remove.
+dojo.html.insertCssRule = function(/* string */selector, /* string */declaration, /* integer? */index) {
+	//	summary
+	//	Attempt to insert declaration as selector on the internal stylesheet; if index try to set it there.
+	if (!dojo.html.styleSheet) {
+		if (document.createStyleSheet) { // IE
+			dojo.html.styleSheet = document.createStyleSheet();
+		} else if (document.styleSheets[0]) { // rest
+			// FIXME: should create a new style sheet here
+			// fall back on an exsiting style sheet
+			dojo.html.styleSheet = document.styleSheets[0];
+		} else { 
+			return null;	//	integer 
+		} // fail
+	}
+
+	if (arguments.length < 3) { // index may == 0
+		if (dojo.html.styleSheet.cssRules) { // W3
+			index = dojo.html.styleSheet.cssRules.length;
+		} else if (dojo.html.styleSheet.rules) { // IE
+			index = dojo.html.styleSheet.rules.length;
+		} else { 
+			return null;	//	integer 
+		} // fail
+	}
+
+	if (dojo.html.styleSheet.insertRule) { // W3
+		var rule = selector + " { " + declaration + " }";
+		return dojo.html.styleSheet.insertRule(rule, index);	//	integer
+	} else if (dojo.html.styleSheet.addRule) { // IE
+		return dojo.html.styleSheet.addRule(selector, declaration, index);	//	integer
+	} else { 
+		return null; // integer
+	} // fail
+}
+
+dojo.html.removeCssRule = function(/* integer? */index){
+	//	summary
+	//	Attempt to remove the rule at index.
+	if(!dojo.html.styleSheet){
+		dojo.debug("no stylesheet defined for removing rules");
+		return false;
+	}
+	if(dojo.render.html.ie){
+		if(!index){
+			index = dojo.html.styleSheet.rules.length;
+			dojo.html.styleSheet.removeRule(index);
+		}
+	}else if(document.styleSheets[0]){
+		if(!index){
+			index = dojo.html.styleSheet.cssRules.length;
+		}
+		dojo.html.styleSheet.deleteRule(index);
+	}
+	return true;	//	boolean
+}
+
+dojo.html._insertedCssFiles = []; // cache container needed because IE reformats cssText when added to DOM
+dojo.html.insertCssFile = function(/* string */URI, /* HTMLDocument? */doc, /* boolean? */checkDuplicates, /* boolean */fail_ok){
+	//	summary
+	// calls css by XmlHTTP and inserts it into DOM as <style [widgetType="widgetType"]> *downloaded cssText*</style>
+	if(!URI){ return; }
+	if(!doc){ doc = document; }
+	var cssStr = dojo.hostenv.getText(URI, false, fail_ok);
+  if(cssStr===null){ return; } 
+	cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
+
+	if(checkDuplicates){
+		var idx = -1, node, ent = dojo.html._insertedCssFiles;
+		for(var i = 0; i < ent.length; i++){
+			if((ent[i].doc == doc) && (ent[i].cssText == cssStr)){
+				idx = i; node = ent[i].nodeRef;
+				break;
+			}
+		}
+		// make sure we havent deleted our node
+		if(node){
+			var styles = doc.getElementsByTagName("style");
+			for(var i = 0; i < styles.length; i++){
+				if(styles[i] == node){
+					return;
+				}
+			}
+			// delete this entry
+			dojo.html._insertedCssFiles.shift(idx, 1);
+		}
+	}
+
+	var style = dojo.html.insertCssText(cssStr);
+	dojo.html._insertedCssFiles.push({'doc': doc, 'cssText': cssStr, 'nodeRef': style});
+
+	// insert custom attribute ex dbgHref="../foo.css" usefull when debugging in DOM inspectors, no?
+	if(style && djConfig.isDebug){
+		style.setAttribute("dbgHref", URI);
+	}
+	return style;	//	HTMLStyleElement
+}
+
+dojo.html.insertCssText = function(/* string */cssStr, /* HTMLDocument? */doc, /* string? */URI){
+	//	summary
+	//	Attempt to insert CSS rules into the document through inserting a style element
+	// DomNode Style  = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ])
+	if(!cssStr){ 
+		return; //	HTMLStyleElement
+	}
+	if(!doc){ doc = document; }
+	if(URI){// fix paths in cssStr
+		cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
+	}
+	var style = doc.createElement("style");
+	style.setAttribute("type", "text/css");
+	// IE is b0rken enough to require that we add the element to the doc
+	// before changing it's properties
+	var head = doc.getElementsByTagName("head")[0];
+	if(!head){ // must have a head tag 
+		dojo.debug("No head tag in document, aborting styles");
+		return;	//	HTMLStyleElement
+	}else{
+		head.appendChild(style);
+	}
+	if(style.styleSheet){// IE
+		style.styleSheet.cssText = cssStr;
+	}else{ // w3c
+		var cssText = doc.createTextNode(cssStr);
+		style.appendChild(cssText);
+	}
+	return style;	//	HTMLStyleElement
+}
+
+dojo.html.fixPathsInCssText = function(/* string */cssStr, /* string */URI){
+	//	summary
+	// usage: cssText comes from dojoroot/src/widget/templates/Foobar.css
+	// 	it has .dojoFoo { background-image: url(images/bar.png);} then uri should point to dojoroot/src/widget/templates/
+	function iefixPathsInCssText() {
+		var regexIe = /AlphaImageLoader\(src\=['"]([\t\s\w()\/.\\'"-:#=&?~]*)['"]/;
+		while(match = regexIe.exec(cssStr)){
+			url = match[1].replace(regexTrim, "$2");
+			if(!regexProtocol.exec(url)){
+				url = (new dojo.uri.Uri(URI, url).toString());
+			}
+			str += cssStr.substring(0, match.index) + "AlphaImageLoader(src='" + url + "'";
+			cssStr = cssStr.substr(match.index + match[0].length);
+		}
+		return str + cssStr;
+	}
+
+	if(!cssStr || !URI){ return; }
+	var match, str = "", url = "";
+	var regex = /url\(\s*([\t\s\w()\/.\\'"-:#=&?]+)\s*\)/;
+	var regexProtocol = /(file|https?|ftps?):\/\//;
+	var regexTrim = /^[\s]*(['"]?)([\w()\/.\\'"-:#=&?]*)\1[\s]*?$/;
+	if (dojo.render.html.ie55 || dojo.render.html.ie60) {
+		cssStr = iefixPathsInCssText();
+	}
+	while(match = regex.exec(cssStr)){
+		url = match[1].replace(regexTrim, "$2");
+		if(!regexProtocol.exec(url)){
+			url = (new dojo.uri.Uri(URI, url).toString());
+		}
+		str += cssStr.substring(0, match.index) + "url(" + url + ")";
+		cssStr = cssStr.substr(match.index + match[0].length);
+	}
+	return str + cssStr;	//	string
+}
+
+dojo.html.setActiveStyleSheet = function(/* string */title){
+	//	summary
+	//	Activate style sheet with specified title.
+	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
+	while (a = els[i++]) {
+		if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")){
+			a.disabled = true;
+			if (a.getAttribute("title") == title) { a.disabled = false; }
+		}
+	}
+}
+
+dojo.html.getActiveStyleSheet = function(){
+	//	summary
+	//	return the title of the currently active stylesheet
+	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
+	while (a = els[i++]) {
+		if (a.getAttribute("rel").indexOf("style") != -1 
+			&& a.getAttribute("title") 
+			&& !a.disabled
+		){
+			return a.getAttribute("title");	//	string 
+		}
+	}
+	return null;	//	string
+}
+
+dojo.html.getPreferredStyleSheet = function(){
+	//	summary
+	//	Return the preferred stylesheet title (i.e. link without alt attribute)
+	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
+	while (a = els[i++]) {
+		if(a.getAttribute("rel").indexOf("style") != -1
+			&& a.getAttribute("rel").indexOf("alt") == -1
+			&& a.getAttribute("title")
+		){ 
+			return a.getAttribute("title"); 	//	string
+		}
+	}
+	return null;	//	string
+}
+
+dojo.html.applyBrowserClass = function(/* HTMLElement */node){
+	//	summary
+	//	Applies pre-set class names based on browser & version to the passed node.
+	//	Modified version of Morris' CSS hack.
+	var drh=dojo.render.html;
+	var classes = {
+		dj_ie: drh.ie,
+		dj_ie55: drh.ie55,
+		dj_ie6: drh.ie60,
+		dj_ie7: drh.ie70,
+		dj_iequirks: drh.ie && drh.quirks,
+		dj_opera: drh.opera,
+		dj_opera8: drh.opera && (Math.floor(dojo.render.version)==8),
+		dj_opera9: drh.opera && (Math.floor(dojo.render.version)==9),
+		dj_khtml: drh.khtml,
+		dj_safari: drh.safari,
+		dj_gecko: drh.mozilla
+	}; // no dojo unsupported browsers
+	for(var p in classes){
+		if(classes[p]){
+			dojo.html.addClass(node, p);
+		}
+	}
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/util.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/util.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html/util.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,484 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.html.util");
+dojo.require("dojo.html.layout");
+
+dojo.html.getElementWindow = function(/* HTMLElement */element){
+	//	summary
+	// 	Get the window object where the element is placed in.
+	return dojo.html.getDocumentWindow( element.ownerDocument );	//	Window
+}
+
+dojo.html.getDocumentWindow = function(doc){
+	//	summary
+	// 	Get window object associated with document doc
+
+	// With Safari, there is not wa to retrieve the window from the document, so we must fix it.
+	if(dojo.render.html.safari && !doc._parentWindow){
+		/*
+			This is a Safari specific function that fix the reference to the parent
+			window from the document object.
+		*/
+
+		var fix=function(win){
+			win.document._parentWindow=win;
+			for(var i=0; i<win.frames.length; i++){
+				fix(win.frames[i]);
+			}
+		}
+		fix(window.top);
+	}
+
+	//In some IE versions (at least 6.0), document.parentWindow does not return a
+	//reference to the real window object (maybe a copy), so we must fix it as well
+	//We use IE specific execScript to attach the real window reference to
+	//document._parentWindow for later use
+	if(dojo.render.html.ie && window !== document.parentWindow && !doc._parentWindow){
+		/*
+		In IE 6, only the variable "window" can be used to connect events (others
+		may be only copies).
+		*/
+		doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
+		//to prevent memory leak, unset it after use
+		//another possibility is to add an onUnload handler which seems overkill to me (liucougar)
+		var win = doc._parentWindow;
+		doc._parentWindow = null;
+		return win;	//	Window
+	}
+
+	return doc._parentWindow || doc.parentWindow || doc.defaultView;	//	Window
+}
+
+dojo.html.gravity = function(/* HTMLElement */node, /* DOMEvent */e){
+	//	summary
+	//	Calculates the mouse's direction of gravity relative to the centre
+	//	of the given node.
+	//	<p>
+	//	If you wanted to insert a node into a DOM tree based on the mouse
+	//	position you might use the following code:
+	//	<pre>
+	//	if (gravity(node, e) & gravity.NORTH) { [insert before]; }
+	//	else { [insert after]; }
+	//	</pre>
+	//
+	//	@param node The node
+	//	@param e		The event containing the mouse coordinates
+	//	@return		 The directions, NORTH or SOUTH and EAST or WEST. These
+	//						 are properties of the function.
+	node = dojo.byId(node);
+	var mouse = dojo.html.getCursorPosition(e);
+
+	with (dojo.html) {
+		var absolute = getAbsolutePosition(node, true);
+		var bb = getBorderBox(node);
+		var nodecenterx = absolute.x + (bb.width / 2);
+		var nodecentery = absolute.y + (bb.height / 2);
+	}
+
+	with (dojo.html.gravity) {
+		return ((mouse.x < nodecenterx ? WEST : EAST) | (mouse.y < nodecentery ? NORTH : SOUTH));	//	integer
+	}
+}
+
+dojo.html.gravity.NORTH = 1;
+dojo.html.gravity.SOUTH = 1 << 1;
+dojo.html.gravity.EAST = 1 << 2;
+dojo.html.gravity.WEST = 1 << 3;
+
+dojo.html.overElement = function(/* HTMLElement */element, /* DOMEvent */e){
+	//	summary
+	//	Returns whether the mouse is over the passed element.
+	element = dojo.byId(element);
+	var mouse = dojo.html.getCursorPosition(e);
+	var bb = dojo.html.getBorderBox(element);
+	var absolute = dojo.html.getAbsolutePosition(element, true, dojo.html.boxSizing.BORDER_BOX);
+	var top = absolute.y;
+	var bottom = top + bb.height;
+	var left = absolute.x;
+	var right = left + bb.width;
+
+	return (mouse.x >= left
+		&& mouse.x <= right
+		&& mouse.y >= top
+		&& mouse.y <= bottom
+	);	//	boolean
+}
+
+dojo.html.renderedTextContent = function(/* HTMLElement */node){
+	//	summary
+	//	Attempts to return the text as it would be rendered, with the line breaks
+	//	sorted out nicely. Unfinished.
+	node = dojo.byId(node);
+	var result = "";
+	if (node == null) { return result; }
+	for (var i = 0; i < node.childNodes.length; i++) {
+		switch (node.childNodes[i].nodeType) {
+			case 1: // ELEMENT_NODE
+			case 5: // ENTITY_REFERENCE_NODE
+				var display = "unknown";
+				try {
+					display = dojo.html.getStyle(node.childNodes[i], "display");
+				} catch(E) {}
+				switch (display) {
+					case "block": case "list-item": case "run-in":
+					case "table": case "table-row-group": case "table-header-group":
+					case "table-footer-group": case "table-row": case "table-column-group":
+					case "table-column": case "table-cell": case "table-caption":
+						// TODO: this shouldn't insert double spaces on aligning blocks
+						result += "\n";
+						result += dojo.html.renderedTextContent(node.childNodes[i]);
+						result += "\n";
+						break;
+
+					case "none": break;
+
+					default:
+						if(node.childNodes[i].tagName && node.childNodes[i].tagName.toLowerCase() == "br") {
+							result += "\n";
+						} else {
+							result += dojo.html.renderedTextContent(node.childNodes[i]);
+						}
+						break;
+				}
+				break;
+			case 3: // TEXT_NODE
+			case 2: // ATTRIBUTE_NODE
+			case 4: // CDATA_SECTION_NODE
+				var text = node.childNodes[i].nodeValue;
+				var textTransform = "unknown";
+				try {
+					textTransform = dojo.html.getStyle(node, "text-transform");
+				} catch(E) {}
+				switch (textTransform){
+					case "capitalize":
+						var words = text.split(' ');
+						for(var i=0; i<words.length; i++){
+							words[i] = words[i].charAt(0).toUpperCase() + words[i].substring(1);
+						}
+						text = words.join(" ");
+						break;
+					case "uppercase": text = text.toUpperCase(); break;
+					case "lowercase": text = text.toLowerCase(); break;
+					default: break; // leave as is
+				}
+				// TODO: implement
+				switch (textTransform){
+					case "nowrap": break;
+					case "pre-wrap": break;
+					case "pre-line": break;
+					case "pre": break; // leave as is
+					default:
+						// remove whitespace and collapse first space
+						text = text.replace(/\s+/, " ");
+						if (/\s$/.test(result)) { text.replace(/^\s/, ""); }
+						break;
+				}
+				result += text;
+				break;
+			default:
+				break;
+		}
+	}
+	return result;	//	string
+}
+
+dojo.html.createNodesFromText = function(/* string */txt, /* boolean? */trim){
+	//	summary
+	//	Attempts to create a set of nodes based on the structure of the passed text.
+	if(trim) { txt = txt.replace(/^\s+|\s+$/g, ""); }
+
+	var tn = dojo.doc().createElement("div");
+	// tn.style.display = "none";
+	tn.style.visibility= "hidden";
+	dojo.body().appendChild(tn);
+	var tableType = "none";
+	if((/^<t[dh][\s\r\n>]/i).test(txt.replace(/^\s+/))) {
+		txt = "<table><tbody><tr>" + txt + "</tr></tbody></table>";
+		tableType = "cell";
+	} else if((/^<tr[\s\r\n>]/i).test(txt.replace(/^\s+/))) {
+		txt = "<table><tbody>" + txt + "</tbody></table>";
+		tableType = "row";
+	} else if((/^<(thead|tbody|tfoot)[\s\r\n>]/i).test(txt.replace(/^\s+/))) {
+		txt = "<table>" + txt + "</table>";
+		tableType = "section";
+	}
+	tn.innerHTML = txt;
+	if(tn["normalize"]){
+		tn.normalize();
+	}
+
+	var parent = null;
+	switch(tableType) {
+		case "cell":
+			parent = tn.getElementsByTagName("tr")[0];
+			break;
+		case "row":
+			parent = tn.getElementsByTagName("tbody")[0];
+			break;
+		case "section":
+			parent = tn.getElementsByTagName("table")[0];
+			break;
+		default:
+			parent = tn;
+			break;
+	}
+
+	/* this doesn't make much sense, I'm assuming it just meant trim() so wrap was replaced with trim
+	if(wrap){
+		var ret = [];
+		// start hack
+		var fc = tn.firstChild;
+		ret[0] = ((fc.nodeValue == " ")||(fc.nodeValue == "\t")) ? fc.nextSibling : fc;
+		// end hack
+		// tn.style.display = "none";
+		dojo.body().removeChild(tn);
+		return ret;
+	}
+	*/
+	var nodes = [];
+	for(var x=0; x<parent.childNodes.length; x++){
+		nodes.push(parent.childNodes[x].cloneNode(true));
+	}
+	tn.style.display = "none"; // FIXME: why do we do this?
+	dojo.body().removeChild(tn);
+	return nodes;	//	array
+}
+
+dojo.html.placeOnScreen = function(
+	/* HTMLElement */node,
+	/* integer */desiredX,
+	/* integer */desiredY,
+	/* integer */padding,
+	/* boolean? */hasScroll,
+	/* string? */corners,
+	/* boolean? */tryOnly
+){
+	//	summary
+	//	Keeps 'node' in the visible area of the screen while trying to
+	//	place closest to desiredX, desiredY. The input coordinates are
+	//	expected to be the desired screen position, not accounting for
+	//	scrolling. If you already accounted for scrolling, set 'hasScroll'
+	//	to true. Set padding to either a number or array for [paddingX, paddingY]
+	//	to put some buffer around the element you want to position.
+	//	Set which corner(s) you want to bind to, such as
+	//
+	//	placeOnScreen(node, desiredX, desiredY, padding, hasScroll, "TR")
+	//	placeOnScreen(node, [desiredX, desiredY], padding, hasScroll, ["TR", "BL"])
+	//
+	//	The desiredX/desiredY will be treated as the topleft(TL)/topright(TR) or
+	//	BottomLeft(BL)/BottomRight(BR) corner of the node. Each corner is tested
+	//	and if a perfect match is found, it will be used. Otherwise, it goes through
+	//	all of the specified corners, and choose the most appropriate one.
+	//	By default, corner = ['TL'].
+	//	If tryOnly is set to true, the node will not be moved to the place.
+	//
+	//	NOTE: node is assumed to be absolutely or relatively positioned.
+	//
+	//	Alternate call sig:
+	//	 placeOnScreen(node, [x, y], padding, hasScroll)
+	//
+	//	Examples:
+	//	 placeOnScreen(node, 100, 200)
+	//	 placeOnScreen("myId", [800, 623], 5)
+	//	 placeOnScreen(node, 234, 3284, [2, 5], true)
+
+	// TODO: make this function have variable call sigs
+	//	kes(node, ptArray, cornerArray, padding, hasScroll)
+	//	kes(node, ptX, ptY, cornerA, cornerB, cornerC, paddingArray, hasScroll)
+	if(desiredX instanceof Array || typeof desiredX == "array") {
+		tryOnly = corners;
+		corners = hasScroll;
+		hasScroll = padding;
+		padding = desiredY;
+		desiredY = desiredX[1];
+		desiredX = desiredX[0];
+	}
+
+	if(corners instanceof String || typeof corners == "string"){
+		corners = corners.split(",");
+	}
+
+	if(!isNaN(padding)) {
+		padding = [Number(padding), Number(padding)];
+	} else if(!(padding instanceof Array || typeof padding == "array")) {
+		padding = [0, 0];
+	}
+
+	var scroll = dojo.html.getScroll().offset;
+	var view = dojo.html.getViewport();
+
+	node = dojo.byId(node);
+	var oldDisplay = node.style.display;
+	node.style.display="";
+	var bb = dojo.html.getBorderBox(node);
+	var w = bb.width;
+	var h = bb.height;
+	node.style.display=oldDisplay;
+
+	if(!(corners instanceof Array || typeof corners == "array")){
+		corners = ['TL'];
+	}
+
+	var bestx, besty, bestDistance = Infinity, bestCorner;
+
+	for(var cidex=0; cidex<corners.length; ++cidex){
+		var corner = corners[cidex];
+		var match = true;
+		var tryX = desiredX - (corner.charAt(1)=='L' ? 0 : w) + padding[0]*(corner.charAt(1)=='L' ? 1 : -1);
+		var tryY = desiredY - (corner.charAt(0)=='T' ? 0 : h) + padding[1]*(corner.charAt(0)=='T' ? 1 : -1);
+		if(hasScroll) {
+			tryX -= scroll.x;
+			tryY -= scroll.y;
+		}
+
+		if(tryX < 0){
+			tryX = 0;
+			match = false;
+		}
+
+		if(tryY < 0){
+			tryY = 0;
+			match = false;
+		}
+
+		var x = tryX + w;
+		if(x > view.width) {
+			x = view.width - w;
+			match = false;
+		} else {
+			x = tryX;
+		}
+		x = Math.max(padding[0], x) + scroll.x;
+
+		var y = tryY + h;
+		if(y > view.height) {
+			y = view.height - h;
+			match = false;
+		} else {
+			y = tryY;
+		}
+		y = Math.max(padding[1], y) + scroll.y;
+
+		if(match){ //perfect match, return now
+			bestx = x;
+			besty = y;
+			bestDistance = 0;
+			bestCorner = corner;
+			break;
+		}else{
+			//not perfect, find out whether it is better than the saved one
+			var dist = Math.pow(x-tryX-scroll.x,2)+Math.pow(y-tryY-scroll.y,2);
+			if(bestDistance > dist){
+				bestDistance = dist;
+				bestx = x;
+				besty = y;
+				bestCorner = corner;
+			}
+		}
+	}
+
+	if(!tryOnly){
+		node.style.left = bestx + "px";
+		node.style.top = besty + "px";
+	}
+
+	return { left: bestx, top: besty, x: bestx, y: besty, dist: bestDistance, corner:  bestCorner};	//	object
+}
+
+dojo.html.placeOnScreenPoint = function(node, desiredX, desiredY, padding, hasScroll) {
+	dojo.deprecated("dojo.html.placeOnScreenPoint", "use dojo.html.placeOnScreen() instead", "0.5");
+	return dojo.html.placeOnScreen(node, desiredX, desiredY, padding, hasScroll, ['TL', 'TR', 'BL', 'BR']);
+}
+
+dojo.html.placeOnScreenAroundElement = function(
+	/* HTMLElement */node,
+	/* HTMLElement */aroundNode,
+	/* integer */padding,
+	/* string? */aroundType,
+	/* string? */aroundCorners,
+	/* boolean? */tryOnly
+){
+	//	summary
+	//	Like placeOnScreen, except it accepts aroundNode instead of x,y
+	//	and attempts to place node around it. aroundType (see
+	//	dojo.html.boxSizing in html/layout.js) determines which box of the
+	//	aroundNode should be used to calculate the outer box.
+	//	aroundCorners specify Which corner of aroundNode should be
+	//	used to place the node => which corner(s) of node to use (see the
+	//	corners parameter in dojo.html.placeOnScreen)
+	//	aroundCorners: {'TL': 'BL', 'BL': 'TL'}
+
+	var best, bestDistance=Infinity;
+	aroundNode = dojo.byId(aroundNode);
+	var oldDisplay = aroundNode.style.display;
+	aroundNode.style.display="";
+	var mb = dojo.html.getElementBox(aroundNode, aroundType);
+	var aroundNodeW = mb.width;
+	var aroundNodeH = mb.height;
+	var aroundNodePos = dojo.html.getAbsolutePosition(aroundNode, true, aroundType);
+	aroundNode.style.display=oldDisplay;
+
+	for(var nodeCorner in aroundCorners){
+		var pos, desiredX, desiredY;
+		var corners = aroundCorners[nodeCorner];
+
+		desiredX = aroundNodePos.x + (nodeCorner.charAt(1)=='L' ? 0 : aroundNodeW);
+		desiredY = aroundNodePos.y + (nodeCorner.charAt(0)=='T' ? 0 : aroundNodeH);
+
+		pos = dojo.html.placeOnScreen(node, desiredX, desiredY, padding, true, corners, true);
+		if(pos.dist == 0){
+			best = pos;
+			break;
+		}else{
+			//not perfect, find out whether it is better than the saved one
+			if(bestDistance > pos.dist){
+				bestDistance = pos.dist;
+				best = pos;
+			}
+		}
+	}
+
+	if(!tryOnly){
+		node.style.left = best.left + "px";
+		node.style.top = best.top + "px";
+	}
+	return best;	//	object
+}
+
+dojo.html.scrollIntoView = function(/* HTMLElement */node){
+	//	summary
+	//	Scroll the passed node into view, if it is not.
+	if(!node){ return; }
+
+	// don't rely on that node.scrollIntoView works just because the function is there
+	// it doesnt work in Konqueror or Opera even though the function is there and probably
+	// not safari either
+	// dont like browser sniffs implementations but sometimes you have to use it
+	if(dojo.render.html.ie){
+		//only call scrollIntoView if there is a scrollbar for this menu,
+		//otherwise, scrollIntoView will scroll the window scrollbar
+		if(dojo.html.getBorderBox(node.parentNode).height < node.parentNode.scrollHeight){
+			node.scrollIntoView(false);
+		}
+	}else if(dojo.render.html.mozilla){
+		// IE, mozilla
+		node.scrollIntoView(false);
+	}else{
+		var parent = node.parentNode;
+		var parentBottom = parent.scrollTop + dojo.html.getBorderBox(parent).height;
+		var nodeBottom = node.offsetTop + dojo.html.getMarginBox(node).height;
+		if(parentBottom < nodeBottom){
+			parent.scrollTop += (nodeBottom - parentBottom);
+		}else if(parent.scrollTop > node.offsetTop){
+			parent.scrollTop -= (parent.scrollTop - node.offsetTop);
+		}
+	}
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/html.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,14 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.html");
+
+dojo.require("dojo.html.*");
+dojo.deprecated("dojo.html", "replaced by dojo.html.*", "0.5");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/iCalendar.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/iCalendar.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/iCalendar.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,12 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.require("dojo.cal.iCalendar");
+dojo.deprecated("dojo.icalendar", "use dojo.cal.iCalendar isntead", "0.5");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/io.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/io.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/io.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,14 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.io");
+
+dojo.require("dojo.io.*");
+dojo.deprecated("dojo.io", "replaced by dojo.io.*", "0.5");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/json.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/json.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/json.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,156 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.json");
+dojo.require("dojo.lang.func");
+dojo.require("dojo.string.extras");
+dojo.require("dojo.AdapterRegistry");
+
+dojo.json = {
+	// jsonRegistry: AdapterRegistry a registry of type-based serializers
+	jsonRegistry: new dojo.AdapterRegistry(),
+
+	register: function(	/*String*/		name, 
+						/*function*/	check, 
+						/*function*/	wrap, 
+						/*optional, boolean*/ override){
+		// summary:
+		//		Register a JSON serialization function. JSON serialization
+		//		functions should take one argument and return an object
+		//		suitable for JSON serialization:
+		//			- string
+		//			- number
+		//			- boolean
+		//			- undefined
+		//			- object
+		//				- null
+		//				- Array-like (length property that is a number)
+		//				- Objects with a "json" method will have this method called
+		//				- Any other object will be used as {key:value, ...} pairs
+		//			
+		//		If override is given, it is used as the highest priority JSON
+		//		serialization, otherwise it will be used as the lowest.
+		// name:
+		//		a descriptive type for this serializer
+		// check:
+		//		a unary function that will be passed an object to determine
+		//		whether or not wrap will be used to serialize the object
+		// wrap:
+		//		the serialization function
+		// override:
+		//		optional, determines if the this serialization function will be
+		//		given priority in the test order
+
+		dojo.json.jsonRegistry.register(name, check, wrap, override);
+	},
+
+	evalJson: function(/*String*/ json){
+		// summary:
+		// 		evaluates the passed string-form of a JSON object
+		// json: 
+		//		a string literal of a JSON item, for instance:
+		//			'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'
+		// return:
+		//		the result of the evaluation
+
+		// FIXME: should this accept mozilla's optional second arg?
+		try {
+			return eval("(" + json + ")");
+		}catch(e){
+			dojo.debug(e);
+			return json;
+		}
+	},
+
+	serialize: function(/*Object*/ o){
+		// summary:
+		//		Create a JSON serialization of an object, note that this
+		//		doesn't check for infinite recursion, so don't do that!
+		// o:
+		//		an object to be serialized. Objects may define their own
+		//		serialization via a special "__json__" or "json" function
+		//		property. If a specialized serializer has been defined, it will
+		//		be used as a fallback.
+		// return:
+		//		a String representing the serialized version of the passed
+		//		object
+
+		var objtype = typeof(o);
+		if(objtype == "undefined"){
+			return "undefined";
+		}else if((objtype == "number")||(objtype == "boolean")){
+			return o + "";
+		}else if(o === null){
+			return "null";
+		}
+		if (objtype == "string") { return dojo.string.escapeString(o); }
+		// recurse
+		var me = arguments.callee;
+		// short-circuit for objects that support "json" serialization
+		// if they return "self" then just pass-through...
+		var newObj;
+		if(typeof(o.__json__) == "function"){
+			newObj = o.__json__();
+			if(o !== newObj){
+				return me(newObj);
+			}
+		}
+		if(typeof(o.json) == "function"){
+			newObj = o.json();
+			if (o !== newObj) {
+				return me(newObj);
+			}
+		}
+		// array
+		if(objtype != "function" && typeof(o.length) == "number"){
+			var res = [];
+			for(var i = 0; i < o.length; i++){
+				var val = me(o[i]);
+				if(typeof(val) != "string"){
+					val = "undefined";
+				}
+				res.push(val);
+			}
+			return "[" + res.join(",") + "]";
+		}
+		// look in the registry
+		try {
+			window.o = o;
+			newObj = dojo.json.jsonRegistry.match(o);
+			return me(newObj);
+		}catch(e){
+			// dojo.debug(e);
+		}
+		// it's a function with no adapter, bad
+		if(objtype == "function"){
+			return null;
+		}
+		// generic object code path
+		res = [];
+		for (var k in o){
+			var useKey;
+			if (typeof(k) == "number"){
+				useKey = '"' + k + '"';
+			}else if (typeof(k) == "string"){
+				useKey = dojo.string.escapeString(k);
+			}else{
+				// skip non-string or number keys
+				continue;
+			}
+			val = me(o[k]);
+			if(typeof(val) != "string"){
+				// skip non-serializable values
+				continue;
+			}
+			res.push(useKey + ":" + val);
+		}
+		return "{" + res.join(",") + "}";
+	}
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/__package__.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/__package__.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/__package__.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,23 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.kwCompoundRequire({
+	common: [
+		"dojo.lang.common",
+		"dojo.lang.assert",
+		"dojo.lang.array",
+		"dojo.lang.type",
+		"dojo.lang.func",
+		"dojo.lang.extras",
+		"dojo.lang.repr",
+		"dojo.lang.declare"
+	]
+});
+dojo.provide("dojo.lang.*");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/array.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/array.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/array.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,186 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.array");
+
+dojo.require("dojo.lang.common");
+
+// FIXME: Is this worthless since you can do: if(name in obj)
+// is this the right place for this?
+dojo.lang.has = function(/*Object*/obj, /*String*/name){
+	try{
+		return typeof obj[name] != "undefined";
+	}catch(e){ return false; }
+}
+
+dojo.lang.isEmpty = function(/*Object*/obj){
+	if(dojo.lang.isObject(obj)){
+		var tmp = {};
+		var count = 0;
+		for(var x in obj){
+			if(obj[x] && (!tmp[x])){
+				count++;
+				break;
+			} 
+		}
+		return count == 0;
+	}else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)){
+		return obj.length == 0;
+	}
+}
+
+dojo.lang.map = function(/*Array*/arr, /*Object|Function*/obj, /*Function?*/unary_func){
+	var isString = dojo.lang.isString(arr);
+	if(isString){
+		// arr: String
+		arr = arr.split("");
+	}
+	if(dojo.lang.isFunction(obj)&&(!unary_func)){
+		unary_func = obj;
+		obj = dj_global;
+	}else if(dojo.lang.isFunction(obj) && unary_func){
+		// ff 1.5 compat
+		var tmpObj = obj;
+		obj = unary_func;
+		unary_func = tmpObj;
+	}
+	if(Array.map){
+	 	var outArr = Array.map(arr, unary_func, obj);
+	}else{
+		var outArr = [];
+		for(var i=0;i<arr.length;++i){
+			outArr.push(unary_func.call(obj, arr[i]));
+		}
+	}
+	if(isString) {
+		return outArr.join(""); // String
+	} else {
+		return outArr; // Array
+	}
+}
+
+dojo.lang.reduce = function(/*Array*/arr, initialValue, /*Object|null*/obj, /*Function*/binary_func){
+	var reducedValue = initialValue;
+	var ob = obj ? obj : dj_global;
+	dojo.lang.map(arr, 
+		function(val){
+			reducedValue = binary_func.call(ob, reducedValue, val);
+		}
+	);
+	return reducedValue;
+}
+
+// http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
+dojo.lang.forEach = function(/*Array*/anArray, /*Function*/callback, /*Object?*/thisObject){
+	if(dojo.lang.isString(anArray)){
+		// anArray: String
+		anArray = anArray.split(""); 
+	}
+	if(Array.forEach){
+		Array.forEach(anArray, callback, thisObject);
+	}else{
+		// FIXME: there are several ways of handilng thisObject. Is dj_global always the default context?
+		if(!thisObject){
+			thisObject=dj_global;
+		}
+		for(var i=0,l=anArray.length; i<l; i++){ 
+			callback.call(thisObject, anArray[i], i, anArray);
+		}
+	}
+}
+
+dojo.lang._everyOrSome = function(/*Boolean*/every, /*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
+	if(dojo.lang.isString(arr)){ 
+		//arr: String
+		arr = arr.split(""); 
+	}
+	if(Array.every){
+		return Array[ every ? "every" : "some" ](arr, callback, thisObject);
+	}else{
+		if(!thisObject){
+			thisObject = dj_global;
+		}
+		for(var i=0,l=arr.length; i<l; i++){
+			var result = callback.call(thisObject, arr[i], i, arr);
+			if(every && !result){
+				return false; // Boolean
+			}else if((!every)&&(result)){
+				return true; // Boolean
+			}
+		}
+		return Boolean(every); // Boolean
+	}
+}
+
+dojo.lang.every = function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
+	return this._everyOrSome(true, arr, callback, thisObject); // Boolean
+}
+
+dojo.lang.some = function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
+	return this._everyOrSome(false, arr, callback, thisObject); // Boolean
+}
+
+dojo.lang.filter = function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
+	var isString = dojo.lang.isString(arr);
+	if(isString){ /*arr: String*/arr = arr.split(""); }
+	var outArr;
+	if(Array.filter){
+		outArr = Array.filter(arr, callback, thisObject);
+	} else {
+		if(!thisObject){
+			if(arguments.length >= 3){ dojo.raise("thisObject doesn't exist!"); }
+			thisObject = dj_global;
+		}
+
+		outArr = [];
+		for(var i = 0; i < arr.length; i++){
+			if(callback.call(thisObject, arr[i], i, arr)){
+				outArr.push(arr[i]);
+			}
+		}
+	}
+	if(isString){
+		return outArr.join(""); // String
+	} else {
+		return outArr; // Array
+	}
+}
+
+dojo.lang.unnest = function(/* ... */){
+	// summary:
+	//	Creates a 1-D array out of all the arguments passed,
+	//	unravelling any array-like objects in the process
+	//
+	// usage:
+	//	unnest(1, 2, 3) ==> [1, 2, 3]
+	//	unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4]
+
+	var out = [];
+	for(var i = 0; i < arguments.length; i++){
+		if(dojo.lang.isArrayLike(arguments[i])){
+			var add = dojo.lang.unnest.apply(this, arguments[i]);
+			out = out.concat(add);
+		}else{
+			out.push(arguments[i]);
+		}
+	}
+	return out; // Array
+}
+
+dojo.lang.toArray = function(/*Object*/arrayLike, /*Number*/startOffset){
+	// summary:
+	//	Converts an array-like object (i.e. arguments, DOMCollection)
+	//	to an array
+	var array = [];
+	for(var i = startOffset||0; i < arrayLike.length; i++){
+		array.push(arrayLike[i]);
+	}
+	return array; // Array
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/assert.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/assert.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/assert.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,116 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.assert");
+
+dojo.require("dojo.lang.common");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.lang.type");
+
+dojo.lang.assert = function(/* boolean */ booleanValue, /* string? */ message){
+	/* summary: 
+	 *   Throws an exception if the assertion fails.
+	 * description: 
+	 *   If the asserted condition is true, this method does nothing. If the
+	 *   condition is false, we throw an error with a error message. 
+	 * booleanValue: Must be true for the assertion to succeed.
+	 * message: A string describing the assertion.
+	 */
+
+	 // throws: Throws an Error if 'booleanValue' is false.
+	 if(!booleanValue){
+		var errorMessage = "An assert statement failed.\n" +
+			"The method dojo.lang.assert() was called with a 'false' value.\n";
+		if(message){
+			errorMessage += "Here's the assert message:\n" + message + "\n";
+		}
+		// Use throw instead of dojo.raise, until bug #264 is fixed:
+		// dojo.raise(errorMessage);
+		throw new Error(errorMessage);
+	}
+}
+
+dojo.lang.assertType = function(/* anything */ value, /* misc. */ type, /* object? */ keywordParameters){
+	/* summary: 
+	 *   Throws an exception if 'value' is not of type 'type'
+	 * description: 
+	 *   Given a value and a data type, this method checks the type of the value
+	 *   to make sure it matches the data type, and throws an exception if there
+	 *   is a mismatch.
+	 * value: Any literal value or object instance.
+	 * type: A class of object, or a literal type, or the string name of a type, or an array with a list of types.
+	 * keywordParameters: {optional: boolean}
+	 */
+	 
+	/* examples: 
+	 *   dojo.lang.assertType("foo", String);
+	 *   dojo.lang.assertType(12345, Number);
+	 *   dojo.lang.assertType(false, Boolean);
+	 *   dojo.lang.assertType([6, 8], Array);
+	 *   dojo.lang.assertType(dojo.lang.assertType, Function);
+	 *   dojo.lang.assertType({foo: "bar"}, Object);
+	 *   dojo.lang.assertType(new Date(), Date);
+	 *   dojo.lang.assertType(null, Array, {optional: true});
+	 * throws: Throws an Error if 'value' is not of type 'type'.
+	 */
+	if (dojo.lang.isString(keywordParameters)) {
+		dojo.deprecated('dojo.lang.assertType(value, type, "message")', 'use dojo.lang.assertType(value, type) instead', "0.5");
+	}
+	if(!dojo.lang.isOfType(value, type, keywordParameters)){
+		if(!dojo.lang.assertType._errorMessage){
+			dojo.lang.assertType._errorMessage = "Type mismatch: dojo.lang.assertType() failed.";
+		}
+		dojo.lang.assert(false, dojo.lang.assertType._errorMessage);
+	}
+}
+
+dojo.lang.assertValidKeywords = function(/* object */ object, /* array */ expectedProperties, /* string? */ message){
+	/* summary: 
+	 *   Throws an exception 'object' has any properties other than the 'expectedProperties'.
+	 * description: 
+	 *   Given an anonymous object and a list of expected property names, this
+	 *   method check to make sure the object does not have any properties
+	 *   that aren't on the list of expected properties, and throws an Error
+	 *   if there are unexpected properties. This is useful for doing error
+	 *   checking on keyword arguments, to make sure there aren't typos.
+	 * object: An anonymous object.
+	 * expectedProperties: An array of strings (or an object with all the expected properties).
+	 * message: A message describing the assertion.
+	 */
+	 
+	/* examples: 
+	 *   dojo.lang.assertValidKeywords({a: 1, b: 2}, ["a", "b"]);
+	 *   dojo.lang.assertValidKeywords({a: 1, b: 2}, ["a", "b", "c"]);
+	 *   dojo.lang.assertValidKeywords({foo: "iggy"}, ["foo"]);
+	 *   dojo.lang.assertValidKeywords({foo: "iggy"}, ["foo", "bar"]);
+	 *   dojo.lang.assertValidKeywords({foo: "iggy"}, {foo: null, bar: null});
+	 * throws: Throws an Error if 'object' has unexpected properties.
+	 */
+	var key;
+	if(!message){
+		if(!dojo.lang.assertValidKeywords._errorMessage){
+			dojo.lang.assertValidKeywords._errorMessage = "In dojo.lang.assertValidKeywords(), found invalid keyword:";
+		}
+		message = dojo.lang.assertValidKeywords._errorMessage;
+	}
+	if(dojo.lang.isArray(expectedProperties)){
+		for(key in object){
+			if(!dojo.lang.inArray(expectedProperties, key)){
+				dojo.lang.assert(false, message + " " + key);
+			}
+		}
+	}else{
+		for(key in object){
+			if(!(key in expectedProperties)){
+				dojo.lang.assert(false, message + " " + key);
+			}
+		}
+	}
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/common.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/common.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/common.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,230 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.common");
+
+dojo.lang.inherits = function(/*Function*/ subclass, /*Function*/ superclass){
+	// summary: Set up inheritance between two classes.
+	if(typeof superclass != 'function'){ 
+		dojo.raise("dojo.inherits: superclass argument ["+superclass+"] must be a function (subclass: ["+subclass+"']");
+	}
+	subclass.prototype = new superclass();
+	subclass.prototype.constructor = subclass;
+	subclass.superclass = superclass.prototype;
+	// DEPRECATED: super is a reserved word, use 'superclass'
+	subclass['super'] = superclass.prototype;
+}
+
+dojo.lang._mixin = function(/*Object*/ obj, /*Object*/ props){
+	// summary:	Adds all properties and methods of props to obj.
+	var tobj = {};
+	for(var x in props){
+		// the "tobj" condition avoid copying properties in "props"
+		// inherited from Object.prototype.  For example, if obj has a custom
+		// toString() method, don't overwrite it with the toString() method
+		// that props inherited from Object.protoype
+		if((typeof tobj[x] == "undefined") || (tobj[x] != props[x])){
+			obj[x] = props[x];
+		}
+	}
+	// IE doesn't recognize custom toStrings in for..in
+	if(dojo.render.html.ie 
+		&& (typeof(props["toString"]) == "function")
+		&& (props["toString"] != obj["toString"])
+		&& (props["toString"] != tobj["toString"]))
+	{
+		obj.toString = props.toString;
+	}
+	return obj; // Object
+}
+
+dojo.lang.mixin = function(/*Object*/ obj, /*Object...*/props){
+	// summary:	Adds all properties and methods of props to obj.
+	for(var i=1, l=arguments.length; i<l; i++){
+		dojo.lang._mixin(obj, arguments[i]);
+	}
+	return obj; // Object
+}
+
+dojo.lang.extend = function(/*Object*/ constructor, /*Object...*/ props){
+	// summary:	Adds all properties and methods of props to constructor's prototype,
+	//			making them available to all instances created with constructor.
+	for(var i=1, l=arguments.length; i<l; i++){
+		dojo.lang._mixin(constructor.prototype, arguments[i]);
+	}
+	return constructor; // Object
+}
+
+// Promote to dojo module
+dojo.inherits = dojo.lang.inherits;
+//dojo.lang._mixin = dojo.lang._mixin;
+dojo.mixin = dojo.lang.mixin;
+dojo.extend = dojo.lang.extend;
+
+dojo.lang.find = function(	/*Array*/		array, 
+							/*Object*/		value,
+							/*Boolean?*/	identity,
+							/*Boolean?*/	findLast){
+	// summary:	Return the index of value in array, returning -1 if not found.
+	// identity: If true, matches with identity comparison (===).  
+	//					 If false, uses normal comparison (==).
+	// findLast: If true, returns index of last instance of value.
+	
+	// examples:
+	//  find(array, value[, identity [findLast]]) // recommended
+ 	//  find(value, array[, identity [findLast]]) // deprecated
+							
+	// support both (array, value) and (value, array)
+	if(!dojo.lang.isArrayLike(array) && dojo.lang.isArrayLike(value)) {
+		dojo.deprecated('dojo.lang.find(value, array)', 'use dojo.lang.find(array, value) instead', "0.5");
+		var temp = array;
+		array = value;
+		value = temp;
+	}
+	var isString = dojo.lang.isString(array);
+	if(isString) { array = array.split(""); }
+
+	if(findLast) {
+		var step = -1;
+		var i = array.length - 1;
+		var end = -1;
+	} else {
+		var step = 1;
+		var i = 0;
+		var end = array.length;
+	}
+	if(identity){
+		while(i != end) {
+			if(array[i] === value){ return i; }
+			i += step;
+		}
+	}else{
+		while(i != end) {
+			if(array[i] == value){ return i; }
+			i += step;
+		}
+	}
+	return -1;	// number
+}
+
+dojo.lang.indexOf = dojo.lang.find;
+
+dojo.lang.findLast = function(/*Array*/ array, /*Object*/ value, /*boolean?*/ identity){
+	// summary:	Return index of last occurance of value in array, returning -1 if not found.
+	// identity: If true, matches with identity comparison (===). If false, uses normal comparison (==).
+	return dojo.lang.find(array, value, identity, true); // number
+}
+
+dojo.lang.lastIndexOf = dojo.lang.findLast;
+
+dojo.lang.inArray = function(array /*Array*/, value /*Object*/){
+	// summary:	Return true if value is present in array.
+	return dojo.lang.find(array, value) > -1; // boolean
+}
+
+/**
+ * Partial implmentation of is* functions from
+ * http://www.crockford.com/javascript/recommend.html
+ * NOTE: some of these may not be the best thing to use in all situations
+ * as they aren't part of core JS and therefore can't work in every case.
+ * See WARNING messages inline for tips.
+ *
+ * The following is* functions are fairly "safe"
+ */
+
+dojo.lang.isObject = function(/*anything*/ it){
+	// summary:	Return true if it is an Object, Array or Function.
+	if(typeof it == "undefined"){ return false; }
+	return (typeof it == "object" || it === null || dojo.lang.isArray(it) || dojo.lang.isFunction(it)); // Boolean
+}
+
+dojo.lang.isArray = function(/*anything*/ it){
+	// summary:	Return true if it is an Array.
+	return (it && it instanceof Array || typeof it == "array"); // Boolean
+}
+
+dojo.lang.isArrayLike = function(/*anything*/ it){
+	// summary:	Return true if it can be used as an array (i.e. is an object with an integer length property).
+	if((!it)||(dojo.lang.isUndefined(it))){ return false; }
+	if(dojo.lang.isString(it)){ return false; }
+	if(dojo.lang.isFunction(it)){ return false; } // keeps out built-in constructors (Number, String, ...) which have length properties
+	if(dojo.lang.isArray(it)){ return true; }
+	// form node itself is ArrayLike, but not always iterable. Use form.elements instead.
+	if((it.tagName)&&(it.tagName.toLowerCase()=='form')){ return false; }
+	if(dojo.lang.isNumber(it.length) && isFinite(it.length)){ return true; }
+	return false; // Boolean
+}
+
+dojo.lang.isFunction = function(/*anything*/ it){
+	// summary:	Return true if it is a Function.
+	if(!it){ return false; }
+	// webkit treats NodeList as a function, which is bad
+	if((typeof(it) == "function") && (it == "[object NodeList]")) { return false; }
+	return (it instanceof Function || typeof it == "function"); // Boolean
+}
+
+dojo.lang.isString = function(/*anything*/ it){
+	// summary:	Return true if it is a String.
+	return (typeof it == "string" || it instanceof String);
+}
+
+dojo.lang.isAlien = function(/*anything*/ it){
+	// summary:	Return true if it is not a built-in function.
+	if(!it){ return false; }
+	return !dojo.lang.isFunction() && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
+}
+
+dojo.lang.isBoolean = function(/*anything*/ it){
+	// summary:	Return true if it is a Boolean.
+	return (it instanceof Boolean || typeof it == "boolean"); // Boolean
+}
+
+/**
+ * The following is***() functions are somewhat "unsafe". Fortunately,
+ * there are workarounds the the language provides and are mentioned
+ * in the WARNING messages.
+ *
+ */
+dojo.lang.isNumber = function(/*anything*/ it){
+	// summary:	Return true if it is a number.
+	// description: 
+	//		WARNING - In most cases, isNaN(it) is sufficient to determine whether or not
+	// 		something is a number or can be used as such. For example, a number or string
+	// 		can be used interchangably when accessing array items (array["1"] is the same as
+	// 		array[1]) and isNaN will return false for both values ("1" and 1). However,
+	// 		isNumber("1")  will return false, which is generally not too useful.
+	// 		Also, isNumber(NaN) returns true, again, this isn't generally useful, but there
+	// 		are corner cases (like when you want to make sure that two things are really
+	// 		the same type of thing). That is really where isNumber "shines".
+	//
+	// Recommendation - Use isNaN(it) when possible
+	
+	return (it instanceof Number || typeof it == "number"); // Boolean
+}
+
+/*
+ * FIXME: Should isUndefined go away since it is error prone?
+ */
+dojo.lang.isUndefined = function(/*anything*/ it){
+	// summary: Return true if it is not defined.
+	// description: 
+	//		WARNING - In some cases, isUndefined will not behave as you
+	// 		might expect. If you do isUndefined(foo) and there is no earlier
+	// 		reference to foo, an error will be thrown before isUndefined is
+	// 		called. It behaves correctly if you scope yor object first, i.e.
+	// 		isUndefined(foo.bar) where foo is an object and bar isn't a
+	// 		property of the object.
+	//
+	// Recommendation - Use typeof foo == "undefined" when possible
+
+	return ((typeof(it) == "undefined")&&(it == undefined)); // Boolean
+}
+
+// end Crockford functions

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/declare.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/declare.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/declare.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,161 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.declare");
+
+dojo.require("dojo.lang.common");
+dojo.require("dojo.lang.extras");
+
+dojo.lang.declare = function(/*String*/ className, /*Function|Array*/ superclass, /*Function?*/ init, /*Object|Array*/ props){
+/*
+ * summary: Create a feature-rich constructor with a compact notation
+ *
+ * className: the name of the constructor (loosely, a "class")
+ *
+ * superclass: may be a Function, or an Array of Functions. 
+ *   If "superclass" is an array, the first element is used 
+ *   as the prototypical ancestor and any following Functions 
+ *   become mixin ancestors.
+ *
+ * init: an initializer function
+ *
+ * props: an object (or array of objects) whose properties are copied to the created prototype
+ *
+ * description: Create a constructor using a compact notation for inheritance and prototype extension.
+ *
+ *   "superclass" argument may be a Function, or an array of 
+ *   Functions. 
+ *
+ *   If "superclass" is an array, the first element is used 
+ *   as the prototypical ancestor and any following Functions 
+ *   become mixin ancestors. 
+ * 
+ *   All "superclass(es)" must be Functions (not mere Objects).
+ *
+ *   Using mixin ancestors provides a type of multiple
+ *   inheritance. Mixin ancestors prototypical 
+ *   properties are copied to the subclass, and any 
+ *   inializater/constructor is invoked. 
+ *
+ *   Properties of object "props" are copied to the constructor 
+ *   prototype. If "props" is an array, properties of each
+ *   object in the array are copied to the constructor prototype.
+ *
+ *   name of the class ("className" argument) is stored in 
+ *   "declaredClass" property
+ * 
+ *   Initializer functions are called when an object 
+ *   is instantiated from this constructor.
+ * 
+ * Aliased as "dojo.declare"
+ *
+ * Usage:
+ *
+ * dojo.declare("my.classes.bar", my.classes.foo,
+ *	function() {
+ *		// initialization function
+ *		this.myComplicatedObject = new ReallyComplicatedObject(); 
+ *	},{
+ *	someValue: 2,
+ *	someMethod: function() { 
+ *		doStuff(); 
+ *	}
+ * });
+ *
+ */
+	if((dojo.lang.isFunction(props))||((!props)&&(!dojo.lang.isFunction(init)))){ 
+	 // parameter juggling to support omitting init param (also allows reordering init and props arguments)
+		var temp = props;
+		props = init;
+		init = temp;
+	}	
+	var mixins = [ ];
+	if(dojo.lang.isArray(superclass)){
+		mixins = superclass;
+		superclass = mixins.shift();
+	}
+	if(!init){
+		init = dojo.evalObjPath(className, false);
+		if((init)&&(!dojo.lang.isFunction(init))){ init = null };
+	}
+	var ctor = dojo.lang.declare._makeConstructor();
+	var scp = (superclass ? superclass.prototype : null);
+	if(scp){
+		scp.prototyping = true;
+		ctor.prototype = new superclass();
+		scp.prototyping = false; 
+	}
+	ctor.superclass = scp;
+	ctor.mixins = mixins;
+	for(var i=0,l=mixins.length; i<l; i++){
+		dojo.lang.extend(ctor, mixins[i].prototype);
+	}
+	ctor.prototype.initializer = null;
+	ctor.prototype.declaredClass = className;
+	if(dojo.lang.isArray(props)){
+		dojo.lang.extend.apply(dojo.lang, [ctor].concat(props));
+	}else{
+		dojo.lang.extend(ctor, (props)||{});
+	}
+	dojo.lang.extend(ctor, dojo.lang.declare._common);
+	ctor.prototype.constructor = ctor;
+	ctor.prototype.initializer = (ctor.prototype.initializer)||(init)||(function(){});
+	dojo.lang.setObjPathValue(className, ctor, null, true);
+	return ctor; // Function
+}
+
+dojo.lang.declare._makeConstructor = function() {
+	return function(){ 
+		// get the generational context (which object [or prototype] should be constructed)
+		var self = this._getPropContext();
+		var s = self.constructor.superclass;
+		if((s)&&(s.constructor)){
+			if(s.constructor==arguments.callee){
+				// if this constructor is invoked directly (my.ancestor.call(this))
+				this._inherited("constructor", arguments);
+			}else{
+				this._contextMethod(s, "constructor", arguments);
+			}
+		}
+		var ms = (self.constructor.mixins)||([]);
+		for(var i=0, m; (m=ms[i]); i++) {
+			(((m.prototype)&&(m.prototype.initializer))||(m)).apply(this, arguments);
+		}
+		if((!this.prototyping)&&(self.initializer)){
+			self.initializer.apply(this, arguments);
+		}
+	}
+}
+
+dojo.lang.declare._common = {
+	_getPropContext: function() { return (this.___proto||this); },
+	// caches ptype context and calls method on it
+	_contextMethod: function(ptype, method, args){
+		var result, stack = this.___proto;
+		this.___proto = ptype;
+		try { result = ptype[method].apply(this,(args||[])); }
+		catch(e) { throw e; }	
+		finally { this.___proto = stack; }
+		return result;
+	},
+	_inherited: function(prop, args){
+		// summary
+		//	Searches backward thru prototype chain to find nearest ancestral instance of prop.
+		//	Internal use only.
+		var p = this._getPropContext();
+		do{
+			if((!p.constructor)||(!p.constructor.superclass)){return;}
+			p = p.constructor.superclass;
+		}while(!(prop in p));
+		return (dojo.lang.isFunction(p[prop]) ? this._contextMethod(p, prop, args) : p[prop]);
+	}
+}
+
+dojo.declare = dojo.lang.declare;
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/extras.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/extras.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/extras.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,130 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.extras");
+
+dojo.require("dojo.lang.common");
+
+dojo.lang.setTimeout = function(/*Function*/func, /*int*/delay /*, ...*/){
+	// summary:
+	//	Sets a timeout in milliseconds to execute a function in a given context
+	//	with optional arguments.
+	//
+	// usage:
+	//	setTimeout (Object context, function func, number delay[, arg1[, ...]]);
+	//	setTimeout (function func, number delay[, arg1[, ...]]);
+
+	var context = window, argsStart = 2;
+	if(!dojo.lang.isFunction(func)){
+		context = func;
+		func = delay;
+		delay = arguments[2];
+		argsStart++;
+	}
+
+	if(dojo.lang.isString(func)){
+		func = context[func];
+	}
+	
+	var args = [];
+	for (var i = argsStart; i < arguments.length; i++){
+		args.push(arguments[i]);
+	}
+	return dojo.global().setTimeout(function () { func.apply(context, args); }, delay); // int
+}
+
+dojo.lang.clearTimeout = function(/*int*/timer){
+	// summary: clears timer by number from the execution queue
+	dojo.global().clearTimeout(timer);
+}
+
+dojo.lang.getNameInObj = function(/*Object*/ns, /*unknown*/item){
+	// summary: looks for a value in the object ns with a value matching item and returns the property name
+	// ns: if null, dj_global is used
+	// item: value to match
+	if(!ns){ ns = dj_global; }
+
+	for(var x in ns){
+		if(ns[x] === item){
+			return new String(x); // String
+		}
+	}
+	return null; // null
+}
+
+dojo.lang.shallowCopy = function(/*Object*/obj, /*Boolean?*/deep){
+	// summary: copies object obj one level deep, or full depth if deep is true
+	var i, ret;	
+
+	if(obj === null){ /*obj: null*/ return null; } // null
+	
+	if(dojo.lang.isObject(obj)){
+		// obj: Object	
+		ret = new obj.constructor();
+		for(i in obj){
+			if(dojo.lang.isUndefined(ret[i])){
+				ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
+			}
+		}
+	} else if(dojo.lang.isArray(obj)){
+		// obj: Array
+		ret = [];
+		for(i=0; i<obj.length; i++){
+			ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
+		}
+	} else {
+		// obj: unknown
+		ret = obj;
+	}
+
+	return ret; // unknown
+}
+
+dojo.lang.firstValued = function(/* ... */){
+	// summary: Return the first argument that isn't undefined
+
+	for(var i = 0; i < arguments.length; i++){
+		if(typeof arguments[i] != "undefined"){
+			return arguments[i]; // unknown
+		}
+	}
+	return undefined; // undefined
+}
+
+dojo.lang.getObjPathValue = function(/*String*/objpath, /*Object?*/context, /*Boolean?*/create){
+	// summary:
+	//	Gets a value from a reference specified as a string descriptor,
+	//	(e.g. "A.B") in the given context.
+	//
+	// context: if not specified, dj_global is used
+	// create: if true, undefined objects in the path are created.
+
+	with(dojo.parseObjPath(objpath, context, create)){
+		return dojo.evalProp(prop, obj, create); // unknown
+	}
+}
+
+dojo.lang.setObjPathValue = function(/*String*/objpath, /*unknown*/value, /*Object?*/context, /*Boolean?*/create){
+	// summary:
+	//	Sets a value on a reference specified as a string descriptor. 
+	//	(e.g. "A.B") in the given context.
+	//
+	//	context: if not specified, dj_global is used
+	//	create: if true, undefined objects in the path are created.
+
+	if(arguments.length < 4){
+		create = true;
+	}
+	with(dojo.parseObjPath(objpath, context, create)){
+		if(obj && (create || (prop in obj))){
+			obj[prop] = value;
+		}
+	}
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/func.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/func.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/func.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,148 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.func");
+
+dojo.require("dojo.lang.common");
+
+/**
+ * Runs a function in a given scope (thisObject), can
+ * also be used to preserve scope.
+ *
+ * hitch(foo, "bar"); // runs foo.bar() in the scope of foo
+ * hitch(foo, myFunction); // runs myFunction in the scope of foo
+ */
+dojo.lang.hitch = function(thisObject, method){
+	var fcn = (dojo.lang.isString(method) ? thisObject[method] : method) || function(){};
+
+	return function() {
+		return fcn.apply(thisObject, arguments);
+	};
+}
+
+dojo.lang.anonCtr = 0;
+dojo.lang.anon = {};
+dojo.lang.nameAnonFunc = function(anonFuncPtr, namespaceObj, searchForNames){
+	var nso = (namespaceObj || dojo.lang.anon);
+	if( (searchForNames) ||
+		((dj_global["djConfig"])&&(djConfig["slowAnonFuncLookups"] == true)) ){
+		for(var x in nso){
+			try{
+				if(nso[x] === anonFuncPtr){
+					return x;
+				}
+			}catch(e){} // window.external fails in IE embedded in Eclipse (Eclipse bug #151165)
+		}
+	}
+	var ret = "__"+dojo.lang.anonCtr++;
+	while(typeof nso[ret] != "undefined"){
+		ret = "__"+dojo.lang.anonCtr++;
+	}
+	nso[ret] = anonFuncPtr;
+	return ret;
+}
+
+dojo.lang.forward = function(funcName){
+	// Returns a function that forwards a method call to this.func(...)
+	return function(){
+		return this[funcName].apply(this, arguments);
+	};
+}
+
+dojo.lang.curry = function(ns, func /* args ... */){
+	var outerArgs = [];
+	ns = ns||dj_global;
+	if(dojo.lang.isString(func)){
+		func = ns[func];
+	}
+	for(var x=2; x<arguments.length; x++){
+		outerArgs.push(arguments[x]);
+	}
+	// since the event system replaces the original function with a new
+	// join-point runner with an arity of 0, we check to see if it's left us
+	// any clues about the original arity in lieu of the function's actual
+	// length property
+	var ecount = (func["__preJoinArity"]||func.length) - outerArgs.length;
+	// borrowed from svend tofte
+	function gather(nextArgs, innerArgs, expected){
+		var texpected = expected;
+		var totalArgs = innerArgs.slice(0); // copy
+		for(var x=0; x<nextArgs.length; x++){
+			totalArgs.push(nextArgs[x]);
+		}
+		// check the list of provided nextArgs to see if it, plus the
+		// number of innerArgs already supplied, meets the total
+		// expected.
+		expected = expected-nextArgs.length;
+		if(expected<=0){
+			var res = func.apply(ns, totalArgs);
+			expected = texpected;
+			return res;
+		}else{
+			return function(){
+				return gather(arguments,// check to see if we've been run
+										// with enough args
+							totalArgs,	// a copy
+							expected);	// how many more do we need to run?;
+			};
+		}
+	}
+	return gather([], outerArgs, ecount);
+}
+
+dojo.lang.curryArguments = function(ns, func, args, offset){
+	var targs = [];
+	var x = offset||0;
+	for(x=offset; x<args.length; x++){
+		targs.push(args[x]); // ensure that it's an arr
+	}
+	return dojo.lang.curry.apply(dojo.lang, [ns, func].concat(targs));
+}
+
+dojo.lang.tryThese = function(){
+	for(var x=0; x<arguments.length; x++){
+		try{
+			if(typeof arguments[x] == "function"){
+				var ret = (arguments[x]());
+				if(ret){
+					return ret;
+				}
+			}
+		}catch(e){
+			dojo.debug(e);
+		}
+	}
+}
+
+dojo.lang.delayThese = function(farr, cb, delay, onend){
+	/**
+	 * alternate: (array funcArray, function callback, function onend)
+	 * alternate: (array funcArray, function callback)
+	 * alternate: (array funcArray)
+	 */
+	if(!farr.length){ 
+		if(typeof onend == "function"){
+			onend();
+		}
+		return;
+	}
+	if((typeof delay == "undefined")&&(typeof cb == "number")){
+		delay = cb;
+		cb = function(){};
+	}else if(!cb){
+		cb = function(){};
+		if(!delay){ delay = 0; }
+	}
+	setTimeout(function(){
+		(farr.shift())();
+		cb();
+		dojo.lang.delayThese(farr, cb, delay, onend);
+	}, delay);
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/repr.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/repr.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/repr.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,85 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.repr");
+
+dojo.require("dojo.lang.common");
+dojo.require("dojo.AdapterRegistry");
+dojo.require("dojo.string.extras");
+
+dojo.lang.reprRegistry = new dojo.AdapterRegistry();
+dojo.lang.registerRepr = function(/*String*/name, /*Function*/check, /*Function*/wrap, /*Boolean?*/override){
+	// summary:
+	//	Register a repr function.  repr functions should take
+	//	one argument and return a string representation of it
+	//	suitable for developers, primarily used when debugging.
+	//
+	//	If override is given, it is used as the highest priority
+	//	repr, otherwise it will be used as the lowest.
+
+	dojo.lang.reprRegistry.register(name, check, wrap, override);
+};
+
+dojo.lang.repr = function(/*Object*/obj){
+	// summary: Return a "programmer representation" for an object
+	// description: returns a string representation of an object suitable for developers, primarily used when debugging
+
+	if(typeof(obj) == "undefined"){
+		// obj: undefined
+		return "undefined"; // String
+	}else if(obj === null){
+		// obj: null
+		return "null"; // String
+	}
+
+	try{
+		if(typeof(obj["__repr__"]) == 'function'){
+			return obj["__repr__"]();
+		}else if((typeof(obj["repr"]) == 'function')&&(obj.repr != arguments.callee)){
+			return obj["repr"]();
+		}
+		return dojo.lang.reprRegistry.match(obj);
+	}catch(e){
+		if(typeof(obj.NAME) == 'string' && (
+				obj.toString == Function.prototype.toString ||
+				obj.toString == Object.prototype.toString
+			)){
+			return obj.NAME; // String
+		}
+	}
+
+	if(typeof(obj) == "function"){
+		// obj: Function
+		obj = (obj + "").replace(/^\s+/, "");
+		var idx = obj.indexOf("{");
+		if(idx != -1){
+			obj = obj.substr(0, idx) + "{...}";
+		}
+	}
+	return obj + ""; // String
+}
+
+dojo.lang.reprArrayLike = function(/*Array*/arr){
+	// summary: Maps each element of arr to dojo.lang.repr and provides output in an array-like format
+	// description: returns an array-like string representation of the provided array suitable for developers, primarily used when debugging
+	try{
+		var na = dojo.lang.map(arr, dojo.lang.repr);
+		return "[" + na.join(", ") + "]"; // String
+	}catch(e){ }
+};
+
+(function(){
+	var m = dojo.lang;
+	m.registerRepr("arrayLike", m.isArrayLike, m.reprArrayLike);
+	m.registerRepr("string", m.isString, m.reprString);
+	m.registerRepr("numbers", m.isNumber, m.reprNumber);
+	m.registerRepr("boolean", m.isBoolean, m.reprNumber);
+	// m.registerRepr("numbers", m.typeMatcher("number", "boolean"), m.reprNumber);
+})();

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/Streamer.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/Streamer.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/Streamer.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,99 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.timing.Streamer");
+dojo.require("dojo.lang.timing.Timer");
+
+dojo.lang.timing.Streamer = function(
+	/* function */input, 
+	/* function */output, 
+	/* int */interval, 
+	/* int */minimum,
+	/* array */initialData
+){
+	//	summary
+	//	Streamer will take an input function that pushes N datapoints into a
+	//		queue, and will pass the next point in that queue out to an
+	//		output function at the passed interval; this way you can emulate
+	//		a constant buffered stream of data.
+	//	input: the function executed when the internal queue reaches minimumSize
+	//	output: the function executed on internal tick
+	//	interval: the interval in ms at which the output function is fired.
+	//	minimum: the minimum number of elements in the internal queue.
+
+	var self = this;
+	var queue = [];
+
+	//	public properties
+	this.interval = interval || 1000;
+	this.minimumSize = minimum || 10;	//	latency usually == interval * minimumSize
+	this.inputFunction = input || function(q){ };
+	this.outputFunction = output || function(point){ };
+
+	//	more setup
+	var timer = new dojo.lang.timing.Timer(this.interval);
+	var tick = function(){
+		self.onTick(self);
+
+		if(queue.length < self.minimumSize){
+			self.inputFunction(queue);
+		}
+
+		var obj = queue.shift();
+		while(typeof(obj) == "undefined" && queue.length > 0){
+			obj = queue.shift();
+		}
+		
+		//	check to see if the input function needs to be fired
+		//	stop before firing the output function
+		//	TODO: relegate this to the output function?
+		if(typeof(obj) == "undefined"){
+			self.stop();
+			return;
+		}
+
+		//	call the output function.
+		self.outputFunction(obj);
+	};
+
+	this.setInterval = function(/* int */ms){
+		//	summary
+		//	sets the interval in milliseconds of the internal timer
+		this.interval = ms;
+		timer.setInterval(ms);
+	};
+
+	this.onTick = function(/* dojo.lang.timing.Streamer */obj){ };
+	// wrap the timer functions so that we can connect to them if needed.
+	this.start = function(){
+		//	summary
+		//	starts the Streamer
+		if(typeof(this.inputFunction) == "function" && typeof(this.outputFunction) == "function"){
+			timer.start();
+			return;
+		}
+		dojo.raise("You cannot start a Streamer without an input and an output function.");
+	};
+	this.onStart = function(){ };
+	this.stop = function(){
+		//	summary
+		//	stops the Streamer
+		timer.stop();
+	};
+	this.onStop = function(){ };
+
+	//	finish initialization
+	timer.onTick = this.tick;
+	timer.onStart = this.onStart;
+	timer.onStop = this.onStop;
+	if(initialData){
+		queue.concat(initialData);
+	}
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/Timer.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/Timer.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/Timer.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,64 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.timing.Timer");
+dojo.require("dojo.lang.func");
+
+dojo.lang.timing.Timer = function(/*int*/ interval){
+	// summary: Timer object executes an "onTick()" method repeatedly at a specified interval. 
+	//			repeatedly at a given interval.
+	// interval: Interval between function calls, in milliseconds.
+	this.timer = null;
+	this.isRunning = false;
+	this.interval = interval;
+
+	this.onStart = null;
+	this.onStop = null;
+};
+
+dojo.extend(dojo.lang.timing.Timer, {
+	onTick : function(){
+		// summary: Method called every time the interval passes.  Override to do something useful.
+	},
+		
+	setInterval : function(interval){
+		// summary: Reset the interval of a timer, whether running or not.
+		// interval: New interval, in milliseconds.
+		if (this.isRunning){
+			dj_global.clearInterval(this.timer);
+		}
+		this.interval = interval;
+		if (this.isRunning){
+			this.timer = dj_global.setInterval(dojo.lang.hitch(this, "onTick"), this.interval);
+		}
+	},
+	
+	start : function(){
+		// summary: Start the timer ticking.
+		// description: Calls the "onStart()" handler, if defined.
+		// 				Note that the onTick() function is not called right away, 
+		//				only after first interval passes.
+		if (typeof this.onStart == "function"){
+			this.onStart();
+		}
+		this.isRunning = true;
+		this.timer = dj_global.setInterval(dojo.lang.hitch(this, "onTick"), this.interval);
+	},
+	
+	stop : function(){
+		// summary: Stop the timer.
+		// description: Calls the "onStop()" handler, if defined.
+		if (typeof this.onStop == "function"){
+			this.onStop();
+		}
+		this.isRunning = false;
+		dj_global.clearInterval(this.timer);
+	}
+});

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/__package__.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/__package__.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/timing/__package__.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,11 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.timing.*");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/type.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/type.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang/type.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,251 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang.type");
+dojo.require("dojo.lang.common");
+
+dojo.lang.whatAmI = function(value) {
+	dojo.deprecated("dojo.lang.whatAmI", "use dojo.lang.getType instead", "0.5");
+	return dojo.lang.getType(value);
+}
+dojo.lang.whatAmI.custom = {};
+
+dojo.lang.getType = function(/* anything */ value){
+	/* summary:
+	 *	 Attempts to determine what type value is.
+	 * value: Any literal value or object instance.
+	 */
+	try {
+		if(dojo.lang.isArray(value)) { 
+			return "array";	//	string 
+		}
+		if(dojo.lang.isFunction(value)) { 
+			return "function";	//	string 
+		}
+		if(dojo.lang.isString(value)) { 
+			return "string";	//	string 
+		}
+		if(dojo.lang.isNumber(value)) { 
+			return "number";	//	string 
+		}
+		if(dojo.lang.isBoolean(value)) { 
+			return "boolean";	//	string 
+		}
+		if(dojo.lang.isAlien(value)) { 
+			return "alien";	//	string 
+		}
+		if(dojo.lang.isUndefined(value)) { 
+			return "undefined";	//	string 
+		}
+		// FIXME: should this go first?
+		for(var name in dojo.lang.whatAmI.custom) {
+			if(dojo.lang.whatAmI.custom[name](value)) {
+				return name;	//	string
+			}
+		}
+		if(dojo.lang.isObject(value)) { 
+			return "object";	//	string 
+		}
+	} catch(e) {}
+	return "unknown";	//	string
+}
+
+dojo.lang.isNumeric = function(/* anything */ value){
+	/* summary:
+	 *   Returns true if value can be interpreted as a number
+	 * value: Any literal value or object instance.
+	 */
+	 
+	/* examples: 
+	 *   dojo.lang.isNumeric(3);                 // returns true
+	 *   dojo.lang.isNumeric("3");               // returns true
+	 *   dojo.lang.isNumeric(new Number(3));     // returns true
+	 *   dojo.lang.isNumeric(new String("3"));   // returns true
+	 *
+	 *   dojo.lang.isNumeric(3/0);               // returns false
+	 *   dojo.lang.isNumeric("foo");             // returns false
+	 *   dojo.lang.isNumeric(new Number("foo")); // returns false
+	 *   dojo.lang.isNumeric(false);             // returns false
+	 *   dojo.lang.isNumeric(true);              // returns false
+	 */
+	return (!isNaN(value) 
+		&& isFinite(value) 
+		&& (value != null) 
+		&& !dojo.lang.isBoolean(value) 
+		&& !dojo.lang.isArray(value) 
+		&& !/^\s*$/.test(value)
+	);	//	boolean
+}
+
+dojo.lang.isBuiltIn = function(/* anything */ value){
+	/* summary:
+	 *   Returns true if value is of a type provided by core JavaScript
+	 * description: 
+	 *   Returns true for any literal, and for any object that is an 
+	 *   instance of a built-in type like String, Number, Boolean, 
+	 *   Array, Function, or Error.
+	 * value: Any literal value or object instance.
+	 */
+	return (dojo.lang.isArray(value)
+		|| dojo.lang.isFunction(value)	
+		|| dojo.lang.isString(value)
+		|| dojo.lang.isNumber(value)
+		|| dojo.lang.isBoolean(value)
+		|| (value == null)
+		|| (value instanceof Error)
+		|| (typeof value == "error") 
+	);	//	boolean
+}
+
+dojo.lang.isPureObject = function(/* anything */ value){
+	/* summary:
+	 *   Returns true for any value where the value of value.constructor == Object
+	 * description: 
+	 *   Returns true for any literal, and for any object that is an 
+	 *   instance of a built-in type like String, Number, Boolean, 
+	 *   Array, Function, or Error.
+	 * value: Any literal value or object instance.
+	 */
+	
+	/* examples: 
+	 *   dojo.lang.isPureObject(new Object()); // returns true
+	 *   dojo.lang.isPureObject({a: 1, b: 2}); // returns true
+	 * 
+	 *   dojo.lang.isPureObject(new Date());   // returns false
+	 *   dojo.lang.isPureObject([11, 2, 3]);   // returns false
+	 */
+	return ((value != null) 
+		&& dojo.lang.isObject(value) 
+		&& value.constructor == Object
+	);	//	boolean
+}
+
+dojo.lang.isOfType = function(/* anything */ value, /* function */ type, /* object? */ keywordParameters) {
+	/* summary:
+	 *	 Returns true if 'value' is of type 'type'
+	 * description: 
+	 *	 Given a value and a datatype, this method returns true if the
+	 *	 type of the value matches the datatype. The datatype parameter
+	 *	 can be an array of datatypes, in which case the method returns
+	 *	 true if the type of the value matches any of the datatypes.
+	 * value: Any literal value or object instance.
+	 * type: A class of object, or a literal type, or the string name of a type, or an array with a list of types.
+	 * keywordParameters: {optional: boolean}
+	 */
+	 
+	/* examples: 
+	 *   dojo.lang.isOfType("foo", String);                // returns true
+	 *   dojo.lang.isOfType(12345, Number);                // returns true
+	 *   dojo.lang.isOfType(false, Boolean);               // returns true
+	 *   dojo.lang.isOfType([6, 8], Array);                // returns true
+	 *   dojo.lang.isOfType(dojo.lang.isOfType, Function); // returns true
+	 *   dojo.lang.isOfType({foo: "bar"}, Object);         // returns true
+	 *   dojo.lang.isOfType(new Date(), Date);             // returns true
+	 *
+	 *   dojo.lang.isOfType("foo", "string");                // returns true
+	 *   dojo.lang.isOfType(12345, "number");                // returns true
+	 *   dojo.lang.isOfType(false, "boolean");               // returns true
+	 *   dojo.lang.isOfType([6, 8], "array");                // returns true
+	 *   dojo.lang.isOfType(dojo.lang.isOfType, "function"); // returns true
+	 *   dojo.lang.isOfType({foo: "bar"}, "object");         // returns true
+	 *   dojo.lang.isOfType(xxxxx, "undefined");             // returns true
+	 *   dojo.lang.isOfType(null, "null");                   // returns true
+	 *
+	 *   dojo.lang.isOfType("foo", [Number, String, Boolean]); // returns true
+	 *   dojo.lang.isOfType(12345, [Number, String, Boolean]); // returns true
+	 *   dojo.lang.isOfType(false, [Number, String, Boolean]); // returns true
+	 *
+	 *   dojo.lang.isOfType(null, Date, {optional: true} );    // returns true	// description: 
+	 */
+	var optional = false;
+	if (keywordParameters) {
+		optional = keywordParameters["optional"];
+	}
+	if (optional && ((value === null) || dojo.lang.isUndefined(value))) {
+		return true;	//	boolean
+	}
+	if(dojo.lang.isArray(type)){
+		var arrayOfTypes = type;
+		for(var i in arrayOfTypes){
+			var aType = arrayOfTypes[i];
+			if(dojo.lang.isOfType(value, aType)) {
+				return true; 	//	boolean
+			}
+		}
+		return false;	//	boolean
+	}else{
+		if(dojo.lang.isString(type)){
+			type = type.toLowerCase();
+		}
+		switch (type) {
+			case Array:
+			case "array":
+				return dojo.lang.isArray(value);	//	boolean
+			case Function:
+			case "function":
+				return dojo.lang.isFunction(value);	//	boolean
+			case String:
+			case "string":
+				return dojo.lang.isString(value);	//	boolean
+			case Number:
+			case "number":
+				return dojo.lang.isNumber(value);	//	boolean
+			case "numeric":
+				return dojo.lang.isNumeric(value);	//	boolean
+			case Boolean:
+			case "boolean":
+				return dojo.lang.isBoolean(value);	//	boolean
+			case Object:
+			case "object":
+				return dojo.lang.isObject(value);	//	boolean
+			case "pureobject":
+				return dojo.lang.isPureObject(value);	//	boolean
+			case "builtin":
+				return dojo.lang.isBuiltIn(value);	//	boolean
+			case "alien":
+				return dojo.lang.isAlien(value);	//	boolean
+			case "undefined":
+				return dojo.lang.isUndefined(value);	//	boolean
+			case null:
+			case "null":
+				return (value === null);	//	boolean
+			case "optional":
+				dojo.deprecated('dojo.lang.isOfType(value, [type, "optional"])', 'use dojo.lang.isOfType(value, type, {optional: true} ) instead', "0.5");
+				return ((value === null) || dojo.lang.isUndefined(value));	//	boolean
+			default:
+				if (dojo.lang.isFunction(type)) {
+					return (value instanceof type);	//	boolean
+				} else {
+					dojo.raise("dojo.lang.isOfType() was passed an invalid type");
+				}
+		}
+	}
+	dojo.raise("If we get here, it means a bug was introduced above.");
+}
+
+dojo.lang.getObject=function(/* String */ str){
+	// summary:
+	//   Will return an object, if it exists, based on the name in the passed string.
+	var parts=str.split("."), i=0, obj=dj_global; 
+	do{ 
+		obj=obj[parts[i++]]; 
+	}while(i<parts.length&&obj); 
+	return (obj!=dj_global)?obj:null;	//	Object
+}
+
+dojo.lang.doesObjectExist=function(/* String */ str){
+	// summary:
+	//   Check to see if object [str] exists, based on the passed string.
+	var parts=str.split("."), i=0, obj=dj_global; 
+	do{ 
+		obj=obj[parts[i++]]; 
+	}while(i<parts.length&&obj); 
+	return (obj&&obj!=dj_global);	//	boolean
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/lang.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,14 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.lang");
+dojo.require("dojo.lang.common");
+
+dojo.deprecated("dojo.lang", "replaced by dojo.lang.common", "0.5");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/loader.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/loader.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/loader.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,727 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/*
+ * loader.js - A bootstrap module.  Runs before the hostenv_*.js file. Contains all of the package loading methods.
+ */
+
+//A semi-colon is at the start of the line because after doing a build, this function definition
+//get compressed onto the same line as the last line in bootstrap1.js. That list line is just a
+//curly bracket, and the browser complains about that syntax. The semicolon fixes it. Putting it
+//here instead of at the end of bootstrap1.js, since it is more of an issue for this file, (using
+//the closure), and bootstrap1.js could change in the future.
+;(function(){
+	//Additional properties for dojo.hostenv
+	var _addHostEnv = {
+		pkgFileName: "__package__",
+	
+		// for recursion protection
+		loading_modules_: {},
+		loaded_modules_: {},
+		addedToLoadingCount: [],
+		removedFromLoadingCount: [],
+	
+		inFlightCount: 0,
+	
+		// FIXME: it should be possible to pull module prefixes in from djConfig
+		modulePrefixes_: {
+			dojo: {name: "dojo", value: "src"}
+		},
+
+		setModulePrefix: function(/*String*/module, /*String*/prefix){
+			// summary: establishes module/prefix pair
+			this.modulePrefixes_[module] = {name: module, value: prefix};
+		},
+
+		moduleHasPrefix: function(/*String*/module){
+			// summary: checks to see if module has been established
+			var mp = this.modulePrefixes_;
+			return Boolean(mp[module] && mp[module].value); // Boolean
+		},
+
+		getModulePrefix: function(/*String*/module){
+			// summary: gets the prefix associated with module
+			if(this.moduleHasPrefix(module)){
+				return this.modulePrefixes_[module].value; // String
+			}
+			return module; // String
+		},
+
+		getTextStack: [],
+		loadUriStack: [],
+		loadedUris: [],
+	
+		//WARNING: This variable is referenced by packages outside of bootstrap: FloatingPane.js and undo/browser.js
+		post_load_: false,
+		
+		//Egad! Lots of test files push on this directly instead of using dojo.addOnLoad.
+		modulesLoadedListeners: [],
+		unloadListeners: [],
+		loadNotifying: false
+	};
+	
+	//Add all of these properties to dojo.hostenv
+	for(var param in _addHostEnv){
+		dojo.hostenv[param] = _addHostEnv[param];
+	}
+})();
+
+dojo.hostenv.loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){
+// summary:
+//	Load a Javascript module given a relative path
+//
+// description:
+//	Loads and interprets the script located at relpath, which is relative to the
+//	script root directory.  If the script is found but its interpretation causes
+//	a runtime exception, that exception is not caught by us, so the caller will
+//	see it.  We return a true value if and only if the script is found.
+//
+//	For now, we do not have an implementation of a true search path.  We
+//	consider only the single base script uri, as returned by getBaseScriptUri().
+//
+// relpath: A relative path to a script (no leading '/', and typically
+// 	ending in '.js').
+// module: A module whose existance to check for after loading a path.
+//	Can be used to determine success or failure of the load.
+// cb: a callback function to pass the result of evaluating the script
+
+	var uri;
+	if(relpath.charAt(0) == '/' || relpath.match(/^\w+:/)){
+		// dojo.raise("relpath '" + relpath + "'; must be relative");
+		uri = relpath;
+	}else{
+		uri = this.getBaseScriptUri() + relpath;
+	}
+	if(djConfig.cacheBust && dojo.render.html.capable){
+		uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,"");
+	}
+	try{
+		return !module ? this.loadUri(uri, cb) : this.loadUriAndCheck(uri, module, cb); // Boolean
+	}catch(e){
+		dojo.debug(e);
+		return false; // Boolean
+	}
+}
+
+dojo.hostenv.loadUri = function(/*String (URL)*/uri, /*Function?*/cb){
+// summary:
+//	Loads JavaScript from a URI
+//
+// description:
+//	Reads the contents of the URI, and evaluates the contents.  This is used to load modules as well
+//	as resource bundles.  Returns true if it succeeded. Returns false if the URI reading failed.
+//	Throws if the evaluation throws.
+//
+// uri: a uri which points at the script to be loaded
+// cb: a callback function to process the result of evaluating the script as an expression, typically
+//	used by the resource bundle loader to load JSON-style resources
+
+	if(this.loadedUris[uri]){
+		return true; // Boolean
+	}
+	var contents = this.getText(uri, null, true);
+	if(!contents){ return false; } // Boolean
+	this.loadedUris[uri] = true;
+	if(cb){ contents = '('+contents+')'; }
+	var value = dj_eval(contents);
+	if(cb){ cb(value); }
+	return true; // Boolean
+}
+
+// FIXME: probably need to add logging to this method
+dojo.hostenv.loadUriAndCheck = function(/*String (URL)*/uri, /*String*/moduleName, /*Function?*/cb){
+	// summary: calls loadUri then findModule and returns true if both succeed
+	var ok = true;
+	try{
+		ok = this.loadUri(uri, cb);
+	}catch(e){
+		dojo.debug("failed loading ", uri, " with error: ", e);
+	}
+	return Boolean(ok && this.findModule(moduleName, false)); // Boolean
+}
+
+dojo.loaded = function(){ }
+dojo.unloaded = function(){ }
+
+dojo.hostenv.loaded = function(){
+	this.loadNotifying = true;
+	this.post_load_ = true;
+	var mll = this.modulesLoadedListeners;
+	for(var x=0; x<mll.length; x++){
+		mll[x]();
+	}
+
+	//Clear listeners so new ones can be added
+	//For other xdomain package loads after the initial load.
+	this.modulesLoadedListeners = [];
+	this.loadNotifying = false;
+
+	dojo.loaded();
+}
+
+dojo.hostenv.unloaded = function(){
+	var mll = this.unloadListeners;
+	while(mll.length){
+		(mll.pop())();
+	}
+	dojo.unloaded();
+}
+
+dojo.addOnLoad = function(/*Object?*/obj, /*String|Function*/functionName) {
+// summary:
+//	Registers a function to be triggered after the DOM has finished loading 
+//	and widgets declared in markup have been instantiated.  Images and CSS files
+//	may or may not have finished downloading when the specified function is called.
+//	(Note that widgets' CSS and HTML code is guaranteed to be downloaded before said
+//	widgets are instantiated.)
+//
+// usage:
+//	dojo.addOnLoad(functionPointer)
+//	dojo.addOnLoad(object, "functionName")
+
+	var dh = dojo.hostenv;
+	if(arguments.length == 1) {
+		dh.modulesLoadedListeners.push(obj);
+	} else if(arguments.length > 1) {
+		dh.modulesLoadedListeners.push(function() {
+			obj[functionName]();
+		});
+	}
+
+	//Added for xdomain loading. dojo.addOnLoad is used to
+	//indicate callbacks after doing some dojo.require() statements.
+	//In the xdomain case, if all the requires are loaded (after initial
+	//page load), then immediately call any listeners.
+	if(dh.post_load_ && dh.inFlightCount == 0 && !dh.loadNotifying){
+		dh.callLoaded();
+	}
+}
+
+dojo.addOnUnload = function(/*Object?*/obj, /*String|Function?*/functionName){
+// summary: registers a function to be triggered when the page unloads
+//
+// usage:
+//	dojo.addOnLoad(functionPointer)
+//	dojo.addOnLoad(object, "functionName")
+	var dh = dojo.hostenv;
+	if(arguments.length == 1){
+		dh.unloadListeners.push(obj);
+	} else if(arguments.length > 1) {
+		dh.unloadListeners.push(function() {
+			obj[functionName]();
+		});
+	}
+}
+
+dojo.hostenv.modulesLoaded = function(){
+	if(this.post_load_){ return; }
+	if(this.loadUriStack.length==0 && this.getTextStack.length==0){
+		if(this.inFlightCount > 0){ 
+			dojo.debug("files still in flight!");
+			return;
+		}
+		dojo.hostenv.callLoaded();
+	}
+}
+
+dojo.hostenv.callLoaded = function(){
+	if(typeof setTimeout == "object"){
+		setTimeout("dojo.hostenv.loaded();", 0);
+	}else{
+		dojo.hostenv.loaded();
+	}
+}
+
+dojo.hostenv.getModuleSymbols = function(/*String*/modulename){
+// summary:
+//	Converts a module name in dotted JS notation to an array representing the path in the source tree
+
+	var syms = modulename.split(".");
+	for(var i = syms.length; i>0; i--){
+		var parentModule = syms.slice(0, i).join(".");
+		if ((i==1) && !this.moduleHasPrefix(parentModule)){		
+			//Support default module directory (sibling of dojo)
+			syms[0] = "../" + syms[0];
+		}else{
+			var parentModulePath = this.getModulePrefix(parentModule);
+			if(parentModulePath != parentModule){
+				syms.splice(0, i, parentModulePath);
+				break;
+			}
+		}
+	}
+	return syms; // Array
+}
+
+dojo.hostenv._global_omit_module_check = false;
+dojo.hostenv.loadModule = function(/*String*/moduleName, /*Boolean?*/exactOnly, /*Boolean?*/omitModuleCheck){
+// summary:
+//	loads a Javascript module from the appropriate URI
+//
+// description:
+//	loadModule("A.B") first checks to see if symbol A.B is defined. 
+//	If it is, it is simply returned (nothing to do).
+//	
+//	If it is not defined, it will look for "A/B.js" in the script root directory,
+//	followed by "A.js".
+//	
+//	It throws if it cannot find a file to load, or if the symbol A.B is not
+//	defined after loading.
+//	
+//	It returns the object A.B.
+//	
+//	This does nothing about importing symbols into the current package.
+//	It is presumed that the caller will take care of that. For example, to import
+//	all symbols:
+//	
+//	   with (dojo.hostenv.loadModule("A.B")) {
+//	      ...
+//	   }
+//	
+//	And to import just the leaf symbol:
+//	
+//	   var B = dojo.hostenv.loadModule("A.B");
+//	   ...
+//	
+//	dj_load is an alias for dojo.hostenv.loadModule
+
+	if(!moduleName){ return; }
+	omitModuleCheck = this._global_omit_module_check || omitModuleCheck;
+	var module = this.findModule(moduleName, false);
+	if(module){
+		return module;
+	}
+
+	// protect against infinite recursion from mutual dependencies
+	if(dj_undef(moduleName, this.loading_modules_)){
+		this.addedToLoadingCount.push(moduleName);
+	}
+	this.loading_modules_[moduleName] = 1;
+
+	// convert periods to slashes
+	var relpath = moduleName.replace(/\./g, '/') + '.js';
+
+	var nsyms = moduleName.split(".");
+	
+	// this line allowed loading of a module manifest as if it were a namespace
+	// it's an interesting idea, but shouldn't be combined with 'namespaces' proper
+	// and leads to unwanted dependencies
+	// the effect can be achieved in other (albeit less-flexible) ways now, so I am
+	// removing this pending further design work
+	// perhaps we can explicitly define this idea of a 'module manifest', and subclass
+	// 'namespace manifest' from that
+	//dojo.getNamespace(nsyms[0]);
+
+	var syms = this.getModuleSymbols(moduleName);
+	var startedRelative = ((syms[0].charAt(0) != '/') && !syms[0].match(/^\w+:/));
+	var last = syms[syms.length - 1];
+	var ok;
+	// figure out if we're looking for a full package, if so, we want to do
+	// things slightly diffrently
+	if(last=="*"){
+		moduleName = nsyms.slice(0, -1).join('.');
+		while(syms.length){
+			syms.pop();
+			syms.push(this.pkgFileName);
+			relpath = syms.join("/") + '.js';
+			if(startedRelative && relpath.charAt(0)=="/"){
+				relpath = relpath.slice(1);
+			}
+			ok = this.loadPath(relpath, !omitModuleCheck ? moduleName : null);
+			if(ok){ break; }
+			syms.pop();
+		}
+	}else{
+		relpath = syms.join("/") + '.js';
+		moduleName = nsyms.join('.');
+		var modArg = !omitModuleCheck ? moduleName : null;
+		ok = this.loadPath(relpath, modArg);
+		if(!ok && !exactOnly){
+			syms.pop();
+			while(syms.length){
+				relpath = syms.join('/') + '.js';
+				ok = this.loadPath(relpath, modArg);
+				if(ok){ break; }
+				syms.pop();
+				relpath = syms.join('/') + '/'+this.pkgFileName+'.js';
+				if(startedRelative && relpath.charAt(0)=="/"){
+					relpath = relpath.slice(1);
+				}
+				ok = this.loadPath(relpath, modArg);
+				if(ok){ break; }
+			}
+		}
+
+		if(!ok && !omitModuleCheck){
+			dojo.raise("Could not load '" + moduleName + "'; last tried '" + relpath + "'");
+		}
+	}
+
+	// check that the symbol was defined
+	//Don't bother if we're doing xdomain (asynchronous) loading.
+	if(!omitModuleCheck && !this["isXDomain"]){
+		// pass in false so we can give better error
+		module = this.findModule(moduleName, false);
+		if(!module){
+			dojo.raise("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'"); 
+		}
+	}
+
+	return module;
+}
+
+dojo.hostenv.startPackage = function(/*String*/packageName){
+// summary:
+//	Creates a JavaScript package
+//
+// description:
+//	startPackage("A.B") follows the path, and at each level creates a new empty
+//	object or uses what already exists. It returns the result.
+//
+// packageName: the package to be created as a String in dot notation
+
+	//Make sure we have a string.
+	var fullPkgName = String(packageName);
+	var strippedPkgName = fullPkgName;
+
+	var syms = packageName.split(/\./);
+	if(syms[syms.length-1]=="*"){
+		syms.pop();
+		strippedPkgName = syms.join(".");
+	}
+	var evaledPkg = dojo.evalObjPath(strippedPkgName, true);
+	this.loaded_modules_[fullPkgName] = evaledPkg;
+	this.loaded_modules_[strippedPkgName] = evaledPkg;
+	
+	return evaledPkg; // Object
+}
+
+dojo.hostenv.findModule = function(/*String*/moduleName, /*Boolean?*/mustExist){
+// summary:
+//	Returns the Object representing the module, if it exists, otherwise null.
+//
+// moduleName A fully qualified module including package name, like 'A.B'.
+// mustExist Optional, default false. throw instead of returning null
+//	if the module does not currently exist.
+
+	var lmn = String(moduleName);
+
+	if(this.loaded_modules_[lmn]){
+		return this.loaded_modules_[lmn]; // Object
+	}
+
+	if(mustExist){
+		dojo.raise("no loaded module named '" + moduleName + "'");
+	}
+	return null; // null
+}
+
+//Start of old bootstrap2:
+
+dojo.kwCompoundRequire = function(/*Object containing Arrays*/modMap){
+// description:
+//	This method taks a "map" of arrays which one can use to optionally load dojo
+//	modules. The map is indexed by the possible dojo.hostenv.name_ values, with
+//	two additional values: "default" and "common". The items in the "default"
+//	array will be loaded if none of the other items have been choosen based on
+//	the hostenv.name_ item. The items in the "common" array will _always_ be
+//	loaded, regardless of which list is chosen.  Here's how it's normally
+//	called:
+//	
+//	dojo.kwCompoundRequire({
+//		browser: [
+//			["foo.bar.baz", true, true], // an example that passes multiple args to loadModule()
+//			"foo.sample.*",
+//			"foo.test,
+//		],
+//		default: [ "foo.sample.*" ],
+//		common: [ "really.important.module.*" ]
+//	});
+
+	var common = modMap["common"]||[];
+	var result = modMap[dojo.hostenv.name_] ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]);
+
+	for(var x=0; x<result.length; x++){
+		var curr = result[x];
+		if(curr.constructor == Array){
+			dojo.hostenv.loadModule.apply(dojo.hostenv, curr);
+		}else{
+			dojo.hostenv.loadModule(curr);
+		}
+	}
+}
+
+dojo.require = function(/*String*/ resourceName){
+	// summary
+	//	Ensure that the given resource (ie, javascript
+	//	source file) has been loaded.
+	// description
+	//	dojo.require() is similar to C's #include command or java's "import" command.
+	//	You call dojo.require() to pull in the resources (ie, javascript source files)
+	//	that define the functions you are using. 
+	//
+	//	Note that in the case of a build, many resources have already been included
+	//	into dojo.js (ie, many of the javascript source files have been compressed and
+	//	concatened into dojo.js), so many dojo.require() calls will simply return
+	//	without downloading anything.
+	dojo.hostenv.loadModule.apply(dojo.hostenv, arguments);
+}
+
+dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){
+	// summary
+	//	If the condition is true then call dojo.require() for the specified resource
+	var arg0 = arguments[0];
+	if((arg0 === true)||(arg0=="common")||(arg0 && dojo.render[arg0].capable)){
+		var args = [];
+		for (var i = 1; i < arguments.length; i++) { args.push(arguments[i]); }
+		dojo.require.apply(dojo, args);
+	}
+}
+
+dojo.requireAfterIf = dojo.requireIf;
+
+dojo.provide = function(/*String*/ resourceName){
+	// summary
+	//	Each javascript source file must have (exactly) one dojo.provide()
+	//	call at the top of the file, corresponding to the file name.
+	//	For example, dojo/src/foo.js must have dojo.provide("dojo.foo"); at the top of the file.
+	//
+	// description
+	//	Each javascript source file is called a resource.  When a resource
+	//	is loaded by the browser, dojo.provide() registers that it has
+	//	been loaded.
+	//	
+	//	For backwards compatibility reasons, in addition to registering the resource,
+	//	dojo.provide() also ensures that the javascript object for the module exists.  For
+	//	example, dojo.provide("dojo.html.common"), in addition to registering that common.js
+	//	is a resource for the dojo.html module, will ensure that the dojo.html javascript object
+	//	exists, so that calls like dojo.html.foo = function(){ ... } don't fail.
+	//
+	//	In the case of a build (or in the future, a rollup), where multiple javascript source
+	//	files are combined into one bigger file (similar to a .lib or .jar file), that file
+	//	will contain multiple dojo.provide() calls, to note that it includes
+	//	multiple resources.
+	return dojo.hostenv.startPackage.apply(dojo.hostenv, arguments);
+}
+
+dojo.registerModulePath = function(/*String*/module, /*String*/prefix){
+	// summary: maps a module name to a path
+	// description: An unregistered module is given the default path of ../<module>,
+	//	relative to Dojo root. For example, module acme is mapped to ../acme.
+	//	If you want to use a different module name, use dojo.registerModulePath. 
+	return dojo.hostenv.setModulePrefix(module, prefix);
+}
+
+dojo.setModulePrefix = function(/*String*/module, /*String*/prefix){
+	// summary: maps a module name to a path
+	dojo.deprecated('dojo.setModulePrefix("' + module + '", "' + prefix + '")', "replaced by dojo.registerModulePath", "0.5");
+	return dojo.registerModulePath(module, prefix);
+}
+
+dojo.exists = function(/*Object*/obj, /*String*/name){
+	// summary: determine if an object supports a given method
+	// description: useful for longer api chains where you have to test each object in the chain
+	var p = name.split(".");
+	for(var i = 0; i < p.length; i++){
+		if(!obj[p[i]]){ return false; } // Boolean
+		obj = obj[p[i]];
+	}
+	return true; // Boolean
+}
+
+// Localization routines
+
+dojo.hostenv.normalizeLocale = function(/*String?*/locale){
+//	summary:
+//		Returns canonical form of locale, as used by Dojo.  All variants are case-insensitive and are separated by '-'
+//		as specified in RFC 3066. If no locale is specified, the user agent's default is returned.
+
+	return locale ? locale.toLowerCase() : dojo.locale; // String
+};
+
+dojo.hostenv.searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
+//	summary:
+//		A helper method to assist in searching for locale-based resources.  Will iterate through
+//		the variants of a particular locale, either up or down, executing a callback function.
+//		For example, "en-us" and true will try "en-us" followed by "en" and finally "ROOT".
+
+	locale = dojo.hostenv.normalizeLocale(locale);
+
+	var elements = locale.split('-');
+	var searchlist = [];
+	for(var i = elements.length; i > 0; i--){
+		searchlist.push(elements.slice(0, i).join('-'));
+	}
+	searchlist.push(false);
+	if(down){searchlist.reverse();}
+
+	for(var j = searchlist.length - 1; j >= 0; j--){
+		var loc = searchlist[j] || "ROOT";
+		var stop = searchFunc(loc);
+		if(stop){ break; }
+	}
+}
+
+//These two functions are placed outside of preloadLocalizations
+//So that the xd loading can use/override them.
+dojo.hostenv.localesGenerated /***BUILD:localesGenerated***/; // value will be inserted here at build time, if necessary
+
+dojo.hostenv.registerNlsPrefix = function(){
+// summary:
+//	Register module "nls" to point where Dojo can find pre-built localization files
+	dojo.registerModulePath("nls","nls");	
+}
+
+dojo.hostenv.preloadLocalizations = function(){
+// summary:
+//	Load built, flattened resource bundles, if available for all locales used in the page.
+//	Execute only once.  Note that this is a no-op unless there is a build.
+
+	if(dojo.hostenv.localesGenerated){
+		dojo.hostenv.registerNlsPrefix();
+
+		function preload(locale){
+			locale = dojo.hostenv.normalizeLocale(locale);
+			dojo.hostenv.searchLocalePath(locale, true, function(loc){
+				for(var i=0; i<dojo.hostenv.localesGenerated.length;i++){
+					if(dojo.hostenv.localesGenerated[i] == loc){
+						dojo["require"]("nls.dojo_"+loc);
+						return true; // Boolean
+					}
+				}
+				return false; // Boolean
+			});
+		}
+		preload();
+		var extra = djConfig.extraLocale||[];
+		for(var i=0; i<extra.length; i++){
+			preload(extra[i]);
+		}
+	}
+	dojo.hostenv.preloadLocalizations = function(){};
+}
+
+dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale){
+// summary:
+//	Declares translated resources and loads them if necessary, in the same style as dojo.require.
+//	Contents of the resource bundle are typically strings, but may be any name/value pair,
+//	represented in JSON format.  See also dojo.i18n.getLocalization.
+//
+// moduleName: name of the package containing the "nls" directory in which the bundle is found
+// bundleName: bundle name, i.e. the filename without the '.js' suffix
+// locale: the locale to load (optional)  By default, the browser's user locale as defined by dojo.locale
+//
+// description:
+//	Load translated resource bundles provided underneath the "nls" directory within a package.
+//	Translated resources may be located in different packages throughout the source tree.  For example,
+//	a particular widget may define one or more resource bundles, structured in a program as follows,
+//	where moduleName is mycode.mywidget and bundleNames available include bundleone and bundletwo:
+//	...
+//	mycode/
+//	 mywidget/
+//	  nls/
+//	   bundleone.js (the fallback translation, English in this example)
+//	   bundletwo.js (also a fallback translation)
+//	   de/
+//	    bundleone.js
+//	    bundletwo.js
+//	   de-at/
+//	    bundleone.js
+//	   en/
+//	    (empty; use the fallback translation)
+//	   en-us/
+//	    bundleone.js
+//	   en-gb/
+//	    bundleone.js
+//	   es/
+//	    bundleone.js
+//	    bundletwo.js
+//	  ...etc
+//	...
+//	Each directory is named for a locale as specified by RFC 3066, (http://www.ietf.org/rfc/rfc3066.txt),
+//	normalized in lowercase.  Note that the two bundles in the example do not define all the same variants.
+//	For a given locale, bundles will be loaded for that locale and all more general locales above it, including
+//	a fallback at the root directory.  For example, a declaration for the "de-at" locale will first
+//	load nls/de-at/bundleone.js, then nls/de/bundleone.js and finally nls/bundleone.js.  The data will
+//	be flattened into a single Object so that lookups will follow this cascading pattern.  An optional build
+//	step can preload the bundles to avoid data redundancy and the multiple network hits normally required to
+//	load these resources.
+
+	dojo.hostenv.preloadLocalizations();
+ 	var bundlePackage = [moduleName, "nls", bundleName].join(".");
+//NOTE: When loading these resources, the packaging does not match what is on disk.  This is an
+// implementation detail, as this is just a private data structure to hold the loaded resources.
+// e.g. tests/hello/nls/en-us/salutations.js is loaded as the object tests.hello.nls.salutations.en_us={...}
+// The structure on disk is intended to be most convenient for developers and translators, but in memory
+// it is more logical and efficient to store in a different order.  Locales cannot use dashes, since the
+// resulting path will not evaluate as valid JS, so we translate them to underscores.
+
+	var bundle = dojo.hostenv.findModule(bundlePackage);
+	if(bundle){
+		if(djConfig.localizationComplete && bundle._built){return;}
+		var jsLoc = dojo.hostenv.normalizeLocale(locale).replace('-', '_');
+		var translationPackage = bundlePackage+"."+jsLoc;
+		if(dojo.hostenv.findModule(translationPackage)){return;}
+	}
+
+	bundle = dojo.hostenv.startPackage(bundlePackage);
+	var syms = dojo.hostenv.getModuleSymbols(moduleName);
+	var modpath = syms.concat("nls").join("/");
+	var parent;
+	dojo.hostenv.searchLocalePath(locale, false, function(loc){
+		var jsLoc = loc.replace('-', '_');
+		var translationPackage = bundlePackage + "." + jsLoc;
+		var loaded = false;
+		if(!dojo.hostenv.findModule(translationPackage)){
+			// Mark loaded whether it's found or not, so that further load attempts will not be made
+			dojo.hostenv.startPackage(translationPackage);
+			var module = [modpath];
+			if(loc != "ROOT"){module.push(loc);}
+			module.push(bundleName);
+			var filespec = module.join("/") + '.js';
+			loaded = dojo.hostenv.loadPath(filespec, null, function(hash){
+				// Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
+				var clazz = function(){};
+				clazz.prototype = parent;
+				bundle[jsLoc] = new clazz();
+				for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
+			});
+		}else{
+			loaded = true;
+		}
+		if(loaded && bundle[jsLoc]){
+			parent = bundle[jsLoc];
+		}else{
+			bundle[jsLoc] = parent;
+		}
+	});
+};
+
+(function(){
+	// If other locales are used, dojo.requireLocalization should load them as well, by default.
+	// Override dojo.requireLocalization to do load the default bundle, then iterate through the
+	// extraLocale list and load those translations as well, unless a particular locale was requested.
+
+	var extra = djConfig.extraLocale;
+	if(extra){
+		if(!extra instanceof Array){
+			extra = [extra];
+		}
+
+		var req = dojo.requireLocalization;
+		dojo.requireLocalization = function(m, b, locale){
+			req(m,b,locale);
+			if(locale){return;}
+			for(var i=0; i<extra.length; i++){
+				req(m,b,extra[i]);
+			}
+		};
+	}
+})();

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/loader_xd.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/loader_xd.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/loader_xd.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,437 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+//Cross-domain package loader.
+
+//FIXME: How will xd loading work with debugAtAllCosts? Any bad interactions?
+//FIXME: widgets won't work fully (HTML/CSS) and also because of the requireIf() thing.
+
+dojo.hostenv.resetXd = function(){
+	//This flag indicates where or not we have crossed into xdomain territory. Once any package says
+	//it is cross domain, then the rest of the packages have to be treated as xdomain because we need
+	//to evaluate packages in order. If there is a xdomain package followed by a xhr package, we can't load
+	//the xhr package until the one before it finishes loading. The text of the xhr package will be converted
+	//to match the format for a xd package and put in the xd load queue.
+	//You can force all packages to be treated as xd by setting the djConfig.forceXDomain.
+	this.isXDomain = djConfig.forceXDomain || false;
+
+	this.xdTimer = 0;
+	this.xdInFlight = {};
+	this.xdOrderedReqs = [];
+	this.xdDepMap = {};
+	this.xdContents = [];
+}
+
+//Call reset immediately to set the state.
+dojo.hostenv.resetXd();
+
+dojo.hostenv.createXdPackage = function(contents){
+	//Find dependencies.
+	var deps = [];
+    var depRegExp = /dojo.(require|requireIf|requireAll|provide|requireAfterIf|requireAfter|kwCompoundRequire|conditionalRequire|hostenv\.conditionalLoadModule|.hostenv\.loadModule|hostenv\.moduleLoaded)\(([\w\W]*?)\)/mg;
+    var match;
+	while((match = depRegExp.exec(contents)) != null){
+		deps.push("\"" + match[1] + "\", " + match[2]);
+	}
+
+	//Create package object and the call to packageLoaded.
+	var output = [];
+	output.push("dojo.hostenv.packageLoaded({\n");
+
+	//Add dependencies
+	if(deps.length > 0){
+		output.push("depends: [");
+		for(var i = 0; i < deps.length; i++){
+			if(i > 0){
+				output.push(",\n");
+			}
+			output.push("[" + deps[i] + "]");
+		}
+		output.push("],");
+	}
+
+	//Add the contents of the file inside a function.
+	//Pass in dojo as an argument to the function to help with
+	//allowing multiple versions of dojo in a page.
+	output.push("\ndefinePackage: function(dojo){");
+	output.push(contents);
+	output.push("\n}});");
+	
+	return output.join("");
+}
+
+dojo.hostenv.loadPath = function(relpath, module /*optional*/, cb /*optional*/){
+	//Only do getBaseScriptUri if path does not start with a URL with a protocol.
+	//If there is a colon before the first / then, we have a URL with a protocol.
+	var colonIndex = relpath.indexOf(":");
+	var slashIndex = relpath.indexOf("/");
+	var uri;
+	var currentIsXDomain = false;
+	if(colonIndex > 0 && colonIndex < slashIndex){
+		uri = relpath;
+		this.isXDomain = currentIsXDomain = true;
+	}else{
+		uri = this.getBaseScriptUri() + relpath;
+
+		//Is ithe base script URI-based URL a cross domain URL?
+		colonIndex = uri.indexOf(":");
+		slashIndex = uri.indexOf("/");
+		if(colonIndex > 0 && colonIndex < slashIndex && (!location.host || uri.indexOf("http://" + location.host) != 0)){
+			this.isXDomain = currentIsXDomain = true;
+		}
+	}
+
+	if(djConfig.cacheBust && dojo.render.html.capable) { uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,""); }
+	try{
+		return ((!module || this.isXDomain) ? this.loadUri(uri, cb, currentIsXDomain, module) : this.loadUriAndCheck(uri, module, cb));
+	}catch(e){
+		dojo.debug(e);
+		return false;
+	}
+}
+
+//Overriding loadUri for now. Wanted to override getText(), but it is used by
+//the widget code in too many, synchronous ways right now. This means the xd stuff
+//is not suitable for widgets yet.
+dojo.hostenv.loadUri = function(uri, cb, currentIsXDomain, module){
+	if(this.loadedUris[uri]){
+		return 1;
+	}
+
+	//Add the module (package) to the list of modules.
+	if(this.isXDomain){
+		//Curious: is this array going to get whacked with multiple access since scripts
+		//load asynchronously and may be accessing the array at the same time?
+		//JS is single-threaded supposedly, so it should be ok. And we don't need
+		//a precise ordering.
+		this.xdOrderedReqs.push(module);
+
+		//Add to waiting packages.
+		//If this is a __package__.js file, then this must be
+		//a package.* request (since xdomain can only work with the first
+		//path in a package search list. However, .* module names are not
+		//passed to this function, so do an adjustment here.
+		if(uri.indexOf("__package__") != -1){
+			module += ".*";
+		}
+
+		this.xdInFlight[module] = true;
+
+		//Increment inFlightCount
+		//This will stop the modulesLoaded from firing all the way.
+		this.inFlightCount++;
+				
+		//Start timer
+		if(!this.xdTimer){
+			this.xdTimer = setInterval("dojo.hostenv.watchInFlightXDomain();", 100);
+		}
+		this.xdStartTime = (new Date()).getTime();
+	}
+
+	if (currentIsXDomain){
+		//Fix name to be a .xd.fileextension name.
+		var lastIndex = uri.lastIndexOf('.');
+		if(lastIndex <= 0){
+			lastIndex = uri.length - 1;
+		}
+
+		var xdUri = uri.substring(0, lastIndex) + ".xd";
+		if(lastIndex != uri.length - 1){
+			xdUri += uri.substring(lastIndex, uri.length);
+		}
+
+		//Add to script src
+		var element = document.createElement("script");
+		element.type = "text/javascript";
+		element.src = xdUri;
+		if(!this.headElement){
+			this.headElement = document.getElementsByTagName("head")[0];
+		}
+		this.headElement.appendChild(element);
+	}else{
+		var contents = this.getText(uri, null, true);
+		if(contents == null){ return 0; }
+		
+		if(this.isXDomain){
+			var pkg = this.createXdPackage(contents);
+			dj_eval(pkg);
+		}else{
+			if(cb){ contents = '('+contents+')'; }
+			var value = dj_eval(contents);
+			if(cb){
+				cb(value);
+			}
+		}
+	}
+
+	//These steps are done in the non-xd loader version of this function.
+	//Maintain these steps to fit in with the existing system.
+	this.loadedUris[uri] = true;
+	return 1;
+}
+
+dojo.hostenv.packageLoaded = function(pkg){
+	var deps = pkg.depends;
+	var requireList = null;
+	var requireAfterList = null;
+	var provideList = [];
+	if(deps && deps.length > 0){
+		var dep = null;
+		var insertHint = 0;
+		var attachedPackage = false;
+		for(var i = 0; i < deps.length; i++){
+			dep = deps[i];
+
+			//Look for specific dependency indicators.
+			if (dep[0] == "provide" || dep[0] == "hostenv.moduleLoaded"){
+				provideList.push(dep[1]);
+			}else{
+				if(!requireList){
+					requireList = [];
+				}
+				if(!requireAfterList){
+					requireAfterList = [];
+				}
+
+				var unpackedDeps = this.unpackXdDependency(dep);
+				if(unpackedDeps.requires){
+					requireList = requireList.concat(unpackedDeps.requires);
+				}
+				if(unpackedDeps.requiresAfter){
+					requireAfterList = requireAfterList.concat(unpackedDeps.requiresAfter);
+				}
+			}
+
+			//Call the dependency indicator to allow for the normal dojo setup.
+			//Only allow for one dot reference, for the hostenv.* type calls.
+			var depType = dep[0];
+			var objPath = depType.split(".");
+			if(objPath.length == 2){
+				dojo[objPath[0]][objPath[1]].apply(dojo[objPath[0]], dep.slice(1));
+			}else{
+				dojo[depType].apply(dojo, dep.slice(1));
+			}
+		}
+
+		//Save off the package contents for definition later.
+		var contentIndex = this.xdContents.push({content: pkg.definePackage, isDefined: false}) - 1;
+
+		//Add provide/requires to dependency map.
+		for(var i = 0; i < provideList.length; i++){
+			this.xdDepMap[provideList[i]] = { requires: requireList, requiresAfter: requireAfterList, contentIndex: contentIndex };
+		}
+
+		//Now update the inflight status for any provided packages in this loaded package.
+		//Do this at the very end (in a *separate* for loop) to avoid shutting down the 
+		//inflight timer check too soon.
+		for(var i = 0; i < provideList.length; i++){
+			this.xdInFlight[provideList[i]] = false;
+		}
+	}
+}
+
+//This is a bit brittle: it has to know about the dojo methods that deal with dependencies
+//It would be ideal to intercept the actual methods and do something fancy at that point,
+//but I have concern about knowing which provide to match to the dependency in that case,
+//since scripts can load whenever they want, and trigger new calls to dojo.hostenv.packageLoaded().
+dojo.hostenv.unpackXdDependency = function(dep){
+	//Extract the dependency(ies).
+	var newDeps = null;
+	var newAfterDeps = null;
+	switch(dep[0]){
+		case "requireIf":
+		case "requireAfterIf":
+		case "conditionalRequire":
+			//First arg (dep[1]) is the test. Depedency is dep[2].
+			if((dep[1] === true)||(dep[1]=="common")||(dep[1] && dojo.render[dep[1]].capable)){
+				newDeps = [{name: dep[2], content: null}];
+			}
+			break;
+		case "requireAll":
+			//the arguments are an array, each element a call to require.
+			//Get rid of first item, which is "requireAll".
+			dep.shift();
+			newDeps = dep;
+			dojo.hostenv.flattenRequireArray(newDeps);
+			break;
+		case "kwCompoundRequire":
+		case "hostenv.conditionalLoadModule":
+			var modMap = dep[1];
+			var common = modMap["common"]||[];
+			var newDeps = (modMap[dojo.hostenv.name_]) ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]);	
+			dojo.hostenv.flattenRequireArray(newDeps);
+			break;
+		case "require":
+		case "requireAfter":
+		case "hostenv.loadModule":
+			//Just worry about dep[1]
+			newDeps = [{name: dep[1], content: null}];
+			break;
+	}
+
+	//The requireAfterIf or requireAfter needs to be evaluated after the current package is evaluated.
+	if(dep[0] == "requireAfterIf"){
+		newAfterDeps = newDeps;
+		newDeps = null;
+	}
+	return {requires: newDeps, requiresAfter: newAfterDeps};
+}
+
+//Walks the requires and evaluates package contents in
+//the right order.
+dojo.hostenv.xdWalkReqs = function(){
+	var reqChain = null;
+	var req;
+	for(var i = 0; i < this.xdOrderedReqs.length; i++){
+		req = this.xdOrderedReqs[i];
+		if(this.xdDepMap[req]){
+			reqChain = [req];
+			reqChain[req] = true; //Allow for fast lookup of the req in the array
+			this.xdEvalReqs(reqChain);
+		}
+	}
+}
+
+//Trace down any requires.
+dojo.hostenv.xdTraceReqs = function(reqs, reqChain){
+	if(reqs && reqs.length > 0){
+		var nextReq;
+		for(var i = 0; i < reqs.length; i++){
+			nextReq = reqs[i].name;
+			if(nextReq && !reqChain[nextReq]){
+				//New req depedency. Follow it down.
+				reqChain.push(nextReq);
+				reqChain[nextReq] = true;
+				this.xdEvalReqs(reqChain);
+			}
+		}
+	}
+}
+
+//Do a depth first, breadth second search and eval or reqs.
+dojo.hostenv.xdEvalReqs = function(reqChain){
+	if(reqChain.length > 0){
+		var req = reqChain[reqChain.length - 1];
+		var pkg = this.xdDepMap[req];
+		if(pkg){
+			//Trace down any requires for this package.
+			this.xdTraceReqs(pkg.requires, reqChain);
+
+			//Evaluate the package.
+			var contents = this.xdContents[pkg.contentIndex];
+			if(!contents.isDefined){
+				//Evaluate the package to bring it into being.
+				//Pass dojo in so that later, to support multiple versions of dojo
+				//in a page, we can pass which version of dojo to use.
+				contents.content(dojo);
+				contents.isDefined = true;
+			}
+			this.xdDepMap[req] = null;
+
+			//Trace down any requireAfters for this package..
+			this.xdTraceReqs(pkg.requiresAfter, reqChain);
+		}
+
+		//Done with that require. Remove it and go to the next one.
+		reqChain.pop();
+		this.xdEvalReqs(reqChain);
+	}
+}
+
+dojo.hostenv.clearXdInterval = function(){
+	clearInterval(this.xdTimer);
+	this.xdTimer = 0;
+}
+
+dojo.hostenv.watchInFlightXDomain = function(){
+	//Make sure we haven't waited timed out.
+	var waitInterval = (djConfig.xdWaitSeconds || 30) * 1000;
+
+	if(this.xdStartTime + waitInterval < (new Date()).getTime()){
+		this.clearXdInterval();
+		var noLoads = "";
+		for(var param in this.xdInFlight){
+			if(this.xdInFlight[param]){
+				noLoads += param + " ";
+			}
+		}
+		dojo.raise("Could not load cross-domain packages: " + noLoads);
+	}
+
+	//If any are true, then still waiting.
+	//Come back later.	
+	for(var param in this.xdInFlight){
+		if(this.xdInFlight[param]){
+			return;
+		}
+	}
+
+	//All done loading. Clean up and notify that we are loaded.
+	this.clearXdInterval();
+
+	this.xdWalkReqs();
+
+	//Evaluate any packages that were not evaled before.
+	//This normally shouldn't happen with proper dojo.provide and dojo.require
+	//usage, but providing it just in case. Note that these may not be executed
+	//in the original order that the developer intended.
+	//Pass dojo in so that later, to support multiple versions of dojo
+	//in a page, we can pass which version of dojo to use.
+	for(var i = 0; i < this.xdContents.length; i++){
+		var current = this.xdContents[i];
+		if(current.content && !current.isDefined){
+			current.content(dojo);
+		}
+	}
+
+	//Clean up for the next round of xd loading.
+	this.resetXd();
+
+	//Clear inflight count so we will finally do finish work.
+	this.inFlightCount = 0; 
+	this.callLoaded();
+}
+
+dojo.hostenv.flattenRequireArray = function(target){
+	//Each result could be an array of 3 elements  (the 3 arguments to dojo.require).
+	//We only need the first one.
+	if(target){
+		for(var i = 0; i < target.length; i++){
+			if(target[i] instanceof Array){
+				target[i] = {name: target[i][0], content: null};
+			}else{
+				target[i] = {name: target[i], content: null};
+			}
+		}
+	}
+}
+
+//Need to preload any flattened i18n bundles before we start
+//executing code, since we cannot do it synchronously, as the
+//i18n code normally expects.
+dojo.hostenv.xdHasCalledPreload = false;
+dojo.hostenv.xdRealCallLoaded = dojo.hostenv.callLoaded;
+dojo.hostenv.callLoaded = function(){
+	//If getModulePrefix for dojo returns anything other than "src", that means
+	//there is a path registered for dojo, with implies that dojo was xdomain loaded.
+	if(this.xdHasCalledPreload || dojo.hostenv.getModulePrefix("dojo") == "src"){
+		this.xdRealCallLoaded();
+		this.xdHasCalledPreload = true;
+	}else{
+		if(this.localesGenerated){
+			this.registerNlsPrefix = function(){
+				//Need to set the nls prefix to be the xd location.
+				dojo.registerModulePath("nls", dojo.hostenv.getModulePrefix("dojo") + "/../nls");	
+			};
+			this.preloadLocalizations();
+		}
+		this.xdHasCalledPreload = true;
+	}
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/math.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/math.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/math.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,127 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.math");
+
+dojo.math.degToRad = function(/* float */x) {
+	//	summary
+	//	Converts degrees to radians.
+	return (x*Math.PI) / 180; 	//	float
+}
+dojo.math.radToDeg = function(/* float */x) { 
+	//	summary
+	//	Converts radians to degrees.
+	return (x*180) / Math.PI; 	//	float
+}
+
+dojo.math.factorial = function(/* integer */n){
+	//	summary
+	//	Returns n!
+	if(n<1){ return 0; }
+	var retVal = 1;
+	for(var i=1;i<=n;i++){ retVal *= i; }
+	return retVal;	//	integer
+}
+
+dojo.math.permutations = function(/* integer */n, /* integer */k) {
+	//	summary
+	//	The number of ways of obtaining an ordered subset of k elements from a set of n elements
+	if(n==0 || k==0) return 1;
+	return (dojo.math.factorial(n) / dojo.math.factorial(n-k));	//	float
+}
+
+dojo.math.combinations = function (/* integer */n, /* integer */r) {
+	//	summary
+	//	The number of ways of picking n unordered outcomes from r possibilities
+	if(n==0 || r==0) return 1;
+	return (dojo.math.factorial(n) / (dojo.math.factorial(n-r) * dojo.math.factorial(r)));	//	float
+}
+
+dojo.math.bernstein = function(/* float */t, /* float */n, /* float */i) {
+	//	summary
+	//	Calculates a weighted average based on the Bernstein theorem.
+	return (dojo.math.combinations(n,i) * Math.pow(t,i) * Math.pow(1-t,n-i));	//	float
+}
+
+dojo.math.gaussianRandom = function(){
+	//	summary
+	//	Returns random numbers with a Gaussian distribution, with the mean set at 0 and the variance set at 1.
+	var k = 2;
+	do {
+		var i = 2 * Math.random() - 1;
+		var j = 2 * Math.random() - 1;
+		k = i * i + j * j;
+	} while (k >= 1);
+	k = Math.sqrt((-2 * Math.log(k)) / k);
+	return i * k;	//	float
+}
+
+dojo.math.mean = function() {
+	//	summary
+	//	Calculates the mean of an Array of numbers.
+	var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments;
+	var mean = 0;
+	for (var i = 0; i < array.length; i++) { mean += array[i]; }
+	return mean / array.length;	//	float
+}
+
+dojo.math.round = function(/* float */number, /* integer */places) {
+	//	summary
+	//	Extends Math.round by adding a second argument specifying the number of decimal places to round to.
+	// TODO: add support for significant figures
+	if (!places) { var shift = 1; }
+	else { var shift = Math.pow(10, places); }
+	return Math.round(number * shift) / shift;	//	float
+}
+
+dojo.math.sd = dojo.math.standardDeviation = function(/* array */){
+	//	summary
+	//	Calculates the standard deviation of an Array of numbers
+	var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments;
+	return Math.sqrt(dojo.math.variance(array));	//	float
+}
+
+dojo.math.variance = function(/* array */) {
+	//	summary
+	//	Calculates the variance of an Array of numbers
+	var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments;
+	var mean = 0, squares = 0;
+	for (var i = 0; i < array.length; i++) {
+		mean += array[i];
+		squares += Math.pow(array[i], 2);
+	}
+	return (squares / array.length) - Math.pow(mean / array.length, 2);	//	float
+}
+
+dojo.math.range = function(/* integer */a, /* integer */b, /* integer */step) {
+	//	summary
+	//	implementation of Python's range()
+    if(arguments.length < 2) {
+        b = a;
+        a = 0;
+    }
+    if(arguments.length < 3) {
+        step = 1;
+    }
+
+    var range = [];
+    if(step > 0) {
+        for(var i = a; i < b; i += step) {
+            range.push(i);
+        }
+    } else if(step < 0) {
+        for(var i = a; i > b; i += step) {
+            range.push(i);
+        }
+    } else {
+        throw new Error("dojo.math.range: step must be non-zero");
+    }
+    return range;	//	array
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/ns.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/ns.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/ns.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,142 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.ns");
+
+dojo.ns = {
+	// summary: private object that implements widget namespace management
+	namespaces: {},
+	failed: {},
+	loading: {},
+	loaded: {},
+	register: function(/*String*/name, /*String*/module, /*Function?*/resolver, /*Boolean?*/noOverride){
+		// summary: creates and registers a dojo.ns.Ns object
+		if(!noOverride || !this.namespaces[name]){
+			this.namespaces[name] = new dojo.ns.Ns(name, module, resolver);
+		}
+	},
+	allow: function(/*String*/name){
+		// summary: Returns false if 'name' is filtered by configuration or has failed to load, true otherwise
+		if(this.failed[name]){return false;} // Boolean
+		if((djConfig.excludeNamespace)&&(dojo.lang.inArray(djConfig.excludeNamespace, name))){return false;} // Boolean
+		// If the namespace is "dojo", or the user has not specified allowed namespaces return true.
+		// Otherwise, if the user has specifically allowed this namespace, return true, otherwise false.
+		return((name==this.dojo)||(!djConfig.includeNamespace)||(dojo.lang.inArray(djConfig.includeNamespace, name))); // Boolean
+	},
+	get: function(/*String*/name){
+		// summary
+		//  Return Ns object registered to 'name', if any
+		return this.namespaces[name]; // Ns
+	},
+	require: function(/*String*/name){
+		// summary
+  	//  Try to ensure that 'name' is registered, loading a namespace manifest if necessary
+		var ns = this.namespaces[name];
+		if((ns)&&(this.loaded[name])){return ns;} // Ns
+		if(!this.allow(name)){return false;} // Boolean
+ 		if(this.loading[name]){
+			// FIXME: do we really ever have re-entrancy situation? this would appear to be really bad
+			// original code did not throw an exception, although that seems the only course
+			// adding debug output here to track if this occurs.
+			dojo.debug('dojo.namespace.require: re-entrant request to load namespace "' + name + '" must fail.'); 
+			return false; // Boolean
+		}
+		// workaround so we don't break the build system
+		var req = dojo.require;
+		this.loading[name] = true;
+		try {
+			//dojo namespace file is always in the Dojo namespaces folder, not any custom folder
+			if(name=="dojo"){
+				req("dojo.namespaces.dojo");
+			}else{
+				// if no registered module prefix, use ../<name> by convention
+				if(!dojo.hostenv.moduleHasPrefix(name)){
+					dojo.registerModulePath(name, "../" + name);
+				}
+				req([name, 'manifest'].join('.'), false, true);
+			}
+			if(!this.namespaces[name]){
+				this.failed[name] = true; //only look for a namespace once
+			}
+		}finally{
+			this.loading[name]=false;
+		}
+		return this.namespaces[name]; // Ns
+	}
+}
+
+dojo.ns.Ns = function(/*String*/name, /*String*/module, /*Function?*/resolver){
+	// summary: this object simply encapsulates namespace data
+	this.name = name;
+	this.module = module;
+	this.resolver = resolver;
+	this._loaded = [ ];
+	this._failed = [ ];
+}
+
+dojo.ns.Ns.prototype.resolve = function(/*String*/name, /*String*/domain, /*Boolean?*/omitModuleCheck){
+	//summary: map component with 'name' and 'domain' to a module via namespace resolver, if specified
+	if(!this.resolver || djConfig["skipAutoRequire"]){return false;} // Boolean
+	var fullName = this.resolver(name, domain);
+	//only load a widget once. This is a quicker check than dojo.require does
+	if((fullName)&&(!this._loaded[fullName])&&(!this._failed[fullName])){
+		//workaround so we don't break the build system
+		var req = dojo.require;
+		req(fullName, false, true); //omit the module check, we'll do it ourselves.
+		if(dojo.hostenv.findModule(fullName, false)){
+			this._loaded[fullName] = true;
+		}else{
+			if(!omitModuleCheck){dojo.raise("dojo.ns.Ns.resolve: module '" + fullName + "' not found after loading via namespace '" + this.name + "'");} 
+			this._failed[fullName] = true;
+		}
+	}
+	return Boolean(this._loaded[fullName]); // Boolean
+}
+
+dojo.registerNamespace = function(/*String*/name, /*String*/module, /*Function?*/resolver){
+	// summary: maps a module name to a namespace for widgets, and optionally maps widget names to modules for auto-loading
+	// description: An unregistered namespace is mapped to an eponymous module.
+	//	For example, namespace acme is mapped to module acme, and widgets are
+	//	assumed to belong to acme.widget. If you want to use a different widget
+	//	module, use dojo.registerNamespace.
+	dojo.ns.register.apply(dojo.ns, arguments);
+}
+
+dojo.registerNamespaceResolver = function(/*String*/name, /*Function*/resolver){
+	// summary: a resolver function maps widget names to modules, so the
+	//	widget manager can auto-load needed widget implementations
+	//
+	// description: The resolver provides information to allow Dojo
+	//	to load widget modules on demand. When a widget is created,
+	//	a namespace resolver can tell Dojo what module to require
+	//	to ensure that the widget implementation code is loaded.
+	//
+	// name: will always be lower-case.
+	//
+	// example:
+	//  dojo.registerNamespaceResolver("acme",
+	//    function(name){ 
+	//      return "acme.widget."+dojo.string.capitalize(name);
+	//    }
+	//  );
+	var n = dojo.ns.namespaces[name];
+	if(n){
+		n.resolver = resolver;
+	}
+}
+
+dojo.registerNamespaceManifest = function(/*String*/module, /*String*/path, /*String*/name, /*String*/widgetModule, /*Function?*/resolver){
+	// summary: convenience function to register a module path, a namespace, and optionally a resolver all at once.
+	dojo.registerModulePath(name, path);
+	dojo.registerNamespace(name, widgetModule, resolver);
+}
+
+// NOTE: rather put this in dojo.widget.Widget, but that fubars debugAtAllCosts
+dojo.registerNamespace("dojo", "dojo.widget");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/profile.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/profile.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/profile.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,146 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.profile");
+
+// summary:
+//		provides a manual profiling utility that can be used to gather relative
+//		timing data.
+
+
+// FIXME: need to tie into the event system or provide a closure-based way to
+// watch timings of functions without manually instrumenting them.
+// FIXME: need to make the dump() function work in command line environments
+
+dojo.profile = {
+	_profiles: {},
+	_pns: [],
+
+	start:function(/*String*/ name){
+		// summary:
+		//		start an iteration for the profiling target with the specified
+		//		name. If a previously started iteration has not yet been ended
+		//		for this name, it's automatically closed out and a new
+		//		iteration begun.
+		// name:
+		//		a unique name to identify the thing being profiled
+		if(!this._profiles[name]){
+			this._profiles[name] = {iters: 0, total: 0};
+			this._pns[this._pns.length] = name;
+		}else{
+			if(this._profiles[name]["start"]){
+				this.end(name);
+			}
+		}
+		this._profiles[name].end = null;
+		this._profiles[name].start = new Date();
+	},
+
+	end:function(/*String*/ name){
+		// summary:
+		//		closes a timing loop for the named profiling target
+		// name:
+		//		a unique name to identify the thing being profiled. The name
+		//		passed to end() should be the same as that passed to start()
+		var ed = new Date();
+		if((this._profiles[name])&&(this._profiles[name]["start"])){
+			with(this._profiles[name]){
+				end = ed;
+				total += (end - start);
+				start = null;
+				iters++;
+			}
+		}else{
+			// oops! bad call to end(), what should we do here?
+			return true;
+		}
+	},
+
+	dump:function(/*boolean*/ appendToDoc){
+		// summary:
+		//		output profiling data to an HTML table, optionally adding it to
+		//		the bottom of the document. If profiling data has already been
+		//		generated and appended to the document, it's replaced with the
+		//		new data.
+		// appendToDoc:
+		//		optional. Defautls to "false". Should profiling information be
+		//		added to the document?
+		var tbl = document.createElement("table");
+		with(tbl.style){
+			border = "1px solid black";
+			borderCollapse = "collapse";
+		}
+		var hdr = tbl.createTHead();
+		var hdrtr = hdr.insertRow(0);
+		// document.createElement("tr");
+		var cols = ["Identifier","Calls","Total","Avg"];
+		for(var x=0; x<cols.length; x++){
+			var ntd = hdrtr.insertCell(x);
+			with(ntd.style){
+				backgroundColor = "#225d94";
+				color = "white";
+				borderBottom = "1px solid black";
+				borderRight = "1px solid black";
+				fontFamily = "tahoma";
+				fontWeight = "bolder";
+				paddingLeft = paddingRight = "5px";
+			}
+			ntd.appendChild(document.createTextNode(cols[x]));
+		}
+
+		for(var x=0; x < this._pns.length; x++){
+			var prf = this._profiles[this._pns[x]];
+			this.end(this._pns[x]);
+			if(prf.iters>0){
+				var bdytr = tbl.insertRow(true);
+				var vals = [this._pns[x], prf.iters, prf.total, parseInt(prf.total/prf.iters)];
+				for(var y=0; y<vals.length; y++){
+					var cc = bdytr.insertCell(y);
+					cc.appendChild(document.createTextNode(vals[y]));
+					with(cc.style){
+						borderBottom = "1px solid gray";
+						paddingLeft = paddingRight = "5px";
+						if(x%2){
+							backgroundColor = "#e1f1ff";
+						}
+						if(y>0){
+							textAlign = "right";
+							borderRight = "1px solid gray";
+						}else{
+							borderRight = "1px solid black";
+						}
+					}
+				}
+			}
+		}
+
+		if(appendToDoc){
+			var ne = document.createElement("div");
+			ne.id = "profileOutputTable";
+			with(ne.style){
+				fontFamily = "Courier New, monospace";
+				fontSize = "12px";
+				lineHeight = "16px";
+				borderTop = "1px solid black";
+				padding = "10px";
+			}
+			if(document.getElementById("profileOutputTable")){
+				dojo.body().replaceChild(ne, document.getElementById("profileOutputTable"));
+			}else{
+				dojo.body().appendChild(ne);
+			}
+			ne.appendChild(tbl);
+		}
+
+		return tbl; // DOMNode
+	}
+}
+
+dojo.profile.stop = dojo.profile.end;

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/regexp.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/regexp.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/regexp.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,576 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.regexp");
+dojo.evalObjPath("dojo.regexp.us", true);	// this file also defines stuff in the dojo.regexp.us module (TODO: move to separate file?)
+
+// *** Regular Expression Generators ***
+
+dojo.regexp.tld = function(/*Object?*/flags){
+	// summary: Builds a RE that matches a top-level domain
+	//
+	// flags:
+	//    flags.allowCC  Include 2 letter country code domains.  Default is true.
+	//    flags.allowGeneric  Include the generic domains.  Default is true.
+	//    flags.allowInfra  Include infrastructure domains.  Default is true.
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.allowCC != "boolean"){ flags.allowCC = true; }
+	if(typeof flags.allowInfra != "boolean"){ flags.allowInfra = true; }
+	if(typeof flags.allowGeneric != "boolean"){ flags.allowGeneric = true; }
+
+	// Infrastructure top-level domain - only one at present
+	var infraRE = "arpa";
+
+	// Generic top-level domains RE.
+	var genericRE = 
+		"aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|xxx|jobs|mobi|post";
+	
+	// Country Code top-level domains RE
+	var ccRE = 
+		"ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|" +
+		"bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|" +
+		"ec|ee|eg|er|es|et|fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|" +
+		"hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kr|kw|ky|kz|la|" +
+		"lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|" +
+		"mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|" +
+		"ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sk|sl|sm|sn|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|" +
+		"to|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw";
+
+	// Build top-level domain RE
+	var a = [];
+	if(flags.allowInfra){ a.push(infraRE); }
+	if(flags.allowGeneric){ a.push(genericRE); }
+	if(flags.allowCC){ a.push(ccRE); }
+
+	var tldRE = "";
+	if (a.length > 0) {
+		tldRE = "(" + a.join("|") + ")";
+	}
+
+	return tldRE; // String
+}
+
+dojo.regexp.ipAddress = function(/*Object?*/flags){
+	// summary: Builds a RE that matches an IP Address
+	//
+	// description:
+	//  Supports 5 formats for IPv4: dotted decimal, dotted hex, dotted octal, decimal and hexadecimal.
+	//  Supports 2 formats for Ipv6.
+	//
+	// flags  An object.  All flags are boolean with default = true.
+	//    flags.allowDottedDecimal  Example, 207.142.131.235.  No zero padding.
+	//    flags.allowDottedHex  Example, 0x18.0x11.0x9b.0x28.  Case insensitive.  Zero padding allowed.
+	//    flags.allowDottedOctal  Example, 0030.0021.0233.0050.  Zero padding allowed.
+	//    flags.allowDecimal  Example, 3482223595.  A decimal number between 0-4294967295.
+	//    flags.allowHex  Example, 0xCF8E83EB.  Hexadecimal number between 0x0-0xFFFFFFFF.
+	//      Case insensitive.  Zero padding allowed.
+	//    flags.allowIPv6   IPv6 address written as eight groups of four hexadecimal digits.
+	//    flags.allowHybrid   IPv6 address written as six groups of four hexadecimal digits
+	//      followed by the usual 4 dotted decimal digit notation of IPv4. x:x:x:x:x:x:d.d.d.d
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.allowDottedDecimal != "boolean"){ flags.allowDottedDecimal = true; }
+	if(typeof flags.allowDottedHex != "boolean"){ flags.allowDottedHex = true; }
+	if(typeof flags.allowDottedOctal != "boolean"){ flags.allowDottedOctal = true; }
+	if(typeof flags.allowDecimal != "boolean"){ flags.allowDecimal = true; }
+	if(typeof flags.allowHex != "boolean"){ flags.allowHex = true; }
+	if(typeof flags.allowIPv6 != "boolean"){ flags.allowIPv6 = true; }
+	if(typeof flags.allowHybrid != "boolean"){ flags.allowHybrid = true; }
+
+	// decimal-dotted IP address RE.
+	var dottedDecimalRE = 
+		// Each number is between 0-255.  Zero padding is not allowed.
+		"((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])";
+
+	// dotted hex IP address RE.  Each number is between 0x0-0xff.  Zero padding is allowed, e.g. 0x00.
+	var dottedHexRE = "(0[xX]0*[\\da-fA-F]?[\\da-fA-F]\\.){3}0[xX]0*[\\da-fA-F]?[\\da-fA-F]";
+
+	// dotted octal IP address RE.  Each number is between 0000-0377.  
+	// Zero padding is allowed, but each number must have at least 4 characters.
+	var dottedOctalRE = "(0+[0-3][0-7][0-7]\\.){3}0+[0-3][0-7][0-7]";
+
+	// decimal IP address RE.  A decimal number between 0-4294967295.  
+	var decimalRE =  "(0|[1-9]\\d{0,8}|[1-3]\\d{9}|4[01]\\d{8}|42[0-8]\\d{7}|429[0-3]\\d{6}|" +
+		"4294[0-8]\\d{5}|42949[0-5]\\d{4}|429496[0-6]\\d{3}|4294967[01]\\d{2}|42949672[0-8]\\d|429496729[0-5])";
+
+	// hexadecimal IP address RE. 
+	// A hexadecimal number between 0x0-0xFFFFFFFF. Case insensitive.  Zero padding is allowed.
+	var hexRE = "0[xX]0*[\\da-fA-F]{1,8}";
+
+	// IPv6 address RE. 
+	// The format is written as eight groups of four hexadecimal digits, x:x:x:x:x:x:x:x,
+	// where x is between 0000-ffff. Zero padding is optional. Case insensitive. 
+	var ipv6RE = "([\\da-fA-F]{1,4}\\:){7}[\\da-fA-F]{1,4}";
+
+	// IPv6/IPv4 Hybrid address RE. 
+	// The format is written as six groups of four hexadecimal digits, 
+	// followed by the 4 dotted decimal IPv4 format. x:x:x:x:x:x:d.d.d.d
+	var hybridRE = "([\\da-fA-F]{1,4}\\:){6}" + 
+		"((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])";
+
+	// Build IP Address RE
+	var a = [];
+	if(flags.allowDottedDecimal){ a.push(dottedDecimalRE); }
+	if(flags.allowDottedHex){ a.push(dottedHexRE); }
+	if(flags.allowDottedOctal){ a.push(dottedOctalRE); }
+	if(flags.allowDecimal){ a.push(decimalRE); }
+	if(flags.allowHex){ a.push(hexRE); }
+	if(flags.allowIPv6){ a.push(ipv6RE); }
+	if(flags.allowHybrid){ a.push(hybridRE); }
+
+	var ipAddressRE = "";
+	if(a.length > 0){
+		ipAddressRE = "(" + a.join("|") + ")";
+	}
+
+	return ipAddressRE; // String
+}
+
+dojo.regexp.host = function(/*Object?*/flags){
+	// summary: Builds a RE that matches a host
+	// description: A host is a domain name or an IP address, possibly followed by a port number.
+	// flags: An object.
+	//    flags.allowIP  Allow an IP address for hostname.  Default is true.
+	//    flags.allowLocal  Allow the host to be "localhost".  Default is false.
+	//    flags.allowPort  Allow a port number to be present.  Default is true.
+	//    flags in regexp.ipAddress can be applied.
+	//    flags in regexp.tld can be applied.
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.allowIP != "boolean"){ flags.allowIP = true; }
+	if(typeof flags.allowLocal != "boolean"){ flags.allowLocal = false; }
+	if(typeof flags.allowPort != "boolean"){ flags.allowPort = true; }
+
+	// Domain names can not end with a dash.
+	var domainNameRE = "([0-9a-zA-Z]([-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?\\.)+" + dojo.regexp.tld(flags);
+
+	// port number RE
+	var portRE = ( flags.allowPort ) ? "(\\:" + dojo.regexp.integer({signed: false}) + ")?" : "";
+
+	// build host RE
+	var hostNameRE = domainNameRE;
+	if(flags.allowIP){ hostNameRE += "|" +  dojo.regexp.ipAddress(flags); }
+	if(flags.allowLocal){ hostNameRE += "|localhost"; }
+
+	return "(" + hostNameRE + ")" + portRE; // String
+}
+
+dojo.regexp.url = function(/*Object?*/flags){
+	// summary: Builds a regular expression that matches a URL
+	//
+	// flags: An object
+	//    flags.scheme  Can be true, false, or [true, false]. 
+	//      This means: required, not allowed, or match either one.
+	//    flags in regexp.host can be applied.
+	//    flags in regexp.ipAddress can be applied.
+	//    flags in regexp.tld can be applied.
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.scheme == "undefined"){ flags.scheme = [true, false]; }
+
+	// Scheme RE
+	var protocolRE = dojo.regexp.buildGroupRE(flags.scheme,
+		function(q){ if(q){ return "(https?|ftps?)\\://"; } return ""; }
+	);
+
+	// Path and query and anchor RE
+	var pathRE = "(/([^?#\\s/]+/)*)?([^?#\\s/]+(\\?[^?#\\s/]*)?(#[A-Za-z][\\w.:-]*)?)?";
+
+	return protocolRE + dojo.regexp.host(flags) + pathRE;
+}
+
+
+dojo.regexp.emailAddress = function(/*Object?*/flags){
+	// summary: Builds a regular expression that matches an email address
+	//
+	//flags: An object
+	//    flags.allowCruft  Allow address like <mailto:foo at yahoo.com>.  Default is false.
+	//    flags in regexp.host can be applied.
+	//    flags in regexp.ipAddress can be applied.
+	//    flags in regexp.tld can be applied.
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if (typeof flags.allowCruft != "boolean") { flags.allowCruft = false; }
+	flags.allowPort = false; // invalid in email addresses
+
+	// user name RE - apostrophes are valid if there's not 2 in a row
+	var usernameRE = "([\\da-z]+[-._+&'])*[\\da-z]+";
+
+	// build emailAddress RE
+	var emailAddressRE = usernameRE + "@" + dojo.regexp.host(flags);
+
+	// Allow email addresses with cruft
+	if ( flags.allowCruft ) {
+		emailAddressRE = "<?(mailto\\:)?" + emailAddressRE + ">?";
+	}
+
+	return emailAddressRE; // String
+}
+
+dojo.regexp.emailAddressList = function(/*Object?*/flags){
+	// summary: Builds a regular expression that matches a list of email addresses.
+	//
+	// flags: An object.
+	//    flags.listSeparator  The character used to separate email addresses.  Default is ";", ",", "\n" or " ".
+	//    flags in regexp.emailAddress can be applied.
+	//    flags in regexp.host can be applied.
+	//    flags in regexp.ipAddress can be applied.
+	//    flags in regexp.tld can be applied.
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.listSeparator != "string"){ flags.listSeparator = "\\s;,"; }
+
+	// build a RE for an Email Address List
+	var emailAddressRE = dojo.regexp.emailAddress(flags);
+	var emailAddressListRE = "(" + emailAddressRE + "\\s*[" + flags.listSeparator + "]\\s*)*" + 
+		emailAddressRE + "\\s*[" + flags.listSeparator + "]?\\s*";
+
+	return emailAddressListRE; // String
+}
+
+dojo.regexp.integer = function(/*Object?*/flags){
+	// summary: Builds a regular expression that matches an integer
+	//
+	// flags: An object
+	//    flags.signed  The leading plus-or-minus sign.  Can be true, false, or [true, false].
+	//      Default is [true, false], (i.e. will match if it is signed or unsigned).
+	//    flags.separator  The character used as the thousands separator.  Default is no separator.
+	//      For more than one symbol use an array, e.g. [",", ""], makes ',' optional.
+	//	flags.groupSize group size between separators
+	//	flags.groupSize2 second grouping (for India)
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.signed == "undefined"){ flags.signed = [true, false]; }
+	if(typeof flags.separator == "undefined"){
+		flags.separator = "";
+	} else if(typeof flags.groupSize == "undefined"){
+		flags.groupSize = 3;
+	}
+	// build sign RE
+	var signRE = dojo.regexp.buildGroupRE(flags.signed,
+		function(q) { return q ? "[-+]" : ""; }
+	);
+
+	// number RE
+	var numberRE = dojo.regexp.buildGroupRE(flags.separator,
+		function(sep){ 
+			if(sep == ""){ 
+				return "(0|[1-9]\\d*)";
+			}
+			var grp = flags.groupSize, grp2 = flags.groupSize2;
+			if(typeof grp2 != "undefined"){
+				var grp2RE = "(0|[1-9]\\d{0," + (grp2-1) + "}([" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
+				return ((grp-grp2) > 0) ? "(" + grp2RE + "|(0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
+			}
+			return  "(0|[1-9]\\d{0," + (grp-1) + "}([" + sep + "]\\d{" + grp + "})*)";
+		}
+	);
+
+	// integer RE
+	return signRE + numberRE; // String
+}
+
+dojo.regexp.realNumber = function(/*Object?*/flags){
+	// summary: Builds a regular expression to match a real number in exponential notation
+	//
+	// flags:An object
+	//    flags.places  The integer number of decimal places.
+	//      If not given, the decimal part is optional and the number of places is unlimited.
+	//    flags.decimal  A string for the character used as the decimal point.  Default is ".".
+	//    flags.fractional  Whether decimal places are allowed.
+	//      Can be true, false, or [true, false].  Default is [true, false]
+	//    flags.exponent  Express in exponential notation.  Can be true, false, or [true, false].
+	//      Default is [true, false], (i.e. will match if the exponential part is present are not).
+	//    flags.eSigned  The leading plus-or-minus sign on the exponent.  Can be true, false, 
+	//      or [true, false].  Default is [true, false], (i.e. will match if it is signed or unsigned).
+	//    flags in regexp.integer can be applied.
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.places != "number"){ flags.places = Infinity; }
+	if(typeof flags.decimal != "string"){ flags.decimal = "."; }
+	if(typeof flags.fractional == "undefined"){ flags.fractional = [true, false]; }
+	if(typeof flags.exponent == "undefined"){ flags.exponent = [true, false]; }
+	if(typeof flags.eSigned == "undefined"){ flags.eSigned = [true, false]; }
+
+	// integer RE
+	var integerRE = dojo.regexp.integer(flags);
+
+	// decimal RE
+	var decimalRE = dojo.regexp.buildGroupRE(flags.fractional,
+		function(q){
+			var re = "";
+			if(q && (flags.places > 0)){
+				re = "\\" + flags.decimal;
+				if(flags.places == Infinity){ 
+					re = "(" + re + "\\d+)?"; 
+				}else{ 
+					re = re + "\\d{" + flags.places + "}"; 
+				}
+			}
+
+			return re;
+		}
+	);
+
+	// exponent RE
+	var exponentRE = dojo.regexp.buildGroupRE(flags.exponent,
+		function(q){ 
+			if(q){ return "([eE]" + dojo.regexp.integer({ signed: flags.eSigned}) + ")"; }
+			return ""; 
+		}
+	);
+
+	// real number RE
+	return integerRE + decimalRE + exponentRE; // String
+}
+
+dojo.regexp.currency = function(/*Object?*/flags){
+	// summary: Builds a regular expression to match a monetary value
+	//
+	// flags: An object
+	//    flags.symbol  A currency symbol such as Yen "�", Pound "�", or the Euro sign "�".  
+	//      Default is "$".  For more than one symbol use an array, e.g. ["$", ""], makes $ optional.
+	//    flags.placement  The symbol can come "before" the number or "after" the number.  Default is "before".
+	//    flags.signPlacement  The sign can come "before" the number or "after" the sign,
+	//      "around" to put parentheses around negative values, or "end" for the final char.  Default is "before".
+	//    flags.cents  deprecated, in favor of flags.fractional
+	//    flags in regexp.realNumber can be applied except exponent, eSigned.
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.signed == "undefined"){ flags.signed = [true, false]; }
+	if(typeof flags.symbol == "undefined"){ flags.symbol = "$"; }
+	if(typeof flags.placement != "string"){ flags.placement = "before"; }
+	if(typeof flags.signPlacement != "string"){ flags.signPlacement = "before"; }
+	if(typeof flags.separator == "undefined"){ flags.separator = ","; }
+	if(typeof flags.fractional == "undefined" && typeof flags.cents != "undefined"){
+		dojo.deprecated("dojo.regexp.currency: flags.cents", "use flags.fractional instead", "0.5");
+		flags.fractional = flags.cents;
+	}
+	if(typeof flags.decimal != "string"){ flags.decimal = "."; }
+
+	// build sign RE
+	var signRE = dojo.regexp.buildGroupRE(flags.signed,
+		function(q){ if (q){ return "[-+]"; } return ""; }
+	);
+
+	// build symbol RE
+	var symbolRE = dojo.regexp.buildGroupRE(flags.symbol,
+		function(symbol){ 
+			// escape all special characters
+			return "\\s?" + symbol.replace( /([.$?*!=:|\\\/^])/g, "\\$1") + "\\s?";
+		}
+	);
+
+	switch (flags.signPlacement){
+		case "before":
+			symbolRE = signRE + symbolRE;
+			break;
+		case "after":
+			symbolRE = symbolRE + signRE;
+			break;
+	}
+
+	// number RE
+	var flagsCopy = flags; //TODO: copy by value?
+	flagsCopy.signed = false; flagsCopy.exponent = false;
+	var numberRE = dojo.regexp.realNumber(flagsCopy);
+
+	// build currency RE
+	var currencyRE;
+	switch (flags.placement){
+		case "before":
+			currencyRE = symbolRE + numberRE;
+			break;
+		case "after":
+			currencyRE = numberRE + symbolRE;
+			break;
+	}
+
+	switch (flags.signPlacement){
+		case "around":
+			currencyRE = "(" + currencyRE + "|" + "\\(" + currencyRE + "\\)" + ")";
+			break;
+		case "begin":
+			currencyRE = signRE + currencyRE;
+			break;
+		case "end":
+			currencyRE = currencyRE + signRE;
+			break;
+	}
+	return currencyRE; // String
+}
+
+
+dojo.regexp.us.state = function(/*Object?*/flags){
+	// summary: A regular expression to match US state and territory abbreviations
+	//
+	// flags  An object.
+	//    flags.allowTerritories  Allow Guam, Puerto Rico, etc.  Default is true.
+	//    flags.allowMilitary  Allow military 'states', e.g. Armed Forces Europe (AE).  Default is true.
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.allowTerritories != "boolean"){ flags.allowTerritories = true; }
+	if(typeof flags.allowMilitary != "boolean"){ flags.allowMilitary = true; }
+
+	// state RE
+	var statesRE = 
+		"AL|AK|AZ|AR|CA|CO|CT|DE|DC|FL|GA|HI|ID|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|" + 
+		"NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VT|VA|WA|WV|WI|WY";
+
+	// territories RE
+	var territoriesRE = "AS|FM|GU|MH|MP|PW|PR|VI";
+
+	// military states RE
+	var militaryRE = "AA|AE|AP";
+
+	// Build states and territories RE
+	if(flags.allowTerritories){ statesRE += "|" + territoriesRE; }
+	if(flags.allowMilitary){ statesRE += "|" + militaryRE; }
+
+	return "(" + statesRE + ")"; // String
+}
+
+dojo.regexp.time = function(/*Object?*/flags){
+	// summary: Builds a regular expression to match any International format for time
+	// description: The RE can match one format or one of multiple formats.
+	//
+	//  Format
+	//  h        12 hour, no zero padding.
+	//  hh       12 hour, has leading zero.
+	//  H        24 hour, no zero padding.
+	//  HH       24 hour, has leading zero.
+	//  m        minutes, no zero padding.
+	//  mm       minutes, has leading zero.
+	//  s        seconds, no zero padding.
+	//  ss       seconds, has leading zero.
+	//  t        am or pm, case insensitive.
+	//  All other characters must appear literally in the expression.
+	//
+	//  Example
+	//    "h:m:s t"  ->   2:5:33 PM
+	//    "HH:mm:ss" ->  14:05:33
+	//
+	// flags: An object
+	//    flags.format  A string or an array of strings.  Default is "h:mm:ss t".
+	//    flags.amSymbol  The symbol used for AM.  Default is "AM".
+	//    flags.pmSymbol  The symbol used for PM.  Default is "PM".
+
+	dojo.deprecated("dojo.regexp.time", "Use dojo.date.parse instead", "0.5");
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.format == "undefined"){ flags.format = "h:mm:ss t"; }
+	if(typeof flags.amSymbol != "string"){ flags.amSymbol = "AM"; }
+	if(typeof flags.pmSymbol != "string"){ flags.pmSymbol = "PM"; }
+
+	// Converts a time format to a RE
+	var timeRE = function(format){
+		// escape all special characters
+		format = format.replace( /([.$?*!=:|{}\(\)\[\]\\\/^])/g, "\\$1");
+		var amRE = flags.amSymbol.replace( /([.$?*!=:|{}\(\)\[\]\\\/^])/g, "\\$1");
+		var pmRE = flags.pmSymbol.replace( /([.$?*!=:|{}\(\)\[\]\\\/^])/g, "\\$1");
+
+		// replace tokens with Regular Expressions
+		format = format.replace("hh", "(0[1-9]|1[0-2])");
+		format = format.replace("h", "([1-9]|1[0-2])");
+		format = format.replace("HH", "([01][0-9]|2[0-3])");
+		format = format.replace("H", "([0-9]|1[0-9]|2[0-3])");
+		format = format.replace("mm", "([0-5][0-9])");
+		format = format.replace("m", "([1-5][0-9]|[0-9])");
+		format = format.replace("ss", "([0-5][0-9])");
+		format = format.replace("s", "([1-5][0-9]|[0-9])");
+		format = format.replace("t", "\\s?(" + amRE + "|" + pmRE + ")\\s?" );
+
+		return format; // String
+	};
+
+	// build RE for multiple time formats
+	return dojo.regexp.buildGroupRE(flags.format, timeRE); // String
+}
+
+dojo.regexp.numberFormat = function(/*Object?*/flags){
+	// summary: Builds a regular expression to match any sort of number based format
+	// description:
+	//  Use this method for phone numbers, social security numbers, zip-codes, etc.
+	//  The RE can match one format or one of multiple formats.
+	//
+	//  Format
+	//    #        Stands for a digit, 0-9.
+	//    ?        Stands for an optional digit, 0-9 or nothing.
+	//    All other characters must appear literally in the expression.
+	//
+	//  Example   
+	//    "(###) ###-####"       ->   (510) 542-9742
+	//    "(###) ###-#### x#???" ->   (510) 542-9742 x153
+	//    "###-##-####"          ->   506-82-1089       i.e. social security number
+	//    "#####-####"           ->   98225-1649        i.e. zip code
+	//
+	// flags:  An object
+	//    flags.format  A string or an Array of strings for multiple formats.
+
+	// assign default values to missing paramters
+	flags = (typeof flags == "object") ? flags : {};
+	if(typeof flags.format == "undefined"){ flags.format = "###-###-####"; }
+
+	// Converts a number format to RE.
+	var digitRE = function(format){
+		// escape all special characters, except '?'
+		format = format.replace( /([.$*!=:|{}\(\)\[\]\\\/^])/g, "\\$1");
+
+		// Now replace '?' with Regular Expression
+		format = format.replace(/\?/g, "\\d?");
+
+		// replace # with Regular Expression
+		format = format.replace(/#/g, "\\d");
+
+		return format; // String
+	};
+
+	// build RE for multiple number formats
+	return dojo.regexp.buildGroupRE(flags.format, digitRE); //String
+}
+
+
+dojo.regexp.buildGroupRE = function(/*value or Array of values*/a, /*Function(x) returns a regular expression as a String*/re){
+	// summary: Builds a regular expression that groups subexpressions
+	// description: A utility function used by some of the RE generators.
+	//  The subexpressions are constructed by the function, re, in the second parameter.
+	//  re builds one subexpression for each elem in the array a, in the first parameter.
+	//  Returns a string for a regular expression that groups all the subexpressions.
+	//
+	// a:  A single value or an array of values.
+	// re:  A function.  Takes one parameter and converts it to a regular expression. 
+
+	// case 1: a is a single value.
+	if(!(a instanceof Array)){
+		return re(a); // String
+	}
+
+	// case 2: a is an array
+	var b = [];
+	for (var i = 0; i < a.length; i++){
+		// convert each elem to a RE
+		b.push(re(a[i]));
+	}
+
+	 // join the REs as alternatives in a RE group.
+	return "(" + b.join("|") + ")"; // String
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/storage.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/storage.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/storage.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,364 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/** 
+		FIXME: Write better docs.
+
+		@author Brad Neuberg, bkn3 at columbia.edu 
+*/
+dojo.provide("dojo.storage");
+
+dojo.require("dojo.lang.*");
+dojo.require("dojo.event.*");
+
+// create an empty "StorageProvider", which was being created as a side-effect
+//	of the erroneous dojo.provide("dojo.storage.StorageProvider")
+dojo.storage.StorageProvider = {}
+
+
+/** The base class for all storage providers. */
+
+/** 
+	 The constructor for a storage provider. You should avoid initialization
+	 in the constructor; instead, define initialization in your initialize()
+	 method. 
+*/
+dojo.declare("dojo.storage", null, {
+	/** A put() call to a storage provider was succesful. */
+	SUCCESS: "success",
+	
+	/** A put() call to a storage provider failed. */
+	FAILED: "failed",
+	
+	/** A put() call to a storage provider is pending user approval. */
+	PENDING: "pending",
+	
+	/** 
+	  Returned by getMaximumSize() if this storage provider can not determine
+	  the maximum amount of data it can support. 
+	*/
+	SIZE_NOT_AVAILABLE: "Size not available",
+	
+	/**
+	  Returned by getMaximumSize() if this storage provider has no theoretical
+	  limit on the amount of data it can store. 
+	*/
+	SIZE_NO_LIMIT: "No size limit",
+	
+	/** 
+		The namespace for all storage operations. This is useful if several
+		applications want access to the storage system from the same domain but
+		want different storage silos. 
+	*/
+	"namespace": "dojoStorage",
+	
+	/**  
+		If a function is assigned to this property, then when the settings
+		provider's UI is closed this function is called. Useful, for example,
+		if the user has just cleared out all storage for this provider using
+		the settings UI, and you want to update your UI.
+	*/
+	onHideSettingsUI: null,
+
+	initialize: function(){
+		// summary: 
+		//		Allows this storage provider to initialize itself. This is
+		//		called after the page has finished loading, so you can not do
+		//		document.writes(). 
+		dojo.unimplemented("dojo.storage.initialize");
+	},
+	
+	isAvailable: function(){ /*Boolean*/
+		// summary: 
+		//		Returns whether this storage provider is available on this
+		//		platform. 
+		dojo.unimplemented("dojo.storage.isAvailable");
+	},
+	
+	/**
+
+	*/
+	put: function(	/*string*/ key,
+					/*object*/ value, 
+					/*function*/ resultsHandler){
+		// summary:
+		//		Puts a key and value into this storage system.
+		// key:
+		//		A string key to use when retrieving this value in the future.
+		// value:
+		//		A value to store; this can be any JavaScript type.
+		// resultsHandler:
+		//		A callback function that will receive three arguments. The
+		//		first argument is one of three values: dojo.storage.SUCCESS,
+		//		dojo.storage.FAILED, or dojo.storage.PENDING; these values
+		//		determine how the put request went. In some storage systems
+		//		users can deny a storage request, resulting in a
+		//		dojo.storage.FAILED, while in other storage systems a storage
+		//		request must wait for user approval, resulting in a
+		//		dojo.storage.PENDING status until the request is either
+		//		approved or denied, resulting in another call back with
+		//		dojo.storage.SUCCESS. 
+		//		The second argument in the call back is the key name that was being stored.
+		//		The third argument in the call back is an optional message that
+		//		details possible error messages that might have occurred during
+		//		the storage process.
+
+//	  Example:
+//		var resultsHandler = function(status, key, message){
+//		  alert("status="+status+", key="+key+", message="+message);
+//		};
+//		dojo.storage.put("test", "hello world", resultsHandler);
+		dojo.unimplemented("dojo.storage.put");
+	},
+
+	get: function(/*string*/ key){ /*Object*/
+		// summary:
+		//		Gets the value with the given key. Returns null if this key is
+		//		not in the storage system.
+		// key:
+		//		A string key to get the value of.
+		// return: Returns any JavaScript object type; null if the key is not present
+		dojo.unimplemented("dojo.storage.get");
+	},
+
+	hasKey: function(/*string*/ key){ /*Boolean*/
+		// summary: Determines whether the storage has the given key. 
+		return (this.get(key) != null);
+	},
+
+	/**
+	
+	getKeys: function(){ //Array
+		// summary: Enumerates all of the available keys in this storage system.
+		dojo.unimplemented("dojo.storage.getKeys");
+	},
+
+	*/
+	clear: function(){
+		// summary: 
+		//		Completely clears this storage system of all of it's values and
+		//		keys. 
+		dojo.unimplemented("dojo.storage.clear");
+	},
+  
+	/** Removes the given key from the storage system. */
+	remove: function(key){
+		dojo.unimplemented("dojo.storage.remove");
+	},
+
+	isPermanent: function(){ /*Boolean*/
+		// summary:
+		//		Returns whether this storage provider's values are persisted
+		//		when this platform is shutdown. 
+		dojo.unimplemented("dojo.storage.isPermanent");
+	},
+
+	/**
+	  The maximum storage allowed by this provider.
+	
+	  @returns Returns the maximum storage size 
+	           supported by this provider, in 
+	           thousands of bytes (i.e., if it 
+	           returns 60 then this means that 60K 
+	           of storage is supported).
+	    
+	           If this provider can not determine 
+	           it's maximum size, then 
+	           dojo.storage.SIZE_NOT_AVAILABLE is 
+	           returned; if there is no theoretical
+	           limit on the amount of storage 
+	           this provider can return, then
+	           dojo.storage.SIZE_NO_LIMIT is 
+	           returned
+	*/
+	getMaximumSize: function(){
+		dojo.unimplemented("dojo.storage.getMaximumSize");
+	},
+
+	hasSettingsUI: function(){ /*Boolean*/
+		// summary: Determines whether this provider has a settings UI.
+		return false;
+	},
+
+	showSettingsUI: function(){
+		// summary: If this provider has a settings UI, it is shown. 
+		dojo.unimplemented("dojo.storage.showSettingsUI");
+	},
+
+	hideSettingsUI: function(){
+		// summary: If this provider has a settings UI, hides it.
+		dojo.unimplemented("dojo.storage.hideSettingsUI");
+	},
+	
+	getType: function(){ /*String*/
+		// summary:
+		//		The provider name as a string, such as
+		//		"dojo.storage.FlashStorageProvider". 
+		dojo.unimplemented("dojo.storage.getType");
+	},
+	
+	isValidKey: function(/*string*/ keyName){ /*Boolean*/
+		// summary:
+		//		Subclasses can call this to ensure that the key given is valid
+		//		in a consistent way across different storage providers. We use
+		//		the lowest common denominator for key values allowed: only
+		//		letters, numbers, and underscores are allowed. No spaces. 
+		if((keyName == null)||(typeof keyName == "undefined")){
+			return false;
+		}
+			
+		return /^[0-9A-Za-z_]*$/.test(keyName);
+	}
+});
+
+
+
+
+/**
+	Initializes the storage systems and figures out the best available 
+	storage options on this platform.
+*/
+dojo.storage.manager = new function(){
+	this.currentProvider = null;
+	this.available = false;
+	this.initialized = false;
+	this.providers = [];
+	
+	// TODO: Provide a way for applications to override the default namespace
+	this["namespace"] = "dojo.storage";
+	
+	this.initialize = function(){
+		// summary: 
+		//		Initializes the storage system and autodetects the best storage
+		//		provider we can provide on this platform
+		this.autodetect();
+	};
+	
+	/**
+	
+	*/
+	this.register = function(/*string*/ name, /*Object*/ instance) {
+		// summary:
+		//		Registers the existence of a new storage provider; used by
+		//		subclasses to inform the manager of their existence. 
+		// name:
+		//		The full class name of this provider, such as
+		//		"dojo.storage.browser.Flash6StorageProvider".
+		// instance:
+		//		An instance of this provider, which we will use to call
+		//		isAvailable() on. 
+		this.providers[this.providers.length] = instance;
+		this.providers[name] = instance;
+	};
+	
+	/**
+	    
+	*/
+	this.setProvider = function(storageClass){
+		// summary:
+		//		Instructs the storageManager to use the given storage class for
+		//		all storage requests.
+		// description:
+		//		Example:
+		//			dojo.storage.setProvider(
+		//				dojo.storage.browser.IEStorageProvider)
+	
+	};
+	
+	this.autodetect = function(){
+		// summary:
+		//		Autodetects the best possible persistent storage provider
+		//		available on this platform. 
+		if(this.initialized == true) // already finished
+			return;
+			
+		// go through each provider, seeing if it can be used
+		var providerToUse = null;
+		for(var i = 0; i < this.providers.length; i++) {
+			providerToUse = this.providers[i];
+			if(providerToUse.isAvailable()){
+				break;
+			}
+		}	
+		
+		if(providerToUse == null){ // no provider available
+			this.initialized = true;
+			this.available = false;
+			this.currentProvider = null;
+			dojo.raise("No storage provider found for this platform");
+		}
+			
+		// create this provider and copy over it's properties
+		this.currentProvider = providerToUse;
+	  	for(var i in providerToUse){
+	  		dojo.storage[i] = providerToUse[i];
+		}
+		dojo.storage.manager = this;
+		
+		// have the provider initialize itself
+		dojo.storage.initialize();
+		
+		this.initialized = true;
+		this.available = true;
+	};
+	
+	this.isAvailable = function(){ /*Boolean*/
+		// summary: Returns whether any storage options are available.
+		return this.available;
+	};
+	
+	this.isInitialized = function(){ /*Boolean*/
+	 	// summary:
+		//		Returns whether the storage system is initialized and ready to
+		//		be used. 
+
+		// FIXME: This should _really_ not be in here, but it fixes a bug
+		if(dojo.flash.ready == false){
+			return false;
+		}else{
+			return this.initialized;
+		}
+	};
+
+	this.supportsProvider = function(/*string*/ storageClass){
+		// summary: Determines if this platform supports the given storage provider.
+		// description:
+		//		Example:
+		//			dojo.storage.manager.supportsProvider(
+		//				"dojo.storage.browser.InternetExplorerStorageProvider");
+
+		// construct this class dynamically
+		try{
+			// dynamically call the given providers class level isAvailable()
+			// method
+			var provider = eval("new " + storageClass + "()");
+			var results = provider.isAvailable();
+			if(results == null || typeof results == "undefined")
+				return false;
+			return results;
+		}catch (exception){
+			dojo.debug("exception="+exception);
+			return false;
+		}
+	};
+
+	/** Gets the current provider. */
+	this.getProvider = function(){
+		return this.currentProvider;
+	};
+	
+	this.loaded = function(){
+		// summary:
+		//		The storage provider should call this method when it is loaded
+		//		and ready to be used. Clients who will use the provider will
+		//		connect to this method to know when they can use the storage
+		//		system:
+	};
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/string.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/string.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/string.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,12 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.string");
+dojo.require("dojo.string.common");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/style.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/style.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/style.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,15 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.style");
+dojo.kwCompoundRequire({
+	browser: ["dojo.html.style"]
+});
+dojo.deprecated("dojo.style", "replaced by dojo.html.style", "0.5");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/svg.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/svg.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/svg.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,320 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.svg");
+dojo.require("dojo.lang.common");
+dojo.require("dojo.dom");
+
+dojo.mixin(dojo.svg, dojo.dom);
+
+dojo.svg.graphics=dojo.svg.g=new function(/* DOMDocument */ d){
+	//	summary
+	//	Singleton to encapsulate SVG rendering functions.
+	this.suspend=function(){
+		//	summary
+		//	Suspend the rendering engine
+		try { d.documentElement.suspendRedraw(0); } catch(e){ }
+	};
+	this.resume=function(){
+		//	summary
+		//	Resume the rendering engine
+		try { d.documentElement.unsuspendRedraw(0); } catch(e){ }
+	};
+	this.force=function(){
+		//	summary
+		//	Force the render engine to redraw
+		try { d.documentElement.forceRedraw(); } catch(e){ }
+	};
+}(document);
+
+dojo.svg.animations=dojo.svg.anim=new function(/* DOMDocument */ d){
+	//	summary
+	//	Singleton to encapsulate SVG animation functionality.
+	this.arePaused=function(){
+		//	summary
+		//	check to see if all animations are paused
+		try {
+			return d.documentElement.animationsPaused();	//	bool
+		} catch(e){
+			return false;	//	bool
+		}
+	} ;
+	this.pause=function(){
+		//	summary
+		//	pause all animations
+		try { d.documentElement.pauseAnimations(); } catch(e){ }
+	};
+	this.resume=function(){
+		//	summary
+		//	resume all animations
+		try { d.documentElement.unpauseAnimations(); } catch(e){ }
+	};
+}(document);
+
+//	fixme: these functions should be mixed in from dojo.style, but dojo.style is HTML-centric and needs to change.
+dojo.svg.toCamelCase=function(/* string */ selector){
+	//	summary
+	//	converts a CSS-style selector to a camelCased one
+	var arr=selector.split('-'), cc=arr[0];
+	for(var i=1; i < arr.length; i++) {
+		cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
+	}
+	return cc;	// string
+};
+dojo.svg.toSelectorCase=function(/* string */ selector) {
+	//	summary
+	//	converts a camelCased selector to a CSS style one
+	return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase();	//	string
+};
+dojo.svg.getStyle=function(/* SVGElement */ node, /* string */ cssSelector){
+	//	summary
+	//	get the computed style of selector for node.
+	return document.defaultView.getComputedStyle(node, cssSelector);	//	object
+};
+dojo.svg.getNumericStyle=function(/* SVGElement */ node, /* string */ cssSelector){
+	//	summary
+	//	return the numeric version of the computed style of selector on node.
+	return parseFloat(dojo.svg.getStyle(node, cssSelector));
+};
+
+//	fixme: there are different ways of doing the following, need to take into account
+dojo.svg.getOpacity=function(/* SVGElement */node){
+	//	summary
+	//	Return the opacity of the passed element
+	return Math.min(1.0, dojo.svg.getNumericStyle(node, "fill-opacity"));	//	float
+};
+dojo.svg.setOpacity=function(/* SVGElement */ node, /* float */ opacity){
+	//	summary
+	//	set the opacity of node using attributes.
+	node.setAttributeNS(this.xmlns.svg, "fill-opacity", opacity);
+	node.setAttributeNS(this.xmlns.svg, "stroke-opacity", opacity);
+};
+dojo.svg.clearOpacity=function(/* SVGElement */ node){
+	//	summary
+	//	Set any attributes setting opacity to opaque (1.0)
+	node.setAttributeNS(this.xmlns.svg, "fill-opacity", "1.0");
+	node.setAttributeNS(this.xmlns.svg, "stroke-opacity", "1.0");
+};
+
+/**
+ *	Coordinates and dimensions.
+ */
+
+// TODO ////////////////////////////////////////////////////////// TODO
+dojo.svg.getCoords=function(/* SVGElement */ node){
+	//	summary
+	//	Returns the x/y coordinates of the passed node, if available.
+	if (node.getBBox) {
+		var box=node.getBBox();
+		return { x: box.x, y: box.y };	//	object
+	}
+	return null;	//	object
+};
+dojo.svg.setCoords=function(/* SVGElement */node, /* object */coords){
+	//	summary
+	//	Set the x/y coordinates of the passed node
+	var p=dojo.svg.getCoords();
+	if (!p) return;
+	var dx=p.x - coords.x;
+	var dy=p.y - coords.y;
+	dojo.svg.translate(node, dx, dy);
+};
+dojo.svg.getDimensions=function(/* SVGElement */node){
+	//	summary
+	//	Get the height and width of the passed node.
+	if (node.getBBox){
+		var box=node.getBBox();
+		return { width: box.width, height : box.height };	//	object
+	}
+	return null;	//	object
+};
+dojo.svg.setDimensions=function(/* SVGElement */node, /* object */dim){
+	//	summary
+	//	Set the dimensions of the passed element if possible.
+	//	will only support shape-based and container elements; path-based elements are ignored.
+	if (node.width){
+		node.width.baseVal.value=dim.width;
+		node.height.baseVal.value=dim.height;
+	}
+	else if (node.r){
+		node.r.baseVal.value=Math.min(dim.width, dim.height)/2;
+	}
+	else if (node.rx){
+		node.rx.baseVal.value=dim.width/2;
+		node.ry.baseVal.value=dim.height/2;
+	}
+};
+
+/**
+ *	Transformations.
+ */
+dojo.svg.translate=function(/* SVGElement */node, /* int */dx, /* int */dy){
+	//	summary
+	//	Translates the passed node by dx and dy
+	if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){
+		var t=node.ownerSVGElement.createSVGTransform();
+		t.setTranslate(dx, dy);
+		node.transform.baseVal.appendItem(t);
+	}
+};
+dojo.svg.scale=function(/* SVGElement */node, /* float */scaleX, /* float? */scaleY){
+	//	summary
+	//	Scales the passed element by factor scaleX and scaleY.  If scaleY not passed, scaleX is used.
+	if (!scaleY) var scaleY=scaleX;
+	if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){
+		var t=node.ownerSVGElement.createSVGTransform();
+		t.setScale(scaleX, scaleY);
+		node.transform.baseVal.appendItem(t);
+	}
+};
+dojo.svg.rotate=function(/* SVGElement */node, /* float */ang, /* int? */cx, /* int? */cy){
+	//	summary
+	//	rotate the passed node by ang, with optional cx/cy as the rotation point.
+	if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){
+		var t=node.ownerSVGElement.createSVGTransform();
+		if (cx == null) t.setMatrix(t.matrix.rotate(ang));
+		else t.setRotate(ang, cx, cy);
+		node.transform.baseVal.appendItem(t);
+	}
+};
+dojo.svg.skew=function(/* SVGElement */node, /* float */ang, /* string? */axis){
+	//	summary
+	//	skew the passed node by ang over axis.
+	var dir=axis || "x";
+	if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){
+		var t=node.ownerSVGElement.createSVGTransform();
+		if (dir != "x") t.setSkewY(ang);
+		else t.setSkewX(ang);
+		node.transform.baseVal.appendItem(t);
+	}
+};
+dojo.svg.flip=function(/* SVGElement */node, /* string? */axis){
+	//	summary
+	//	flip the passed element over axis
+	var dir=axis || "x";
+	if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){
+		var t=node.ownerSVGElement.createSVGTransform();
+		t.setMatrix((dir != "x") ? t.matrix.flipY() : t.matrix.flipX());
+		node.transform.baseVal.appendItem(t);
+	}
+};
+dojo.svg.invert=function(/* SVGElement */node){
+	//	summary
+	//	transform the passed node by the inverse of the current matrix
+	if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){
+		var t=node.ownerSVGElement.createSVGTransform();
+		t.setMatrix(t.matrix.inverse());
+		node.transform.baseVal.appendItem(t);
+	}
+};
+dojo.svg.applyMatrix=function(
+	/* SVGElement */node, 
+	/* int || SVGMatrix */a, 
+	/* int? */b, 
+	/* int? */c, 
+	/* int? */d, 
+	/* int? */e, 
+	/* int? */f
+){
+	//	summary
+	//	apply the passed matrix to node.  If params b - f are passed, a matrix will be created.
+	if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){
+		var m;
+		if (b){
+			var m=node.ownerSVGElement.createSVGMatrix();
+			m.a=a;
+			m.b=b;
+			m.c=c;
+			m.d=d;
+			m.e=e;
+			m.f=f;
+		} else m=a;
+		var t=node.ownerSVGElement.createSVGTransform();
+		t.setMatrix(m);
+		node.transform.baseVal.appendItem(t);
+	}
+};
+
+/**
+ *	Grouping and z-index operations.
+ */
+dojo.svg.group=function(/* Nodelist || array */nodes){
+	//	summary
+	//	expect an array of nodes, attaches the group to the parent of the first node.
+	var p=nodes.item(0).parentNode;
+	var g=document.createElementNS(this.xmlns.svg, "g");
+	for (var i=0; i < nodes.length; i++) g.appendChild(nodes.item(i));
+	p.appendChild(g);
+	return g;
+};
+dojo.svg.ungroup=function(/* SVGGElement */g){
+	//	summary
+	//	puts the children of the group on the same level as group was.
+	var p=g.parentNode;
+	while (g.childNodes.length > 0) p.appendChild(g.childNodes.item(0));
+	p.removeChild(g);
+};
+//	if the node is part of a group, return the group, else return null.
+dojo.svg.getGroup=function(/* SVGElement */node){
+	//	summary
+	//	if the node is part of a group, return the group, else return null.
+	var a=this.getAncestors(node);
+	for (var i=0; i < a.length; i++){
+		if (a[i].nodeType == this.ELEMENT_NODE && a[i].nodeName.toLowerCase() == "g")
+			return a[i];
+	}
+	return null;
+};
+dojo.svg.bringToFront=function(/* SVGElement */node){
+	//	summary
+	//	move the passed node the to top of the group (i.e. last child)
+	var n=this.getGroup(node) || node;
+	n.ownerSVGElement.appendChild(n);
+};
+dojo.svg.sendToBack=function(/* SVGElement */node){
+	//	summary
+	//	move the passed node to the bottom of the group (i.e. first child)
+	var n=this.getGroup(node) || node;
+	n.ownerSVGElement.insertBefore(n, n.ownerSVGElement.firstChild);
+};
+
+//	TODO: possibly push node up a level in the DOM if it's at the beginning or end of the childNodes list.
+dojo.svg.bringForward=function(/* SVGElement */node){
+	//	summary
+	//	move the passed node up one in the child node chain
+	var n=this.getGroup(node) || node;
+	if (this.getLastChildElement(n.parentNode) != n){
+		this.insertAfter(n, this.getNextSiblingElement(n), true);
+	}
+};
+dojo.svg.sendBackward=function(/* SVGElement */node){
+	//	summary
+	//	move the passed node down one in the child node chain
+	var n=this.getGroup(node) || node;
+	if (this.getFirstChildElement(n.parentNode) != n){
+		this.insertBefore(n, this.getPreviousSiblingElement(n), true);
+	}
+};
+// END TODO ////////////////////////////////////////////////////// TODO
+
+dojo.svg.createNodesFromText=function(/* string */ txt, /* bool? */ wrap){
+	//	summary
+	//	Create a list of nodes from text
+	var docFrag=(new DOMParser()).parseFromString(txt, "text/xml").normalize();
+	if(wrap){ 
+		return [docFrag.firstChild.cloneNode(true)];	//	array
+	}
+	var nodes=[];
+	for(var x=0; x<docFrag.childNodes.length; x++){
+		nodes.push(docFrag.childNodes.item(x).cloneNode(true));
+	}
+	return nodes;	// array
+}
+// vim:ts=4:noet:tw=0:

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/validate.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/validate.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/src/validate.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,12 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.validate");
+dojo.require("dojo.validate.common");

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_container.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_container.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_container.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
+<html>
+<head>
+<title>Dojo Drag Constraints Box Test</title>
+
+<script type="text/javascript">
+	var djConfig = { isDebug: true /* , debugAtAllCosts: true */ };
+</script>
+
+<script type="text/javascript" src="../../dojo.js"></script>
+<script type="text/javascript">
+	dojo.require("dojo.dnd.*");
+	dojo.require("dojo.event.*");
+	// dojo.hostenv.writeIncludes();
+</script>
+<script type="text/javascript">
+
+	function init() {
+		var tests = 9;
+		var src = [];
+		for (var i=0;i<tests; i++) {
+			var tmpBox = dojo.byId("myContainer"+i);
+			var tmpDragMe = dojo.byId("dragMe"+i);
+			new dojo.dnd.HtmlDropTarget(tmpBox);
+			src[i] = new dojo.dnd.HtmlDragSource(tmpDragMe);
+			src[i].constrainTo(tmpBox);
+		}
+	}
+
+	dojo.event.connect(dojo, "loaded", "init");
+
+</script>
+</head>
+<body>
+	<div id="myContainer0" style="position: absolute; left: 0px; top:30px; width:200px; height:200px; margin: 0px; padding: 0px; border: 1px solid blue;" >
+		1px;border, no margin, no padding
+		<div id="dragMe0" style="border:1px solid red; width: 50px; height: 50px; padding:0px;">drag me</div>
+	</div>
+	<div id="myContainer1" style="position: absolute; left: 250px; top:30px; width:200px; height:200px; margin: 0px; padding: 5px; border: 1px solid blue;" >
+		1px;border, no margin, 5px padding
+		<div id="dragMe1" style="border:1px solid red; width: 50px; height: 50px; padding:0px;">drag me</div>
+	</div>
+	<div id="myContainer2" style="position: absolute; left: 500px; top:30px; width:200px; height:200px; margin: 5px; padding: 5px; border: 1px solid blue;" >
+		1px border, 5px margin, 5px padding
+		<div id="dragMe2" style="border:1px solid red; width: 50px; height: 50px; padding:0px;">drag me</div>
+	</div>
+
+	<div id="myContainer3" style="position: absolute; left: 0px; top:300px; width:200px; height:200px; margin: 5px; padding: 0px; border: 1px solid blue;" >
+		1px border, 5px margin, no padding
+		<div id="dragMe3" style="border:1px solid red; width: 50px; height: 50px; padding:0px;">drag me</div>
+	</div>
+	<div id="myContainer4" style="position: absolute; left: 250px; top:300px; width:200px; height:200px; margin: 0px; padding: 5px; border: 5px solid blue;" >
+		5px border, no margin, 5px padding
+		<div id="dragMe4" style="border:1px solid red; width: 50px; height: 50px; padding:0px;">drag me</div>
+	</div>
+	<div id="myContainer5" style="position: absolute; left: 500px; top:300px; width:200px; height:200px; margin: 5px; padding: 0px; border: 5px solid blue;" >
+		5px border, 5px margin, no padding
+		<div id="dragMe5" style="border:1px solid red; width: 50px; height: 50px; padding:0px;">drag me</div>
+	</div>
+
+	<div id="myContainer6" style="position: absolute; left: 0px; top:600px; width:200px; height:200px; margin: 0px; padding: 0px; border: 0px solid blue;" >
+		0px border, no margin, no padding
+		<div id="dragMe6" style="border:1px solid red; width: 50px; height: 50px; padding:0px;">drag me</div>
+	</div>
+	<div id="myContainer7" style="position: absolute; left: 250px; top:600px;width:200px; height:200px; margin: 0px; padding: 5px; border: 0px solid blue;" >
+		0px border, no margin, 5px padding
+		<div id="dragMe7" style="border:1px solid red; width: 50px; height: 50px; padding:0px;">drag me</div>
+	</div>
+	<div id="myContainer8" style="position: absolute; left: 500px; top:600px; width:200px; height:200px; margin: 5px; padding: 5px; border: 0px solid blue;" >
+		0px border, 5px margin, 5px padding
+		<div id="dragMe8" style="border:1px solid red; width: 50px; height: 50px; padding:0px;">drag me</div>
+	</div>
+
+</body>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragcopy.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragcopy.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragcopy.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,58 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Drag-and-drop test</title>
+<script type="text/javascript">
+var djConfig = {
+	isDebug: true
+	//, debugAtAllCosts: true
+};
+</script>
+<script type="text/javascript" src="../../dojo.js"></script>
+<!--<script type="text/javascript" src="../../src/dnd/HtmlDragCopy.js"></script>-->
+<script type="text/javascript">
+
+dojo.require("dojo.dnd.HtmlDragCopy");
+
+dojo.addOnLoad(function() {
+	// list one
+	var dl = dojo.byId("dragList1");
+	var lis = dl.getElementsByTagName("li");
+	for(var x=0; x<lis.length; x++){
+		new dojo.dnd.HtmlDragCopySource(lis[x], "li1", x == 1);
+	}
+	// list two
+	var dl = dojo.byId("dragList2");
+	new dojo.dnd.HtmlDropTarget(dl, ["li1"]);
+	var lis = dl.getElementsByTagName("li");
+	for(var x=0; x<lis.length; x++){
+		new dojo.dnd.HtmlDragSource(lis[x], "li1");
+	}
+});
+
+</script>
+<style type="text/css">
+.move li { cursor: move; }
+</style>
+</head>
+<body>
+
+<h1>Drag-and-drop test</h1>
+
+<p>The first list is a source of items for the second list. Items will be copied to the target (rather than being moved, like normal).</p>
+
+<ul id="dragList1" class="move">
+	<li>list 1 item 1 (dragging the clone will create a new copy)</li>
+	<li>list 1 item 2 (dragging the clone will move it)</li>
+	<li>list 1 item 3 (dragging the clone will create a new copy)</li>
+</ul>
+
+<p>This is the second list (drag items from the top down here and they will be cloned, and a clone of the object will be added to this list)</p>
+
+<ul id="dragList2" class="move">
+	<li>list 2 item 1</li>
+	<li>list 2 item 2</li>
+	<li>list 2 item 3</li>
+</ul>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragdropparent.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragdropparent.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragdropparent.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,119 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+        "http://www.w3.org/TR/html4/strict.dtd"> 
+
+<title>Dojo DnD parent position test</title>
+
+<style type="text/css"> body { margin: 50px; } </style>
+
+<script type="text/javascript">
+	var djConfig = { isDebug: true };
+</script>
+
+<script type="text/javascript" src="../../dojo.js"></script>
+
+<script type="text/javascript">
+	dojo.require("dojo.dnd.HtmlDragAndDrop");
+	dojo.require("dojo.widget.DebugConsole");
+	dojo.require("dojo.widget.ResizeHandle");
+	dojo.require("dojo.event.*");
+
+	function init(){
+		new dojo.dnd.HtmlDragSource(dojo.byId("layer1"));
+		new dojo.dnd.HtmlDragSource(dojo.byId("layer2"));
+		new dojo.dnd.HtmlDragSource(dojo.byId("layer3"));
+		new dojo.dnd.HtmlDragSource(dojo.byId("layer4"));
+		new dojo.dnd.HtmlDragSource(dojo.byId("layer5"));
+		new dojo.dnd.HtmlDragSource(dojo.byId("layer6"));
+		new dojo.dnd.HtmlDragSource(dojo.byId("layer7"));
+	}
+	dojo.event.connect(dojo, "loaded", "init");
+</script>
+<p>Make sure that all can be dragged nicely.</p>
+<p>Remember to check that it works when the window is scrolled!</p>
+<p>a</p>
+<p>b</p>
+<p>c</p>
+<!-- test for positioning bug if object is inside a form (that isn't at the top left of the body -->
+<form>
+	This list is inside a form: Drag should Not start from top left of screen.
+	<ul id="layer3">
+		<li>list 3 item 1</li>
+		<li>list 3 item 2</li>
+		<li>list 3 item 3</li>
+	</ul>
+</form>
+<div>
+	This list is inside a div
+	<ul id="layer5">
+		<li>list 5 item 1</li>
+		<li>list 5 item 2</li>
+	</ul>
+</div>
+<div>
+	This list is inside a div, and the list itself is relative positioned
+	<ul id="layer6" style="position:relative; left:100px">
+		<li>list 6 item 1</li>
+		<li>list 6 item 2</li>
+	</ul>
+</div>
+
+<ul id="layer7">
+	<li>list at root item 1</li>
+	<li>list at root item 2</li>
+</ul>
+
+<form style="position:relative; left:200px">
+	This list is also inside a form which is relative positioned.
+	<ul id="layer1">
+		<li>list 1 item 1</li>
+		<li>list 1 item 2</li>
+		<li>list 1 item 3</li>
+	</ul>
+</form>
+
+<div style="position:relative; left:250px;background-color:yellow"> 	
+<form>
+	This list is inside a form, which is inside a relative positioned div
+	<ul id="layer2">
+		<li>list 2 item 1</li>
+		<li>list 2 item 2</li>
+		<li>list 2 item 3</li>
+	</ul>
+</form>
+</div>
+
+<div style="position:absolute; top:100px;left:300px;background-color:red"> 	
+<form style="position:relative; left:50px">	
+	This list is inside a relative-postioned form, which is inside an absolute positioned div
+	<ul id="layer4">
+		<li>list 4 item 1</li>
+		<li>list 4 item 2</li>
+		<li>list 4 item 3</li>
+	</ul>
+</form>
+</div>
+
+<div style="position:relative; left:200 px">
+  <p>a debug conosle inside a relative div</p>
+  <div dojoType="DebugConsole"
+	title="Debug Console"
+	style="width: 600px; height: 200px; left: 200px;"
+	hasShadow="true"
+	resizable="true"
+	displayCloseAction="true"
+	layoutAlign="client"
+	>
+  </div>
+</div>
+
+<p>a debug console in body</p>
+  <div dojoType="DebugConsole"
+	title="Debug Console"
+	style="width: 600px; height: 200px; left: 200px;"
+	hasShadow="true"
+	resizable="true"
+	displayCloseAction="true"
+	layoutAlign="client"
+	>
+  </div>
+	
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_draghandle.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_draghandle.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_draghandle.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+        "http://www.w3.org/TR/html4/strict.dtd"> 
+<html>
+<head>
+<title>Dojo DragHandle Test</title>
+
+<script type="text/javascript">
+	var djConfig = { isDebug: true /* , debugAtAllCosts: true */ };
+</script>
+
+<script type="text/javascript" src="../../dojo.js"></script>
+<script type="text/javascript">
+	dojo.require("dojo.dnd.*");
+	dojo.require("dojo.event.*");
+	// dojo.hostenv.writeIncludes();
+</script>
+<script type="text/javascript">
+
+	function init(){
+		var dl = dojo.byId("dragList");
+		new dojo.dnd.HtmlDropTarget(dl, ["li"]);
+		var lis = dl.getElementsByTagName("li");
+		for(var x=0; x<lis.length; x++){
+			new dojo.dnd.HtmlDragSource(lis[x], null, lis[x].firstChild);
+		}
+	}
+
+	dojo.event.connect(dojo, "loaded", "init");
+</script>
+</head>
+<body>
+	<ul id="dragList">
+		<li><b>*</b>item 1</li>
+		<li><b>*</b>item 2</li>
+		<li><b>*</b>item 3</li>
+	</ul>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_draghandle_2.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_draghandle_2.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_draghandle_2.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+        "http://www.w3.org/TR/html4/strict.dtd"> 
+<html>
+<head>
+<title>Dojo Dragdrop Handle Test</title>
+
+<script type="text/javascript">
+	var djConfig = { isDebug: true };
+</script>
+
+<script type="text/javascript" src="../../dojo.js"></script>
+<script type="text/javascript">
+	dojo.require("dojo.html");
+	dojo.require("dojo.dnd.*");
+	dojo.require("dojo.event.*");
+
+	function init(){
+		dojo.html.disableSelection(dojo.byId('inner1'));
+
+		var drag = new dojo.dnd.HtmlDragSource(dojo.byId('outer1'));
+		drag.setDragHandle(dojo.byId('inner1'));
+
+		var drag = new dojo.dnd.HtmlDragSource(dojo.byId('inner2'));
+		drag.setDragTarget(dojo.byId('outer2'));
+	}
+
+	dojo.event.connect(dojo, "loaded", "init");
+</script>
+<style>
+.outer {
+	position: absolute;
+	top: 25px;
+	left: 25;
+	width: 300px;
+	height: 300px;
+	background-color: lightblue;
+}
+
+.inner {
+	position: absolute;
+	top: 100px;
+	left: 10px;
+	width: 100px;
+	height: 30px;
+	background-color: pink;
+}
+
+#outer2 {
+	left: 375px;
+}
+
+</style>
+</head>
+<body>
+
+<div class="outer" id="outer1">
+	<div class="inner" id="inner1">
+		drag me!
+	</div>
+	i am dragged only by my inner handle<br />
+	we first register the outer box as a drag source, then change it's handle to the inner box
+</div>
+
+<div class="outer" id="outer2">
+	<div class="inner" id="inner2">
+		drag me!
+	</div>
+	i am dragged only by my inner handle<br />
+	we first register the inner box as a drag source, then change it's target to the outer box
+</div>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragmove.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragmove.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragmove.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+        "http://www.w3.org/TR/html4/strict.dtd"> 
+
+<title>Dojo Button Widget Test</title>
+
+<style type="text/css"> body { margin: 50px; } </style>
+
+<script type="text/javascript">
+	var djConfig = { isDebug: true };
+</script>
+
+<script type="text/javascript" src="../../dojo.js"></script>
+<script type="text/javascript">
+	dojo.require("dojo.dnd.HtmlDragMove");
+	dojo.require("dojo.event.*");
+
+	function byId(id){
+		return document.getElementById(id);
+	}
+
+	function init(){
+		new dojo.dnd.HtmlDragMoveSource(byId("layer1"));
+		new dojo.dnd.HtmlDragSource(byId("layer2"));
+		new dojo.dnd.HtmlDragMoveSource(byId("layer3"));
+		new dojo.dnd.HtmlDragMoveSource(byId("link1"));
+		new dojo.dnd.HtmlDragMoveSource(byId("span1"));
+		new dojo.dnd.HtmlDragMoveSource(byId("table1"));
+	}
+
+	dojo.event.connect(dojo, "loaded", "init");
+</script>
+
+<div id="layer1" style="border: 1px solid black;">
+	<ul>
+		<li>list 1 item 1</li>
+		<li>list 1 item 2</li>
+		<li>list 1 item 3</li>
+	</ul>
+</div>
+
+<ul id="layer2">
+	<li>list 2 item 1</li>
+	<li>list 2 item 2</li>
+	<li>list 2 item 3</li>
+</ul>
+
+<!-- test for positioning bug if object is inside a form (that isn't at the top left of the body -->
+<form>
+	This list is inside a form:
+	<ul id="layer3">
+		<li>list 3 item 1</li>
+		<li>list 3 item 2</li>
+		<li>list 3 item 3</li>
+	</ul>
+</form>
+
+<a id="link1" href="#">a link</a>
+
+<span id="span1">a span</span>
+
+<table id="table1" border=1>
+<tr><td>i</td><td>'</td><td>m</td></tr>
+<tr><td>a</td><td>t</td><td>a</td></tr>
+<tr><td>b</td><td>l</td><td>e</td></tr>
+</table>
+
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragmoveparent.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragmoveparent.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_dragmoveparent.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,112 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+        "http://www.w3.org/TR/html4/strict.dtd"> 
+
+<title>Dojo DnD parent position test</title>
+
+<style type="text/css"> body { margin: 50px; } </style>
+
+<script type="text/javascript">
+	var djConfig = { isDebug: true };
+</script>
+
+<script type="text/javascript" src="../../dojo.js"></script>
+
+<script type="text/javascript">
+	dojo.require("dojo.dnd.HtmlDragMove");
+	dojo.require("dojo.widget.DebugConsole");
+	dojo.require("dojo.widget.ResizeHandle");
+	dojo.require("dojo.event.*");
+
+	function init(){
+		new dojo.dnd.HtmlDragMoveSource(dojo.byId("layer1"));
+		new dojo.dnd.HtmlDragMoveSource(dojo.byId("layer2"));
+		new dojo.dnd.HtmlDragMoveSource(dojo.byId("layer3"));
+		new dojo.dnd.HtmlDragMoveSource(dojo.byId("layer4"));
+		new dojo.dnd.HtmlDragMoveSource(dojo.byId("layer5"));
+		new dojo.dnd.HtmlDragMoveSource(dojo.byId("layer6"));
+	}
+	dojo.event.connect(dojo, "loaded", "init");
+</script>
+<p>Make sure that all can be dragged nicely.</p>
+<p>Remember to check that it works when the window is scrolled!</p>
+<p>a</p>
+<p>b</p>
+<p>c</p>
+<!-- test for positioning bug if object is inside a form (that isn't at the top left of the body -->
+<form>
+	This list is inside a form: Drag should Not start from top left of screen.
+	<ul id="layer3">
+		<li>list 3 item 1</li>
+		<li>list 3 item 2</li>
+		<li>list 3 item 3</li>
+	</ul>
+</form>
+<div>
+	This list is inside a div
+	<ul id="layer5">
+		<li>list 5 item 1</li>
+		<li>list 5 item 2</li>
+	</ul>
+</div>
+<div>
+	This list is inside a div, and the list itself is relative positioned
+	<ul id="layer6" style="position:relative; left:100px;">
+		<li>list 5 item 1</li>
+		<li>list 5 item 2</li>
+	</ul>
+</div>		
+<form style="position:relative; left:200px;">
+	This list is also inside a form which is relative positioned.
+	<ul id="layer1">
+		<li>list 1 item 1</li>
+		<li>list 1 item 2</li>
+		<li>list 1 item 3</li>
+	</ul>
+</form>
+
+<div style="position:relative; left:250px; height: 100px; background-color:yellow"> 	
+<form>
+	This list is inside a form, which is inside a relative positioned div
+	<ul id="layer2">
+		<li>list 2 item 1</li>
+		<li>list 2 item 2</li>
+		<li>list 2 item 3</li>
+	</ul>
+</form>
+</div>
+
+<div style="position:absolute; top:100px; left:300px; height: 100px; background-color:red"> 	
+<form style="position:relative; left:50px">	
+	This list is inside a relative-postioned form, which is inside an absolute positioned div
+	<ul id="layer4">
+		<li>list 4 item 1</li>
+		<li>list 4 item 2</li>
+		<li>list 4 item 3</li>
+	</ul>
+</form>
+</div>
+
+<div style="position:relative; left:200 px">
+  <p>a debug conosle inside a relative div</p>
+  <div dojoType="DebugConsole"
+	title="Debug Console"
+	style="width: 600px; height: 200px; left: 200px;"
+	hasShadow="true"
+	resizable="true"
+	displayCloseAction="true"
+	layoutAlign="client"
+	>
+  </div>
+</div>
+
+<p>a debug console in body</p>
+  <div dojoType="DebugConsole"
+	title="Debug Console"
+	style="width: 600px; height: 200px; left: 200px;"
+	hasShadow="true"
+	resizable="true"
+	displayCloseAction="true"
+	layoutAlign="client"
+	>
+  </div>
+	
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_nested_drop_targets.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_nested_drop_targets.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_nested_drop_targets.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+        "http://www.w3.org/TR/html4/strict.dtd"> 
+
+<title>Dojo Button Widget Test</title>
+
+<script type="text/javascript">
+	var djConfig = { isDebug: true };
+</script>
+
+<script type="text/javascript" src="../../dojo.js"></script>
+<script type="text/javascript">
+	dojo.require("dojo.dnd.*");
+	dojo.require("dojo.event.*");
+
+	dojo.dnd.dragManager.nestedTargets = true;
+
+	function byId(id){
+		return document.getElementById(id);
+	}
+
+	function outerMove(){
+		dojo.debug("outer");
+	}
+
+	function innerMove(){
+		dojo.debug("inner");
+	}
+
+	function init(){
+		new dojo.dnd.HtmlDragSource(byId("source"), "source");
+		var dt1 = new dojo.dnd.HtmlDropTarget(byId("outer"), ["*"]);
+		dojo.event.connect(dt1, "onDragOver", "outerMove");
+		var dt2 = new dojo.dnd.HtmlDropTarget(byId("inner"), ["*"]);
+		dojo.event.connect(dt2, "onDragOver", "innerMove");
+	}
+
+	dojo.event.connect(dojo, "loaded", "init");
+</script>
+
+<div style="width: 50px; height: 30px; border: 1px dotted black;" id="source">source</div>
+
+<div style="width: 400px; height: 400px; border: 1px solid black;" id="outer">
+	outter drop target
+	<div style="width: 200px; height: 200px; border: 1px dotted black;" id="inner">
+		<p>inner drop target</p>
+	</div>
+</div>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_simple.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_simple.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/dojo-0.4.0-mini/tests/dnd/test_simple.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+        "http://www.w3.org/TR/html4/strict.dtd"> 
+
+<title>Dojo Button Widget Test</title>
+
+<script type="text/javascript">
+	var djConfig = { isDebug: true };
+</script>
+
+<script type="text/javascript" src="../../dojo.js"></script>
+<script type="text/javascript">
+	dojo.require("dojo.dnd.*");
+	dojo.require("dojo.event.*");
+
+	function byId(id){
+		return document.getElementById(id);
+	}
+
+	function init(){
+		// list one
+		var dl = byId("dragList1");
+		new dojo.dnd.HtmlDropTarget(dl, ["li1"]);
+		var lis = dl.getElementsByTagName("li");
+		for(var x=0; x<lis.length; x++){
+			new dojo.dnd.HtmlDragSource(lis[x], "li1");
+		}
+
+		// list two
+		var dl = byId("dragList2");
+		new dojo.dnd.HtmlDropTarget(dl, ["li1"]);
+		var lis = dl.getElementsByTagName("li");
+		for(var x=0; x<lis.length; x++){
+			new dojo.dnd.HtmlDragSource(lis[x], "li1");
+		}
+
+		// list three
+		var dl = byId("dragList3");
+		new dojo.dnd.HtmlDropTarget(dl, ["li2"]);
+		var lis = dl.getElementsByTagName("li");
+		for(var x=0; x<lis.length; x++){
+			new dojo.dnd.HtmlDragSource(lis[x], "li2");
+		}
+	}
+
+	dojo.event.connect(dojo, "loaded", "init");
+</script>
+
+<ul id="dragList1">
+	<li>list 1 item 1 
+		<a href="http://www.google.com">Google</a>
+		<input value="type in me">
+		<input type="button" value="ClickMe">
+		<input type="checkbox">
+		<input type="radio" name="radio" value="1" checked> 
+		<input type="radio" name="radio" value="2"> 
+		<button>Inline Button</button>
+	</li>
+	<li>list 1 item 2</li>
+	<li>list 1 item 3</li>
+</ul>
+
+<p>Here some text between two lists. You should be able to drag between the lists either side of me.</p>
+
+<ul id="dragList2">
+	<li>list 2 item 1</li>
+	<li>list 2 item 2</li>
+	<li>list 2 item 3</li>
+</ul>
+
+<p>Here some text between two lists. You should only be able to drag items from the list below to the list below and not to the other two lists.</p>
+
+<ul id="dragList3">
+	<li>list 3 item 1</li>
+	<li>list 3 item 2</li>
+	<li>list 3 item 3</li>
+</ul>
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame-left.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame-left.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame-left.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,5 @@
+<html>
+	<body>
+		This is frame LEFT
+	</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame-right.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame-right.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame-right.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,10 @@
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html">
+<title>frame-right</title>
+</head>
+<body>
+This frame RIGHT.
+<a href="./frame_to_be_loaded.html" target="leftFrame" id="show">Show new frame in leftFrame</a>
+</body>
+</html> 
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame_to_be_loaded.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame_to_be_loaded.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/frame_to_be_loaded.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,5 @@
+<html>
+	<body>
+		content loaded
+	</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/path1/cookie1.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/path1/cookie1.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/path1/cookie1.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1 @@
+<html><head><title>Cookie 1</title><body><h1>Cookie 1</h1></body></html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/path2/cookie2.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/path2/cookie2.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/path2/cookie2.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1 @@
+<html><head><title>Cookie 2</title><body><h2>Cookie 2</h2></body></html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/show_message.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/show_message.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/show_message.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,53 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<script type="text/javascript">
+<!--
+    // This JavaScript will dump all the parameters in the URL. Useful for checking forms.
+    // if you are are not using a webserver.
+
+    // paramsMap is an associative array (like a Java map, Perl hash, or Python dictionary).
+    var paramsMap = new Array();
+
+    var query=this.location.search.substring(1);
+    if (query.length > 0){
+        var params=query.split("&");
+        for (var i=0 ; i<params.length ; i++){
+            var pos = params[i].indexOf("=");
+            var name = params[i].substring(0, pos);
+            var value = params[i].substring(pos + 1);
+            paramsMap[name] = value;
+        }
+    }
+// -->
+</script>
+
+</head>
+<body>
+    <div style="margin-left:50px; font-family: Monaco, 'lucida console', Courier; font-size: 14pt; ">
+        <H1>
+            <script type="text/javascript">
+            <!--
+            message = ">>> " + paramsMap['msg'] + "";
+            document.writeln(message);
+            // -->
+            </script>
+        </H1>
+    </div>
+</body>
+<html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html><head><title>Javascript Slider Control - Example</title>
+
+
+
+<link rel="stylesheet" type="text/css" href="example_files/slider.css">
+<script type="text/javascript" src="example_files/addanevent.js"></script>
+<script type="text/javascript" src="example_files/slider.js"></script>
+<script type="text/javascript" src="example_files/slider-setup.js"></script></head><body>
+
+Slider 1:
+<div class="slider" id="slider01">
+	<div class="left"></div>
+	<div class="right"></div>
+	<img style="left: 207px;" src="example_files/knob.png" height="15" width="31">
+</div>
+Box 1:
+<input id="output1">
+<hr>
+
+</body></html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/addanevent.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/addanevent.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/addanevent.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,11 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+function addAnEvent(el, evname, func) {
+    if (el.attachEvent) { // IE
+        el.attachEvent("on" + evname, func);
+    } else if (el.addEventListener) { // Gecko / W3C
+        el.addEventListener(evname, func, true);
+    } else {
+        el["on" + evname] = func;
+    }
+}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/img/bg.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/img/bg.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/img/left.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/img/left.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/img/right.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/img/right.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/knob.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/knob.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/knob2.png
===================================================================
(Binary files differ)


Property changes on: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/knob2.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider-setup.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider-setup.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider-setup.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,20 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+var slider=new Array();
+slider[1]=new Object();
+slider[1].min=0;
+slider[1].max=20;
+slider[1].val=20/3;
+slider[1].onchange=setBoxValue;
+slider[2]=new Object();
+slider[2].min=10;
+slider[2].max=20;
+slider[2].val=10/3;
+slider[2].onchange=setBoxValue;
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+function setBoxValue(val, box) {
+    var b=document.getElementById('output'+box);
+	val=Math.round(val*1000)/1000;
+	b.value=val;
+}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider.css
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider.css	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider.css	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,26 @@
+.slider {
+	margin: 0.5em auto;
+	position: relative;
+	width: 300px;
+	height: 15px;
+	background: url(img/bg.png) 50% 50% repeat-x;
+}
+.slider img {
+	position: absolute;
+	top: 0px;
+	left: 85px;
+}
+.slider .left, .slider .right {
+	height: 12px;
+	width: 2px;
+	position: absolute;
+	top: 0px;
+}
+.slider .left {
+	left: 0px;
+	background: url(img/left.png) 0px 1px no-repeat;
+}
+.slider .right {
+	right: 0px;
+	background: url(img/right.png) 0px 1px no-repeat;
+}

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/slider/example_files/slider.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,125 @@
+
+function drawSliderByVal(slider) {
+	var knob=slider.getElementsByTagName('img')[0];
+	var p=(slider.val-slider.min)/(slider.max-slider.min);
+	var x=(slider.scrollWidth-30)*p;
+	knob.style.left=x+"px";
+}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+function setSliderByClientX(slider, clientX) {
+	var p=(clientX-slider.offsetLeft-15)/(slider.scrollWidth-30);
+	slider.val=(slider.max-slider.min)*p + slider.min;
+	if (slider.val>slider.max) slider.val=slider.max;
+	if (slider.val<slider.min) slider.val=slider.min;
+
+	drawSliderByVal(slider);
+	slider.onchange(slider.val, slider.num);
+}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+function sliderClick(e) {
+	var el=sliderFromEvent(e);
+	if (!el) return;
+
+	setSliderByClientX(el, e.clientX);
+}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+function sliderMouseMove(e) {
+	var el=sliderFromEvent(e);
+	if (!el) return;
+	if (activeSlider<0) return;
+
+	setSliderByClientX(el, e.clientX);
+	stopEvent(e);
+}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+function sliderFromEvent(e) {
+	if (!e && window.event) e=window.event;
+	if (!e) return false;
+    if (!isLeftButton(e)) return false;
+
+    var el;
+	if (e.target) el=e.target;
+	if (e.srcElement) el=e.srcElement;
+
+	if (!el.id || !el.id.match(/slider\d+/)) el=el.parentNode;
+	if (!el) return false;
+	if (!el.id || !el.id.match(/slider\d+/)) return false;
+
+	return el;
+}
+
+function isLeftButton(e) {
+    return (((e.which) && (e.which == 1)) || ((e.button) && (e.button == 1)));
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+function attachSliderEvents() {
+	var divs=document.getElementsByTagName('div');
+	var divNum;
+	for(var i=0; i<divs.length; i++) {
+		if (divNum=divs[i].id.match(/\bslider(\d+)\b/)) {
+			divNum=parseInt(divNum[1]);
+			divs[i].min=slider[divNum].min;
+			divs[i].max=slider[divNum].max;
+			divs[i].val=slider[divNum].val;
+			divs[i].onchange=slider[divNum].onchange;
+			divs[i].num=divNum;
+			addAnEvent(divs[i], 'mousedown', function(e){
+				sliderClick(e);
+				var el=sliderFromEvent(e);
+				if (!el) return;
+				activeSlider=el.num;
+				stopEvent(e);
+			});
+                        
+                        
+                        // to prove that http://jira.openqa.org/browse/SEL-280 is in fact fixed, assign directly to onmouseup 
+                        // (instead of using the addAnEvent code below; this used to cause trouble because the global event var was not getting set)
+                        	divs[i].onmouseup = function(e){
+                        
+                        		if (e==null) e = event;	// only needed when we do direct assignment to onmouseup
+                                        
+				activeSlider=-1;
+				stopEvent(e);
+			};
+			//addAnEvent(divs[i], 'mouseup', function(e){
+			//	activeSlider=-1;
+			//	stopEvent(e);
+			//});
+                        
+                        
+                        
+                        
+                        
+                        
+                        
+                        
+                        
+                        
+                        
+                        
+		}
+	}
+}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+function stopEvent(event) {
+	if (event.preventDefault) {
+		event.preventDefault();
+		event.stopPropagation();
+	} else {
+		event.returnValue=false;
+		event.cancelBubble=true;
+	}
+}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+addAnEvent(window, 'load', attachSliderEvents);
+
+
+
+addAnEvent(document, 'mousemove', sliderMouseMove);
+//document.onmousemove = sliderMouseMove;
+
+
+
+var activeSlider=-1;
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_async_event.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_async_event.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_async_event.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,52 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<form name="theForm">
+  <input id="theField" value="oldValue"/>
+  <br>
+  <input type="button" name="theButton" value="Delayed Field Change" onclick="javascript: changeFieldValue();"/>
+  <br>
+  <br>
+  <span id="theSpan">Some text</span>
+  <br>
+  <br>
+  <input type="button" name="theSpanButton" value="Delayed Text Change" onclick="javascript: changeSpanText();"/>
+  <br>
+  <br>
+  <input type="button" name="theAlertButton" value="Delayed Alert" onclick="javascript: delayedAlert();"/>
+</form>
+</body>
+
+<script language="javascript">
+<!--
+function changeFieldValue() {
+  setTimeout("document.theForm.theField.value = 'newValue'", 1500);
+}
+function changeSpanText() {
+  setTimeout("document.getElementById('theSpan').innerHTML = 'Some new text'", 1500);
+}
+function delayedAlert() {
+  setTimeout("alert('An alert')", 1500);
+}
+-->
+</script>
+
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_bottom.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_bottom.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_bottom.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,28 @@
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+	<title>Bottom frame</title>
+</head>
+<body>
+This is bottom frame
+
+<a href="test_page.slow.html" target="_top" id="changeTop">changeTop</a>
+<a href="test_page.slow.html" target="_parent" id="changeParent">changeParent</a>
+<a href="test_page.slow.html" target="_blank" id="changeBlank">changeBlank</a>
+<form action="test_page.slow.html" target="_blank" id="formBlank">formBlank</form>
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_check_uncheck.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_check_uncheck.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_check_uncheck.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,46 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+
+<h3>Test for management of check-box and radio-button inputs</h3>
+
+<form>
+
+<fieldset>
+<legend>Base</legend>
+<input type="radio" id="base-spud" name="base" value="spud" checked="yes"/> Baked Potato<br/>
+<input type="radio" id="base-rice" name="base" value="rice"/> Steamed Rice<br/>
+<input type="radio" id="base-tortilla" name="base" value="tortilla"/> Tortilla<br/>
+</fieldset>
+
+<fieldset>
+<legend>Options</legend>
+<input type="checkbox" id="option-beans" name="option" value="beans" checked="yes"/> Beans<br/>
+<input type="checkbox" id="option-butter" name="option" value="butter"/> Butter<br/>
+<input type="checkbox" id="option-cheese" name="option" value="cheese" checked="yes"/> Cheese<br/>
+<input type="checkbox" id="option-chilli" name="option" value="chilli"/> Chilli Flakes<br/>
+<input type="checkbox" id="option-onions" name="option" value="onions"/> Onions<br/>
+<input type="checkbox" id="option-sourcream" name="option" value="sourcream"/> Sour Cream<br/>
+</fieldset>
+
+</form>
+</body>
+<html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_javascript_page.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_javascript_page.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_javascript_page.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,36 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<title>Click Page 1</title>
+<script type="text/javascript">
+var foo = "foo";
+function navigateTo(url) {
+}
+</script>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<a id="link" href="javascript:alert('link clicked: ' + foo)">link</a><br>
+<a id="linkWithEnclosedImage" href="javascript:alert('enclosedImage clicked')"><img id="enclosedImage" src="./banner.gif"></a><br/>
+<a id="linkWithJavascriptVoidHref" href="javascript:void(0);" onclick="alert('onclick')">link with void href</a><br/>
+<a id="linkWithOnclickReturnsFalse" href="javascript:alert('linkWithOnclickReturnsFalse clicked')" onclick="return false;">link with onclick returns false</a><br/>
+<a id="linkWithMultipleJavascriptStatements" href="javascript:  alert('alert1'); alert('alert2') ; alert ('alert3')">link with multiple javascript calls</a><br/>
+
+</body>
+
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_page1.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_page1.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_page1.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,34 @@
+<html>
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<title>Click Page 1</title>
+<script type="text/javascript">
+</script>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<a id="link" href="./test_click_page2.html">Click here for next page</a><br>
+<a id="absoluteLink" href="/tests/html/test_click_page2.html">Click here for next page via absolute link</a><br>
+<a id="linkWithEnclosedImage" href="./test_click_page2.html"><img id="enclosedImage" src="./banner.gif"></a><br/>
+<a id="linkToAnchorOnThisPage" href="#link">link to other link</a><br/>
+<a id="linkWithOnclickReturnsFalse" href="./test_click_page2.html" onclick="return false;">link with onclick="return false"</a><br/>
+<a id="linkWithExtraEnclosedImage" href="./test_click_page2.html"><div><img id="extraEnclosedImage" src="./banner.gif"></div></a><br/>
+<button id="doubleClickable" ondblclick="alert('double clicked!');">double click me</button>
+</body>
+
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_page2.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_page2.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_click_page2.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,31 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<title>Click Page Target</title>
+<script type="text/javascript">
+</script>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+This is a test of the click command.
+
+<br />
+<br />
+Return to  <a id="previousPage" href="./test_click_page1.html">test_click_page1.html</a>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_confirm.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_confirm.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_confirm.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,34 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+  <script type="text/javascript">
+ 
+  function confirmLeaving() {
+    if (confirm("You are about to go to a dummy page.")) {
+        document.location = "test_dummy_page.html";
+    }
+ }
+ 
+  </script>
+  <title>Test Confirm</title>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<a id="confirmAndLeave" href="javascript:confirmLeaving();">click to navigate to a new page</a>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_dummy_page.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_dummy_page.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_dummy_page.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,27 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<title>Dummy Page</title>
+<script type="text/javascript">
+</script>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<span id="theSpan">This is a dummy page.</span>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_editable.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_editable.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_editable.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,37 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<title>Testing-ground for assertEditable and friends</title>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<form>
+<table>
+
+<tr><th>Editable text-field</th><td><input name="normal_text" value="black"></td></tr>
+<tr><th>Editable select</th><td><select name="normal_select"><option>black</option></select></td></tr>
+
+<tr><th>Disabled text-field</th><td><input name="disabled_text" value="black" disabled="yup"></td></tr>
+<tr><th>Disabled select</th><td><select name="disabled_select" disabled="yup"><option>black</option></select></td></tr>
+
+<tr><th>Faked read-only input</th><td><span id="fake_input" name="fake_input" style="border: 1px solid grey">black</span></td></tr>
+
+</table>
+</form>
+
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_element_order.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_element_order.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_element_order.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,43 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+    <META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+
+<body>
+<!-- this is a comment node -->
+<div id="d1">
+    <span id="s1.1">s1.1</span>
+    <span>s1.2</span>
+    <span>s1.3</span>
+    <span>s1.4</span>
+
+    <div id="d1.1">
+        <div id="d1.1.1">d1.1.1</div>
+
+        <div>d1.1.2</div>
+    </div>
+
+    <div id="d1.2">d1.2</div>
+
+    <div>d1.3</div>
+
+    <div>d1.4</div>
+</div>
+
+<div id="d2">d2</div>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_element_present.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_element_present.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_element_present.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,53 @@
+<html>
+<head>
+<title>test for element present</title>		
+
+<script type="text/Javascript">
+
+	function createLink(){
+		linkContainer().innerHTML = 
+		"<a id='aLink' href='about:blank'>here is a link</a>"
+	}			
+
+function removeTheLink(){				
+	var link = document.getElementById("aLink");
+	if(link) linkContainer().removeChild(link);
+}
+
+function linkContainer(){
+	return document.getElementById('linkContainer');
+}
+
+</script>
+
+</head>
+
+
+<body>
+
+
+<br>
+<div id="linkContainer">
+</div>
+<br>
+
+<button id="removeLinkAfterAWhile" onclick="javascript:
+	setTimeout('removeTheLink();',150)">
+	click to remove the link after a while
+</button>
+
+<br>
+
+<br>
+
+<button id="addLinkAfterAWhile" onclick="javascript:
+	setTimeout('createLink();',150)">
+	click to add the link back after a while
+</button>
+
+<script type="text/Javascript">
+	createLink();
+</script>
+
+</body>
+</html>	

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_fast_reloader.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_fast_reloader.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_fast_reloader.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,38 @@
+<html>
+<!--
+Copyright 2006 BEA, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+    <title>Reloading and incrementing counter</title>
+<script type="text/javascript">
+	function changeSpan() {
+		var span = document.getElementById("theSpan");
+                var i = span.innerHTML;
+                i++;
+                
+                if (i==2)
+			location.reload(true);
+                
+                span.innerHTML = i;
+		window.setTimeout("changeSpan();", 200);
+	}	
+        onload=changeSpan;
+</script>
+</head>
+<body>
+==<span id="theSpan">0</span>==
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_focus_on_blur.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_focus_on_blur.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_focus_on_blur.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,34 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<title>Testing-ground for assertEditable and friends</title>
+<script type="text/javascript">
+ function generateAnAlertAndFocus(inputElement) {
+     if (inputElement.value == "test") {
+        alert("Bad value");
+        inputElement.focus();
+     }
+ }
+</script>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<form>
+<input name="testInput" onblur="javascript:generateAnAlertAndFocus(this);" />
+</form>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_form_events.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_form_events.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_form_events.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,128 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<script language="javascript">
+<!--
+/*  flag to determine if event should be recorded. */
+var recording = true;
+var sawCtrlKeyDown = false;
+
+/* Records an event in the events textarea */
+function recordEvent(e) {
+    if (!recording) return;
+    
+    if (!e) e = window.event; // Handle IE
+    var eventMessage;
+    if (!e) {
+    	eventMessage = "{ No window.event is defined, which is expected that if this is a selenium-generated mouse event created by createEventObject under IE }";
+    }
+    else {
+    	eventMessage = '{' + e.type + '(' + this.id;
+    	if (e.keyCode) {
+	      eventMessage += ' - ' + e.keyCode;
+            }
+    	if (e.altKey) {
+	      eventMessage += ' altKeyDown';
+            }
+    	if (e.ctrlKey) {
+	      eventMessage += ' ctrlKeyDown';
+            }
+            sawCtrlKeyDown = e.ctrlKey;
+
+    	if (e.shiftKey) {
+	      eventMessage += ' shiftKeyDown';
+            }            
+    	if (e.metaKey) {
+	      eventMessage += ' metaKeyDown';
+            }            
+        eventMessage += ')} ';
+    }
+    log(eventMessage);
+    return true;
+}
+
+/** Attach recording event handlers to the element */
+function attachEventRecorders(elementId) {
+    var element = document.getElementById(elementId);
+    element.onfocus=recordEvent;
+    element.onchange=recordEvent;
+    element.onclick=recordEvent;
+    element.onselect=recordEvent;
+    element.onblur=recordEvent;
+    element.onmouseover=recordEvent;
+    element.onmousedown=recordEvent;
+    element.onkeydown=recordEvent;
+    element.onkeypress=recordEvent;
+    element.onkeyup=recordEvent;
+}
+
+function setupEventRecorders() {
+    attachEventRecorders('theTextbox');
+    attachEventRecorders('theCheckbox');
+    attachEventRecorders('theRadio1');
+    attachEventRecorders('theRadio2');
+    attachEventRecorders('theSelect');
+    attachEventRecorders('theButton');
+    attachEventRecorders('theSubmit');
+    attachEventRecorders('theLink');
+    document.theForm.onsubmit = function() {
+        log('{submit} ');
+        return false;
+    }
+}
+
+// This functionality is only provided for manual clickety-click assessing of events.
+// Not used in automated testing
+function setupFormEventRecorders(enable) {
+    var recorder = enable ? recordEvent : null;
+    var formElement = document.forms[0];
+    formElement.onfocus=recorder;
+    formElement.onchange=recorder;
+    formElement.onclick=recorder;
+    formElement.onselect=recorder;
+    formElement.onblur=recorder;
+}
+
+function log(message) {
+    document.getElementById('eventlog').value += message;
+}
+
+-->
+</script>
+</head>
+<body id="theBody" onload="setupEventRecorders();">
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<form id="theForm" name="theForm" onsubmit="return false;">
+<input name="theTextbox" id="theTextbox" type="text" value=""/>
+<input name="theCheckbox" id="theCheckbox" type="checkbox"/>
+<input name="theRadio" id="theRadio1" type="radio" value="radio1"/><input name="theRadio" id="theRadio2" type="radio" value="theRadio"/>
+<select name="theSelect" id="theSelect"><option value="">Empty Option</option><option value="option1">First Option</option><option value="option2">Second Option</option></select>
+<input name="theButton" id="theButton" type="button" value="A Button"/>
+<input name="theSubmit" id="theSubmit" type="submit" value="The Submit"/>
+</form>
+<a id="theLink" href="javascript:alert('link clicked');">The Link</a>
+
+<hr/>
+
+<div>EventLog<br/>
+<textarea id="eventlog" rows="15" cols="80"></textarea><br/>
+Record events:<input type="checkbox" checked="checked" onclick="recording=this.checked;"/><br/>
+Record form events:<input type="checkbox" onclick="setupFormEventRecorders(this.checked);"/><br/>
+</div>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_framed_page.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_framed_page.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_framed_page.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,25 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<iframe name="theFrame" id="theFrame" width="220px;" height="20%" border="0" src="./test_click_page1.html"></iframe>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_funk_event_handling_shouldnt_go_here.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_funk_event_handling_shouldnt_go_here.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_funk_event_handling_shouldnt_go_here.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,7 @@
+<html>
+<head>
+    <body>
+    You shouldn't be here!
+    </body>
+</head>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_funky_event_handling.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_funky_event_handling.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_funky_event_handling.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,33 @@
+<html>
+<head>
+   <script type="text/javascript">
+       function onLoadFunc() {
+           var parent = document.getElementById("clickMeParent");
+           var handler = function(evt) {
+               if (evt.preventDefault) {
+                   evt.preventDefault();
+                   evt.stopPropagation();
+                } else {
+                    evt.returnValue = false;
+                    evt.cancelBubble = true;
+                }
+           };
+           
+           if (window.addEventListener && !window.opera) {
+                parent.addEventListener("click", handler, false);
+            } else if (window.attachEvent) {
+                parent.attachEvent("onclick", handler);
+            }
+
+       }
+   </script>
+</head>
+
+<body onload="onLoadFunc()">
+
+<div id="clickMeParent">
+    <a id="clickMe" href="test_funk_event_handling_shouldnt_go_here.html">Click Me</a>
+</div>
+
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_html_source.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_html_source.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_html_source.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,13 @@
+<!--
+  Test getHtmlSource Command
+  
+  Robert Zimmermann - 2006-10-25
+-->
+<html>
+<head>
+	<title>HTML Source</title>
+</head>
+<body>
+Text is here
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_i18n.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_i18n.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_i18n.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,53 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+This is a test of internationalization (i18n).
+
+<ul><li>Romance: <span id="romance">&#x00FC;&#x00F6;&#x00E4;&#x00DC;&#x00D6;&#x00C4; &#x00E7;&#x00E8;&#x00E9; &#x00BF;&#x00F1; &#x00E8;&#x00E0;&#x00F9;&#x00F2;</span>
+<ul>
+<li>HTML encoded: &amp;#x00FC;&amp;#x00F6;&amp;#x00E4;&amp;#x00DC;&amp;#x00D6;&amp;#x00C4; &amp;#x00E7;&amp;#x00E8;&amp;#x00E9; &amp;#x00BF;&amp;#x00F1; &amp;#x00E8;&amp;#x00E0;&amp;#x00F9;&amp;#x00F2;
+<li>URI encoded (UTF-8): %C3%BC%C3%B6%C3%A4%C3%9C%C3%96%C3%84+%C3%A7%C3%A8%C3%A9+%C2%BF%C3%B1+%C3%A8%C3%A0%C3%B9%C3%B2
+</ul>
+</li>
+<li>Korean: <span id="korean">&#xC5F4;&#xC5D0;</span>
+<ul>
+<li>HTML encoded: &amp;#xC5F4;&amp;#xC5D0;
+<li>URI encoded (UTF-8): %EC%97%B4%EC%97%90
+</ul>
+</li>
+<li>Chinese: <span id="chinese">&#x4E2D;&#x6587;</span>
+<ul>
+<li>HTML encoded: &amp;#x4E2D;&amp;#x6587;
+<li>URI encoded (UTF-8): %E4%B8%AD%E6%96%87
+</ul>
+</li>
+<li>Japanese: <span id="japanese">&#x307E;&#x3077;</span>
+<ul>
+<li>HTML encoded: &amp;#x307E;&amp;#x3077;
+<li>URI encoded (UTF-8): %E3%81%BE%E3%81%B7
+</ul>
+</li>
+<li>Dangerous: <span id="dangerous">&amp;%?\+|,%*</span>
+</li>
+<li>Dangerous Link IDs: <a id="veni, vidi, vici">veni, vidi, vici</a>, <a id="c:\foo\bar">c:\foo\bar</a>, <a id="c:\I came, I \saw\, I conquered">c:\I came, I \saw\, I conquered</a>
+</li>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_incrementing_counter.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_incrementing_counter.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_incrementing_counter.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,38 @@
+<html>
+<!--
+Copyright 2006 BEA, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+    <title>Reloading and incrementing counter</title>
+<script type="text/javascript">
+	function changeSpan() {
+		var span = document.getElementById("theSpan");
+                var i = span.innerHTML;
+                i++;
+                
+                if (i==3)
+			location.reload(true);
+                
+                span.innerHTML = i;
+		window.setTimeout("changeSpan();", 500);
+	}	
+        onload=changeSpan;
+</script>
+</head>
+<body>
+==<span id="theSpan">0</span>==
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_javascript_attributes.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_javascript_attributes.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_javascript_attributes.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+
+<body>
+
+<a href="#" onclick="alert('foo')">ALERT FOO</a>
+
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_just_text.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_just_text.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_just_text.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,23 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head>
+<body>
+This is the entire text of the page.
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_locators.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_locators.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_locators.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,94 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<body lang="en">
+    <p>this is the first element in the document</p>
+    <img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+    <a id="id1" href="#id1" class="a1">this is the first element</a>
+    <!-- DO NOT DELETE THIS COMMENT: see SEL-68 -->
+    <a id="id2" name="name1" href="#id2" class="a2" alt="foo">this is the&nbsp;<b>second</b> <span selenium:foo="bar">element</span></a>
+    <a id="id3" name="name1" href="#id3" class="a3">this is the third element</a>
+    <a id="foo:bar" href="#id3" class="a4">this is the fourth element</a>
+    <a class="class1 class2 class3">this is the fifth element</a>
+    <a lang="en-cn-something">this is the sixth element</a>
+	<input type="radio" name="name2" value="yes">	<input type="radio" name="name2" value="no">
+    
+    <table class="stylee">
+        <tr>
+            <th>theHeaderText</th>
+            <td>theCellText</td>
+        </tr>
+    </table>
+
+    <div id="linkPseudoTest"><a href="#">link pseudo test</a></div>
+    <div id="combinatorTest">this is the parent. <span id="firstChild">this is a child <a>and grandson</a></span>, <span>another child</span>, <span>last child<span></span></span></div>
+
+    <div id="css3test">
+        <a name="foobar">foobar</a>
+        <a name="barfoo">barfoo</a>
+        <a name="foozoobar">foozoobar</a>
+    </div>
+
+    <div id="onlyChild"><span>only child</span></div>
+    <span></span>
+
+    <input type="text" name="enabled" value="enabled">
+    <input type="text" name="disabled" value="disabled" disabled="true">
+    <input type="checkbox" name="checked" value="checked" checked="true">
+
+    <div id="structuralPseudo">
+        <span>span1</span>
+        <span>span2</span>
+        <span>span3</span>
+        <span>span4</span>
+        <div>div1</div>
+        <div>div2</div>
+        <div>div3</div>
+        <div>div4</div>
+    </div>
+
+    <div id="nested1">
+        <div id="nested2">
+            <input id="nested3a" type="button" value="nested3a">
+            <input id="nested3b" type="button" value="nested3b">
+        </div>
+    </div>
+    
+    <div id="anotherNested">
+        <span>
+            <div id="usefulStuff">
+                <span>
+                    <input value="winner"></input>
+                </span>
+            </div>  
+        </span>
+    </div>
+
+    <div id="targetTest">
+        <span name="target">target</span>
+        <a href="#target">referreing URI</a>
+    </div>
+
+    <a id="jshref" href="javascript:doFoo('a', 'b')">javascript href with spaces</a>
+
+    <div id="buttons">
+        <button>left</button> <button>right</button>
+    </div>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_locators.xhtml
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_locators.xhtml	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_locators.xhtml	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,23 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<!--<meta http-equiv="Content-Type" content="application/xhtml+xhm"  />-->
+</head>
+<body>
+<h1>Test XHTML Page</h1>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_modal_dialog.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_modal_dialog.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_modal_dialog.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,32 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<title>Modal Dialog Host Window</title>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<script type="text/javascript">
+function openModal() {
+    myModalDialogReturnValue = window.showModalDialog("/selenium-server/tests/html/test_modal_dialog_dialog.html", "","" );
+    document.getElementById('changeText').innerHTML = "after modal dialog";
+}
+</script>
+</head>
+<body>
+    <img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+    <button id="modal" onclick="javascript:openModal()">Click here to open a modal dialog</button>
+	<div id="changeText">before modal dialog</div>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_modal_dialog_dialog.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_modal_dialog_dialog.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_modal_dialog_dialog.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,36 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<script type="text/javascript">
+    window.returnValue="ted";
+
+function closeWindow() {
+    window.returnValue = "no ways";
+    window.close();
+}
+
+</script>
+<title>Modal Dialog Popup</title>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<a id="change" onClick="javascript:window.returnValue='bill';">Click to change</a>
+<p/>
+<a id="close" onClick="javascript:closeWindow();">Click to close</a>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_multi_level_frame.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_multi_level_frame.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_multi_level_frame.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,6 @@
+<html>
+<frameset cols="20%, 80%">
+		<frame src="./test_click_page2.html" id="frame1" name="frame1">
+		<frame src="./test_framed_page.html" name="frame2">
+</frameset>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_multiselect.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_multiselect.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_multiselect.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,38 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<h3>Test for selecting options from a Selection.</h3>
+<br>
+<form name="myform">
+      <select name="theSelect" id="theSelect" multiple="multiple">
+        <option value="option1" id="o1">First Option</option>
+        <option value="option2" id="o2" selected="true">Second Option</option>
+        <option value="option3" id="o3">Third Option</option>
+        <option value="option4" id="o4">Fourth Option</option>
+        <option value="option5" id="o4">Fifth Option</option>
+        <option value="option6" id="o6">Sixth Option</option>
+        <option value="" id="o7">Empty Value Option</option>
+        <option value="option8" id="o8"></option>
+      </select>
+      <br>
+      <input name="theInput">
+</form>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_open.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_open.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_open.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,24 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head><title>Open Test</title>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+This is a test of the open command.
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_open_in_target_frame.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_open_in_target_frame.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_open_in_target_frame.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,14 @@
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html">
+<title>frame-1</title>
+</head>
+<frameset rows="*" cols="260,*" frameborder="NO" border="1" framespacing="0">
+  <frame src="frame-left.html" name="leftFrame" scrolling="NO" noresize>
+  <frame src="frame-right.html" name="rightFrame">
+</frameset>
+
+<body>
+Multiple frames test
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_page.slow.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_page.slow.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_page.slow.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,39 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<title>Slow Loading Page</title>
+<script type="text/javascript">
+	function changeSpan() {
+		var span = document.getElementById("theSpan");
+		span.innerHTML = "Changed the text";
+	}
+
+	function slowRefresh() {
+		window.setTimeout("window.location.reload(true);", 2000);
+	}
+</script>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<a name="linktarget"></a><span id="theSpan">This is a slow-loading page.</span>
+<a id="slowPage_reload" href="./test_page.slow.html">Click to reload</a>
+<button id="changeSpan" onclick="changeSpan();">change span</button>
+<button id="slowRefresh" onclick="slowRefresh();">slow refresh</button>
+<p><a id="anchor" href="#linktarget">anchor</a></p>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_prompt.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_prompt.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_prompt.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,34 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+  <script type="text/javascript">
+
+  function confirmLeaving() {
+    if (prompt("Type 'yes' and click OK") == 'yes') {
+        document.location = "test_dummy_page.html";
+    }
+ }
+
+  </script>
+  <title>Test Prompt</title>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<a id="promptAndLeave" href="javascript:confirmLeaving();">click to navigate to a new page</a>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_reload_onchange_page.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_reload_onchange_page.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_reload_onchange_page.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,33 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<title>Reload Page</title>
+<script type="text/javascript">
+</script>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+Selecting an option or changing text causes page to reload.
+<a id="theLink" href="./test_page.slow.html">Click here</a>
+<form action="./test_page.slow.html">
+<select name="theSelect" id="theSelect" onchange="this.form.submit();"><option value="">Empty Option</option><option value="option1">First Option</option><option value="option2">Second Option</option></select>
+<input name="theTextbox" id="theTextbox" onblur="this.form.submit();" value="initialValue"/>
+<input type="submit" name="theSubmit" id="theSubmit" value="submit"/>
+</form>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_reloader.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_reloader.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_reloader.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,34 @@
+<html>
+<!--
+Copyright 2006 BEA, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+    <title>Reloading and incrementing counter</title>
+<script type="text/javascript">
+	function changeSpan() {
+		var span = document.getElementById("theSpan");
+                var i = span.innerHTML;
+                i++;
+                span.innerHTML = i;
+		window.setTimeout("changeSpan();", 2000);
+	}	
+        onload=changeSpan;
+</script>
+</head>
+<body>
+==<span id="theSpan">0</span>==
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_rewrite_doc_from_js.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_rewrite_doc_from_js.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_rewrite_doc_from_js.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,30 @@
+<html>
+<!--
+Copyright 2006 BEA, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+    <title>Reloading and incrementing counter</title>
+<script type="text/javascript">
+	function reloadIt() {
+	        document.write("<html>version 4</html>");
+	        document.close();
+ 	}	
+        window.setTimeout("reloadIt();", 1000);
+</script>
+</head>
+<body>version 3
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_rich_text.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_rich_text.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_rich_text.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,11 @@
+<html>
+<head><title>rich text</title></head>
+<script>
+function start() {
+    window.frames["richtext"].document.designMode="on";
+}
+</script>
+<body onload="start();">
+    <iframe id="richtext" name="richtext"/>
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,39 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<h3>Test for selecting options from a Selection.</h3>
+<br>
+<form name="myform">
+      <select name="theSelect" id="theSelect">
+        <option value="option1" id="o1">First Option</option>
+        <option value="option2" id="o2" selected="true">Second Option</option>
+        <option value="option3" id="o3">Third Option</option>
+        <option value="option4" id="o4">Fourth Option</option>
+        <option value="option5" id="o4">Fifth Option</option>
+        <option value="option6" id="o6">Sixth Option</option>
+        <option value="" id="o7">Empty Value Option</option>
+        <option value="option8" id="o8"></option>
+      </select>
+      <br>
+      <input name="theInput">
+</form>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select_window.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select_window.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select_window.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,31 @@
+<html>
+	<!--
+	Copyright 2006 ThoughtWorks, Inc
+	
+	 Licensed under the Apache License, Version 2.0 (the "License");
+	 you may not use this file except in compliance with the License.
+	 You may obtain a copy of the License at
+	
+		 http://www.apache.org/licenses/LICENSE-2.0
+	
+	 Unless required by applicable law or agreed to in writing, software
+	 distributed under the License is distributed on an "AS IS" BASIS,
+	 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+	 See the License for the specific language governing permissions and
+	 limitations under the License.
+	-->
+	<head>
+		<title>Select Window Base</title>
+		<script type="text/javascript">
+			function openWindow() {
+				myPopupWindow = window.open('./test_select_window_popup.html', 'myPopupWindow', 'height=200,width=500,top=400,left=50');
+			}
+		</script>
+	</head>
+	<body>
+		<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif" /><br />
+		<button id="popupPage" onclick="openWindow();">Click here to open a popup window</button>
+		<button id="popupAnonymous" onclick="javascript:window.open('./test_select_window_popup.html', 'anonymouspopup', 'height=200,width=500,top=400,left=50');">Click here to open an anonymous popup window</button>
+		<button id="popupBlank" onclick="javascript:window.open('./test_select_window_popup.html', '_blank', 'height=200,width=500,top=400,left=50');">Click here to open a popup window into a _blank window</button>
+	</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select_window_popup.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select_window_popup.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_select_window_popup.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,29 @@
+<html>
+	<!--
+	Copyright 2006 ThoughtWorks, Inc
+	
+	 Licensed under the Apache License, Version 2.0 (the "License");
+	 you may not use this file except in compliance with the License.
+	 You may obtain a copy of the License at
+	
+		 http://www.apache.org/licenses/LICENSE-2.0
+	
+	 Unless required by applicable law or agreed to in writing, software
+	 distributed under the License is distributed on an "AS IS" BASIS,
+	 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+	 See the License for the specific language governing permissions and
+	 limitations under the License.
+	-->
+	<head>
+		<title>Select Window Popup</title>
+		<script type="text/javascript">
+		</script>
+	</head>
+	<body>
+		<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif" /><br />
+		<a href="./test_select_window_popup.html">Click to reload</a>
+		<a href="./test_reload_onchange_page.html">Click to load new page</a>
+		<a id="closePage" href="javascript:window.close()">Click to close</a>
+		<input type="button" id="closePage2" onclick="window.close();" value="Alternate click to close" />
+	</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_slow_reloader.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_slow_reloader.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_slow_reloader.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,38 @@
+<html>
+<!--
+Copyright 2006 BEA, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+    <title>Reloading and incrementing counter</title>
+<script type="text/javascript">
+	function changeSpan() {
+		var span = document.getElementById("theSpan");
+                var i = span.innerHTML;
+                i++;
+                
+                if (i==3)
+			location.reload(true);
+                
+                span.innerHTML = i;
+		window.setTimeout("changeSpan();", 1000);
+	}	
+        onload=changeSpan;
+</script>
+</head>
+<body>
+==<span id="theSpan">0</span>==
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_store_value.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_store_value.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_store_value.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,26 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<form>
+<input name="theText" value=""/>
+</form>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_submit.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_submit.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_submit.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,32 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head>
+<body
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<h3>Test for submit command</h3>
+<form id="searchForm" method="GET" action="javascript:alert('form submitted')"
+      onsubmit="javascript:alert('onsubmit called'); return document.forms[0].elements.okayToSubmit.checked;">
+search: <input name="search" type="text">
+<br />
+OK to submit? <input name="okayToSubmit" type="checkbox" />
+<br />
+<input type="submit" />
+</form>
+</body>
+<html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_text_content.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_text_content.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_text_content.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,74 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+	<title>theTitle</title>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<div id="spaces">
+<kbd>1 space|2  space|3   space|1&nbsp;nbsp|2&nbsp;&nbsp;nbsp|3&nbsp;&nbsp;&nbsp;nbsp|2 &nbsp;space_nbsp|2&nbsp; nbsp_space|3 &nbsp; space_nbsp_space|3&nbsp; &nbsp;nbsp_space_nbsp</kbd>
+</div>
+<div>
+<kbd>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</kbd>
+</div>
+<hr/>
+<div id="tabcharacter">
+<kbd>	tab		character			between</kbd>
+</div>
+<hr/>
+<div id="nonTextMarkup">
+There is <span>non-visible</span> and <a href="javascript:void()"><strong><em>visible</em></strong></a> markup here that <!-- comment -->doesn't change the text content
+</div>
+<hr/>
+<div id="nonVisibleNewlines">
+non
+visible
+newlines
+between
+</div>
+<hr/>
+<div id="visibleNewlines">
+visible<br/>newlines<br/>between
+</div>
+<hr/>
+<div id="paragraphs">
+<p>First paragraph</p><p>Second paragraph</p>
+</div>
+<hr/>
+<div id="preformatted">
+<pre>preformatted
+newline</pre>
+</div>
+<hr/>
+<div id="mixedMarkup">
+visible<br/>
+newlines <b>and markup</b> and non-visible
+newlines and <span id="foo">markup</span>
+With
+<p>
+  a paragraph
+</p>
+and
+<pre>
+pre
+formatted
+text
+</pre>
+</div>
+</body>
+ </html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_top.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_top.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_top.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,23 @@
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+	<title>Top frame</title>
+</head>
+<body>
+This is top frame
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_type_page1.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_type_page1.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_type_page1.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,40 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<h3>Test for typing values into form text fields.</h3>
+<br>
+<form METHOD=GET action="./test_type_page2.html">
+username: <input name="username"
+                 type="text" maxlength="10">
+<br />
+<br />
+password: <input name="password"
+                 type="password" maxlength="20">
+<br />
+<br />
+<input id="submitButton"
+
+       type="submit"
+       value="Log in" >
+
+</form>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_type_page2.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_type_page2.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_type_page2.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,62 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<script type="text/javascript">
+<!--
+    // This JavaScript will dump all the parameters in the URL. Useful for checking forms.
+    // if you are are not using a webserver.
+
+    // paramsMap is an associative array (like a Java map, Perl hash, or Python dictionary).
+    var paramsMap = new Array();
+
+    var query=this.location.search.substring(1);
+    if (query.length > 0){
+        var params=query.split("&");
+        for (var i=0 ; i<params.length ; i++){
+            var pos = params[i].indexOf("=");
+            var name = params[i].substring(0, pos);
+            var value = params[i].substring(pos + 1);
+            paramsMap[name] = value;
+        }
+    }
+// -->
+</script>
+
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<h2>
+    <script type="text/javascript">
+    <!--
+    message = "Welcome, " + paramsMap['username'] + "!";
+    document.writeln(message);
+    // -->
+    </script>
+</h2>
+<br>
+
+Here are the values you entered in <a href="./test_type_page1.html">test_type_page1.html</a>:
+<br />
+<script type="text/javascript">
+<!--
+    document.writeln("username&nbsp;:&nbsp;" + paramsMap['username'] + "<br \/>");
+    document.writeln("password&nbsp;:&nbsp;" + paramsMap['password'] + "<br \/>");
+// -->
+</script>
+</body>
+<html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_verifications.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_verifications.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_verifications.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,63 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+	<title>theTitle</title>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<span id="theSpan">this is the&nbsp;span</span>
+<form>
+<input name="theText" value="the text value" class="foo"/>
+<input type="hidden" name="theHidden" value="the hidden value">
+<select id="theSelect">
+    <option value="option1" id="o1">first option</option>
+    <option value="option2" id="o2" selected="selected">second option</option>
+    <option value="option3" id="o3">third,,option</option>
+</select>
+<textarea name="theTextarea">
+Line 1
+Line 2
+</textarea>
+</form>
+<table id="theTable">
+    <thead>
+        <tr>
+            <th>th1</th>
+            <th>th2</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td>a</td>
+            <td>b</td>
+        </tr>
+        <tr>
+            <td>c</td>
+            <td>d</td>
+        </tr>
+    </tbody>
+    <tfoot>
+        <tr>
+            <th>f1</th>
+            <th>f2</th>
+        </tr>
+    </tfoot>
+</table>
+
+</body>
+ </html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_verify_alert.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_verify_alert.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_verify_alert.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,47 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<script type="text/javascript">
+
+ function generateAnAlert() {
+    alert("Store Below 494 degrees K!");
+ }
+ 
+ function generateTwoAlerts() {
+    alert("Store Below 220 degrees C!");
+    alert("Store Below 429 degrees F!");
+ }
+ 
+  function generateAnAlertBeforeLeaving() {
+    alert("I'm Melting! I'm Melting!");
+    document.location = "test_dummy_page.html";
+ }
+ 
+
+</script>
+</head>
+<body>
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+<button id="oneAlert" onclick="generateAnAlert();"> click to generate one alert</button>
+<br>
+<button id="twoAlerts" onclick="generateTwoAlerts();"> click to generate two alerts</button>
+<br>
+<button id="alertAndLeave" onclick="generateAnAlertBeforeLeaving();"> click to generate an alert before navigating to new page</button>
+</body>
+
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_visibility.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_visibility.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/html/test_visibility.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,64 @@
+<html>
+<!--
+Copyright 2006 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<head>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<style type="text/css">
+.suppressed {
+    display: none;
+}
+</style>
+</head>
+<body>
+    
+    
+    
+    <!-- pardon me, need this as a test case to fool PI mode into inappropriate injection.  This
+    won't affect the usual test: -->
+    <script language="JavaScript" src="../proxy_injection_meta_equiv_test.js">
+    </script>
+    
+    
+    
+<img style="width: 644px; height: 41px;" alt="banner" src="banner.gif"><br>
+
+<p id="visibleParagraph">A visible paragraph</p>
+
+<p><i>Note: much of this page is hidden, in one way or another</i></p>
+
+<p id="hiddenParagraph" style="visibility: hidden">A paragraph hidden using CSS visibility=hidden</p>
+
+<p id="suppressedParagraph" style="display: none">A paragraph suppressed using CSS display=none</p>
+
+<p style="visibility: hidden">A <b id="hiddenSubElement">sub-element</b> hidden using CSS visibility=hidden</p>
+
+<p style="visibility: hidden">A <b id="visibleSubElement" style="visibility: visible">sub-element that is explicitly visible</b> using CSS visibility=visible</p>
+
+<p style="display: none">A <b id="suppressedSubElement">sub-element</b> suppressed using CSS display=none</p>
+
+<p id="jsHiddenParagraph">A paragraph hidden using CSS visibility=hidden, applied by JavaScript</p>
+
+<p id="classSuppressedParagraph" class="suppressed">A paragraph suppressed via CSS class</p>
+
+<p id="jsClassSuppressedParagraph">A paragraph suppressed via CSS class, applied by JavaScript</p>
+
+<script type="text/javascript">
+document.getElementById('jsHiddenParagraph').style.visibility = "hidden";
+document.getElementById('jsClassSuppressedParagraph').className = "suppressed";
+</script>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/proxy_injection_meta_equiv_test.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/proxy_injection_meta_equiv_test.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/tests/proxy_injection_meta_equiv_test.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,5 @@
+// this fragment was making PI mode inject.  Oops -- should only inject HTML containing 66
+//#DWR-START#
+var s1="<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN \" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-"; var s2="1\" />\n  <title>Insert</title>\n</head>\n<body>n<p><strong>DWR tests passed</strong></p>\n\n</body>\n</html>\n";
+var s0=s1+s2;
+//#DWR-END#

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/alert-handling-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/alert-handling-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/alert-handling-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+    <script language="JavaScript" type="text/javascript">
+
+        function setUp() {
+            $("body").innerHTML = "<iframe NAME='frame' ID='frame' SRC='about:blank'></iframe>"
+            var alertTestFrame = document.getElementById("frame");
+            alertTestWindow = alertTestFrame.contentWindow;
+            alertTestBrowserBot = BrowserBot.createForWindow(alertTestWindow);
+            alertTestBrowserBot.getCurrentPage();
+        }
+
+        function testShouldNotReportAnyAlertsIfNoneHaveBeenGenerated() {
+            assertFalse(alertTestBrowserBot.hasAlerts());
+            assertUndefined(alertTestBrowserBot.getNextAlert());
+        }
+
+        function testShouldReportMultipleAlertsInOrderIfGenerated() {
+            alertTestWindow.alert("Warning: unfunny joke ahead");
+            alertTestWindow.alert("Be Alert, We need more Lerts");
+
+            assertTrue(alertTestBrowserBot.hasAlerts());
+            assertEquals("Warning: unfunny joke ahead", alertTestBrowserBot.getNextAlert());
+            assertTrue(alertTestBrowserBot.hasAlerts());
+            assertEquals("Be Alert, We need more Lerts", alertTestBrowserBot.getNextAlert());
+            assertFalse(alertTestBrowserBot.hasAlerts());
+        }
+
+        function testShouldRemoveAlertWhenItIsRetreived() {
+            alertTestWindow.alert("Be Alert, Not Alarmed");
+
+            assertTrue(alertTestBrowserBot.hasAlerts());
+            assertNotUndefined(alertTestBrowserBot.getNextAlert());
+            assertFalse(alertTestBrowserBot.hasAlerts());
+            assertUndefined(alertTestBrowserBot.getNextAlert());
+        }
+
+
+        function testShouldReportSingleAlertIfGenerated() {
+            alertTestWindow.alert("Be Alert, Not Alarmed");
+            assertTrue(alertTestBrowserBot.hasAlerts());
+            assertEquals("Be Alert, Not Alarmed", alertTestBrowserBot.getNextAlert());
+        }
+
+
+    </script>
+</head>
+
+<body id="body">
+<h1>Selenium PageBot Alert Handling Tests</h1>
+
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/assert-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/assert-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/assert-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Assert tests</title>
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-api.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function testAssertEqalsDoesntThrowExceptionWhenMatches() {
+	Assert.equals("foo", "foo");
+}
+
+function testAssertEqualsThrowsExceptionWhenNotMatches() {
+	try {
+		Assert.equals("foo", "fox");
+	}
+	catch (e) {		
+		assertEquals("Expected 'foo' but was 'fox'", e.failureMessage);
+		return;
+	}
+	fail("Should have thrown exception");
+}
+
+function testAssertEqualsCanIncludeAComment() {
+	try {
+		Assert.equals("testComment", "foo", "fox");
+	}
+	catch (e) {		
+		assertEquals("testComment; Expected 'foo' but was 'fox'", e.failureMessage);
+		return;
+	}
+	fail("Should have thrown exception");
+}
+
+function testAssertMatchesDoesntThrowExceptionWhenMatches() {
+	Assert.matches("regexp:fo[aeiou]", "foo");
+}
+
+function testAssertMatchesThrowsExceptionWhenNotMatches() {
+	try {
+		Assert.matches("regexp:fo[aei]", "foo");
+	}
+	catch (e) {		
+		assertEquals("Actual value 'foo' did not match 'regexp:fo[aei]'", e.failureMessage);
+		return;
+	}
+	fail("Should have thrown exception");
+}
+
+function testPatternMatchesCanIncludeComment() {
+	try {
+		Assert.matches("TestComment", "regexp:fo[aei]", "foo");
+		assertEquals("TestComment; Actual value 'foo' did not match 'regexp:fo[aei]'", e.failureMessage);
+	}
+	catch (e) {
+		return;
+	}
+	fail("Should have thrown exception");
+}
+
+function testAssertNotMatchesDoesntThrowExceptionWhenNotMatches() {
+	Assert.notMatches("regexp:fo[aeiou]", "fox");
+}
+
+function testAssertNotMatchesThrowsExceptionWhenMatches() {
+	try {
+		Assert.notMatches("regexp:fo[aeix]", "fox");
+	}
+	catch (e) {
+		assertEquals("Actual value 'fox' did match 'regexp:fo[aeix]'", e.failureMessage);
+		return;
+	}
+	fail("Should have thrown exception");
+}
+
+function testAssertNotMatchesCanIncludeComment() {
+	try {
+		Assert.notMatches("TestComment", "regexp:fo[aeix]", "fox");
+	}
+	catch (e) {
+		assertEquals("TestComment; Actual value 'fox' did match 'regexp:fo[aeix]'", e.failureMessage);
+		return;
+	}
+	fail("Should have thrown exception");
+}
+
+</script>
+  </head>
+  <body>Assert Tests</body>
+</html>s

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/browserbot-frame-finder-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/browserbot-frame-finder-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/browserbot-frame-finder-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>JsUnit Utility Tests</title>
+<link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function setUp() {
+    browserbot = BrowserBot.createForWindow(window);
+}
+
+    function testShouldAbleToGetFirstLevelFrameAccordingNameGiven(){
+        var frame = browserbot._getFrameFromGlobal("testIframe-name");
+        assertEquals("testIframe-name", frame.name)
+    }
+
+
+</script>
+</head>
+
+<body>
+<iframe name="testIframe-name" src="./pagebot-locator-tests-include.html"/>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/browserbot-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/browserbot-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/browserbot-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>BrowserBot tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-logging.js"></script>
+<script language="JavaScript" type="text/javascript">
+function setUp() {
+    mockWindow = new String('mockWindow');
+    mockWindow.closed = false;
+    browserBot = new MozillaBrowserBot(mockWindow);
+
+    browserBot.currentWindowName = 'originalWindowName';
+    browserBot.currentPage = 'originalPage';
+}
+
+function testCurrentWindowNameIsSetToNullWhenSelectWindowIsCalledWithNull() {
+    browserBot.selectWindow('null');
+
+    assertNull(browserBot.currentWindowName);
+}
+
+function testCurrentWindowIsSetWhenSelectWindowIsCalledWithNonNull() {
+    // Make sure new window can be found
+    mockWindow.windowName = new String('anotherWindow');
+
+    browserBot.selectWindow('windowName');
+
+    assertEquals('windowName', browserBot.currentWindowName);
+}
+
+function testExceptionWhenSelectWindowIsCalledWithUnknownWindowName() {
+    try {
+        browserBot.selectWindow('notAwindow');
+        fail("Should have thrown exception");
+    }
+    catch (e) {
+        assertEquals("Could not find window with title notAwindow", e.message);
+    }
+}
+
+function testFrameSrcIsSetOnOpenLocationWhenThereIsNoCurrentWindow() {
+    browserBot.currentWindowName = null;
+    var mockLocation = {
+        href:""
+    };
+    mockWindow.location = mockLocation;
+    var mockDocument = new Object();
+    mockWindow.document = mockDocument;
+    mockWindow.document.location = mockLocation;
+    browserBot.baseUrl = "http://x";
+    browserBot.openLocation('myNewLocation');
+
+    assertEquals('http://x/myNewLocation', mockWindow.location.href);
+}
+
+function testCurrentPageIsLazyCreatedBasedOnContentWindowWhenCurrentWindowNameIsNull() {
+    browserBot.currentWindowName = null;
+    browserBot.currentPage = null;
+
+    var mockLocation = new Mock();
+    mockLocation.expectsProperty('pathname').returns('thelocation');
+
+    mockWindow.location = mockLocation;
+    var mockDocument = new Object();
+    mockWindow.document = mockDocument;
+    mockWindow.document.location = mockLocation;
+    mockWindow.addEventListener = function(){};
+    mockWindow.attachEvent = function(){};
+
+    var pageBot = browserBot.getCurrentPage();
+
+    assertEquals(mockWindow, pageBot.getCurrentWindow());
+    assertEquals(mockDocument, pageBot.getDocument());
+
+    mockLocation.verify();
+}
+
+function testCurrentPageIsLazyCreatedBasedOnNamedWindowWhenCurrentWindowNameIsSet() {
+    browserBot.currentPage = null;
+
+    var mockLocation = new Mock();
+    mockLocation.expectsProperty('pathname').returns('thelocation');
+
+    mockWindow.location = mockLocation;
+    var mockDocument = new Object();
+    mockWindow.document = mockDocument;
+    mockWindow.document.location = mockLocation;
+    mockWindow.addEventListener = function(){};
+    mockWindow.attachEvent = function(){};
+
+    var pageBot = browserBot.getCurrentPage();
+    assertEquals(mockWindow, pageBot.getCurrentWindow());
+
+    mockLocation.verify();
+}
+</script>
+  </head>
+  <body>Selenium BrowserBot Tests</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/command-factory-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/command-factory-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/command-factory-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,454 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>JsUnit Utility Tests</title>
+<link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-logging.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-commandhandlers.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function setUp() {
+    LOG = new DummyLogger();
+    Selenium = new Object();
+    Selenium.decorateFunctionWithTimeout = function(f, timeout) {
+        return f;
+    }
+}
+
+// Allow mocking of Function.prototype.bind()
+String.prototype.bind = function(target) {
+    return "commandTarget." + this;
+}
+
+function testNullIsReturnedForUnknownCommands() {
+    var factory = new CommandHandlerFactory();
+    assertUndefined(factory.getCommandHandler("unknown"));
+}
+
+function testActionHandlerConstructorWithNoValueForDontCheckAlerts() {
+    var handler = new ActionHandler({}, false, this.someUndefinedProperty);
+    assertTrue("Should be checking", handler.checkAlerts);
+    assertEquals("action", handler.type);
+    assertTrue("Should halt on failure", handler.haltOnFailure);
+    assertUndefined("Should not be waiting", handler.wait);
+};
+
+function testActionsAreTypedAndAvailableAfterRegistrationInCommandHandlerFactory() {
+    var factory = new CommandHandlerFactory();
+    factory.registerAction("myAction", fnBind("actionFunction", this));
+
+    var myAction = factory.getCommandHandler("myAction");
+    assertNotUndefined(myAction);
+    assertEquals(CommandHandler, myAction.constructor);
+    assertEquals("actionFunction", myAction.actionBlock.__method);
+    assertEquals("action", myAction.type);
+}
+
+function testAssertsAreTypedAndAvailableAfterRegistrationInCommandHandlerFactory() {
+    var factory = new CommandHandlerFactory();
+    factory.registerAssert("assertFoo", "assertFunction", true);
+
+    var myAssert = factory.getCommandHandler("assertFoo");
+    assertNotUndefined(myAssert);
+    assertEquals(CommandHandler, myAssert.constructor);
+    assertEquals("assertFunction", myAssert.assertBlock);
+    assertEquals("assert", myAssert.type);
+    assertTrue(myAssert.haltOnFailure);
+}
+
+function testAccessorsAreTypedAndAvailableAfterRegistrationInCommandHandlerFactory() {
+    var factory = new CommandHandlerFactory();
+    factory.registerAccessor("getFoo", "accessorBlock");
+
+    var myAccessor = factory.getCommandHandler("getFoo");
+    assertNotUndefined(myAccessor);
+    assertEquals(CommandHandler, myAccessor.constructor);
+    assertEquals("accessorBlock", myAccessor.accessBlock);
+    assertEquals("accessor", myAccessor.type);
+}
+
+
+function testCreatePredicateFromSingleArgAccessorReturnsDesiredPredicate() {
+    var factory = new CommandHandlerFactory();
+    var accessor = function(arg) {
+        assertEquals("target", arg);
+        return this.foo();
+    };
+    var seleniumApi = {foo: function() {
+        return "theValue";
+    }};
+    var predicate = factory._predicateForSingleArgAccessor(accessor.bind(seleniumApi));
+
+    var result = predicate("target", "regexp:theV[aeiou]lue");
+    assertEquals("Actual value 'theValue' did match 'regexp:theV[aeiou]lue'", result.message);
+    assertTrue("Should have matched", result.isTrue);
+
+    var result = predicate("target", "betterNotMatch");
+    assertEquals("Actual value 'theValue' did not match 'betterNotMatch'", result.message);
+    assertFalse("Should not have matched", result.isTrue);
+}
+
+function testCreatePredicateFromNoArgAccessorReturnsDesiredPredicate() {
+    var factory = new CommandHandlerFactory();
+    var accessor = function() {
+        return this.foo();
+    };
+    var seleniumApi = {foo: function() {
+        return "theValue";
+    }};
+    var predicate = factory._predicateForNoArgAccessor(accessor.bind(seleniumApi));
+
+    var result = predicate("theV*e", "");
+    assertEquals("Actual value 'theValue' did match 'theV*e'", result.message);
+    assertTrue("Should have matched", result.isTrue);
+
+    var result = predicate("betterNotMatch", "");
+    assertEquals("Actual value 'theValue' did not match 'betterNotMatch'", result.message);
+    assertFalse("Should not have matched", result.isTrue);
+}
+
+function testCreatePredicateFromAccessorWhenNoArgs() {
+    var factory = new CommandHandlerFactory();
+    factory._predicateForNoArgAccessor = function(accessor) {
+        // mock
+        return "predicate";
+    }
+    var accessor = function() {
+        return 42;
+    };
+    var predicate = factory._predicateForAccessor(accessor, false);
+    assertEquals("predicate", predicate);
+};
+
+function testCreatePredicateFromAccessorWhenOneArg() {
+    var factory = new CommandHandlerFactory();
+    factory._predicateForSingleArgAccessor = function(accessor) {
+        // mock
+        return "predicate";
+    }
+    var accessor = function(arg) {
+        return 42;
+    };
+    var predicate = factory._predicateForAccessor(accessor, true);
+    assertEquals("predicate", predicate);
+};
+
+function testInvertPredicateReturnsDesiredPredicate() {
+    var factory = new CommandHandlerFactory();
+    var seleniumApi = {foo: function() {
+        return true;
+    }};
+    var predicate = function(target, value) {
+        return new PredicateResult(this.foo(), "msg");
+    };
+    var invertedPredicate = factory._invertPredicate(predicate.bind(seleniumApi));
+    var result = invertedPredicate("target", "value");
+    assertFalse("Result should have been negated", result.isTrue);
+    assertEquals("msg", result.message);
+};
+
+function testCreateAssertionFromPredicateForPositiveCase() {
+    // Make sure that the method looks at the isTrue property of the result.
+    var mockPredicateResult = new Mock();
+    mockPredicateResult.expectsProperty("isTrue").returns(true);
+
+    // Make sure that the executeAssertion method invokes the predicate in
+    // the context of the Selenium API.
+    var mockSeleniumApi = new Mock();
+    mockSeleniumApi.expects("foo");
+
+    var predicate = function(target, value) {
+        assertEquals("target", target);
+        assertEquals("value", value);
+        this.foo();
+        return mockPredicateResult;
+    };
+    var factory = new CommandHandlerFactory();
+    var assertion = factory.createAssertionFromPredicate(predicate.bind(mockSeleniumApi));
+
+    assertion("target", "value");
+
+    mockPredicateResult.verify();
+    mockSeleniumApi.verify();
+};
+
+function testCreateAssertionFromPredicateForNegativeCase() {
+    var predicate = function(target, value) {
+        return new PredicateResult(false, "message");
+    };
+    var factory = new CommandHandlerFactory();
+    var assertion = factory.createAssertionFromPredicate(predicate);
+
+    try {
+        assertion.call("seleniumApi", "target", "value");
+    } catch(e) {
+        if (!e.isAssertionFailedError) {
+            throw e;
+        }
+        assertEquals("message", e.failureMessage);
+        return;
+    }
+    fail("Should have thrown an exception");
+};
+
+function testCreateWaitForActionFromPredicateSetsCurrentTest() {
+    // Make sure that the method looks at the isTrue property of the result.
+    var mockPredicateResult = new Mock();
+    mockPredicateResult.expectsProperty("isTrue").returns(true);
+
+    // Make sure that the executeAssertion method invokes the predicate in
+    // the context of the Selenium API.
+    var mockSeleniumApi = new Mock();
+    mockSeleniumApi.expects("foo");
+
+    var predicate = function(target, value) {
+        assertEquals("target", target);
+        assertEquals("value", value);
+        this.foo();
+        return mockPredicateResult;
+    };
+    var factory = new CommandHandlerFactory();
+    var actionBlock = factory._waitForActionForPredicate(predicate.bind(mockSeleniumApi));
+
+    var terminationCondition = actionBlock("target", "value");
+    assertEquals('function', typeof(terminationCondition));
+    assertTrue(terminationCondition());
+
+    mockPredicateResult.verify();
+    mockSeleniumApi.verify();
+};
+
+function testPredicateBasedWaitForActionReturnsFalseForExceptions() {
+    // We treat exceptions as meaning that the condition is not yet true.
+    // Handy for things like waitForValue when the specified element
+    // has yet to be created!
+    var predicate = function(target, value) {
+        throw new Error("test exception");
+    };
+    var factory = new CommandHandlerFactory();
+    var action = factory._waitForActionForPredicate(predicate);
+
+    var terminationCondition = action.call("seleniumApi", "target", "value");
+    assertFalse(terminationCondition());
+};
+
+
+function testAllMethodsWithGetPrefixAreRegisteredAsAccessorsByRegisterAll() {
+    var actionSet = {getOne: "get1", getTwo: "get2", getdontGet: "another"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    assertUndefined(factory.getCommandHandler("getdontGet"));
+    assertUndefined(factory.getCommandHandler("notEvenClose"));
+
+    assertEquals("get1", factory.getCommandHandler("getOne").accessBlock.__method);
+    assertEquals("get2", factory.getCommandHandler("getTwo").accessBlock.__method);
+}
+
+function testAllMethodsWithGetPrefixAreRegisteredAsAssertsByRegisterAll() {
+    var actionSet = {getOne: function(target) {
+        return target + "foo";
+    }, getTwo: "get2", getdontGet: "another"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    assertUndefined(factory.getCommandHandler("assertdontGet"));
+    assertUndefined(factory.getCommandHandler("notEvenClose"));
+
+    var myAssert = factory.getCommandHandler("assertOne");
+    assertEquals(CommandHandler, myAssert.constructor);
+    assertNotUndefined(myAssert.assertBlock);
+    assertEquals("assert", myAssert.type);
+    assertTrue(myAssert.haltOnFailure);
+}
+
+function testAllMethodsWithGetPrefixAreRegisteredAsVerifiesByRegisterAll() {
+    var actionSet = {getOne: function(target) {
+        return target + "foo";
+    }, getTwo: "get2", getdontGet: "another"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    var myAssert = factory.getCommandHandler("verifyOne");
+    assertEquals(CommandHandler, myAssert.constructor);
+    assertNotUndefined(myAssert.assertBlock);
+    assertEquals("assert", myAssert.type);
+    assertFalse(myAssert.haltOnFailure);
+}
+
+function testAllMethodsWithGetPrefixAreRegisteredAsAssertNotsByRegisterAll() {
+    var actionSet = {getOne: function(target) {
+        return target + "foo";
+    }, getTwo: "get2", getdontGet: "another"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    var myAssert = factory.getCommandHandler("assertNotOne");
+    assertEquals(CommandHandler, myAssert.constructor);
+    assertNotUndefined(myAssert.assertBlock);
+    assertEquals("assert", myAssert.type);
+    assertTrue(myAssert.haltOnFailure);
+    try {
+        myAssert.assertBlock("blah", "blahfoo");
+        fail("Should have thrown an exception");
+    }
+    catch (e) {
+        // Expected.
+    }
+}
+
+function testAllMethodsWithGetPrefixAreRegisteredAsVerifyNotsByRegisterAll() {
+    var actionSet = {getOne: function(target) {
+        return target + "foo";
+    }, getTwo: "get2", getdontGet: "another"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    var myAssert = factory.getCommandHandler("verifyNotOne");
+    assertEquals(CommandHandler, myAssert.constructor);
+    assertNotUndefined(myAssert.assertBlock);
+    assertEquals("assert", myAssert.type);
+    assertFalse(myAssert.haltOnFailure);
+    try {
+        myAssert.assertBlock("blah", "blahfoo");
+        fail("Should have thrown an exception");
+    }
+    catch (e) {
+        // Expected.
+    }
+}
+
+// This object is normally declared in selenium-api.js
+storedVars = new Object();
+
+function testAllMethodsWithGetPrefixAreRegisteredAsStoreCommandsByRegisterAll() {
+    var actionSet = {getOne: function(target) {
+        return target + "foo";
+    }, getTwo: "get2", getdontGet: "another"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    var storeCommand = factory.getCommandHandler("storeOne");
+    assertEquals(CommandHandler, storeCommand.constructor);
+    assertNotUndefined(storeCommand.actionBlock);
+    assertEquals("action", storeCommand.type);
+    assertTrue(storeCommand.haltOnFailure);
+    storeCommand.actionBlock("mytarget", "myvar");
+    assertEquals("mytargetfoo", storedVars["myvar"]);
+}
+
+function testAllMethodsWithGetPrefixAreRegisteredAsWaitForCommandsByRegisterAll() {
+    var actionSet = {getOne: function(target) {
+        return target + "foo";
+    }, getTwo: "get2", getdontGet: "another"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    var command = factory.getCommandHandler("waitForOne");
+    assertEquals(CommandHandler, command.constructor);
+    assertNotUndefined(command.actionBlock);
+    assertEquals("action", command.type);
+    assertTrue(command.haltOnFailure);
+}
+
+function testAllMethodsWithGetPrefixAreRegisteredAsWaitForNotCommandsByRegisterAll() {
+    var actionSet = {getOne: function(target) {
+        return target + "foo";
+    }, getTwo: "get2", getdontGet: "another"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    var command = factory.getCommandHandler("waitForNotOne");
+    assertEquals(CommandHandler, command.constructor);
+    assertNotUndefined(command.actionBlock);
+    assertEquals("action", command.type);
+    assertTrue(command.haltOnFailure);
+}
+
+function testHaltOnFailureDefaultsToFalseForAsserts() {
+    var factory = new CommandHandlerFactory();
+    factory.registerAssert("doHalt", "assertFunction", true);
+    factory.registerAssert("dontHalt", "assertFunction");
+
+    assertTrue(factory.getCommandHandler("doHalt").haltOnFailure);
+    assertFalse(factory.getCommandHandler("dontHalt").haltOnFailure);
+}
+
+function testAllMethodsWithDoPrefixAreRegisteredAsActionsByRegisterAll() {
+    var actionSet = {doAnAction: "action1", doAnotherAction: "action2", dontRegister: "another"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    assertUndefined(factory.getCommandHandler("dontRegister"));
+    assertUndefined(factory.getCommandHandler("notEvenClose"));
+
+    assertEquals("action1", factory.getCommandHandler("anAction").actionBlock.__method);
+    assertEquals("action2", factory.getCommandHandler("anotherAction").actionBlock.__method);
+}
+
+function testActionsAreRegisteredWithAndWaitSuffix() {
+    var actionSet = {doAnAction: "action1"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    assertEquals("action1", factory.getCommandHandler("anAction").actionBlock.__method);
+    assertEquals("action1", factory.getCommandHandler("anActionAndWait").actionBlock.__method);
+    assertUndefined(factory.getCommandHandler("anAction").wait);
+    assertTrue(factory.getCommandHandler("anActionAndWait").wait);
+}
+
+function testAllMethodsWithAssertPrefixAreRegisteredForAssertAndVerifyByRegisterAll() {
+    var actionSet = {assertSomething: "assert1", assertSomeOtherThing: "assert2", assertionOther: "shouldn't register"};
+    var factory = new CommandHandlerFactory();
+    factory.registerAll(actionSet);
+
+    assertUndefined(factory.getCommandHandler("assertionOther"));
+    assertUndefined(factory.getCommandHandler("notEvenClose"));
+
+    var myAssert = factory.getCommandHandler("assertSomething");
+    assertEquals("assert1", myAssert.assertBlock.__method);
+    assertTrue(myAssert.haltOnFailure);
+    assertEquals("assert", myAssert.type);
+
+    var myVerify = factory.getCommandHandler("verifySomething");
+    assertEquals("assert1", myVerify.assertBlock.__method);
+    assertFalse(myVerify.haltOnFailure);
+    assertEquals("assert", myVerify.type);
+
+    assertEquals("assert2", factory.getCommandHandler("assertSomeOtherThing").assertBlock.__method);
+    assertEquals("assert2", factory.getCommandHandler("verifySomeOtherThing").assertBlock.__method);
+}
+</script>
+</head>
+
+<body>
+<a href="#" onclick="testAllMethodsWithDoPrefixAreRegisteredAsActionsByRegisterAll();">try</a>
+
+<p>This page contains tests for the CommandFactory object in selenium-commandhandlers.js. To see them, take a look at
+    the source. To run them, load this file via JsUnit's testRunner.html</p>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/command-handler-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/command-handler-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/command-handler-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Test for the CommandHandler</title>
+    <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-commandhandlers.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function testActionCommandsShouldCheckForUnhandledPopups() {
+
+     var mockSelenium = new Mock();
+     mockSelenium.expects("ensureNoUnhandledPopups");
+
+     var noop = function() {};
+     var handler = new ActionHandler(noop, false);
+     handler.execute(mockSelenium, "command");
+
+     mockSelenium.verify();
+}
+
+
+function testAccessorHandlerShouldReturnResultInCommandResult() {
+        function MockSelenium() {
+     }
+
+        var executorCalled = false;
+        var executor = function() { executorCalled = true; return "foo"; };
+        var handler = new AccessorHandler(executor, false);
+
+        result = handler.execute(new MockSelenium(), "command");
+
+        assertTrue(executorCalled);
+        assertEquals("foo", result.result);
+}
+
+</script>
+  </head>
+
+  <body>
+    <h1>CommandHandler component tests</h1>
+
+    <p>This page contains tests for different components in the CommandHandler. To see them, take a look at the source. To run them, load this file via JsUnit's testRunner.html</p>
+  </body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/confirm-handling-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/confirm-handling-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/confirm-handling-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+    
+    <script language="JavaScript" type="text/javascript">
+
+        function setUp() {
+            $("body").innerHTML = "<iframe NAME='frame' ID='frame' SRC='about:blank'></iframe>"
+            testWindow = window;
+            browserBot = BrowserBot.createForWindow(testWindow);
+            browserBot.getCurrentPage();
+        }
+
+        function testShouldConfirmConfirmationsByDefault() {
+            assertTrue(testWindow.confirm("Continue?"));
+        }
+
+        function testShouldCancelConfirmationIfPreviouslyInstructedTo() {
+            browserBot.cancelNextConfirmation(false);
+            assertFalse(testWindow.confirm("Continue?"));
+        }
+
+        function testShouldRevertToDefaultBehaviourAfterCancellingConfirmation() {
+            browserBot.cancelNextConfirmation(false);
+            testWindow.confirm("Continue?");
+            testShouldConfirmConfirmationsByDefault();
+        }
+
+        function testShouldNotReportAnyConfirmationsIfNoneHaveBeenGenerated() {
+            assertFalse(browserBot.hasConfirmations());
+            assertUndefined(browserBot.getNextConfirmation());
+        }
+
+        function testShouldReportSingleConfirmationIfGenerated() {
+            testWindow.confirm("Continue?");
+
+            assertTrue(browserBot.hasConfirmations());
+            assertEquals("Continue?", browserBot.getNextConfirmation());
+        }
+
+        function testShouldReportMultipleConfirmationsInOrderIfGenerated() {
+            testWindow.confirm("Continue?");
+            testWindow.confirm("Really Continue?");
+
+            assertTrue(browserBot.hasConfirmations());
+            assertEquals("Continue?", browserBot.getNextConfirmation());
+            assertTrue(browserBot.hasConfirmations());
+            assertEquals("Really Continue?", browserBot.getNextConfirmation());
+            assertFalse(browserBot.hasConfirmations());
+        }
+
+        function testShouldRemoveConfirmationWhenItIsRetreived() {
+            testWindow.confirm("Continue?");
+
+            assertTrue(browserBot.hasConfirmations());
+            assertNotUndefined(browserBot.getNextConfirmation());
+            assertFalse(browserBot.hasConfirmations());
+            assertUndefined(browserBot.getNextConfirmation());
+        }
+
+    </script>
+</head>
+
+<body id="body">
+<h1>Selenium PageBot Confirm Handling Tests</h1>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/error-checking-command-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/error-checking-command-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/error-checking-command-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,190 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-api.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-commandhandlers.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-testrunner.js"></script>
+<script language="JavaScript" type="text/javascript">
+    function setUp() {
+        selenium = new Selenium();
+        commandFactory = new Mock();
+        
+        testCase = new Mock();
+        testCase.expects("reset");
+        
+        testCase.testDocument = {
+            getElementById : function() {},
+            location: { href: "test.html" }
+        }
+        
+        global = {};
+        
+        var metrics = {
+            numCommandErrors : 0
+        };
+        
+        htmlTestRunner = {
+            markFailed : function(){}
+        };
+        
+        currentTest = new HtmlRunnerTestLoop(testCase, metrics, commandFactory);
+        currentTest.currentRow = {
+            markDone : function() {}
+        }
+    };
+
+    function verifyMocks() {
+        commandFactory.verify();
+        testCase.verify();
+    }
+
+    function testMustProvideMessageToExpectFailure() {
+        try {
+            selenium.assertFailureOnNext();
+        }
+        catch (expected) {
+            return;
+        }
+        fail("Message is a required parameter");
+    };
+
+    function testExpectFailureSucceedsWhenSubsequentCommandFails() {
+        var failingCommand = new Mock();
+        failingCommand.expects("execute").returns({failed:true, failureMessage: "Expected failure message"});
+        commandFactory.expects("getCommandHandler", "cmd").returns(failingCommand);
+
+        selenium.assertFailureOnNext("Expected failure message");
+        currentTest.expectedFailureJustSet = false;
+        var result = currentTest.commandFactory.getCommandHandler("cmd").execute();
+        currentTest._checkExpectedFailure(result);
+        assertTrue(result.passed);
+        verifyMocks();
+    };
+
+    function testExpectFailureFailsWhenSubsequentCommandFailsWithTheWrongMessage() {
+        var failingCommand = new Mock();
+        failingCommand.expects("execute").returns({failed:true, failureMessage: "foo"});
+        commandFactory.expects("getCommandHandler", "cmd").returns(failingCommand);
+
+        selenium.assertFailureOnNext("bar");
+        currentTest.expectedFailureJustSet = false;
+        var result = currentTest.commandFactory.getCommandHandler("cmd").execute();
+        currentTest._checkExpectedFailure(result);
+        assertTrue(result.failed);
+        assertEquals("Expected failure message 'bar' but was 'foo'", result.failureMessage);
+        verifyMocks();
+    };
+
+    function testExpectFailureFailsWhenSubsequentCommandPasses() {
+        var successCommand = new Mock();
+        successCommand.expects("execute").returns({passed:true});
+        commandFactory.expects("getCommandHandler", "foo").returns(successCommand);
+
+        selenium.assertFailureOnNext("expectedFailureMessage");
+        currentTest.expectedFailureJustSet = false;
+        var result = currentTest.commandFactory.getCommandHandler("foo").execute();
+        currentTest._checkExpectedFailure(result);
+        assertTrue(result.failed);
+        assertEquals("Expected failure did not occur.", result.failureMessage);
+        verifyMocks();
+    };
+
+    function testExpectFailureFailsWhenSubsequentCommandErrors() {
+        var msg = "error message";
+        selenium.assertFailureOnNext(msg);
+        currentTest.expectedFailureJustSet = false;
+        testCase.expects("addErrorMessage", "Expected failure, but error occurred instead", currentTest.currentRow);
+        var handled = currentTest.commandError("error message");
+        assertTrue(!handled);
+        verifyMocks();
+    };
+
+    function testMustProvideMessageToExpectError() {
+        try {
+            selenium.assertErrorOnNext();
+        }
+        catch (expected) {
+            return;
+        }
+        fail("Message is a required parameter");
+    };
+
+    function testExpectErrorSucceedsWhenSubsequentCommandErrors() {
+        var msg = "error message";
+        selenium.assertErrorOnNext(msg);
+        currentTest.expectedFailureJustSet = false;
+        var handled = currentTest.commandError("error message");
+        assertTrue(handled);
+        verifyMocks();
+    };
+
+    function testExpectErrorFailsWhenSubsequentCommandErrorsWithTheWrongMessage() {
+        selenium.assertErrorOnNext("expectedError");
+        currentTest.expectedFailureJustSet = false;
+        testCase.expects("addErrorMessage", "Expected error message 'expectedError' but was 'actualError'", currentTest.currentRow);
+        var handled = currentTest.commandError("actualError");
+        assertTrue(!handled);
+        verifyMocks();
+    };
+
+     function testExpectErrorFailsWhenSubsequentCommandPasses() {
+        var successCommand = new Mock();
+        successCommand.expects("execute").returns({passed:true});
+        commandFactory.expects("getCommandHandler", "foo").returns(successCommand);
+
+        selenium.assertErrorOnNext("Expected error message");
+        currentTest.expectedFailureJustSet = false;
+        var result = currentTest.commandFactory.getCommandHandler("foo").execute();
+        currentTest._checkExpectedFailure(result);
+        assertTrue(result.failed);
+        assertEquals("Expected error did not occur.", result.failureMessage);
+        verifyMocks();
+    };
+
+    function testExpectErrorFailsWhenSubsequentCommandFails() {
+        var failingCommand = new Mock();
+        failingCommand.expects("execute").returns({failed:true, failureMessage: "message"});
+        commandFactory.expects("getCommandHandler", "cmd").returns(failingCommand);
+
+        selenium.assertErrorOnNext("message");
+        currentTest.expectedFailureJustSet = false;
+        var result = currentTest.commandFactory.getCommandHandler("cmd").execute();
+        currentTest._checkExpectedFailure(result);
+        assertTrue(result.failed);
+        
+        assertEquals("Expected error, but failure occurred instead", result.failureMessage);
+        verifyMocks();
+    };
+  </script>
+  </head>
+
+  <body>
+    <p>This page contains tests for the ExpectFailureCommandFactory object. To see them, take a look at the source. To run them, load this file via JsUnit's testRunner.html</p>
+  </body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/event-bubble-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/event-bubble-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/event-bubble-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function setUp() {
+    pageBot = BrowserBot.createForWindow(window);
+    pageBot._windowClosed = function() {};
+
+    theForm = document.getElementById("theFormElement");
+    theTextbox = document.getElementById("theTextboxElement");
+}
+
+function testFocusEventNeverBubbles() {
+    var textboxFocus = false;
+    var formFocus = false;
+    theTextbox.onfocus = function() {textboxFocus = true};
+    theForm.onfocus = function() {formFocus = true};
+
+    pageBot.replaceText(theTextbox, 'foo1');
+
+    assertTrue(textboxFocus);
+    assertFalse(formFocus);
+}
+
+function testSelectEventCanBubbleInFirefoxButNotInIE() {
+    assertTrue(true);
+    var textboxSelect = false;
+    var formSelect = false;
+    theTextbox.onselect = function() {textboxSelect = true};
+    theForm.onselect = function() {formSelect = true};
+
+    pageBot.replaceText(theTextbox, 'foo1');
+
+    assertTrue(textboxSelect);
+    if (browserVersion.isIE) {
+        assertFalse(formSelect);
+    } else {
+        assertTrue(formSelect);
+    }
+}
+
+function testChangeEventCanBubbleInFirefoxButNotInIE() {
+    var textboxChange = false;
+    var formChange = false;
+    theTextbox.onchange = function() {textboxChange = true};
+    theForm.onchange = function() {formChange = true};
+
+    pageBot.replaceText(theTextbox, 'foo2');
+
+    assertTrue(textboxChange);
+    if (browserVersion.isIE) {
+        assertFalse(formChange);
+    } else {
+        assertTrue(formChange);
+    }
+}
+
+function testClickEventAlwaysBubbles() {
+    var buttonClick = false;
+    var formClick = false;
+    var theButton = document.getElementById("theButtonElement")
+    theButton.onclick = function() {buttonClick = true};
+    theForm.onclick = function() {formClick = true};
+
+    pageBot.clickElement(theButton);
+
+    assertTrue(buttonClick);
+    assertTrue(formClick);
+}
+</script>
+  </head>
+
+  <body>
+    <h1>Selenium Browserbot Tests</h1>
+
+    <form id="theFormElement" name="theFormElement">
+      <input name="theTextboxElement" id="theTextboxElement" type="text" value="theTextbox"/>
+      <input name="theButtonElement" id="theButtonElement" type="button" value="theButton"/>
+    </form>
+  </body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/htmlutil-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/htmlutil-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/htmlutil-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Assert tests</title>
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+    <script language="JavaScript" type="text/javascript">
+
+        function setUp() {
+            element1 = document.getElementById("id1");
+            window.setTimeout = function (func, time) {
+                func.call();
+            }
+        }
+
+        function testFlashEffectShouldHighlightElementBackgroundColorThenChangeItBack() {
+            var previousColor = "#000000";
+            var highlightColor = "yellow";
+            var colorsChanged = [];
+            elementGetStyle = function(element, styleAttribut) {
+                return previousColor;
+            }
+            elementSetStyle = function(element, style) {
+                colorsChanged.push(style["background-color"]);
+            };
+
+            highlight(element1);
+
+            assertObjectEquals([highlightColor, previousColor], colorsChanged);
+        }
+
+        function testgetKeyCodeFromKeySequenceShouldReturnCorrectAsciiCodeOfInputKeySequence() {
+            assertObjectEquals(119, getKeyCodeFromKeySequence("w"));
+            assertObjectEquals(119, getKeyCodeFromKeySequence("\\119"));
+            assertObjectEquals(92, getKeyCodeFromKeySequence("\\"));
+            assertObjectEquals(92, getKeyCodeFromKeySequence("\\92"));
+            assertObjectEquals(55, getKeyCodeFromKeySequence("7"));
+            assertObjectEquals(55, getKeyCodeFromKeySequence("\\55"));
+        }
+
+        function testgetKeyCodeFromKeySequenceShouldBackwardCompatibleFor2Or3DigitAsciiCodes() {
+            assertObjectEquals(119, getKeyCodeFromKeySequence("119"));
+            assertObjectEquals(92, getKeyCodeFromKeySequence("92"));
+            assertObjectEquals(55, getKeyCodeFromKeySequence("55"));
+        }
+
+        function testgetKeyCodeFromKeySequenceShouldFailOnIncorrectInput() {
+            checkKeySequence("");
+            checkKeySequence("\\a");
+            checkKeySequence("\\1234");
+            checkKeySequence("ab");
+        }
+        
+        function testAbsolutify() {
+            assertEquals("http://x/blah", absolutify("http://x/blah", "http://y"));
+            
+            assertEquals("http://y/blah", absolutify("blah", "http://y"));
+            
+            assertEquals("http://y/blah", absolutify("blah", "http://y/foo"));
+            assertEquals("http://y/foo/blah", absolutify("blah", "http://y/foo/"));
+            
+            assertEquals("http://y/foo/blah", absolutify("blah", "http://y/foo/?bar=1"));
+            assertEquals("http://y/foo/blah", absolutify("blah", "http://y/foo/?bar=1#baz=2"));
+            assertEquals("http://y/foo/blah", absolutify("blah", "http://y/foo/#baz=2"));
+            
+            // windows file urls
+            assertEquals("file:///c:/foo/blah", absolutify("blah", "c:\\foo\\"));
+            assertEquals("file:///c:/blah", absolutify("blah", "c:\\foo"));
+            assertEquals("file:///blah", absolutify("/blah", "c:\\foo\\bar"));
+            
+        }
+        
+        
+        function testParseAndReassembleUrl() {
+            var tests = [
+                "http://www.google.com"
+                ,"file://localhost/c:/blah"
+                ,"file:///c:/blah"
+                ,"http://www.google.com/"
+                ,"http://www.google.com/foo"
+                ,"http://www.google.com/foo?blah=blah/blah"
+                ,"http://www.google.com/foo?blah=blah/blah#barbar"
+                ,"http://www.google.com/foo#bur?blah"
+                ,"http://foo:bar@www.google.com"
+                ,"http://foo@www.google.com"
+                ,"http://foo:ba%20r@www.google.com"
+            ];
+            for (var i = 0; i < tests.length; i++) {
+                assertEquals(tests[i], reassembleLocation(parseUrl(tests[i])));
+            }
+        }
+        
+        function checkKeySequence(input) {
+            try {
+                getKeyCodeFromKeySequence(input);
+                fail("exception expected");
+            } catch (e) {
+                assertTrue(e.isSeleniumError);
+                assertEquals("invalid keySequence", e.message);
+            }
+        }
+    </script>
+</head>
+
+<body>
+
+<a id="id1" href="#id1">this is the first element</a>
+</body>
+</html>
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/optionlocator-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/optionlocator-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/optionlocator-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,194 @@
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Option Locator tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-api.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+var mockSelect = new Object();
+var optionLocatorFactory = new OptionLocatorFactory();
+function setUp() {
+    mockSelect.options = [{text: "Option Zero", value: "option0"},
+                          {text: "Option One",  value: "option1"},
+                          {text: "Option Two",  value: "option2"},
+                          {text: "",  value: ""}];
+    mockSelect.selectedIndex = 1;
+}
+
+function testSelectByIndexSuccess() {
+    var locator = optionLocatorFactory.fromLocatorString("index=2");
+    var option = locator.findOption(mockSelect);
+    assertEquals("option2", option.value);
+}
+
+function testSelectByIndexOutOfBounds() {
+    var locator = optionLocatorFactory.fromLocatorString("index=" + mockSelect.options.length);
+    assertCallFails("Should not be able to find an option out of bounds",
+                    function() {locator.findOption(mockSelect);});
+}
+
+function testSelectByLabelSuccess() {
+    var locator = optionLocatorFactory.fromLocatorString("label=Opt*Two");
+    var option = locator.findOption(mockSelect);
+    assertEquals("option2", option.value);
+}
+
+function testSelectByLabelFailure() {
+    var locator = optionLocatorFactory.fromLocatorString("label=nosuchlabel");
+    assertCallFails(
+        "Should not be able to find an option with label of 'nosuchlabel'",
+        function() {locator.findOption(mockSelect);});
+}
+
+function testSelectByValueSuccess() {
+    var locator = optionLocatorFactory.fromLocatorString("value=opt*2");
+    var option = locator.findOption(mockSelect);
+    assertEquals("option2", option.value);
+}
+
+function testSelectByValueFailure() {
+    var locator = optionLocatorFactory.fromLocatorString("value=nosuchvalue");
+    assertCallFails(
+        "Should not be able to find an option with label of 'nosuchvalue'",
+        function() {locator.findOption(mockSelect);});
+}
+
+function testIsSelectedByLabelSuccess() {
+    var locator = optionLocatorFactory.fromLocatorString("label=Option One");
+    locator.assertSelected(mockSelect);
+}
+
+function testIsSelectedByLabelFailure() {
+    var locator = optionLocatorFactory.fromLocatorString("label=O*ion Two");
+    try {
+    	locator.assertSelected(mockSelect);
+		fail();
+    }
+    catch (e){}
+}
+
+function testIsSelectedByValueSuccess() {
+    var locator = optionLocatorFactory.fromLocatorString("value=opt*n1");
+    locator.assertSelected(mockSelect);
+}
+
+function testIsSelectedByValueFailure() {
+    var locator = optionLocatorFactory.fromLocatorString("value=option2");
+    try {
+    	locator.assertSelected(mockSelect);
+		fail();
+    }
+    catch (e){}
+}
+
+function testIsSelectedByIndexSuccess() {
+    var locator = optionLocatorFactory.fromLocatorString("index=1");
+    locator.assertSelected(mockSelect);
+}
+
+function testIsSelectedByIndexFailure() {
+    var locator = optionLocatorFactory.fromLocatorString("index=2");
+    try {
+    	locator.assertSelected(mockSelect);
+		fail();
+    }
+    catch (e){}
+}
+
+function testIsSelectedByEmptyLabelSuccess() {
+    mockSelect.selectedIndex = 3;
+    var locator = optionLocatorFactory.fromLocatorString("label=");
+    locator.assertSelected(mockSelect);
+}
+
+function testIsSelectedByEmptyLabelFailure() {
+    var locator = optionLocatorFactory.fromLocatorString("label=");
+    try {
+    	locator.assertSelected(mockSelect);
+		fail();
+    }
+    catch (e){}
+}
+
+function testIsSelectedWithIndexOutOfBounds() {
+    var locator = optionLocatorFactory.fromLocatorString("index=" + mockSelect.options.length);
+    try {
+    	locator.assertSelected(mockSelect);
+		fail();
+    }
+    catch (e){}
+}
+
+function testOptionLocatorWithBadLocatorType() {
+    assertCallFails(
+        "Should not be able to create a locator with an unkown type",
+        function() {optionLocatorFactory.fromLocatorString("badtype=foo");});
+}
+
+function testOptionLocatorWithBadIndex() {
+    assertCallFails(
+        "Should not be able to create a locator with a bad index.",
+        function() {optionLocatorFactory.fromLocatorString("index=foo");});
+}
+
+function testOptionLocatorWithNegativeIndex() {
+    assertCallFails(
+        "Should not be able to create a locator with a bad index.",
+        function() {optionLocatorFactory.fromLocatorString("index=-100");});
+}
+
+function assertCallFails(message, theCall, expectedFailureMessage) {
+    try {
+        theCall();
+    } catch (expected) {
+        if (expectedFailureMessage) {
+            assertEquals(expectedFailureMessage, e.failureMessage);
+        }
+        return;
+    }
+    fail(message);
+}
+
+function assertAssertionFails(message, theCall, expectedFailureMessage) {
+    try {
+        theCall();
+    } catch (e) {
+        if (!e.isAssertionFailedError) {
+            throw e;
+        }
+        if (expectedFailureMessage) {
+            assertEquals(expectedFailureMessage, e.failureMessage);
+        }
+        return;
+    }
+    fail(message);
+}
+
+</script>
+  </head>
+  <body>Option Locator Tests</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-accessor-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-accessor-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-accessor-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function setUp() {
+    pageBot = BrowserBot.createForWindow(window);
+}
+
+function testGetButtonsReturnsBothButtons() {
+    var result = pageBot.getAllButtons();
+    assertObjectEquals(["theButton","theSubmit"], result);
+}
+
+function testGetLinksReturnsLinks() {
+    var result = pageBot.getAllLinks();
+    assertObjectEquals(["myLink","myOtherLink","yetAnotherLink"], result);
+}
+
+function testGetFieldsReturnsFields() {
+    var result = pageBot.getAllFields();
+    assertObjectEquals(["theTextbox","theOtherTextbox"], result);
+}
+</script>
+  </head>
+
+  <body>
+    <h1>Selenium Browserbot Tests</h1>
+
+    <form id="theForm" name="theForm" onsubmit="return false;">
+      <a id="myLink" href="test_click_page2.html">This is a link</a>
+      <a id="myOtherLink" href="test_click_page2.html">So is this</a>
+      <a id="yetAnotherLink" href="test_click_page2.html">This too</a>
+      <input name="theTextbox" id="theTextbox" type="text" value="theTextbox"/>
+      <input name="theOtherTextbox" id="theOtherTextbox" type="text" value="theOtherTextbox"/>
+      <input name="theButton" id="theButton" type="button" value="The Button"/>
+      <input name="theSubmit" id="theSubmit" type="submit" value="The Submit"/>
+    </form>
+  </body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-action-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-action-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-action-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,236 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>JsUnit Utility Tests</title>
+<link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function setUp() {
+    initialiseSelect()
+    initialiseTextbox()
+    initialiseCheckbox()
+    initialiseRadios()
+    initialiseButtons()
+    pageBot = BrowserBot.createForWindow(window);
+    pageBot._windowClosed = function() {
+    };
+}
+
+function attachEventListeners(element, eventSink) {
+    element.onfocus = function() {
+        eventSink.push('focus')
+    }
+    element.onselect = function() {
+        eventSink.push('select')
+    }
+    element.onclick = function() {
+        eventSink.push('click')
+    }
+    element.onchange = function() {
+        eventSink.push('change')
+    }
+    element.onblur = function() {
+        eventSink.push('blur')
+    }
+}
+
+function initialiseSelect() {
+    theSelect = document.getElementById("theSelect")
+    theSelect.options[1].selected = true
+
+    theSelectEvents = new Array()
+    attachEventListeners(theSelect, theSelectEvents)
+
+    assertEquals("option2", theSelect.value)
+}
+
+function initialiseTextbox() {
+    theTextbox = document.getElementById("theTextbox")
+    theTextbox.value = "theTextbox"
+
+    textboxEvents = new Array()
+    attachEventListeners(theTextbox, textboxEvents)
+}
+
+function initialiseCheckbox() {
+    theCheckbox = document.getElementById("theCheckbox")
+    theCheckbox.checked = false
+    assertFalse(theCheckbox.checked)
+
+    checkboxEvents = new Array()
+    attachEventListeners(theCheckbox, checkboxEvents)
+}
+
+function initialiseRadios() {
+    theRadio1 = document.getElementById("theRadio1")
+    theRadio1.checked = true
+    assertTrue(theRadio1.checked)
+    radio1Events = new Array()
+    attachEventListeners(theRadio1, radio1Events)
+
+    theRadio2 = document.getElementById("theRadio2")
+    theRadio2.checked = false
+    assertFalse(theRadio2.checked)
+    radio2Events = new Array()
+    attachEventListeners(theRadio2, radio2Events)
+}
+
+function initialiseButtons() {
+    theButton = document.getElementById("theButton")
+    buttonEvents = new Array()
+    attachEventListeners(theButton, buttonEvents)
+
+    theSubmit = document.getElementById("theSubmit")
+    submitEvents = new Array()
+    attachEventListeners(theSubmit, submitEvents)
+}
+
+function testSelectOptionShouldSelectAndTriggerFocusChange() {
+    pageBot.selectOption(theSelect, theSelect.options[2])
+
+    assertEquals("option3", theSelect.value)
+    assertEquals("focus,change", theSelectEvents.join());
+}
+
+function testSelectAlreadySelectedOptionWithLabelShouldNotTriggerChange() {
+    pageBot.selectOption(theSelect, theSelect.options[1])
+
+    assertEquals("option2", theSelect.value)
+    assertEquals("focus", theSelectEvents.join())
+}
+
+function testReplaceTextInTextBoxShouldTriggerFocusSelectChange() {
+    pageBot.replaceText(theTextbox, "new text")
+    assertEquals("new text", theTextbox.value)
+
+    assertEquals("focus,select,change", textboxEvents.join())
+}
+
+function TODO_testReplaceTextShouldNotTriggerSelectIfOriginallyEmpty() {
+    theTextbox.value = ""
+
+    pageBot.replaceText(theTextbox, "new text")
+
+    assertEquals("new text", theTextbox.value)
+    assertEquals("focus,change", textboxEvents.join());
+}
+
+function testClickCheckboxShouldTriggerFocusClickChange() {
+    pageBot.clickElement(theCheckbox)
+
+    assertTrue("Checkbox should be checked", theCheckbox.checked)
+
+    // Emulate actual event order (varies per browser for manual click)
+    if (browserVersion.isSafari)
+    {
+        assertEquals("focus,change,click", checkboxEvents.join())
+    } else if (browserVersion.isOpera) {
+        assertEquals("focus,click", checkboxEvents.join())
+    } else
+    {
+        assertEquals("focus,click,change", checkboxEvents.join())
+    }
+}
+
+function testClickCheckboxShouldUncheckIfAlreadyChecked() {
+    theCheckbox.checked = true
+    pageBot.clickElement(theCheckbox)
+    assertFalse("Checkbox shouldn't be checked", theCheckbox.checked)
+}
+
+function testClickRadioShouldTriggerFocusClickChange() {
+    pageBot.clickElement(theRadio2)
+    assertFalse("Radio1 shouldn't be checked", theRadio1.checked)
+    assertTrue("Radio should be checked", theRadio2.checked)
+
+    assertEquals("", radio1Events.join());
+
+    // Safari-real doesn't support change events on radio
+    if (browserVersion.isSafari)
+    {
+        assertEquals("focus,click", radio2Events.join());
+    } else if (browserVersion.isOpera) {
+        assertEquals("focus,click", radio2Events.join())
+    } else
+    {
+        assertEquals("focus,click,change", radio2Events.join())
+    }
+}
+
+function testClickRadioShouldNotTriggerChangeIfAlreadySelected() {
+    assertTrue(theRadio1.checked)
+    pageBot.clickElement(theRadio1)
+    assertTrue("Radio1 should be checked", theRadio1.checked)
+
+    assertEquals("focus,click", radio1Events.join())
+    assertEquals("", radio2Events.join())
+}
+
+function testClickButtonShouldTriggerFocusClick() {
+    pageBot.clickElement(theButton)
+
+    assertEquals("focus,click", buttonEvents.join())
+}
+
+function testClickSubmitShouldTriggerFocusClick() {
+    pageBot.clickElement(theSubmit)
+    assertEquals("focus,click", submitEvents.join())
+}
+
+function testClickShouldNotTriggerEventsPassedClickIfWindowIsClosed() {
+    theButton.onclick = function() {
+        buttonEvents.push('clickNclose');
+        pageBot._windowClosed = function() {
+            return true
+        };
+    }
+
+    pageBot.clickElement(theButton);
+    assertEquals("focus,clickNclose", buttonEvents.join());
+}
+</script>
+</head>
+
+<body>
+<h1>Selenium Browserbot Tests</h1>
+
+<form id="theForm" name="theForm" onsubmit="return false;">
+    <input name="theTextbox" id="theTextbox" type="text" value="theTextbox"/>
+    <input name="theCheckbox" id="theCheckbox" type="checkbox"/>
+    <input name="theRadio" id="theRadio1" type="radio" value="radio1"/><input name="theRadio" id="theRadio2"
+                                                                              type="radio" value="radio2"/>
+    <select name="theSelect" id="theSelect">
+        <option value="option1">First Option</option>
+        <option value="option2" selected="true">Second Option</option>
+        <option value="option3">Third Option</option>
+    </select>
+    <input name="theButton" id="theButton" type="button" value="The Button"/>
+    <input name="theSubmit" id="theSubmit" type="submit" value="The Submit"/>
+</form>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-attribute-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-attribute-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-attribute-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function setUp() {
+    pageBot = BrowserBot.createForWindow(window);
+
+    element1 = document.getElementById("id1");
+    element2 = document.getElementById("id2");
+    element3 = document.getElementById("id3");
+    element4 = document.getElementById("document.links[0]");
+}
+
+
+//
+// Tests for finding attribute with id.
+//
+function testFindAttributeThrowsErrorIfNotElementFound() {
+    var errorMessage = null;
+    try {
+        pageBot.findAttribute('noSuchElement at attribute');
+    } catch (e) {
+        errorMessage = e.message;
+    }
+    assertEquals("Element noSuchElement not found", errorMessage);
+}
+
+function testFindAttributeReturnsNullIfElementDoesNotHaveAttribute() {
+    assertNull(pageBot.findAttribute('id1 at wrong-attribute'));
+}
+
+// Different type of attributes
+function testFindAttributeReturnsClassAttributeFromElementWithId() {
+    assertEquals("id3class", pageBot.findAttribute('id3 at class'));
+}
+
+function testFindAttributeReturnsAltAttributeFromElementWithId() {
+    assertEquals("the second link", pageBot.findAttribute('id2 at alt'));
+}
+
+function testFindAttributeReturnsCustomAttributeFromElementWithId() {
+    assertEquals("bar", pageBot.findAttribute("id3 at foo"));
+}
+
+function testFindAttributeReturnsNumericAttributeFromElementWithId() {
+    assertEquals("24", pageBot.findAttribute("theInput at maxlength"));
+}
+
+// Different ways of locating elements
+function testFindAttributeReturnsAttributeFromElementWithDomTraversal() {
+    assertEquals("bar", pageBot.findAttribute('document.links[2]@foo'));
+}
+
+function testFindAttributeReturnsAttributeFromElementWithXPath() {
+    assertEquals("parabar", pageBot.findAttribute('//p/@foo'));
+}
+
+function testFindAttributeReturnsAttributeFromElementWithXPathAndAttributeSelector() {
+    assertEquals("parabar", pageBot.findAttribute("//p[@name='name1']/@foo"));
+}
+
+
+</script>
+  </head>
+
+  <body>
+    <a id="id1" href="#id1">this is the first element</a>
+    <a id="id2" name="name1" href="#id2" alt="the second link">this is the second element</a>
+    <a id="id3" name="name1" href="#id3" class="id3class" foo="bar">this is the third element</a>
+    <p id="document.links[0]" name="name1" foo="parabar">dummy element</p>
+    <input name="theInput" maxlength="24"/>
+  </body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-locator-tests-include.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-locator-tests-include.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-locator-tests-include.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+  </head>
+
+  <body>
+    <a id="id1" href="#id1">this is the first element</a>
+    <a id="id2" name="name1" href="#id2">this is the second element</a>
+    <a id="id3" name="name1" href="#id3">this is the third element</a>
+    <a id="id4" name="id3" href="#id4">this is the fourth element</a>
+
+    <p id="document.links[0]" name="name1" class="pstyle">dummy element</p>
+    
+    <input id="toggle-a" name="toggle" value="a">
+    <input id="toggle-b" name="toggle" value="b">
+    <input id="toggle-c" name="toggle" value="c">
+  </body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-locator-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-locator-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-locator-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,324 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>JsUnit Utility Tests</title>
+<link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+var highlighter = new Object();
+highlighter.highlight = function(){};
+
+function highlight() {
+    highlighter.highlight();
+}
+
+function setUp() {
+
+    // To be realistic, the elements are located in a separate IFrame.
+    var iframe = document.getElementById("testIframe");
+    iWindow = iframe.contentWindow.window;
+    frameDocument = iWindow.document;
+
+    pageBot = BrowserBot.createForWindow(iWindow);
+
+    element1 = iWindow.document.getElementById("id1");
+    element2 = iWindow.document.getElementById("id2");
+    element3 = iWindow.document.getElementById("id3");
+    element4 = iWindow.document.getElementById("document.links[0]");
+
+    toggleA = iWindow.document.getElementById("toggle-a");
+    toggleB = iWindow.document.getElementById("toggle-b");
+    toggleC = iWindow.document.getElementById("toggle-c");
+}
+
+function testShouldNotHighlightFoundElementIfHighlightFlagNotSetted() {
+    highlighter = new Mock();
+    pageBot.findElement('id1');
+    highlighter.verify();
+}
+
+
+function testShouldBeAbleToHighlightFoundElement() {
+    pageBot.browserbot.setShouldHighlightElement(true);
+    highlighter = new Mock();
+    
+    highlighter.expects("highlight", element1);
+    pageBot.findElement('id1');
+    highlighter.verify();
+}
+
+function testShouldNotTryToHightlightIfNoElementFound() {
+    pageBot.browserbot.setShouldHighlightElement(true);
+    highlighter = new Mock();
+    try {
+        pageBot.findElement('IDontExist');
+        fail("Should have failed for element not found");
+    } catch(e) {
+        //expects
+    }
+    highlighter.verify();
+}
+
+//
+// Tests for "id" locator
+//
+function testIdLocatorReturnsNullIfElementNotPresent() {
+    assertNull(pageBot.findElementBy('id', 'noSuchElement', frameDocument));
+}
+
+function testIdLocatorReturnsElementWithMatchingId() {
+    assertEquals(element3, pageBot.findElementBy('id', 'id3', frameDocument));
+}
+
+function testIdLocatorDoesNotReturnElementWithMatchingName() {
+    assertNull(pageBot.findElementBy('id', 'name1', frameDocument));
+}
+
+//
+// Tests for "name" locator
+//
+function testNameLocatorReturnsNullIfElementNotPresent() {
+    assertNull(pageBot.findElementBy('name', 'noSuchElement', frameDocument));
+}
+
+function testNameLocatorDoesNotReturnElementWithMatchingId() {
+    assertNull(pageBot.findElementBy('name', 'id2', frameDocument));
+}
+
+function testNameLocatorReturnsElementWithMatchingName() {
+    assertEquals(element2, pageBot.findElementBy('name', 'name1', frameDocument));
+}
+
+function testNameLocatorReturnsFirstMatch() {
+    assertEquals(toggleA, pageBot.findElementBy('name', 'toggle', frameDocument));
+}
+
+function testNameLocatorCanRefineSelectionByElementValue() {
+    assertEquals(toggleB, pageBot.findElementBy('name', 'toggle value=b', frameDocument));
+    assertEquals(toggleB, pageBot.findElementBy('name', 'toggle b', frameDocument));
+}
+
+function testNameLocatorCanRefineSelectionByIndex() {
+    assertEquals(toggleB, pageBot.findElementBy('name', 'toggle index=1', frameDocument));
+}
+
+//
+// Tests for "identifier" locator
+//
+function testIdentifierLocatorReturnsNullIfElementNotPresent() {
+    assertNull(pageBot.findElementBy('identifier', 'noSuchElement', frameDocument));
+}
+
+function testIdentifierLocatorReturnsHtmlElementWithMatchingId() {
+    assertEquals(element3, pageBot.findElementBy('identifier', 'id3', frameDocument));
+}
+
+function testIdentifierLocatorReturnsFirstElementWithMatchingName() {
+    assertEquals(element2, pageBot.findElementBy('identifier', 'name1', frameDocument));
+}
+
+//
+// Tests for "dom" locator
+//
+function testLocateElementByDomTraversal() {
+    assertEquals(element2,
+            pageBot.findElementBy('dom', "document.links[1]", frameDocument, iWindow));
+}
+
+function testLocateElementByDomTraversalReturnsNullForNoMatchingElement() {
+}
+
+function testLocateElementByDomTraversalFailsWithInvalidDomString() {
+    try {
+        pageBot.findElementBy('dom', "document.foo.bar", frameDocument);
+        fail("Should have thrown exception");
+    }
+    catch (e) {
+        // Expected
+    }
+}
+
+//
+// Tests for "xpath" locator
+//
+function testLocateElementByXPathWithTagNameOnly() {
+    assertEquals(element4,
+            pageBot.findElementBy('xpath', "//p", frameDocument));
+}
+
+function testLocateElementByXPathWithAttributeOnly() {
+    assertEquals(element4,
+            pageBot.findElementBy('xpath', "//*[@class='pstyle']", frameDocument));
+}
+
+function testLocateElementByXPathWithTagNameAndAttribute() {
+    assertEquals(element4,
+            pageBot.findElementBy('xpath', "//p[@name='name1']", frameDocument));
+}
+
+function testLocateElementByXPathReturnsFirstMatchingElement() {
+    assertEquals(element2,
+            pageBot.findElementBy('xpath', "//*[@name='name1']", frameDocument));
+}
+
+//
+// Tests for "link" locator
+//
+function testLocateLinkReturnsNullIfLocatorIsNotLinkLocator() {
+    assertNull(pageBot.findElementBy('link', '//NotALinkLocator', frameDocument));
+}
+
+function testLocateLinkReturnsNullIfLinkNotPresent() {
+    assertNull(pageBot.findElementBy('link', 'No Such Link', frameDocument));
+}
+
+function testLinkElementReturnsLinkWithMatchingText() {
+    assertEquals(element3, pageBot.findElementBy('link', 'this is the third element', frameDocument));
+}
+
+function testLinkElementWithGlob() {
+    assertEquals(element3, pageBot.findElementBy('link', 'this * third element', frameDocument));
+}
+
+function testLinkElementWithRegex() {
+    assertEquals(element3, pageBot.findElementBy('link', 'regexp:this [aeiou]s the third element', frameDocument));
+}
+
+//
+// Tests for PageBot.findElement()
+//
+function testFindElementWithNoLocatorPrefixUsesImplicitStrategy() {
+    var calls = new Array();
+    pageBot.locationStrategies['implicit'] = function(locator) {
+        calls.push(locator);
+        return "bar";
+    };
+
+    var element = pageBot.findElement("foo");
+    assertEquals("bar", element);
+    assertEquals("foo", calls.join());
+}
+
+function testFindElementThrowsExceptionIfNoElementFound() {
+    var calls = new Array();
+    pageBot.locationStrategies['implicit'] = function(locator) {
+        calls.push(locator);
+        return null;
+    };
+
+    var element = null;
+    var failureMessage = null;
+    try {
+        element = pageBot.findElement("foo");
+    } catch (e) {
+        failureMessage = e.message;
+    }
+
+    assertNull(element);
+    assertEquals("Element foo not found", failureMessage);
+    assertEquals("foo", calls.join());
+}
+
+function testFindElementWithLocatorPrefixUsesSpecifiedStrategy() {
+    var calls = new Array();
+    pageBot.locationStrategies["anotherlocator"] = function(locator) {
+        calls.push(locator);
+        return "bar";
+    };
+
+    element = pageBot.findElement("anotherlocator=foo");
+    assertEquals("bar", element);
+    assertEquals("foo", calls.join());
+}
+
+function testFindElementWithUnknownLocatorPrefixThrowsException() {
+    var calls = new Array();
+    pageBot.locationStrategies['implicit'] = function(locator) {
+        calls.push(locator);
+        return "bar";
+    };
+
+    var element = null;
+    var failureMessage = null;
+    try {
+        element = pageBot.findElement("bogus=foo");
+    } catch (e) {
+        failureMessage = e.message;
+    }
+
+    assertNull(element);
+    assertEquals("Unrecognised locator type: 'bogus'", failureMessage);
+    assertEquals("", calls.join());
+}
+
+function testFindElementReturnsElementWithMatchingIdentifier() {
+    assertEquals(element2, pageBot.findElement("id2"));
+}
+
+function testFindElementReturnsMatchingName() {
+    assertEquals(element2, pageBot.findElement("name1"));
+}
+
+function testFindElementTriesDomTraversalBeforeId() {
+    assertEquals(element1, pageBot.findElement("document.links[0]"));
+    assertEquals(element4, pageBot.findElement("id=document.links[0]"));
+}
+
+function testFindElementHandlesLinkLocators() {
+    assertEquals(element3, pageBot.findElement('link=this is the third element'));
+}
+
+function testUnknownElementWithoutDomTraversal() {
+    try {
+        pageBot.findElement("unknownElement");
+        fail("Should have failed for element not found");
+    }
+    catch (e) {
+        // expected
+    }
+}
+
+function testUnknownElementWithDomTraversal() {
+    try {
+        pageBot.findElement("document.foo");
+        fail("Should have failed for element not found");
+    }
+    catch (e) {
+        // expected
+    }
+}
+
+function testFindElementHandlesDomTraversal() {
+    assertEquals(element3, pageBot.findElement("document.links[2]"));
+}
+</script>
+</head>
+
+<body>
+<iframe id="testIframe" src="./pagebot-locator-tests-include.html"/>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-property-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-property-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pagebot-property-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>PageBot Property Tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function setUp() {
+    pageBot = BrowserBot.createForWindow(window);
+}
+
+function testTitleProperty() {
+    assertEquals("PageBot Property Tests", pageBot.getTitle());
+}
+
+function testCurrentWindowProperty() {
+  assertEquals(window, pageBot.getCurrentWindow());
+}
+
+function testBodyText() {
+    assertEquals("Selenium Browserbot Tests", pageBot.bodyText());
+}
+</script>
+  </head>
+  <body>Selenium Browserbot Tests</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pattern-matcher-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pattern-matcher-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/pattern-matcher-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>PatternMatcher tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-api.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function testRegexpFromGlob() {
+    var regexpFromGlob = PatternMatcher.regexpFromGlob;
+    assertEquals("^(.|[\r\n])*$", regexpFromGlob("*"));
+    assertEquals("^(.|[\r\n])$", regexpFromGlob("?"));
+    assertEquals("^a(.|[\r\n])*b$", regexpFromGlob("a*b"));
+    assertEquals("^\\.\\|\\^\\$\\(\\)\\[\\]\\{\\}$", regexpFromGlob(".|^$()[]{}"));
+    assertEquals("^(.|[\r\n])*/foo/bar(.|[\r\n])*$", regexpFromGlob("*/foo/bar*"));
+}
+
+function testCanMatchUsingGlob() {
+    assertTrue(new PatternMatcher("glob:a*e").matches("apple"));
+    assertFalse(new PatternMatcher("glob:a*z").matches("apple"));
+}
+
+function testMatchUsingGlobByDefault() {
+    assertTrue(new PatternMatcher("a*e").matches("apple"));
+    assertFalse(new PatternMatcher("a*z").matches("apple"));
+}
+
+function testCanMatchUsingRegexp() {
+    assertTrue(new PatternMatcher("regexp:pp").matches("apple"));
+    assertFalse(new PatternMatcher("regexp:^pp").matches("apple"));
+    assertTrue(new PatternMatcher("regexp:[a-z]$").matches("apple"));
+}
+
+function testCanDoExactMatch() {
+    assertTrue(new PatternMatcher("exact:abc").matches("abc"));
+    assertFalse(new PatternMatcher("exact:a*c").matches("abc"));
+    assertTrue(new PatternMatcher("exact:a*c").matches("a*c"));
+}
+
+</script>
+  </head>
+  <body>PatternMatcher Tests</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/remoterunner-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/remoterunner-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/remoterunner-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,212 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2007 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>RemoteRunner tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-commandhandlers.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-remoterunner.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function setUp() {
+    postResult = "START";
+    debugMode = false;
+    relayToRC = null;
+    proxyInjectionMode = false;
+    uniqueId = 'sel_654321';
+    
+    Selenium = function() {
+        return {
+            browserbot: {
+                _modifyWindow: function() {},
+                getCurrentWindow: function() { return window; },
+                runScheduledPollers: function() {}
+            },
+            reset: function() {},
+            preprocessParameter: function(arg) { return (arg); },
+            ensureNoUnhandledPopups: function() {},
+            doSpecialTestCommand: function() {},
+            getSpecialTestValue: function() { return "foo"; },
+            isSpecialTestBoolean: function() { return true; }
+        }
+    }
+    
+    Selenium.createForWindow = function() {
+        return new Selenium();
+    };
+    
+    RemoteRunnerOptions = function() {
+        return {
+            initialize: function() {},
+            isDebugMode: function() { return false; },
+            isMultiWindowMode: function() { return false; },
+            getContinue: function() { return null; },
+            getDriverUrl: function() { return "http://localhost:4444/selenium-server/driver/";},
+            getBaseUrl: function() { return "http://localhost:4444/";},
+            getSessionId: function() { return "123456"; },
+        }
+    }
+    
+    xhrs = [];
+    xhr = null;
+    
+    MockXhr = function() {};
+    MockXhr.prototype.open = function(method, url, async) {
+        if (!async) {
+            throw new Error("MockXhr can only handle asynchronous requests");
+        }
+        this.url = url;
+        this.method = method;
+    }
+    MockXhr.prototype.send = function(body) {
+        this.body = body;
+    }
+    MockXhr.prototype.respond = function(body) {
+        this.readyState = 4;
+        this.status = 200;
+        this.responseText = body;
+        this.onreadystatechange();
+        while (timeouts.length > 0) {
+            timeouts.pop().call();
+        }
+    }
+            
+    
+    XmlHttp = {
+        create: function() {
+            var xhr = new MockXhr();
+            xhrs.push(xhr);
+            return xhr;
+        }
+    }
+    
+    parseArgs = function(str) {
+        var clauses = str.split('&');
+        var result = {};
+        for (var i in result) {
+            delete result[i];
+        }
+        for (var i = 0; i < clauses.length; i++) {
+            var keyValuePair = clauses[i].split('=', 2);
+            var key = unescape(keyValuePair[0]);
+            var value = unescape(keyValuePair[1]);
+            result[key] = value;
+        }
+        return result;
+    }
+    
+    assertEvalNotNull = function(str) {
+        assertNotNull(str, eval(str));
+    }
+    
+    assertEvalEquals = function(expected, str) {
+        assertEquals(str, expected, eval(str));
+    }
+    
+    __originalSetTimeout = window.setTimeout;
+    timeouts = [];
+    setTimeout = function(arg) {
+        timeouts.push(arg);
+    }
+}
+
+function tearDown() {
+    window.setTimeout = __originalSetTimeout;
+}
+
+function testRemoteRunnerStart() {
+    runSeleniumTest();
+    assertEvalNotNull("currentTest");
+    assertEvalNotNull("currentTest.xmlHttpForCommandsAndResults");
+    xhr = currentTest.xmlHttpForCommandsAndResults;
+    assertEvalEquals("POST", "xhr.method");
+    assertEvalEquals("postedData=START", "xhr.body");
+    url = parseUrl(xhr.url);
+    args = parseArgs(url.search);
+    baseUrl = url;
+    baseUrl.search = "";
+    baseUrlStr = reassembleLocation(baseUrl);
+    assertEquals("url", "http://localhost:4444/selenium-server/driver/", baseUrlStr);
+    assertEvalEquals("123456", "args.sessionId");
+    assertEvalEquals("true", "args.seleniumStart");
+    assertEvalEquals("", "args.seleniumWindowName");
+    // DGF should we be asserting on localFrameAddress?  It seems like this might be fragile
+    assertEvalEquals("top.frames[2].frames[1]", "args.localFrameAddress");
+    assertEvalEquals("sel_654321", "args.uniqueId");
+    assertEvalNotNull("args.counterToMakeURsUniqueAndSoStopPageCachingInTheBrowser"); // DGF randomly generated
+}
+
+function testInvalidCommand() {
+    testRemoteRunnerStart();
+    xhr.respond("cmd=invalidCommand&1=&2=");
+    xhr = currentTest.xmlHttpForCommandsAndResults;
+    assertEquals("Couldn't get handle to XHR", xhr, currentTest.xmlHttpForCommandsAndResults);
+    response = parseArgs(xhr.body).postedData;
+    assertEquals("ERROR: Unknown command: 'invalidCommand'", response);
+}
+
+function testNormalCommands() {
+    testRemoteRunnerStart();
+    xhr.respond("cmd=specialTestCommand&1=&2=");
+    xhr = currentTest.xmlHttpForCommandsAndResults;
+    response = parseArgs(xhr.body).postedData;
+    assertEvalEquals("OK", "response");
+    
+    xhr.respond("cmd=getSpecialTestValue&1=&2=");
+    xhr = currentTest.xmlHttpForCommandsAndResults;
+    response = parseArgs(xhr.body).postedData;
+    assertEvalEquals("OK,foo", "response");
+    
+    xhr.respond("cmd=isSpecialTestBoolean&1=&2=");
+    xhr = currentTest.xmlHttpForCommandsAndResults;
+    response = parseArgs(xhr.body).postedData;
+    assertEvalEquals("OK,true", "response");
+}
+
+function testRetryLast() {
+    testRemoteRunnerStart();
+    xhr.respond("cmd=retryLast&1=&2=");
+    
+    xhr = currentTest.xmlHttpForCommandsAndResults;
+    url = parseUrl(xhr.url);
+    args = parseArgs(url.search);
+    assertEvalEquals("true", "args.retry");
+    response = parseArgs(xhr.body).postedData;
+    assertEvalEquals("RETRY", "response");
+    xhr.respond("cmd=retryLast&1=&2=");
+
+    xhr = currentTest.xmlHttpForCommandsAndResults;
+    url = parseUrl(xhr.url);
+    args = parseArgs(url.search);
+    assertEvalEquals("true", "args.retry");
+    response = parseArgs(xhr.body).postedData;
+    assertEvalEquals("RETRY", "response");
+}
+
+</script>
+  </head>
+  <body>RemoteRunner Tests</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/selenium-api-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/selenium-api-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/selenium-api-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,384 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>BrowserBot tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+
+<script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-api.js"></script>
+<script language="JavaScript" type="text/javascript">
+function setUp() {
+    mockPageBot = mockBrowserBot = new Mock();
+
+    selenium = new Selenium(mockBrowserBot);
+}
+
+function verifyMocks() {
+    mockBrowserBot.verify();
+    mockPageBot.verify();
+}
+
+// Tests for Element actions
+function testClickElementWithoutCallback() {
+    mockPageBot.expects("findElement", "id").returns("elementToClick");
+    mockPageBot.expects("clickElement", "elementToClick");
+
+    selenium.doClick("id");
+    verifyMocks();
+}
+
+function testType() {
+    mockPageBot.expects("findElement", "id").returns("elementToType");
+    mockPageBot.expects("replaceText", "elementToType", "new text");
+
+    selenium.doType("id", "new text");
+    verifyMocks();
+}
+
+function testSelect() {
+    var mockOptionLocatorFactory = new Mock();
+    selenium.optionLocatorFactory = mockOptionLocatorFactory;
+    var mockSelect = new Mock();
+    // The doSelect() method checks the options property exists as a way
+    // of ensuring that the element is a Select element.  Hence the following expectation.
+    mockSelect.expectsProperty("options").returns("some options");
+    mockPageBot.expects("findElement", "id").returns(mockSelect);
+    var mockOptionLocator = new Mock();
+    mockOptionLocatorFactory.expects("fromLocatorString", "Option One").returns(mockOptionLocator);
+    var option = new Object();
+    mockOptionLocator.expects("findOption", mockSelect).returns(option);
+    mockPageBot.expects("selectOption", mockSelect, option);
+
+    selenium.doSelect("id", "Option One");
+    mockOptionLocatorFactory.verify();
+    mockOptionLocator.verify();
+    mockSelect.verify();
+    verifyMocks();
+}
+
+// Browser actions
+function testOpen() {
+    mockBrowserBot.expects("openLocation", "new/location");
+
+    selenium.doOpen("new/location");
+    verifyMocks();
+}
+
+function testSelectWindow() {
+    mockBrowserBot.expects("selectWindow", "windowName");
+
+    selenium.doSelectWindow("windowName");
+    verifyMocks();
+}
+
+function testGetLocation() {
+    var mockWindow = new Object();
+    mockWindow.location = "http://page/path?foo=bar";
+    mockPageBot.getCurrentWindow = function() {
+        return mockWindow;
+    }
+
+    assertTrue(selenium.getLocation().indexOf("path") > 0);
+    assertTrue(selenium.getLocation().indexOf("page/path") > 0);
+    assertTrue(selenium.getLocation().indexOf("http://page/path?foo=bar") > -1);
+
+}
+
+function testGetTitleReturnsTheTitle() {
+    mockPageBot.expects("getTitle").returns("foo");
+    assertEquals("foo", selenium.getTitle());
+    verifyMocks();
+}
+
+function testGetValueOfText() {
+    var mockTextControl = Object();
+    mockTextControl.type = "TEXT";
+    mockTextControl.value = "the value";
+    mockPageBot.expects("findElement", "id").returns(mockTextControl);
+
+    assertEquals("the value", selenium.getValue("id"));
+    verifyMocks();
+}
+
+function testGetValueOfCheckbox() {
+    var mockControl = Object();
+    mockControl.type = "CHECKBOX";
+    mockControl.value = "the value";
+    mockControl.checked = true;
+    mockPageBot.expects("findElement", "id").returns(mockControl);
+
+    assertEquals("on", selenium.getValue("id"));
+    verifyMocks();
+}
+
+function testGetText() {
+    var element = document.getElementById("testGetText")
+    mockPageBot.expects("findElement", "id").returns(element);
+    assertEquals("foo", selenium.getText("id"));
+    verifyMocks();
+}
+
+function textCell(val) {
+   var cell = new Object();
+   cell.textContent = val;
+   return cell;
+}
+
+function testGetTableSuccess() {
+    mockPageBot.expects("findElement", "table").returns(getMockTable());
+    assertEquals("buz", selenium.getTable("table.1.1"));
+    verifyMocks();
+}
+
+function testGetTableInvalidLocator() {
+    assertCallErrors("VerifyTable should have failed for invalid locator",
+                    function() {selenium.getTable("foo");},
+                    "Invalid target format. Correct format is tableName.rowNum.columnNum");
+    verifyMocks();
+}
+
+function testGetTableNoSuchRow() {
+    var mockTable = getMockTable();
+    mockPageBot.expects("findElement", "table").returns(mockTable);
+    assertCallFails("VerifyTable should have failed for no such row",
+                    function() {selenium.getTable("table.11.0", "bar");},
+                    "Cannot access row 11 - table has 2 rows");
+    verifyMocks();
+}
+
+function testGetTableNoSuchColumn() {
+    var mockTable = getMockTable();
+    mockPageBot.expects("findElement", "table").returns(mockTable);
+    assertCallFails("VerifyTable should have failed for no such column",
+                    function() {selenium.getTable("table.0.11", "bar");},
+                    "Cannot access column 11 - table row has 2 columns");
+    verifyMocks();
+}
+
+function getMockTable() {
+    return document.getElementById("test_table")
+}
+
+function testTextPresent() {
+    mockPageBot.expects("bodyText").returns("this is some foo text");
+    mockPageBot.expects("bodyText").returns("this is some foo text");
+    assertTrue(selenium.isTextPresent("foo"));
+    assertFalse(selenium.isTextPresent("bar"));
+    verifyMocks();
+}
+
+function testElementPresent() {
+    mockPageBot.expects("findElementOrNull", "id").returns("foo");
+    assertTrue(selenium.isElementPresent("id"));
+    verifyMocks();
+}
+
+function testElementNotPresent() {
+    mockPageBot.expects("findElementOrNull", "id").returns(null);
+    assertFalse(selenium.isElementPresent("id"));
+    verifyMocks();
+}
+
+function testGetAllButtonsShouldCallPageBot() {
+    mockPageBot.expects("getAllButtons").returns("foo");
+    selenium.getAllButtons();
+    verifyMocks();
+}
+
+function testGetAllFieldsShouldCallPageBot() {
+    mockPageBot.expects("getAllFields").returns("foo");
+    selenium.getAllFields();
+    verifyMocks();
+}
+
+function testGetAllLinksShouldCallPageBot() {
+    mockPageBot.expects("getAllLinks").returns("foo");
+    selenium.getAllLinks();
+    verifyMocks();
+}
+
+function testShouldFailIfTryToGetAlertWhenThereAreNone() {
+    mockBrowserBot.expects("hasAlerts").returns(false);
+
+    assertCallFails("getAlert should have failed",
+                    function() {selenium.getAlert(); },
+                    "There were no alerts");
+
+    verifyMocks();
+}
+
+function testGetAlertReturnsNextAlert() {
+    mockBrowserBot.expects("hasAlerts").returns(true);
+    mockBrowserBot.expects("getNextAlert").returns("The real alert");
+
+    assertEquals("The real alert", selenium.getAlert());
+
+    verifyMocks();
+}
+
+function testShouldFailIfTryToVerifyConfirmationWhenThereAreNone() {
+    mockBrowserBot.expects("hasConfirmations").returns(false);
+
+    assertCallFails("verifyConfirmation should have failed",
+                    function() {selenium.getConfirmation();},
+                    "There were no confirmations");
+
+    verifyMocks();
+}
+
+function testGetConfirmationReturnsNextConfirmation() {
+      mockBrowserBot.expects("hasConfirmations").returns(true);
+      mockBrowserBot.expects("getNextConfirmation").returns("The real confirmation");
+
+     assertEquals("The real confirmation", selenium.getConfirmation());
+
+     verifyMocks();
+}
+
+function testShouldTellBroswerBotIfAskedToCancelNextConfirmation() {
+     mockBrowserBot.expects("cancelNextConfirmation", false);
+     selenium.doChooseCancelOnNextConfirmation();
+     verifyMocks();
+}
+
+function testIsSelectedSuccess() {
+    var mockTextControl = Object();
+    mockTextControl.selectedIndex = 1;
+
+    mockTextControl.options = [{text: "option0"},{text: "option1", selected:true},{text: "option2"}];
+    mockPageBot.expects("findElement", "id=option1").returns(mockTextControl);
+    assertTrue(selenium.isSomethingSelected("id=option1"));
+    verifyMocks();
+
+    mockPageBot.expects("findElement", "id=option2").returns({});
+    try {
+        selenium.isSomethingSelected("id=option2");
+        fail();
+    }
+    catch (e)
+    {
+        // pass
+    }
+    verifyMocks();
+
+}
+
+function testGetSelectOptions() {
+    var mockTextControl = Object();
+    mockTextControl.options = [{text: "option0"},{text: "option1"},{text: "option2"}];
+    mockPageBot.expects("findElement", "id").returns(mockTextControl);
+
+    assertObjectEquals(["option0","option1","option2"], selenium.getSelectOptions("id"));
+    verifyMocks();
+}
+
+function testGetSelectOptionsWithCommasEscaped() {
+    var mockTextControl = Object();
+    mockTextControl.options = [{text: "option,0"},{text: "option.1"}];
+    mockPageBot.expects("findElement", "id").returns(mockTextControl);
+
+    assertObjectEquals(["option,0","option.1"], selenium.getSelectOptions("id"));
+    verifyMocks();
+}
+
+function testGetAttributeWithId() {
+    mockPageBot.expects("findAttribute", "id at attribute").returns("foo");
+
+    assertEquals("foo", selenium.getAttribute("id at attribute"));
+    verifyMocks();
+}
+
+function assertCallFails(message, theCall, expectedFailureMessage) {
+    try {
+        theCall();
+    } catch (e) {
+        if (!e.isAssertionFailedError) {
+            throw e;
+        }
+        if (expectedFailureMessage) {
+            assertEquals(expectedFailureMessage, e.failureMessage);
+        }
+        return;
+    }
+    fail(message);
+}
+function assertCallErrors(message, theCall, expectedFailureMessage) {
+    try {
+        theCall();
+    } catch (e) {
+        if (expectedFailureMessage) {
+            assertEquals(expectedFailureMessage, e.message);
+        }
+        return;
+    }
+    fail(message);
+}
+
+function testEnsureNoUnhandledPopupsThrowsExceptionIfAlertPresent() {
+
+     mockBrowserBot.expects("hasAlerts").returns(true);
+     mockBrowserBot.expects("getNextAlert").returns("The Alert");
+
+     try {
+        selenium.ensureNoUnhandledPopups();
+        fail("exception expected");
+     } catch (e) {
+        assertTrue(e.isSeleniumError);
+        assertEquals("There was an unexpected Alert! [The Alert]", e.message);
+     }
+
+     mockBrowserBot.verify();
+}
+
+function testShouldFailActionCommandsIfConfirmPresent() {
+
+     mockBrowserBot.expects("hasAlerts").returns(false);
+     mockBrowserBot.expects("hasConfirmations").returns(true);
+     mockBrowserBot.expects("getNextConfirmation").returns("The Confirmation");
+
+     try {
+        selenium.ensureNoUnhandledPopups();
+        fail("exception expected");
+     } catch (e) {
+        assertTrue(e.isSeleniumError);
+        assertEquals("There was an unexpected Confirmation! [The Confirmation]", e.message);
+     }
+
+     mockBrowserBot.verify();
+}
+</script>
+  </head>
+  <body>Selenium API Tests
+  <span id="testGetText"> foo </span>
+
+  <table id="test_table">
+    <tr><td>foo</td><td>bar</td></tr>
+    <tr><td>fuz</td><td>buz</td></tr>
+  </table>
+  </body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/selenium-parameter-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/selenium-parameter-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/selenium-parameter-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>BrowserBot tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-api.js"></script>
+<script language="JavaScript" type="text/javascript">
+function setUp() {
+    mockPageBot = new Mock();
+
+    mockBrowserBot = new Mock();
+
+    selenium = new Selenium(mockBrowserBot);
+
+    LOG = new DummyLogger();
+}
+
+function testHandleSimpleStrings() {
+    assertEquals('', selenium.preprocessParameter(''));
+    assertEquals(' ', selenium.preprocessParameter(' '));
+    assertEquals('ABC', selenium.preprocessParameter('ABC'));
+    assertEquals('1234567890!@#$%^&*()', selenium.preprocessParameter('1234567890!@#$%^&*()'));
+}
+
+function testSimpleVariableSubstitution() {
+    storedVars['var'] = 'value';
+    storedVars['var2'] = 'another value';
+    assertEquals('_value_', selenium.preprocessParameter('_${var}_'));
+    assertEquals('_value value_', selenium.preprocessParameter('_${var} ${var}_'));
+    assertEquals('_value another value_', selenium.preprocessParameter('_${var} ${var2}_'));
+}
+
+function testUnkownVariableNotSubstituted() {
+    storedVars['var'] = 'value';
+    assertEquals('_${bar}_',
+                 selenium.preprocessParameter('_${bar}_'));
+    assertEquals('_${bar} value_',
+                 selenium.preprocessParameter('_${bar} ${var}_'));
+    assertEquals('_value ${bar} ${bar} value ${bar}_',
+                 selenium.preprocessParameter('_${var} ${bar} ${bar} ${var} ${bar}_'));
+}
+
+function testSimpleJavascriptEvaluation() {
+    assertEquals('25', selenium.preprocessParameter('javascript{"2" + "5"}'));
+    assertEquals('25', selenium.preprocessParameter('javascript{5 * 5}'));
+}
+
+function testVariableSubstitutionDoesntApplyForJavascriptParameters() {
+    assertEquals(' ${foo} ', selenium.preprocessParameter('javascript{" ${foo} "}'));
+}
+
+function testCanAccessStoredVariablesFromJavascriptParameters() {
+    storedVars['var'] = 'value';
+    assertEquals(' value ', selenium.preprocessParameter('javascript{" " + storedVars["var"] + " "}'));
+}
+
+function testJavascriptEvaluationOfEmptyBody() {
+    assertEquals('javascript{}', selenium.preprocessParameter('javascript{}'));
+}
+
+function testIllegalJavascriptParameter() {
+    try {
+        selenium.preprocessParameter('javascript{foo}');
+    } catch (e) {
+        return;
+    }
+    fail("call should have failed");
+}
+</script>
+  </head>
+  <body>Selenium API Tests</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/string-override-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/string-override-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/string-override-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Assert tests</title>
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="stringOverride.js"></script>
+
+    <script language="JavaScript" type="text/javascript">
+
+        function testShouldAbleToOverrideStringDefinitionWithinJsFile() {
+            assertEquals("Hello, abc", "abc".sayHello());
+        }
+
+    </script>
+</head>
+
+<body>Assert Tests</body>
+</html>s

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/stringOverride.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/stringOverride.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/stringOverride.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,3 @@
+String.prototype.sayHello = function() {
+    return "Hello, " + this;
+}
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/suite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/suite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/suite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Selenium Browserbot Test Suite</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/xbDebug.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitTestSuite.js"></script>
+    <script language="Javascript" type="text/javascript">
+       function suite() {
+           var testSuite = new top.jsUnitTestSuite();
+           testSuite.addTestPage("../unittest/browserbot/browserbot-frame-finder-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/alert-handling-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/browserbot-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/command-factory-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/command-handler-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/confirm-handling-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/error-checking-command-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/event-bubble-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/pattern-matcher-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/assert-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/optionlocator-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/pagebot-accessor-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/pagebot-action-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/pagebot-attribute-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/pagebot-locator-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/pagebot-property-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/selenium-api-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/selenium-parameter-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/testloop-handle-error-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/text-content-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/htmlutil-tests.html");
+           testSuite.addTestPage("../unittest/browserbot/remoterunner-tests.html");
+           return testSuite;
+       }
+    </script>
+  </head>
+  <body>Selenium Browserbot Test Suite</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/testloop-handle-error-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/testloop-handle-error-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/testloop-handle-error-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css">
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-api.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserbot.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-testrunner.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-commandhandlers.js"></script>
+    <script language="JavaScript" type="text/javascript">
+        function setUp() {
+            commandFactory = new Mock();
+            // DGF We want to do assertions on the LOG, but only after setup
+            LOG = { info: function() {} };
+            
+            selenium = new Selenium();
+            global = new Object();
+            htmlTestCase = new Object();
+            htmlTestCase.reset = function() {
+            };
+            htmlTestCase.testDocument = document;
+            seleniumTest = new HtmlRunnerTestLoop(htmlTestCase, false, commandFactory);
+            // We want to verify that testLoop.commandError() function is called appropriately
+            // by testLoop.handleCommandError()
+            LOG = new Mock();   
+            commandErrorHandler = new Mock();
+            seleniumTest.commandError = function(message) {
+                commandErrorHandler.handleError(message);
+            };
+        };
+
+        function verifyMocks() {
+            commandFactory.verify();
+            LOG.verify();
+            commandErrorHandler.verify();
+        }
+
+        function testOrdinaryCommandError() {
+            var error = new SeleniumError("Test Error");
+            LOG.expects("error", "Test Error");
+            commandErrorHandler.expects("handleError", "Test Error");
+            seleniumTest._handleCommandError(error);
+        };
+
+        function testSeleniumMalfunction() {
+            var error = new Error("Test Error");
+            LOG.expects("exception", error);
+            commandErrorHandler.expects("handleError", "Selenium failure. Please report to the Selenium Users forum at http://forums.openqa.org, with error details from the log window.  The error message is: Test Error");
+            seleniumTest._handleCommandError(error);
+        };
+
+    </script>
+</head>
+
+<body>
+<a href="#" id="testLink">Test Link</a>
+
+<p>
+    This page contains tests for the SeleniumTest : handleCommandError()
+    To see them, take a look at the source. To run them, load this file via JsUnit's testRunner.html
+</p>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/text-content-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/text-content-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/browserbot/text-content-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,174 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+  <title>JsUnit Utility Tests</title>
+  <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css">
+  <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+  <script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+  <script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+  <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+  <script language="JavaScript" type="text/javascript">
+      function testEmpty()
+      {
+          assertText("", "empty");
+      }
+      function testSpaces()
+      {
+          assertText("1 1 1", "one_space");
+          assertText("2 2  2", "two_spaces");
+          assertText("3 3   3  3   3", "three_spaces");
+          assertText("1 tab2 tabs", "tabs");
+      }
+
+      function testNonTextMarkup()
+      {
+          assertText("There is non-visible and visible markup here that doesn't change the text content", "non_text_markup");
+          assertText("There is an image here and an image link", "image_in_text");
+      }
+
+      function testNonVisibleNewlinesInText()
+      {
+          assertText("non visible newlines", "non_visible_newlines");
+      }
+
+      function testInlineElementsForcingNewlines()
+      {
+          assertText("<br/>\nnewline", "br");
+          // TODO we should probably always return a single (or double) newline
+          assertText("<hr/>\n+newline", "hr");
+      }
+
+      function testBlockElementsForcingNewlines()
+      {
+          // TODO we should probably always return a single (or double) newline
+          assertText("First paragraph\n+Second paragraph", "paragraphs");
+          assertText("First div\n+Second div", "divs");
+      }
+
+      function testPreformattedNewlines()
+      {
+          // TODO What other Html tags support preformatted newlines?
+          assertText("preformatted\nnewline", "preformatted");
+      }
+
+      function assertText(pattern, elementId)
+      {
+          var text = getText(document.getElementById(elementId));
+          var re = new RegExp(pattern);
+
+          assertTrue(text + "\n does not match pattern \n'" + pattern.replace(/\n/g, "\\n") + "'",
+                  re.test(text));
+      }
+
+      function assertTextContent(expectedTextContent, elementId) {
+          var actualTextContent = normalizeNewlines(getTextContent(document.getElementById(elementId)));
+          assertEquals(expectedTextContent, actualTextContent);
+      }
+
+      function testTextContentOfEmptyElementIsBlankString() {
+          assertTextContent("", 'empty');
+      }
+
+      function testTextContentOfSimpleElementIsContainedText() {
+          assertTextContent("abc", 'simple');
+      }
+
+      function testTextContentIncludesContentOfChildElements() {
+          assertTextContent("this is not simple", 'not_simple');
+      }
+
+      function testTextContentOfPreElementIncludesNewlines() {
+          assertTextContent("preformatted block\n", 'pre');
+      }
+
+      function testTextContentOfElementsWithinPreElementIncludesNewlines() {
+          assertTextContent("preformatted block with nested\nbold tag\n", 'pre_with_nested_tags');
+      }
+
+      function testTextContentOfBlockElementsIncludesTrailingNewline() {
+          assertTextContent("this is a paragraph\n", 'p_element');
+          assertTextContent("\n", 'br_element');
+          assertTextContent("\n", 'hr_element');
+          assertTextContent("this is a div\n", 'div_element');
+      }
+  </script>
+</head>
+
+<body>
+
+<div id="one_space">1 1&nbsp;1</div>
+<div id="two_spaces">2  2&nbsp;&nbsp;2</div>
+<div id="three_spaces">3   3&nbsp;&nbsp;&nbsp;3  &nbsp;3 &nbsp;&nbsp;3</div>
+<div id="tabs">1	tab2		tabs</div>
+<div id="non_text_markup">
+  There is <span>non-visible</span> and <a href="javascript:void()"><strong><em>visible</em></strong></a> markup here that <!-- comment -->doesn't change the text content
+</div>
+<div id="image_in_text">
+  There is <img alt="alt text" src="../../core/selenium-logo.png"/>an image here and an <a href="javascript:void()"><img src="../../core/selenium-logo.png" alt="alt text"/>image link</a>
+</div>
+<div id="non_visible_newlines">non
+<span>visible
+newlines</span>
+</div>
+<div id="br">
+  &lt;br/><br/>newline
+</div>
+<div id="hr">
+  &lt;hr/><hr/>newline
+</div>
+<div id="paragraphs">
+  <p>First paragraph</p><p>Second paragraph</p>
+</div>
+<div id="divs">
+  <div>First div</div><div>Second div</div>
+</div>
+<div id="preformatted">
+  <pre>preformatted
+newline</pre>
+</div>
+
+<hr/>
+
+<span id="empty"></span>
+
+<span id="simple">abc</span>
+
+<span id="not_simple">this is <b>not</b> simple</span>
+
+<pre id="pre">
+preformatted block
+</pre>
+
+<pre id="pre_with_nested_tags">
+preformatted block with <b>nested
+bold tag</b>
+</pre>
+
+<p id="p_element">this is a paragraph</p>
+
+<hr id="hr_element"/>
+<br id="br_element"/>
+<div id="div_element">this is a div</div>
+
+
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/dummy-logging.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/dummy-logging.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/dummy-logging.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,15 @@
+
+function noop() {};
+
+var DummyLogger = function() {};
+DummyLogger.prototype = {
+    show: noop,
+    log: noop,
+    debug: noop,
+    info: noop,
+    warn: noop,
+    error: noop,
+    exception: noop
+};
+
+var LOG = new DummyLogger();
\ No newline at end of file

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestcase-row-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestcase-row-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestcase-row-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>JsUnit Utility Tests</title>
+<link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-commandhandlers.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+<script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-testrunner.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function setUp() {
+    $("bodyid").innerHTML = "<table><tr id='commandRow'><td>theCommand</td> <td>theTarget</td><td>theValue</td></tr></table>";
+    htmlTrElement = $("commandRow");
+    htmlTestCaseRow = new HtmlTestCaseRow(htmlTrElement);
+    htmlTestCaseRow.reset();
+}
+
+function testCanGetSeleniumCommandFromCommandRow() {
+    var command = htmlTestCaseRow.getCommand();
+    assertEquals("theCommand", command.command);
+    assertEquals("theTarget", command.target);
+    assertEquals("theValue", command.value);
+}
+
+function testSelectAddSelectedClass() {
+    htmlTestCaseRow.select();
+    assertEquals("selected", htmlTrElement.className);
+}
+
+function testSelectWillScrollRowToView() {
+    var scrolled = false;
+    safeScrollIntoView = function(element) {
+        scrolled = true;
+        assertEquals(htmlTrElement, element);
+    };
+    htmlTestCaseRow.select();
+    assert(scrolled);
+}
+
+function testMarkPassedAddsPassedClass() {
+    htmlTestCaseRow.markPassed();
+    assertEquals("status_passed", htmlTrElement.className);
+}
+
+function testMarkDoneAddsDoneClass() {
+    htmlTestCaseRow.markDone();
+    assertEquals("status_done", htmlTrElement.className);
+}
+
+function testSetFailedAddsFailedClassAndErrorMessage() {
+    htmlTestCaseRow.markFailed("an error message");
+    assertEquals("status_failed", htmlTrElement.className);
+    assertEquals("an error message", getText(htmlTestCaseRow.trElement.cells[2]));
+}
+
+function testEchoWillSetMessageToLastCell() {
+    htmlTestCaseRow.setMessage("some echo message");
+    assertEquals("some echo message", getText(htmlTrElement.cells[2]));
+}
+
+function testResetWillClearStatusAndErrorMessage() {
+    htmlTestCaseRow.markFailed("some error");
+    htmlTestCaseRow.reset();
+    assertEquals('', htmlTrElement.className);
+    assertEquals("theValue", getText(htmlTrElement.cells[2]));
+}
+
+function testAddBreakpointSupportBindRowsOnClickToElementsClickHandler() {
+    assertTrue(htmlTrElement.onclick == null);
+    htmlTestCaseRow.addBreakpointSupport();
+    assertNotNull(htmlTrElement.onclick);
+    htmlTestCaseRow.onClickCalled = false;
+    htmlTestCaseRow.onClick = function() {
+        this.onClickCalled = true;
+    };
+    htmlTrElement.onclick();
+    assertTrue(htmlTestCaseRow.onClickCalled);
+}
+
+function testOnClickShouldToggleBreakpoint() {
+    assertFalse(htmlTestCaseRow.isBreakpoint());
+    assertEquals("", htmlTrElement.className);
+
+    htmlTestCaseRow.onClick();
+
+    assertTrue(htmlTestCaseRow.isBreakpoint());
+    assertEquals("breakpoint", htmlTrElement.className);
+
+    htmlTestCaseRow.onClick();
+
+    assertFalse(htmlTestCaseRow.isBreakpoint());
+    assertEquals("", htmlTrElement.className);
+}
+
+function testAddBreakpointSupportShouldMakeRowCursorSmallHand() {
+    var defaultStyle = Element.getStyle(htmlTrElement, "cursor");
+    // on opera, the default style is 'default'
+    // on other browsers which is null
+    assert(defaultStyle == null || defaultStyle == 'default');
+    htmlTestCaseRow.addBreakpointSupport();
+    assertEquals("pointer", Element.getStyle(htmlTrElement, "cursor"));
+}
+
+function assertColorEquals(leftColor, rightColor) {
+    assertEquals(normalizeColor(leftColor), normalizeColor(rightColor));
+}
+
+function normalizeColor(aColor) {
+    var dummyBody = document.createElement("body");
+    Element.setStyle(dummyBody, {"background-color":aColor});
+    return Element.getStyle(dummyBody, "background-color");
+}
+
+
+</script>
+</head>
+
+<body id="bodyid">
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestcase-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestcase-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestcase-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,120 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-commandhandlers.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-testrunner.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+    <script language="JavaScript" type="text/javascript">
+
+        function setUp() {
+            mockSuiteRow = new Mock();
+            testcase = new HtmlTestCase(document, mockSuiteRow);
+        }
+
+        function testGetCommandRowsShouldReturnHtmlTestCaseRows() {
+            var rows = testcase.getCommandRows();
+            assertEquals(2, rows.length);
+            assertEquals("open", rows[0].getCommand().command);
+            assertEquals("verifyLocation", rows[1].getCommand().command);
+        }
+
+        function testAddBreakpointSupportAddsBreakpointSupportToAllCommandRows() {
+            HtmlTestCaseRow.prototype.addBreakpointSupport = function () {
+                this.clickHandlerAdded = true;
+            };
+            testcase._addBreakpointSupport();
+            assert(testcase.getNextCommandRow().clickHandlerAdded);
+            assert(testcase.getNextCommandRow().clickHandlerAdded);
+        }
+
+        function testGetNextCommandShouldGetCommandInOrder() {
+            assertEquals("open", testcase.getNextCommandRow().getCommand().command);
+            assertEquals("verifyLocation", testcase.getNextCommandRow().getCommand().command);
+        }
+
+        function testHasMoreCommandRowsShouldBeFalseAtEndOfTest() {
+            assertTrue(testcase.hasMoreCommandRows());
+            testcase.getNextCommandRow();
+            assertTrue(testcase.hasMoreCommandRows());
+            testcase.getNextCommandRow();
+            assertFalse(testcase.hasMoreCommandRows());
+        }
+
+        function testAfterResetGetNextCommandShouldReturnTheFirstCommandAgain() {
+            testcase.getNextCommandRow();
+            testcase.getNextCommandRow();
+            testcase.reset();
+            assertEquals("open", testcase.getNextCommandRow().getCommand().command);
+            assertTrue(testcase.hasMoreCommandRows());
+        }
+
+        function testGetNextCommandShouldReturnNullAtEndOfTest() {
+            testcase.getNextCommandRow();
+            testcase.getNextCommandRow();
+            assertFalse(testcase.hasMoreCommandRows());
+            assertNull(testcase.getNextCommandRow());
+        }
+
+        function testMarkFailedShouldMarkSuiteRowFailed() {
+            mockSuiteRow.expects('markFailed')
+            testcase.markFailed();
+            mockSuiteRow.verify();
+        }
+
+        function testMarkPassedShouldMarkSuiteRowPassed() {
+            mockSuiteRow.expects('markPassed')
+            testcase.markPassed();
+            mockSuiteRow.verify();
+        }
+
+    </script>
+</head>
+
+<body>
+<table cellpadding="1" cellspacing="1" border="1" id="testTable">
+    <tbody>
+        <tr>
+            <td rowspan="1" colspan="3">Test Open<br>
+            </td>
+        </tr>
+        <tr id="openRow">
+            <td>open</td>
+            <td>../tests/html/test_open.html</td>
+            <td>&nbsp;</td>
+        </tr>
+        <tr><td colspan="3">this is a comment</td></tr>
+        <tr id="verifyRow">
+            <td>verifyLocation</td>
+            <td>*/tests/html/test_open.html</td>
+            <td>&nbsp;</td>
+        </tr>
+    </tbody>
+</table>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestframe-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestframe-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestframe-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-commandhandlers.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-testrunner.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+    <script language="JavaScript" type="text/javascript">
+
+        function setUp() {
+            testFrame = new HtmlTestFrame($("testframe"));
+        }
+
+        function testInitialization() {
+            assertEquals($("testframe"), testFrame.frame);
+        }
+
+    </script>
+</head>
+
+<body>
+<iframe id="testframe"></iframe>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestsuite-row-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestsuite-row-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestsuite-row-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-commandhandlers.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-testrunner.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../dummy-logging.js"></script>
+    <script language="JavaScript" type="text/javascript">
+
+        var MockSeleniumFrame = Class.create();
+        Object.extend(MockSeleniumFrame.prototype, {
+            initialize: function() {
+                this.fireLoading = false;
+            },
+            load: function() {
+                this.location = arguments[0];
+                if (this.fireLoading) this.loadListener();
+            },
+            addLoadListener: function(listener) {
+                this.loadListener = listener;
+            }
+        });
+
+        function setUp() {
+            mockFrame = new MockSeleniumFrame();
+            mockHtmlTestSuite = {};
+            suiteRow = new HtmlTestSuiteRow($("trRow"), mockFrame, mockHtmlTestSuite);
+        }
+
+        function testShouldLoadTestCaseWhenRowElementsClick() {
+            assertNotUndefined($("link").onclick);
+            suiteRow.testCaseLoaded = false;
+            suiteRow.loadTestCase = function() {
+                suiteRow.testCaseLoaded = true;
+            }
+            // testing onclick method must return false to prevent event spreading
+            assertFalse($("link").onclick());
+            assert(suiteRow.testCaseLoaded);
+        }
+
+        function testLoadTestCaseShouldChangeTestFramesLocation() {
+            mockFrame.fireLoading = false;
+            mockHtmlTestSuite.currentRowUnselected = false;
+            mockHtmlTestSuite.unselectCurrentRow = function() {
+                this.currentRowUnselected = true;
+            };
+            assertUndefined(mockFrame.location);
+            suiteRow.loadTestCase();
+            assertEquals($("link").href, mockFrame.location);
+            assert(mockHtmlTestSuite.currentRowUnselected);
+        }
+
+        function testMarkFailedShouldMarkTrElementFailed() {
+            suiteRow.markFailed();
+            assertEquals("status_failed", $("trRow").className);
+        }
+
+        function testMarkPassedShouldMarkTrElementPassed() {
+            $("trRow").bgColor = "";
+            suiteRow.markPassed();
+            assertEquals("status_passed", $("trRow").className);
+        }
+
+        function testSelectShouldMarkTrElementSelected() {
+            $("trRow").bgColor = "";
+            suiteRow.select();
+            assertEquals("selected", $("trRow").className);
+        }
+
+    </script>
+</head>
+
+<body>
+<table id="suiteTable" cellpadding="1" cellspacing="1" border="1" class="selenium">
+    <tbody>
+        <tr id="trRow"><td><a id="link" href="test1.html">test1</a></td></tr>
+        <tr id="trRow2"><td><a id="link" href="test2.html">test2</a></td></tr>
+    </tbody>
+</table>
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestsuite-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestsuite-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/htmltestsuite-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../jsmock/mock.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/lib/prototype.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/htmlutils.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-commandhandlers.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-browserdetect.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-executionloop.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../core/scripts/selenium-testrunner.js"></script>
+    <script language="JavaScript" type="text/javascript">
+        function setUp() {
+            //mock up global testFrame
+            testFrame = new Object();
+            testsuite = new HtmlTestSuite(document);
+        }
+
+        function testShouldAbleToParseTestSuiteRows() {
+            var suiteRows = testsuite.getSuiteRows();
+            assertEquals(2, suiteRows.length);
+            assertEquals($("link0"), suiteRows[0].link);
+            assertEquals(testFrame, suiteRows[0].testFrame);
+            assertEquals($("link1"), suiteRows[1].link);
+            assertEquals(testFrame, suiteRows[1].testFrame);
+        }
+
+        function testSetFailedShouldChangeTitleRowBgColorToFailedColor() {
+            $("titleRow").bgColor = "";
+            testsuite.markFailed();
+            assertContains("failed", $("titleRow").className);
+        }
+
+        function testMarkDoneShouldMarkSuitePassed() {
+            testsuite.markDone();
+            assertContains("status_passed", $("titleRow").className);
+        }
+
+        function testMarkDoneShouldLeaveSuiteMarkedFailed() {
+            testsuite.markFailed();
+            testsuite.markDone();
+            assertContains("status_failed", $("titleRow").className);
+            assertTrue(
+                    "should not be marked passed",
+                    $("titleRow").className.indexOf("status_passed") == -1
+                    );
+        }
+
+        function testResetShouldClearFailedFlag() {
+            testsuite.failed = true;
+            testsuite.reset();
+            assertFalse(testsuite.failed);
+        }
+
+        function testGetCurrentRowShouldReturnCurrentRow() {
+            testsuite.currentRowInSuite = 0;
+            assertEquals($('row1'), testsuite.getCurrentRow().trElement);
+            testsuite.currentRowInSuite = 1;
+            assertEquals($('row2'), testsuite.getCurrentRow().trElement);
+        }
+
+    </script>
+</head>
+
+<body>
+
+<table cellpadding="1"
+       cellspacing="1"
+       border="1">
+    <tbody>
+        <tr id="titleRow"><td><B>Test Suite Title</B></td></tr>
+        <tr id="row1"><td><a id='link0' href="./TestFormAutocomplete.html">Test Form Auto-complete</a></td></tr>
+        <tr id="row2"><td><a id='link1' href="./TestPopupWindow.html">Test Popup Window</a></td></tr>
+    </tbody>
+</table>
+
+
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/suite.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/suite.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/htmlrunner/suite.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Selenium Browserbot Test Suite</title>
+    <link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/xbDebug.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+    <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitTestSuite.js"></script>
+    <script language="Javascript" type="text/javascript">
+       function suite() {
+           var testSuite = new top.jsUnitTestSuite();
+           testSuite.addTestPage("../unittest/htmlrunner/htmltestcase-row-tests.html");
+           testSuite.addTestPage("../unittest/htmlrunner/htmltestcase-tests.html");
+           testSuite.addTestPage("../unittest/htmlrunner/htmltestsuite-tests.html");
+           testSuite.addTestPage("../unittest/htmlrunner/htmltestsuite-row-tests.html");
+           testSuite.addTestPage("../unittest/htmlrunner/htmltestframe-tests.html");
+           return testSuite;
+       }
+    </script>
+  </head>
+  <body>Selenium Browserbot Test Suite</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/jsmock/mock-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/jsmock/mock-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/jsmock/mock-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,219 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>JsMock Tests</title>
+<link rel="stylesheet" type="text/css" href="../../jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="mock.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function testCallingExpectedFunctionShouldPass() {
+    var myMock = new Mock()
+    myMock.expects("aslak")
+    myMock.aslak()
+    myMock.verify()
+}
+
+function testAccessingExpectedPropertyShouldPass() {
+    var myMock = new Mock()
+    myMock.expectsProperty("hello").returns("world")
+    assertEquals("world", myMock.hello)
+}
+
+function testAccessingExpectedPropertyWithObjectShouldPass() {
+    var myMock = new Mock()
+    ob = [1,2]
+    myMock.expectsProperty("hello").returns(ob)
+    assertEquals(ob, myMock.hello)
+}
+
+function testCallingUnexpectedFunctionShouldFail() {
+    var myMock = new Mock();
+    try {
+        myMock.someMethod();
+        fail("Should fail because someMethod wasn't expected!");
+    } catch(expected) {
+        // pass
+    }
+}
+
+function testNotCallingExpectedFunctionShouldFail() {
+    var myMock = new Mock()
+    myMock.expects("someMethod")
+    try {
+        myMock.verify()
+        fail("Should fail because someMethod wasn't called!")
+    } catch(expected) {
+        // pass
+    }
+}
+
+function testCallingTooManyExpectedFunctionShouldFail() {
+    var myMock = new Mock()
+    myMock.expects("someMethod")
+    try {
+        myMock.someMethod();
+        myMock.someMethod();
+        myMock.verify()
+        fail("Should fail because someMethod was called more than once!")
+    } catch(expected) {
+        // pass
+    }
+}
+
+function testCallingExpectedFunctionWithBadArgumentsShouldFail() {
+    var myMock = new Mock()
+    myMock.expects("someMethod", "foo")
+    try {
+        myMock.someMethod("bar")
+    } catch(expected) {
+        return
+    }
+    fail("Should fail because bar wasn't the expected arg!")
+}
+
+function testCallingExpectedFunctionWithExpectedArgumentsShouldPass() {
+    var myMock = new Mock()
+    myMock.expects("someMethod", "foo")
+    myMock.expects("anotherMethod", "bar", "zap")
+    assertUndefined(myMock.someMethod("foo"))
+    assertUndefined(myMock.anotherMethod("bar", "zap"))
+}
+
+function testCallingExpectedFunctionWithTooFewArgumentsShouldFail() {
+    var myMock = new Mock()
+    myMock.expects("someMethod", "foo", "bar")
+    try {
+        myMock.someMethod("foo")
+    } catch(expected) {
+        return
+    }
+    fail("Should fail because too few arguments were passed!")
+}
+
+function testCallingExpectedFunctionWithTooManyArgumentsShouldFail() {
+    var myMock = new Mock()
+    myMock.expects("someMethod", "foo")
+    try {
+        myMock.someMethod("foo", "bar")
+    } catch(expected) {
+        return
+    }
+    fail("Should fail because too many arguments were passed!")
+}
+
+function testShouldCreateMockInstancesWithoutSideffects() {
+    var foo = new Mock()
+    var bar = new Mock()
+
+    foo.expects("foo")
+    bar.expects("bar")
+
+    try {
+        bar.foo()
+    } catch(expected) {
+        return
+    }
+    fail("Should fail because an unexpected bar was called!")
+}
+
+function testCallingExpectedFunctionWithReturnShouldReturnValue() {
+    var myMock = new Mock()
+    myMock.expects("someMethod", "bar").returns("foo")
+    myMock.expects("theOtherMethod", "zap", "ping", "pong").returns("bang")
+    assertEquals("foo", myMock.someMethod("bar"))
+    assertEquals("bang", myMock.theOtherMethod("zap", "ping", "pong"))
+    myMock.verify()
+}
+
+function testCallingExpectedFunctionWithThrowsShouldThrowError() {
+    var myMock = new Mock();
+    myMock.expects("someMethod", "bar").andThrows("failure")
+    try {
+        myMock.someMethod("bar")
+    } catch (e) {
+        assertEquals("failure", e.message);
+        return
+    }
+    fail("Mock did not throw exception when required");
+}
+
+function testSettingExpectedPropertyShouldPass() {
+    var myMock = new Mock()
+    myMock.expectsProperty("foo", "bar")
+    myMock.foo = "bar"
+    myMock.verify()
+}
+
+function testShouldAllowExpectationOfSameFunctionWithDifferentArguments() {
+    var myMock = new Mock()
+    myMock.expects("aslak", "hello").returns("world")
+    myMock.expects("aslak", "bonjour").returns("monde")
+    assertEquals("world", myMock.aslak("hello"))
+    assertEquals("monde", myMock.aslak("bonjour"))
+    myMock.verify()
+}
+
+function TODO_testSettingUnexpectedPropertyShouldFail() {
+    var myMock = new Mock()
+    myMock.foo = "bar"
+
+    try {
+        myMock.verify()
+    } catch(expected) {
+        return
+    }
+    fail("Should fail because an unexpected property was set!")
+}
+
+function TODO_testNotSettingExpectedPropertyShouldFail() {
+    var myMock = new Mock()
+    myMock.expectsProperty("foo", "bar")
+    try {
+        myMock.verify()
+    } catch(expected) {
+        return
+    }
+    fail("Should fail because an expected property was not set!")
+}
+
+function TODO_testSettingExpectedPropertyWithUnexpectedValueShouldFail() {
+    var myMock = new Mock()
+    myMock.expectsProperty("foo", "bar")
+    myMock.foo = "zap"
+    try {
+        myMock.verify()
+    } catch(expected) {
+        return
+    }
+    fail("Should fail because an expected property was set with unexpected value!")
+}
+
+</script>
+</head>
+
+<body>
+<h1>JsMock Tests</h1>
+
+<p>This page contains tests for JsMock. To see them, take a look at the source. To run them, load this file via JsUnit's
+    testRunner.html</p>
+</body>
+</html>

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/jsmock/mock.js
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/jsmock/mock.js	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/jsmock/mock.js	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+// A simple mock library for Javascript
+//
+// Original code by Aslak Hellesoy and Ji Wang
+
+var Mock = function() {
+    this.expectedMethodInvocations = [];
+    this.attrs = [];
+    this.expectedProperties = {};
+
+    var attrCount = 0;
+    for (var attr in this) {
+        this.attrs[attrCount] = attr;
+        attrCount++;
+    }
+};
+
+Mock.prototype.expects = function() {
+    var functionName = arguments[0];
+    var expectedArgs = [];
+    for (var i = 1; i < arguments.length; i++) {
+        expectedArgs[i - 1] = arguments[i];
+    }
+    var methodInvocation = new MethodInvocation(functionName, expectedArgs);
+
+    eval("if (!this." + functionName + ") { this." + functionName + " = this._executeMethod; }");
+
+    this.expectedMethodInvocations.push(methodInvocation);
+    this.attrs[this.attrs.length] = "dummy";
+    return new Returner(methodInvocation);
+};
+
+Mock.prototype._executeMethod = function() {
+    var methodInvocation = this.expectedMethodInvocations.shift();
+    if (!methodInvocation) {
+        fail("No more expected method invocations.")
+    }
+    assertEquals(methodInvocation.functionName + ": Wrong number of arguments.", methodInvocation.expectedArgs.length, arguments.length);
+    for (var i = 0; i < arguments.length; i++) {
+        assertEquals(methodInvocation.expectedArgs[i], arguments[i]);
+    }
+    var returnValue = methodInvocation.returnValue;
+    if (returnValue && returnValue.isMockError) {
+        throw returnValue;
+    }
+    return returnValue;
+};
+
+Mock.prototype.expectsProperty = function() {
+    var propertyName = arguments[0];
+    if (arguments.length == 2) {
+        var expectedPropertyValue = arguments[1];
+        this.expectedProperties[propertyName] = expectedPropertyValue;
+        this.attrs[this.attrs.length] = "dummy";
+    } else {
+        return new PropertySetter(this, propertyName);
+    }
+};
+
+Mock.prototype.verify = function() {
+    // loop over all expected invocations and see if they were called
+    for (var i = 0; i < this.expectedMethodInvocations.length; i++) {
+        var methodInvocation = this.expectedMethodInvocations[i];
+        fail("Expected function not called:" + methodInvocation.functionName);
+    }
+};
+
+var MethodInvocation = function(functionName, expectedArgs) {
+    this.functionName = functionName;
+    this.expectedArgs = expectedArgs;
+    this.returnValue = undefined;
+}
+
+var Returner = function(methodInvocation) {
+    this.methodInvocation = methodInvocation;
+};
+
+Returner.prototype.returns = function(returnValue) {
+    this.methodInvocation.returnValue = returnValue;
+};
+
+Returner.prototype.andThrows = function(message) {
+    var error = new Error(message);
+    error.isMockError = true;
+    this.methodInvocation.returnValue = error;
+};
+
+var PropertySetter = function(mock, propertyName) {
+    this.mock = mock;
+    this.propertyName = propertyName;
+};
+
+PropertySetter.prototype.returns = function(returnValue) {
+    var ref = new Object();
+    ref.value = returnValue;
+    eval("this.mock." + this.propertyName + "=ref.value");
+};

Added: Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/tableparser/tableparser-tests.html
===================================================================
--- Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/tableparser/tableparser-tests.html	                        (rev 0)
+++ Zelenium/branches/gotcha-selenium-0.8.3/selenium/unittest/tableparser/tableparser-tests.html	2007-12-08 13:40:46 UTC (rev 82197)
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html>
+  <head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JsUnit Utility Tests</title>
+    <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css">
+<script language="JavaScript" type="text/javascript" src="/jsunit/app/jsUnitCore.js"></script>
+<script language="JavaScript" type="text/javascript" src="/jsmock/mock.js"></script>
+<script language="JavaScript" type="text/javascript" src="/selenium-commandhandlers.js"></script>
+<script language="JavaScript" type="text/javascript" src="/selenium-remoterunner.js"></script>
+<script language="JavaScript" type="text/javascript" src="/htmlutils.js"></script>
+<script language="JavaScript" type="text/javascript">
+
+function assertCommand(expectedName, expectedTarget, expectedValue, command) {
+    assertEquals(expectedName, command.command);
+    assertEquals(expectedTarget, command.target);
+    assertEquals(expectedValue, command.value);
+}
+
+function testShouldParseOneArgWikiTableRowToJavascript() {
+    var wikiRow = "| click | someButton ||"
+    var command = createCommandFromWikiRow(wikiRow);
+    assertCommand("click", "someButton", "", command);
+}
+
+function testShouldParseTwoArgWikiTableRowToJavascript() {
+    var wikiRow = "| verifyText | someButton |Click me!|"
+    var command = createCommandFromWikiRow(wikiRow);
+    assertCommand("verifyText", "someButton", "Click me!", command);
+}
+
+function testShouldFailWithBadWikiRowFormat() {
+    var wikiRow = "| verifyText | someButton |Click me!||"
+    try {
+        createCommandFromWikiRow(wikiRow);
+        fail("Should fail")
+    } catch(expected) {
+        assertEquals("Bad wiki row format:| verifyText | someButton |Click me!||", expected.message)
+    }
+}
+</script>
+  </head>
+
+  <body>
+    <h1>TableParser Tests</h1>
+    <p>This page contains tests for SeleneseRunner command parsing.
+  </body>
+
+</html>



More information about the Checkins mailing list