<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,7 +1,7 @@
 &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
+
 &lt;apidoc&gt;
-
+
 &lt;top&gt;Defines an object that runs Selenium commands.
 
 &lt;h3&gt;&lt;a name=&quot;locators&quot;&gt;&lt;/a&gt;Element Locators&lt;/h3&gt;
@@ -132,235 +132,235 @@ stuff.&lt;/li&gt;
 If no pattern prefix is specified, Selenium assumes that it's a &quot;glob&quot;
 pattern.
 &lt;/p&gt;&lt;/top&gt;
-
+
 &lt;function name=&quot;click&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;doubleClick&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;clickAt&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;param name=&quot;coordString&quot;&gt;specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;doubleClickAt&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;param name=&quot;coordString&quot;&gt;specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;fireEvent&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;eventName&quot;&gt;the event name, e.g. &quot;focus&quot; or &quot;blur&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Explicitly simulate an event, to trigger the corresponding &amp;quot;on&lt;em&gt;event&lt;/em&gt;&amp;quot;
 handler.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;keyPress&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;keySequence&quot;&gt;Either be a string(&quot;\&quot; followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: &quot;w&quot;, &quot;\119&quot;.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing and releasing a key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;shiftKeyDown&quot;&gt;
-
+
 &lt;comment&gt;Press the shift key and hold it down until doShiftUp() is called or a new page is loaded.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;shiftKeyUp&quot;&gt;
-
+
 &lt;comment&gt;Release the shift key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;metaKeyDown&quot;&gt;
-
+
 &lt;comment&gt;Press the meta key and hold it down until doMetaUp() is called or a new page is loaded.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;metaKeyUp&quot;&gt;
-
+
 &lt;comment&gt;Release the meta key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;altKeyDown&quot;&gt;
-
+
 &lt;comment&gt;Press the alt key and hold it down until doAltUp() is called or a new page is loaded.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;altKeyUp&quot;&gt;
-
+
 &lt;comment&gt;Release the alt key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;controlKeyDown&quot;&gt;
-
+
 &lt;comment&gt;Press the control key and hold it down until doControlUp() is called or a new page is loaded.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;controlKeyUp&quot;&gt;
-
+
 &lt;comment&gt;Release the control key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;keyDown&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;keySequence&quot;&gt;Either be a string(&quot;\&quot; followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: &quot;w&quot;, &quot;\119&quot;.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing a key (without releasing it yet).&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;keyUp&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;keySequence&quot;&gt;Either be a string(&quot;\&quot; followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: &quot;w&quot;, &quot;\119&quot;.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user releasing a key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseOver&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user hovering a mouse over the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseOut&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user moving the mouse pointer away from the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseDown&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing the mouse button (without releasing it yet) on
 the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseDownAt&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;coordString&quot;&gt;specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing the mouse button (without releasing it yet) at
 the specified location.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseUp&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates the event that occurs when the user releases the mouse button (i.e., stops
 holding the button down) on the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseUpAt&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;coordString&quot;&gt;specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates the event that occurs when the user releases the mouse button (i.e., stops
 holding the button down) at the specified location.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseMove&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing the mouse button (without releasing it yet) on
 the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseMoveAt&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;coordString&quot;&gt;specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing the mouse button (without releasing it yet) on
 the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;type&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;value&quot;&gt;the value to type&lt;/param&gt;
-
+
 &lt;comment&gt;Sets the value of an input field, as though you typed it in.
 
 &lt;p&gt;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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;typeKeys&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;value&quot;&gt;the value to type&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates keystroke events on the specified element, as though you typed the value key-by-key.
 
 &lt;p&gt;This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string;
@@ -372,49 +372,49 @@ For example, if you use &quot;typeKeys&quot; on a form element, you may or may not see the
 the field.&lt;/p&gt;
 &lt;p&gt;In some cases, you may need to use the simple &quot;type&quot; command to set the value of the field and then the &quot;typeKeys&quot; command to
 send the keystroke events corresponding to what you just typed.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setSpeed&quot;&gt;
-
+
 &lt;param name=&quot;value&quot;&gt;the number of milliseconds to pause after operation&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSpeed&quot;&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;check&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Check a toggle-button (checkbox/radio)&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;uncheck&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Uncheck a toggle-button (checkbox/radio)&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;select&quot;&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;param name=&quot;optionLocator&quot;&gt;an option locator (a label by default)&lt;/param&gt;
-
+
 &lt;comment&gt;Select an option from a drop-down using an option locator.
 
 &lt;p&gt;
@@ -457,54 +457,54 @@ matches an option based on its index (offset from zero).
 &lt;p&gt;
 If no option locator prefix is provided, the default behaviour is to match on &lt;strong&gt;label&lt;/strong&gt;.
 &lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;addSelection&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box&lt;/param&gt;
-
+
 &lt;param name=&quot;optionLocator&quot;&gt;an option locator (a label by default)&lt;/param&gt;
-
+
 &lt;comment&gt;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&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;removeSelection&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box&lt;/param&gt;
-
+
 &lt;param name=&quot;optionLocator&quot;&gt;an option locator (a label by default)&lt;/param&gt;
-
+
 &lt;comment&gt;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&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;removeAllSelections&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box&lt;/param&gt;
-
+
 &lt;comment&gt;Unselects all of the selected options in a multi-select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;submit&quot;&gt;
-
+
 &lt;param name=&quot;formLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; for the form you want to submit&lt;/param&gt;
-
+
 &lt;comment&gt;Submit the specified form. This is particularly useful for forms without
 submit buttons, e.g. single-input &quot;Search&quot; forms.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;open&quot;&gt;
-
+
 &lt;param name=&quot;url&quot;&gt;the URL to open; may be relative or absolute&lt;/param&gt;
-
+
 &lt;comment&gt;Opens an URL in the test frame. This accepts both relative and absolute
 URLs.
 
@@ -515,15 +515,15 @@ ie. the &amp;quot;AndWait&amp;quot; suffix is implicit.
 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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;openWindow&quot;&gt;
-
+
 &lt;param name=&quot;url&quot;&gt;the URL to open, which can be blank&lt;/param&gt;
-
+
 &lt;param name=&quot;windowID&quot;&gt;the JavaScript window ID of the window to select&lt;/param&gt;
-
+
 &lt;comment&gt;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.
@@ -531,13 +531,13 @@ command.
 &lt;p&gt;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 &quot;onLoad&quot; 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(&quot;&quot;, &quot;myFunnyWindow&quot;).&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;selectWindow&quot;&gt;
-
+
 &lt;param name=&quot;windowID&quot;&gt;the JavaScript window ID of the window to select&lt;/param&gt;
-
+
 &lt;comment&gt;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.
@@ -566,13 +566,13 @@ like the following for each window as it is opened:&lt;/p&gt;
 &lt;p&gt;In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the &quot;onLoad&quot; 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(&quot;&quot;, &quot;myFunnyWindow&quot;).&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;selectFrame&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a frame or iframe&lt;/param&gt;
-
+
 &lt;comment&gt;Selects a frame within the current window.  (You may invoke this command
 multiple times to select nested frames.)  To select the parent frame, use
 &quot;relative=parent&quot; as a locator; to select the top frame, use &quot;relative=top&quot;.
@@ -581,17 +581,17 @@ You can also select a frame by its 0-based index number; select the first frame
 
 &lt;p&gt;You may also use a DOM expression to identify the frame you want directly,
 like this: &lt;code&gt;dom=frames[&quot;main&quot;].frames[&quot;subframe&quot;]&lt;/code&gt;&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getWhetherThisFrameMatchFrameExpression&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the new frame is this code's window&lt;/return&gt;
-
+
 &lt;param name=&quot;currentFrameString&quot;&gt;starting frame&lt;/param&gt;
-
+
 &lt;param name=&quot;target&quot;&gt;new frame (which might be relative to the current one)&lt;/param&gt;
-
+
 &lt;comment&gt;Determine whether current/locator identify the frame containing this running code.
 
 &lt;p&gt;This is useful in proxy injection mode, where this code runs in every
@@ -599,17 +599,17 @@ browser frame and window, and sometimes the selenium server needs to identify
 the &quot;current&quot; 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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getWhetherThisWindowMatchWindowExpression&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the new window is this code's window&lt;/return&gt;
-
+
 &lt;param name=&quot;currentWindowString&quot;&gt;starting window&lt;/param&gt;
-
+
 &lt;param name=&quot;target&quot;&gt;new window (which might be relative to the current one, e.g., &quot;_parent&quot;)&lt;/param&gt;
-
+
 &lt;comment&gt;Determine whether currentWindowString plus target identify the window containing this running code.
 
 &lt;p&gt;This is useful in proxy injection mode, where this code runs in every
@@ -617,21 +617,21 @@ browser frame and window, and sometimes the selenium server needs to identify
 the &quot;current&quot; 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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;waitForPopUp&quot;&gt;
-
+
 &lt;param name=&quot;windowID&quot;&gt;the JavaScript window ID of the window that will appear&lt;/param&gt;
-
+
 &lt;param name=&quot;timeout&quot;&gt;a timeout in milliseconds, after which the action will return with an error&lt;/param&gt;
-
+
 &lt;comment&gt;Waits for a popup window to appear and load up.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;chooseCancelOnNextConfirmation&quot;&gt;
-
+
 &lt;comment&gt;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
@@ -639,11 +639,11 @@ 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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;chooseOkOnNextConfirmation&quot;&gt;
-
+
 &lt;comment&gt;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
@@ -652,77 +652,77 @@ your mind prior to the next confirmation.  After any confirmation, Selenium will
 default behavior for future confirmations, automatically returning 
 true (OK) unless/until you explicitly call chooseCancelOnNextConfirmation for each
 confirmation.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;answerOnNextPrompt&quot;&gt;
-
+
 &lt;param name=&quot;answer&quot;&gt;the answer to give in response to the prompt pop-up&lt;/param&gt;
-
+
 &lt;comment&gt;Instructs Selenium to return the specified answer string in response to
 the next JavaScript prompt [window.prompt()].&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;goBack&quot;&gt;
-
+
 &lt;comment&gt;Simulates the user clicking the &quot;back&quot; button on their browser.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;refresh&quot;&gt;
-
+
 &lt;comment&gt;Simulates the user clicking the &quot;Refresh&quot; button on their browser.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;close&quot;&gt;
-
+
 &lt;comment&gt;Simulates the user clicking the &quot;close&quot; button in the titlebar of a popup
 window or tab.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isAlertPresent&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if there is an alert&lt;/return&gt;
-
+
 &lt;comment&gt;Has an alert occurred?
 
 &lt;p&gt;
 This function never throws an exception
 &lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isPromptPresent&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if there is a pending prompt&lt;/return&gt;
-
+
 &lt;comment&gt;Has a prompt occurred?
 
 &lt;p&gt;
 This function never throws an exception
 &lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isConfirmationPresent&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if there is a pending confirmation&lt;/return&gt;
-
+
 &lt;comment&gt;Has confirm() been called?
 
 &lt;p&gt;
 This function never throws an exception
 &lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAlert&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;The message of the most recent JavaScript alert&lt;/return&gt;
-
+
 &lt;comment&gt;Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts.
 
 &lt;p&gt;Getting an alert has the same effect as manually clicking OK. If an
@@ -735,13 +735,13 @@ dialog.&lt;/p&gt;
 &lt;p&gt;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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getConfirmation&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the message of the most recent JavaScript confirmation dialog&lt;/return&gt;
-
+
 &lt;comment&gt;Retrieves the message of a JavaScript confirmation dialog generated during
 the previous action.
 
@@ -763,13 +763,13 @@ 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.
 &lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getPrompt&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the message of the most recent JavaScript question prompt&lt;/return&gt;
-
+
 &lt;comment&gt;Retrieves the message of a JavaScript question prompt dialog generated during
 the previous action.
 
@@ -783,72 +783,72 @@ dialog.&lt;/p&gt;
 &lt;p&gt;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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getLocation&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the absolute URL of the current page&lt;/return&gt;
-
+
 &lt;comment&gt;Gets the absolute URL of the current page.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getTitle&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the title of the current page&lt;/return&gt;
-
+
 &lt;comment&gt;Gets the title of the current page.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getBodyText&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the entire text of the page&lt;/return&gt;
-
+
 &lt;comment&gt;Gets the entire text of the page.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getValue&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the element value, or &quot;on/off&quot; for checkbox/radio elements&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Gets the (whitespace-trimmed) value of an input field (or anything else with a value parameter).
 For checkbox/radio elements, the value will be &quot;on&quot; or &quot;off&quot; depending on
 whether the element is checked or not.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getText&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the text of the element&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;highlight&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Briefly changes the backgroundColor of the specified element yellow.  Useful for debugging.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getEval&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the results of evaluating the snippet&lt;/return&gt;
-
+
 &lt;param name=&quot;script&quot;&gt;the JavaScript snippet to run&lt;/param&gt;
-
+
 &lt;comment&gt;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.
 
@@ -859,239 +859,239 @@ refer to the window of your application, e.g. &lt;code&gt;window.document.getElementBy
 &lt;p&gt;If you need to use
 a locator to refer to a single element in your application page, you can
 use &lt;code&gt;this.browserbot.findElement(&quot;id=foo&quot;)&lt;/code&gt; where &quot;id=foo&quot; is your locator.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isChecked&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the checkbox is checked, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to a checkbox or radio button&lt;/param&gt;
-
+
 &lt;comment&gt;Gets whether a toggle-button (checkbox/radio) is checked.  Fails if the specified element doesn't exist or isn't a toggle-button.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getTable&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the text from the specified cell&lt;/return&gt;
-
+
 &lt;param name=&quot;tableCellAddress&quot;&gt;a cell address, e.g. &quot;foo.1.4&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Gets the text from a cell of a table. The cellAddress syntax
 tableLocator.row.column, where row and column start at 0.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedLabels&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;an array of all selected option labels in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets all option labels (visible text) for selected options in the specified select or multi-select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedLabel&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the selected option label in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets option label (visible text) for selected option in the specified select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedValues&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;an array of all selected option values in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets all option values (value attributes) for selected options in the specified select or multi-select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedValue&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the selected option value in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets option value (value attribute) for selected option in the specified select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedIndexes&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;an array of all selected option indexes in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets all option indexes (option number, starting at 0) for selected options in the specified select or multi-select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedIndex&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the selected option index in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets option index (option number, starting at 0) for selected option in the specified select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedIds&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;an array of all selected option IDs in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets all option element IDs for selected options in the specified select or multi-select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedId&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the selected option ID in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets option element ID for selected option in the specified select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isSomethingSelected&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if some option has been selected, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Determines whether some option in a drop-down menu is selected.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectOptions&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;an array of all option labels in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets all option labels in the specified select drop-down.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAttribute&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the value of the specified attribute&lt;/return&gt;
-
+
 &lt;param name=&quot;attributeLocator&quot;&gt;an element locator followed by an &amp;#064; sign and then the name of the attribute, e.g. &quot;foo&amp;#064;bar&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Gets the value of an element attribute.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isTextPresent&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the pattern matches the text, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;pattern&quot;&gt;a &lt;a href=&quot;#patterns&quot;&gt;pattern&lt;/a&gt; to match with the text of the page&lt;/param&gt;
-
+
 &lt;comment&gt;Verifies that the specified text pattern appears somewhere on the rendered page shown to the user.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isElementPresent&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the element is present, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Verifies that the specified element is somewhere on the page.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isVisible&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the specified element is visible, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Determines if the specified element is visible. An
 element can be rendered invisible by setting the CSS &quot;visibility&quot;
 property to &quot;hidden&quot;, or the &quot;display&quot; property to &quot;none&quot;, either for the
 element itself or one if its ancestors.  This method will fail if
 the element is not present.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isEditable&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the input element is editable, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllButtons&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the IDs of all buttons on the page&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the IDs of all buttons on the page.
 
 &lt;p&gt;If a given button has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllLinks&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the IDs of all links on the page&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the IDs of all links on the page.
 
 &lt;p&gt;If a given link has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllFields&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the IDs of all field on the page&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the IDs of all input fields on the page.
 
 &lt;p&gt;If a given field has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAttributeFromAllWindows&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the set of values of this attribute from all known windows.&lt;/return&gt;
-
+
 &lt;param name=&quot;attributeName&quot;&gt;name of an attribute on the windows&lt;/param&gt;
-
+
 &lt;comment&gt;Returns every instance of some attribute from all known windows.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;dragdrop&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;param name=&quot;movementsString&quot;&gt;offset in pixels from the current location to which the element should be moved, e.g., &quot;+70,-300&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;deprecated - use dragAndDrop instead&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setMouseSpeed&quot;&gt;
-
+
 &lt;param name=&quot;pixels&quot;&gt;the number of pixels between &quot;mousemove&quot; events&lt;/param&gt;
-
+
 &lt;comment&gt;Configure the number of pixels between &quot;mousemove&quot; events during dragAndDrop commands (default=10).
 &lt;p&gt;Setting this value to 0 means that we'll send a &quot;mousemove&quot; event to every single pixel
 in between the start location and the end location; that can be very slow, and may
@@ -1099,226 +1099,226 @@ cause some browsers to force the JavaScript to timeout.&lt;/p&gt;
 
 &lt;p&gt;If the mouse speed is greater than the distance between the two dragged objects, we'll
 just send one &quot;mousemove&quot; at the start location and then one final one at the end location.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getMouseSpeed&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;the number of pixels between &quot;mousemove&quot; events during dragAndDrop commands (default=10)&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the number of pixels between &quot;mousemove&quot; events during dragAndDrop commands (default=10).&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;dragAndDrop&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;param name=&quot;movementsString&quot;&gt;offset in pixels from the current location to which the element should be moved, e.g., &quot;+70,-300&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Drags an element a certain distance and then drops it&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;dragAndDropToObject&quot;&gt;
-
+
 &lt;param name=&quot;locatorOfObjectToBeDragged&quot;&gt;an element to be dragged&lt;/param&gt;
-
+
 &lt;param name=&quot;locatorOfDragDestinationObject&quot;&gt;an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged  is dropped&lt;/param&gt;
-
+
 &lt;comment&gt;Drags an element and drops it on another element&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;windowFocus&quot;&gt;
-
+
 &lt;comment&gt;Gives focus to the currently selected window&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;windowMaximize&quot;&gt;
-
+
 &lt;comment&gt;Resize currently selected window to take up the entire screen&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllWindowIds&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the IDs of all windows that the browser knows about.&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the IDs of all windows that the browser knows about.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllWindowNames&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the names of all windows that the browser knows about.&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the names of all windows that the browser knows about.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllWindowTitles&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the titles of all windows that the browser knows about.&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the titles of all windows that the browser knows about.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getHtmlSource&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the entire HTML source&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the entire HTML source between the opening and
 closing &quot;html&quot; tags.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setCursorPosition&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an input element or textarea&lt;/param&gt;
-
+
 &lt;param name=&quot;position&quot;&gt;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.&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getElementIndex&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;of relative index of the element to its parent (starting from 0)&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element&lt;/param&gt;
-
+
 &lt;comment&gt;Get the relative index of an element to its parent (starting from 0). The comment node and empty text node
 will be ignored.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isOrdered&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if element1 is the previous sibling of element2, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;locator1&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to the first element&lt;/param&gt;
-
+
 &lt;param name=&quot;locator2&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to the second element&lt;/param&gt;
-
+
 &lt;comment&gt;Check if these two elements have same parent and are ordered siblings in the DOM. Two same elements will
 not be considered ordered.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getElementPositionLeft&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;of pixels from the edge of the frame.&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element OR an element itself&lt;/param&gt;
-
+
 &lt;comment&gt;Retrieves the horizontal position of an element&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getElementPositionTop&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;of pixels from the edge of the frame.&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element OR an element itself&lt;/param&gt;
-
+
 &lt;comment&gt;Retrieves the vertical position of an element&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getElementWidth&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;width of an element in pixels&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element&lt;/param&gt;
-
+
 &lt;comment&gt;Retrieves the width of an element&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getElementHeight&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;height of an element in pixels&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element&lt;/param&gt;
-
+
 &lt;comment&gt;Retrieves the height of an element&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getCursorPosition&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;the numerical position of the cursor in the field&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an input element or textarea&lt;/param&gt;
-
+
 &lt;comment&gt;Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers.
 
 &lt;p&gt;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 &lt;a href=&quot;http://jira.openqa.org/browse/SEL-243&quot;&gt;SEL-243&lt;/a&gt;.&lt;/p&gt;
 This method will fail if the specified element isn't an input element or textarea, or there is no cursor in the element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getExpression&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the value passed in&lt;/return&gt;
-
+
 &lt;param name=&quot;expression&quot;&gt;the value to return&lt;/param&gt;
-
+
 &lt;comment&gt;Returns the specified expression.
 
 &lt;p&gt;This is useful because of JavaScript preprocessing.
 It is used to generate commands like assertExpression and waitForExpression.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getXpathCount&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;the number of nodes that match the specified xpath&lt;/return&gt;
-
+
 &lt;param name=&quot;xpath&quot;&gt;the xpath expression to evaluate. do NOT wrap this expression in a 'count()' function; we will do that for you.&lt;/param&gt;
-
+
 &lt;comment&gt;Returns the number of nodes that match the specified xpath, eg. &quot;//table&quot; would give
 the number of tables.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;assignId&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element&lt;/param&gt;
-
+
 &lt;param name=&quot;identifier&quot;&gt;a string to be used as the ID of the specified element&lt;/param&gt;
-
+
 &lt;comment&gt;Temporarily sets the &quot;id&quot; 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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;allowNativeXpath&quot;&gt;
-
+
 &lt;param name=&quot;allow&quot;&gt;boolean, true means we'll prefer to use native XPath; false means we'll only use JS XPath&lt;/param&gt;
-
+
 &lt;comment&gt;Specifies whether Selenium should use the native in-browser implementation
 of XPath (if any native version is available); if you pass &quot;false&quot; 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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;waitForCondition&quot;&gt;
-
+
 &lt;param name=&quot;script&quot;&gt;the JavaScript snippet to run&lt;/param&gt;
-
+
 &lt;param name=&quot;timeout&quot;&gt;a timeout in milliseconds, after which this command will return with an error&lt;/param&gt;
-
+
 &lt;comment&gt;Runs the specified JavaScript snippet repeatedly until it evaluates to &quot;true&quot;.
 The snippet may have multiple lines, but only the result of the last line
 will be considered.
@@ -1327,24 +1327,24 @@ will be considered.
 of your application.  To get the window of your application, you can use
 the JavaScript snippet &lt;code&gt;selenium.browserbot.getCurrentWindow()&lt;/code&gt;, and then
 run your JavaScript in there&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setTimeout&quot;&gt;
-
+
 &lt;param name=&quot;timeout&quot;&gt;a timeout in milliseconds, after which the action will return with an error&lt;/param&gt;
-
+
 &lt;comment&gt;Specifies the amount of time that Selenium will wait for actions to complete.
 
 &lt;p&gt;Actions that require waiting include &quot;open&quot; and the &quot;waitFor*&quot; actions.&lt;/p&gt;
 The default timeout is 30 seconds.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;waitForPageToLoad&quot;&gt;
-
+
 &lt;param name=&quot;timeout&quot;&gt;a timeout in milliseconds, after which this command will return with an error&lt;/param&gt;
-
+
 &lt;comment&gt;Waits for a new page to load.
 
 &lt;p&gt;You can use this command instead of the &quot;AndWait&quot; suffixes, &quot;clickAndWait&quot;, &quot;selectAndWait&quot;, &quot;typeAndWait&quot; etc.
@@ -1354,68 +1354,68 @@ The default timeout is 30 seconds.&lt;/comment&gt;
 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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;waitForFrameToLoad&quot;&gt;
-
+
 &lt;param name=&quot;frameAddress&quot;&gt;FrameAddress from the server side&lt;/param&gt;
-
+
 &lt;param name=&quot;timeout&quot;&gt;a timeout in milliseconds, after which this command will return with an error&lt;/param&gt;
-
+
 &lt;comment&gt;Waits for a new frame to load.
 
 &lt;p&gt;Selenium constantly keeps track of new pages and frames loading, 
 and sets a &quot;newPageLoaded&quot; flag when it first notices a page load.&lt;/p&gt;
 
 See waitForPageToLoad for more information.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getCookie&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;all cookies of the current page under test&lt;/return&gt;
-
+
 &lt;comment&gt;Return all cookies of the current page under test.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;createCookie&quot;&gt;
-
+
 &lt;param name=&quot;nameValuePair&quot;&gt;name and value of the cookie in a format &quot;name=value&quot;&lt;/param&gt;
-
+
 &lt;param name=&quot;optionsString&quot;&gt;options for the cookie. Currently supported options include 'path' and 'max_age'.      the optionsString's format is &quot;path=/path/, max_age=60&quot;. The order of options are irrelevant, the unit      of the value of 'max_age' is second.&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;deleteCookie&quot;&gt;
-
+
 &lt;param name=&quot;name&quot;&gt;the name of the cookie to be deleted&lt;/param&gt;
-
+
 &lt;param name=&quot;path&quot;&gt;the path property of the cookie to be deleted&lt;/param&gt;
-
+
 &lt;comment&gt;Delete a named cookie with specified path.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setBrowserLogLevel&quot;&gt;
-
+
 &lt;param name=&quot;logLevel&quot;&gt;one of the following: &quot;debug&quot;, &quot;info&quot;, &quot;warn&quot;, &quot;error&quot; or &quot;off&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Sets the threshold for browser-side logging messages; log messages beneath this threshold will be discarded.
 Valid logLevel strings are: &quot;debug&quot;, &quot;info&quot;, &quot;warn&quot;, &quot;error&quot; or &quot;off&quot;.
 To see the browser logs, you need to
 either show the log window in GUI mode, or enable browser-side logging in Selenium RC.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;runScript&quot;&gt;
-
+
 &lt;param name=&quot;script&quot;&gt;the JavaScript snippet to run&lt;/param&gt;
-
+
 &lt;comment&gt;Creates a new &quot;script&quot; 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
@@ -1423,15 +1423,15 @@ Selenium's &quot;getEval&quot; 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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;addLocationStrategy&quot;&gt;
-
+
 &lt;param name=&quot;strategyName&quot;&gt;the name of the strategy to define; this should use only   letters [a-zA-Z] with no spaces or other punctuation.&lt;/param&gt;
-
+
 &lt;param name=&quot;functionDefinition&quot;&gt;a string defining the body of a function in JavaScript.   For example: &lt;code&gt;return inDocument.getElementById(locator);&lt;/code&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Defines a new function for Selenium to locate elements on the page.
 For example,
 if you define the strategy &quot;foo&quot;, and someone runs click(&quot;foo=blah&quot;), we'll
@@ -1446,70 +1446,70 @@ We'll pass three arguments to your function:
 &lt;li&gt;inDocument: the currently selected document&lt;/li&gt;
 &lt;/ul&gt;
 The function must return null if the element can't be found.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;pause&quot;&gt;
-
+
 &lt;param name=&quot;waitTime&quot;&gt;the amount of time to sleep (in milliseconds)&lt;/param&gt;
-
+
 &lt;comment&gt;Wait for the specified amount of time (in milliseconds)&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;break&quot;&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;store&quot;&gt;
-
+
 &lt;param name=&quot;expression&quot;&gt;the value to store&lt;/param&gt;
-
+
 &lt;param name=&quot;variableName&quot;&gt;the name of a &lt;a href=&quot;#storedVars&quot;&gt;variable&lt;/a&gt; in which the result is to be stored.&lt;/param&gt;
-
+
 &lt;comment&gt;This command is a synonym for storeExpression.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;echo&quot;&gt;
-
+
 &lt;param name=&quot;message&quot;&gt;the message to print&lt;/param&gt;
-
+
 &lt;comment&gt;Prints the specified message into the third table cell in your Selenese tables.
 Useful for debugging.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;assertSelected&quot;&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;param name=&quot;optionLocator&quot;&gt;an option locator, typically just an option label (e.g. &quot;John Smith&quot;)&lt;/param&gt;
-
+
 &lt;comment&gt;Verifies that the selected option of a drop-down satisfies the optionSpecifier.  &lt;i&gt;Note that this command is deprecated; you should use assertSelectedLabel, assertSelectedValue, assertSelectedIndex, or assertSelectedId instead.&lt;/i&gt;
 
 &lt;p&gt;See the select command for more information about option locators.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;assertFailureOnNext&quot;&gt;
-
+
 &lt;param name=&quot;message&quot;&gt;The failure message we should expect.  This command will fail if the wrong failure message appears.&lt;/param&gt;
-
+
 &lt;comment&gt;Tell Selenium to expect a failure on the next command execution.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;assertErrorOnNext&quot;&gt;
-
+
 &lt;param name=&quot;message&quot;&gt;The error message we should expect.  This command will fail if the wrong error message appears.&lt;/param&gt;
-
+
 &lt;comment&gt;Tell Selenium to expect an error on the next command execution.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
-&lt;/apidoc&gt;
+
+&lt;/apidoc&gt;</diff>
      <filename>testframework/selenium/iedoc-core.xml</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
+
 &lt;apidoc&gt;
-
+
 &lt;top&gt;Defines an object that runs Selenium commands.
 
 &lt;h3&gt;&lt;a name=&quot;locators&quot;&gt;&lt;/a&gt;Element Locators&lt;/h3&gt;
@@ -132,235 +132,235 @@ stuff.&lt;/li&gt;
 If no pattern prefix is specified, Selenium assumes that it's a &quot;glob&quot;
 pattern.
 &lt;/p&gt;&lt;/top&gt;
-
+
 &lt;function name=&quot;click&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;doubleClick&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;clickAt&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;param name=&quot;coordString&quot;&gt;specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;doubleClickAt&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;param name=&quot;coordString&quot;&gt;specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;fireEvent&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;eventName&quot;&gt;the event name, e.g. &quot;focus&quot; or &quot;blur&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Explicitly simulate an event, to trigger the corresponding &amp;quot;on&lt;em&gt;event&lt;/em&gt;&amp;quot;
 handler.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;keyPress&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;keySequence&quot;&gt;Either be a string(&quot;\&quot; followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: &quot;w&quot;, &quot;\119&quot;.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing and releasing a key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;shiftKeyDown&quot;&gt;
-
+
 &lt;comment&gt;Press the shift key and hold it down until doShiftUp() is called or a new page is loaded.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;shiftKeyUp&quot;&gt;
-
+
 &lt;comment&gt;Release the shift key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;metaKeyDown&quot;&gt;
-
+
 &lt;comment&gt;Press the meta key and hold it down until doMetaUp() is called or a new page is loaded.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;metaKeyUp&quot;&gt;
-
+
 &lt;comment&gt;Release the meta key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;altKeyDown&quot;&gt;
-
+
 &lt;comment&gt;Press the alt key and hold it down until doAltUp() is called or a new page is loaded.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;altKeyUp&quot;&gt;
-
+
 &lt;comment&gt;Release the alt key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;controlKeyDown&quot;&gt;
-
+
 &lt;comment&gt;Press the control key and hold it down until doControlUp() is called or a new page is loaded.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;controlKeyUp&quot;&gt;
-
+
 &lt;comment&gt;Release the control key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;keyDown&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;keySequence&quot;&gt;Either be a string(&quot;\&quot; followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: &quot;w&quot;, &quot;\119&quot;.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing a key (without releasing it yet).&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;keyUp&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;keySequence&quot;&gt;Either be a string(&quot;\&quot; followed by the numeric keycode  of the key to be pressed, normally the ASCII value of that key), or a single  character. For example: &quot;w&quot;, &quot;\119&quot;.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user releasing a key.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseOver&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user hovering a mouse over the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseOut&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user moving the mouse pointer away from the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseDown&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing the mouse button (without releasing it yet) on
 the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseDownAt&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;coordString&quot;&gt;specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing the mouse button (without releasing it yet) at
 the specified location.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseUp&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates the event that occurs when the user releases the mouse button (i.e., stops
 holding the button down) on the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseUpAt&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;coordString&quot;&gt;specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates the event that occurs when the user releases the mouse button (i.e., stops
 holding the button down) at the specified location.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseMove&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing the mouse button (without releasing it yet) on
 the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;mouseMoveAt&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;coordString&quot;&gt;specifies the x,y position (i.e. - 10,20) of the mouse      event relative to the element returned by the locator.&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates a user pressing the mouse button (without releasing it yet) on
 the specified element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;type&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;value&quot;&gt;the value to type&lt;/param&gt;
-
+
 &lt;comment&gt;Sets the value of an input field, as though you typed it in.
 
 &lt;p&gt;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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;typeKeys&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;param name=&quot;value&quot;&gt;the value to type&lt;/param&gt;
-
+
 &lt;comment&gt;Simulates keystroke events on the specified element, as though you typed the value key-by-key.
 
 &lt;p&gt;This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string;
@@ -372,49 +372,49 @@ For example, if you use &quot;typeKeys&quot; on a form element, you may or may not see the
 the field.&lt;/p&gt;
 &lt;p&gt;In some cases, you may need to use the simple &quot;type&quot; command to set the value of the field and then the &quot;typeKeys&quot; command to
 send the keystroke events corresponding to what you just typed.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setSpeed&quot;&gt;
-
+
 &lt;param name=&quot;value&quot;&gt;the number of milliseconds to pause after operation&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSpeed&quot;&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;check&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Check a toggle-button (checkbox/radio)&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;uncheck&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Uncheck a toggle-button (checkbox/radio)&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;select&quot;&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;param name=&quot;optionLocator&quot;&gt;an option locator (a label by default)&lt;/param&gt;
-
+
 &lt;comment&gt;Select an option from a drop-down using an option locator.
 
 &lt;p&gt;
@@ -457,54 +457,54 @@ matches an option based on its index (offset from zero).
 &lt;p&gt;
 If no option locator prefix is provided, the default behaviour is to match on &lt;strong&gt;label&lt;/strong&gt;.
 &lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;addSelection&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box&lt;/param&gt;
-
+
 &lt;param name=&quot;optionLocator&quot;&gt;an option locator (a label by default)&lt;/param&gt;
-
+
 &lt;comment&gt;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&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;removeSelection&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box&lt;/param&gt;
-
+
 &lt;param name=&quot;optionLocator&quot;&gt;an option locator (a label by default)&lt;/param&gt;
-
+
 &lt;comment&gt;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&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;removeAllSelections&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box&lt;/param&gt;
-
+
 &lt;comment&gt;Unselects all of the selected options in a multi-select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;submit&quot;&gt;
-
+
 &lt;param name=&quot;formLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; for the form you want to submit&lt;/param&gt;
-
+
 &lt;comment&gt;Submit the specified form. This is particularly useful for forms without
 submit buttons, e.g. single-input &quot;Search&quot; forms.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;open&quot;&gt;
-
+
 &lt;param name=&quot;url&quot;&gt;the URL to open; may be relative or absolute&lt;/param&gt;
-
+
 &lt;comment&gt;Opens an URL in the test frame. This accepts both relative and absolute
 URLs.
 
@@ -515,15 +515,15 @@ ie. the &amp;quot;AndWait&amp;quot; suffix is implicit.
 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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;openWindow&quot;&gt;
-
+
 &lt;param name=&quot;url&quot;&gt;the URL to open, which can be blank&lt;/param&gt;
-
+
 &lt;param name=&quot;windowID&quot;&gt;the JavaScript window ID of the window to select&lt;/param&gt;
-
+
 &lt;comment&gt;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.
@@ -531,13 +531,13 @@ command.
 &lt;p&gt;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 &quot;onLoad&quot; 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(&quot;&quot;, &quot;myFunnyWindow&quot;).&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;selectWindow&quot;&gt;
-
+
 &lt;param name=&quot;windowID&quot;&gt;the JavaScript window ID of the window to select&lt;/param&gt;
-
+
 &lt;comment&gt;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.
@@ -566,13 +566,13 @@ like the following for each window as it is opened:&lt;/p&gt;
 &lt;p&gt;In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the &quot;onLoad&quot; 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(&quot;&quot;, &quot;myFunnyWindow&quot;).&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;selectFrame&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a frame or iframe&lt;/param&gt;
-
+
 &lt;comment&gt;Selects a frame within the current window.  (You may invoke this command
 multiple times to select nested frames.)  To select the parent frame, use
 &quot;relative=parent&quot; as a locator; to select the top frame, use &quot;relative=top&quot;.
@@ -581,17 +581,17 @@ You can also select a frame by its 0-based index number; select the first frame
 
 &lt;p&gt;You may also use a DOM expression to identify the frame you want directly,
 like this: &lt;code&gt;dom=frames[&quot;main&quot;].frames[&quot;subframe&quot;]&lt;/code&gt;&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getWhetherThisFrameMatchFrameExpression&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the new frame is this code's window&lt;/return&gt;
-
+
 &lt;param name=&quot;currentFrameString&quot;&gt;starting frame&lt;/param&gt;
-
+
 &lt;param name=&quot;target&quot;&gt;new frame (which might be relative to the current one)&lt;/param&gt;
-
+
 &lt;comment&gt;Determine whether current/locator identify the frame containing this running code.
 
 &lt;p&gt;This is useful in proxy injection mode, where this code runs in every
@@ -599,17 +599,17 @@ browser frame and window, and sometimes the selenium server needs to identify
 the &quot;current&quot; 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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getWhetherThisWindowMatchWindowExpression&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the new window is this code's window&lt;/return&gt;
-
+
 &lt;param name=&quot;currentWindowString&quot;&gt;starting window&lt;/param&gt;
-
+
 &lt;param name=&quot;target&quot;&gt;new window (which might be relative to the current one, e.g., &quot;_parent&quot;)&lt;/param&gt;
-
+
 &lt;comment&gt;Determine whether currentWindowString plus target identify the window containing this running code.
 
 &lt;p&gt;This is useful in proxy injection mode, where this code runs in every
@@ -617,21 +617,21 @@ browser frame and window, and sometimes the selenium server needs to identify
 the &quot;current&quot; 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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;waitForPopUp&quot;&gt;
-
+
 &lt;param name=&quot;windowID&quot;&gt;the JavaScript window ID of the window that will appear&lt;/param&gt;
-
+
 &lt;param name=&quot;timeout&quot;&gt;a timeout in milliseconds, after which the action will return with an error&lt;/param&gt;
-
+
 &lt;comment&gt;Waits for a popup window to appear and load up.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;chooseCancelOnNextConfirmation&quot;&gt;
-
+
 &lt;comment&gt;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
@@ -639,11 +639,11 @@ 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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;chooseOkOnNextConfirmation&quot;&gt;
-
+
 &lt;comment&gt;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
@@ -652,77 +652,77 @@ your mind prior to the next confirmation.  After any confirmation, Selenium will
 default behavior for future confirmations, automatically returning 
 true (OK) unless/until you explicitly call chooseCancelOnNextConfirmation for each
 confirmation.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;answerOnNextPrompt&quot;&gt;
-
+
 &lt;param name=&quot;answer&quot;&gt;the answer to give in response to the prompt pop-up&lt;/param&gt;
-
+
 &lt;comment&gt;Instructs Selenium to return the specified answer string in response to
 the next JavaScript prompt [window.prompt()].&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;goBack&quot;&gt;
-
+
 &lt;comment&gt;Simulates the user clicking the &quot;back&quot; button on their browser.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;refresh&quot;&gt;
-
+
 &lt;comment&gt;Simulates the user clicking the &quot;Refresh&quot; button on their browser.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;close&quot;&gt;
-
+
 &lt;comment&gt;Simulates the user clicking the &quot;close&quot; button in the titlebar of a popup
 window or tab.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isAlertPresent&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if there is an alert&lt;/return&gt;
-
+
 &lt;comment&gt;Has an alert occurred?
 
 &lt;p&gt;
 This function never throws an exception
 &lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isPromptPresent&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if there is a pending prompt&lt;/return&gt;
-
+
 &lt;comment&gt;Has a prompt occurred?
 
 &lt;p&gt;
 This function never throws an exception
 &lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isConfirmationPresent&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if there is a pending confirmation&lt;/return&gt;
-
+
 &lt;comment&gt;Has confirm() been called?
 
 &lt;p&gt;
 This function never throws an exception
 &lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAlert&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;The message of the most recent JavaScript alert&lt;/return&gt;
-
+
 &lt;comment&gt;Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts.
 
 &lt;p&gt;Getting an alert has the same effect as manually clicking OK. If an
@@ -735,13 +735,13 @@ dialog.&lt;/p&gt;
 &lt;p&gt;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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getConfirmation&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the message of the most recent JavaScript confirmation dialog&lt;/return&gt;
-
+
 &lt;comment&gt;Retrieves the message of a JavaScript confirmation dialog generated during
 the previous action.
 
@@ -763,13 +763,13 @@ 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.
 &lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getPrompt&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the message of the most recent JavaScript question prompt&lt;/return&gt;
-
+
 &lt;comment&gt;Retrieves the message of a JavaScript question prompt dialog generated during
 the previous action.
 
@@ -783,72 +783,72 @@ dialog.&lt;/p&gt;
 &lt;p&gt;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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getLocation&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the absolute URL of the current page&lt;/return&gt;
-
+
 &lt;comment&gt;Gets the absolute URL of the current page.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getTitle&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the title of the current page&lt;/return&gt;
-
+
 &lt;comment&gt;Gets the title of the current page.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getBodyText&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the entire text of the page&lt;/return&gt;
-
+
 &lt;comment&gt;Gets the entire text of the page.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getValue&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the element value, or &quot;on/off&quot; for checkbox/radio elements&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Gets the (whitespace-trimmed) value of an input field (or anything else with a value parameter).
 For checkbox/radio elements, the value will be &quot;on&quot; or &quot;off&quot; depending on
 whether the element is checked or not.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getText&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the text of the element&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;highlight&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Briefly changes the backgroundColor of the specified element yellow.  Useful for debugging.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getEval&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the results of evaluating the snippet&lt;/return&gt;
-
+
 &lt;param name=&quot;script&quot;&gt;the JavaScript snippet to run&lt;/param&gt;
-
+
 &lt;comment&gt;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.
 
@@ -859,239 +859,239 @@ refer to the window of your application, e.g. &lt;code&gt;window.document.getElementBy
 &lt;p&gt;If you need to use
 a locator to refer to a single element in your application page, you can
 use &lt;code&gt;this.browserbot.findElement(&quot;id=foo&quot;)&lt;/code&gt; where &quot;id=foo&quot; is your locator.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isChecked&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the checkbox is checked, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to a checkbox or radio button&lt;/param&gt;
-
+
 &lt;comment&gt;Gets whether a toggle-button (checkbox/radio) is checked.  Fails if the specified element doesn't exist or isn't a toggle-button.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getTable&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the text from the specified cell&lt;/return&gt;
-
+
 &lt;param name=&quot;tableCellAddress&quot;&gt;a cell address, e.g. &quot;foo.1.4&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Gets the text from a cell of a table. The cellAddress syntax
 tableLocator.row.column, where row and column start at 0.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedLabels&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;an array of all selected option labels in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets all option labels (visible text) for selected options in the specified select or multi-select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedLabel&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the selected option label in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets option label (visible text) for selected option in the specified select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedValues&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;an array of all selected option values in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets all option values (value attributes) for selected options in the specified select or multi-select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedValue&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the selected option value in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets option value (value attribute) for selected option in the specified select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedIndexes&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;an array of all selected option indexes in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets all option indexes (option number, starting at 0) for selected options in the specified select or multi-select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedIndex&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the selected option index in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets option index (option number, starting at 0) for selected option in the specified select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedIds&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;an array of all selected option IDs in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets all option element IDs for selected options in the specified select or multi-select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectedId&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the selected option ID in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets option element ID for selected option in the specified select element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isSomethingSelected&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if some option has been selected, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Determines whether some option in a drop-down menu is selected.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getSelectOptions&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;an array of all option labels in the specified select drop-down&lt;/return&gt;
-
+
 &lt;param name=&quot;selectLocator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu&lt;/param&gt;
-
+
 &lt;comment&gt;Gets all option labels in the specified select drop-down.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAttribute&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the value of the specified attribute&lt;/return&gt;
-
+
 &lt;param name=&quot;attributeLocator&quot;&gt;an element locator followed by an &amp;#064; sign and then the name of the attribute, e.g. &quot;foo&amp;#064;bar&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Gets the value of an element attribute.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isTextPresent&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the pattern matches the text, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;pattern&quot;&gt;a &lt;a href=&quot;#patterns&quot;&gt;pattern&lt;/a&gt; to match with the text of the page&lt;/param&gt;
-
+
 &lt;comment&gt;Verifies that the specified text pattern appears somewhere on the rendered page shown to the user.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isElementPresent&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the element is present, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Verifies that the specified element is somewhere on the page.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isVisible&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the specified element is visible, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Determines if the specified element is visible. An
 element can be rendered invisible by setting the CSS &quot;visibility&quot;
 property to &quot;hidden&quot;, or the &quot;display&quot; property to &quot;none&quot;, either for the
 element itself or one if its ancestors.  This method will fail if
 the element is not present.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isEditable&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if the input element is editable, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllButtons&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the IDs of all buttons on the page&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the IDs of all buttons on the page.
 
 &lt;p&gt;If a given button has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllLinks&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the IDs of all links on the page&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the IDs of all links on the page.
 
 &lt;p&gt;If a given link has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllFields&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the IDs of all field on the page&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the IDs of all input fields on the page.
 
 &lt;p&gt;If a given field has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAttributeFromAllWindows&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the set of values of this attribute from all known windows.&lt;/return&gt;
-
+
 &lt;param name=&quot;attributeName&quot;&gt;name of an attribute on the windows&lt;/param&gt;
-
+
 &lt;comment&gt;Returns every instance of some attribute from all known windows.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;dragdrop&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;param name=&quot;movementsString&quot;&gt;offset in pixels from the current location to which the element should be moved, e.g., &quot;+70,-300&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;deprecated - use dragAndDrop instead&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setMouseSpeed&quot;&gt;
-
+
 &lt;param name=&quot;pixels&quot;&gt;the number of pixels between &quot;mousemove&quot; events&lt;/param&gt;
-
+
 &lt;comment&gt;Configure the number of pixels between &quot;mousemove&quot; events during dragAndDrop commands (default=10).
 &lt;p&gt;Setting this value to 0 means that we'll send a &quot;mousemove&quot; event to every single pixel
 in between the start location and the end location; that can be very slow, and may
@@ -1099,226 +1099,226 @@ cause some browsers to force the JavaScript to timeout.&lt;/p&gt;
 
 &lt;p&gt;If the mouse speed is greater than the distance between the two dragged objects, we'll
 just send one &quot;mousemove&quot; at the start location and then one final one at the end location.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getMouseSpeed&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;the number of pixels between &quot;mousemove&quot; events during dragAndDrop commands (default=10)&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the number of pixels between &quot;mousemove&quot; events during dragAndDrop commands (default=10).&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;dragAndDrop&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an element locator&lt;/param&gt;
-
+
 &lt;param name=&quot;movementsString&quot;&gt;offset in pixels from the current location to which the element should be moved, e.g., &quot;+70,-300&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Drags an element a certain distance and then drops it&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;dragAndDropToObject&quot;&gt;
-
+
 &lt;param name=&quot;locatorOfObjectToBeDragged&quot;&gt;an element to be dragged&lt;/param&gt;
-
+
 &lt;param name=&quot;locatorOfDragDestinationObject&quot;&gt;an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged  is dropped&lt;/param&gt;
-
+
 &lt;comment&gt;Drags an element and drops it on another element&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;windowFocus&quot;&gt;
-
+
 &lt;comment&gt;Gives focus to the currently selected window&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;windowMaximize&quot;&gt;
-
+
 &lt;comment&gt;Resize currently selected window to take up the entire screen&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllWindowIds&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the IDs of all windows that the browser knows about.&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the IDs of all windows that the browser knows about.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllWindowNames&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the names of all windows that the browser knows about.&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the names of all windows that the browser knows about.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getAllWindowTitles&quot;&gt;
-
+
 &lt;return type=&quot;string[]&quot;&gt;the titles of all windows that the browser knows about.&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the titles of all windows that the browser knows about.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getHtmlSource&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the entire HTML source&lt;/return&gt;
-
+
 &lt;comment&gt;Returns the entire HTML source between the opening and
 closing &quot;html&quot; tags.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setCursorPosition&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an input element or textarea&lt;/param&gt;
-
+
 &lt;param name=&quot;position&quot;&gt;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.&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getElementIndex&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;of relative index of the element to its parent (starting from 0)&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element&lt;/param&gt;
-
+
 &lt;comment&gt;Get the relative index of an element to its parent (starting from 0). The comment node and empty text node
 will be ignored.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;isOrdered&quot;&gt;
-
+
 &lt;return type=&quot;boolean&quot;&gt;true if element1 is the previous sibling of element2, false otherwise&lt;/return&gt;
-
+
 &lt;param name=&quot;locator1&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to the first element&lt;/param&gt;
-
+
 &lt;param name=&quot;locator2&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to the second element&lt;/param&gt;
-
+
 &lt;comment&gt;Check if these two elements have same parent and are ordered siblings in the DOM. Two same elements will
 not be considered ordered.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getElementPositionLeft&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;of pixels from the edge of the frame.&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element OR an element itself&lt;/param&gt;
-
+
 &lt;comment&gt;Retrieves the horizontal position of an element&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getElementPositionTop&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;of pixels from the edge of the frame.&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element OR an element itself&lt;/param&gt;
-
+
 &lt;comment&gt;Retrieves the vertical position of an element&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getElementWidth&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;width of an element in pixels&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element&lt;/param&gt;
-
+
 &lt;comment&gt;Retrieves the width of an element&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getElementHeight&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;height of an element in pixels&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element&lt;/param&gt;
-
+
 &lt;comment&gt;Retrieves the height of an element&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getCursorPosition&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;the numerical position of the cursor in the field&lt;/return&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an input element or textarea&lt;/param&gt;
-
+
 &lt;comment&gt;Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers.
 
 &lt;p&gt;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 &lt;a href=&quot;http://jira.openqa.org/browse/SEL-243&quot;&gt;SEL-243&lt;/a&gt;.&lt;/p&gt;
 This method will fail if the specified element isn't an input element or textarea, or there is no cursor in the element.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getExpression&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;the value passed in&lt;/return&gt;
-
+
 &lt;param name=&quot;expression&quot;&gt;the value to return&lt;/param&gt;
-
+
 &lt;comment&gt;Returns the specified expression.
 
 &lt;p&gt;This is useful because of JavaScript preprocessing.
 It is used to generate commands like assertExpression and waitForExpression.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getXpathCount&quot;&gt;
-
+
 &lt;return type=&quot;number&quot;&gt;the number of nodes that match the specified xpath&lt;/return&gt;
-
+
 &lt;param name=&quot;xpath&quot;&gt;the xpath expression to evaluate. do NOT wrap this expression in a 'count()' function; we will do that for you.&lt;/param&gt;
-
+
 &lt;comment&gt;Returns the number of nodes that match the specified xpath, eg. &quot;//table&quot; would give
 the number of tables.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;assignId&quot;&gt;
-
+
 &lt;param name=&quot;locator&quot;&gt;an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element&lt;/param&gt;
-
+
 &lt;param name=&quot;identifier&quot;&gt;a string to be used as the ID of the specified element&lt;/param&gt;
-
+
 &lt;comment&gt;Temporarily sets the &quot;id&quot; 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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;allowNativeXpath&quot;&gt;
-
+
 &lt;param name=&quot;allow&quot;&gt;boolean, true means we'll prefer to use native XPath; false means we'll only use JS XPath&lt;/param&gt;
-
+
 &lt;comment&gt;Specifies whether Selenium should use the native in-browser implementation
 of XPath (if any native version is available); if you pass &quot;false&quot; 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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;waitForCondition&quot;&gt;
-
+
 &lt;param name=&quot;script&quot;&gt;the JavaScript snippet to run&lt;/param&gt;
-
+
 &lt;param name=&quot;timeout&quot;&gt;a timeout in milliseconds, after which this command will return with an error&lt;/param&gt;
-
+
 &lt;comment&gt;Runs the specified JavaScript snippet repeatedly until it evaluates to &quot;true&quot;.
 The snippet may have multiple lines, but only the result of the last line
 will be considered.
@@ -1327,24 +1327,24 @@ will be considered.
 of your application.  To get the window of your application, you can use
 the JavaScript snippet &lt;code&gt;selenium.browserbot.getCurrentWindow()&lt;/code&gt;, and then
 run your JavaScript in there&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setTimeout&quot;&gt;
-
+
 &lt;param name=&quot;timeout&quot;&gt;a timeout in milliseconds, after which the action will return with an error&lt;/param&gt;
-
+
 &lt;comment&gt;Specifies the amount of time that Selenium will wait for actions to complete.
 
 &lt;p&gt;Actions that require waiting include &quot;open&quot; and the &quot;waitFor*&quot; actions.&lt;/p&gt;
 The default timeout is 30 seconds.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;waitForPageToLoad&quot;&gt;
-
+
 &lt;param name=&quot;timeout&quot;&gt;a timeout in milliseconds, after which this command will return with an error&lt;/param&gt;
-
+
 &lt;comment&gt;Waits for a new page to load.
 
 &lt;p&gt;You can use this command instead of the &quot;AndWait&quot; suffixes, &quot;clickAndWait&quot;, &quot;selectAndWait&quot;, &quot;typeAndWait&quot; etc.
@@ -1354,68 +1354,68 @@ The default timeout is 30 seconds.&lt;/comment&gt;
 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.&lt;/p&gt;&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;waitForFrameToLoad&quot;&gt;
-
+
 &lt;param name=&quot;frameAddress&quot;&gt;FrameAddress from the server side&lt;/param&gt;
-
+
 &lt;param name=&quot;timeout&quot;&gt;a timeout in milliseconds, after which this command will return with an error&lt;/param&gt;
-
+
 &lt;comment&gt;Waits for a new frame to load.
 
 &lt;p&gt;Selenium constantly keeps track of new pages and frames loading, 
 and sets a &quot;newPageLoaded&quot; flag when it first notices a page load.&lt;/p&gt;
 
 See waitForPageToLoad for more information.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;getCookie&quot;&gt;
-
+
 &lt;return type=&quot;string&quot;&gt;all cookies of the current page under test&lt;/return&gt;
-
+
 &lt;comment&gt;Return all cookies of the current page under test.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;createCookie&quot;&gt;
-
+
 &lt;param name=&quot;nameValuePair&quot;&gt;name and value of the cookie in a format &quot;name=value&quot;&lt;/param&gt;
-
+
 &lt;param name=&quot;optionsString&quot;&gt;options for the cookie. Currently supported options include 'path' and 'max_age'.      the optionsString's format is &quot;path=/path/, max_age=60&quot;. The order of options are irrelevant, the unit      of the value of 'max_age' is second.&lt;/param&gt;
-
+
 &lt;comment&gt;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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;deleteCookie&quot;&gt;
-
+
 &lt;param name=&quot;name&quot;&gt;the name of the cookie to be deleted&lt;/param&gt;
-
+
 &lt;param name=&quot;path&quot;&gt;the path property of the cookie to be deleted&lt;/param&gt;
-
+
 &lt;comment&gt;Delete a named cookie with specified path.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setBrowserLogLevel&quot;&gt;
-
+
 &lt;param name=&quot;logLevel&quot;&gt;one of the following: &quot;debug&quot;, &quot;info&quot;, &quot;warn&quot;, &quot;error&quot; or &quot;off&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Sets the threshold for browser-side logging messages; log messages beneath this threshold will be discarded.
 Valid logLevel strings are: &quot;debug&quot;, &quot;info&quot;, &quot;warn&quot;, &quot;error&quot; or &quot;off&quot;.
 To see the browser logs, you need to
 either show the log window in GUI mode, or enable browser-side logging in Selenium RC.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;runScript&quot;&gt;
-
+
 &lt;param name=&quot;script&quot;&gt;the JavaScript snippet to run&lt;/param&gt;
-
+
 &lt;comment&gt;Creates a new &quot;script&quot; 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
@@ -1423,15 +1423,15 @@ Selenium's &quot;getEval&quot; 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.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;addLocationStrategy&quot;&gt;
-
+
 &lt;param name=&quot;strategyName&quot;&gt;the name of the strategy to define; this should use only   letters [a-zA-Z] with no spaces or other punctuation.&lt;/param&gt;
-
+
 &lt;param name=&quot;functionDefinition&quot;&gt;a string defining the body of a function in JavaScript.   For example: &lt;code&gt;return inDocument.getElementById(locator);&lt;/code&gt;&lt;/param&gt;
-
+
 &lt;comment&gt;Defines a new function for Selenium to locate elements on the page.
 For example,
 if you define the strategy &quot;foo&quot;, and someone runs click(&quot;foo=blah&quot;), we'll
@@ -1446,24 +1446,24 @@ We'll pass three arguments to your function:
 &lt;li&gt;inDocument: the currently selected document&lt;/li&gt;
 &lt;/ul&gt;
 The function must return null if the element can't be found.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;setContext&quot;&gt;
-
+
 &lt;param name=&quot;context&quot;&gt;the message to be sent to the browser&lt;/param&gt;
-
+
 &lt;comment&gt;Writes a message to the status bar and adds a note to the browser-side
 log.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
+
 &lt;function name=&quot;captureScreenshot&quot;&gt;
-
+
 &lt;param name=&quot;filename&quot;&gt;the absolute path to the file to be written, e.g. &quot;c:\blah\screenshot.png&quot;&lt;/param&gt;
-
+
 &lt;comment&gt;Captures a PNG screenshot to the specified file.&lt;/comment&gt;
-
+
 &lt;/function&gt;
-
-&lt;/apidoc&gt;
+
+&lt;/apidoc&gt;</diff>
      <filename>testframework/selenium/iedoc.xml</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +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&lt;a?'':e(parseInt(c/a)))+((c=c%a)&gt;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=&quot;2.0.2&quot;;7 C=/\\s*,\\s*/;7 x=6(s,A){33{7 m=[];7 u=1z.32.2c&amp;&amp;!A;7 b=(A)?(A.31==22)?A:[A]:[1g];7 1E=18(s).1l(C),i;9(i=0;i&lt;1E.y;i++){s=1y(1E[i]);8(U&amp;&amp;s.Z(0,3).2b(&quot;&quot;)==&quot; *#&quot;){s=s.Z(2);A=24([],b,s[1])}1A A=b;7 j=0,t,f,a,c=&quot;&quot;;H(j&lt;s.y){t=s[j++];f=s[j++];c+=t+f;a=&quot;&quot;;8(s[j]==&quot;(&quot;){H(s[j++]!=&quot;)&quot;)a+=s[j];a=a.Z(0,-1);c+=&quot;(&quot;+a+&quot;)&quot;}A=(u&amp;&amp;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&quot;6 x() {\\n  [1D &quot;+1D+&quot;]\\n}&quot;};7 V={};x.2c=L;x.2Y=6(s){8(s){s=1y(s).2b(&quot;&quot;);2a V[s]}1A V={}};7 29={};7 19=L;x.15=6(n,s){8(19)1i(&quot;s=&quot;+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[&quot; &quot;]=6(r,f,t,n){7 e,i,j;9(i=0;i&lt;f.y;i++){7 s=X(f[i],t,n);9(j=0;(e=s[j]);j++){8(M(e)&amp;&amp;14(e,n))r.z(e)}}};D[&quot;#&quot;]=6(r,f,i){7 e,j;9(j=0;(e=f[j]);j++)8(e.B==i)r.z(e)};D[&quot;.&quot;]=6(r,f,c){c=12 1t(&quot;(^|\\\\s)&quot;+c+&quot;(\\\\s|$)&quot;);7 e,i;9(i=0;(e=f[i]);i++)8(c.l(e.1V))r.z(e)};D[&quot;:&quot;]=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[&quot;2W&quot;]=6(e){7 d=Q(e);8(d.1C)9(7 i=0;i&lt;d.1C.y;i++){8(d.1C[i]==e)5 K}};h[&quot;2V&quot;]=6(e){};7 M=6(e){5(e&amp;&amp;e.1c==1&amp;&amp;e.1f!=&quot;!&quot;)?e:23};7 16=6(e){H(e&amp;&amp;(e=e.2U)&amp;&amp;!M(e))28;5 e};7 G=6(e){H(e&amp;&amp;(e=e.2T)&amp;&amp;!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==&quot;2R&quot;)?/\\.1J$/i.l(d.2Q):2P(d.25==&quot;2O 2N&quot;)};7 Q=6(e){5 e.2M||e.1g};7 X=6(e,t){5(t==&quot;*&quot;&amp;&amp;e.1B)?e.1B:e.X(t)};7 17=6(e,t,n){8(t==&quot;*&quot;)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==&quot;*&quot;)||(e.2K==n)};7 1e=6(e){5 e.1G};6 24(r,f,B){7 m,i,j;9(i=0;i&lt;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&lt;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&lt;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&gt;+~]/;7 20=/[\\s#.:&gt;+~()@]|[^\\s#.:&gt;+~()@]+/g;6 1y(s){8(S.l(s))s=&quot; &quot;+s;5 s.P(20)||[]};7 W=/\\s*([\\s&gt;+~(),]|^|$)\\s*/g;7 I=/([\\s&gt;+~,]|[^(]\\+|^)([#.:@])/g;7 18=6(s){5 s.O(W,&quot;$1&quot;).O(I,&quot;$1*$2&quot;)};7 1u={1Z:6(){5&quot;\'&quot;},P:/^(\'[^\']*\')|(&quot;[^&quot;]*&quot;)$/,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,&quot;\\\\$1&quot;)};x.15(&quot;1j-2H&quot;,6(){D[&quot;&gt;&quot;]=6(r,f,t,n){7 e,i,j;9(i=0;i&lt;f.y;i++){7 s=1o(f[i]);9(j=0;(e=s[j]);j++)8(17(e,t,n))r.z(e)}};D[&quot;+&quot;]=6(r,f,t,n){9(7 i=0;i&lt;f.y;i++){7 e=G(f[i]);8(e&amp;&amp;17(e,t,n))r.z(e)}};D[&quot;@&quot;]=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[&quot;2G-10&quot;]=6(e){5!16(e)};h[&quot;1x&quot;]=6(e,c){c=12 1t(&quot;^&quot;+c,&quot;i&quot;);H(e&amp;&amp;!e.13(&quot;1x&quot;))e=e.1n;5 e&amp;&amp;c.l(e.13(&quot;1x&quot;))};q.1X=/\\\\:/g;q.1w=&quot;@&quot;;q.J={};q.O=6(m,a,n,c,v){7 k=o.1w+m;8(!T[k]){a=o.1W(a,c||&quot;&quot;,v||&quot;&quot;);T[k]=a;T.z(a)}5 T[k].B};q.1Q=6(s){s=s.O(o.1X,&quot;|&quot;);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(&quot;e&quot;,&quot;5 &quot;+t);5 a};q.13=6(n){1d(n.2D()){F&quot;B&quot;:5&quot;e.B&quot;;F&quot;2C&quot;:5&quot;e.1V&quot;;F&quot;9&quot;:5&quot;e.2B&quot;;F&quot;1T&quot;:8(U){5&quot;1U((e.2A.P(/1T=\\\\1v?([^\\\\s\\\\1v]*)\\\\1v?/)||[])[1]||\'\')&quot;}}5&quot;e.13(\'&quot;+n.O(N,&quot;:&quot;)+&quot;\')&quot;};q.J[&quot;&quot;]=6(a){5 a};q.J[&quot;=&quot;]=6(a,v){5 a+&quot;==&quot;+1u.1S(v)};q.J[&quot;~=&quot;]=6(a,v){5&quot;/(^| )&quot;+R(v)+&quot;( |$)/.l(&quot;+a+&quot;)&quot;};q.J[&quot;|=&quot;]=6(a,v){5&quot;/^&quot;+R(v)+&quot;(-|$)/.l(&quot;+a+&quot;)&quot;};7 1R=18;18=6(s){5 1R(q.1Q(s))}});x.15(&quot;1j-2z&quot;,6(){D[&quot;~&quot;]=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[&quot;2y&quot;]=6(e,t){t=12 1t(R(1s(t)));5 t.l(1e(e))};h[&quot;2x&quot;]=6(e){5 e==Q(e).1H};h[&quot;2w&quot;]=6(e){7 n,i;9(i=0;(n=e.1F[i]);i++){8(M(n)||n.1c==3)5 L}5 K};h[&quot;1N-10&quot;]=6(e){5!G(e)};h[&quot;2v-10&quot;]=6(e){e=e.1n;5 1r(e)==1P(e)};h[&quot;2u&quot;]=6(e,s){7 n=x(s,Q(e));9(7 i=0;i&lt;n.y;i++){8(n[i]==e)5 L}5 K};h[&quot;1O-10&quot;]=6(e,a){5 1p(e,a,16)};h[&quot;1O-1N-10&quot;]=6(e,a){5 1p(e,a,G)};h[&quot;2t&quot;]=6(e){5 e.B==2s.2r.Z(1)};h[&quot;1M&quot;]=6(e){5 e.1M};h[&quot;2q&quot;]=6(e){5 e.1q===L};h[&quot;1q&quot;]=6(e){5 e.1q};h[&quot;1L&quot;]=6(e){5 e.1L};q.J[&quot;^=&quot;]=6(a,v){5&quot;/^&quot;+R(v)+&quot;/.l(&quot;+a+&quot;)&quot;};q.J[&quot;$=&quot;]=6(a,v){5&quot;/&quot;+R(v)+&quot;$/.l(&quot;+a+&quot;)&quot;};q.J[&quot;*=&quot;]=6(a,v){5&quot;/&quot;+R(v)+&quot;/.l(&quot;+a+&quot;)&quot;};6 1p(e,a,t){1d(a){F&quot;n&quot;:5 K;F&quot;2p&quot;:a=&quot;2n&quot;;1a;F&quot;2o&quot;:a=&quot;2n+1&quot;}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(&quot;n&quot;);7 m=1K(a[0]);7 s=1K(a[1]);8((Y(m)||m==1)&amp;&amp;s==0)5 K;8(m==0&amp;&amp;!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&lt;=s):(s&gt;=c);5(c%m)==s}});x.15(&quot;1j-2m&quot;,6(){U=1i(&quot;L;/*@2l@8(@\\2k)U=K@2j@*/&quot;);8(!U){X=6(e,t,n){5 n?e.2i(&quot;*&quot;,t):e.X(t)};14=6(e,n){5!n||(n==&quot;*&quot;)||(e.2h==n)};1h=1g.1I?6(e){5/1J/i.l(Q(e).1I)}:6(e){5 Q(e).1H.1f!=&quot;2g&quot;};1e=6(e){5 e.2f||e.1G||1b(e)};6 1b(e){7 t=&quot;&quot;,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,{}))
+/*
+	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&lt;a?'':e(parseInt(c/a)))+((c=c%a)&gt;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=&quot;2.0.2&quot;;7 C=/\\s*,\\s*/;7 x=6(s,A){33{7 m=[];7 u=1z.32.2c&amp;&amp;!A;7 b=(A)?(A.31==22)?A:[A]:[1g];7 1E=18(s).1l(C),i;9(i=0;i&lt;1E.y;i++){s=1y(1E[i]);8(U&amp;&amp;s.Z(0,3).2b(&quot;&quot;)==&quot; *#&quot;){s=s.Z(2);A=24([],b,s[1])}1A A=b;7 j=0,t,f,a,c=&quot;&quot;;H(j&lt;s.y){t=s[j++];f=s[j++];c+=t+f;a=&quot;&quot;;8(s[j]==&quot;(&quot;){H(s[j++]!=&quot;)&quot;)a+=s[j];a=a.Z(0,-1);c+=&quot;(&quot;+a+&quot;)&quot;}A=(u&amp;&amp;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&quot;6 x() {\\n  [1D &quot;+1D+&quot;]\\n}&quot;};7 V={};x.2c=L;x.2Y=6(s){8(s){s=1y(s).2b(&quot;&quot;);2a V[s]}1A V={}};7 29={};7 19=L;x.15=6(n,s){8(19)1i(&quot;s=&quot;+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[&quot; &quot;]=6(r,f,t,n){7 e,i,j;9(i=0;i&lt;f.y;i++){7 s=X(f[i],t,n);9(j=0;(e=s[j]);j++){8(M(e)&amp;&amp;14(e,n))r.z(e)}}};D[&quot;#&quot;]=6(r,f,i){7 e,j;9(j=0;(e=f[j]);j++)8(e.B==i)r.z(e)};D[&quot;.&quot;]=6(r,f,c){c=12 1t(&quot;(^|\\\\s)&quot;+c+&quot;(\\\\s|$)&quot;);7 e,i;9(i=0;(e=f[i]);i++)8(c.l(e.1V))r.z(e)};D[&quot;:&quot;]=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[&quot;2W&quot;]=6(e){7 d=Q(e);8(d.1C)9(7 i=0;i&lt;d.1C.y;i++){8(d.1C[i]==e)5 K}};h[&quot;2V&quot;]=6(e){};7 M=6(e){5(e&amp;&amp;e.1c==1&amp;&amp;e.1f!=&quot;!&quot;)?e:23};7 16=6(e){H(e&amp;&amp;(e=e.2U)&amp;&amp;!M(e))28;5 e};7 G=6(e){H(e&amp;&amp;(e=e.2T)&amp;&amp;!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==&quot;2R&quot;)?/\\.1J$/i.l(d.2Q):2P(d.25==&quot;2O 2N&quot;)};7 Q=6(e){5 e.2M||e.1g};7 X=6(e,t){5(t==&quot;*&quot;&amp;&amp;e.1B)?e.1B:e.X(t)};7 17=6(e,t,n){8(t==&quot;*&quot;)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==&quot;*&quot;)||(e.2K==n)};7 1e=6(e){5 e.1G};6 24(r,f,B){7 m,i,j;9(i=0;i&lt;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&lt;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&lt;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&gt;+~]/;7 20=/[\\s#.:&gt;+~()@]|[^\\s#.:&gt;+~()@]+/g;6 1y(s){8(S.l(s))s=&quot; &quot;+s;5 s.P(20)||[]};7 W=/\\s*([\\s&gt;+~(),]|^|$)\\s*/g;7 I=/([\\s&gt;+~,]|[^(]\\+|^)([#.:@])/g;7 18=6(s){5 s.O(W,&quot;$1&quot;).O(I,&quot;$1*$2&quot;)};7 1u={1Z:6(){5&quot;\'&quot;},P:/^(\'[^\']*\')|(&quot;[^&quot;]*&quot;)$/,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,&quot;\\\\$1&quot;)};x.15(&quot;1j-2H&quot;,6(){D[&quot;&gt;&quot;]=6(r,f,t,n){7 e,i,j;9(i=0;i&lt;f.y;i++){7 s=1o(f[i]);9(j=0;(e=s[j]);j++)8(17(e,t,n))r.z(e)}};D[&quot;+&quot;]=6(r,f,t,n){9(7 i=0;i&lt;f.y;i++){7 e=G(f[i]);8(e&amp;&amp;17(e,t,n))r.z(e)}};D[&quot;@&quot;]=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[&quot;2G-10&quot;]=6(e){5!16(e)};h[&quot;1x&quot;]=6(e,c){c=12 1t(&quot;^&quot;+c,&quot;i&quot;);H(e&amp;&amp;!e.13(&quot;1x&quot;))e=e.1n;5 e&amp;&amp;c.l(e.13(&quot;1x&quot;))};q.1X=/\\\\:/g;q.1w=&quot;@&quot;;q.J={};q.O=6(m,a,n,c,v){7 k=o.1w+m;8(!T[k]){a=o.1W(a,c||&quot;&quot;,v||&quot;&quot;);T[k]=a;T.z(a)}5 T[k].B};q.1Q=6(s){s=s.O(o.1X,&quot;|&quot;);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(&quot;e&quot;,&quot;5 &quot;+t);5 a};q.13=6(n){1d(n.2D()){F&quot;B&quot;:5&quot;e.B&quot;;F&quot;2C&quot;:5&quot;e.1V&quot;;F&quot;9&quot;:5&quot;e.2B&quot;;F&quot;1T&quot;:8(U){5&quot;1U((e.2A.P(/1T=\\\\1v?([^\\\\s\\\\1v]*)\\\\1v?/)||[])[1]||\'\')&quot;}}5&quot;e.13(\'&quot;+n.O(N,&quot;:&quot;)+&quot;\')&quot;};q.J[&quot;&quot;]=6(a){5 a};q.J[&quot;=&quot;]=6(a,v){5 a+&quot;==&quot;+1u.1S(v)};q.J[&quot;~=&quot;]=6(a,v){5&quot;/(^| )&quot;+R(v)+&quot;( |$)/.l(&quot;+a+&quot;)&quot;};q.J[&quot;|=&quot;]=6(a,v){5&quot;/^&quot;+R(v)+&quot;(-|$)/.l(&quot;+a+&quot;)&quot;};7 1R=18;18=6(s){5 1R(q.1Q(s))}});x.15(&quot;1j-2z&quot;,6(){D[&quot;~&quot;]=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[&quot;2y&quot;]=6(e,t){t=12 1t(R(1s(t)));5 t.l(1e(e))};h[&quot;2x&quot;]=6(e){5 e==Q(e).1H};h[&quot;2w&quot;]=6(e){7 n,i;9(i=0;(n=e.1F[i]);i++){8(M(n)||n.1c==3)5 L}5 K};h[&quot;1N-10&quot;]=6(e){5!G(e)};h[&quot;2v-10&quot;]=6(e){e=e.1n;5 1r(e)==1P(e)};h[&quot;2u&quot;]=6(e,s){7 n=x(s,Q(e));9(7 i=0;i&lt;n.y;i++){8(n[i]==e)5 L}5 K};h[&quot;1O-10&quot;]=6(e,a){5 1p(e,a,16)};h[&quot;1O-1N-10&quot;]=6(e,a){5 1p(e,a,G)};h[&quot;2t&quot;]=6(e){5 e.B==2s.2r.Z(1)};h[&quot;1M&quot;]=6(e){5 e.1M};h[&quot;2q&quot;]=6(e){5 e.1q===L};h[&quot;1q&quot;]=6(e){5 e.1q};h[&quot;1L&quot;]=6(e){5 e.1L};q.J[&quot;^=&quot;]=6(a,v){5&quot;/^&quot;+R(v)+&quot;/.l(&quot;+a+&quot;)&quot;};q.J[&quot;$=&quot;]=6(a,v){5&quot;/&quot;+R(v)+&quot;$/.l(&quot;+a+&quot;)&quot;};q.J[&quot;*=&quot;]=6(a,v){5&quot;/&quot;+R(v)+&quot;/.l(&quot;+a+&quot;)&quot;};6 1p(e,a,t){1d(a){F&quot;n&quot;:5 K;F&quot;2p&quot;:a=&quot;2n&quot;;1a;F&quot;2o&quot;:a=&quot;2n+1&quot;}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(&quot;n&quot;);7 m=1K(a[0]);7 s=1K(a[1]);8((Y(m)||m==1)&amp;&amp;s==0)5 K;8(m==0&amp;&amp;!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&lt;=s):(s&gt;=c);5(c%m)==s}});x.15(&quot;1j-2m&quot;,6(){U=1i(&quot;L;/*@2l@8(@\\2k)U=K@2j@*/&quot;);8(!U){X=6(e,t,n){5 n?e.2i(&quot;*&quot;,t):e.X(t)};14=6(e,n){5!n||(n==&quot;*&quot;)||(e.2h==n)};1h=1g.1I?6(e){5/1J/i.l(Q(e).1I)}:6(e){5 Q(e).1H.1f!=&quot;2g&quot;};1e=6(e){5 e.2f||e.1G||1b(e)};6 1b(e){7 t=&quot;&quot;,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,{}))</diff>
      <filename>testframework/selenium/lib/cssQuery/cssQuery-p.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,142 +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(&quot;css-level2&quot;, function() {
-
-// -----------------------------------------------------------------------
-// selectors
-// -----------------------------------------------------------------------
-
-// child selector
-selectors[&quot;&gt;&quot;] = function($results, $from, $tagName, $namespace) {
-	var $element, i, j;
-	for (i = 0; i &lt; $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[&quot;+&quot;] = function($results, $from, $tagName, $namespace) {
-	for (var i = 0; i &lt; $from.length; i++) {
-		var $element = nextElementSibling($from[i]);
-		if ($element &amp;&amp; compareTagName($element, $tagName, $namespace))
-			$results.push($element);
-	}
-};
-
-// attribute selector
-selectors[&quot;@&quot;] = 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[&quot;first-child&quot;] = function($element) {
-	return !previousElementSibling($element);
-};
-
-pseudoClasses[&quot;lang&quot;] = function($element, $code) {
-	$code = new RegExp(&quot;^&quot; + $code, &quot;i&quot;);
-	while ($element &amp;&amp; !$element.getAttribute(&quot;lang&quot;)) $element = $element.parentNode;
-	return $element &amp;&amp; $code.test($element.getAttribute(&quot;lang&quot;));
-};
-
-// -----------------------------------------------------------------------
-//  attribute selectors
-// -----------------------------------------------------------------------
-
-// constants
-AttributeSelector.NS_IE = /\\:/g;
-AttributeSelector.PREFIX = &quot;@&quot;;
-// 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 || &quot;&quot;, $value || &quot;&quot;);
-		// store the selector
-		attributeSelectors[$key] = $attribute;
-		attributeSelectors.push($attribute);
-	}
-	return attributeSelectors[$key].id;
-};
-AttributeSelector.parse = function($selector) {
-	$selector = $selector.replace(this.NS_IE, &quot;|&quot;);
-	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(&quot;e&quot;, &quot;return &quot; + $test);
-	return $attributeSelector;
-};
-AttributeSelector.getAttribute = function($name) {
-	switch ($name.toLowerCase()) {
-		case &quot;id&quot;:
-			return &quot;e.id&quot;;
-		case &quot;class&quot;:
-			return &quot;e.className&quot;;
-		case &quot;for&quot;:
-			return &quot;e.htmlFor&quot;;
-		case &quot;href&quot;:
-			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 &quot;String((e.outerHTML.match(/href=\\x22?([^\\s\\x22]*)\\x22?/)||[])[1]||'')&quot;;
-			}
-	}
-	return &quot;e.getAttribute('&quot; + $name.replace($NAMESPACE, &quot;:&quot;) + &quot;')&quot;;
-};
-
-// -----------------------------------------------------------------------
-//  attribute selector tests
-// -----------------------------------------------------------------------
-
-AttributeSelector.tests[&quot;&quot;] = function($attribute) {
-	return $attribute;
-};
-
-AttributeSelector.tests[&quot;=&quot;] = function($attribute, $value) {
-	return $attribute + &quot;==&quot; + Quote.add($value);
-};
-
-AttributeSelector.tests[&quot;~=&quot;] = function($attribute, $value) {
-	return &quot;/(^| )&quot; + regEscape($value) + &quot;( |$)/.test(&quot; + $attribute + &quot;)&quot;;
-};
-
-AttributeSelector.tests[&quot;|=&quot;] = function($attribute, $value) {
-	return &quot;/^&quot; + regEscape($value) + &quot;(-|$)/.test(&quot; + $attribute + &quot;)&quot;;
-};
-
-// -----------------------------------------------------------------------
-//  parsing
-// -----------------------------------------------------------------------
-
-// override parseSelector to parse out attribute selectors
-var _parseSelector = parseSelector;
-parseSelector = function($selector) {
-	return _parseSelector(AttributeSelector.parse($selector));
-};
-
-}); // addModule
+/*
+	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(&quot;css-level2&quot;, function() {
+
+// -----------------------------------------------------------------------
+// selectors
+// -----------------------------------------------------------------------
+
+// child selector
+selectors[&quot;&gt;&quot;] = function($results, $from, $tagName, $namespace) {
+	var $element, i, j;
+	for (i = 0; i &lt; $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[&quot;+&quot;] = function($results, $from, $tagName, $namespace) {
+	for (var i = 0; i &lt; $from.length; i++) {
+		var $element = nextElementSibling($from[i]);
+		if ($element &amp;&amp; compareTagName($element, $tagName, $namespace))
+			$results.push($element);
+	}
+};
+
+// attribute selector
+selectors[&quot;@&quot;] = 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[&quot;first-child&quot;] = function($element) {
+	return !previousElementSibling($element);
+};
+
+pseudoClasses[&quot;lang&quot;] = function($element, $code) {
+	$code = new RegExp(&quot;^&quot; + $code, &quot;i&quot;);
+	while ($element &amp;&amp; !$element.getAttribute(&quot;lang&quot;)) $element = $element.parentNode;
+	return $element &amp;&amp; $code.test($element.getAttribute(&quot;lang&quot;));
+};
+
+// -----------------------------------------------------------------------
+//  attribute selectors
+// -----------------------------------------------------------------------
+
+// constants
+AttributeSelector.NS_IE = /\\:/g;
+AttributeSelector.PREFIX = &quot;@&quot;;
+// 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 || &quot;&quot;, $value || &quot;&quot;);
+		// store the selector
+		attributeSelectors[$key] = $attribute;
+		attributeSelectors.push($attribute);
+	}
+	return attributeSelectors[$key].id;
+};
+AttributeSelector.parse = function($selector) {
+	$selector = $selector.replace(this.NS_IE, &quot;|&quot;);
+	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(&quot;e&quot;, &quot;return &quot; + $test);
+	return $attributeSelector;
+};
+AttributeSelector.getAttribute = function($name) {
+	switch ($name.toLowerCase()) {
+		case &quot;id&quot;:
+			return &quot;e.id&quot;;
+		case &quot;class&quot;:
+			return &quot;e.className&quot;;
+		case &quot;for&quot;:
+			return &quot;e.htmlFor&quot;;
+		case &quot;href&quot;:
+			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 &quot;String((e.outerHTML.match(/href=\\x22?([^\\s\\x22]*)\\x22?/)||[])[1]||'')&quot;;
+			}
+	}
+	return &quot;e.getAttribute('&quot; + $name.replace($NAMESPACE, &quot;:&quot;) + &quot;')&quot;;
+};
+
+// -----------------------------------------------------------------------
+//  attribute selector tests
+// -----------------------------------------------------------------------
+
+AttributeSelector.tests[&quot;&quot;] = function($attribute) {
+	return $attribute;
+};
+
+AttributeSelector.tests[&quot;=&quot;] = function($attribute, $value) {
+	return $attribute + &quot;==&quot; + Quote.add($value);
+};
+
+AttributeSelector.tests[&quot;~=&quot;] = function($attribute, $value) {
+	return &quot;/(^| )&quot; + regEscape($value) + &quot;( |$)/.test(&quot; + $attribute + &quot;)&quot;;
+};
+
+AttributeSelector.tests[&quot;|=&quot;] = function($attribute, $value) {
+	return &quot;/^&quot; + regEscape($value) + &quot;(-|$)/.test(&quot; + $attribute + &quot;)&quot;;
+};
+
+// -----------------------------------------------------------------------
+//  parsing
+// -----------------------------------------------------------------------
+
+// override parseSelector to parse out attribute selectors
+var _parseSelector = parseSelector;
+parseSelector = function($selector) {
+	return _parseSelector(AttributeSelector.parse($selector));
+};
+
+}); // addModule</diff>
      <filename>testframework/selenium/lib/cssQuery/src/cssQuery-level2.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,150 +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(&quot;css-level3&quot;, function() {
-
-// -----------------------------------------------------------------------
-// selectors
-// -----------------------------------------------------------------------
-
-// indirect sibling selector
-selectors[&quot;~&quot;] = 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[&quot;contains&quot;] = function($element, $text) {
-	$text = new RegExp(regEscape(getText($text)));
-	return $text.test(getTextContent($element));
-};
-
-pseudoClasses[&quot;root&quot;] = function($element) {
-	return $element == getDocument($element).documentElement;
-};
-
-pseudoClasses[&quot;empty&quot;] = function($element) {
-	var $node, i;
-	for (i = 0; ($node = $element.childNodes[i]); i++) {
-		if (thisElement($node) || $node.nodeType == 3) return false;
-	}
-	return true;
-};
-
-pseudoClasses[&quot;last-child&quot;] = function($element) {
-	return !nextElementSibling($element);
-};
-
-pseudoClasses[&quot;only-child&quot;] = function($element) {
-	$element = $element.parentNode;
-	return firstElementChild($element) == lastElementChild($element);
-};
-
-pseudoClasses[&quot;not&quot;] = function($element, $selector) {
-	var $negated = cssQuery($selector, getDocument($element));
-	for (var i = 0; i &lt; $negated.length; i++) {
-		if ($negated[i] == $element) return false;
-	}
-	return true;
-};
-
-pseudoClasses[&quot;nth-child&quot;] = function($element, $arguments) {
-	return nthChild($element, $arguments, previousElementSibling);
-};
-
-pseudoClasses[&quot;nth-last-child&quot;] = function($element, $arguments) {
-	return nthChild($element, $arguments, nextElementSibling);
-};
-
-pseudoClasses[&quot;target&quot;] = function($element) {
-	return $element.id == location.hash.slice(1);
-};
-
-// UI element states
-
-pseudoClasses[&quot;checked&quot;] = function($element) {
-	return $element.checked;
-};
-
-pseudoClasses[&quot;enabled&quot;] = function($element) {
-	return $element.disabled === false;
-};
-
-pseudoClasses[&quot;disabled&quot;] = function($element) {
-	return $element.disabled;
-};
-
-pseudoClasses[&quot;indeterminate&quot;] = function($element) {
-	return $element.indeterminate;
-};
-
-// -----------------------------------------------------------------------
-//  attribute selector tests
-// -----------------------------------------------------------------------
-
-AttributeSelector.tests[&quot;^=&quot;] = function($attribute, $value) {
-	return &quot;/^&quot; + regEscape($value) + &quot;/.test(&quot; + $attribute + &quot;)&quot;;
-};
-
-AttributeSelector.tests[&quot;$=&quot;] = function($attribute, $value) {
-	return &quot;/&quot; + regEscape($value) + &quot;$/.test(&quot; + $attribute + &quot;)&quot;;
-};
-
-AttributeSelector.tests[&quot;*=&quot;] = function($attribute, $value) {
-	return &quot;/&quot; + regEscape($value) + &quot;/.test(&quot; + $attribute + &quot;)&quot;;
-};
-
-// -----------------------------------------------------------------------
-//  nth child support (Bill Edney)
-// -----------------------------------------------------------------------
-
-function nthChild($element, $arguments, $traverse) {
-	switch ($arguments) {
-		case &quot;n&quot;: return true;
-		case &quot;even&quot;: $arguments = &quot;2n&quot;; break;
-		case &quot;odd&quot;: $arguments = &quot;2n+1&quot;;
-	}
-
-	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 &quot;n&quot;)
-	if (!isNaN($arguments)) return _checkIndex($arguments);
-
-	$arguments = $arguments.split(&quot;n&quot;);
-	var $multiplier = parseInt($arguments[0]);
-	var $step = parseInt($arguments[1]);
-
-	if ((isNaN($multiplier) || $multiplier == 1) &amp;&amp; $step == 0) return true;
-	if ($multiplier == 0 &amp;&amp; !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 &lt;= $step) : ($step &gt;= $count);
-
-	return ($count % $multiplier) == $step;
-};
-
-}); // addModule
+/*
+	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(&quot;css-level3&quot;, function() {
+
+// -----------------------------------------------------------------------
+// selectors
+// -----------------------------------------------------------------------
+
+// indirect sibling selector
+selectors[&quot;~&quot;] = 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[&quot;contains&quot;] = function($element, $text) {
+	$text = new RegExp(regEscape(getText($text)));
+	return $text.test(getTextContent($element));
+};
+
+pseudoClasses[&quot;root&quot;] = function($element) {
+	return $element == getDocument($element).documentElement;
+};
+
+pseudoClasses[&quot;empty&quot;] = function($element) {
+	var $node, i;
+	for (i = 0; ($node = $element.childNodes[i]); i++) {
+		if (thisElement($node) || $node.nodeType == 3) return false;
+	}
+	return true;
+};
+
+pseudoClasses[&quot;last-child&quot;] = function($element) {
+	return !nextElementSibling($element);
+};
+
+pseudoClasses[&quot;only-child&quot;] = function($element) {
+	$element = $element.parentNode;
+	return firstElementChild($element) == lastElementChild($element);
+};
+
+pseudoClasses[&quot;not&quot;] = function($element, $selector) {
+	var $negated = cssQuery($selector, getDocument($element));
+	for (var i = 0; i &lt; $negated.length; i++) {
+		if ($negated[i] == $element) return false;
+	}
+	return true;
+};
+
+pseudoClasses[&quot;nth-child&quot;] = function($element, $arguments) {
+	return nthChild($element, $arguments, previousElementSibling);
+};
+
+pseudoClasses[&quot;nth-last-child&quot;] = function($element, $arguments) {
+	return nthChild($element, $arguments, nextElementSibling);
+};
+
+pseudoClasses[&quot;target&quot;] = function($element) {
+	return $element.id == location.hash.slice(1);
+};
+
+// UI element states
+
+pseudoClasses[&quot;checked&quot;] = function($element) {
+	return $element.checked;
+};
+
+pseudoClasses[&quot;enabled&quot;] = function($element) {
+	return $element.disabled === false;
+};
+
+pseudoClasses[&quot;disabled&quot;] = function($element) {
+	return $element.disabled;
+};
+
+pseudoClasses[&quot;indeterminate&quot;] = function($element) {
+	return $element.indeterminate;
+};
+
+// -----------------------------------------------------------------------
+//  attribute selector tests
+// -----------------------------------------------------------------------
+
+AttributeSelector.tests[&quot;^=&quot;] = function($attribute, $value) {
+	return &quot;/^&quot; + regEscape($value) + &quot;/.test(&quot; + $attribute + &quot;)&quot;;
+};
+
+AttributeSelector.tests[&quot;$=&quot;] = function($attribute, $value) {
+	return &quot;/&quot; + regEscape($value) + &quot;$/.test(&quot; + $attribute + &quot;)&quot;;
+};
+
+AttributeSelector.tests[&quot;*=&quot;] = function($attribute, $value) {
+	return &quot;/&quot; + regEscape($value) + &quot;/.test(&quot; + $attribute + &quot;)&quot;;
+};
+
+// -----------------------------------------------------------------------
+//  nth child support (Bill Edney)
+// -----------------------------------------------------------------------
+
+function nthChild($element, $arguments, $traverse) {
+	switch ($arguments) {
+		case &quot;n&quot;: return true;
+		case &quot;even&quot;: $arguments = &quot;2n&quot;; break;
+		case &quot;odd&quot;: $arguments = &quot;2n+1&quot;;
+	}
+
+	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 &quot;n&quot;)
+	if (!isNaN($arguments)) return _checkIndex($arguments);
+
+	$arguments = $arguments.split(&quot;n&quot;);
+	var $multiplier = parseInt($arguments[0]);
+	var $step = parseInt($arguments[1]);
+
+	if ((isNaN($multiplier) || $multiplier == 1) &amp;&amp; $step == 0) return true;
+	if ($multiplier == 0 &amp;&amp; !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 &lt;= $step) : ($step &gt;= $count);
+
+	return ($count % $multiplier) == $step;
+};
+
+}); // addModule</diff>
      <filename>testframework/selenium/lib/cssQuery/src/cssQuery-level3.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,53 +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(&quot;css-standard&quot;, 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(&quot;false;/*@cc_on@if(@\x5fwin32)isMSIE=true@end@*/&quot;);
-
-if (!isMSIE) {
-	getElementsByTagName = function($element, $tagName, $namespace) {
-		return $namespace ? $element.getElementsByTagNameNS(&quot;*&quot;, $tagName) :
-			$element.getElementsByTagName($tagName);
-	};
-
-	compareNamespace = function($element, $namespace) {
-		return !$namespace || ($namespace == &quot;*&quot;) || ($element.prefix == $namespace);
-	};
-
-	isXML = document.contentType ? function($element) {
-		return /xml/i.test(getDocument($element).contentType);
-	} : function($element) {
-		return getDocument($element).documentElement.tagName != &quot;HTML&quot;;
-	};
-
-	getTextContent = function($element) {
-		// mozilla || opera || other
-		return $element.textContent || $element.innerText || _getTextContent($element);
-	};
-
-	function _getTextContent($element) {
-		var $textContent = &quot;&quot;, $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
+/*
+	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(&quot;css-standard&quot;, 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(&quot;false;/*@cc_on@if(@\x5fwin32)isMSIE=true@end@*/&quot;);
+
+if (!isMSIE) {
+	getElementsByTagName = function($element, $tagName, $namespace) {
+		return $namespace ? $element.getElementsByTagNameNS(&quot;*&quot;, $tagName) :
+			$element.getElementsByTagName($tagName);
+	};
+
+	compareNamespace = function($element, $namespace) {
+		return !$namespace || ($namespace == &quot;*&quot;) || ($element.prefix == $namespace);
+	};
+
+	isXML = document.contentType ? function($element) {
+		return /xml/i.test(getDocument($element).contentType);
+	} : function($element) {
+		return getDocument($element).documentElement.tagName != &quot;HTML&quot;;
+	};
+
+	getTextContent = function($element) {
+		// mozilla || opera || other
+		return $element.textContent || $element.innerText || _getTextContent($element);
+	};
+
+	function _getTextContent($element) {
+		var $textContent = &quot;&quot;, $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</diff>
      <filename>testframework/selenium/lib/cssQuery/src/cssQuery-standard.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,356 +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 = &quot;2.0.2&quot;;
-
-// -----------------------------------------------------------------------
-// main query function
-// -----------------------------------------------------------------------
-
-var $COMMA = /\s*,\s*/;
-var cssQuery = function($selector, $$from) {
-try {
-	var $match = [];
-	var $useCache = arguments.callee.caching &amp;&amp; !$$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 &lt; $$selectors.length; i++) {
-		// convert the selector to a stream
-		$selector = _toStream($$selectors[i]);
-		// faster chop if it starts with id (MSIE only)
-		if (isMSIE &amp;&amp; $selector.slice(0, 3).join(&quot;&quot;) == &quot; *#&quot;) {
-			$selector = $selector.slice(2);
-			$$from = _msie_selectById([], $base, $selector[1]);
-		} else $$from = $base;
-		// process the stream
-		var j = 0, $token, $filter, $arguments, $cacheSelector = &quot;&quot;;
-		while (j &lt; $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 = &quot;&quot;;
-			if ($selector[j] == &quot;(&quot;) {
-				while ($selector[j++] != &quot;)&quot; &amp;&amp; j &lt; $selector.length) {
-					$arguments += $selector[j];
-				}
-				$arguments = $arguments.slice(0, -1);
-				$cacheSelector += &quot;(&quot; + $arguments + &quot;)&quot;;
-			}
-			// process a token/filter pair use cached results if possible
-			$$from = ($useCache &amp;&amp; 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 &quot;function cssQuery() {\n  [version &quot; + version + &quot;]\n}&quot;;
-};
-
-// caching
-var cache = {};
-cssQuery.caching = false;
-cssQuery.clearCache = function($selector) {
-	if ($selector) {
-		$selector = _toStream($selector).join(&quot;&quot;);
-		delete cache[$selector];
-	} else cache = {};
-};
-
-// allow extensions
-var modules = {};
-var loaded = false;
-cssQuery.addModule = function($name, $script) {
-	if (loaded) eval(&quot;$script=&quot; + 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[&quot; &quot;] = function($results, $from, $tagName, $namespace) {
-	// loop through current selection
-	var $element, i, j;
-	for (i = 0; i &lt; $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) &amp;&amp; compareNamespace($element, $namespace))
-				$results.push($element);
-		}
-	}
-};
-
-// ID selector
-selectors[&quot;#&quot;] = 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[&quot;.&quot;] = function($results, $from, $className) {
-	// create a RegExp version of the class
-	$className = new RegExp(&quot;(^|\\s)&quot; + $className + &quot;(\\s|$)&quot;);
-	// 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[&quot;:&quot;] = 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 &quot;true&quot; add the element
-		if ($test($element, $arguments)) $results.push($element);
-};
-
-// -----------------------------------------------------------------------
-// pseudo-classes
-// -----------------------------------------------------------------------
-
-pseudoClasses[&quot;link&quot;] = function($element) {
-	var $document = getDocument($element);
-	if ($document.links) for (var i = 0; i &lt; $document.links.length; i++) {
-		if ($document.links[i] == $element) return true;
-	}
-};
-
-pseudoClasses[&quot;visited&quot;] = 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 != &quot;!&quot;. LOL (again).
-var thisElement = function($element) {
-	return ($element &amp;&amp; $element.nodeType == 1 &amp;&amp; $element.tagName != &quot;!&quot;) ? $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 &amp;&amp; ($element = $element.previousSibling) &amp;&amp; !thisElement($element)) continue;
-	return $element;
-};
-
-// return the next element to the supplied element
-var nextElementSibling = function($element) {
-	while ($element &amp;&amp; ($element = $element.nextSibling) &amp;&amp; !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 == &quot;unknown&quot;) ?
-		/\.xml$/i.test($document.URL) :
-		Boolean($document.mimeType == &quot;XML Document&quot;);
-};
-
-// return the element's containing document
-var getDocument = function($element) {
-	return $element.ownerDocument || $element.document;
-};
-
-var getElementsByTagName = function($element, $tagName) {
-	return ($tagName == &quot;*&quot; &amp;&amp; $element.all) ? $element.all : $element.getElementsByTagName($tagName);
-};
-
-var compareTagName = function($element, $tagName, $namespace) {
-	if ($tagName == &quot;*&quot;) 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 == &quot;*&quot;) || ($element.scopeName == $namespace);
-};
-
-var getTextContent = function($element) {
-	return $element.innerText;
-};
-
-function _msie_selectById($results, $from, id) {
-	var $match, i, j;
-	for (i = 0; i &lt; $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 &lt; $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 &lt; arguments.length; i++) {
-		this[this.length] = arguments[i];
-	}
-	return this.length;
-};
-
-// -----------------------------------------------------------------------
-// query support
-// -----------------------------------------------------------------------
-
-// select a set of matching elements.
-// &quot;from&quot; is an array of elements.
-// &quot;token&quot; is a character representing the type of filter
-//  e.g. &quot;&gt;&quot; means child selector
-// &quot;filter&quot; 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&gt;+~]/;
-var $$STREAM = /[\s#.:&gt;+~()@]|[^\s#.:&gt;+~()@]+/g;
-function _toStream($selector) {
-	if ($STANDARD_SELECT.test($selector)) $selector = &quot; &quot; + $selector;
-	return $selector.match($$STREAM) || [];
-};
-
-var $WHITESPACE = /\s*([\s&gt;+~(),]|^|$)\s*/g;
-var $IMPLIED_ALL = /([\s&gt;+~,]|[^(]\+|^)([#.:@])/g;
-var parseSelector = function($selector) {
-	return $selector
-	// trim whitespace
-	.replace($WHITESPACE, &quot;$1&quot;)
-	// e.g. &quot;.class1&quot; --&gt; &quot;*.class1&quot;
-	.replace($IMPLIED_ALL, &quot;$1*$2&quot;);
-};
-
-var Quote = {
-	toString: function() {return &quot;'&quot;},
-	match: /^('[^']*')|(&quot;[^&quot;]*&quot;)$/,
-	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, &quot;\\$1&quot;);
-};
-
-// -----------------------------------------------------------------------
-// modules
-// -----------------------------------------------------------------------
-
-// -------- &gt;&gt;      insert modules here for packaging       &lt;&lt; -------- \\
-
-loaded = true;
-
-// -----------------------------------------------------------------------
-// return the query function
-// -----------------------------------------------------------------------
-
-return cssQuery;
-
-}(); // cssQuery
+/*
+	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 = &quot;2.0.2&quot;;
+
+// -----------------------------------------------------------------------
+// main query function
+// -----------------------------------------------------------------------
+
+var $COMMA = /\s*,\s*/;
+var cssQuery = function($selector, $$from) {
+try {
+	var $match = [];
+	var $useCache = arguments.callee.caching &amp;&amp; !$$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 &lt; $$selectors.length; i++) {
+		// convert the selector to a stream
+		$selector = _toStream($$selectors[i]);
+		// faster chop if it starts with id (MSIE only)
+		if (isMSIE &amp;&amp; $selector.slice(0, 3).join(&quot;&quot;) == &quot; *#&quot;) {
+			$selector = $selector.slice(2);
+			$$from = _msie_selectById([], $base, $selector[1]);
+		} else $$from = $base;
+		// process the stream
+		var j = 0, $token, $filter, $arguments, $cacheSelector = &quot;&quot;;
+		while (j &lt; $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 = &quot;&quot;;
+			if ($selector[j] == &quot;(&quot;) {
+				while ($selector[j++] != &quot;)&quot; &amp;&amp; j &lt; $selector.length) {
+					$arguments += $selector[j];
+				}
+				$arguments = $arguments.slice(0, -1);
+				$cacheSelector += &quot;(&quot; + $arguments + &quot;)&quot;;
+			}
+			// process a token/filter pair use cached results if possible
+			$$from = ($useCache &amp;&amp; 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 &quot;function cssQuery() {\n  [version &quot; + version + &quot;]\n}&quot;;
+};
+
+// caching
+var cache = {};
+cssQuery.caching = false;
+cssQuery.clearCache = function($selector) {
+	if ($selector) {
+		$selector = _toStream($selector).join(&quot;&quot;);
+		delete cache[$selector];
+	} else cache = {};
+};
+
+// allow extensions
+var modules = {};
+var loaded = false;
+cssQuery.addModule = function($name, $script) {
+	if (loaded) eval(&quot;$script=&quot; + 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[&quot; &quot;] = function($results, $from, $tagName, $namespace) {
+	// loop through current selection
+	var $element, i, j;
+	for (i = 0; i &lt; $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) &amp;&amp; compareNamespace($element, $namespace))
+				$results.push($element);
+		}
+	}
+};
+
+// ID selector
+selectors[&quot;#&quot;] = 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[&quot;.&quot;] = function($results, $from, $className) {
+	// create a RegExp version of the class
+	$className = new RegExp(&quot;(^|\\s)&quot; + $className + &quot;(\\s|$)&quot;);
+	// 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[&quot;:&quot;] = 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 &quot;true&quot; add the element
+		if ($test($element, $arguments)) $results.push($element);
+};
+
+// -----------------------------------------------------------------------
+// pseudo-classes
+// -----------------------------------------------------------------------
+
+pseudoClasses[&quot;link&quot;] = function($element) {
+	var $document = getDocument($element);
+	if ($document.links) for (var i = 0; i &lt; $document.links.length; i++) {
+		if ($document.links[i] == $element) return true;
+	}
+};
+
+pseudoClasses[&quot;visited&quot;] = 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 != &quot;!&quot;. LOL (again).
+var thisElement = function($element) {
+	return ($element &amp;&amp; $element.nodeType == 1 &amp;&amp; $element.tagName != &quot;!&quot;) ? $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 &amp;&amp; ($element = $element.previousSibling) &amp;&amp; !thisElement($element)) continue;
+	return $element;
+};
+
+// return the next element to the supplied element
+var nextElementSibling = function($element) {
+	while ($element &amp;&amp; ($element = $element.nextSibling) &amp;&amp; !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 == &quot;unknown&quot;) ?
+		/\.xml$/i.test($document.URL) :
+		Boolean($document.mimeType == &quot;XML Document&quot;);
+};
+
+// return the element's containing document
+var getDocument = function($element) {
+	return $element.ownerDocument || $element.document;
+};
+
+var getElementsByTagName = function($element, $tagName) {
+	return ($tagName == &quot;*&quot; &amp;&amp; $element.all) ? $element.all : $element.getElementsByTagName($tagName);
+};
+
+var compareTagName = function($element, $tagName, $namespace) {
+	if ($tagName == &quot;*&quot;) 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 == &quot;*&quot;) || ($element.scopeName == $namespace);
+};
+
+var getTextContent = function($element) {
+	return $element.innerText;
+};
+
+function _msie_selectById($results, $from, id) {
+	var $match, i, j;
+	for (i = 0; i &lt; $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 &lt; $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 &lt; arguments.length; i++) {
+		this[this.length] = arguments[i];
+	}
+	return this.length;
+};
+
+// -----------------------------------------------------------------------
+// query support
+// -----------------------------------------------------------------------
+
+// select a set of matching elements.
+// &quot;from&quot; is an array of elements.
+// &quot;token&quot; is a character representing the type of filter
+//  e.g. &quot;&gt;&quot; means child selector
+// &quot;filter&quot; 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&gt;+~]/;
+var $$STREAM = /[\s#.:&gt;+~()@]|[^\s#.:&gt;+~()@]+/g;
+function _toStream($selector) {
+	if ($STANDARD_SELECT.test($selector)) $selector = &quot; &quot; + $selector;
+	return $selector.match($$STREAM) || [];
+};
+
+var $WHITESPACE = /\s*([\s&gt;+~(),]|^|$)\s*/g;
+var $IMPLIED_ALL = /([\s&gt;+~,]|[^(]\+|^)([#.:@])/g;
+var parseSelector = function($selector) {
+	return $selector
+	// trim whitespace
+	.replace($WHITESPACE, &quot;$1&quot;)
+	// e.g. &quot;.class1&quot; --&gt; &quot;*.class1&quot;
+	.replace($IMPLIED_ALL, &quot;$1*$2&quot;);
+};
+
+var Quote = {
+	toString: function() {return &quot;'&quot;},
+	match: /^('[^']*')|(&quot;[^&quot;]*&quot;)$/,
+	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, &quot;\\$1&quot;);
+};
+
+// -----------------------------------------------------------------------
+// modules
+// -----------------------------------------------------------------------
+
+// -------- &gt;&gt;      insert modules here for packaging       &lt;&lt; -------- \\
+
+loaded = true;
+
+// -----------------------------------------------------------------------
+// return the query function
+// -----------------------------------------------------------------------
+
+return cssQuery;
+
+}(); // cssQuery</diff>
      <filename>testframework/selenium/lib/cssQuery/src/cssQuery.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,69 +1,69 @@
-/*
- * Copyright 2004 ThoughtWorks, Inc
- *
- *  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
- *  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 &quot;AS IS&quot; 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&lt;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&lt;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} );
-}
-
+/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+ *  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 &quot;AS IS&quot; 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&lt;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&lt;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} );
+}
+</diff>
      <filename>testframework/selenium/scripts/find_matching_child.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,660 +1,660 @@
-/*
-	html-xpath, an implementation of DOM Level 3 XPath for Internet Explorer 5+
-	Copyright (C) 2004 Dimitri Glazkov
-
-	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
-
-*/
-
-/** SELENIUM:PATCH TO ALLOW USE WITH DOCUMENTS FROM OTHER WINDOWS: 2004-11-24
-    TODO resubmit this to http://sf.net/projects/html-xpath */
-function addXPathSupport(document) {
-/** END SELENIUM:PATCH */
-
-var isIe = /MSIE [56789]/.test(navigator.userAgent) &amp;&amp; (navigator.platform == &quot;Win32&quot;);
-
-// Mozilla has support by default, we don't have an implementation for the rest
-if (isIe)
-{
-	// release number
-	document.DomL3XPathRelease = &quot;0.0.3.0&quot;;
-	
-	// XPathException
-	// An Error object will be thrown, this is just a handler to instantiate that object
-	var XPathException = new _XPathExceptionHandler();
-	function _XPathExceptionHandler()
-	{
-		this.INVALID_EXPRESSION_ERR = 51;
-		this.TYPE_ERR = 52;
-		this.NOT_IMPLEMENTED_ERR = -1;
-		this.RUNTIME_ERR = -2;
-		
-		this.ThrowNotImplemented = function(message)
-		{
-			ThrowError(this.NOT_IMPLEMENTED_ERR, &quot;This functionality is not implemented.&quot;, message);
-		}
-		
-		this.ThrowInvalidExpression = function(message)
-		{
-			ThrowError(this.INVALID_EXPRESSION_ERR, &quot;Invalid expression&quot;, message);
-		}
-		
-		this.ThrowType = function(message)
-		{
-			ThrowError(this.TYPE_ERR, &quot;Type error&quot;, message);
-		}
-		
-		this.Throw = function(message)
-		{
-			ThrowError(this.RUNTIME_ERR, &quot;Run-time error&quot;, message);
-		}
-		
-		function ThrowError(code, description, message)
-		{
-			var error = new Error(code, &quot;DOM-L3-XPath &quot; + document.DomL3XPathRelease + &quot;: &quot; + description + (message ? &quot;, \&quot;&quot; + message  + &quot;\&quot;&quot;: &quot;&quot;));
-			error.code = code;
-			error.name = &quot;XPathException&quot;;
-			throw error;
-		}
-	}
-	
-	// DOMException
-	// An Error object will be thrown, this is just a handler to instantiate that object
-	var DOMException = new _DOMExceptionHandler();
-	function _DOMExceptionHandler()
-	{
-		this.ThrowInvalidState = function(message)
-		{
-			ThrowError(13, &quot;The state of the object is no longer valid&quot;, message);
-		}
-
-		function ThrowError(code, description, message)
-		{
-			var error = new Error(code, &quot;DOM : &quot; + description + (message ? &quot;, \&quot;&quot; + message  + &quot;\&quot;&quot;: &quot;&quot;));
-			error.code = code;
-			error.name = &quot;DOMException&quot;;
-			throw error;
-		}
-	}
-
-	// XPathEvaluator 
-	// implemented as document object methods
-	
-	// XPathExpression createExpression(String expression, XPathNSResolver resolver)
-	document.createExpression = function
-		(
-		expression,		// String
-		resolver		// XPathNSResolver
-		)
-	{
-		// returns XPathExpression object
-		return new XPathExpression(expression, resolver);
-	}
-
-	// XPathNSResolver createNSResolver(nodeResolver)
-	document.createNSResolver = function
-		(
-		nodeResolver	// Node
-		)
-	{
-		// returns XPathNSResolver
-		return new XPathNSResolver(nodeResolver);
-	}
-
-	// XPathResult evaluate(String expresison, Node contextNode, XPathNSResolver resolver, Number type, XPathResult result)
-	document.evaluate = function
-		(
-		expression,		// String
-		contextNode,	// Node
-		resolver,		// XPathNSResolver
-		type,			// Number
-		result			// XPathResult
-		)
-		// can raise XPathException, DOMException
-	{
-		// return XPathResult
-		return document.createExpression(expression, resolver).evaluate(contextNode, type, result);
-	}
-
-	// XPathExpression
-	function XPathExpression
-	(
-		expression, // String
-		resolver // XPathNSResolver
-	)
-	{
-		this.expressionString = expression;
-		this.resolver = resolver;
-
-		// XPathResult evaluate(Node contextNode, Number type, XPathResult result)
-		this.evaluate = function
-		(
-			contextNode, // Node
-			type, // Number
-			result // XPathResult		
-		)
-			// raises XPathException, DOMException
-		{
-			// return XPathResult
-			return (result &amp;&amp; result.constructor == XPathResult ? result.initialize(this, contextNode, resolver, type) : new XPathResult(this, contextNode, resolver, type));
-		}
-		
-		this.toString = function()
-		{
-			return &quot;[XPathExpression]&quot;;
-		}
-	}
-
-	// XPathNSResolver
-	function XPathNSResolver(node)
-	{
-		this.node = node;
-	
-		// String lookupNamespaceURI(String prefix)
-		this.lookupNamespaceURI = function
-			(
-			prefix			// String
-			)
-		{
-			XPathException.ThrowNotImplemented();
-			// return String
-			return null;
-		}
-
-		this.toString = function()
-		{
-			return &quot;[XPathNSResolver]&quot;;
-		}
-	}
-
-	// XPathResult
-	XPathResult.ANY_TYPE = 0;
-	XPathResult.NUMBER_TYPE = 1;
-	XPathResult.STRING_TYPE = 2;
-	XPathResult.BOOLEAN_TYPE = 3;
-	XPathResult.UNORDERED_NODE_ITERATOR_TYPE = 4;
-	XPathResult.ORDERED_NODE_ITERATOR_TYPE = 5;
-	XPathResult.UNORDERED_SNAPSHOT_TYPE = 6;
-	XPathResult.ORDERED_SNAPSHOT_TYPE = 7;
-	XPathResult.ANY_UNORDERED_NODE_TYPE = 8;
-	XPathResult.FIRST_ORDERED_NODE_TYPE = 9;
-	
-	function XPathResult
-			(
-			expression,		// XPathExpression
-			contextNode,	// Node
-			resolver,		// XPathNSResolver
-			type			// Number
-			)
-	{
-		this.initialize = function(expression, contextNode, resolver, type)
-		{
-			this._domResult = null;
-			this._expression = expression;
-			this._contextNode = contextNode;
-			this._resolver = resolver;
-			if (type)
-			{
-				this.resultType = type;
-				this._isIterator = (type == XPathResult.UNORDERED_NODE_ITERATOR_TYPE || 
-					type == XPathResult.ORDERED_NODE_ITERATOR_TYPE || 
-					type == XPathResult.ANY_TYPE);
-				this._isSnapshot = (type == XPathResult.UNORDERED_SNAPSHOT_TYPE || type == XPathResult.ORDERED_SNAPSHOT_TYPE);
-				this._isNodeSet = type &gt; XPathResult.BOOLEAN_TYPE;
-			}
-			else
-			{
-				this.resultType = XPathResult.ANY_TYPE;
-				this._isIterator = true;
-				this._isSnapshot = false;
-				this._isNodeSet = true;
-			}
-			return this;
-		}
-		
-		this.initialize(expression, contextNode, resolver, type);
-		
-		this.getInvalidIteratorState = function()
-		{
-			return documentChangeDetected() || !this._isIterator;
-		}
-		
-		this.getSnapshotLength = function()
-			// raises XPathException
-		{
-			if (!this._isSnapshot)
-			{
-				XPathException.ThrowType(&quot;Snapshot is not an expected result type&quot;);
-			}
-			activateResult(this);
-			// return Number
-			return this._domResult.length;
-		}
-		
-		// Node iterateNext()
-		this.iterateNext = function()
-			// raises XPathException, DOMException
-		{
-			if (!this._isIterator)
-			{
-				XPathException.ThrowType(&quot;Iterator is not an expected result type&quot;);
-			}
-			activateResult(this);
-			if (documentChangeDetected())
-			{
-				DOMException.ThrowInvalidState(&quot;iterateNext&quot;);
-			}
-			// return Node
-			return getNextNode(this);
-		}
-		
-		// Node snapshotItem(Number index)
-		this.snapshotItem = function(index)
-			// raises XPathException
-		{
-			if (!this._isSnapshot)
-			{
-				XPathException.ThrowType(&quot;Snapshot is not an expected result type&quot;);
-			}
-			// return Node
-			return getItemNode(this, index); 
-		}
-		
-		this.toString = function()
-		{
-			return &quot;[XPathResult]&quot;;
-		}
-		
-		// returns string value of the result, if result type is STRING_TYPE
-		// otherwise throws an XPathException
-		this.getStringValue = function()
-		{
-			if (this.resultType != XPathResult.STRING_TYPE)
-			{
-				XPathException.ThrowType(&quot;The expression can not be converted to return String&quot;);
-			}
-			return getNodeText(this);
-		}
-		
-		// returns number value of the result, if the result is NUMBER_TYPE
-		// otherwise throws an XPathException
-		this.getNumberValue = function()
-		{
-			if (this.resultType != XPathResult.NUMBER_TYPE)
-			{
-				XPathException.ThrowType(&quot;The expression can not be converted to return Number&quot;);
-			}
-			var number = parseInt(getNodeText(this));
-			if (isNaN(number))
-			{
-				XPathException.ThrowType(&quot;The result can not be converted to Number&quot;);
-			}
-			return number;
-		}
-		
-		// returns boolean value of the result, if the result is BOOLEAN_TYPE
-		// otherwise throws an XPathException
-		this.getBooleanValue = function()
-		{
-			if (this.resultType != XPathResult.BOOLEAN_TYPE)
-			{
-				XPathException.ThrowType(&quot;The expression can not be converted to return Boolean&quot;);
-			}
-			
-			var	
-				text = getNodeText(this);
-				bool = (text ? text.toLowerCase() : null);
-			if (bool == &quot;false&quot; || bool == &quot;true&quot;)
-			{
-				return bool;
-			}
-			XPathException.ThrowType(&quot;The result can not be converted to Boolean&quot;);
-		}
-		
-		// returns single node, if the result is ANY_UNORDERED_NODE_TYPE or FIRST_ORDERED_NODE_TYPE
-		// otherwise throws an XPathException
-		this.getSingleNodeValue = function()
-		{
-			if (this.resultType != XPathResult.ANY_UNORDERED_NODE_TYPE &amp;&amp; 
-				this.resultType != XPathResult.FIRST_ORDERED_NODE_TYPE)
-			{
-				XPathException.ThrowType(&quot;The expression can not be converted to return single Node value&quot;);
-			}
-			return getSingleNode(this);
-		}
-		
-		function documentChangeDetected()
-		{
-			return document._XPathMsxmlDocumentHelper.documentChangeDetected();
-		}
-		
-		function getNodeText(result)
-		{
-			activateResult(result);
-			return result._textResult;
-//			return ((node = getSingleNode(result)) ? (node.nodeType == 1 ? node.innerText : node.nodeValue) : null);
-		}
-		
-		function findNode(result, current)
-		{
-			switch(current.nodeType)
-			{
-				case 1: // NODE_ELEMENT
-					var id = current.attributes.getNamedItem(&quot;id&quot;);
-					if (id)
-					{
-						return document.getElementById(id.value);
-					}
-					XPathException.Throw(&quot;unable to locate element in XML tree&quot;);
-				case 2: // NODE_ATTRIBUTE
-					var id = current.selectSingleNode(&quot;..&quot;).attributes.getNamedItem(&quot;id&quot;);
-					if (id)
-					{
-						var node = document.getElementById(id.text);
-						if (node)
-						{
-							return node.attributes.getNamedItem(current.nodeName);
-						}
-					}
-					XPathException.Throw(&quot;unable to locate attribute in XML tree&quot;);
-				case 3: // NODE_TEXT
-					var id = current.selectSingleNode(&quot;..&quot;).attributes.getNamedItem(&quot;id&quot;);
-					if (id)
-					{
-						var node = document.getElementById(id.value);
-						if (node)
-						{
-							for(child in node.childNodes)
-							{
-								if (child.nodeType == 3 &amp;&amp; child.nodeValue == current.nodeValue)
-								{
-									return child;
-								}
-							}
-						}
-					}
-					XPathException.Throw(&quot;unable to locate text in XML tree&quot;);
-			}
-			XPathException.Throw(&quot;unknown node type&quot;);
-		}
-		
-		function activateResult(result)
-		{
-			if (!result._domResult)
-			{
-				try
-				{
-					var expression = result._expression.expressionString;
-					
-					// adjust expression if contextNode is not a document
-					if (result._contextNode != document &amp;&amp; expression.indexOf(&quot;//&quot;) != 0)
-					{
-
-						expression = &quot;//*[@id = '&quot; + result._contextNode.id + &quot;']&quot; +
-							(expression.indexOf(&quot;/&quot;) == 0 ? &quot;&quot; : &quot;/&quot;) + expression;
-					}
-					
-					if (result._isNodeSet)
-					{
-						result._domResult = document._XPathMsxmlDocumentHelper.getDom().selectNodes(expression);
-					}
-					else
-					{
-						result._domResult = true;
-						result._textResult = document._XPathMsxmlDocumentHelper.getTextResult(expression);
-					}
-					
-				}
-				catch(error)
-				{
-					alert(error.description);
-					XPathException.ThrowInvalidExpression(error.description);
-				}
-			}
-		}
-
-		function getSingleNode(result)
-		{
-			var node = getItemNode(result, 0);
-			result._domResult = null;
-			return node;
-		}
-		
-		function getItemNode(result, index)
-		{
-			activateResult(result);
-			var current = result._domResult.item(index);
-			return (current ? findNode(result, current) : null);
-		}
-		
-		function getNextNode(result)
-		{
-			var current = result._domResult.nextNode;
-			if (current)
-			{
-				return findNode(result, current);
-			}
-			result._domResult = null;
-			return null;
-		}
-	}
-	
-	document.reloadDom = function()
-	{
-		document._XPathMsxmlDocumentHelper.reset();
-	}
-
-	document._XPathMsxmlDocumentHelper = new _XPathMsxmlDocumentHelper();
-	function _XPathMsxmlDocumentHelper()
-	{
-		this.getDom = function()
-		{
-			activateDom(this);
-			return this.dom;
-		}
-		
-		this.getXml = function()
-		{
-			activateDom(this);
-			return this.dom.xml;
-		}
-		
-		this.getTextResult = function(expression)
-		{
-			expression = expression.replace(/&lt;/g, &quot;&amp;lt;&quot;).replace(/&gt;/g, &quot;&amp;gt;&quot;).replace(/&quot;/g, &quot;\&quot;&quot;);
-			var xslText = &quot;&lt;xsl:stylesheet version=\&quot;1.0\&quot; xmlns:xsl=\&quot;http://www.w3.org/1999/XSL/Transform\&quot;&gt;&quot; +
-				&quot;&lt;xsl:output method=\&quot;text\&quot;/&gt;&lt;xsl:template match=\&quot;*\&quot;&gt;&lt;xsl:value-of select=\&quot;&quot; + expression + &quot;\&quot;/&gt;&quot; +
-				&quot;&lt;/xsl:template&gt;&lt;/xsl:stylesheet&gt;&quot;;
-			var xsl = new ActiveXObject(&quot;Msxml2.DOMDocument&quot;);
-			xsl.loadXML(xslText);
-			try
-			{
-				var result = this.getDom().transformNode(xsl);
-			}
-			catch(error)
-			{
-				alert(&quot;Error: &quot; + error.description);
-			}
-			return result;
-		}
-		
-		this.reset = function()
-		{
-			this.dom = null;
-		}
-		
-		function onPropertyChangeEventHandler()
-		{
-			document._propertyChangeDetected = true;
-		}
-		
-		this.documentChangeDetected = function()
-		{
-			return (document.ignoreDocumentChanges ? false : this._currentElementCount != document.all.length || document._propertyChangeDetected);
-		}
-		
-		function activateDom(helper)
-		{
-			if (!helper.dom)
-			{
-				var dom = new ActiveXObject(&quot;Msxml2.DOMDocument&quot;);
-/** SELENIUM:PATCH TO ALLOW PROVIDE FULL XPATH SUPPORT */
-				dom.setProperty(&quot;SelectionLanguage&quot;, &quot;XPath&quot;);
-/** END SELENIUM:PATCH */
-				dom.async = false;
-				dom.resolveExternals = false;
-				loadDocument(dom, helper);
-				helper.dom = dom;
-				helper._currentElementCount = document.all.length;
-				document._propertyChangeDetected = false;
-			}
-			else
-			{
-				if (helper.documentChangeDetected())
-				{
-					var dom = helper.dom;
-					dom.load(&quot;&quot;);
-					loadDocument(dom, helper);
-					helper._currentElementCount = document.all.length;
-					document._propertyChangeDetected = false;
-				}
-			}
-		}
-		
-		function loadDocument(dom, helper)
-		{
-			return loadNode(dom, dom, document.body, helper);
-		}
-			
-
-/** SELENIUM:PATCH for loadNode() - see SEL-68 */
-		function loadNode(dom, domParentNode, node, helper)
-		{
-			// Bad node scenarios
-			// 1. If the node contains a /, it's broken HTML
-			// 2. If the node doesn't have a name (typically from broken HTML), the node can't be loaded
-			// 3. Node types we can't deal with
-			//
-			// In all scenarios, we just skip the node. We won't be able to
-			// query on these nodes, but they're broken anyway.
-			if (node.nodeName.indexOf(&quot;/&quot;) &gt; -1
-			    || node.nodeName == &quot;&quot;
-			    || node.nodeName == &quot;#document&quot;
-			    || node.nodeName == &quot;#document-fragment&quot;
-			    || node.nodeName == &quot;#cdata-section&quot;
-			    || node.nodeName == &quot;#xml-declaration&quot;
-			    || node.nodeName == &quot;#whitespace&quot;
-			    || node.nodeName == &quot;#significat-whitespace&quot;
-			   )
-			{
-				return;
-			}
-			
-			// #comment is a &lt;!-- comment --&gt;, which must be created with createComment()
-			if (node.nodeName == &quot;#comment&quot;)
-			{
-				try
-				{
-					domParentNode.appendChild(dom.createComment(node.nodeValue));
-				}
-				catch (ex)
-				{
-					// it's just a comment, we don't care
-				}
-			}
-			else if (node.nodeType == 3)
-			{
-				domParentNode.appendChild(dom.createTextNode(node.nodeValue));
-			}
-			else
-			{
-				var domNode = dom.createElement(node.nodeName.toLowerCase());
-				if (!node.id)
-				{
-					node.id = node.uniqueID;
-				}
-				domParentNode.appendChild(domNode);
-				loadAttributes(dom, domNode, node);
-				var length = node.childNodes.length;
-				for(var i = 0; i &lt; length; i ++ )
-				{
-					loadNode(dom, domNode, node.childNodes[i], helper);
-				}
-				node.attachEvent(&quot;onpropertychange&quot;, onPropertyChangeEventHandler);
-			}
-		}
-/** END SELENIUM:PATCH */
-
-		function loadAttributes(dom, domParentNode, node)
-		{
-			for (var i = 0; i &lt; node.attributes.length; i ++ )
-			{
-				var attribute = node.attributes[i];
-				var attributeValue = attribute.nodeValue;
-				
-/** SELENIUM:PATCH for loadAttributes() - see SEL-176 */
-				if (attributeValue &amp;&amp; (attribute.specified || attribute.nodeName == 'value'))
-/** END SELENIUM:PATCH */
-				{
-					var domAttribute = dom.createAttribute(attribute.nodeName);
-					domAttribute.value = attributeValue;
-					domParentNode.setAttributeNode(domAttribute);				
-				}
-			}
-		}
-	
-	}
-}
-else
-{
-	document.reloadDom = function() {}
-	XPathResult.prototype.getStringValue = function()
-	{
-		return this.stringValue;
-	}
-	
-	XPathResult.prototype.getNumberValue = function()
-	{
-		return this.numberValue;
-	}
-	
-	XPathResult.prototype.getBooleanValue = function()
-	{
-		return this.booleanValue;
-	}
-	
-	XPathResult.prototype.getSingleNodeValue = function()
-	{
-		return this.singleNodeValue;	
-	}
-	
-	XPathResult.prototype.getInvalidIteratorState = function()
-	{
-		return this.invalidIteratorState;
-	}
-	
-	XPathResult.prototype.getSnapshotLength = function()
-	{
-		return this.snapshotLength;
-	}
-	
-	XPathResult.prototype.getResultType = function()
-	{
-		return this.resultType;
-	}
-}
-/** SELENIUM:PATCH TO ALLOW USE WITH CONTAINED DOCUMENTS */
-}
-/** END SELENIUM:PATCH */
-
+/*
+	html-xpath, an implementation of DOM Level 3 XPath for Internet Explorer 5+
+	Copyright (C) 2004 Dimitri Glazkov
+
+	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
+
+*/
+
+/** SELENIUM:PATCH TO ALLOW USE WITH DOCUMENTS FROM OTHER WINDOWS: 2004-11-24
+    TODO resubmit this to http://sf.net/projects/html-xpath */
+function addXPathSupport(document) {
+/** END SELENIUM:PATCH */
+
+var isIe = /MSIE [56789]/.test(navigator.userAgent) &amp;&amp; (navigator.platform == &quot;Win32&quot;);
+
+// Mozilla has support by default, we don't have an implementation for the rest
+if (isIe)
+{
+	// release number
+	document.DomL3XPathRelease = &quot;0.0.3.0&quot;;
+	
+	// XPathException
+	// An Error object will be thrown, this is just a handler to instantiate that object
+	var XPathException = new _XPathExceptionHandler();
+	function _XPathExceptionHandler()
+	{
+		this.INVALID_EXPRESSION_ERR = 51;
+		this.TYPE_ERR = 52;
+		this.NOT_IMPLEMENTED_ERR = -1;
+		this.RUNTIME_ERR = -2;
+		
+		this.ThrowNotImplemented = function(message)
+		{
+			ThrowError(this.NOT_IMPLEMENTED_ERR, &quot;This functionality is not implemented.&quot;, message);
+		}
+		
+		this.ThrowInvalidExpression = function(message)
+		{
+			ThrowError(this.INVALID_EXPRESSION_ERR, &quot;Invalid expression&quot;, message);
+		}
+		
+		this.ThrowType = function(message)
+		{
+			ThrowError(this.TYPE_ERR, &quot;Type error&quot;, message);
+		}
+		
+		this.Throw = function(message)
+		{
+			ThrowError(this.RUNTIME_ERR, &quot;Run-time error&quot;, message);
+		}
+		
+		function ThrowError(code, description, message)
+		{
+			var error = new Error(code, &quot;DOM-L3-XPath &quot; + document.DomL3XPathRelease + &quot;: &quot; + description + (message ? &quot;, \&quot;&quot; + message  + &quot;\&quot;&quot;: &quot;&quot;));
+			error.code = code;
+			error.name = &quot;XPathException&quot;;
+			throw error;
+		}
+	}
+	
+	// DOMException
+	// An Error object will be thrown, this is just a handler to instantiate that object
+	var DOMException = new _DOMExceptionHandler();
+	function _DOMExceptionHandler()
+	{
+		this.ThrowInvalidState = function(message)
+		{
+			ThrowError(13, &quot;The state of the object is no longer valid&quot;, message);
+		}
+
+		function ThrowError(code, description, message)
+		{
+			var error = new Error(code, &quot;DOM : &quot; + description + (message ? &quot;, \&quot;&quot; + message  + &quot;\&quot;&quot;: &quot;&quot;));
+			error.code = code;
+			error.name = &quot;DOMException&quot;;
+			throw error;
+		}
+	}
+
+	// XPathEvaluator 
+	// implemented as document object methods
+	
+	// XPathExpression createExpression(String expression, XPathNSResolver resolver)
+	document.createExpression = function
+		(
+		expression,		// String
+		resolver		// XPathNSResolver
+		)
+	{
+		// returns XPathExpression object
+		return new XPathExpression(expression, resolver);
+	}
+
+	// XPathNSResolver createNSResolver(nodeResolver)
+	document.createNSResolver = function
+		(
+		nodeResolver	// Node
+		)
+	{
+		// returns XPathNSResolver
+		return new XPathNSResolver(nodeResolver);
+	}
+
+	// XPathResult evaluate(String expresison, Node contextNode, XPathNSResolver resolver, Number type, XPathResult result)
+	document.evaluate = function
+		(
+		expression,		// String
+		contextNode,	// Node
+		resolver,		// XPathNSResolver
+		type,			// Number
+		result			// XPathResult
+		)
+		// can raise XPathException, DOMException
+	{
+		// return XPathResult
+		return document.createExpression(expression, resolver).evaluate(contextNode, type, result);
+	}
+
+	// XPathExpression
+	function XPathExpression
+	(
+		expression, // String
+		resolver // XPathNSResolver
+	)
+	{
+		this.expressionString = expression;
+		this.resolver = resolver;
+
+		// XPathResult evaluate(Node contextNode, Number type, XPathResult result)
+		this.evaluate = function
+		(
+			contextNode, // Node
+			type, // Number
+			result // XPathResult		
+		)
+			// raises XPathException, DOMException
+		{
+			// return XPathResult
+			return (result &amp;&amp; result.constructor == XPathResult ? result.initialize(this, contextNode, resolver, type) : new XPathResult(this, contextNode, resolver, type));
+		}
+		
+		this.toString = function()
+		{
+			return &quot;[XPathExpression]&quot;;
+		}
+	}
+
+	// XPathNSResolver
+	function XPathNSResolver(node)
+	{
+		this.node = node;
+	
+		// String lookupNamespaceURI(String prefix)
+		this.lookupNamespaceURI = function
+			(
+			prefix			// String
+			)
+		{
+			XPathException.ThrowNotImplemented();
+			// return String
+			return null;
+		}
+
+		this.toString = function()
+		{
+			return &quot;[XPathNSResolver]&quot;;
+		}
+	}
+
+	// XPathResult
+	XPathResult.ANY_TYPE = 0;
+	XPathResult.NUMBER_TYPE = 1;
+	XPathResult.STRING_TYPE = 2;
+	XPathResult.BOOLEAN_TYPE = 3;
+	XPathResult.UNORDERED_NODE_ITERATOR_TYPE = 4;
+	XPathResult.ORDERED_NODE_ITERATOR_TYPE = 5;
+	XPathResult.UNORDERED_SNAPSHOT_TYPE = 6;
+	XPathResult.ORDERED_SNAPSHOT_TYPE = 7;
+	XPathResult.ANY_UNORDERED_NODE_TYPE = 8;
+	XPathResult.FIRST_ORDERED_NODE_TYPE = 9;
+	
+	function XPathResult
+			(
+			expression,		// XPathExpression
+			contextNode,	// Node
+			resolver,		// XPathNSResolver
+			type			// Number
+			)
+	{
+		this.initialize = function(expression, contextNode, resolver, type)
+		{
+			this._domResult = null;
+			this._expression = expression;
+			this._contextNode = contextNode;
+			this._resolver = resolver;
+			if (type)
+			{
+				this.resultType = type;
+				this._isIterator = (type == XPathResult.UNORDERED_NODE_ITERATOR_TYPE || 
+					type == XPathResult.ORDERED_NODE_ITERATOR_TYPE || 
+					type == XPathResult.ANY_TYPE);
+				this._isSnapshot = (type == XPathResult.UNORDERED_SNAPSHOT_TYPE || type == XPathResult.ORDERED_SNAPSHOT_TYPE);
+				this._isNodeSet = type &gt; XPathResult.BOOLEAN_TYPE;
+			}
+			else
+			{
+				this.resultType = XPathResult.ANY_TYPE;
+				this._isIterator = true;
+				this._isSnapshot = false;
+				this._isNodeSet = true;
+			}
+			return this;
+		}
+		
+		this.initialize(expression, contextNode, resolver, type);
+		
+		this.getInvalidIteratorState = function()
+		{
+			return documentChangeDetected() || !this._isIterator;
+		}
+		
+		this.getSnapshotLength = function()
+			// raises XPathException
+		{
+			if (!this._isSnapshot)
+			{
+				XPathException.ThrowType(&quot;Snapshot is not an expected result type&quot;);
+			}
+			activateResult(this);
+			// return Number
+			return this._domResult.length;
+		}
+		
+		// Node iterateNext()
+		this.iterateNext = function()
+			// raises XPathException, DOMException
+		{
+			if (!this._isIterator)
+			{
+				XPathException.ThrowType(&quot;Iterator is not an expected result type&quot;);
+			}
+			activateResult(this);
+			if (documentChangeDetected())
+			{
+				DOMException.ThrowInvalidState(&quot;iterateNext&quot;);
+			}
+			// return Node
+			return getNextNode(this);
+		}
+		
+		// Node snapshotItem(Number index)
+		this.snapshotItem = function(index)
+			// raises XPathException
+		{
+			if (!this._isSnapshot)
+			{
+				XPathException.ThrowType(&quot;Snapshot is not an expected result type&quot;);
+			}
+			// return Node
+			return getItemNode(this, index); 
+		}
+		
+		this.toString = function()
+		{
+			return &quot;[XPathResult]&quot;;
+		}
+		
+		// returns string value of the result, if result type is STRING_TYPE
+		// otherwise throws an XPathException
+		this.getStringValue = function()
+		{
+			if (this.resultType != XPathResult.STRING_TYPE)
+			{
+				XPathException.ThrowType(&quot;The expression can not be converted to return String&quot;);
+			}
+			return getNodeText(this);
+		}
+		
+		// returns number value of the result, if the result is NUMBER_TYPE
+		// otherwise throws an XPathException
+		this.getNumberValue = function()
+		{
+			if (this.resultType != XPathResult.NUMBER_TYPE)
+			{
+				XPathException.ThrowType(&quot;The expression can not be converted to return Number&quot;);
+			}
+			var number = parseInt(getNodeText(this));
+			if (isNaN(number))
+			{
+				XPathException.ThrowType(&quot;The result can not be converted to Number&quot;);
+			}
+			return number;
+		}
+		
+		// returns boolean value of the result, if the result is BOOLEAN_TYPE
+		// otherwise throws an XPathException
+		this.getBooleanValue = function()
+		{
+			if (this.resultType != XPathResult.BOOLEAN_TYPE)
+			{
+				XPathException.ThrowType(&quot;The expression can not be converted to return Boolean&quot;);
+			}
+			
+			var	
+				text = getNodeText(this);
+				bool = (text ? text.toLowerCase() : null);
+			if (bool == &quot;false&quot; || bool == &quot;true&quot;)
+			{
+				return bool;
+			}
+			XPathException.ThrowType(&quot;The result can not be converted to Boolean&quot;);
+		}
+		
+		// returns single node, if the result is ANY_UNORDERED_NODE_TYPE or FIRST_ORDERED_NODE_TYPE
+		// otherwise throws an XPathException
+		this.getSingleNodeValue = function()
+		{
+			if (this.resultType != XPathResult.ANY_UNORDERED_NODE_TYPE &amp;&amp; 
+				this.resultType != XPathResult.FIRST_ORDERED_NODE_TYPE)
+			{
+				XPathException.ThrowType(&quot;The expression can not be converted to return single Node value&quot;);
+			}
+			return getSingleNode(this);
+		}
+		
+		function documentChangeDetected()
+		{
+			return document._XPathMsxmlDocumentHelper.documentChangeDetected();
+		}
+		
+		function getNodeText(result)
+		{
+			activateResult(result);
+			return result._textResult;
+//			return ((node = getSingleNode(result)) ? (node.nodeType == 1 ? node.innerText : node.nodeValue) : null);
+		}
+		
+		function findNode(result, current)
+		{
+			switch(current.nodeType)
+			{
+				case 1: // NODE_ELEMENT
+					var id = current.attributes.getNamedItem(&quot;id&quot;);
+					if (id)
+					{
+						return document.getElementById(id.value);
+					}
+					XPathException.Throw(&quot;unable to locate element in XML tree&quot;);
+				case 2: // NODE_ATTRIBUTE
+					var id = current.selectSingleNode(&quot;..&quot;).attributes.getNamedItem(&quot;id&quot;);
+					if (id)
+					{
+						var node = document.getElementById(id.text);
+						if (node)
+						{
+							return node.attributes.getNamedItem(current.nodeName);
+						}
+					}
+					XPathException.Throw(&quot;unable to locate attribute in XML tree&quot;);
+				case 3: // NODE_TEXT
+					var id = current.selectSingleNode(&quot;..&quot;).attributes.getNamedItem(&quot;id&quot;);
+					if (id)
+					{
+						var node = document.getElementById(id.value);
+						if (node)
+						{
+							for(child in node.childNodes)
+							{
+								if (child.nodeType == 3 &amp;&amp; child.nodeValue == current.nodeValue)
+								{
+									return child;
+								}
+							}
+						}
+					}
+					XPathException.Throw(&quot;unable to locate text in XML tree&quot;);
+			}
+			XPathException.Throw(&quot;unknown node type&quot;);
+		}
+		
+		function activateResult(result)
+		{
+			if (!result._domResult)
+			{
+				try
+				{
+					var expression = result._expression.expressionString;
+					
+					// adjust expression if contextNode is not a document
+					if (result._contextNode != document &amp;&amp; expression.indexOf(&quot;//&quot;) != 0)
+					{
+
+						expression = &quot;//*[@id = '&quot; + result._contextNode.id + &quot;']&quot; +
+							(expression.indexOf(&quot;/&quot;) == 0 ? &quot;&quot; : &quot;/&quot;) + expression;
+					}
+					
+					if (result._isNodeSet)
+					{
+						result._domResult = document._XPathMsxmlDocumentHelper.getDom().selectNodes(expression);
+					}
+					else
+					{
+						result._domResult = true;
+						result._textResult = document._XPathMsxmlDocumentHelper.getTextResult(expression);
+					}
+					
+				}
+				catch(error)
+				{
+					alert(error.description);
+					XPathException.ThrowInvalidExpression(error.description);
+				}
+			}
+		}
+
+		function getSingleNode(result)
+		{
+			var node = getItemNode(result, 0);
+			result._domResult = null;
+			return node;
+		}
+		
+		function getItemNode(result, index)
+		{
+			activateResult(result);
+			var current = result._domResult.item(index);
+			return (current ? findNode(result, current) : null);
+		}
+		
+		function getNextNode(result)
+		{
+			var current = result._domResult.nextNode;
+			if (current)
+			{
+				return findNode(result, current);
+			}
+			result._domResult = null;
+			return null;
+		}
+	}
+	
+	document.reloadDom = function()
+	{
+		document._XPathMsxmlDocumentHelper.reset();
+	}
+
+	document._XPathMsxmlDocumentHelper = new _XPathMsxmlDocumentHelper();
+	function _XPathMsxmlDocumentHelper()
+	{
+		this.getDom = function()
+		{
+			activateDom(this);
+			return this.dom;
+		}
+		
+		this.getXml = function()
+		{
+			activateDom(this);
+			return this.dom.xml;
+		}
+		
+		this.getTextResult = function(expression)
+		{
+			expression = expression.replace(/&lt;/g, &quot;&amp;lt;&quot;).replace(/&gt;/g, &quot;&amp;gt;&quot;).replace(/&quot;/g, &quot;\&quot;&quot;);
+			var xslText = &quot;&lt;xsl:stylesheet version=\&quot;1.0\&quot; xmlns:xsl=\&quot;http://www.w3.org/1999/XSL/Transform\&quot;&gt;&quot; +
+				&quot;&lt;xsl:output method=\&quot;text\&quot;/&gt;&lt;xsl:template match=\&quot;*\&quot;&gt;&lt;xsl:value-of select=\&quot;&quot; + expression + &quot;\&quot;/&gt;&quot; +
+				&quot;&lt;/xsl:template&gt;&lt;/xsl:stylesheet&gt;&quot;;
+			var xsl = new ActiveXObject(&quot;Msxml2.DOMDocument&quot;);
+			xsl.loadXML(xslText);
+			try
+			{
+				var result = this.getDom().transformNode(xsl);
+			}
+			catch(error)
+			{
+				alert(&quot;Error: &quot; + error.description);
+			}
+			return result;
+		}
+		
+		this.reset = function()
+		{
+			this.dom = null;
+		}
+		
+		function onPropertyChangeEventHandler()
+		{
+			document._propertyChangeDetected = true;
+		}
+		
+		this.documentChangeDetected = function()
+		{
+			return (document.ignoreDocumentChanges ? false : this._currentElementCount != document.all.length || document._propertyChangeDetected);
+		}
+		
+		function activateDom(helper)
+		{
+			if (!helper.dom)
+			{
+				var dom = new ActiveXObject(&quot;Msxml2.DOMDocument&quot;);
+/** SELENIUM:PATCH TO ALLOW PROVIDE FULL XPATH SUPPORT */
+				dom.setProperty(&quot;SelectionLanguage&quot;, &quot;XPath&quot;);
+/** END SELENIUM:PATCH */
+				dom.async = false;
+				dom.resolveExternals = false;
+				loadDocument(dom, helper);
+				helper.dom = dom;
+				helper._currentElementCount = document.all.length;
+				document._propertyChangeDetected = false;
+			}
+			else
+			{
+				if (helper.documentChangeDetected())
+				{
+					var dom = helper.dom;
+					dom.load(&quot;&quot;);
+					loadDocument(dom, helper);
+					helper._currentElementCount = document.all.length;
+					document._propertyChangeDetected = false;
+				}
+			}
+		}
+		
+		function loadDocument(dom, helper)
+		{
+			return loadNode(dom, dom, document.body, helper);
+		}
+			
+
+/** SELENIUM:PATCH for loadNode() - see SEL-68 */
+		function loadNode(dom, domParentNode, node, helper)
+		{
+			// Bad node scenarios
+			// 1. If the node contains a /, it's broken HTML
+			// 2. If the node doesn't have a name (typically from broken HTML), the node can't be loaded
+			// 3. Node types we can't deal with
+			//
+			// In all scenarios, we just skip the node. We won't be able to
+			// query on these nodes, but they're broken anyway.
+			if (node.nodeName.indexOf(&quot;/&quot;) &gt; -1
+			    || node.nodeName == &quot;&quot;
+			    || node.nodeName == &quot;#document&quot;
+			    || node.nodeName == &quot;#document-fragment&quot;
+			    || node.nodeName == &quot;#cdata-section&quot;
+			    || node.nodeName == &quot;#xml-declaration&quot;
+			    || node.nodeName == &quot;#whitespace&quot;
+			    || node.nodeName == &quot;#significat-whitespace&quot;
+			   )
+			{
+				return;
+			}
+			
+			// #comment is a &lt;!-- comment --&gt;, which must be created with createComment()
+			if (node.nodeName == &quot;#comment&quot;)
+			{
+				try
+				{
+					domParentNode.appendChild(dom.createComment(node.nodeValue));
+				}
+				catch (ex)
+				{
+					// it's just a comment, we don't care
+				}
+			}
+			else if (node.nodeType == 3)
+			{
+				domParentNode.appendChild(dom.createTextNode(node.nodeValue));
+			}
+			else
+			{
+				var domNode = dom.createElement(node.nodeName.toLowerCase());
+				if (!node.id)
+				{
+					node.id = node.uniqueID;
+				}
+				domParentNode.appendChild(domNode);
+				loadAttributes(dom, domNode, node);
+				var length = node.childNodes.length;
+				for(var i = 0; i &lt; length; i ++ )
+				{
+					loadNode(dom, domNode, node.childNodes[i], helper);
+				}
+				node.attachEvent(&quot;onpropertychange&quot;, onPropertyChangeEventHandler);
+			}
+		}
+/** END SELENIUM:PATCH */
+
+		function loadAttributes(dom, domParentNode, node)
+		{
+			for (var i = 0; i &lt; node.attributes.length; i ++ )
+			{
+				var attribute = node.attributes[i];
+				var attributeValue = attribute.nodeValue;
+				
+/** SELENIUM:PATCH for loadAttributes() - see SEL-176 */
+				if (attributeValue &amp;&amp; (attribute.specified || attribute.nodeName == 'value'))
+/** END SELENIUM:PATCH */
+				{
+					var domAttribute = dom.createAttribute(attribute.nodeName);
+					domAttribute.value = attributeValue;
+					domParentNode.setAttributeNode(domAttribute);				
+				}
+			}
+		}
+	
+	}
+}
+else
+{
+	document.reloadDom = function() {}
+	XPathResult.prototype.getStringValue = function()
+	{
+		return this.stringValue;
+	}
+	
+	XPathResult.prototype.getNumberValue = function()
+	{
+		return this.numberValue;
+	}
+	
+	XPathResult.prototype.getBooleanValue = function()
+	{
+		return this.booleanValue;
+	}
+	
+	XPathResult.prototype.getSingleNodeValue = function()
+	{
+		return this.singleNodeValue;	
+	}
+	
+	XPathResult.prototype.getInvalidIteratorState = function()
+	{
+		return this.invalidIteratorState;
+	}
+	
+	XPathResult.prototype.getSnapshotLength = function()
+	{
+		return this.snapshotLength;
+	}
+	
+	XPathResult.prototype.getResultType = function()
+	{
+		return this.resultType;
+	}
+}
+/** SELENIUM:PATCH TO ALLOW USE WITH CONTAINED DOCUMENTS */
+}
+/** END SELENIUM:PATCH */
+</diff>
      <filename>testframework/selenium/scripts/html-xpath-patched.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,894 +1,894 @@
-/*
- * Copyright 2004 ThoughtWorks, Inc
- *
- *  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
- *  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 &quot;AS IS&quot; 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 &lt; arguments.length; i++) {
-    element = arguments[i];
-    if (typeof element == 'string')
-      element = document.getElementById(element);
-    results[results.length] = element;
-  }
-  return results.length &lt; 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 &lt; 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(&quot;\\b&quot; + name + &quot;\\b&quot;, &quot;g&quot;);
-    element.className = element.className.replace(re, &quot;&quot;);
-}
-
-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 = &quot;&quot;;
-      element.style[name] = value;
-    }
-}
-
-function elementGetStyle(element, style) {
-    var value = element.style[style];
-    if (!value) {
-      if (document.defaultView &amp;&amp; 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 &amp;&amp; ['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, &quot;&quot;);
-    // strip leading
-    return result.replace(/\s+$/g, &quot;&quot;);
-    // 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 = &quot;&quot;;
-
-    var isRecentFirefox = (browserVersion.isFirefox &amp;&amp; browserVersion.firefoxVersion &gt;= &quot;1.5&quot;);
-    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, &quot; &quot;);
-        }
-        return text;
-    }
-    if (element.nodeType == 1 /*Node.ELEMENT_NODE*/) {
-        var childrenPreformatted = preformatted || (element.tagName == &quot;PRE&quot;);
-        var text = &quot;&quot;;
-        for (var i = 0; i &lt; element.childNodes.length; i++) {
-            var child = element.childNodes.item(i);
-            text += getTextContent(child, childrenPreformatted);
-        }
-        // Handle block elements that introduce newlines
-        // -- From HTML spec:
-        //&lt;!ENTITY % block
-        //     &quot;P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT |
-        //      BLOCKQUOTE | F:wORM | HR | TABLE | FIELDSET | ADDRESS&quot;&gt;
-        //
-        // TODO: should potentially introduce multiple newlines to separate blocks
-        if (element.tagName == &quot;P&quot; || element.tagName == &quot;BR&quot; || element.tagName == &quot;HR&quot; || element.tagName == &quot;DIV&quot;) {
-            text += &quot;\n&quot;;
-        }
-        return text;
-    }
-    return '';
-}
-
-/**
- * Convert all newlines to \m
- */
-function normalizeNewlines(text)
-{
-    return text.replace(/\r\n|\r/g, &quot;\n&quot;);
-}
-
-/**
- * Replace multiple sequential spaces with a single space, and then convert &amp;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, &quot; &quot;);
-
-    // Replace &amp;nbsp; with a space
-    var nbspPattern = new RegExp(String.fromCharCode(160), &quot;g&quot;);
-    if (browserVersion.isSafari) {
-	return replaceAll(text, String.fromCharCode(160), &quot; &quot;);
-    } else {
-	return text.replace(nbspPattern, &quot; &quot;);
-    }
-}
-
-function replaceAll(text, oldText, newText) {
-    while (text.indexOf(oldText) != -1) {
-	text = text.replace(oldText, newText);
-    }
-    return text;
-}
-
-
-function xmlDecode(text) {
-    text = text.replace(/&amp;quot;/g, '&quot;');
-    text = text.replace(/&amp;apos;/g, &quot;'&quot;);
-    text = text.replace(/&amp;lt;/g, &quot;&lt;&quot;);
-    text = text.replace(/&amp;gt;/g, &quot;&gt;&quot;);
-    text = text.replace(/&amp;amp;/g, &quot;&amp;&quot;);
-    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 &lt;input&gt; 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(&quot;This element has no value; is it really a form field?&quot;);
-    }
-    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(&quot;invalid keySequence&quot;);
-}
-
-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(&quot;load&quot;, command, true);
-    else if (window.detachEvent)
-        element.detachEvent(&quot;onload&quot;, command);
-}
-
-function addLoadListener(element, command) {
-    LOG.debug('Adding loadListenter for ' + element + ', ' + command);
-    var augmentedCommand = function() {
-        command.call(this, element);
-    }
-    if (window.addEventListener &amp;&amp; !browserVersion.isOpera)
-        element.addEventListener(&quot;load&quot;, augmentedCommand, true);
-    else if (window.attachEvent)
-        element.attachEvent(&quot;onload&quot;, 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 &amp;&amp; regexpResult[1]) {
-        return regexpResult[1];
-    }
-    return 'anonymous';
-}
-
-function getDocumentBase(doc) {
-    var bases = document.getElementsByTagName(&quot;base&quot;);
-    if (bases &amp;&amp; bases.length &amp;&amp; bases[0].href) {
-        return bases[0].href;
-    }
-    return &quot;&quot;;
-}
-
-function getTagName(element) {
-    var tagName;
-    if (element &amp;&amp; element.tagName &amp;&amp; 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 &lt; 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) == &quot;object&quot;) &amp;&amp; (x[&quot;length&quot;] != 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 
-    * &amp;lt;base&amp;gt; tag; if the baseUrl doesn't end with &quot;/&quot;, we'll assume
-    * that it points to a file, and strip the filename off to find its
-    * base directory.
-    *
-    * So absolutify(&quot;foo&quot;, &quot;http://x/bar&quot;) will return &quot;http://x/foo&quot; (stripping off bar),
-    * whereas absolutify(&quot;foo&quot;, &quot;http://x/bar/&quot;) will return &quot;http://x/bar/foo&quot; (preserving bar).
-    * Naturally absolutify(&quot;foo&quot;, &quot;http://x&quot;) will return &quot;http://x/foo&quot;, 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 = &quot;file:///&quot; + baseUrl.replace(/\\/g, &quot;/&quot;);
-            loc = parseUrl(baseUrl);
-        } else {
-            throw new SeleniumError(&quot;baseUrl wasn't absolute: &quot; + 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 &quot;/&quot; + the url
-    if (!loc.pathname) {
-        loc.pathname = &quot;/&quot; + 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(&quot;Invalid URL: &quot; + url);
-    }
-    var loc = new Object();
-    for (var i = 0; i &lt; 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(&quot;Not a valid location object: &quot; + o2s(loc));
-    }
-    var protocol = loc.protocol;
-    protocol = protocol.replace(/:$/, &quot;&quot;);
-    var url = protocol + &quot;://&quot;;
-    if (loc.username) {
-        url += loc.username;
-        if (loc.password) {
-            url += &quot;:&quot; + loc.password;
-        }
-        url += &quot;@&quot;;
-    }
-    if (loc.host) {
-        url += loc.host;
-    }
-    
-    if (loc.port) {
-        url += &quot;:&quot; + loc.port;
-    }
-    
-    if (loc.pathname) {
-        url += loc.pathname;
-    }
-    
-    if (loc.search) {
-        url += &quot;?&quot; + loc.search;
-    }
-    if (loc.hash) {
-        var hash = loc.hash;
-        hash = loc.hash.replace(/^#/, &quot;&quot;);
-        url += &quot;#&quot; + hash;
-    }
-    return url;
-}
-
-function canonicalize(url) {
-    var tempLink = window.document.createElement(&quot;link&quot;);
-    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(&quot;/&quot;);
-  	var newParts = [];
-  	newParts.push(originalParts.shift());
-  	for (var i = 0; i &lt; originalParts.length; i++) {
-  		var part = originalParts[i];
-  		if (&quot;..&quot; == part) {
-  			newParts.pop();
-  			continue;
-  		}
-  		newParts.push(part);
-  	}
-  	loc.pathname = newParts.join(&quot;/&quot;);
-    return reassembleLocation(loc);
-}
-
-function extractExceptionMessage(ex) {
-    if (ex == null) return &quot;null exception&quot;;
-    if (ex.message != null) return ex.message;
-    if (ex.toString &amp;&amp; ex.toString() != null) return ex.toString();
-}
-    
-
-function describe(object, delimiter) {
-    var props = new Array();
-    for (var prop in object) {
-        try {
-            props.push(prop + &quot; -&gt; &quot; + object[prop]);
-        } catch (e) {
-            props.push(prop + &quot; -&gt; [htmlutils: ack! couldn't read this property! (Permission Denied?)]&quot;);
-        }
-    }
-    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(&quot;cannot find PatternMatcher.strategies.&quot; + 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 &quot;static&quot; convenience method for easy matching
- */
-PatternMatcher.matches = function(pattern, actual) {
-    return new PatternMatcher(pattern).matches(actual);
-};
-
-PatternMatcher.strategies = {
-
-/**
- * Exact matching, e.g. &quot;exact:***&quot;
- */
-    exact: function(expected) {
-        this.expected = expected;
-        this.matches = function(actual) {
-            return actual == this.expected;
-        };
-    },
-
-/**
- * Match by regular expression, e.g. &quot;regexp:^[0-9]+$&quot;
- */
-    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);
-        };
-    },
-
-/**
- * &quot;globContains&quot; (aka &quot;wildmat&quot;) patterns, e.g. &quot;glob:one,two,*&quot;,
- * 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 &quot;.*&quot;.  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);
-        };
-    },
-
-
-/**
- * &quot;glob&quot; (aka &quot;wildmat&quot;) patterns, e.g. &quot;glob:one,two,*&quot;
- */
-    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, &quot;\\$1&quot;);
-    re = re.replace(/\?/g, &quot;(.|[\r\n])&quot;);
-    re = re.replace(/\*/g, &quot;(.|[\r\n])*&quot;);
-    return re;
-};
-
-PatternMatcher.regexpFromGlobContains = function(globContains) {
-    return PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(globContains);
-};
-
-PatternMatcher.regexpFromGlob = function(glob) {
-    return &quot;^&quot; + PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(glob) + &quot;$&quot;;
-};
-
-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 +
-                    &quot;Expected '&quot; + args.expected +
-                    &quot;' but was '&quot; + args.actual + &quot;'&quot;);
-    },
-
-/*
-* Assert.matches(comment?, pattern, actual)
-*/
-    matches: function() {
-        var args = new AssertionArguments(arguments);
-        if (PatternMatcher.matches(args.expected, args.actual)) {
-            return;
-        }
-        Assert.fail(args.comment +
-                    &quot;Actual value '&quot; + args.actual +
-                    &quot;' did not match '&quot; + args.expected + &quot;'&quot;);
-    },
-
-/*
-* Assert.notMtches(comment?, pattern, actual)
-*/
-    notMatches: function() {
-        var args = new AssertionArguments(arguments);
-        if (!PatternMatcher.matches(args.expected, args.actual)) {
-            return;
-        }
-        Assert.fail(args.comment +
-                    &quot;Actual value '&quot; + args.actual +
-                    &quot;' did match '&quot; + args.expected + &quot;'&quot;);
-    }
-
-};
-
-// Preprocess the arguments to allow for an optional comment.
-function AssertionArguments(args) {
-    if (args.length == 2) {
-        this.comment = &quot;&quot;;
-        this.expected = args[0];
-        this.actual = args[1];
-    } else {
-        this.comment = args[0] + &quot;; &quot;;
-        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 += '&gt; ' + a.callee.toString() + '\n';
-            if (a.caller == a) {
-                result += '*';
-                break;
-            }
-        }
-        error.stack = result;
-    }
-    error.isSeleniumError = true;
-    return error;
-}
-
-function highlight(element) {
-    var highLightColor = &quot;yellow&quot;;
-    if (element.originalColor == undefined) { // avoid picking up highlight
-        element.originalColor = elementGetStyle(element, &quot;background-color&quot;);
-    }
-    elementSetStyle(element, {&quot;backgroundColor&quot; : highLightColor});
-    window.setTimeout(function() {
-        try {
-            //if element is orphan, probably page of it has already gone, so ignore
-            if (!element.parentNode) {
-                return;
-            }
-            elementSetStyle(element, {&quot;backgroundColor&quot; : element.originalColor});
-        } catch (e) {} // DGF unhighlighting is very dangerous and low priority
-    }, 200);
-}
-
-
-
-// for use from vs.2003 debugger
-function o2s(obj) {
-    var s = &quot;&quot;;
-    for (key in obj) {
-        var line = key + &quot;-&gt;&quot; + obj[key];
-        line.replace(&quot;\n&quot;, &quot; &quot;);
-        s += line + &quot;\n&quot;;
-    }
-    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 = &quot;Couldn't open app window; is the pop-up blocker enabled?&quot;
-        LOG.error(errorMessage);
-        throw new Error(&quot;Couldn't open app window; is the pop-up blocker enabled?&quot;);
-    }
-    try {
-        var windowHeight = 500;
-        if (window.outerHeight) {
-            windowHeight = window.outerHeight;
-        } else if (document.documentElement &amp;&amp; document.documentElement.offsetHeight) {
-            windowHeight = document.documentElement.offsetHeight;
-        }
-
-        if (window.screenLeft &amp;&amp; !window.screenX) window.screenX = window.screenLeft;
-        if (window.screenTop &amp;&amp; !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(&quot;Couldn't resize app window&quot;);
-        LOG.exception(e);
-    }
-
-
-    if (!suppressMozillaWarning &amp;&amp; window.document.readyState == null &amp;&amp; !seenReadyStateWarning) {
-        alert(&quot;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.&quot;);
-        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() == &quot;true&quot;) return true;
-        if (parameterValue.toLowerCase() == &quot;on&quot;) return true;
-        return false;
-    },
-
-    _getQueryParameter: function(searchKey) {
-        var str = this.queryString
-        if (str == null) return null;
-        var clauses = str.split('&amp;');
-        for (var i = 0; i &lt; 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 == &quot;&quot;) return new Array();
-        var matches = str.match(/(?:\&quot;([^\&quot;]+)\&quot;|(?!\&quot;([^\&quot;]+)\&quot;)(\S+))/g);
-        // We either want non quote stuff ([^&quot;]+) 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: &quot;foo&quot; bar
-        // the results &quot;\&quot;foo\&quot;&quot; and &quot;bar&quot;
-
-        // So, let's unquote the quoted arguments:
-        var args = new Array;
-        for (var i = 0; i &lt; matches.length; i++) {
-            args[i] = matches[i];
-            args[i] = args[i].replace(/^&quot;(.*)&quot;$/, &quot;$1&quot;);
-        }
-        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)
-}
+/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+ *  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 &quot;AS IS&quot; 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 &lt; arguments.length; i++) {
+    element = arguments[i];
+    if (typeof element == 'string')
+      element = document.getElementById(element);
+    results[results.length] = element;
+  }
+  return results.length &lt; 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 &lt; 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(&quot;\\b&quot; + name + &quot;\\b&quot;, &quot;g&quot;);
+    element.className = element.className.replace(re, &quot;&quot;);
+}
+
+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 = &quot;&quot;;
+      element.style[name] = value;
+    }
+}
+
+function elementGetStyle(element, style) {
+    var value = element.style[style];
+    if (!value) {
+      if (document.defaultView &amp;&amp; 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 &amp;&amp; ['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, &quot;&quot;);
+    // strip leading
+    return result.replace(/\s+$/g, &quot;&quot;);
+    // 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 = &quot;&quot;;
+
+    var isRecentFirefox = (browserVersion.isFirefox &amp;&amp; browserVersion.firefoxVersion &gt;= &quot;1.5&quot;);
+    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, &quot; &quot;);
+        }
+        return text;
+    }
+    if (element.nodeType == 1 /*Node.ELEMENT_NODE*/) {
+        var childrenPreformatted = preformatted || (element.tagName == &quot;PRE&quot;);
+        var text = &quot;&quot;;
+        for (var i = 0; i &lt; element.childNodes.length; i++) {
+            var child = element.childNodes.item(i);
+            text += getTextContent(child, childrenPreformatted);
+        }
+        // Handle block elements that introduce newlines
+        // -- From HTML spec:
+        //&lt;!ENTITY % block
+        //     &quot;P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT |
+        //      BLOCKQUOTE | F:wORM | HR | TABLE | FIELDSET | ADDRESS&quot;&gt;
+        //
+        // TODO: should potentially introduce multiple newlines to separate blocks
+        if (element.tagName == &quot;P&quot; || element.tagName == &quot;BR&quot; || element.tagName == &quot;HR&quot; || element.tagName == &quot;DIV&quot;) {
+            text += &quot;\n&quot;;
+        }
+        return text;
+    }
+    return '';
+}
+
+/**
+ * Convert all newlines to \m
+ */
+function normalizeNewlines(text)
+{
+    return text.replace(/\r\n|\r/g, &quot;\n&quot;);
+}
+
+/**
+ * Replace multiple sequential spaces with a single space, and then convert &amp;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, &quot; &quot;);
+
+    // Replace &amp;nbsp; with a space
+    var nbspPattern = new RegExp(String.fromCharCode(160), &quot;g&quot;);
+    if (browserVersion.isSafari) {
+	return replaceAll(text, String.fromCharCode(160), &quot; &quot;);
+    } else {
+	return text.replace(nbspPattern, &quot; &quot;);
+    }
+}
+
+function replaceAll(text, oldText, newText) {
+    while (text.indexOf(oldText) != -1) {
+	text = text.replace(oldText, newText);
+    }
+    return text;
+}
+
+
+function xmlDecode(text) {
+    text = text.replace(/&amp;quot;/g, '&quot;');
+    text = text.replace(/&amp;apos;/g, &quot;'&quot;);
+    text = text.replace(/&amp;lt;/g, &quot;&lt;&quot;);
+    text = text.replace(/&amp;gt;/g, &quot;&gt;&quot;);
+    text = text.replace(/&amp;amp;/g, &quot;&amp;&quot;);
+    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 &lt;input&gt; 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(&quot;This element has no value; is it really a form field?&quot;);
+    }
+    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(&quot;invalid keySequence&quot;);
+}
+
+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(&quot;load&quot;, command, true);
+    else if (window.detachEvent)
+        element.detachEvent(&quot;onload&quot;, command);
+}
+
+function addLoadListener(element, command) {
+    LOG.debug('Adding loadListenter for ' + element + ', ' + command);
+    var augmentedCommand = function() {
+        command.call(this, element);
+    }
+    if (window.addEventListener &amp;&amp; !browserVersion.isOpera)
+        element.addEventListener(&quot;load&quot;, augmentedCommand, true);
+    else if (window.attachEvent)
+        element.attachEvent(&quot;onload&quot;, 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 &amp;&amp; regexpResult[1]) {
+        return regexpResult[1];
+    }
+    return 'anonymous';
+}
+
+function getDocumentBase(doc) {
+    var bases = document.getElementsByTagName(&quot;base&quot;);
+    if (bases &amp;&amp; bases.length &amp;&amp; bases[0].href) {
+        return bases[0].href;
+    }
+    return &quot;&quot;;
+}
+
+function getTagName(element) {
+    var tagName;
+    if (element &amp;&amp; element.tagName &amp;&amp; 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 &lt; 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) == &quot;object&quot;) &amp;&amp; (x[&quot;length&quot;] != 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 
+    * &amp;lt;base&amp;gt; tag; if the baseUrl doesn't end with &quot;/&quot;, we'll assume
+    * that it points to a file, and strip the filename off to find its
+    * base directory.
+    *
+    * So absolutify(&quot;foo&quot;, &quot;http://x/bar&quot;) will return &quot;http://x/foo&quot; (stripping off bar),
+    * whereas absolutify(&quot;foo&quot;, &quot;http://x/bar/&quot;) will return &quot;http://x/bar/foo&quot; (preserving bar).
+    * Naturally absolutify(&quot;foo&quot;, &quot;http://x&quot;) will return &quot;http://x/foo&quot;, 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 = &quot;file:///&quot; + baseUrl.replace(/\\/g, &quot;/&quot;);
+            loc = parseUrl(baseUrl);
+        } else {
+            throw new SeleniumError(&quot;baseUrl wasn't absolute: &quot; + 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 &quot;/&quot; + the url
+    if (!loc.pathname) {
+        loc.pathname = &quot;/&quot; + 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(&quot;Invalid URL: &quot; + url);
+    }
+    var loc = new Object();
+    for (var i = 0; i &lt; 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(&quot;Not a valid location object: &quot; + o2s(loc));
+    }
+    var protocol = loc.protocol;
+    protocol = protocol.replace(/:$/, &quot;&quot;);
+    var url = protocol + &quot;://&quot;;
+    if (loc.username) {
+        url += loc.username;
+        if (loc.password) {
+            url += &quot;:&quot; + loc.password;
+        }
+        url += &quot;@&quot;;
+    }
+    if (loc.host) {
+        url += loc.host;
+    }
+    
+    if (loc.port) {
+        url += &quot;:&quot; + loc.port;
+    }
+    
+    if (loc.pathname) {
+        url += loc.pathname;
+    }
+    
+    if (loc.search) {
+        url += &quot;?&quot; + loc.search;
+    }
+    if (loc.hash) {
+        var hash = loc.hash;
+        hash = loc.hash.replace(/^#/, &quot;&quot;);
+        url += &quot;#&quot; + hash;
+    }
+    return url;
+}
+
+function canonicalize(url) {
+    var tempLink = window.document.createElement(&quot;link&quot;);
+    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(&quot;/&quot;);
+  	var newParts = [];
+  	newParts.push(originalParts.shift());
+  	for (var i = 0; i &lt; originalParts.length; i++) {
+  		var part = originalParts[i];
+  		if (&quot;..&quot; == part) {
+  			newParts.pop();
+  			continue;
+  		}
+  		newParts.push(part);
+  	}
+  	loc.pathname = newParts.join(&quot;/&quot;);
+    return reassembleLocation(loc);
+}
+
+function extractExceptionMessage(ex) {
+    if (ex == null) return &quot;null exception&quot;;
+    if (ex.message != null) return ex.message;
+    if (ex.toString &amp;&amp; ex.toString() != null) return ex.toString();
+}
+    
+
+function describe(object, delimiter) {
+    var props = new Array();
+    for (var prop in object) {
+        try {
+            props.push(prop + &quot; -&gt; &quot; + object[prop]);
+        } catch (e) {
+            props.push(prop + &quot; -&gt; [htmlutils: ack! couldn't read this property! (Permission Denied?)]&quot;);
+        }
+    }
+    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(&quot;cannot find PatternMatcher.strategies.&quot; + 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 &quot;static&quot; convenience method for easy matching
+ */
+PatternMatcher.matches = function(pattern, actual) {
+    return new PatternMatcher(pattern).matches(actual);
+};
+
+PatternMatcher.strategies = {
+
+/**
+ * Exact matching, e.g. &quot;exact:***&quot;
+ */
+    exact: function(expected) {
+        this.expected = expected;
+        this.matches = function(actual) {
+            return actual == this.expected;
+        };
+    },
+
+/**
+ * Match by regular expression, e.g. &quot;regexp:^[0-9]+$&quot;
+ */
+    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);
+        };
+    },
+
+/**
+ * &quot;globContains&quot; (aka &quot;wildmat&quot;) patterns, e.g. &quot;glob:one,two,*&quot;,
+ * 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 &quot;.*&quot;.  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);
+        };
+    },
+
+
+/**
+ * &quot;glob&quot; (aka &quot;wildmat&quot;) patterns, e.g. &quot;glob:one,two,*&quot;
+ */
+    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, &quot;\\$1&quot;);
+    re = re.replace(/\?/g, &quot;(.|[\r\n])&quot;);
+    re = re.replace(/\*/g, &quot;(.|[\r\n])*&quot;);
+    return re;
+};
+
+PatternMatcher.regexpFromGlobContains = function(globContains) {
+    return PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(globContains);
+};
+
+PatternMatcher.regexpFromGlob = function(glob) {
+    return &quot;^&quot; + PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(glob) + &quot;$&quot;;
+};
+
+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 +
+                    &quot;Expected '&quot; + args.expected +
+                    &quot;' but was '&quot; + args.actual + &quot;'&quot;);
+    },
+
+/*
+* Assert.matches(comment?, pattern, actual)
+*/
+    matches: function() {
+        var args = new AssertionArguments(arguments);
+        if (PatternMatcher.matches(args.expected, args.actual)) {
+            return;
+        }
+        Assert.fail(args.comment +
+                    &quot;Actual value '&quot; + args.actual +
+                    &quot;' did not match '&quot; + args.expected + &quot;'&quot;);
+    },
+
+/*
+* Assert.notMtches(comment?, pattern, actual)
+*/
+    notMatches: function() {
+        var args = new AssertionArguments(arguments);
+        if (!PatternMatcher.matches(args.expected, args.actual)) {
+            return;
+        }
+        Assert.fail(args.comment +
+                    &quot;Actual value '&quot; + args.actual +
+                    &quot;' did match '&quot; + args.expected + &quot;'&quot;);
+    }
+
+};
+
+// Preprocess the arguments to allow for an optional comment.
+function AssertionArguments(args) {
+    if (args.length == 2) {
+        this.comment = &quot;&quot;;
+        this.expected = args[0];
+        this.actual = args[1];
+    } else {
+        this.comment = args[0] + &quot;; &quot;;
+        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 += '&gt; ' + a.callee.toString() + '\n';
+            if (a.caller == a) {
+                result += '*';
+                break;
+            }
+        }
+        error.stack = result;
+    }
+    error.isSeleniumError = true;
+    return error;
+}
+
+function highlight(element) {
+    var highLightColor = &quot;yellow&quot;;
+    if (element.originalColor == undefined) { // avoid picking up highlight
+        element.originalColor = elementGetStyle(element, &quot;background-color&quot;);
+    }
+    elementSetStyle(element, {&quot;backgroundColor&quot; : highLightColor});
+    window.setTimeout(function() {
+        try {
+            //if element is orphan, probably page of it has already gone, so ignore
+            if (!element.parentNode) {
+                return;
+            }
+            elementSetStyle(element, {&quot;backgroundColor&quot; : element.originalColor});
+        } catch (e) {} // DGF unhighlighting is very dangerous and low priority
+    }, 200);
+}
+
+
+
+// for use from vs.2003 debugger
+function o2s(obj) {
+    var s = &quot;&quot;;
+    for (key in obj) {
+        var line = key + &quot;-&gt;&quot; + obj[key];
+        line.replace(&quot;\n&quot;, &quot; &quot;);
+        s += line + &quot;\n&quot;;
+    }
+    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 = &quot;Couldn't open app window; is the pop-up blocker enabled?&quot;
+        LOG.error(errorMessage);
+        throw new Error(&quot;Couldn't open app window; is the pop-up blocker enabled?&quot;);
+    }
+    try {
+        var windowHeight = 500;
+        if (window.outerHeight) {
+            windowHeight = window.outerHeight;
+        } else if (document.documentElement &amp;&amp; document.documentElement.offsetHeight) {
+            windowHeight = document.documentElement.offsetHeight;
+        }
+
+        if (window.screenLeft &amp;&amp; !window.screenX) window.screenX = window.screenLeft;
+        if (window.screenTop &amp;&amp; !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(&quot;Couldn't resize app window&quot;);
+        LOG.exception(e);
+    }
+
+
+    if (!suppressMozillaWarning &amp;&amp; window.document.readyState == null &amp;&amp; !seenReadyStateWarning) {
+        alert(&quot;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.&quot;);
+        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() == &quot;true&quot;) return true;
+        if (parameterValue.toLowerCase() == &quot;on&quot;) return true;
+        return false;
+    },
+
+    _getQueryParameter: function(searchKey) {
+        var str = this.queryString
+        if (str == null) return null;
+        var clauses = str.split('&amp;');
+        for (var i = 0; i &lt; 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 == &quot;&quot;) return new Array();
+        var matches = str.match(/(?:\&quot;([^\&quot;]+)\&quot;|(?!\&quot;([^\&quot;]+)\&quot;)(\S+))/g);
+        // We either want non quote stuff ([^&quot;]+) 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: &quot;foo&quot; bar
+        // the results &quot;\&quot;foo\&quot;&quot; and &quot;bar&quot;
+
+        // So, let's unquote the quoted arguments:
+        var args = new Array;
+        for (var i = 0; i &lt; matches.length; i++) {
+            args[i] = matches[i];
+            args[i] = args[i].replace(/^&quot;(.*)&quot;$/, &quot;$1&quot;);
+        }
+        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)
+}</diff>
      <filename>testframework/selenium/scripts/htmlutils.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,72 +1,72 @@
-&lt;script language=&quot;JavaScript&quot;&gt;
-    if (window[&quot;selenium_has_been_loaded_into_this_window&quot;]==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 &quot;selenium&quot;
-// 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) + &quot;\n&quot;;
-        sendToRC(s,&quot;state=true&quot;);
-}
-
-function selenium_frameRunTest(oldOnLoadRoutine) {
-	if (oldOnLoadRoutine) {
-		eval(oldOnLoadRoutine);
-	}
-        runSeleniumTest();
-}
-
-function seleniumOnLoad() {
-    injectedSessionId = @SESSION_ID@;
-    window[&quot;selenium_has_been_loaded_into_this_window&quot;] = true;
-    runSeleniumTest();
-}
-
-function seleniumOnUnload() {
-	sendToRC(&quot;Current window or frame is closed!&quot;, &quot;closing=true&quot;);
-}
-
-if (window.addEventListener) {
-        window.addEventListener(&quot;load&quot;, seleniumOnLoad, false);	// firefox
-        window.addEventListener(&quot;unload&quot;, seleniumOnUnload, false);	// firefox
-} else if (window.attachEvent){
-    	window.attachEvent(&quot;onload&quot;, seleniumOnLoad);	// IE
-        window.attachEvent(&quot;onunload&quot;, seleniumOnUnload);	// IE
-}
-else {
-    	throw &quot;causing a JavaScript error to tell the world that I did not arrange to be run on load&quot;;
-}
-
-injectedSessionId = @SESSION_ID@;
-proxyInjectionMode = true;
-}
-&lt;/script&gt;
+&lt;script language=&quot;JavaScript&quot;&gt;
+    if (window[&quot;selenium_has_been_loaded_into_this_window&quot;]==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 &quot;selenium&quot;
+// 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) + &quot;\n&quot;;
+        sendToRC(s,&quot;state=true&quot;);
+}
+
+function selenium_frameRunTest(oldOnLoadRoutine) {
+	if (oldOnLoadRoutine) {
+		eval(oldOnLoadRoutine);
+	}
+        runSeleniumTest();
+}
+
+function seleniumOnLoad() {
+    injectedSessionId = @SESSION_ID@;
+    window[&quot;selenium_has_been_loaded_into_this_window&quot;] = true;
+    runSeleniumTest();
+}
+
+function seleniumOnUnload() {
+	sendToRC(&quot;Current window or frame is closed!&quot;, &quot;closing=true&quot;);
+}
+
+if (window.addEventListener) {
+        window.addEventListener(&quot;load&quot;, seleniumOnLoad, false);	// firefox
+        window.addEventListener(&quot;unload&quot;, seleniumOnUnload, false);	// firefox
+} else if (window.attachEvent){
+    	window.attachEvent(&quot;onload&quot;, seleniumOnLoad);	// IE
+        window.attachEvent(&quot;onunload&quot;, seleniumOnUnload);	// IE
+}
+else {
+    	throw &quot;causing a JavaScript error to tell the world that I did not arrange to be run on load&quot;;
+}
+
+injectedSessionId = @SESSION_ID@;
+proxyInjectionMode = true;
+}
+&lt;/script&gt;</diff>
      <filename>testframework/selenium/scripts/injection.html</filename>
    </modified>
    <modified>
      <diff>@@ -1,70 +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 &quot;var&quot; 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&lt;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 = '&lt;tr&gt;&lt;td style=&quot;display:none;&quot; class=&quot;js&quot;&gt;getEval&lt;/td&gt;' +
-                   '&lt;td style=&quot;display:none;&quot;&gt;currentTest.doNextCommand()&lt;/td&gt;' +
-                   '&lt;td style=&quot;white-space: pre;&quot;&gt;' + script_fragment + '&lt;/td&gt;' + 
-                   '&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;\n';
-        new_test_source += new_line;
-        //eval(script_fragment);
-        
-              
-    };
-    
-    
-    
-    execute(parse_result,x2)
-
-    // Create HTML Table        
-    body = document.body      
-    body.innerHTML += &quot;&lt;table class='selenium' id='se-js-table'&gt;&quot;+
-                      &quot;&lt;tbody&gt;&quot; +
-                      &quot;&lt;tr&gt;&lt;td&gt;// &quot; + document.title + &quot;&lt;/td&gt;&lt;/tr&gt;&quot; +
-                      new_test_source +
-                      &quot;&lt;/tbody&quot; +
-                      &quot;&lt;/table&gt;&quot;;          
-   
-    //body.innerHTML = &quot;&lt;pre&gt;&quot; + parse_result + &quot;&lt;/pre&gt;&quot;
-}
-
-
+/*
+
+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 &quot;var&quot; 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&lt;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 = '&lt;tr&gt;&lt;td style=&quot;display:none;&quot; class=&quot;js&quot;&gt;getEval&lt;/td&gt;' +
+                   '&lt;td style=&quot;display:none;&quot;&gt;currentTest.doNextCommand()&lt;/td&gt;' +
+                   '&lt;td style=&quot;white-space: pre;&quot;&gt;' + script_fragment + '&lt;/td&gt;' + 
+                   '&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;\n';
+        new_test_source += new_line;
+        //eval(script_fragment);
+        
+              
+    };
+    
+    
+    
+    execute(parse_result,x2)
+
+    // Create HTML Table        
+    body = document.body      
+    body.innerHTML += &quot;&lt;table class='selenium' id='se-js-table'&gt;&quot;+
+                      &quot;&lt;tbody&gt;&quot; +
+                      &quot;&lt;tr&gt;&lt;td&gt;// &quot; + document.title + &quot;&lt;/td&gt;&lt;/tr&gt;&quot; +
+                      new_test_source +
+                      &quot;&lt;/tbody&quot; +
+                      &quot;&lt;/table&gt;&quot;;          
+   
+    //body.innerHTML = &quot;&lt;pre&gt;&quot; + parse_result + &quot;&lt;/pre&gt;&quot;
+}
+
+</diff>
      <filename>testframework/selenium/scripts/js2html.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,63 +1,63 @@
-/*
-
-This is an experiment in creating a &quot;selenese&quot; 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 &quot;param&quot;
-    command &quot;param&quot; // comment
-    command &quot;param&quot; &quot;param2&quot;
-    command &quot;param&quot; &quot;param2&quot; // this is a comment
-
-TODO: 
-1) Deal with multiline parameters
-2) Escape quotes properly
-3) Determine whether this should/will become the &quot;preferred&quot; 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+) *&quot;([^&quot;]*)&quot; *(?:&quot;([^&quot;]*)&quot;){0,1}(?: *(\/\/ *.+))*/i;
-    var comment_pattern = /^ *(\/\/ *.+)/
-
-    // Regex each line into selenium command and convert into table row.
-    // eg. &quot;&lt;command&gt; &lt;quote&gt; &lt;quote&gt; &lt;comment&gt;&quot;
-    var new_test_source = '';
-    var new_line        = '';
-    for (var x=0; x &lt; lines.length; x++) {
-        result = lines[x].match(command_pattern);
-        if (result != null) {
-            new_line = &quot;&lt;tr&gt;&lt;td&gt;&quot; + (result[1] || '&amp;nbsp;') + &quot;&lt;/td&gt;&quot; +
-                           &quot;&lt;td&gt;&quot; + (result[2] || '&amp;nbsp;') + &quot;&lt;/td&gt;&quot; +
-                           &quot;&lt;td&gt;&quot; + (result[3] || '&amp;nbsp;') + &quot;&lt;/td&gt;&quot; +
-                           &quot;&lt;td&gt;&quot; + (result[4] || '&amp;nbsp;') + &quot;&lt;/td&gt;&lt;/tr&gt;\n&quot;;
-            new_test_source += new_line;
-        }
-        result = lines[x].match(comment_pattern);
-        if (result != null) {
-            new_line = '&lt;tr&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;4&quot;&gt;' +
-                       (result[1] || '&amp;nbsp;') +
-                       '&lt;/td&gt;&lt;/tr&gt;';
-            new_test_source += new_line;
-        }
-    }
-
-    // Create HTML Table        
-    body = doc.body
-    body.innerHTML += &quot;&lt;table class='selenium' id='testtable'&gt;&quot;+
-                      new_test_source +
-                      &quot;&lt;/table&gt;&quot;;
-
-}
-
-
+/*
+
+This is an experiment in creating a &quot;selenese&quot; 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 &quot;param&quot;
+    command &quot;param&quot; // comment
+    command &quot;param&quot; &quot;param2&quot;
+    command &quot;param&quot; &quot;param2&quot; // this is a comment
+
+TODO: 
+1) Deal with multiline parameters
+2) Escape quotes properly
+3) Determine whether this should/will become the &quot;preferred&quot; 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+) *&quot;([^&quot;]*)&quot; *(?:&quot;([^&quot;]*)&quot;){0,1}(?: *(\/\/ *.+))*/i;
+    var comment_pattern = /^ *(\/\/ *.+)/
+
+    // Regex each line into selenium command and convert into table row.
+    // eg. &quot;&lt;command&gt; &lt;quote&gt; &lt;quote&gt; &lt;comment&gt;&quot;
+    var new_test_source = '';
+    var new_line        = '';
+    for (var x=0; x &lt; lines.length; x++) {
+        result = lines[x].match(command_pattern);
+        if (result != null) {
+            new_line = &quot;&lt;tr&gt;&lt;td&gt;&quot; + (result[1] || '&amp;nbsp;') + &quot;&lt;/td&gt;&quot; +
+                           &quot;&lt;td&gt;&quot; + (result[2] || '&amp;nbsp;') + &quot;&lt;/td&gt;&quot; +
+                           &quot;&lt;td&gt;&quot; + (result[3] || '&amp;nbsp;') + &quot;&lt;/td&gt;&quot; +
+                           &quot;&lt;td&gt;&quot; + (result[4] || '&amp;nbsp;') + &quot;&lt;/td&gt;&lt;/tr&gt;\n&quot;;
+            new_test_source += new_line;
+        }
+        result = lines[x].match(comment_pattern);
+        if (result != null) {
+            new_line = '&lt;tr&gt;&lt;td rowspan=&quot;1&quot; colspan=&quot;4&quot;&gt;' +
+                       (result[1] || '&amp;nbsp;') +
+                       '&lt;/td&gt;&lt;/tr&gt;';
+            new_test_source += new_line;
+        }
+    }
+
+    // Create HTML Table        
+    body = doc.body
+    body.innerHTML += &quot;&lt;table class='selenium' id='testtable'&gt;&quot;+
+                      new_test_source +
+                      &quot;&lt;/table&gt;&quot;;
+
+}
+
+</diff>
      <filename>testframework/selenium/scripts/se2html.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,2409 +1,2409 @@
-/*
- * Copyright 2004 ThoughtWorks, Inc
- *
- *  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
- *  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 &quot;AS IS&quot; 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.
-     *
-     * &lt;h3&gt;&lt;a name=&quot;locators&quot;&gt;&lt;/a&gt;Element Locators&lt;/h3&gt;
-     * &lt;p&gt;
-     * Element Locators tell Selenium which HTML element a command refers to.
-     * The format of a locator is:&lt;/p&gt;
-     * &lt;blockquote&gt;
-     * &lt;em&gt;locatorType&lt;/em&gt;&lt;strong&gt;=&lt;/strong&gt;&lt;em&gt;argument&lt;/em&gt;
-     * &lt;/blockquote&gt;
-     *
-     * &lt;p&gt;
-     * We support the following strategies for locating elements:
-     * &lt;/p&gt;
-     * 
-     * &lt;ul&gt;
-     * &lt;li&gt;&lt;strong&gt;identifier&lt;/strong&gt;=&lt;em&gt;id&lt;/em&gt;: 
-     * Select the element with the specified &amp;#064;id attribute. If no match is
-     * found, select the first element whose &amp;#064;name attribute is &lt;em&gt;id&lt;/em&gt;.
-     * (This is normally the default; see below.)&lt;/li&gt;
-     * &lt;li&gt;&lt;strong&gt;id&lt;/strong&gt;=&lt;em&gt;id&lt;/em&gt;:
-     * Select the element with the specified &amp;#064;id attribute.&lt;/li&gt;
-     *
-     * &lt;li&gt;&lt;strong&gt;name&lt;/strong&gt;=&lt;em&gt;name&lt;/em&gt;:
-     * Select the first element with the specified &amp;#064;name attribute.
-     * &lt;ul class=&quot;first last simple&quot;&gt;
-     * &lt;li&gt;username&lt;/li&gt;
-     * &lt;li&gt;name=username&lt;/li&gt;
-     * &lt;/ul&gt;
-     * 
-     * &lt;p&gt;The name may optionally be followed by one or more &lt;em&gt;element-filters&lt;/em&gt;, separated from the name by whitespace.  If the &lt;em&gt;filterType&lt;/em&gt; is not specified, &lt;strong&gt;value&lt;/strong&gt; is assumed.&lt;/p&gt;
-     *
-     * &lt;ul class=&quot;first last simple&quot;&gt;
-     * &lt;li&gt;name=flavour value=chocolate&lt;/li&gt;
-     * &lt;/ul&gt;
-     * &lt;/li&gt;
-     * &lt;li&gt;&lt;strong&gt;dom&lt;/strong&gt;=&lt;em&gt;javascriptExpression&lt;/em&gt;: 
-     *
-     * 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.
-     * &lt;ul class=&quot;first last simple&quot;&gt;
-     * &lt;li&gt;dom=document.forms['myForm'].myDropdown&lt;/li&gt;
-     * &lt;li&gt;dom=document.images[56]&lt;/li&gt;
-     * &lt;li&gt;dom=function foo() { return document.links[1]; }; foo();&lt;/li&gt;
-     * &lt;/ul&gt;
-     *
-     * &lt;/li&gt;
-     *
-     * &lt;li&gt;&lt;strong&gt;xpath&lt;/strong&gt;=&lt;em&gt;xpathExpression&lt;/em&gt;: 
-     * Locate an element using an XPath expression.
-     * &lt;ul class=&quot;first last simple&quot;&gt;
-     * &lt;li&gt;xpath=//img[&amp;#064;alt='The image alt text']&lt;/li&gt;
-     * &lt;li&gt;xpath=//table[&amp;#064;id='table1']//tr[4]/td[2]&lt;/li&gt;
-     * &lt;li&gt;xpath=//a[contains(&amp;#064;href,'#id1')]&lt;/li&gt;
-     * &lt;li&gt;xpath=//a[contains(&amp;#064;href,'#id1')]/&amp;#064;class&lt;/li&gt;
-     * &lt;li&gt;xpath=(//table[&amp;#064;class='stylee'])//th[text()='theHeaderText']/../td&lt;/li&gt;
-     * &lt;li&gt;xpath=//input[&amp;#064;name='name2' and &amp;#064;value='yes']&lt;/li&gt;
-     * &lt;li&gt;xpath=//*[text()=&quot;right&quot;]&lt;/li&gt;
-     *
-     * &lt;/ul&gt;
-     * &lt;/li&gt;
-     * &lt;li&gt;&lt;strong&gt;link&lt;/strong&gt;=&lt;em&gt;textPattern&lt;/em&gt;:
-     * Select the link (anchor) element which contains text matching the
-     * specified &lt;em&gt;pattern&lt;/em&gt;.
-     * &lt;ul class=&quot;first last simple&quot;&gt;
-     * &lt;li&gt;link=The link text&lt;/li&gt;
-     * &lt;/ul&gt;
-     *
-     * &lt;/li&gt;
-     *
-     * &lt;li&gt;&lt;strong&gt;css&lt;/strong&gt;=&lt;em&gt;cssSelectorSyntax&lt;/em&gt;:
-     * Select the element using css selectors. Please refer to &lt;a href=&quot;http://www.w3.org/TR/REC-CSS2/selector.html&quot;&gt;CSS2 selectors&lt;/a&gt;, &lt;a href=&quot;http://www.w3.org/TR/2001/CR-css3-selectors-20011113/&quot;&gt;CSS3 selectors&lt;/a&gt; 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.
-     * &lt;ul class=&quot;first last simple&quot;&gt;
-     * &lt;li&gt;css=a[href=&quot;#id3&quot;]&lt;/li&gt;
-     * &lt;li&gt;css=span#firstChild + span&lt;/li&gt;
-     * &lt;/ul&gt;
-     * &lt;p&gt;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). &lt;/p&gt;
-     * &lt;/li&gt;
-     * &lt;/ul&gt;
-     * 
-     * &lt;p&gt;
-     * Without an explicit locator prefix, Selenium uses the following default
-     * strategies:
-     * &lt;/p&gt;
-     *
-     * &lt;ul class=&quot;simple&quot;&gt;
-     * &lt;li&gt;&lt;strong&gt;dom&lt;/strong&gt;, for locators starting with &amp;quot;document.&amp;quot;&lt;/li&gt;
-     * &lt;li&gt;&lt;strong&gt;xpath&lt;/strong&gt;, for locators starting with &amp;quot;//&amp;quot;&lt;/li&gt;
-     * &lt;li&gt;&lt;strong&gt;identifier&lt;/strong&gt;, otherwise&lt;/li&gt;
-     * &lt;/ul&gt;
-     *
-     * &lt;h3&gt;&lt;a name=&quot;element-filters&quot;&gt;Element Filters&lt;/a&gt;&lt;/h3&gt;
-     * &lt;blockquote&gt;
-     * &lt;p&gt;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.&lt;/p&gt;
-     * &lt;p&gt;Filters look much like locators, ie.&lt;/p&gt;
-     * &lt;blockquote&gt;
-     * &lt;em&gt;filterType&lt;/em&gt;&lt;strong&gt;=&lt;/strong&gt;&lt;em&gt;argument&lt;/em&gt;&lt;/blockquote&gt;
-     *
-     * &lt;p&gt;Supported element-filters are:&lt;/p&gt;
-     * &lt;p&gt;&lt;strong&gt;value=&lt;/strong&gt;&lt;em&gt;valuePattern&lt;/em&gt;&lt;/p&gt;
-     * &lt;blockquote&gt;
-     * Matches elements based on their values.  This is particularly useful for refining a list of similarly-named toggle-buttons.&lt;/blockquote&gt;
-     * &lt;p&gt;&lt;strong&gt;index=&lt;/strong&gt;&lt;em&gt;index&lt;/em&gt;&lt;/p&gt;
-     * &lt;blockquote&gt;
-     * Selects a single element based on its position in the list (offset from zero).&lt;/blockquote&gt;
-     * &lt;/blockquote&gt;
-     *
-     * &lt;h3&gt;&lt;a name=&quot;patterns&quot;&gt;&lt;/a&gt;String-match Patterns&lt;/h3&gt;
-     *
-     * &lt;p&gt;
-     * Various Pattern syntaxes are available for matching string values:
-     * &lt;/p&gt;
-     * &lt;ul&gt;
-     * &lt;li&gt;&lt;strong&gt;glob:&lt;/strong&gt;&lt;em&gt;pattern&lt;/em&gt;:
-     * Match a string against a &quot;glob&quot; (aka &quot;wildmat&quot;) pattern. &quot;Glob&quot; is a
-     * kind of limited regular-expression syntax typically used in command-line
-     * shells. In a glob pattern, &quot;*&quot; represents any sequence of characters, and &quot;?&quot;
-     * represents any single character. Glob patterns match against the entire
-     * string.&lt;/li&gt;
-     * &lt;li&gt;&lt;strong&gt;regexp:&lt;/strong&gt;&lt;em&gt;regexp&lt;/em&gt;:
-     * Match a string using a regular-expression. The full power of JavaScript
-     * regular-expressions is available.&lt;/li&gt;
-     * &lt;li&gt;&lt;strong&gt;exact:&lt;/strong&gt;&lt;em&gt;string&lt;/em&gt;:
-     *
-     * Match a string exactly, verbatim, without any of that fancy wildcard
-     * stuff.&lt;/li&gt;
-     * &lt;/ul&gt;
-     * &lt;p&gt;
-     * If no pattern prefix is specified, Selenium assumes that it's a &quot;glob&quot;
-     * pattern.
-     * &lt;/p&gt;
-     */
-    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(&quot;Timeout is not a number: '&quot; + timeout + &quot;'&quot;);
-    }
-    var now = new Date().getTime();
-    var timeoutTime = now + timeoutValue;
-    return function() {
-        if (new Date().getTime() &gt; timeoutTime) {
-            throw new SeleniumError(&quot;Timed out after &quot; + timeoutValue + &quot;ms&quot;);
-        }
-        return f();
-    };
-}
-
-Selenium.createForWindow = function(window, proxyInjectionMode) {
-    if (!window.location) {
-        throw &quot;error: not a window!&quot;;
-    }
-    return new Selenium(BrowserBot.createForWindow(window, proxyInjectionMode));
-};
-
-Selenium.prototype.reset = function() {
-    this.defaultTimeout = Selenium.DEFAULT_TIMEOUT;
-    // todo: this.browserbot.reset()
-    this.browserbot.selectWindow(&quot;null&quot;);
-    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 &amp;quot;on&lt;em&gt;event&lt;/em&gt;&amp;quot;
-   * handler.
-   *
-   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @param eventName the event name, e.g. &quot;focus&quot; or &quot;blur&quot;
-   */
-    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @param keySequence Either be a string(&quot;\&quot; followed by the numeric keycode
-   *  of the key to be pressed, normally the ASCII value of that key), or a single
-   *  character. For example: &quot;w&quot;, &quot;\119&quot;.
-   */
-    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @param keySequence Either be a string(&quot;\&quot; followed by the numeric keycode
-   *  of the key to be pressed, normally the ASCII value of that key), or a single
-   *  character. For example: &quot;w&quot;, &quot;\119&quot;.
-   */
-    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @param keySequence Either be a string(&quot;\&quot; followed by the numeric keycode
-   *  of the key to be pressed, normally the ASCII value of that key), or a single
-   *  character. For example: &quot;w&quot;, &quot;\119&quot;.
-   */
-    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   */
-    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   */
-    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   */
-   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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   */
-   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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   */
-   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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @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.
-   *
-   * &lt;p&gt;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.&lt;/p&gt;
-   *
-   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @param value the value to type
-   */
-   if (this.browserbot.controlKeyDown || this.browserbot.altKeyDown || this.browserbot.metaKeyDown) {
-        throw new SeleniumError(&quot;type not supported immediately after call to controlKeyDown() or altKeyDown() or metaKeyDown()&quot;);
-    }
-        // 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.
-    *
-    * &lt;p&gt;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.&lt;/p&gt;
-    * 
-    * &lt;p&gt;Unlike the simple &quot;type&quot; 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 &quot;typeKeys&quot; on a form element, you may or may not see the results of what you typed in
-    * the field.&lt;/p&gt;
-    * &lt;p&gt;In some cases, you may need to use the simple &quot;type&quot; command to set the value of the field and then the &quot;typeKeys&quot; command to
-    * send the keystroke events corresponding to what you just typed.&lt;/p&gt;
-    *
-    * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-    * @param value the value to type
-    */
-    var keys = new String(value).split(&quot;&quot;);
-    for (var i = 0; i &lt; 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(&quot;this operation is only implemented in selenium-rc, and should never result in a request making it across the wire&quot;);
-};
-
-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(&quot;this operation is only implemented in selenium-rc, and should never result in a request making it across the wire&quot;);
-};
-
-Selenium.prototype.findToggleButton = function(locator) {
-    var element = this.browserbot.findElement(locator);
-    if (element.checked == null) {
-        Assert.fail(&quot;Element &quot; + locator + &quot; is not a toggle-button.&quot;);
-    }
-    return element;
-}
-
-Selenium.prototype.doCheck = function(locator) {
-    /**
-   * Check a toggle-button (checkbox/radio)
-   *
-   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   */
-    this.findToggleButton(locator).checked = true;
-};
-
-Selenium.prototype.doUncheck = function(locator) {
-    /**
-   * Uncheck a toggle-button (checkbox/radio)
-   *
-   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   */
-    this.findToggleButton(locator).checked = false;
-};
-
-Selenium.prototype.doSelect = function(selectLocator, optionLocator) {
-    /**
-   * Select an option from a drop-down using an option locator.
-   *
-   * &lt;p&gt;
-   * 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.
-   * &lt;/p&gt;
-   * &lt;ul&gt;
-   * &lt;li&gt;&lt;strong&gt;label&lt;/strong&gt;=&lt;em&gt;labelPattern&lt;/em&gt;:
-   * matches options based on their labels, i.e. the visible text. (This
-   * is the default.)
-   * &lt;ul class=&quot;first last simple&quot;&gt;
-   * &lt;li&gt;label=regexp:^[Oo]ther&lt;/li&gt;
-   * &lt;/ul&gt;
-   * &lt;/li&gt;
-   * &lt;li&gt;&lt;strong&gt;value&lt;/strong&gt;=&lt;em&gt;valuePattern&lt;/em&gt;:
-   * matches options based on their values.
-   * &lt;ul class=&quot;first last simple&quot;&gt;
-   * &lt;li&gt;value=other&lt;/li&gt;
-   * &lt;/ul&gt;
-   *
-   *
-   * &lt;/li&gt;
-   * &lt;li&gt;&lt;strong&gt;id&lt;/strong&gt;=&lt;em&gt;id&lt;/em&gt;:
-   *
-   * matches options based on their ids.
-   * &lt;ul class=&quot;first last simple&quot;&gt;
-   * &lt;li&gt;id=option1&lt;/li&gt;
-   * &lt;/ul&gt;
-   * &lt;/li&gt;
-   * &lt;li&gt;&lt;strong&gt;index&lt;/strong&gt;=&lt;em&gt;index&lt;/em&gt;:
-   * matches an option based on its index (offset from zero).
-   * &lt;ul class=&quot;first last simple&quot;&gt;
-   *
-   * &lt;li&gt;index=2&lt;/li&gt;
-   * &lt;/ul&gt;
-   * &lt;/li&gt;
-   * &lt;/ul&gt;
-   * &lt;p&gt;
-   * If no option locator prefix is provided, the default behaviour is to match on &lt;strong&gt;label&lt;/strong&gt;.
-   * &lt;/p&gt;
-   *
-   *
-   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-   * @param optionLocator an option locator (a label by default)
-   */
-    var element = this.browserbot.findElement(selectLocator);
-    if (!(&quot;options&quot; in element)) {
-        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
-    }
-    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box
-   * @param optionLocator an option locator (a label by default)
-   */
-    var element = this.browserbot.findElement(locator);
-    if (!(&quot;options&quot; in element)) {
-        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
-    }
-    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box
-   * @param optionLocator an option locator (a label by default)
-   */
-
-    var element = this.browserbot.findElement(locator);
-    if (!(&quot;options&quot; in element)) {
-        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
-    }
-    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box
-    */
-    var element = this.browserbot.findElement(locator);
-    if (!(&quot;options&quot; in element)) {
-        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
-    }
-    for (var i = 0; i &lt; 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 &quot;Search&quot; forms.
-   *
-   * @param formLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &amp;quot;open&amp;quot; command waits for the page to load before proceeding,
-   * ie. the &amp;quot;AndWait&amp;quot; suffix is implicit.
-   *
-   * &lt;em&gt;Note&lt;/em&gt;: 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[&quot;proxyInjectionMode&quot;] == null || !window[&quot;proxyInjectionMode&quot;]) {
-        return this.makePageLoadCondition();
-    } // in PI mode, just return &quot;OK&quot;; 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.
-   * 
-   * &lt;p&gt;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 &quot;onLoad&quot; 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(&quot;&quot;, &quot;myFunnyWindow&quot;).&lt;/p&gt;
-   *
-   * @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.
-   *
-   * &lt;p&gt;Note that there is a big difference between a window's internal JavaScript &quot;name&quot; property
-   * and the &quot;title&quot; 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 &quot;name&quot; is normally invisible to the end-user; it's the second 
-   * parameter &quot;windowName&quot; passed to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag)
-   * (which selenium intercepts).&lt;/p&gt;
-   *
-   * &lt;p&gt;Selenium has several strategies for finding the window object referred to by the &quot;windowID&quot; parameter.&lt;/p&gt;
-   * 
-   * &lt;p&gt;1.) if windowID is null, (or the string &quot;null&quot;) then it is assumed the user is referring to the original window instantiated by the browser).&lt;/p&gt;
-   * &lt;p&gt;2.) if the value of the &quot;windowID&quot; 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.&lt;/p&gt;
-   * &lt;p&gt;3.) Otherwise, selenium looks in a hash it maintains that maps string names to window &quot;names&quot;.&lt;/p&gt;
-   * &lt;p&gt;4.) If &lt;i&gt;that&lt;/i&gt; fails, we'll try looping over all of the known windows to try to find the appropriate &quot;title&quot;.
-   * Since &quot;title&quot; is not necessarily unique, this may have unexpected behavior.&lt;/p&gt;
-   *
-   * &lt;p&gt;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:&lt;/p&gt;
-   * 
-   * &lt;p&gt;&lt;code&gt;debug: window.open call intercepted; window ID (which you can use with selectWindow()) is &quot;myNewWindow&quot;&lt;/code&gt;&lt;/p&gt;
-   *
-   * &lt;p&gt;In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the &quot;onLoad&quot; 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(&quot;&quot;, &quot;myFunnyWindow&quot;).&lt;/p&gt;
-   * 
-   * @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
-    * &quot;relative=parent&quot; as a locator; to select the top frame, use &quot;relative=top&quot;.
-    * You can also select a frame by its 0-based index number; select the first frame with
-    * &quot;index=0&quot;, or the third frame with &quot;index=2&quot;.
-    *
-    * &lt;p&gt;You may also use a DOM expression to identify the frame you want directly,
-    * like this: &lt;code&gt;dom=frames[&quot;main&quot;].frames[&quot;subframe&quot;]&lt;/code&gt;&lt;/p&gt;
-    *
-    * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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.
-     *
-     * &lt;p&gt;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 &quot;current&quot; 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.&lt;/p&gt;
-     *
-     * @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.
-     *
-     * &lt;p&gt;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 &quot;current&quot; 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.&lt;/p&gt;
-     *
-     * @param currentWindowString starting window
-     * @param target new window (which might be relative to the current one, e.g., &quot;_parent&quot;)
-     * @return boolean true if the new window is this code's window
-     */
-     if (window.opener!=null &amp;&amp; window.opener[target]!=null &amp;&amp; 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 (&quot;about:blank&quot; == targetWindow.location) return false;
-        if (browserVersion.isKonqueror) {
-            if (&quot;/&quot; == 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(&quot;DGF what a world!&quot;);
-                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 &quot;back&quot; button on their browser.
-     *
-     */
-    this.browserbot.goBack();
-};
-
-Selenium.prototype.doRefresh = function() {
-    /**
-     * Simulates the user clicking the &quot;Refresh&quot; button on their browser.
-     *
-     */
-    this.browserbot.refresh();
-};
-
-Selenium.prototype.doClose = function() {
-    /**
-     * Simulates the user clicking the &quot;close&quot; button in the titlebar of a popup
-     * window or tab.
-     */
-    this.browserbot.close();
-};
-
-Selenium.prototype.ensureNoUnhandledPopups = function() {
-    if (this.browserbot.hasAlerts()) {
-        throw new SeleniumError(&quot;There was an unexpected Alert! [&quot; + this.browserbot.getNextAlert() + &quot;]&quot;);
-    }
-    if ( this.browserbot.hasConfirmations() ) {
-        throw new SeleniumError(&quot;There was an unexpected Confirmation! [&quot; + this.browserbot.getNextConfirmation() + &quot;]&quot;);
-    }
-};
-
-Selenium.prototype.isAlertPresent = function() {
-   /**
-   * Has an alert occurred?
-   *
-   * &lt;p&gt;
-   * This function never throws an exception
-   * &lt;/p&gt;
-   * @return boolean true if there is an alert
-   */
-    return this.browserbot.hasAlerts();
-};
-
-Selenium.prototype.isPromptPresent = function() {
-   /**
-   * Has a prompt occurred?
-   *
-   * &lt;p&gt;
-   * This function never throws an exception
-   * &lt;/p&gt;
-   * @return boolean true if there is a pending prompt
-   */
-    return this.browserbot.hasPrompts();
-};
-
-Selenium.prototype.isConfirmationPresent = function() {
-   /**
-   * Has confirm() been called?
-   *
-   * &lt;p&gt;
-   * This function never throws an exception
-   * &lt;/p&gt;
-   * @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.
-   *
-   * &lt;p&gt;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.&lt;/p&gt;
-   *
-   * &lt;p&gt;NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert
-   * dialog.&lt;/p&gt;
-   *
-   * &lt;p&gt;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.&lt;/p&gt;
-   * @return string The message of the most recent JavaScript alert
-   */
-    if (!this.browserbot.hasAlerts()) {
-        Assert.fail(&quot;There were no alerts&quot;);
-    }
-    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.
-   *
-   * &lt;p&gt;
-   * 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.
-   * &lt;/p&gt;
-   *
-   * &lt;p&gt;
-   * NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible
-   * dialog.
-   * &lt;/p&gt;
-   *
-   * &lt;p&gt;
-   * 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.
-   * &lt;/p&gt;
-   *
-   * @return string the message of the most recent JavaScript confirmation dialog
-   */
-    if (!this.browserbot.hasConfirmations()) {
-        Assert.fail(&quot;There were no confirmations&quot;);
-    }
-    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.
-   *
-   * &lt;p&gt;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.&lt;/p&gt;
-   *
-   * &lt;p&gt;NOTE: under Selenium, JavaScript prompts will NOT pop up a visible
-   * dialog.&lt;/p&gt;
-   *
-   * &lt;p&gt;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.&lt;/p&gt;
-   * @return string the message of the most recent JavaScript question prompt
-   */
-    if (! this.browserbot.hasPrompts()) {
-        Assert.fail(&quot;There were no prompts&quot;);
-    }
-    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 &quot;on&quot; or &quot;off&quot; depending on
-   * whether the element is checked or not.
-   *
-   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @return string the element value, or &quot;on/off&quot; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-    */
-    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.
-   *
-   * &lt;p&gt;Note that, by default, the snippet will run in the context of the &quot;selenium&quot;
-   * object itself, so &lt;code&gt;this&lt;/code&gt; will refer to the Selenium object.  Use &lt;code&gt;window&lt;/code&gt; to
-   * refer to the window of your application, e.g. &lt;code&gt;window.document.getElementById('foo')&lt;/code&gt;&lt;/p&gt;
-   *
-   * &lt;p&gt;If you need to use
-   * a locator to refer to a single element in your application page, you can
-   * use &lt;code&gt;this.browserbot.findElement(&quot;id=foo&quot;)&lt;/code&gt; where &quot;id=foo&quot; is your locator.&lt;/p&gt;
-   *
-   * @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 &quot;null&quot;;
-        return result;
-    } catch (e) {
-        throw new SeleniumError(&quot;Threw an exception: &quot; + 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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(&quot;Element &quot; + locator + &quot; is not a toggle-button.&quot;);
-    }
-    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. &quot;foo.1.4&quot;
-   * @return string the text from the specified cell
-   */
-    // This regular expression matches &quot;tableName.row.column&quot;
-    // For example, &quot;mytable.3.4&quot;
-    pattern = /(.*)\.(\d+)\.(\d+)/;
-
-    if(!pattern.test(tableCellAddress)) {
-        throw new SeleniumError(&quot;Invalid target format. Correct format is tableName.rowNum.columnNum&quot;);
-    }
-
-    pieces = tableCellAddress.match(pattern);
-
-    tableName = pieces[1];
-    row = pieces[2];
-    col = pieces[3];
-
-    var table = this.browserbot.findElement(tableName);
-    if (row &gt; table.rows.length) {
-        Assert.fail(&quot;Cannot access row &quot; + row + &quot; - table has &quot; + table.rows.length + &quot; rows&quot;);
-    }
-    else if (col &gt; table.rows[row].cells.length) {
-        Assert.fail(&quot;Cannot access column &quot; + col + &quot; - table row has &quot; + table.rows[row].cells.length + &quot; columns&quot;);
-    }
-    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-   * @return string[] an array of all selected option labels in the specified select drop-down
-   */
-    return this.findSelectedOptionProperties(selectLocator, &quot;text&quot;);
-}
-
-Selenium.prototype.getSelectedLabel = function(selectLocator) {
-    /** Gets option label (visible text) for selected option in the specified select element.
-   *
-   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-   * @return string the selected option label in the specified select drop-down
-   */
-    return this.findSelectedOptionProperty(selectLocator, &quot;text&quot;);
-}
-
-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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-   * @return string[] an array of all selected option values in the specified select drop-down
-   */
-    return this.findSelectedOptionProperties(selectLocator, &quot;value&quot;);
-}
-
-Selenium.prototype.getSelectedValue = function(selectLocator) {
-    /** Gets option value (value attribute) for selected option in the specified select element.
-   *
-   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-   * @return string the selected option value in the specified select drop-down
-   */
-    return this.findSelectedOptionProperty(selectLocator, &quot;value&quot;);
-}
-
-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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-   * @return string[] an array of all selected option indexes in the specified select drop-down
-   */
-    return this.findSelectedOptionProperties(selectLocator, &quot;index&quot;);
-}
-
-Selenium.prototype.getSelectedIndex = function(selectLocator) {
-    /** Gets option index (option number, starting at 0) for selected option in the specified select element.
-   *
-   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-   * @return string the selected option index in the specified select drop-down
-   */
-    return this.findSelectedOptionProperty(selectLocator, &quot;index&quot;);
-}
-
-Selenium.prototype.getSelectedIds = function(selectLocator) {
-    /** Gets all option element IDs for selected options in the specified select or multi-select element.
-   *
-   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-   * @return string[] an array of all selected option IDs in the specified select drop-down
-   */
-    return this.findSelectedOptionProperties(selectLocator, &quot;id&quot;);
-}
-
-Selenium.prototype.getSelectedId = function(selectLocator) {
-    /** Gets option element ID for selected option in the specified select element.
-   *
-   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-   * @return string the selected option ID in the specified select drop-down
-   */
-    return this.findSelectedOptionProperty(selectLocator, &quot;id&quot;);
-}
-
-Selenium.prototype.isSomethingSelected = function(selectLocator) {
-    /** Determines whether some option in a drop-down menu is selected.
-   *
-   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-   * @return boolean true if some option has been selected, false otherwise
-   */
-    var element = this.browserbot.findElement(selectLocator);
-    if (!(&quot;options&quot; in element)) {
-        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
-    }
-
-    var selectedOptions = [];
-
-    for (var i = 0; i &lt; 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 (!(&quot;options&quot; in element)) {
-        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
-    }
-
-    var selectedOptions = [];
-
-    for (var i = 0; i &lt; element.options.length; i++) {
-        if (element.options[i].selected)
-        {
-            var propVal = element.options[i][property];
-            selectedOptions.push(propVal);
-        }
-    }
-    if (selectedOptions.length == 0) Assert.fail(&quot;No option selected&quot;);
-    return selectedOptions;
-}
-
-Selenium.prototype.findSelectedOptionProperty = function(locator, property) {
-    var selectedOptions = this.findSelectedOptionProperties(locator, property);
-    if (selectedOptions.length &gt; 1) {
-        Assert.fail(&quot;More than one selected option!&quot;);
-    }
-    return selectedOptions[0];
-}
-
-Selenium.prototype.getSelectOptions = function(selectLocator) {
-    /** Gets all option labels in the specified select drop-down.
-   *
-   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &lt; 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 &amp;#064; sign and then the name of the attribute, e.g. &quot;foo&amp;#064;bar&quot;
-   * @return string the value of the specified attribute
-   */
-   var result = this.browserbot.findAttribute(attributeLocator);
-   if (result == null) {
-           throw new SeleniumError(&quot;Could not find element attribute: &quot; + 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 &lt;a href=&quot;#patterns&quot;&gt;pattern&lt;/a&gt; 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(&quot;glob:&quot;)==0) {
-                    pattern = pattern.substring(&quot;glob:&quot;.length); // strip off &quot;glob:&quot;
-                }
-        patternMatcher.matcher = new PatternMatcher.strategies.globContains(pattern);
-    }
-    else if (patternMatcher.strategy == PatternMatcher.strategies.exact) {
-                pattern = pattern.substring(&quot;exact:&quot;.length); // strip off &quot;exact:&quot;
-        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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-    * @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 &quot;visibility&quot;
-   * property to &quot;hidden&quot;, or the &quot;display&quot; property to &quot;none&quot;, either for the
-   * element itself or one if its ancestors.  This method will fail if
-   * the element is not present.
-   *
-   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @return boolean true if the specified element is visible, false otherwise
-   */
-    var element;
-    element = this.browserbot.findElement(locator);
-    var visibility = this.findEffectiveStyleProperty(element, &quot;visibility&quot;);
-    var _isDisplayed = this._isDisplayed(element);
-    return (visibility != &quot;hidden&quot; &amp;&amp; _isDisplayed);
-};
-
-Selenium.prototype.findEffectiveStyleProperty = function(element, property) {
-    var effectiveStyle = this.findEffectiveStyle(element);
-    var propertyValue = effectiveStyle[property];
-    if (propertyValue == 'inherit' &amp;&amp; element.parentNode.style) {
-        return this.findEffectiveStyleProperty(element.parentNode, property);
-    }
-    return propertyValue;
-};
-
-Selenium.prototype._isDisplayed = function(element) {
-    var display = this.findEffectiveStyleProperty(element, &quot;display&quot;);
-    if (display == &quot;none&quot;) 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 &quot;visibility&quot;
-    }
-    throw new SeleniumError(&quot;cannot determine effective stylesheet in this browser&quot;);
-};
-
-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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
-   * @return boolean true if the input element is editable, false otherwise
-   */
-    var element = this.browserbot.findElement(locator);
-    if (element.value == undefined) {
-        Assert.fail(&quot;Element &quot; + locator + &quot; is not an input.&quot;);
-    }
-    return !element.disabled;
-};
-
-Selenium.prototype.getAllButtons = function() {
-    /** Returns the IDs of all buttons on the page.
-   *
-   * &lt;p&gt;If a given button has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;
-   *
-   * @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.
-   *
-   * &lt;p&gt;If a given link has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;
-   *
-   * @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.
-   *
-   * &lt;p&gt;If a given field has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;
-   *
-   * @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 &quot;win.&quot;+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(&quot;win.&quot;+attributeName));
-    for (var windowName in this.browserbot.openedWindows)
-    {
-        try {
-            win = selenium.browserbot.openedWindows[windowName];
-            attributes.push(eval(&quot;win.&quot;+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 = &quot;name&quot;;
-   if (soughtAfterWindowPropertyValue.match(&quot;^title=&quot;)) {
-       targetPropertyName = &quot;document.title&quot;;
-        soughtAfterWindowPropertyValue = soughtAfterWindowPropertyValue.replace(/^title=/, &quot;&quot;);
-   }
-   else {
-       // matching &quot;name&quot;:
-       // 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, &quot;&quot;)) {
-           return this.browserbot.getCurrentWindow();
-        }
-   }
-
-   // DGF normally you should use []s instead of eval &quot;win.&quot;+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(&quot;this.browserbot.topWindow.&quot; + targetPropertyName))) {
-       return this.browserbot.topWindow;
-   }
-   for (windowName in selenium.browserbot.openedWindows) {
-       var openedWindow = selenium.browserbot.openedWindows[windowName];
-       if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval(&quot;openedWindow.&quot; + targetPropertyName))) {
-            return openedWindow;
-        }
-   }
-   throw new SeleniumError(&quot;could not find window with property &quot; + targetPropertyName + &quot; matching &quot; + 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., &quot;+70,-300&quot;
-   */
-   this.doDragAndDrop(locator, movementsString);
-};
-
-Selenium.prototype.doSetMouseSpeed = function(pixels) {
-    /** Configure the number of pixels between &quot;mousemove&quot; events during dragAndDrop commands (default=10).
-    * &lt;p&gt;Setting this value to 0 means that we'll send a &quot;mousemove&quot; 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.&lt;/p&gt;
-    * 
-    * &lt;p&gt;If the mouse speed is greater than the distance between the two dragged objects, we'll
-    * just send one &quot;mousemove&quot; at the start location and then one final one at the end location.&lt;/p&gt;
-    * @param pixels the number of pixels between &quot;mousemove&quot; events
-    */
-    this.mouseSpeed = pixels;
-}
- 
-Selenium.prototype.getMouseSpeed = function() {
-    /** Returns the number of pixels between &quot;mousemove&quot; events during dragAndDrop commands (default=10).
-    * 
-    * @return number the number of pixels between &quot;mousemove&quot; 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., &quot;+70,-300&quot;
-    */
-    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) &lt; 0) ? 0 : (clientStartX + movementX);
-    var clientFinishY = ((clientStartY + movementY) &lt; 0) ? 0 : (clientStartY + movementY);
-    
-    var mouseSpeed = this.mouseSpeed;
-    var move = function(current, dest) {
-        if (current == dest) return current;
-        if (Math.abs(current - dest) &lt; mouseSpeed) return dest;
-        return (current &lt; 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 = &quot;&quot; + deltaX + &quot;,&quot; + 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 &amp;&amp; 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(&quot;id&quot;);
-};
-
-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(&quot;name&quot;);
-};
-
-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(&quot;document.title&quot;);
-};
-
-Selenium.prototype.getHtmlSource = function() {
-    /** Returns the entire HTML source between the opening and
-   * closing &quot;html&quot; tags.
-   *
-   * @return string the entire HTML source
-   */
-    return this.browserbot.getDocument().getElementsByTagName(&quot;html&quot;)[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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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(&quot;Element &quot; + locator + &quot; is not an input.&quot;);
-    }
-    if (position == -1) {
-        position = element.value.length;
-    }
-
-   if( element.setSelectionRange &amp;&amp; !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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to the first element
-     * @param locator2 an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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) &amp;&amp; !(/[^\t\n\r ]/.test(node.data)));
-}
-
-Selenium.prototype.getElementPositionLeft = function(locator) {
-   /**
-   * Retrieves the horizontal position of an element
-   *
-   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element OR an element itself
-   * @return number of pixels from the edge of the frame.
-   */
-       var element;
-        if (&quot;string&quot;==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 != &quot;TABLE&quot;) &amp;&amp; (elementParent.tagName != &quot;BODY&quot;) )
-            {
-                x += elementParent.clientLeft;
-            }
-        }
-        else // Netscape/DOM
-        {
-            if(elementParent.tagName == &quot;TABLE&quot;)
-            {
-                var parentBorder = parseInt(elementParent.border);
-                if(isNaN(parentBorder))
-                {
-                    var parentFrame = elementParent.getAttribute('frame');
-                    if(parentFrame != null)
-                    {
-                        x += 1;
-                    }
-                }
-                else if(parentBorder &gt; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element OR an element itself
-   * @return number of pixels from the edge of the frame.
-   */
-       var element;
-        if (&quot;string&quot;==typeof locator) {
-            element = this.browserbot.findElement(locator);
-        }
-        else {
-            element = locator;
-        }
-
-       var y = 0;
-
-       while (element != null)
-    {
-        if(document.all)
-        {
-            if( (element.tagName != &quot;TABLE&quot;) &amp;&amp; (element.tagName != &quot;BODY&quot;) )
-            {
-            y += element.clientTop;
-            }
-        }
-        else // Netscape/DOM
-        {
-            if(element.tagName == &quot;TABLE&quot;)
-            {
-            var parentBorder = parseInt(element.border);
-            if(isNaN(parentBorder))
-            {
-                var parentFrame = element.getAttribute('frame');
-                if(parentFrame != null)
-                {
-                    y += 1;
-                }
-            }
-            else if(parentBorder &gt; 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 &amp;&amp; element.offsetParent.offsetHeight &amp;&amp; element.offsetParent.offsetHeight &lt; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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.
-   *
-   * &lt;p&gt;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 &lt;a href=&quot;http://jira.openqa.org/browse/SEL-243&quot;&gt;SEL-243&lt;/a&gt;.&lt;/p&gt;
-   * 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &amp;&amp; !browserVersion.isOpera){
-        try {
-            var selectRange = doc.selection.createRange().duplicate();
-            var elementRange = element.createTextRange();
-            selectRange.move(&quot;character&quot;,0);
-            elementRange.move(&quot;character&quot;,0);
-            var inRange1 = selectRange.inRange(elementRange);
-            var inRange2 = elementRange.inRange(selectRange);
-            elementRange.setEndPoint(&quot;EndToEnd&quot;, selectRange);
-        } catch (e) {
-            Assert.fail(&quot;There is no cursor on this page!&quot;);
-        }
-        var answer = String(elementRange.text).replace(/\r/g,&quot;&quot;).length;
-        return answer;
-    } else {
-        if (typeof(element.selectionStart) != &quot;undefined&quot;) {
-            if (win.getSelection &amp;&amp; typeof(win.getSelection().rangeCount) != undefined &amp;&amp; win.getSelection().rangeCount == 0) {
-                Assert.fail(&quot;There is no cursor on this page!&quot;);
-            }
-            return element.selectionStart;
-        }
-    }
-    throw new Error(&quot;Couldn't detect cursor position on this browser!&quot;);
-}
-
-
-Selenium.prototype.getExpression = function(expression) {
-    /**
-     * Returns the specified expression.
-     *
-     * &lt;p&gt;This is useful because of JavaScript preprocessing.
-     * It is used to generate commands like assertExpression and waitForExpression.&lt;/p&gt;
-     *
-     * @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. &quot;//table&quot; 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 &quot;id&quot; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &quot;false&quot; 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 (&quot;false&quot; == allow || &quot;0&quot; == allow) { // The strings &quot;false&quot; and &quot;0&quot; 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 &quot;true&quot;.
-   * The snippet may have multiple lines, but only the result of the last line
-   * will be considered.
-   *
-   * &lt;p&gt;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 &lt;code&gt;selenium.browserbot.getCurrentWindow()&lt;/code&gt;, and then
-   * run your JavaScript in there&lt;/p&gt;
-   * @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.
-     *
-     * &lt;p&gt;Actions that require waiting include &quot;open&quot; and the &quot;waitFor*&quot; actions.&lt;/p&gt;
-     * 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.
-     *
-     * &lt;p&gt;You can use this command instead of the &quot;AndWait&quot; suffixes, &quot;clickAndWait&quot;, &quot;selectAndWait&quot;, &quot;typeAndWait&quot; etc.
-     * (which are only available in the JS API).&lt;/p&gt;
-     *
-     * &lt;p&gt;Selenium constantly keeps track of new pages loading, and sets a &quot;newPageLoaded&quot;
-     * 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.&lt;/p&gt;
-     * @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[&quot;proxyInjectionMode&quot;] == null || !window[&quot;proxyInjectionMode&quot;]) {
-        return this.makePageLoadCondition(timeout);
-    }
-};
-
-Selenium.prototype.doWaitForFrameToLoad = function(frameAddress, timeout) {
-    /**
-     * Waits for a new frame to load.
-     *
-     * &lt;p&gt;Selenium constantly keeps track of new pages and frames loading, 
-     * and sets a &quot;newPageLoaded&quot; flag when it first notices a page load.&lt;/p&gt;
-     * 
-     * 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[&quot;proxyInjectionMode&quot;] == null || !window[&quot;proxyInjectionMode&quot;]) {
-        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 &quot;javascript{ ... }&quot;, evaluate the string between the braces.
- */
-Selenium.prototype.preprocessParameter = function(value) {
-    var match = value.match(/^javascript\{((.|\r?\n)+)\}$/);
-    if (match &amp;&amp; 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 &amp;&amp; i &lt; 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 &quot;name=value&quot;
-     * @param optionsString options for the cookie. Currently supported options include 'path' and 'max_age'.
-     *      the optionsString's format is &quot;path=/path/, max_age=60&quot;. The order of options are irrelevant, the unit
-     *      of the value of 'max_age' is second.
-     */
-    var results = /[^\s=\[\]\(\),&quot;\/\?@:;]+=[^\s=\[\]\(\),&quot;\/\?@:;]*/.test(nameValuePair);
-    if (!results) {
-        throw new SeleniumError(&quot;Invalid parameter.&quot;);
-    }
-    var cookie = nameValuePair.trim();
-    results = /max_age=(\d+)/.exec(optionsString);
-    if (results) {
-        var expireDateInMilliseconds = (new Date()).getTime() + results[1] * 1000;
-        cookie += &quot;; expires=&quot; + 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 (&quot;/&quot; != path) {
-                path = path.replace(/\/$/, &quot;&quot;);
-            }
-        }
-        cookie += &quot;; path=&quot; + path;
-    }
-    LOG.debug(&quot;Setting cookie to: &quot; + 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 (&quot;/&quot; != path) {
-            path = path.replace(/\/$/, &quot;&quot;);
-        }
-    }
-    var expireDateInMilliseconds = (new Date()).getTime() + (-1 * 1000);
-    var cookie = name.trim() + &quot;=deleted; path=&quot; + path + &quot;; expires=&quot; + new Date(expireDateInMilliseconds).toGMTString();
-    LOG.debug(&quot;Setting cookie to: &quot; + 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: &quot;debug&quot;, &quot;info&quot;, &quot;warn&quot;, &quot;error&quot; or &quot;off&quot;.
-    * 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: &quot;debug&quot;, &quot;info&quot;, &quot;warn&quot;, &quot;error&quot; or &quot;off&quot;
-    */
-    if (logLevel == null || logLevel == &quot;&quot;) {
-        throw new SeleniumError(&quot;You must specify a log level&quot;);
-    }
-    logLevel = logLevel.toLowerCase();
-    if (LOG.logLevels[logLevel] == null) {
-        throw new SeleniumError(&quot;Invalid log level: &quot; + logLevel);
-    }
-    LOG.setLogLevelThreshold(logLevel);
-}
-
-Selenium.prototype.doRunScript = function(script) {
-    /**
-    * Creates a new &quot;script&quot; 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 &quot;getEval&quot; 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(&quot;script&quot;);
-    scriptTag.type = &quot;text/javascript&quot;
-    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 &quot;foo&quot;, and someone runs click(&quot;foo=blah&quot;), we'll
-    * run your function, passing you the string &quot;blah&quot;, and click on the element 
-    * that your function
-    * returns, or throw an &quot;Element not found&quot; error if your function returns null.
-    *
-    * We'll pass three arguments to your function:
-    * &lt;ul&gt;
-    * &lt;li&gt;locator: the string the user passed in&lt;/li&gt;
-    * &lt;li&gt;inWindow: the currently selected window&lt;/li&gt;
-    * &lt;li&gt;inDocument: the currently selected document&lt;/li&gt;
-    * &lt;/ul&gt;
-    * 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: &lt;code&gt;return inDocument.getElementById(locator);&lt;/code&gt;
-    */
-    if (!/^[a-zA-Z]+$/.test(strategyName)) {
-        throw new SeleniumError(&quot;Invalid strategy name: &quot; + strategyName);
-    }
-    var strategyFunction;
-    try {
-        strategyFunction = new Function(&quot;locator&quot;, &quot;inDocument&quot;, &quot;inWindow&quot;, functionDefinition);
-    } catch (ex) {
-        throw new SeleniumError(&quot;Error evaluating function definition: &quot; + extractExceptionMessage(ex));
-    }
-    var safeStrategyFunction = function() {
-        try {
-            return strategyFunction.apply(this, arguments);
-        } catch (ex) {
-            throw new SeleniumError(&quot;Error executing strategy function &quot; + strategyName + &quot;: &quot; + extractExceptionMessage(ex));
-        }
-    }
-    this.browserbot.locationStrategies[strategyName] = safeStrategyFunction;
-}
-
-/**
- *  Factory for creating &quot;Option Locators&quot;.
- *  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=&lt;exp&gt;  (OptionLocatorByLabel)
- *     value=&lt;exp&gt;  (OptionLocatorByValue)
- *     index=&lt;exp&gt;  (OptionLocatorByIndex)
- *     id=&lt;exp&gt;     (OptionLocatorById)
- *     &lt;exp&gt; (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(&quot;Unkown option locator type: &quot; + locatorType);
-};
-
-/**
- * To allow for easy extension, all of the option locators are found by
- * searching for all methods of OptionLocatorFactory.prototype that start
- * with &quot;OptionLocatorBy&quot;.
- * TODO: Consider using the term &quot;Option Specifier&quot; instead of &quot;Option Locator&quot;.
- */
-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 &lt; element.options.length; i++) {
-            if (this.labelMatcher.matches(element.options[i].text)) {
-                return element.options[i];
-            }
-        }
-        throw new SeleniumError(&quot;Option with label '&quot; + this.label + &quot;' not found&quot;);
-    };
-
-    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 &lt; element.options.length; i++) {
-            if (this.valueMatcher.matches(element.options[i].value)) {
-                return element.options[i];
-            }
-        }
-        throw new SeleniumError(&quot;Option with value '&quot; + this.value + &quot;' not found&quot;);
-    };
-
-    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 &lt; 0) {
-        throw new SeleniumError(&quot;Illegal Index: &quot; + index);
-    }
-
-    this.findOption = function(element) {
-        if (element.options.length &lt;= this.index) {
-            throw new SeleniumError(&quot;Index out of range.  Only &quot; + element.options.length + &quot; options available&quot;);
-        }
-        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 &lt; element.options.length; i++) {
-            if (this.idMatcher.matches(element.options[i].id)) {
-                return element.options[i];
-            }
-        }
-        throw new SeleniumError(&quot;Option with id '&quot; + this.id + &quot;' not found&quot;);
-    };
-
-    this.assertSelected = function(element) {
-        var selectedId = element.options[element.selectedIndex].id;
-        Assert.matches(this.id, selectedId)
-    };
-};
+/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+ *  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 &quot;AS IS&quot; 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.
+     *
+     * &lt;h3&gt;&lt;a name=&quot;locators&quot;&gt;&lt;/a&gt;Element Locators&lt;/h3&gt;
+     * &lt;p&gt;
+     * Element Locators tell Selenium which HTML element a command refers to.
+     * The format of a locator is:&lt;/p&gt;
+     * &lt;blockquote&gt;
+     * &lt;em&gt;locatorType&lt;/em&gt;&lt;strong&gt;=&lt;/strong&gt;&lt;em&gt;argument&lt;/em&gt;
+     * &lt;/blockquote&gt;
+     *
+     * &lt;p&gt;
+     * We support the following strategies for locating elements:
+     * &lt;/p&gt;
+     * 
+     * &lt;ul&gt;
+     * &lt;li&gt;&lt;strong&gt;identifier&lt;/strong&gt;=&lt;em&gt;id&lt;/em&gt;: 
+     * Select the element with the specified &amp;#064;id attribute. If no match is
+     * found, select the first element whose &amp;#064;name attribute is &lt;em&gt;id&lt;/em&gt;.
+     * (This is normally the default; see below.)&lt;/li&gt;
+     * &lt;li&gt;&lt;strong&gt;id&lt;/strong&gt;=&lt;em&gt;id&lt;/em&gt;:
+     * Select the element with the specified &amp;#064;id attribute.&lt;/li&gt;
+     *
+     * &lt;li&gt;&lt;strong&gt;name&lt;/strong&gt;=&lt;em&gt;name&lt;/em&gt;:
+     * Select the first element with the specified &amp;#064;name attribute.
+     * &lt;ul class=&quot;first last simple&quot;&gt;
+     * &lt;li&gt;username&lt;/li&gt;
+     * &lt;li&gt;name=username&lt;/li&gt;
+     * &lt;/ul&gt;
+     * 
+     * &lt;p&gt;The name may optionally be followed by one or more &lt;em&gt;element-filters&lt;/em&gt;, separated from the name by whitespace.  If the &lt;em&gt;filterType&lt;/em&gt; is not specified, &lt;strong&gt;value&lt;/strong&gt; is assumed.&lt;/p&gt;
+     *
+     * &lt;ul class=&quot;first last simple&quot;&gt;
+     * &lt;li&gt;name=flavour value=chocolate&lt;/li&gt;
+     * &lt;/ul&gt;
+     * &lt;/li&gt;
+     * &lt;li&gt;&lt;strong&gt;dom&lt;/strong&gt;=&lt;em&gt;javascriptExpression&lt;/em&gt;: 
+     *
+     * 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.
+     * &lt;ul class=&quot;first last simple&quot;&gt;
+     * &lt;li&gt;dom=document.forms['myForm'].myDropdown&lt;/li&gt;
+     * &lt;li&gt;dom=document.images[56]&lt;/li&gt;
+     * &lt;li&gt;dom=function foo() { return document.links[1]; }; foo();&lt;/li&gt;
+     * &lt;/ul&gt;
+     *
+     * &lt;/li&gt;
+     *
+     * &lt;li&gt;&lt;strong&gt;xpath&lt;/strong&gt;=&lt;em&gt;xpathExpression&lt;/em&gt;: 
+     * Locate an element using an XPath expression.
+     * &lt;ul class=&quot;first last simple&quot;&gt;
+     * &lt;li&gt;xpath=//img[&amp;#064;alt='The image alt text']&lt;/li&gt;
+     * &lt;li&gt;xpath=//table[&amp;#064;id='table1']//tr[4]/td[2]&lt;/li&gt;
+     * &lt;li&gt;xpath=//a[contains(&amp;#064;href,'#id1')]&lt;/li&gt;
+     * &lt;li&gt;xpath=//a[contains(&amp;#064;href,'#id1')]/&amp;#064;class&lt;/li&gt;
+     * &lt;li&gt;xpath=(//table[&amp;#064;class='stylee'])//th[text()='theHeaderText']/../td&lt;/li&gt;
+     * &lt;li&gt;xpath=//input[&amp;#064;name='name2' and &amp;#064;value='yes']&lt;/li&gt;
+     * &lt;li&gt;xpath=//*[text()=&quot;right&quot;]&lt;/li&gt;
+     *
+     * &lt;/ul&gt;
+     * &lt;/li&gt;
+     * &lt;li&gt;&lt;strong&gt;link&lt;/strong&gt;=&lt;em&gt;textPattern&lt;/em&gt;:
+     * Select the link (anchor) element which contains text matching the
+     * specified &lt;em&gt;pattern&lt;/em&gt;.
+     * &lt;ul class=&quot;first last simple&quot;&gt;
+     * &lt;li&gt;link=The link text&lt;/li&gt;
+     * &lt;/ul&gt;
+     *
+     * &lt;/li&gt;
+     *
+     * &lt;li&gt;&lt;strong&gt;css&lt;/strong&gt;=&lt;em&gt;cssSelectorSyntax&lt;/em&gt;:
+     * Select the element using css selectors. Please refer to &lt;a href=&quot;http://www.w3.org/TR/REC-CSS2/selector.html&quot;&gt;CSS2 selectors&lt;/a&gt;, &lt;a href=&quot;http://www.w3.org/TR/2001/CR-css3-selectors-20011113/&quot;&gt;CSS3 selectors&lt;/a&gt; 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.
+     * &lt;ul class=&quot;first last simple&quot;&gt;
+     * &lt;li&gt;css=a[href=&quot;#id3&quot;]&lt;/li&gt;
+     * &lt;li&gt;css=span#firstChild + span&lt;/li&gt;
+     * &lt;/ul&gt;
+     * &lt;p&gt;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). &lt;/p&gt;
+     * &lt;/li&gt;
+     * &lt;/ul&gt;
+     * 
+     * &lt;p&gt;
+     * Without an explicit locator prefix, Selenium uses the following default
+     * strategies:
+     * &lt;/p&gt;
+     *
+     * &lt;ul class=&quot;simple&quot;&gt;
+     * &lt;li&gt;&lt;strong&gt;dom&lt;/strong&gt;, for locators starting with &amp;quot;document.&amp;quot;&lt;/li&gt;
+     * &lt;li&gt;&lt;strong&gt;xpath&lt;/strong&gt;, for locators starting with &amp;quot;//&amp;quot;&lt;/li&gt;
+     * &lt;li&gt;&lt;strong&gt;identifier&lt;/strong&gt;, otherwise&lt;/li&gt;
+     * &lt;/ul&gt;
+     *
+     * &lt;h3&gt;&lt;a name=&quot;element-filters&quot;&gt;Element Filters&lt;/a&gt;&lt;/h3&gt;
+     * &lt;blockquote&gt;
+     * &lt;p&gt;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.&lt;/p&gt;
+     * &lt;p&gt;Filters look much like locators, ie.&lt;/p&gt;
+     * &lt;blockquote&gt;
+     * &lt;em&gt;filterType&lt;/em&gt;&lt;strong&gt;=&lt;/strong&gt;&lt;em&gt;argument&lt;/em&gt;&lt;/blockquote&gt;
+     *
+     * &lt;p&gt;Supported element-filters are:&lt;/p&gt;
+     * &lt;p&gt;&lt;strong&gt;value=&lt;/strong&gt;&lt;em&gt;valuePattern&lt;/em&gt;&lt;/p&gt;
+     * &lt;blockquote&gt;
+     * Matches elements based on their values.  This is particularly useful for refining a list of similarly-named toggle-buttons.&lt;/blockquote&gt;
+     * &lt;p&gt;&lt;strong&gt;index=&lt;/strong&gt;&lt;em&gt;index&lt;/em&gt;&lt;/p&gt;
+     * &lt;blockquote&gt;
+     * Selects a single element based on its position in the list (offset from zero).&lt;/blockquote&gt;
+     * &lt;/blockquote&gt;
+     *
+     * &lt;h3&gt;&lt;a name=&quot;patterns&quot;&gt;&lt;/a&gt;String-match Patterns&lt;/h3&gt;
+     *
+     * &lt;p&gt;
+     * Various Pattern syntaxes are available for matching string values:
+     * &lt;/p&gt;
+     * &lt;ul&gt;
+     * &lt;li&gt;&lt;strong&gt;glob:&lt;/strong&gt;&lt;em&gt;pattern&lt;/em&gt;:
+     * Match a string against a &quot;glob&quot; (aka &quot;wildmat&quot;) pattern. &quot;Glob&quot; is a
+     * kind of limited regular-expression syntax typically used in command-line
+     * shells. In a glob pattern, &quot;*&quot; represents any sequence of characters, and &quot;?&quot;
+     * represents any single character. Glob patterns match against the entire
+     * string.&lt;/li&gt;
+     * &lt;li&gt;&lt;strong&gt;regexp:&lt;/strong&gt;&lt;em&gt;regexp&lt;/em&gt;:
+     * Match a string using a regular-expression. The full power of JavaScript
+     * regular-expressions is available.&lt;/li&gt;
+     * &lt;li&gt;&lt;strong&gt;exact:&lt;/strong&gt;&lt;em&gt;string&lt;/em&gt;:
+     *
+     * Match a string exactly, verbatim, without any of that fancy wildcard
+     * stuff.&lt;/li&gt;
+     * &lt;/ul&gt;
+     * &lt;p&gt;
+     * If no pattern prefix is specified, Selenium assumes that it's a &quot;glob&quot;
+     * pattern.
+     * &lt;/p&gt;
+     */
+    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(&quot;Timeout is not a number: '&quot; + timeout + &quot;'&quot;);
+    }
+    var now = new Date().getTime();
+    var timeoutTime = now + timeoutValue;
+    return function() {
+        if (new Date().getTime() &gt; timeoutTime) {
+            throw new SeleniumError(&quot;Timed out after &quot; + timeoutValue + &quot;ms&quot;);
+        }
+        return f();
+    };
+}
+
+Selenium.createForWindow = function(window, proxyInjectionMode) {
+    if (!window.location) {
+        throw &quot;error: not a window!&quot;;
+    }
+    return new Selenium(BrowserBot.createForWindow(window, proxyInjectionMode));
+};
+
+Selenium.prototype.reset = function() {
+    this.defaultTimeout = Selenium.DEFAULT_TIMEOUT;
+    // todo: this.browserbot.reset()
+    this.browserbot.selectWindow(&quot;null&quot;);
+    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 &amp;quot;on&lt;em&gt;event&lt;/em&gt;&amp;quot;
+   * handler.
+   *
+   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @param eventName the event name, e.g. &quot;focus&quot; or &quot;blur&quot;
+   */
+    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @param keySequence Either be a string(&quot;\&quot; followed by the numeric keycode
+   *  of the key to be pressed, normally the ASCII value of that key), or a single
+   *  character. For example: &quot;w&quot;, &quot;\119&quot;.
+   */
+    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @param keySequence Either be a string(&quot;\&quot; followed by the numeric keycode
+   *  of the key to be pressed, normally the ASCII value of that key), or a single
+   *  character. For example: &quot;w&quot;, &quot;\119&quot;.
+   */
+    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @param keySequence Either be a string(&quot;\&quot; followed by the numeric keycode
+   *  of the key to be pressed, normally the ASCII value of that key), or a single
+   *  character. For example: &quot;w&quot;, &quot;\119&quot;.
+   */
+    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   */
+    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   */
+    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   */
+   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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   */
+   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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   */
+   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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @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.
+   *
+   * &lt;p&gt;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.&lt;/p&gt;
+   *
+   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @param value the value to type
+   */
+   if (this.browserbot.controlKeyDown || this.browserbot.altKeyDown || this.browserbot.metaKeyDown) {
+        throw new SeleniumError(&quot;type not supported immediately after call to controlKeyDown() or altKeyDown() or metaKeyDown()&quot;);
+    }
+        // 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.
+    *
+    * &lt;p&gt;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.&lt;/p&gt;
+    * 
+    * &lt;p&gt;Unlike the simple &quot;type&quot; 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 &quot;typeKeys&quot; on a form element, you may or may not see the results of what you typed in
+    * the field.&lt;/p&gt;
+    * &lt;p&gt;In some cases, you may need to use the simple &quot;type&quot; command to set the value of the field and then the &quot;typeKeys&quot; command to
+    * send the keystroke events corresponding to what you just typed.&lt;/p&gt;
+    *
+    * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+    * @param value the value to type
+    */
+    var keys = new String(value).split(&quot;&quot;);
+    for (var i = 0; i &lt; 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(&quot;this operation is only implemented in selenium-rc, and should never result in a request making it across the wire&quot;);
+};
+
+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(&quot;this operation is only implemented in selenium-rc, and should never result in a request making it across the wire&quot;);
+};
+
+Selenium.prototype.findToggleButton = function(locator) {
+    var element = this.browserbot.findElement(locator);
+    if (element.checked == null) {
+        Assert.fail(&quot;Element &quot; + locator + &quot; is not a toggle-button.&quot;);
+    }
+    return element;
+}
+
+Selenium.prototype.doCheck = function(locator) {
+    /**
+   * Check a toggle-button (checkbox/radio)
+   *
+   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   */
+    this.findToggleButton(locator).checked = true;
+};
+
+Selenium.prototype.doUncheck = function(locator) {
+    /**
+   * Uncheck a toggle-button (checkbox/radio)
+   *
+   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   */
+    this.findToggleButton(locator).checked = false;
+};
+
+Selenium.prototype.doSelect = function(selectLocator, optionLocator) {
+    /**
+   * Select an option from a drop-down using an option locator.
+   *
+   * &lt;p&gt;
+   * 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.
+   * &lt;/p&gt;
+   * &lt;ul&gt;
+   * &lt;li&gt;&lt;strong&gt;label&lt;/strong&gt;=&lt;em&gt;labelPattern&lt;/em&gt;:
+   * matches options based on their labels, i.e. the visible text. (This
+   * is the default.)
+   * &lt;ul class=&quot;first last simple&quot;&gt;
+   * &lt;li&gt;label=regexp:^[Oo]ther&lt;/li&gt;
+   * &lt;/ul&gt;
+   * &lt;/li&gt;
+   * &lt;li&gt;&lt;strong&gt;value&lt;/strong&gt;=&lt;em&gt;valuePattern&lt;/em&gt;:
+   * matches options based on their values.
+   * &lt;ul class=&quot;first last simple&quot;&gt;
+   * &lt;li&gt;value=other&lt;/li&gt;
+   * &lt;/ul&gt;
+   *
+   *
+   * &lt;/li&gt;
+   * &lt;li&gt;&lt;strong&gt;id&lt;/strong&gt;=&lt;em&gt;id&lt;/em&gt;:
+   *
+   * matches options based on their ids.
+   * &lt;ul class=&quot;first last simple&quot;&gt;
+   * &lt;li&gt;id=option1&lt;/li&gt;
+   * &lt;/ul&gt;
+   * &lt;/li&gt;
+   * &lt;li&gt;&lt;strong&gt;index&lt;/strong&gt;=&lt;em&gt;index&lt;/em&gt;:
+   * matches an option based on its index (offset from zero).
+   * &lt;ul class=&quot;first last simple&quot;&gt;
+   *
+   * &lt;li&gt;index=2&lt;/li&gt;
+   * &lt;/ul&gt;
+   * &lt;/li&gt;
+   * &lt;/ul&gt;
+   * &lt;p&gt;
+   * If no option locator prefix is provided, the default behaviour is to match on &lt;strong&gt;label&lt;/strong&gt;.
+   * &lt;/p&gt;
+   *
+   *
+   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+   * @param optionLocator an option locator (a label by default)
+   */
+    var element = this.browserbot.findElement(selectLocator);
+    if (!(&quot;options&quot; in element)) {
+        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
+    }
+    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box
+   * @param optionLocator an option locator (a label by default)
+   */
+    var element = this.browserbot.findElement(locator);
+    if (!(&quot;options&quot; in element)) {
+        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
+    }
+    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box
+   * @param optionLocator an option locator (a label by default)
+   */
+
+    var element = this.browserbot.findElement(locator);
+    if (!(&quot;options&quot; in element)) {
+        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
+    }
+    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a multi-select box
+    */
+    var element = this.browserbot.findElement(locator);
+    if (!(&quot;options&quot; in element)) {
+        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
+    }
+    for (var i = 0; i &lt; 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 &quot;Search&quot; forms.
+   *
+   * @param formLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &amp;quot;open&amp;quot; command waits for the page to load before proceeding,
+   * ie. the &amp;quot;AndWait&amp;quot; suffix is implicit.
+   *
+   * &lt;em&gt;Note&lt;/em&gt;: 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[&quot;proxyInjectionMode&quot;] == null || !window[&quot;proxyInjectionMode&quot;]) {
+        return this.makePageLoadCondition();
+    } // in PI mode, just return &quot;OK&quot;; 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.
+   * 
+   * &lt;p&gt;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 &quot;onLoad&quot; 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(&quot;&quot;, &quot;myFunnyWindow&quot;).&lt;/p&gt;
+   *
+   * @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.
+   *
+   * &lt;p&gt;Note that there is a big difference between a window's internal JavaScript &quot;name&quot; property
+   * and the &quot;title&quot; 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 &quot;name&quot; is normally invisible to the end-user; it's the second 
+   * parameter &quot;windowName&quot; passed to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag)
+   * (which selenium intercepts).&lt;/p&gt;
+   *
+   * &lt;p&gt;Selenium has several strategies for finding the window object referred to by the &quot;windowID&quot; parameter.&lt;/p&gt;
+   * 
+   * &lt;p&gt;1.) if windowID is null, (or the string &quot;null&quot;) then it is assumed the user is referring to the original window instantiated by the browser).&lt;/p&gt;
+   * &lt;p&gt;2.) if the value of the &quot;windowID&quot; 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.&lt;/p&gt;
+   * &lt;p&gt;3.) Otherwise, selenium looks in a hash it maintains that maps string names to window &quot;names&quot;.&lt;/p&gt;
+   * &lt;p&gt;4.) If &lt;i&gt;that&lt;/i&gt; fails, we'll try looping over all of the known windows to try to find the appropriate &quot;title&quot;.
+   * Since &quot;title&quot; is not necessarily unique, this may have unexpected behavior.&lt;/p&gt;
+   *
+   * &lt;p&gt;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:&lt;/p&gt;
+   * 
+   * &lt;p&gt;&lt;code&gt;debug: window.open call intercepted; window ID (which you can use with selectWindow()) is &quot;myNewWindow&quot;&lt;/code&gt;&lt;/p&gt;
+   *
+   * &lt;p&gt;In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the &quot;onLoad&quot; 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(&quot;&quot;, &quot;myFunnyWindow&quot;).&lt;/p&gt;
+   * 
+   * @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
+    * &quot;relative=parent&quot; as a locator; to select the top frame, use &quot;relative=top&quot;.
+    * You can also select a frame by its 0-based index number; select the first frame with
+    * &quot;index=0&quot;, or the third frame with &quot;index=2&quot;.
+    *
+    * &lt;p&gt;You may also use a DOM expression to identify the frame you want directly,
+    * like this: &lt;code&gt;dom=frames[&quot;main&quot;].frames[&quot;subframe&quot;]&lt;/code&gt;&lt;/p&gt;
+    *
+    * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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.
+     *
+     * &lt;p&gt;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 &quot;current&quot; 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.&lt;/p&gt;
+     *
+     * @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.
+     *
+     * &lt;p&gt;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 &quot;current&quot; 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.&lt;/p&gt;
+     *
+     * @param currentWindowString starting window
+     * @param target new window (which might be relative to the current one, e.g., &quot;_parent&quot;)
+     * @return boolean true if the new window is this code's window
+     */
+     if (window.opener!=null &amp;&amp; window.opener[target]!=null &amp;&amp; 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 (&quot;about:blank&quot; == targetWindow.location) return false;
+        if (browserVersion.isKonqueror) {
+            if (&quot;/&quot; == 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(&quot;DGF what a world!&quot;);
+                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 &quot;back&quot; button on their browser.
+     *
+     */
+    this.browserbot.goBack();
+};
+
+Selenium.prototype.doRefresh = function() {
+    /**
+     * Simulates the user clicking the &quot;Refresh&quot; button on their browser.
+     *
+     */
+    this.browserbot.refresh();
+};
+
+Selenium.prototype.doClose = function() {
+    /**
+     * Simulates the user clicking the &quot;close&quot; button in the titlebar of a popup
+     * window or tab.
+     */
+    this.browserbot.close();
+};
+
+Selenium.prototype.ensureNoUnhandledPopups = function() {
+    if (this.browserbot.hasAlerts()) {
+        throw new SeleniumError(&quot;There was an unexpected Alert! [&quot; + this.browserbot.getNextAlert() + &quot;]&quot;);
+    }
+    if ( this.browserbot.hasConfirmations() ) {
+        throw new SeleniumError(&quot;There was an unexpected Confirmation! [&quot; + this.browserbot.getNextConfirmation() + &quot;]&quot;);
+    }
+};
+
+Selenium.prototype.isAlertPresent = function() {
+   /**
+   * Has an alert occurred?
+   *
+   * &lt;p&gt;
+   * This function never throws an exception
+   * &lt;/p&gt;
+   * @return boolean true if there is an alert
+   */
+    return this.browserbot.hasAlerts();
+};
+
+Selenium.prototype.isPromptPresent = function() {
+   /**
+   * Has a prompt occurred?
+   *
+   * &lt;p&gt;
+   * This function never throws an exception
+   * &lt;/p&gt;
+   * @return boolean true if there is a pending prompt
+   */
+    return this.browserbot.hasPrompts();
+};
+
+Selenium.prototype.isConfirmationPresent = function() {
+   /**
+   * Has confirm() been called?
+   *
+   * &lt;p&gt;
+   * This function never throws an exception
+   * &lt;/p&gt;
+   * @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.
+   *
+   * &lt;p&gt;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.&lt;/p&gt;
+   *
+   * &lt;p&gt;NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert
+   * dialog.&lt;/p&gt;
+   *
+   * &lt;p&gt;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.&lt;/p&gt;
+   * @return string The message of the most recent JavaScript alert
+   */
+    if (!this.browserbot.hasAlerts()) {
+        Assert.fail(&quot;There were no alerts&quot;);
+    }
+    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.
+   *
+   * &lt;p&gt;
+   * 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.
+   * &lt;/p&gt;
+   *
+   * &lt;p&gt;
+   * NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible
+   * dialog.
+   * &lt;/p&gt;
+   *
+   * &lt;p&gt;
+   * 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.
+   * &lt;/p&gt;
+   *
+   * @return string the message of the most recent JavaScript confirmation dialog
+   */
+    if (!this.browserbot.hasConfirmations()) {
+        Assert.fail(&quot;There were no confirmations&quot;);
+    }
+    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.
+   *
+   * &lt;p&gt;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.&lt;/p&gt;
+   *
+   * &lt;p&gt;NOTE: under Selenium, JavaScript prompts will NOT pop up a visible
+   * dialog.&lt;/p&gt;
+   *
+   * &lt;p&gt;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.&lt;/p&gt;
+   * @return string the message of the most recent JavaScript question prompt
+   */
+    if (! this.browserbot.hasPrompts()) {
+        Assert.fail(&quot;There were no prompts&quot;);
+    }
+    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 &quot;on&quot; or &quot;off&quot; depending on
+   * whether the element is checked or not.
+   *
+   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @return string the element value, or &quot;on/off&quot; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+    */
+    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.
+   *
+   * &lt;p&gt;Note that, by default, the snippet will run in the context of the &quot;selenium&quot;
+   * object itself, so &lt;code&gt;this&lt;/code&gt; will refer to the Selenium object.  Use &lt;code&gt;window&lt;/code&gt; to
+   * refer to the window of your application, e.g. &lt;code&gt;window.document.getElementById('foo')&lt;/code&gt;&lt;/p&gt;
+   *
+   * &lt;p&gt;If you need to use
+   * a locator to refer to a single element in your application page, you can
+   * use &lt;code&gt;this.browserbot.findElement(&quot;id=foo&quot;)&lt;/code&gt; where &quot;id=foo&quot; is your locator.&lt;/p&gt;
+   *
+   * @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 &quot;null&quot;;
+        return result;
+    } catch (e) {
+        throw new SeleniumError(&quot;Threw an exception: &quot; + 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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(&quot;Element &quot; + locator + &quot; is not a toggle-button.&quot;);
+    }
+    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. &quot;foo.1.4&quot;
+   * @return string the text from the specified cell
+   */
+    // This regular expression matches &quot;tableName.row.column&quot;
+    // For example, &quot;mytable.3.4&quot;
+    pattern = /(.*)\.(\d+)\.(\d+)/;
+
+    if(!pattern.test(tableCellAddress)) {
+        throw new SeleniumError(&quot;Invalid target format. Correct format is tableName.rowNum.columnNum&quot;);
+    }
+
+    pieces = tableCellAddress.match(pattern);
+
+    tableName = pieces[1];
+    row = pieces[2];
+    col = pieces[3];
+
+    var table = this.browserbot.findElement(tableName);
+    if (row &gt; table.rows.length) {
+        Assert.fail(&quot;Cannot access row &quot; + row + &quot; - table has &quot; + table.rows.length + &quot; rows&quot;);
+    }
+    else if (col &gt; table.rows[row].cells.length) {
+        Assert.fail(&quot;Cannot access column &quot; + col + &quot; - table row has &quot; + table.rows[row].cells.length + &quot; columns&quot;);
+    }
+    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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+   * @return string[] an array of all selected option labels in the specified select drop-down
+   */
+    return this.findSelectedOptionProperties(selectLocator, &quot;text&quot;);
+}
+
+Selenium.prototype.getSelectedLabel = function(selectLocator) {
+    /** Gets option label (visible text) for selected option in the specified select element.
+   *
+   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+   * @return string the selected option label in the specified select drop-down
+   */
+    return this.findSelectedOptionProperty(selectLocator, &quot;text&quot;);
+}
+
+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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+   * @return string[] an array of all selected option values in the specified select drop-down
+   */
+    return this.findSelectedOptionProperties(selectLocator, &quot;value&quot;);
+}
+
+Selenium.prototype.getSelectedValue = function(selectLocator) {
+    /** Gets option value (value attribute) for selected option in the specified select element.
+   *
+   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+   * @return string the selected option value in the specified select drop-down
+   */
+    return this.findSelectedOptionProperty(selectLocator, &quot;value&quot;);
+}
+
+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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+   * @return string[] an array of all selected option indexes in the specified select drop-down
+   */
+    return this.findSelectedOptionProperties(selectLocator, &quot;index&quot;);
+}
+
+Selenium.prototype.getSelectedIndex = function(selectLocator) {
+    /** Gets option index (option number, starting at 0) for selected option in the specified select element.
+   *
+   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+   * @return string the selected option index in the specified select drop-down
+   */
+    return this.findSelectedOptionProperty(selectLocator, &quot;index&quot;);
+}
+
+Selenium.prototype.getSelectedIds = function(selectLocator) {
+    /** Gets all option element IDs for selected options in the specified select or multi-select element.
+   *
+   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+   * @return string[] an array of all selected option IDs in the specified select drop-down
+   */
+    return this.findSelectedOptionProperties(selectLocator, &quot;id&quot;);
+}
+
+Selenium.prototype.getSelectedId = function(selectLocator) {
+    /** Gets option element ID for selected option in the specified select element.
+   *
+   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+   * @return string the selected option ID in the specified select drop-down
+   */
+    return this.findSelectedOptionProperty(selectLocator, &quot;id&quot;);
+}
+
+Selenium.prototype.isSomethingSelected = function(selectLocator) {
+    /** Determines whether some option in a drop-down menu is selected.
+   *
+   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+   * @return boolean true if some option has been selected, false otherwise
+   */
+    var element = this.browserbot.findElement(selectLocator);
+    if (!(&quot;options&quot; in element)) {
+        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
+    }
+
+    var selectedOptions = [];
+
+    for (var i = 0; i &lt; 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 (!(&quot;options&quot; in element)) {
+        throw new SeleniumError(&quot;Specified element is not a Select (has no options)&quot;);
+    }
+
+    var selectedOptions = [];
+
+    for (var i = 0; i &lt; element.options.length; i++) {
+        if (element.options[i].selected)
+        {
+            var propVal = element.options[i][property];
+            selectedOptions.push(propVal);
+        }
+    }
+    if (selectedOptions.length == 0) Assert.fail(&quot;No option selected&quot;);
+    return selectedOptions;
+}
+
+Selenium.prototype.findSelectedOptionProperty = function(locator, property) {
+    var selectedOptions = this.findSelectedOptionProperties(locator, property);
+    if (selectedOptions.length &gt; 1) {
+        Assert.fail(&quot;More than one selected option!&quot;);
+    }
+    return selectedOptions[0];
+}
+
+Selenium.prototype.getSelectOptions = function(selectLocator) {
+    /** Gets all option labels in the specified select drop-down.
+   *
+   * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &lt; 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 &amp;#064; sign and then the name of the attribute, e.g. &quot;foo&amp;#064;bar&quot;
+   * @return string the value of the specified attribute
+   */
+   var result = this.browserbot.findAttribute(attributeLocator);
+   if (result == null) {
+           throw new SeleniumError(&quot;Could not find element attribute: &quot; + 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 &lt;a href=&quot;#patterns&quot;&gt;pattern&lt;/a&gt; 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(&quot;glob:&quot;)==0) {
+                    pattern = pattern.substring(&quot;glob:&quot;.length); // strip off &quot;glob:&quot;
+                }
+        patternMatcher.matcher = new PatternMatcher.strategies.globContains(pattern);
+    }
+    else if (patternMatcher.strategy == PatternMatcher.strategies.exact) {
+                pattern = pattern.substring(&quot;exact:&quot;.length); // strip off &quot;exact:&quot;
+        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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+    * @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 &quot;visibility&quot;
+   * property to &quot;hidden&quot;, or the &quot;display&quot; property to &quot;none&quot;, either for the
+   * element itself or one if its ancestors.  This method will fail if
+   * the element is not present.
+   *
+   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @return boolean true if the specified element is visible, false otherwise
+   */
+    var element;
+    element = this.browserbot.findElement(locator);
+    var visibility = this.findEffectiveStyleProperty(element, &quot;visibility&quot;);
+    var _isDisplayed = this._isDisplayed(element);
+    return (visibility != &quot;hidden&quot; &amp;&amp; _isDisplayed);
+};
+
+Selenium.prototype.findEffectiveStyleProperty = function(element, property) {
+    var effectiveStyle = this.findEffectiveStyle(element);
+    var propertyValue = effectiveStyle[property];
+    if (propertyValue == 'inherit' &amp;&amp; element.parentNode.style) {
+        return this.findEffectiveStyleProperty(element.parentNode, property);
+    }
+    return propertyValue;
+};
+
+Selenium.prototype._isDisplayed = function(element) {
+    var display = this.findEffectiveStyleProperty(element, &quot;display&quot;);
+    if (display == &quot;none&quot;) 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 &quot;visibility&quot;
+    }
+    throw new SeleniumError(&quot;cannot determine effective stylesheet in this browser&quot;);
+};
+
+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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt;
+   * @return boolean true if the input element is editable, false otherwise
+   */
+    var element = this.browserbot.findElement(locator);
+    if (element.value == undefined) {
+        Assert.fail(&quot;Element &quot; + locator + &quot; is not an input.&quot;);
+    }
+    return !element.disabled;
+};
+
+Selenium.prototype.getAllButtons = function() {
+    /** Returns the IDs of all buttons on the page.
+   *
+   * &lt;p&gt;If a given button has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;
+   *
+   * @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.
+   *
+   * &lt;p&gt;If a given link has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;
+   *
+   * @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.
+   *
+   * &lt;p&gt;If a given field has no ID, it will appear as &quot;&quot; in this array.&lt;/p&gt;
+   *
+   * @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 &quot;win.&quot;+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(&quot;win.&quot;+attributeName));
+    for (var windowName in this.browserbot.openedWindows)
+    {
+        try {
+            win = selenium.browserbot.openedWindows[windowName];
+            attributes.push(eval(&quot;win.&quot;+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 = &quot;name&quot;;
+   if (soughtAfterWindowPropertyValue.match(&quot;^title=&quot;)) {
+       targetPropertyName = &quot;document.title&quot;;
+        soughtAfterWindowPropertyValue = soughtAfterWindowPropertyValue.replace(/^title=/, &quot;&quot;);
+   }
+   else {
+       // matching &quot;name&quot;:
+       // 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, &quot;&quot;)) {
+           return this.browserbot.getCurrentWindow();
+        }
+   }
+
+   // DGF normally you should use []s instead of eval &quot;win.&quot;+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(&quot;this.browserbot.topWindow.&quot; + targetPropertyName))) {
+       return this.browserbot.topWindow;
+   }
+   for (windowName in selenium.browserbot.openedWindows) {
+       var openedWindow = selenium.browserbot.openedWindows[windowName];
+       if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval(&quot;openedWindow.&quot; + targetPropertyName))) {
+            return openedWindow;
+        }
+   }
+   throw new SeleniumError(&quot;could not find window with property &quot; + targetPropertyName + &quot; matching &quot; + 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., &quot;+70,-300&quot;
+   */
+   this.doDragAndDrop(locator, movementsString);
+};
+
+Selenium.prototype.doSetMouseSpeed = function(pixels) {
+    /** Configure the number of pixels between &quot;mousemove&quot; events during dragAndDrop commands (default=10).
+    * &lt;p&gt;Setting this value to 0 means that we'll send a &quot;mousemove&quot; 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.&lt;/p&gt;
+    * 
+    * &lt;p&gt;If the mouse speed is greater than the distance between the two dragged objects, we'll
+    * just send one &quot;mousemove&quot; at the start location and then one final one at the end location.&lt;/p&gt;
+    * @param pixels the number of pixels between &quot;mousemove&quot; events
+    */
+    this.mouseSpeed = pixels;
+}
+ 
+Selenium.prototype.getMouseSpeed = function() {
+    /** Returns the number of pixels between &quot;mousemove&quot; events during dragAndDrop commands (default=10).
+    * 
+    * @return number the number of pixels between &quot;mousemove&quot; 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., &quot;+70,-300&quot;
+    */
+    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) &lt; 0) ? 0 : (clientStartX + movementX);
+    var clientFinishY = ((clientStartY + movementY) &lt; 0) ? 0 : (clientStartY + movementY);
+    
+    var mouseSpeed = this.mouseSpeed;
+    var move = function(current, dest) {
+        if (current == dest) return current;
+        if (Math.abs(current - dest) &lt; mouseSpeed) return dest;
+        return (current &lt; 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 = &quot;&quot; + deltaX + &quot;,&quot; + 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 &amp;&amp; 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(&quot;id&quot;);
+};
+
+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(&quot;name&quot;);
+};
+
+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(&quot;document.title&quot;);
+};
+
+Selenium.prototype.getHtmlSource = function() {
+    /** Returns the entire HTML source between the opening and
+   * closing &quot;html&quot; tags.
+   *
+   * @return string the entire HTML source
+   */
+    return this.browserbot.getDocument().getElementsByTagName(&quot;html&quot;)[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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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(&quot;Element &quot; + locator + &quot; is not an input.&quot;);
+    }
+    if (position == -1) {
+        position = element.value.length;
+    }
+
+   if( element.setSelectionRange &amp;&amp; !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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to the first element
+     * @param locator2 an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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) &amp;&amp; !(/[^\t\n\r ]/.test(node.data)));
+}
+
+Selenium.prototype.getElementPositionLeft = function(locator) {
+   /**
+   * Retrieves the horizontal position of an element
+   *
+   * @param locator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element OR an element itself
+   * @return number of pixels from the edge of the frame.
+   */
+       var element;
+        if (&quot;string&quot;==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 != &quot;TABLE&quot;) &amp;&amp; (elementParent.tagName != &quot;BODY&quot;) )
+            {
+                x += elementParent.clientLeft;
+            }
+        }
+        else // Netscape/DOM
+        {
+            if(elementParent.tagName == &quot;TABLE&quot;)
+            {
+                var parentBorder = parseInt(elementParent.border);
+                if(isNaN(parentBorder))
+                {
+                    var parentFrame = elementParent.getAttribute('frame');
+                    if(parentFrame != null)
+                    {
+                        x += 1;
+                    }
+                }
+                else if(parentBorder &gt; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; pointing to an element OR an element itself
+   * @return number of pixels from the edge of the frame.
+   */
+       var element;
+        if (&quot;string&quot;==typeof locator) {
+            element = this.browserbot.findElement(locator);
+        }
+        else {
+            element = locator;
+        }
+
+       var y = 0;
+
+       while (element != null)
+    {
+        if(document.all)
+        {
+            if( (element.tagName != &quot;TABLE&quot;) &amp;&amp; (element.tagName != &quot;BODY&quot;) )
+            {
+            y += element.clientTop;
+            }
+        }
+        else // Netscape/DOM
+        {
+            if(element.tagName == &quot;TABLE&quot;)
+            {
+            var parentBorder = parseInt(element.border);
+            if(isNaN(parentBorder))
+            {
+                var parentFrame = element.getAttribute('frame');
+                if(parentFrame != null)
+                {
+                    y += 1;
+                }
+            }
+            else if(parentBorder &gt; 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 &amp;&amp; element.offsetParent.offsetHeight &amp;&amp; element.offsetParent.offsetHeight &lt; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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.
+   *
+   * &lt;p&gt;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 &lt;a href=&quot;http://jira.openqa.org/browse/SEL-243&quot;&gt;SEL-243&lt;/a&gt;.&lt;/p&gt;
+   * 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &amp;&amp; !browserVersion.isOpera){
+        try {
+            var selectRange = doc.selection.createRange().duplicate();
+            var elementRange = element.createTextRange();
+            selectRange.move(&quot;character&quot;,0);
+            elementRange.move(&quot;character&quot;,0);
+            var inRange1 = selectRange.inRange(elementRange);
+            var inRange2 = elementRange.inRange(selectRange);
+            elementRange.setEndPoint(&quot;EndToEnd&quot;, selectRange);
+        } catch (e) {
+            Assert.fail(&quot;There is no cursor on this page!&quot;);
+        }
+        var answer = String(elementRange.text).replace(/\r/g,&quot;&quot;).length;
+        return answer;
+    } else {
+        if (typeof(element.selectionStart) != &quot;undefined&quot;) {
+            if (win.getSelection &amp;&amp; typeof(win.getSelection().rangeCount) != undefined &amp;&amp; win.getSelection().rangeCount == 0) {
+                Assert.fail(&quot;There is no cursor on this page!&quot;);
+            }
+            return element.selectionStart;
+        }
+    }
+    throw new Error(&quot;Couldn't detect cursor position on this browser!&quot;);
+}
+
+
+Selenium.prototype.getExpression = function(expression) {
+    /**
+     * Returns the specified expression.
+     *
+     * &lt;p&gt;This is useful because of JavaScript preprocessing.
+     * It is used to generate commands like assertExpression and waitForExpression.&lt;/p&gt;
+     *
+     * @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. &quot;//table&quot; 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 &quot;id&quot; 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 &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; 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 &quot;false&quot; 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 (&quot;false&quot; == allow || &quot;0&quot; == allow) { // The strings &quot;false&quot; and &quot;0&quot; 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 &quot;true&quot;.
+   * The snippet may have multiple lines, but only the result of the last line
+   * will be considered.
+   *
+   * &lt;p&gt;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 &lt;code&gt;selenium.browserbot.getCurrentWindow()&lt;/code&gt;, and then
+   * run your JavaScript in there&lt;/p&gt;
+   * @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.
+     *
+     * &lt;p&gt;Actions that require waiting include &quot;open&quot; and the &quot;waitFor*&quot; actions.&lt;/p&gt;
+     * 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.
+     *
+     * &lt;p&gt;You can use this command instead of the &quot;AndWait&quot; suffixes, &quot;clickAndWait&quot;, &quot;selectAndWait&quot;, &quot;typeAndWait&quot; etc.
+     * (which are only available in the JS API).&lt;/p&gt;
+     *
+     * &lt;p&gt;Selenium constantly keeps track of new pages loading, and sets a &quot;newPageLoaded&quot;
+     * 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.&lt;/p&gt;
+     * @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[&quot;proxyInjectionMode&quot;] == null || !window[&quot;proxyInjectionMode&quot;]) {
+        return this.makePageLoadCondition(timeout);
+    }
+};
+
+Selenium.prototype.doWaitForFrameToLoad = function(frameAddress, timeout) {
+    /**
+     * Waits for a new frame to load.
+     *
+     * &lt;p&gt;Selenium constantly keeps track of new pages and frames loading, 
+     * and sets a &quot;newPageLoaded&quot; flag when it first notices a page load.&lt;/p&gt;
+     * 
+     * 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[&quot;proxyInjectionMode&quot;] == null || !window[&quot;proxyInjectionMode&quot;]) {
+        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 &quot;javascript{ ... }&quot;, evaluate the string between the braces.
+ */
+Selenium.prototype.preprocessParameter = function(value) {
+    var match = value.match(/^javascript\{((.|\r?\n)+)\}$/);
+    if (match &amp;&amp; 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 &amp;&amp; i &lt; 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 &quot;name=value&quot;
+     * @param optionsString options for the cookie. Currently supported options include 'path' and 'max_age'.
+     *      the optionsString's format is &quot;path=/path/, max_age=60&quot;. The order of options are irrelevant, the unit
+     *      of the value of 'max_age' is second.
+     */
+    var results = /[^\s=\[\]\(\),&quot;\/\?@:;]+=[^\s=\[\]\(\),&quot;\/\?@:;]*/.test(nameValuePair);
+    if (!results) {
+        throw new SeleniumError(&quot;Invalid parameter.&quot;);
+    }
+    var cookie = nameValuePair.trim();
+    results = /max_age=(\d+)/.exec(optionsString);
+    if (results) {
+        var expireDateInMilliseconds = (new Date()).getTime() + results[1] * 1000;
+        cookie += &quot;; expires=&quot; + 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 (&quot;/&quot; != path) {
+                path = path.replace(/\/$/, &quot;&quot;);
+            }
+        }
+        cookie += &quot;; path=&quot; + path;
+    }
+    LOG.debug(&quot;Setting cookie to: &quot; + 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 (&quot;/&quot; != path) {
+            path = path.replace(/\/$/, &quot;&quot;);
+        }
+    }
+    var expireDateInMilliseconds = (new Date()).getTime() + (-1 * 1000);
+    var cookie = name.trim() + &quot;=deleted; path=&quot; + path + &quot;; expires=&quot; + new Date(expireDateInMilliseconds).toGMTString();
+    LOG.debug(&quot;Setting cookie to: &quot; + 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: &quot;debug&quot;, &quot;info&quot;, &quot;warn&quot;, &quot;error&quot; or &quot;off&quot;.
+    * 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: &quot;debug&quot;, &quot;info&quot;, &quot;warn&quot;, &quot;error&quot; or &quot;off&quot;
+    */
+    if (logLevel == null || logLevel == &quot;&quot;) {
+        throw new SeleniumError(&quot;You must specify a log level&quot;);
+    }
+    logLevel = logLevel.toLowerCase();
+    if (LOG.logLevels[logLevel] == null) {
+        throw new SeleniumError(&quot;Invalid log level: &quot; + logLevel);
+    }
+    LOG.setLogLevelThreshold(logLevel);
+}
+
+Selenium.prototype.doRunScript = function(script) {
+    /**
+    * Creates a new &quot;script&quot; 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 &quot;getEval&quot; 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(&quot;script&quot;);
+    scriptTag.type = &quot;text/javascript&quot;
+    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 &quot;foo&quot;, and someone runs click(&quot;foo=blah&quot;), we'll
+    * run your function, passing you the string &quot;blah&quot;, and click on the element 
+    * that your function
+    * returns, or throw an &quot;Element not found&quot; error if your function returns null.
+    *
+    * We'll pass three arguments to your function:
+    * &lt;ul&gt;
+    * &lt;li&gt;locator: the string the user passed in&lt;/li&gt;
+    * &lt;li&gt;inWindow: the currently selected window&lt;/li&gt;
+    * &lt;li&gt;inDocument: the currently selected document&lt;/li&gt;
+    * &lt;/ul&gt;
+    * 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: &lt;code&gt;return inDocument.getElementById(locator);&lt;/code&gt;
+    */
+    if (!/^[a-zA-Z]+$/.test(strategyName)) {
+        throw new SeleniumError(&quot;Invalid strategy name: &quot; + strategyName);
+    }
+    var strategyFunction;
+    try {
+        strategyFunction = new Function(&quot;locator&quot;, &quot;inDocument&quot;, &quot;inWindow&quot;, functionDefinition);
+    } catch (ex) {
+        throw new SeleniumError(&quot;Error evaluating function definition: &quot; + extractExceptionMessage(ex));
+    }
+    var safeStrategyFunction = function() {
+        try {
+            return strategyFunction.apply(this, arguments);
+        } catch (ex) {
+            throw new SeleniumError(&quot;Error executing strategy function &quot; + strategyName + &quot;: &quot; + extractExceptionMessage(ex));
+        }
+    }
+    this.browserbot.locationStrategies[strategyName] = safeStrategyFunction;
+}
+
+/**
+ *  Factory for creating &quot;Option Locators&quot;.
+ *  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=&lt;exp&gt;  (OptionLocatorByLabel)
+ *     value=&lt;exp&gt;  (OptionLocatorByValue)
+ *     index=&lt;exp&gt;  (OptionLocatorByIndex)
+ *     id=&lt;exp&gt;     (OptionLocatorById)
+ *     &lt;exp&gt; (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(&quot;Unkown option locator type: &quot; + locatorType);
+};
+
+/**
+ * To allow for easy extension, all of the option locators are found by
+ * searching for all methods of OptionLocatorFactory.prototype that start
+ * with &quot;OptionLocatorBy&quot;.
+ * TODO: Consider using the term &quot;Option Specifier&quot; instead of &quot;Option Locator&quot;.
+ */
+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 &lt; element.options.length; i++) {
+            if (this.labelMatcher.matches(element.options[i].text)) {
+                return element.options[i];
+            }
+        }
+        throw new SeleniumError(&quot;Option with label '&quot; + this.label + &quot;' not found&quot;);
+    };
+
+    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 &lt; element.options.length; i++) {
+            if (this.valueMatcher.matches(element.options[i].value)) {
+                return element.options[i];
+            }
+        }
+        throw new SeleniumError(&quot;Option with value '&quot; + this.value + &quot;' not found&quot;);
+    };
+
+    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 &lt; 0) {
+        throw new SeleniumError(&quot;Illegal Index: &quot; + index);
+    }
+
+    this.findOption = function(element) {
+        if (element.options.length &lt;= this.index) {
+            throw new SeleniumError(&quot;Index out of range.  Only &quot; + element.options.length + &quot; options available&quot;);
+        }
+        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 &lt; element.options.length; i++) {
+            if (this.idMatcher.matches(element.options[i].id)) {
+                return element.options[i];
+            }
+        }
+        throw new SeleniumError(&quot;Option with id '&quot; + this.id + &quot;' not found&quot;);
+    };
+
+    this.assertSelected = function(element) {
+        var selectedId = element.options[element.selectedIndex].id;
+        Assert.matches(this.id, selectedId)
+    };
+};</diff>
      <filename>testframework/selenium/scripts/selenium-api.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,2203 +1,2203 @@
-/*
-* Copyright 2004 ThoughtWorks, Inc
-*
-*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-*  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 &quot;AS IS&quot; 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 &quot;user-entry&quot; 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 = &quot;seleniumMarker&quot; + 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(&quot;Page load detected&quot;);
-        try {
-            if (elementOrWindow.location &amp;&amp; elementOrWindow.location.href) {
-                LOG.debug(&quot;Page load location=&quot; + elementOrWindow.location.href);
-            } else if (elementOrWindow.contentWindow &amp;&amp; elementOrWindow.contentWindow.location &amp;&amp; elementOrWindow.contentWindow.location.href) {
-                LOG.debug(&quot;Page load location=&quot; + elementOrWindow.contentWindow.location.href);
-            } else {
-                LOG.debug(&quot;Page load location unknown, current window location=&quot; + this.getCurrentWindow(true).location);
-            }
-        } catch (e) {
-            LOG.error(&quot;Caught an exception attempting to log location; this should get noticed soon!&quot;);
-            LOG.exception(e);
-            self.pageLoadError = e;
-            return;
-        }
-        self.newPageLoaded = true;
-    };
-
-    this.isNewPageLoaded = function() {
-        if (this.pageLoadError) {
-            LOG.error(&quot;isNewPageLoaded found an old pageLoadError&quot;);
-            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(&quot;browserName: &quot; + browserVersion.name);
-    LOG.debug(&quot;userAgent: &quot; + 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 &quot;do&quot; 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 &gt; 0);
-};
-
-BrowserBot.prototype.relayBotToRC = function(s) {
-    // DGF need to do this funny trick to see if we're in PI mode, because
-    // &quot;this&quot; might be the window, rather than the browserbot (e.g. during window.alert) 
-    var piMode = this.proxyInjectionMode;
-    if (!piMode) {
-        if (typeof(selenium) != &quot;undefined&quot;) {
-            piMode = selenium.browserbot &amp;&amp; selenium.browserbot.proxyInjectionMode;
-        }
-    }
-    if (piMode) {
-        this.relayToRC(&quot;selenium.&quot; + s);
-    }
-};
-
-BrowserBot.prototype.relayToRC = function(name) {
-        var object = eval(name);
-        var s = 'state:' + serializeObject(name, object) + &quot;\n&quot;;
-        sendToRC(s,&quot;state=true&quot;);
-}
-
-BrowserBot.prototype.resetPopups = function() {
-    this.recordedAlerts = [];
-    this.recordedConfirmations = [];
-    this.recordedPrompts = [];
-}
-
-BrowserBot.prototype.getNextAlert = function() {
-    var t = this.recordedAlerts.shift();
-    this.relayBotToRC(&quot;browserbot.recordedAlerts&quot;);
-    return t;
-};
-
-BrowserBot.prototype.hasConfirmations = function() {
-    return (this.recordedConfirmations.length &gt; 0);
-};
-
-BrowserBot.prototype.getNextConfirmation = function() {
-    var t = this.recordedConfirmations.shift();
-    this.relayBotToRC(&quot;browserbot.recordedConfirmations&quot;);
-    return t;
-};
-
-BrowserBot.prototype.hasPrompts = function() {
-    return (this.recordedPrompts.length &gt; 0);
-};
-
-BrowserBot.prototype.getNextPrompt = function() {
-    var t = this.recordedPrompts.shift();
-    this.relayBotToRC(&quot;browserbot.recordedPrompts&quot;);
-    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(&quot;triggerMouseEvent assumes setting screenX and screenY to 0 is ok&quot;);
-    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 &amp;&amp; !screenY &amp;&amp; !clientX &amp;&amp; !clientY &amp;&amp; !this.controlKeyDown &amp;&amp; !this.altKeyDown &amp;&amp; !this.shiftKeyDown &amp;&amp; !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 &quot;Object does not support this action or property&quot; 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(&quot;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&quot;);
-            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(&quot;modifyWindow: Window was closed!&quot;);
-        }
-        return null;
-    }
-    if (!this.proxyInjectionMode) {
-        LOG.debug('modifyWindow ' + this.uniqueId + &quot;:&quot; + 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 &amp;&amp; win.frames.length &amp;&amp; win.frames.length &gt; 0) {
-        for (var i = 0; i &lt; 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 &amp;&amp; target != &quot;null&quot;) {
-        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(&quot;index=&quot;) == 0) {
-        target = target.substr(6);
-        var frame = this.getCurrentWindow().frames[target];
-        if (frame == null) {
-            throw new SeleniumError(&quot;Not found: frames[&quot;+index+&quot;]&quot;);
-        }
-        if (!frame.document) {
-            throw new SeleniumError(&quot;frames[&quot;+index+&quot;] is not a frame&quot;);
-        }
-        this.currentWindow = frame;
-        this.isSubFrameSelected = true;
-    }
-    else if (target == &quot;relative=up&quot;) {
-        this.currentWindow = this.getCurrentWindow().parent;
-        this.isSubFrameSelected = (this._getFrameElement(this.currentWindow) != null);
-    } else if (target == &quot;relative=top&quot;) {
-        this.currentWindow = this.topFrame;
-        this.isSubFrameSelected = false;
-    } else {
-        var frame = this.findElement(target);
-        if (frame == null) {
-            throw new SeleniumError(&quot;Not found: &quot; + 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 &amp;&amp; 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 &amp;&amp; win.frames &amp;&amp; win.frames.length) {
-                for (var i = 0; i &lt; 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(&quot;Not a frame: &quot; + target);
-            }
-        }
-    }
-    // modifies the window
-    this.getCurrentWindow();
-};
-
-BrowserBot.prototype.doesThisFrameMatchFrameExpression = function(currentFrameString, target) {
-    var isDom = false;
-    if (target.indexOf(&quot;dom=&quot;) == 0) {
-        target = target.substr(4);
-        isDom = true;
-    } else if (target.indexOf(&quot;index=&quot;) == 0) {
-        target = &quot;frames[&quot; + target.substr(6) + &quot;]&quot;;
-        isDom = true;
-    }
-    var t;
-    try {
-        eval(&quot;t=&quot; + currentFrameString + &quot;.&quot; + 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(&quot;currentFrame=&quot; + currentFrameString);
-    if (target == &quot;relative=up&quot;) {
-        if (currentFrame.window.parent == autWindow) {
-            return true;
-        }
-        return false;
-    }
-    if (target == &quot;relative=top&quot;) {
-        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(&quot;openLocation newPageLoaded = false&quot;);
-    this.newPageLoaded = false;
-
-    this.setOpenLocation(win, target);
-};
-
-BrowserBot.prototype.openWindow = function(url, windowID) {
-    if (url != &quot;&quot;) {
-        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 &amp;&amp; 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, &quot;browserbot.recordedAlerts&quot;);
-    };
-
-    windowToModify.confirm = function(message) {
-        browserBot.recordedConfirmations.push(message);
-        var result = browserBot.nextConfirmResult;
-        browserBot.nextConfirmResult = true;
-        self.relayBotToRC.call(self, &quot;browserbot.recordedConfirmations&quot;);
-        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, &quot;browserbot.recordedPrompts&quot;);
-        return result;
-    };
-
-    // Keep a reference to all popup windows by name
-    // note that in IE the &quot;windowName&quot; 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(&quot;window.open call intercepted; window ID (which you can use with selectWindow()) is \&quot;&quot; +  windowName + &quot;\&quot;&quot;);
-        if (windowName!=null) {
-            openedWindow[&quot;seleniumWindowName&quot;] = 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 = &quot;this['&quot; + originalOpenReference + &quot;'] = this.open;&quot;;
-
-        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 += &quot;this.open = this['&quot; + newOpenReference + &quot;'];&quot;;
-            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(&quot;modifySeparateTestWindowToDetectPageLoads: no windowObject!&quot;);
-        return;
-    }
-    if (this._windowClosed(windowObject)) {
-        LOG.info(&quot;modifySeparateTestWindowToDetectPageLoads: windowObject was closed&quot;);
-        return;
-    }
-    var oldMarker = this.isPollingForLoad(windowObject);
-    if (oldMarker) {
-        LOG.debug(&quot;modifySeparateTestWindowToDetectPageLoads: already polling this window: &quot; + oldMarker);
-        return;
-    }
-
-    var marker = 'selenium' + new Date().getTime();
-    LOG.debug(&quot;Starting pollForLoad (&quot; + marker + &quot;): &quot; + 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 &amp;&amp; !htaSubFrame) {
-        LOG.debug(&quot;modifySeparateTestWindowToDetectPageLoads: this window is a frame; attaching a load listener&quot;);
-        addLoadListener(frameElement, this.recordPageLoad);
-        frameElement[marker] = true;
-        frameElement[&quot;frame&quot;+this.uniqueId] = marker;
-	LOG.debug(&quot;dgf this.uniqueId=&quot;+this.uniqueId);
-	LOG.debug(&quot;dgf marker=&quot;+marker);
-	LOG.debug(&quot;dgf frameElement['frame'+this.uniqueId]=&quot;+frameElement['frame'+this.uniqueId]);
-frameElement[this.uniqueId] = marker;
-LOG.debug(&quot;dgf frameElement[this.uniqueId]=&quot;+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 &quot;win&quot; 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 &quot;No such interface supported&quot; 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(&quot;_getFrameElement: frameElement=&quot;+frameElement); 
-    if (frameElement) {
-        LOG.debug(&quot;frameElement.name=&quot;+frameElement.name);
-    }
-    return frameElement;
-}
-
-BrowserBot.prototype._getFrameElementByName = function(name, doc, win) {
-    var frames;
-    var frame;
-    var i;
-    frames = doc.getElementsByTagName(&quot;iframe&quot;);
-    for (i = 0; i &lt; frames.length; i++) {
-        frame = frames[i];        
-        if (frame.name === name) {
-            return frame;
-        }
-    }
-    frames = doc.getElementsByTagName(&quot;frame&quot;);
-    for (i = 0; i &lt; 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(&quot;_getFrameElementByName couldn't find a frame or iframe; checking every element for the name &quot; + 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(&quot;pollForLoad original (&quot; + marker + &quot;): &quot; + originalHref);
-    try {
-        if (this._windowClosed(windowObject)) {
-            LOG.debug(&quot;pollForLoad WINDOW CLOSED (&quot; + marker + &quot;)&quot;);
-            delete this.pollingForLoad[marker];
-            return;
-        }
-
-        var isSamePage = this._isSamePage(windowObject, originalDocument, originalLocation, originalHref, marker);
-        var rs = this.getReadyState(windowObject, windowObject.document);
-
-        if (!isSamePage &amp;&amp; rs == 'complete') {
-            var currentHref = windowObject.location.href;
-            LOG.debug(&quot;pollForLoad FINISHED (&quot; + marker + &quot;): &quot; + rs + &quot; (&quot; + currentHref + &quot;)&quot;);
-            delete this.pollingForLoad[marker];
-            this._modifyWindow(windowObject);
-            var newMarker = this.isPollingForLoad(windowObject);
-            if (!newMarker) {
-                LOG.debug(&quot;modifyWindow didn't start new poller: &quot; + newMarker);
-                this.modifySeparateTestWindowToDetectPageLoads(windowObject);
-            }
-            newMarker = this.isPollingForLoad(windowObject);
-            var currentlySelectedWindow;
-            var currentlySelectedWindowMarker;
-            currentlySelectedWindow =this.getCurrentWindow(true);
-            currentlySelectedWindowMarker = currentlySelectedWindow[this.uniqueId];
-
-            LOG.debug(&quot;pollForLoad (&quot; + marker + &quot;) restarting &quot; + newMarker);
-            if (/(TestRunner-splash|Blank)\.html\?start=true$/.test(currentHref)) {
-                LOG.debug(&quot;pollForLoad Oh, it's just the starting page.  Never mind!&quot;);
-            } else if (currentlySelectedWindowMarker == newMarker) {
-                loadFunction(currentlySelectedWindow);
-            } else {
-                LOG.debug(&quot;pollForLoad page load detected in non-current window; ignoring (currentlySelected=&quot;+currentlySelectedWindowMarker+&quot;, detection in &quot;+newMarker+&quot;)&quot;);
-            }
-            return;
-        }
-        LOG.debug(&quot;pollForLoad continue (&quot; + marker + &quot;): &quot; + currentHref);
-        this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
-    } catch (e) {
-        LOG.debug(&quot;Exception during pollForLoad; this should get noticed soon (&quot; + e.message + &quot;)!&quot;);
-        //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 &gt; 0) {
-        currentHref = currentHref.substring(0, currentHash);
-    }
-    var originalHash = originalHref.indexOf('#');
-    if (originalHash &gt; 0) {
-        originalHref = originalHref.substring(0, originalHash);
-    }
-    LOG.debug(&quot;_isSamePage: currentHref: &quot; + currentHref);
-    LOG.debug(&quot;_isSamePage: originalHref: &quot; + 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(&quot;_isSamePage: sameDoc: &quot; + sameDoc);
-    LOG.debug(&quot;_isSamePage: sameLoc: &quot; + sameLoc);
-    LOG.debug(&quot;_isSamePage: sameHref: &quot; + sameHref);
-    LOG.debug(&quot;_isSamePage: markedLoc: &quot; + markedLoc);
-
-    return sameDoc &amp;&amp; sameLoc &amp;&amp; sameHref &amp;&amp; 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 &amp;&amp; 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'
-                    &amp;&amp; typeof currentDocument.getElementById != 'undefined'
-                    &amp;&amp; ( currentDocument.getElementsByTagName('body')[0] != null
-                    || currentDocument.body != null )) {
-                if (windowObject.frameElement &amp;&amp; windowObject.location.href == &quot;about:blank&quot; &amp;&amp; windowObject.frameElement.src != &quot;about:blank&quot;) {
-                    LOG.info(&quot;getReadyState not loaded, frame location was about:blank, but frame src = &quot; + windowObject.frameElement.src);
-                    return null;
-                }
-                LOG.debug(&quot;getReadyState = windowObject.frames.length = &quot; + windowObject.frames.length);
-                for (var i = 0; i &lt; windowObject.frames.length; i++) {
-                    LOG.debug(&quot;i = &quot; + i);
-                    if (this.getReadyState(windowObject.frames[i], windowObject.frames[i].document) != 'complete') {
-                        LOG.debug(&quot;getReadyState aha! the nested frame &quot; + windowObject.frames[i].name + &quot; wasn't ready!&quot;);
-                        return null;
-                    }
-                }
-
-                rs = 'complete';
-            } else {
-                LOG.debug(&quot;pollForLoad readyState was null and DOM appeared to not be ready yet&quot;);
-            }
-        }
-    }
-    else if (rs == &quot;loading&quot; &amp;&amp; browserVersion.isIE) {
-        LOG.debug(&quot;pageUnloading = true!!!!&quot;);
-        this.pageUnloading = true;
-    }
-    LOG.debug(&quot;getReadyState returning &quot; + 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 &quot;reschedulePoller&quot;, 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(&quot;button&quot;);
-    var buttonName = doc.createTextNode(marker + &quot; - &quot; + windowObject.name);
-    button.appendChild(buttonName);
-    var tools = doc.getElementById(&quot;tools&quot;);
-    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(&quot;runScheduledPollers&quot;);
-    var oldPollers = this.windowPollers;
-    this.windowPollers = new Array();
-    for (var i = 0; i &lt; oldPollers.length; i++) {
-        oldPollers[i].call();
-    }
-    LOG.debug(&quot;runScheduledPollers DONE&quot;);
-};
-
-BrowserBot.prototype.isPollingForLoad = function(win) {
-    var marker;
-    var frameElement = this._getFrameElement(win);
-    var htaSubFrame = this._isHTASubFrame(win);
-    if (frameElement &amp;&amp; !htaSubFrame) {
-	marker = frameElement[&quot;frame&quot;+this.uniqueId];
-    } else {
-        marker = win[this.uniqueId];
-    }
-    if (!marker) {
-        LOG.debug(&quot;isPollingForLoad false, missing uniqueId &quot; + this.uniqueId + &quot;: &quot; + marker);
-        return false;
-    }
-    if (!this.pollingForLoad[marker]) {
-        LOG.debug(&quot;isPollingForLoad false, this.pollingForLoad[&quot; + marker + &quot;]: &quot; + this.pollingForLoad[marker]);
-        return false;
-    }
-    return marker;
-};
-
-BrowserBot.prototype.getWindowByName = function(windowName, doNotModify) {
-    LOG.debug(&quot;getWindowByName(&quot; + windowName + &quot;)&quot;);
-    // First look in the map of opened windows
-    var targetWindow = this.openedWindows[windowName];
-    if (!targetWindow) {
-        targetWindow = this.topWindow[windowName];
-    }
-    if (!targetWindow &amp;&amp; windowName == &quot;_blank&quot;) {
-        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(&quot;Window does not exist&quot;);
-    }
-    if (browserVersion.isHTA) {
-        try {
-            targetWindow.location.href;
-        } catch (e) {
-            targetWindow = window.open(&quot;&quot;, 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(&quot;getWindowNameByTitle(&quot; + windowTitle + &quot;)&quot;);
-
-    // 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) &amp;&amp;
-                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 &quot;&quot;;
-        }
-    } catch (e) {} // IE Perm denied
-
-    throw new SeleniumError(&quot;Could not find window with title &quot; + windowTitle);
-};
-
-BrowserBot.prototype.getCurrentWindow = function(doNotModify) {
-    if (this.proxyInjectionMode) {
-        return window;
-    }
-    var testWindow = this.currentWindow;
-    if (!doNotModify) {
-        this._modifyWindow(testWindow);
-        LOG.debug(&quot;getCurrentWindow newPageLoaded = false&quot;);
-        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 &amp;&amp; testWindow.parent.frames &amp;&amp; testWindow.parent.frames.length) {
-            for (var i = 0; i &lt; testWindow.parent.frames.length; i++) {
-                if (testWindow.parent.frames[i] == testWindow) {
-                    missing = false;
-                    break;
-                }
-            }
-        }
-        if (missing) {
-            LOG.warn(&quot;Current subframe appears to have closed; selecting top frame&quot;);
-            this.selectFrame(&quot;relative=top&quot;);
-            return this.getCurrentWindow(doNotModify);
-        }
-    } else if (this._windowClosed(testWindow)) {
-        var closedError = new SeleniumError(&quot;Current window or frame is closed!&quot;);
-        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(&quot;Unrecognised locator type: '&quot; + locatorType + &quot;'&quot;);
-        }
-        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) == &quot;string&quot;) {
-        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 &lt; 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(&quot;Element &quot; + locator + &quot; not found&quot;);
-    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 &amp;&amp; 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(&quot;*&quot;);
-
-    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 &gt; 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 = &quot;dom&quot;;
-
-/**
- * Finds an element identified by the xpath expression. Expressions _must_
- * begin with &quot;//&quot;.
- */
-BrowserBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
-    // Trim any trailing &quot;/&quot;: 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+)=('([^\']+)'|&quot;([^\&quot;]+)&quot;)\]$/);
-    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\(\)=('([^\']+)'|&quot;([^\&quot;]+)&quot;)\]$/);
-    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 &amp;&amp; attributeName == &quot;class&quot;) {
-        attributeName = &quot;className&quot;;
-    }
-    var elements = inDocument.getElementsByTagName(tagName);
-    for (var i = 0; i &lt; 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 &lt; 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(&quot;Unknown namespace: &quot; + prefix + &quot;.&quot;);
-    }
-}
-
-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 &amp;&amp; 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 &amp;&amp; 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(&quot;xpath=&quot;) == 0) {
-        xpath = xpath.substring(6); 
-    }
-    if (xpath.indexOf(&quot;count(&quot;) == 0) {
-        // DGF we COULD just fix this up for the user, but we might get it wrong (parens?)
-        throw new SeleniumError(&quot;XPath count expressions must not be wrapped in count() function: &quot; + xpath);
-    }
-    
-    xpath=&quot;count(&quot;+xpath+&quot;)&quot;;
-
-    // Use document.evaluate() if it's available
-    if (this.allowNativeXpath &amp;&amp; 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 &amp;&amp; xpathResult.value) {
-        return xpathResult.value;
-    }
-    return 0;
-};
-
-/**
- * Finds a link element with text matching the expression supplied. Expressions must
- * begin with &quot;link:&quot;.
- */
-BrowserBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWindow) {
-    var links = inDocument.getElementsByTagName('a');
-    for (var i = 0; i &lt; links.length; i++) {
-        var element = links[i];
-        if (PatternMatcher.matches(linkText, getText(element))) {
-            return element;
-        }
-    }
-    return null;
-};
-BrowserBot.prototype.locateElementByLinkText.prefix = &quot;link&quot;;
-
-/**
- * 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(&quot;@&quot;);
-    var elementLocator = locator.slice(0, attributePos);
-    var attributeName = locator.slice(attributePos + 1);
-
-    // Find the element.
-    var element = this.findElement(elementLocator);
-
-    // Handle missing &quot;class&quot; attribute in IE.
-    if (browserVersion.isIE &amp;&amp; attributeName == &quot;class&quot;) {
-        attributeName = &quot;className&quot;;
-    }
-
-    // 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 &lt; element.options.length; i++) {
-        var option = element.options[i];
-        if (option.selected &amp;&amp; option != optionToSelect) {
-            option.selected = false;
-            changed = true;
-        }
-        else if (!option.selected &amp;&amp; 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(&quot;Not a multi-select&quot;);
-    }
-
-};
-
-BrowserBot.prototype.replaceText = function(element, stringValue) {
-    triggerEvent(element, 'focus', false);
-    triggerEvent(element, 'select', true);
-    var maxLengthAttr = element.getAttribute(&quot;maxLength&quot;);
-    var actualValue = stringValue;
-    if (maxLengthAttr != null) {
-        var maxLength = parseInt(maxLengthAttr);
-        if (stringValue.length &gt; maxLength) {
-            actualValue = stringValue.substr(0, maxLength);
-        }
-    }
-
-    if (getTagName(element) == &quot;body&quot;) {
-        if (element.ownerDocument &amp;&amp; element.ownerDocument.designMode) {
-            var designMode = new String(element.ownerDocument.designMode).toLowerCase();
-            if (designMode = &quot;on&quot;) {
-                // 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(&quot;var actuallySubmit = &quot;+marker+&quot;.onsubmit();&quot; +
-                &quot;if (actuallySubmit) { &quot; +
-                    marker+&quot;.submit(); &quot; +
-                    &quot;if (&quot;+marker+&quot;.target &amp;&amp; !/^_/.test(&quot;+marker+&quot;.target)) {&quot;+
-                        &quot;window.open('', &quot;+marker+&quot;.target);&quot;+
-                    &quot;}&quot;+
-                &quot;};&quot;+
-                marker+&quot;=null&quot;, 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 &amp;&amp; !/^_/.test(formElement.target)) {
-                    this.browserbot.openWindow('', formElement.target);
-                }
-            }
-        }
-    } else {
-        formElement.submit();
-    }
-}
-
-BrowserBot.prototype.clickElement = function(element, clientX, clientY) {
-       this._fireEventOnElement(&quot;click&quot;, element, clientX, clientY);
-};
-
-BrowserBot.prototype.doubleClickElement = function(element, clientX, clientY) {
-       this._fireEventOnElement(&quot;dblclick&quot;, element, clientX, clientY);
-};
-
-BrowserBot.prototype._modifyElementTarget = function(element) {
-    if (element.target) {
-        if (element.target == &quot;_blank&quot; || /^selenium_blank/.test(element.target) ) {
-            var tagName = getTagName(element);
-            if (tagName == &quot;a&quot; || tagName == &quot;form&quot;) {
-                var newTarget = &quot;selenium_blank&quot; + Math.round(100000 * Math.random());
-                LOG.warn(&quot;Link has target '_blank', which is not supported in Selenium!  Randomizing target to be: &quot; + 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 == &quot;_top&quot;) {
-        return this.topFrame;
-    } else if (target == &quot;_parent&quot;) {
-        return this.getCurrentWindow().parent;
-    } else if (target == &quot;_blank&quot;) {
-        // TODO should this set cleverer window defaults?
-        return this.getCurrentWindow().open('', '_blank');
-    }
-    var frameElement = this.findElementBy(&quot;implicit&quot;, 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(&quot;Couldn't access document.body.  Is this HTML page fully loaded?&quot;);
-    }
-    return getText(this.getDocument().body);
-};
-
-BrowserBot.prototype.getAllButtons = function() {
-    var elements = this.getDocument().getElementsByTagName('input');
-    var result = [];
-
-    for (var i = 0; i &lt; 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 &lt; 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 &lt; 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(&quot;window.close();&quot;);
-    }
-};
-
-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(&quot;Unrecognised element-filter type: '&quot; + filterType + &quot;'&quot;);
-    }
-
-    return filterFunction(filter, elements);
-};
-
-BrowserBot.filterFunctions = {};
-
-BrowserBot.filterFunctions.name = function(name, elements) {
-    var selectedElements = [];
-    for (var i = 0; i &lt; 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 &lt; 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 &lt; 0) {
-        throw new SeleniumError(&quot;Illegal Index: &quot; + index);
-    }
-    if (elements.length &lt;= index) {
-        throw new SeleniumError(&quot;Index out of range: &quot; + 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 = &quot;about:blank&quot;;
-    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 (&quot;about:blank&quot; != win.location.href) {
-        var startLoc = parseUrl(win.location.href);
-        startLoc.hash = null;
-        var startUrl = reassembleLocation(startLoc);
-    }
-    LOG.debug(&quot;startUrl=&quot;+startUrl);
-    LOG.debug(&quot;win.location.href=&quot;+win.location.href);
-    LOG.debug(&quot;loc=&quot;+loc);
-    if (startUrl == loc) {
-        LOG.debug(&quot;opening exact same location&quot;);
-        this.refresh();
-    } else {
-        LOG.debug(&quot;locations differ&quot;);
-        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 &gt; 4) {
-        if (this.isSubFrameSelected) {
-            LOG.warn(&quot;Current subframe appears to have closed; selecting top frame&quot;);
-            this.selectFrame(&quot;relative=top&quot;);
-            return this.getCurrentWindow(doNotModify);
-        } else {
-            var closedError = new SeleniumError(&quot;Current window or frame is closed!&quot;);
-            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 = &quot;&amp;runInterval=&quot; + runOptions.runInterval;
-        }
-            
-        var testRunnerURL = &quot;TestRunner.html?auto=true&amp;singletest=&quot; 
-            + escape(browserBot.modalDialogTest)
-            + &quot;&amp;autoURL=&quot; 
-            + 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(&quot;Invoking showModalDialog and injecting URL &quot; + 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(&quot;onbeforeunload&quot;, pageUnloadDetector);
-    BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads.call(this, windowObject);
-};
-
-IEBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
-    LOG.debug(&quot;IEBrowserBot.pollForLoad: &quot; + 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(&quot;pollForLoad UNLOADING (&quot; + marker + &quot;): caught exception while firing events on unloading page: &quot; + this.pageLoadError.message);
-            this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
-            this.pageLoadError = null;
-            return;
-        } else if (((this.pageLoadError.message == &quot;Permission denied&quot;) || (/^Access is denied/.test(this.pageLoadError.message)))
-                &amp;&amp; this.permDeniedCount[marker]++ &lt; 8) {
-            if (this.permDeniedCount[marker] &gt; 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 &amp; !canAccessThisWindow) {
-                    LOG.debug(&quot;pollForLoad (&quot; + marker + &quot;) ABORTING: &quot; + this.pageLoadError.message + &quot; (&quot; + this.permDeniedCount[marker] + &quot;), but the currently selected window is fine&quot;);
-                    // returning without rescheduling
-                    this.pageLoadError = null;
-                    return;
-                }
-            }
-
-            var self = this;
-            LOG.debug(&quot;pollForLoad (&quot; + marker + &quot;): &quot; + this.pageLoadError.message + &quot; (&quot; + this.permDeniedCount[marker] + &quot;), waiting to see if it goes away&quot;);
-            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 == &quot;Permission denied&quot;) {
-                    // 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 &quot;Permission denied&quot;?
-                    LOG.debug(&quot;IEBrowserBot.windowClosed: got &quot; + de.message + &quot; (this.pageUnloading=&quot; + this.pageUnloading + &quot;); assuming window is unloading, probably not closed yet&quot;);
-                    return false;
-                } else {
-                    // this is probably one of those frame window situations
-                    LOG.debug(&quot;IEBrowserBot.windowClosed: couldn't read win.document, assume closed: &quot; + de.message + &quot; (this.pageUnloading=&quot; + this.pageUnloading + &quot;)&quot;);
-                    return true;
-                }
-            }
-        }
-        if (c == null) {
-            LOG.debug(&quot;IEBrowserBot.windowClosed: win.closed was null, assuming closed&quot;);
-            return true;
-        }
-        return c;
-    } catch (e) {
-        LOG.debug(&quot;IEBrowserBot._windowClosed: Got an exception trying to read win.closed; we'll have to take a guess!&quot;);
-
-        if (browserVersion.isHTA) {
-            if (e.message == &quot;Permission denied&quot;) {
-                // 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 == &quot;class&quot;) {
-        attributeName = &quot;className&quot;;
-    }
-    var elements = inDocument.getElementsByTagName(tagName);
-    for (var i = 0; i &lt; 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(&quot;http://&quot;) || url.startsWith(&quot;https://&quot;) || url.startsWith(&quot;/&quot;)) {
-            return originalOpen(url, windowName, windowFeatures, replaceFlag);
-        }
-
-        // Reduce the current path to the directory
-        var currentPath = windowToModify.location.pathname || &quot;/&quot;;
-        currentPath = currentPath.replace(/\/[^\/]*$/, &quot;/&quot;);
-
-        // Remove any leading &quot;./&quot; from the new url.
-        url = url.replace(/^\.\//, &quot;&quot;);
-
-        newUrl = currentPath + url;
-
-        var openedWindow = originalOpen(newUrl, windowName, windowFeatures, replaceFlag);
-        LOG.debug(&quot;window.open call intercepted; window ID (which you can use with selectWindow()) is \&quot;&quot; +  windowName + &quot;\&quot;&quot;);
-        if (windowName!=null) {
-            openedWindow[&quot;seleniumWindowName&quot;] = 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 &amp;&amp; savedEvent != null &amp;&amp; !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 = &quot;&quot;;
-        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
-    // &lt;a href=&quot;javascript:alert('foo'):&quot;&gt; triggers the onbeforeunload event, even thought the page won't unload
-    var pageUnloading = false;
-    var pageUnloadDetector = function() {
-        pageUnloading = true;
-    };
-    win.attachEvent(&quot;onbeforeunload&quot;, 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(&quot;onbeforeunload&quot;, pageUnloadDetector);
-
-        if (this._windowClosed(win)) {
-            return;
-        }
-
-        // Onchange event is not triggered automatically in IE.
-        if (isDefined(element.checked) &amp;&amp; wasChecked != element.checked) {
-            triggerEvent(element, 'change', true);
-        }
-
-    }
-    catch (e) {
-        // If the page is unloading, we may get a &quot;Permission denied&quot; or &quot;Unspecified error&quot;.
-        // Just ignore it, because the document may have unloaded.
-        if (pageUnloading) {
-            LOG.logHook = function() {
-            };
-            LOG.warn(&quot;Caught exception when firing events on unloading page: &quot; + e.message);
-            return;
-        }
-        throw e;
-    }
-};
+/*
+* Copyright 2004 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+*  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 &quot;AS IS&quot; 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 &quot;user-entry&quot; 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 = &quot;seleniumMarker&quot; + 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(&quot;Page load detected&quot;);
+        try {
+            if (elementOrWindow.location &amp;&amp; elementOrWindow.location.href) {
+                LOG.debug(&quot;Page load location=&quot; + elementOrWindow.location.href);
+            } else if (elementOrWindow.contentWindow &amp;&amp; elementOrWindow.contentWindow.location &amp;&amp; elementOrWindow.contentWindow.location.href) {
+                LOG.debug(&quot;Page load location=&quot; + elementOrWindow.contentWindow.location.href);
+            } else {
+                LOG.debug(&quot;Page load location unknown, current window location=&quot; + this.getCurrentWindow(true).location);
+            }
+        } catch (e) {
+            LOG.error(&quot;Caught an exception attempting to log location; this should get noticed soon!&quot;);
+            LOG.exception(e);
+            self.pageLoadError = e;
+            return;
+        }
+        self.newPageLoaded = true;
+    };
+
+    this.isNewPageLoaded = function() {
+        if (this.pageLoadError) {
+            LOG.error(&quot;isNewPageLoaded found an old pageLoadError&quot;);
+            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(&quot;browserName: &quot; + browserVersion.name);
+    LOG.debug(&quot;userAgent: &quot; + 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 &quot;do&quot; 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 &gt; 0);
+};
+
+BrowserBot.prototype.relayBotToRC = function(s) {
+    // DGF need to do this funny trick to see if we're in PI mode, because
+    // &quot;this&quot; might be the window, rather than the browserbot (e.g. during window.alert) 
+    var piMode = this.proxyInjectionMode;
+    if (!piMode) {
+        if (typeof(selenium) != &quot;undefined&quot;) {
+            piMode = selenium.browserbot &amp;&amp; selenium.browserbot.proxyInjectionMode;
+        }
+    }
+    if (piMode) {
+        this.relayToRC(&quot;selenium.&quot; + s);
+    }
+};
+
+BrowserBot.prototype.relayToRC = function(name) {
+        var object = eval(name);
+        var s = 'state:' + serializeObject(name, object) + &quot;\n&quot;;
+        sendToRC(s,&quot;state=true&quot;);
+}
+
+BrowserBot.prototype.resetPopups = function() {
+    this.recordedAlerts = [];
+    this.recordedConfirmations = [];
+    this.recordedPrompts = [];
+}
+
+BrowserBot.prototype.getNextAlert = function() {
+    var t = this.recordedAlerts.shift();
+    this.relayBotToRC(&quot;browserbot.recordedAlerts&quot;);
+    return t;
+};
+
+BrowserBot.prototype.hasConfirmations = function() {
+    return (this.recordedConfirmations.length &gt; 0);
+};
+
+BrowserBot.prototype.getNextConfirmation = function() {
+    var t = this.recordedConfirmations.shift();
+    this.relayBotToRC(&quot;browserbot.recordedConfirmations&quot;);
+    return t;
+};
+
+BrowserBot.prototype.hasPrompts = function() {
+    return (this.recordedPrompts.length &gt; 0);
+};
+
+BrowserBot.prototype.getNextPrompt = function() {
+    var t = this.recordedPrompts.shift();
+    this.relayBotToRC(&quot;browserbot.recordedPrompts&quot;);
+    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(&quot;triggerMouseEvent assumes setting screenX and screenY to 0 is ok&quot;);
+    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 &amp;&amp; !screenY &amp;&amp; !clientX &amp;&amp; !clientY &amp;&amp; !this.controlKeyDown &amp;&amp; !this.altKeyDown &amp;&amp; !this.shiftKeyDown &amp;&amp; !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 &quot;Object does not support this action or property&quot; 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(&quot;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&quot;);
+            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(&quot;modifyWindow: Window was closed!&quot;);
+        }
+        return null;
+    }
+    if (!this.proxyInjectionMode) {
+        LOG.debug('modifyWindow ' + this.uniqueId + &quot;:&quot; + 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 &amp;&amp; win.frames.length &amp;&amp; win.frames.length &gt; 0) {
+        for (var i = 0; i &lt; 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 &amp;&amp; target != &quot;null&quot;) {
+        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(&quot;index=&quot;) == 0) {
+        target = target.substr(6);
+        var frame = this.getCurrentWindow().frames[target];
+        if (frame == null) {
+            throw new SeleniumError(&quot;Not found: frames[&quot;+index+&quot;]&quot;);
+        }
+        if (!frame.document) {
+            throw new SeleniumError(&quot;frames[&quot;+index+&quot;] is not a frame&quot;);
+        }
+        this.currentWindow = frame;
+        this.isSubFrameSelected = true;
+    }
+    else if (target == &quot;relative=up&quot;) {
+        this.currentWindow = this.getCurrentWindow().parent;
+        this.isSubFrameSelected = (this._getFrameElement(this.currentWindow) != null);
+    } else if (target == &quot;relative=top&quot;) {
+        this.currentWindow = this.topFrame;
+        this.isSubFrameSelected = false;
+    } else {
+        var frame = this.findElement(target);
+        if (frame == null) {
+            throw new SeleniumError(&quot;Not found: &quot; + 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 &amp;&amp; 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 &amp;&amp; win.frames &amp;&amp; win.frames.length) {
+                for (var i = 0; i &lt; 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(&quot;Not a frame: &quot; + target);
+            }
+        }
+    }
+    // modifies the window
+    this.getCurrentWindow();
+};
+
+BrowserBot.prototype.doesThisFrameMatchFrameExpression = function(currentFrameString, target) {
+    var isDom = false;
+    if (target.indexOf(&quot;dom=&quot;) == 0) {
+        target = target.substr(4);
+        isDom = true;
+    } else if (target.indexOf(&quot;index=&quot;) == 0) {
+        target = &quot;frames[&quot; + target.substr(6) + &quot;]&quot;;
+        isDom = true;
+    }
+    var t;
+    try {
+        eval(&quot;t=&quot; + currentFrameString + &quot;.&quot; + 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(&quot;currentFrame=&quot; + currentFrameString);
+    if (target == &quot;relative=up&quot;) {
+        if (currentFrame.window.parent == autWindow) {
+            return true;
+        }
+        return false;
+    }
+    if (target == &quot;relative=top&quot;) {
+        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(&quot;openLocation newPageLoaded = false&quot;);
+    this.newPageLoaded = false;
+
+    this.setOpenLocation(win, target);
+};
+
+BrowserBot.prototype.openWindow = function(url, windowID) {
+    if (url != &quot;&quot;) {
+        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 &amp;&amp; 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, &quot;browserbot.recordedAlerts&quot;);
+    };
+
+    windowToModify.confirm = function(message) {
+        browserBot.recordedConfirmations.push(message);
+        var result = browserBot.nextConfirmResult;
+        browserBot.nextConfirmResult = true;
+        self.relayBotToRC.call(self, &quot;browserbot.recordedConfirmations&quot;);
+        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, &quot;browserbot.recordedPrompts&quot;);
+        return result;
+    };
+
+    // Keep a reference to all popup windows by name
+    // note that in IE the &quot;windowName&quot; 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(&quot;window.open call intercepted; window ID (which you can use with selectWindow()) is \&quot;&quot; +  windowName + &quot;\&quot;&quot;);
+        if (windowName!=null) {
+            openedWindow[&quot;seleniumWindowName&quot;] = 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 = &quot;this['&quot; + originalOpenReference + &quot;'] = this.open;&quot;;
+
+        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 += &quot;this.open = this['&quot; + newOpenReference + &quot;'];&quot;;
+            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(&quot;modifySeparateTestWindowToDetectPageLoads: no windowObject!&quot;);
+        return;
+    }
+    if (this._windowClosed(windowObject)) {
+        LOG.info(&quot;modifySeparateTestWindowToDetectPageLoads: windowObject was closed&quot;);
+        return;
+    }
+    var oldMarker = this.isPollingForLoad(windowObject);
+    if (oldMarker) {
+        LOG.debug(&quot;modifySeparateTestWindowToDetectPageLoads: already polling this window: &quot; + oldMarker);
+        return;
+    }
+
+    var marker = 'selenium' + new Date().getTime();
+    LOG.debug(&quot;Starting pollForLoad (&quot; + marker + &quot;): &quot; + 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 &amp;&amp; !htaSubFrame) {
+        LOG.debug(&quot;modifySeparateTestWindowToDetectPageLoads: this window is a frame; attaching a load listener&quot;);
+        addLoadListener(frameElement, this.recordPageLoad);
+        frameElement[marker] = true;
+        frameElement[&quot;frame&quot;+this.uniqueId] = marker;
+	LOG.debug(&quot;dgf this.uniqueId=&quot;+this.uniqueId);
+	LOG.debug(&quot;dgf marker=&quot;+marker);
+	LOG.debug(&quot;dgf frameElement['frame'+this.uniqueId]=&quot;+frameElement['frame'+this.uniqueId]);
+frameElement[this.uniqueId] = marker;
+LOG.debug(&quot;dgf frameElement[this.uniqueId]=&quot;+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 &quot;win&quot; 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 &quot;No such interface supported&quot; 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(&quot;_getFrameElement: frameElement=&quot;+frameElement); 
+    if (frameElement) {
+        LOG.debug(&quot;frameElement.name=&quot;+frameElement.name);
+    }
+    return frameElement;
+}
+
+BrowserBot.prototype._getFrameElementByName = function(name, doc, win) {
+    var frames;
+    var frame;
+    var i;
+    frames = doc.getElementsByTagName(&quot;iframe&quot;);
+    for (i = 0; i &lt; frames.length; i++) {
+        frame = frames[i];        
+        if (frame.name === name) {
+            return frame;
+        }
+    }
+    frames = doc.getElementsByTagName(&quot;frame&quot;);
+    for (i = 0; i &lt; 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(&quot;_getFrameElementByName couldn't find a frame or iframe; checking every element for the name &quot; + 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(&quot;pollForLoad original (&quot; + marker + &quot;): &quot; + originalHref);
+    try {
+        if (this._windowClosed(windowObject)) {
+            LOG.debug(&quot;pollForLoad WINDOW CLOSED (&quot; + marker + &quot;)&quot;);
+            delete this.pollingForLoad[marker];
+            return;
+        }
+
+        var isSamePage = this._isSamePage(windowObject, originalDocument, originalLocation, originalHref, marker);
+        var rs = this.getReadyState(windowObject, windowObject.document);
+
+        if (!isSamePage &amp;&amp; rs == 'complete') {
+            var currentHref = windowObject.location.href;
+            LOG.debug(&quot;pollForLoad FINISHED (&quot; + marker + &quot;): &quot; + rs + &quot; (&quot; + currentHref + &quot;)&quot;);
+            delete this.pollingForLoad[marker];
+            this._modifyWindow(windowObject);
+            var newMarker = this.isPollingForLoad(windowObject);
+            if (!newMarker) {
+                LOG.debug(&quot;modifyWindow didn't start new poller: &quot; + newMarker);
+                this.modifySeparateTestWindowToDetectPageLoads(windowObject);
+            }
+            newMarker = this.isPollingForLoad(windowObject);
+            var currentlySelectedWindow;
+            var currentlySelectedWindowMarker;
+            currentlySelectedWindow =this.getCurrentWindow(true);
+            currentlySelectedWindowMarker = currentlySelectedWindow[this.uniqueId];
+
+            LOG.debug(&quot;pollForLoad (&quot; + marker + &quot;) restarting &quot; + newMarker);
+            if (/(TestRunner-splash|Blank)\.html\?start=true$/.test(currentHref)) {
+                LOG.debug(&quot;pollForLoad Oh, it's just the starting page.  Never mind!&quot;);
+            } else if (currentlySelectedWindowMarker == newMarker) {
+                loadFunction(currentlySelectedWindow);
+            } else {
+                LOG.debug(&quot;pollForLoad page load detected in non-current window; ignoring (currentlySelected=&quot;+currentlySelectedWindowMarker+&quot;, detection in &quot;+newMarker+&quot;)&quot;);
+            }
+            return;
+        }
+        LOG.debug(&quot;pollForLoad continue (&quot; + marker + &quot;): &quot; + currentHref);
+        this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+    } catch (e) {
+        LOG.debug(&quot;Exception during pollForLoad; this should get noticed soon (&quot; + e.message + &quot;)!&quot;);
+        //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 &gt; 0) {
+        currentHref = currentHref.substring(0, currentHash);
+    }
+    var originalHash = originalHref.indexOf('#');
+    if (originalHash &gt; 0) {
+        originalHref = originalHref.substring(0, originalHash);
+    }
+    LOG.debug(&quot;_isSamePage: currentHref: &quot; + currentHref);
+    LOG.debug(&quot;_isSamePage: originalHref: &quot; + 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(&quot;_isSamePage: sameDoc: &quot; + sameDoc);
+    LOG.debug(&quot;_isSamePage: sameLoc: &quot; + sameLoc);
+    LOG.debug(&quot;_isSamePage: sameHref: &quot; + sameHref);
+    LOG.debug(&quot;_isSamePage: markedLoc: &quot; + markedLoc);
+
+    return sameDoc &amp;&amp; sameLoc &amp;&amp; sameHref &amp;&amp; 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 &amp;&amp; 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'
+                    &amp;&amp; typeof currentDocument.getElementById != 'undefined'
+                    &amp;&amp; ( currentDocument.getElementsByTagName('body')[0] != null
+                    || currentDocument.body != null )) {
+                if (windowObject.frameElement &amp;&amp; windowObject.location.href == &quot;about:blank&quot; &amp;&amp; windowObject.frameElement.src != &quot;about:blank&quot;) {
+                    LOG.info(&quot;getReadyState not loaded, frame location was about:blank, but frame src = &quot; + windowObject.frameElement.src);
+                    return null;
+                }
+                LOG.debug(&quot;getReadyState = windowObject.frames.length = &quot; + windowObject.frames.length);
+                for (var i = 0; i &lt; windowObject.frames.length; i++) {
+                    LOG.debug(&quot;i = &quot; + i);
+                    if (this.getReadyState(windowObject.frames[i], windowObject.frames[i].document) != 'complete') {
+                        LOG.debug(&quot;getReadyState aha! the nested frame &quot; + windowObject.frames[i].name + &quot; wasn't ready!&quot;);
+                        return null;
+                    }
+                }
+
+                rs = 'complete';
+            } else {
+                LOG.debug(&quot;pollForLoad readyState was null and DOM appeared to not be ready yet&quot;);
+            }
+        }
+    }
+    else if (rs == &quot;loading&quot; &amp;&amp; browserVersion.isIE) {
+        LOG.debug(&quot;pageUnloading = true!!!!&quot;);
+        this.pageUnloading = true;
+    }
+    LOG.debug(&quot;getReadyState returning &quot; + 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 &quot;reschedulePoller&quot;, 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(&quot;button&quot;);
+    var buttonName = doc.createTextNode(marker + &quot; - &quot; + windowObject.name);
+    button.appendChild(buttonName);
+    var tools = doc.getElementById(&quot;tools&quot;);
+    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(&quot;runScheduledPollers&quot;);
+    var oldPollers = this.windowPollers;
+    this.windowPollers = new Array();
+    for (var i = 0; i &lt; oldPollers.length; i++) {
+        oldPollers[i].call();
+    }
+    LOG.debug(&quot;runScheduledPollers DONE&quot;);
+};
+
+BrowserBot.prototype.isPollingForLoad = function(win) {
+    var marker;
+    var frameElement = this._getFrameElement(win);
+    var htaSubFrame = this._isHTASubFrame(win);
+    if (frameElement &amp;&amp; !htaSubFrame) {
+	marker = frameElement[&quot;frame&quot;+this.uniqueId];
+    } else {
+        marker = win[this.uniqueId];
+    }
+    if (!marker) {
+        LOG.debug(&quot;isPollingForLoad false, missing uniqueId &quot; + this.uniqueId + &quot;: &quot; + marker);
+        return false;
+    }
+    if (!this.pollingForLoad[marker]) {
+        LOG.debug(&quot;isPollingForLoad false, this.pollingForLoad[&quot; + marker + &quot;]: &quot; + this.pollingForLoad[marker]);
+        return false;
+    }
+    return marker;
+};
+
+BrowserBot.prototype.getWindowByName = function(windowName, doNotModify) {
+    LOG.debug(&quot;getWindowByName(&quot; + windowName + &quot;)&quot;);
+    // First look in the map of opened windows
+    var targetWindow = this.openedWindows[windowName];
+    if (!targetWindow) {
+        targetWindow = this.topWindow[windowName];
+    }
+    if (!targetWindow &amp;&amp; windowName == &quot;_blank&quot;) {
+        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(&quot;Window does not exist&quot;);
+    }
+    if (browserVersion.isHTA) {
+        try {
+            targetWindow.location.href;
+        } catch (e) {
+            targetWindow = window.open(&quot;&quot;, 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(&quot;getWindowNameByTitle(&quot; + windowTitle + &quot;)&quot;);
+
+    // 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) &amp;&amp;
+                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 &quot;&quot;;
+        }
+    } catch (e) {} // IE Perm denied
+
+    throw new SeleniumError(&quot;Could not find window with title &quot; + windowTitle);
+};
+
+BrowserBot.prototype.getCurrentWindow = function(doNotModify) {
+    if (this.proxyInjectionMode) {
+        return window;
+    }
+    var testWindow = this.currentWindow;
+    if (!doNotModify) {
+        this._modifyWindow(testWindow);
+        LOG.debug(&quot;getCurrentWindow newPageLoaded = false&quot;);
+        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 &amp;&amp; testWindow.parent.frames &amp;&amp; testWindow.parent.frames.length) {
+            for (var i = 0; i &lt; testWindow.parent.frames.length; i++) {
+                if (testWindow.parent.frames[i] == testWindow) {
+                    missing = false;
+                    break;
+                }
+            }
+        }
+        if (missing) {
+            LOG.warn(&quot;Current subframe appears to have closed; selecting top frame&quot;);
+            this.selectFrame(&quot;relative=top&quot;);
+            return this.getCurrentWindow(doNotModify);
+        }
+    } else if (this._windowClosed(testWindow)) {
+        var closedError = new SeleniumError(&quot;Current window or frame is closed!&quot;);
+        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(&quot;Unrecognised locator type: '&quot; + locatorType + &quot;'&quot;);
+        }
+        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) == &quot;string&quot;) {
+        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 &lt; 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(&quot;Element &quot; + locator + &quot; not found&quot;);
+    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 &amp;&amp; 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(&quot;*&quot;);
+
+    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 &gt; 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 = &quot;dom&quot;;
+
+/**
+ * Finds an element identified by the xpath expression. Expressions _must_
+ * begin with &quot;//&quot;.
+ */
+BrowserBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
+    // Trim any trailing &quot;/&quot;: 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+)=('([^\']+)'|&quot;([^\&quot;]+)&quot;)\]$/);
+    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\(\)=('([^\']+)'|&quot;([^\&quot;]+)&quot;)\]$/);
+    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 &amp;&amp; attributeName == &quot;class&quot;) {
+        attributeName = &quot;className&quot;;
+    }
+    var elements = inDocument.getElementsByTagName(tagName);
+    for (var i = 0; i &lt; 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 &lt; 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(&quot;Unknown namespace: &quot; + prefix + &quot;.&quot;);
+    }
+}
+
+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 &amp;&amp; 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 &amp;&amp; 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(&quot;xpath=&quot;) == 0) {
+        xpath = xpath.substring(6); 
+    }
+    if (xpath.indexOf(&quot;count(&quot;) == 0) {
+        // DGF we COULD just fix this up for the user, but we might get it wrong (parens?)
+        throw new SeleniumError(&quot;XPath count expressions must not be wrapped in count() function: &quot; + xpath);
+    }
+    
+    xpath=&quot;count(&quot;+xpath+&quot;)&quot;;
+
+    // Use document.evaluate() if it's available
+    if (this.allowNativeXpath &amp;&amp; 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 &amp;&amp; xpathResult.value) {
+        return xpathResult.value;
+    }
+    return 0;
+};
+
+/**
+ * Finds a link element with text matching the expression supplied. Expressions must
+ * begin with &quot;link:&quot;.
+ */
+BrowserBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWindow) {
+    var links = inDocument.getElementsByTagName('a');
+    for (var i = 0; i &lt; links.length; i++) {
+        var element = links[i];
+        if (PatternMatcher.matches(linkText, getText(element))) {
+            return element;
+        }
+    }
+    return null;
+};
+BrowserBot.prototype.locateElementByLinkText.prefix = &quot;link&quot;;
+
+/**
+ * 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(&quot;@&quot;);
+    var elementLocator = locator.slice(0, attributePos);
+    var attributeName = locator.slice(attributePos + 1);
+
+    // Find the element.
+    var element = this.findElement(elementLocator);
+
+    // Handle missing &quot;class&quot; attribute in IE.
+    if (browserVersion.isIE &amp;&amp; attributeName == &quot;class&quot;) {
+        attributeName = &quot;className&quot;;
+    }
+
+    // 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 &lt; element.options.length; i++) {
+        var option = element.options[i];
+        if (option.selected &amp;&amp; option != optionToSelect) {
+            option.selected = false;
+            changed = true;
+        }
+        else if (!option.selected &amp;&amp; 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(&quot;Not a multi-select&quot;);
+    }
+
+};
+
+BrowserBot.prototype.replaceText = function(element, stringValue) {
+    triggerEvent(element, 'focus', false);
+    triggerEvent(element, 'select', true);
+    var maxLengthAttr = element.getAttribute(&quot;maxLength&quot;);
+    var actualValue = stringValue;
+    if (maxLengthAttr != null) {
+        var maxLength = parseInt(maxLengthAttr);
+        if (stringValue.length &gt; maxLength) {
+            actualValue = stringValue.substr(0, maxLength);
+        }
+    }
+
+    if (getTagName(element) == &quot;body&quot;) {
+        if (element.ownerDocument &amp;&amp; element.ownerDocument.designMode) {
+            var designMode = new String(element.ownerDocument.designMode).toLowerCase();
+            if (designMode = &quot;on&quot;) {
+                // 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(&quot;var actuallySubmit = &quot;+marker+&quot;.onsubmit();&quot; +
+                &quot;if (actuallySubmit) { &quot; +
+                    marker+&quot;.submit(); &quot; +
+                    &quot;if (&quot;+marker+&quot;.target &amp;&amp; !/^_/.test(&quot;+marker+&quot;.target)) {&quot;+
+                        &quot;window.open('', &quot;+marker+&quot;.target);&quot;+
+                    &quot;}&quot;+
+                &quot;};&quot;+
+                marker+&quot;=null&quot;, 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 &amp;&amp; !/^_/.test(formElement.target)) {
+                    this.browserbot.openWindow('', formElement.target);
+                }
+            }
+        }
+    } else {
+        formElement.submit();
+    }
+}
+
+BrowserBot.prototype.clickElement = function(element, clientX, clientY) {
+       this._fireEventOnElement(&quot;click&quot;, element, clientX, clientY);
+};
+
+BrowserBot.prototype.doubleClickElement = function(element, clientX, clientY) {
+       this._fireEventOnElement(&quot;dblclick&quot;, element, clientX, clientY);
+};
+
+BrowserBot.prototype._modifyElementTarget = function(element) {
+    if (element.target) {
+        if (element.target == &quot;_blank&quot; || /^selenium_blank/.test(element.target) ) {
+            var tagName = getTagName(element);
+            if (tagName == &quot;a&quot; || tagName == &quot;form&quot;) {
+                var newTarget = &quot;selenium_blank&quot; + Math.round(100000 * Math.random());
+                LOG.warn(&quot;Link has target '_blank', which is not supported in Selenium!  Randomizing target to be: &quot; + 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 == &quot;_top&quot;) {
+        return this.topFrame;
+    } else if (target == &quot;_parent&quot;) {
+        return this.getCurrentWindow().parent;
+    } else if (target == &quot;_blank&quot;) {
+        // TODO should this set cleverer window defaults?
+        return this.getCurrentWindow().open('', '_blank');
+    }
+    var frameElement = this.findElementBy(&quot;implicit&quot;, 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(&quot;Couldn't access document.body.  Is this HTML page fully loaded?&quot;);
+    }
+    return getText(this.getDocument().body);
+};
+
+BrowserBot.prototype.getAllButtons = function() {
+    var elements = this.getDocument().getElementsByTagName('input');
+    var result = [];
+
+    for (var i = 0; i &lt; 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 &lt; 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 &lt; 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(&quot;window.close();&quot;);
+    }
+};
+
+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(&quot;Unrecognised element-filter type: '&quot; + filterType + &quot;'&quot;);
+    }
+
+    return filterFunction(filter, elements);
+};
+
+BrowserBot.filterFunctions = {};
+
+BrowserBot.filterFunctions.name = function(name, elements) {
+    var selectedElements = [];
+    for (var i = 0; i &lt; 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 &lt; 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 &lt; 0) {
+        throw new SeleniumError(&quot;Illegal Index: &quot; + index);
+    }
+    if (elements.length &lt;= index) {
+        throw new SeleniumError(&quot;Index out of range: &quot; + 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 = &quot;about:blank&quot;;
+    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 (&quot;about:blank&quot; != win.location.href) {
+        var startLoc = parseUrl(win.location.href);
+        startLoc.hash = null;
+        var startUrl = reassembleLocation(startLoc);
+    }
+    LOG.debug(&quot;startUrl=&quot;+startUrl);
+    LOG.debug(&quot;win.location.href=&quot;+win.location.href);
+    LOG.debug(&quot;loc=&quot;+loc);
+    if (startUrl == loc) {
+        LOG.debug(&quot;opening exact same location&quot;);
+        this.refresh();
+    } else {
+        LOG.debug(&quot;locations differ&quot;);
+        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 &gt; 4) {
+        if (this.isSubFrameSelected) {
+            LOG.warn(&quot;Current subframe appears to have closed; selecting top frame&quot;);
+            this.selectFrame(&quot;relative=top&quot;);
+            return this.getCurrentWindow(doNotModify);
+        } else {
+            var closedError = new SeleniumError(&quot;Current window or frame is closed!&quot;);
+            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 = &quot;&amp;runInterval=&quot; + runOptions.runInterval;
+        }
+            
+        var testRunnerURL = &quot;TestRunner.html?auto=true&amp;singletest=&quot; 
+            + escape(browserBot.modalDialogTest)
+            + &quot;&amp;autoURL=&quot; 
+            + 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(&quot;Invoking showModalDialog and injecting URL &quot; + 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(&quot;onbeforeunload&quot;, pageUnloadDetector);
+    BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads.call(this, windowObject);
+};
+
+IEBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
+    LOG.debug(&quot;IEBrowserBot.pollForLoad: &quot; + 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(&quot;pollForLoad UNLOADING (&quot; + marker + &quot;): caught exception while firing events on unloading page: &quot; + this.pageLoadError.message);
+            this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+            this.pageLoadError = null;
+            return;
+        } else if (((this.pageLoadError.message == &quot;Permission denied&quot;) || (/^Access is denied/.test(this.pageLoadError.message)))
+                &amp;&amp; this.permDeniedCount[marker]++ &lt; 8) {
+            if (this.permDeniedCount[marker] &gt; 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 &amp; !canAccessThisWindow) {
+                    LOG.debug(&quot;pollForLoad (&quot; + marker + &quot;) ABORTING: &quot; + this.pageLoadError.message + &quot; (&quot; + this.permDeniedCount[marker] + &quot;), but the currently selected window is fine&quot;);
+                    // returning without rescheduling
+                    this.pageLoadError = null;
+                    return;
+                }
+            }
+
+            var self = this;
+            LOG.debug(&quot;pollForLoad (&quot; + marker + &quot;): &quot; + this.pageLoadError.message + &quot; (&quot; + this.permDeniedCount[marker] + &quot;), waiting to see if it goes away&quot;);
+            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 == &quot;Permission denied&quot;) {
+                    // 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 &quot;Permission denied&quot;?
+                    LOG.debug(&quot;IEBrowserBot.windowClosed: got &quot; + de.message + &quot; (this.pageUnloading=&quot; + this.pageUnloading + &quot;); assuming window is unloading, probably not closed yet&quot;);
+                    return false;
+                } else {
+                    // this is probably one of those frame window situations
+                    LOG.debug(&quot;IEBrowserBot.windowClosed: couldn't read win.document, assume closed: &quot; + de.message + &quot; (this.pageUnloading=&quot; + this.pageUnloading + &quot;)&quot;);
+                    return true;
+                }
+            }
+        }
+        if (c == null) {
+            LOG.debug(&quot;IEBrowserBot.windowClosed: win.closed was null, assuming closed&quot;);
+            return true;
+        }
+        return c;
+    } catch (e) {
+        LOG.debug(&quot;IEBrowserBot._windowClosed: Got an exception trying to read win.closed; we'll have to take a guess!&quot;);
+
+        if (browserVersion.isHTA) {
+            if (e.message == &quot;Permission denied&quot;) {
+                // 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 == &quot;class&quot;) {
+        attributeName = &quot;className&quot;;
+    }
+    var elements = inDocument.getElementsByTagName(tagName);
+    for (var i = 0; i &lt; 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(&quot;http://&quot;) || url.startsWith(&quot;https://&quot;) || url.startsWith(&quot;/&quot;)) {
+            return originalOpen(url, windowName, windowFeatures, replaceFlag);
+        }
+
+        // Reduce the current path to the directory
+        var currentPath = windowToModify.location.pathname || &quot;/&quot;;
+        currentPath = currentPath.replace(/\/[^\/]*$/, &quot;/&quot;);
+
+        // Remove any leading &quot;./&quot; from the new url.
+        url = url.replace(/^\.\//, &quot;&quot;);
+
+        newUrl = currentPath + url;
+
+        var openedWindow = originalOpen(newUrl, windowName, windowFeatures, replaceFlag);
+        LOG.debug(&quot;window.open call intercepted; window ID (which you can use with selectWindow()) is \&quot;&quot; +  windowName + &quot;\&quot;&quot;);
+        if (windowName!=null) {
+            openedWindow[&quot;seleniumWindowName&quot;] = 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 &amp;&amp; savedEvent != null &amp;&amp; !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 = &quot;&quot;;
+        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
+    // &lt;a href=&quot;javascript:alert('foo'):&quot;&gt; triggers the onbeforeunload event, even thought the page won't unload
+    var pageUnloading = false;
+    var pageUnloadDetector = function() {
+        pageUnloading = true;
+    };
+    win.attachEvent(&quot;onbeforeunload&quot;, 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(&quot;onbeforeunload&quot;, pageUnloadDetector);
+
+        if (this._windowClosed(win)) {
+            return;
+        }
+
+        // Onchange event is not triggered automatically in IE.
+        if (isDefined(element.checked) &amp;&amp; wasChecked != element.checked) {
+            triggerEvent(element, 'change', true);
+        }
+
+    }
+    catch (e) {
+        // If the page is unloading, we may get a &quot;Permission denied&quot; or &quot;Unspecified error&quot;.
+        // Just ignore it, because the document may have unloaded.
+        if (pageUnloading) {
+            LOG.logHook = function() {
+            };
+            LOG.warn(&quot;Caught exception when firing events on unloading page: &quot; + e.message);
+            return;
+        }
+        throw e;
+    }
+};</diff>
      <filename>testframework/selenium/scripts/selenium-browserbot.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,377 +1,377 @@
-/*
-* Copyright 2004 ThoughtWorks, Inc
-*
-*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-*  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 &quot;AS IS&quot; 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 &quot;seleniumApi&quot; is an instance of the Selenium object, defined in selenium-api.js.
-//
-//   - a &quot;Method&quot; 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 &quot;Block&quot; is a function that has been bound to a target object, so can be called invoked directly
-//     (or with a null target)
-//
-//   - &quot;CommandHandler&quot; 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 &quot;result&quot; 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] == &quot;is&quot;);
-                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 + &quot;AndWait&quot;, 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 &quot;assert&quot; prefix, and halt on failure.
-                var assertName = functionName;
-                this.registerAssert(assertName, assertBlock, true);
-
-                // Register the assert with the &quot;verify&quot; prefix, and do not halt on failure.
-                var verifyName = &quot;verify&quot; + 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 &quot;predicate&quot; 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, &quot;Actual value '&quot; + accessorResult + &quot;' did match '&quot; + value + &quot;'&quot;);
-            } else {
-                return new PredicateResult(false, &quot;Actual value '&quot; + accessorResult + &quot;' did not match '&quot; + value + &quot;'&quot;);
-            }
-        };
-    },
-
-    _predicateForNoArgAccessor: function(accessBlock) {
-        // Given a (no-arg) accessor function getBlah(),
-        // return a &quot;predicate&quot; 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, &quot;Actual value '&quot; + accessorResult + &quot;' did match '&quot; + value + &quot;'&quot;);
-            } else {
-                return new PredicateResult(false, &quot;Actual value '&quot; + accessorResult + &quot;' did not match '&quot; + value + &quot;'&quot;);
-            }
-        };
-    },
-
-    _predicateForBooleanAccessor: function(accessBlock) {
-        // Given a boolean accessor function isBlah(),
-        // return a &quot;predicate&quot; equivalient to isBlah() that
-        // returns an appropriate PredicateResult value.
-        return function() {
-            var accessorResult;
-            if (arguments.length &gt; 2) throw new SeleniumError(&quot;Too many arguments! &quot; + 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, &quot;true&quot;);
-            } else {
-                return new PredicateResult(false, &quot;false&quot;);
-            }
-        };
-    },
-
-    _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] + &quot;NotPresent&quot;;
-        }
-        return &quot;Not&quot; + 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(&quot;assert&quot; + baseName, assertBlock, true);
-        this.registerAssert(&quot;verify&quot; + baseName, assertBlock, false);
-
-        var invertedPredicateBlock = this._invertPredicate(predicateBlock);
-        var negativeassertBlock = this.createAssertionFromPredicate(invertedPredicateBlock);
-        this.registerAssert(&quot;assert&quot; + this._invertPredicateName(baseName), negativeassertBlock, true);
-        this.registerAssert(&quot;verify&quot; + 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(&quot;waitFor&quot; + baseName, waitForActionBlock, false, true);
-        this.registerAction(&quot;waitFor&quot; + this._invertPredicateName(baseName), waitForNotActionBlock, false, true);
-        //TODO decide remove &quot;waitForNot.*Present&quot; action name or not
-        //for the back compatiblity issues we still make waitForNot.*Present availble
-        this.registerAction(&quot;waitForNot&quot; + 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(&quot;store&quot; + 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, &quot;action&quot;, 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 &amp;&amp; (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 &amp;&amp; this.wait) {
-        terminationCondition = seleniumApi.makePageLoadCondition();
-    }
-    return new ActionResult(terminationCondition);
-};
-
-function ActionResult(terminationCondition) {
-    this.terminationCondition = terminationCondition;
-}
-
-function AccessorHandler(accessBlock) {
-    this.accessBlock = accessBlock;
-    CommandHandler.call(this, &quot;accessor&quot;, 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, &quot;assert&quot;, 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;
-}
-
+/*
+* Copyright 2004 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+*  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 &quot;AS IS&quot; 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 &quot;seleniumApi&quot; is an instance of the Selenium object, defined in selenium-api.js.
+//
+//   - a &quot;Method&quot; 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 &quot;Block&quot; is a function that has been bound to a target object, so can be called invoked directly
+//     (or with a null target)
+//
+//   - &quot;CommandHandler&quot; 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 &quot;result&quot; 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] == &quot;is&quot;);
+                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 + &quot;AndWait&quot;, 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 &quot;assert&quot; prefix, and halt on failure.
+                var assertName = functionName;
+                this.registerAssert(assertName, assertBlock, true);
+
+                // Register the assert with the &quot;verify&quot; prefix, and do not halt on failure.
+                var verifyName = &quot;verify&quot; + 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 &quot;predicate&quot; 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, &quot;Actual value '&quot; + accessorResult + &quot;' did match '&quot; + value + &quot;'&quot;);
+            } else {
+                return new PredicateResult(false, &quot;Actual value '&quot; + accessorResult + &quot;' did not match '&quot; + value + &quot;'&quot;);
+            }
+        };
+    },
+
+    _predicateForNoArgAccessor: function(accessBlock) {
+        // Given a (no-arg) accessor function getBlah(),
+        // return a &quot;predicate&quot; 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, &quot;Actual value '&quot; + accessorResult + &quot;' did match '&quot; + value + &quot;'&quot;);
+            } else {
+                return new PredicateResult(false, &quot;Actual value '&quot; + accessorResult + &quot;' did not match '&quot; + value + &quot;'&quot;);
+            }
+        };
+    },
+
+    _predicateForBooleanAccessor: function(accessBlock) {
+        // Given a boolean accessor function isBlah(),
+        // return a &quot;predicate&quot; equivalient to isBlah() that
+        // returns an appropriate PredicateResult value.
+        return function() {
+            var accessorResult;
+            if (arguments.length &gt; 2) throw new SeleniumError(&quot;Too many arguments! &quot; + 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, &quot;true&quot;);
+            } else {
+                return new PredicateResult(false, &quot;false&quot;);
+            }
+        };
+    },
+
+    _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] + &quot;NotPresent&quot;;
+        }
+        return &quot;Not&quot; + 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(&quot;assert&quot; + baseName, assertBlock, true);
+        this.registerAssert(&quot;verify&quot; + baseName, assertBlock, false);
+
+        var invertedPredicateBlock = this._invertPredicate(predicateBlock);
+        var negativeassertBlock = this.createAssertionFromPredicate(invertedPredicateBlock);
+        this.registerAssert(&quot;assert&quot; + this._invertPredicateName(baseName), negativeassertBlock, true);
+        this.registerAssert(&quot;verify&quot; + 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(&quot;waitFor&quot; + baseName, waitForActionBlock, false, true);
+        this.registerAction(&quot;waitFor&quot; + this._invertPredicateName(baseName), waitForNotActionBlock, false, true);
+        //TODO decide remove &quot;waitForNot.*Present&quot; action name or not
+        //for the back compatiblity issues we still make waitForNot.*Present availble
+        this.registerAction(&quot;waitForNot&quot; + 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(&quot;store&quot; + 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, &quot;action&quot;, 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 &amp;&amp; (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 &amp;&amp; this.wait) {
+        terminationCondition = seleniumApi.makePageLoadCondition();
+    }
+    return new ActionResult(terminationCondition);
+};
+
+function ActionResult(terminationCondition) {
+    this.terminationCondition = terminationCondition;
+}
+
+function AccessorHandler(accessBlock) {
+    this.accessBlock = accessBlock;
+    CommandHandler.call(this, &quot;accessor&quot;, 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, &quot;assert&quot;, 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;
+}
+</diff>
      <filename>testframework/selenium/scripts/selenium-commandhandlers.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,175 +1,175 @@
-/*
-* Copyright 2004 ThoughtWorks, Inc
-*
-*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-*  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 &quot;AS IS&quot; 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(&quot;currentTest.start()&quot;);
-        this.continueTest();
-    },
-
-    continueTest : function() {
-        /**
-         * Select the next command and continue the test.
-         */
-        LOG.debug(&quot;currentTest.continueTest() - acquire the next command&quot;);
-        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(&quot;currentTest.continueTestAtCurrentCommand()&quot;);
-        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 &lt; 0) {
-            // Pause: enable the &quot;next/continue&quot; button
-            this.pause();
-        } else {
-            window.setTimeout(fnBind(this.resume, this), delay);
-        }
-    },
-
-    resume: function() {
-        /**
-         * Select the next command and continue the test.
-         */
-        LOG.debug(&quot;currentTest.resume() - actually execute&quot;);
-        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(&quot;Executing: |&quot; + command.command + &quot; | &quot; + command.target + &quot; | &quot; + command.value + &quot; |&quot;);
-
-        var handler = this.commandFactory.getCommandHandler(command.command);
-        if (handler == null) {
-            throw new SeleniumError(&quot;Unknown command: '&quot; + command.command + &quot;'&quot;);
-        }
-
-        command.target = selenium.preprocessParameter(command.target);
-        command.value = selenium.preprocessParameter(command.value);
-        LOG.debug(&quot;Command found, going to execute &quot; + command.command);
-        this.result = handler.execute(selenium, command);
-        
-
-        this.waitForCondition = this.result.terminationCondition;
-
-    },
-
-    _handleCommandError : function(e) {
-        if (!e.isSeleniumError) {
-            LOG.exception(e);
-            var msg = &quot;Selenium failure. Please report to the Selenium Users forum at http://forums.openqa.org, with error details from the log window.&quot;;
-            msg += &quot;  The error message is: &quot; + 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(&quot;currentTest.continueTestWhenConditionIsTrue()&quot;);
-        selenium.browserbot.runScheduledPollers();
-        try {
-            if (this.waitForCondition == null) {
-                LOG.debug(&quot;null condition; let's continueTest()&quot;);
-                LOG.debug(&quot;Command complete&quot;);
-                this.commandComplete(this.result);
-                this.continueTest();
-            } else if (this.waitForCondition()) {
-                LOG.debug(&quot;condition satisfied; let's continueTest()&quot;);
-                this.waitForCondition = null;
-                LOG.debug(&quot;Command complete&quot;);
-                this.commandComplete(this.result);
-                this.continueTest();
-            } else {
-                //LOG.debug(&quot;waitForCondition was false; keep waiting!&quot;);
-                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;
-    }
-
-}
+/*
+* Copyright 2004 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+*  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 &quot;AS IS&quot; 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(&quot;currentTest.start()&quot;);
+        this.continueTest();
+    },
+
+    continueTest : function() {
+        /**
+         * Select the next command and continue the test.
+         */
+        LOG.debug(&quot;currentTest.continueTest() - acquire the next command&quot;);
+        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(&quot;currentTest.continueTestAtCurrentCommand()&quot;);
+        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 &lt; 0) {
+            // Pause: enable the &quot;next/continue&quot; button
+            this.pause();
+        } else {
+            window.setTimeout(fnBind(this.resume, this), delay);
+        }
+    },
+
+    resume: function() {
+        /**
+         * Select the next command and continue the test.
+         */
+        LOG.debug(&quot;currentTest.resume() - actually execute&quot;);
+        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(&quot;Executing: |&quot; + command.command + &quot; | &quot; + command.target + &quot; | &quot; + command.value + &quot; |&quot;);
+
+        var handler = this.commandFactory.getCommandHandler(command.command);
+        if (handler == null) {
+            throw new SeleniumError(&quot;Unknown command: '&quot; + command.command + &quot;'&quot;);
+        }
+
+        command.target = selenium.preprocessParameter(command.target);
+        command.value = selenium.preprocessParameter(command.value);
+        LOG.debug(&quot;Command found, going to execute &quot; + command.command);
+        this.result = handler.execute(selenium, command);
+        
+
+        this.waitForCondition = this.result.terminationCondition;
+
+    },
+
+    _handleCommandError : function(e) {
+        if (!e.isSeleniumError) {
+            LOG.exception(e);
+            var msg = &quot;Selenium failure. Please report to the Selenium Users forum at http://forums.openqa.org, with error details from the log window.&quot;;
+            msg += &quot;  The error message is: &quot; + 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(&quot;currentTest.continueTestWhenConditionIsTrue()&quot;);
+        selenium.browserbot.runScheduledPollers();
+        try {
+            if (this.waitForCondition == null) {
+                LOG.debug(&quot;null condition; let's continueTest()&quot;);
+                LOG.debug(&quot;Command complete&quot;);
+                this.commandComplete(this.result);
+                this.continueTest();
+            } else if (this.waitForCondition()) {
+                LOG.debug(&quot;condition satisfied; let's continueTest()&quot;);
+                this.waitForCondition = null;
+                LOG.debug(&quot;Command complete&quot;);
+                this.commandComplete(this.result);
+                this.continueTest();
+            } else {
+                //LOG.debug(&quot;waitForCondition was false; keep waiting!&quot;);
+                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;
+    }
+
+}</diff>
      <filename>testframework/selenium/scripts/selenium-executionloop.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,147 +1,147 @@
-/*
- * Copyright 2004 ThoughtWorks, Inc
- *
- *  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
- *  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 &quot;AS IS&quot; 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: &quot;info&quot;,
-
-    setLogLevelThreshold: function(logLevel) {
-        this.threshold = logLevel;
-        var logWindow = this.getLogWindow()
-        if (logWindow &amp;&amp; logWindow.setThresholdLevel) {
-            logWindow.setThresholdLevel(logLevel);
-        }
-        // NOTE: log messages will be discarded until the log window is
-        // fully loaded.
-    },
-
-    getLogWindow: function() {
-        if (this.logWindow &amp;&amp; this.logWindow.closed) {
-            this.logWindow = null;
-        }
-        return this.logWindow;
-    },
-    
-    openLogWindow: function() {
-        this.logWindow = window.open(
-            getDocumentBase(document) + &quot;SeleniumLog.html?startingThreshold=&quot;+this.threshold, &quot;SeleniumLog&quot;,
-            &quot;width=600,height=1000,bottom=0,right=0,status,scrollbars,resizable&quot;
-        );
-        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(&quot;warn&quot;, &quot;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.&quot;);
-            this.pendingMessages.push(pendingMessage);
-        }
-        return this.logWindow;
-    },
-    
-    show: function() {
-        if (! this.getLogWindow()) {
-            this.openLogWindow();
-        }
-        setTimeout(function(){LOG.error(&quot;Log window displayed.  Logging events will now be recorded to this window.&quot;);}, 500);
-    },
-
-    logHook: function(logLevel, message) {
-    },
-
-    log: function(logLevel, message) {
-        if (this.logLevels[logLevel] &lt; 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 &gt; 0) {
-                    logWindow.append(&quot;info: Appending missed logging messages&quot;, &quot;info&quot;);
-                    while (this.pendingMessages.length &gt; 0) {
-                        var msg = this.pendingMessages.shift();
-                        logWindow.append(msg.type + &quot;: &quot; + msg.msg, msg.type);
-                    }
-                    logWindow.append(&quot;info: Done appending missed logging messages&quot;, &quot;info&quot;);
-                }
-                logWindow.append(logLevel + &quot;: &quot; + 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(&quot;debug&quot;, message);
-    },
-
-    info: function(message) {
-       this.log(&quot;info&quot;, message);
-    },
-
-    warn: function(message) {
-       this.log(&quot;warn&quot;, message);
-    },
-
-    error: function(message) {
-       this.log(&quot;error&quot;, message);
-    },
-
-    exception: function(exception) {
-        this.error(&quot;Unexpected Exception: &quot; + extractExceptionMessage(exception));
-        this.error(&quot;Exception details: &quot; + describe(exception, ', '));
-    }
-
-};
-
-var LOG = new Logger();
-
-var LogMessage = function(type, msg) {
-    this.type = type;
-    this.msg = msg;
-}
+/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+ *  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 &quot;AS IS&quot; 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: &quot;info&quot;,
+
+    setLogLevelThreshold: function(logLevel) {
+        this.threshold = logLevel;
+        var logWindow = this.getLogWindow()
+        if (logWindow &amp;&amp; logWindow.setThresholdLevel) {
+            logWindow.setThresholdLevel(logLevel);
+        }
+        // NOTE: log messages will be discarded until the log window is
+        // fully loaded.
+    },
+
+    getLogWindow: function() {
+        if (this.logWindow &amp;&amp; this.logWindow.closed) {
+            this.logWindow = null;
+        }
+        return this.logWindow;
+    },
+    
+    openLogWindow: function() {
+        this.logWindow = window.open(
+            getDocumentBase(document) + &quot;SeleniumLog.html?startingThreshold=&quot;+this.threshold, &quot;SeleniumLog&quot;,
+            &quot;width=600,height=1000,bottom=0,right=0,status,scrollbars,resizable&quot;
+        );
+        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(&quot;warn&quot;, &quot;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.&quot;);
+            this.pendingMessages.push(pendingMessage);
+        }
+        return this.logWindow;
+    },
+    
+    show: function() {
+        if (! this.getLogWindow()) {
+            this.openLogWindow();
+        }
+        setTimeout(function(){LOG.error(&quot;Log window displayed.  Logging events will now be recorded to this window.&quot;);}, 500);
+    },
+
+    logHook: function(logLevel, message) {
+    },
+
+    log: function(logLevel, message) {
+        if (this.logLevels[logLevel] &lt; 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 &gt; 0) {
+                    logWindow.append(&quot;info: Appending missed logging messages&quot;, &quot;info&quot;);
+                    while (this.pendingMessages.length &gt; 0) {
+                        var msg = this.pendingMessages.shift();
+                        logWindow.append(msg.type + &quot;: &quot; + msg.msg, msg.type);
+                    }
+                    logWindow.append(&quot;info: Done appending missed logging messages&quot;, &quot;info&quot;);
+                }
+                logWindow.append(logLevel + &quot;: &quot; + 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(&quot;debug&quot;, message);
+    },
+
+    info: function(message) {
+       this.log(&quot;info&quot;, message);
+    },
+
+    warn: function(message) {
+       this.log(&quot;warn&quot;, message);
+    },
+
+    error: function(message) {
+       this.log(&quot;error&quot;, message);
+    },
+
+    exception: function(exception) {
+        this.error(&quot;Unexpected Exception: &quot; + extractExceptionMessage(exception));
+        this.error(&quot;Exception details: &quot; + describe(exception, ', '));
+    }
+
+};
+
+var LOG = new Logger();
+
+var LogMessage = function(type, msg) {
+    this.type = type;
+    this.msg = msg;
+}</diff>
      <filename>testframework/selenium/scripts/selenium-logging.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,571 +1,571 @@
-/*
-* Copyright 2005 ThoughtWorks, Inc
-*
-*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-*  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 &quot;AS IS&quot; 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 = &quot;#cfffcf&quot;;
-failColor = &quot;#ffcfcf&quot;;
-errorColor = &quot;#ffffff&quot;;
-workingColor = &quot;#DEE7EC&quot;;
-doneColor = &quot;#FFFFCC&quot;;
-
-var injectedSessionId;
-var cmd1 = document.createElement(&quot;div&quot;);
-var cmd2 = document.createElement(&quot;div&quot;);
-var cmd3 = document.createElement(&quot;div&quot;);
-var cmd4 = document.createElement(&quot;div&quot;);
-
-var postResult = &quot;START&quot;;
-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(&quot;debugMode&quot;);
-    },
-
-    getContinue: function() {
-        return this._getQueryParameter(&quot;continue&quot;);
-    },
-
-    getDriverUrl: function() {
-        return this._getQueryParameter(&quot;driverUrl&quot;);
-    },
-
-    getSessionId: function() {
-        return this._getQueryParameter(&quot;sessionId&quot;);
-    },
-
-    _acquireQueryString: function () {
-        if (this.queryString) return;
-        if (browserVersion.isHTA) {
-            var args = this._extractArgs();
-            if (args.length &lt; 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(&quot;commandList&quot;) != null) {
-        document.getElementById(&quot;commandList&quot;).appendChild(cmd4);
-        document.getElementById(&quot;commandList&quot;).appendChild(cmd3);
-        document.getElementById(&quot;commandList&quot;).appendChild(cmd2);
-        document.getElementById(&quot;commandList&quot;).appendChild(cmd1);
-    }
-
-    var doContinue = runOptions.getContinue();
-    if (doContinue != null) postResult = &quot;OK&quot;;
-
-    currentTest.start();
-}
-
-function buildDriverUrl() {
-    var driverUrl = runOptions.getDriverUrl();
-    if (driverUrl != null) {
-        return driverUrl;
-    }
-    var s = window.location.href
-    var slashPairOffset = s.indexOf(&quot;//&quot;) + &quot;//&quot;.length
-    var pathSlashOffset = s.substring(slashPairOffset).indexOf(&quot;/&quot;)
-    return s.substring(0, slashPairOffset + pathSlashOffset) + &quot;/selenium-server/driver/&quot;;
-    //return &quot;http://localhost&quot; + uniqueId + &quot;/selenium-server/driver/&quot;;
-}
-
-function logToRc(logLevel, message) {
-    if (debugMode) {
-        if (logLevel == null) {
-            logLevel = &quot;debug&quot;;
-        }
-        sendToRCAndForget(&quot;logLevel=&quot; + logLevel + &quot;:&quot; + message.replace(/[\n\r\015]/g, &quot; &quot;) + &quot;\n&quot;, &quot;logging=true&quot;);
-    }
-}
-
-function serializeString(name, s) {
-    return name + &quot;=unescape(\&quot;&quot; + escape(s) + &quot;\&quot;);&quot;;
-}
-
-function serializeObject(name, x)
-{
-    var s = '';
-
-    if (isArray(x))
-    {
-        s = name + &quot;=new Array(); &quot;;
-        var len = x[&quot;length&quot;];
-        for (var j = 0; j &lt; len; j++)
-        {
-            s += serializeString(name + &quot;[&quot; + j + &quot;]&quot;, x[j]);
-        }
-    }
-    else if (typeof x == &quot;string&quot;)
-    {
-        s = serializeString(name, x);
-    }
-    else
-    {
-        throw &quot;unrecognized object not encoded: &quot; + name + &quot;(&quot; + x + &quot;)&quot;;
-    }
-    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 = &quot;&quot;;
-        if (postResult == &quot;START&quot;) {
-            urlParms += &quot;seleniumStart=true&quot;;
-        }
-        this.xmlHttpForCommandsAndResults = XmlHttp.create();
-        sendToRC(postResult, urlParms, fnBind(this._HandleHttpResponse, this), this.xmlHttpForCommandsAndResults);
-    },
-
-    commandStarted : function(command) {
-        this.commandNode = document.createElement(&quot;div&quot;);
-        var cmdText = command.command + '(';
-        if (command.target != null &amp;&amp; command.target != &quot;&quot;) {
-            cmdText += command.target;
-            if (command.value != null &amp;&amp; command.value != &quot;&quot;) {
-                cmdText += ', ' + command.value;
-            }
-        }
-        cmdText += &quot;)&quot;;
-        if (cmdText.length &gt;40) {
-            cmdText = cmdText.substring(0,40);
-            cmdText += &quot;...&quot;;
-        }
-        this.commandNode.appendChild(document.createTextNode(cmdText));
-        this.commandNode.style.backgroundColor = workingColor;
-        if (document.getElementById(&quot;commandList&quot;) != null) {
-            document.getElementById(&quot;commandList&quot;).removeChild(cmd1);
-            document.getElementById(&quot;commandList&quot;).removeChild(cmd2);
-            document.getElementById(&quot;commandList&quot;).removeChild(cmd3);
-            document.getElementById(&quot;commandList&quot;).removeChild(cmd4);
-            cmd4 = cmd3;
-            cmd3 = cmd2;
-            cmd2 = cmd1;
-            cmd1 = this.commandNode;
-            document.getElementById(&quot;commandList&quot;).appendChild(cmd4);
-            document.getElementById(&quot;commandList&quot;).appendChild(cmd3);
-            document.getElementById(&quot;commandList&quot;).appendChild(cmd2);
-            document.getElementById(&quot;commandList&quot;).appendChild(cmd1);
-        }
-    },
-
-    commandComplete : function(result) {
-
-        if (result.failed) {
-            if (postResult == &quot;CONTINUATION&quot;) {
-                currentTest.aborted = true;
-            }
-            postResult = result.failureMessage;
-            this.commandNode.title = result.failureMessage;
-            this.commandNode.style.backgroundColor = failColor;
-        } else if (result.passed) {
-            postResult = &quot;OK&quot;;
-            this.commandNode.style.backgroundColor = passColor;
-        } else {
-            if (result.result == null) {
-                postResult = &quot;OK&quot;;
-            } else {
-                var actualResult = result.result;
-                actualResult = selArrayToString(actualResult);
-                postResult = &quot;OK,&quot; + actualResult;
-            }
-            this.commandNode.style.backgroundColor = doneColor;
-        }
-    },
-
-    commandError : function(message) {
-        postResult = &quot;ERROR: &quot; + message;
-        this.commandNode.style.backgroundColor = errorColor;
-        this.commandNode.title = message;
-    },
-
-    testComplete : function() {
-        window.status = &quot;Selenium Tests Complete, for this Test&quot;
-        // Continue checking for new results
-        this.continueTest();
-        postResult = &quot;START&quot;;
-    },
-
-    _HandleHttpResponse : function() {
-        // When request is completed
-        if (this.xmlHttpForCommandsAndResults.readyState == 4) {
-            // OK
-            if (this.xmlHttpForCommandsAndResults.status == 200) {
-            	if (this.xmlHttpForCommandsAndResults.responseText==&quot;&quot;) {
-                    LOG.error(&quot;saw blank string xmlHttpForCommandsAndResults.responseText&quot;);
-                    return;
-                }
-                var command = this._extractCommand(this.xmlHttpForCommandsAndResults);
-                this.currentCommand = command;
-                this.continueTestAtCurrentCommand();
-            }
-            // Not OK 
-            else {
-                var s = 'xmlHttp returned: ' + this.xmlHttpForCommandsAndResults.status + &quot;: &quot; + 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(&quot;^(.*?)\n((.|[\r\n])*)&quot;);
-            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 &gt; 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, &quot;%20&quot;);
-        // strip trailing spaces
-        var processed = processed.replace(/\s+$/, &quot;&quot;);
-        var vars = processed.split(&quot;&amp;&quot;);
-        var cmdArgs = new Object();
-        for (var i = 0; i &lt; vars.length; i++) {
-            var pair = vars[i].split(&quot;=&quot;);
-            cmdArgs[pair[0]] = pair[1];
-        }
-        var cmd = cmdArgs['cmd'];
-        var arg1 = cmdArgs['1'];
-        if (null == arg1) arg1 = &quot;&quot;;
-        arg1 = decodeURIComponent(arg1);
-        var arg2 = cmdArgs['2'];
-        if (null == arg2) arg2 = &quot;&quot;;
-        arg2 = decodeURIComponent(arg2);
-        if (cmd == null) {
-            throw new Error(&quot;Bad command request: &quot; + 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() + &quot;?&quot;
-    if (urlParms) {
-        url += urlParms;
-    }
-    url = addUrlParams(url);
-    url += &quot;&amp;sequenceNumber=&quot; + 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(&quot;*********** &quot; + command.command + &quot; | &quot; + command.target + &quot; | &quot; + command.value);
-                        if (command.command == 'retryLast') {
-                            retry = true;
-                        }
-                    }
-                    if (retry) {
-                        setTimeout(fnBind(function() {
-                            sendToRC(&quot;RETRY&quot;, &quot;retry=true&quot;, callback, xmlHttpObject, async);
-                        }, this), 1000);
-                    } else {
-                        callback();
-                    }
-                }
-            }
-        }
-    }
-    
-    var postedData = &quot;postedData=&quot; + encodeURIComponent(dataToBePosted);
-
-    //xmlHttpObject.setRequestHeader(&quot;Content-Type&quot;, &quot;application/x-www-form-urlencoded&quot;);
-    xmlHttpObject.open(&quot;POST&quot;, url, async);
-    xmlHttpObject.onreadystatechange = wrappingCallback;
-    xmlHttpObject.send(postedData);
-    return null;
-}
-
-function addUrlParams(url) {
-    return url + &quot;&amp;localFrameAddress=&quot; + (proxyInjectionMode ? makeAddressToAUTFrame() : &quot;top&quot;)
-    + getSeleniumWindowNameURLparameters()
-    + &quot;&amp;uniqueId=&quot; + 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 = &quot;http:&quot;;
-        if (window.location.protocol == &quot;https:&quot;) {
-            // DGF if we're in HTTPS, use another HTTPS url to avoid security warning
-            protocol = &quot;https:&quot;;
-        }
-        // 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 + &quot;//&quot; + Math.floor(Math.random()* 16 + 1) + &quot;.selenium.doesnotexist/selenium-server/driver/?&quot; + urlParams;
-    } else {
-        url = buildDriverUrl() + &quot;?&quot; + urlParams;
-    }
-    url = addUrlParams(url);
-    
-    var method = &quot;GET&quot;;
-    if (method == &quot;POST&quot;) {
-        // 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(&quot;form&quot;);
-        loggingForm.method = &quot;POST&quot;;
-        loggingForm.action = url;
-        loggingForm.target = &quot;seleniumLoggingFrame&quot;;
-        var postedDataInput = document.createElement(&quot;input&quot;);
-        postedDataInput.type = &quot;hidden&quot;;
-        postedDataInput.name = &quot;postedData&quot;;
-        postedDataInput.value = dataToBePosted;
-        loggingForm.appendChild(postedDataInput);
-        document.body.appendChild(loggingForm);
-        loggingForm.submit();
-        document.body.removeChild(loggingForm);
-    } else {
-        var postedData = &quot;&amp;postedData=&quot; + encodeURIComponent(dataToBePosted);
-        var scriptTag = document.createElement(&quot;script&quot;);
-        scriptTag.src = url + postedData;
-        document.body.appendChild(scriptTag);
-        document.body.removeChild(scriptTag);
-    }
-}
-
-function buildDriverParams() {
-    var params = &quot;&quot;;
-
-    var sessionId = runOptions.getSessionId();
-    if (sessionId == undefined) {
-        sessionId = injectedSessionId;
-    }
-    if (sessionId != undefined) {
-        params = params + &quot;&amp;sessionId=&quot; + sessionId;
-    }
-    return params;
-}
-
-function preventBrowserCaching() {
-    var t = (new Date()).getTime();
-    return &quot;&amp;counterToMakeURsUniqueAndSoStopPageCachingInTheBrowser=&quot; + 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[&quot;windowABC&quot;] = window.open(...)
-//
-function getSeleniumWindowNameURLparameters() {
-    var w = (proxyInjectionMode ? selenium.browserbot.getCurrentWindow() : window).top;
-    var s = &quot;&amp;seleniumWindowName=&quot;;
-    if (w.opener == null) {
-        return s;
-    }
-    if (w[&quot;seleniumWindowName&quot;] == null) {
-        if (w.name) {
-            w[&quot;seleniumWindowName&quot;] = w.name;
-        } else {
-    	    w[&quot;seleniumWindowName&quot;] = 'generatedSeleniumWindowName_' + Math.round(100000 * Math.random());
-    	}
-    }
-    s += w[&quot;seleniumWindowName&quot;];
-    var windowOpener = w.opener;
-    for (key in windowOpener) {
-        var val = null;
-        try {
-    	    val = windowOpener[key];
-        }
-        catch(e) {
-        }
-        if (val==w) {
-	    s += &quot;&amp;jsWindowNameVar=&quot; + 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 = &quot;top&quot;;
-    }
-
-    if (w == selenium.browserbot.getCurrentWindow())
-    {
-        return frameNavigationalJSexpression;
-    }
-    for (var j = 0; j &lt; w.frames.length; j++)
-    {
-        var t = makeAddressToAUTFrame(w.frames[j], frameNavigationalJSexpression + &quot;.frames[&quot; + j + &quot;]&quot;);
-        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(&quot;context&quot;);
-    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. &quot;c:\blah\screenshot.png&quot;
-    */
-    // This doesn't really do anything on the JS side; we let the Selenium Server take care of this for us!
+/*
+* Copyright 2005 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+*  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 &quot;AS IS&quot; 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 = &quot;#cfffcf&quot;;
+failColor = &quot;#ffcfcf&quot;;
+errorColor = &quot;#ffffff&quot;;
+workingColor = &quot;#DEE7EC&quot;;
+doneColor = &quot;#FFFFCC&quot;;
+
+var injectedSessionId;
+var cmd1 = document.createElement(&quot;div&quot;);
+var cmd2 = document.createElement(&quot;div&quot;);
+var cmd3 = document.createElement(&quot;div&quot;);
+var cmd4 = document.createElement(&quot;div&quot;);
+
+var postResult = &quot;START&quot;;
+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(&quot;debugMode&quot;);
+    },
+
+    getContinue: function() {
+        return this._getQueryParameter(&quot;continue&quot;);
+    },
+
+    getDriverUrl: function() {
+        return this._getQueryParameter(&quot;driverUrl&quot;);
+    },
+
+    getSessionId: function() {
+        return this._getQueryParameter(&quot;sessionId&quot;);
+    },
+
+    _acquireQueryString: function () {
+        if (this.queryString) return;
+        if (browserVersion.isHTA) {
+            var args = this._extractArgs();
+            if (args.length &lt; 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(&quot;commandList&quot;) != null) {
+        document.getElementById(&quot;commandList&quot;).appendChild(cmd4);
+        document.getElementById(&quot;commandList&quot;).appendChild(cmd3);
+        document.getElementById(&quot;commandList&quot;).appendChild(cmd2);
+        document.getElementById(&quot;commandList&quot;).appendChild(cmd1);
+    }
+
+    var doContinue = runOptions.getContinue();
+    if (doContinue != null) postResult = &quot;OK&quot;;
+
+    currentTest.start();
+}
+
+function buildDriverUrl() {
+    var driverUrl = runOptions.getDriverUrl();
+    if (driverUrl != null) {
+        return driverUrl;
+    }
+    var s = window.location.href
+    var slashPairOffset = s.indexOf(&quot;//&quot;) + &quot;//&quot;.length
+    var pathSlashOffset = s.substring(slashPairOffset).indexOf(&quot;/&quot;)
+    return s.substring(0, slashPairOffset + pathSlashOffset) + &quot;/selenium-server/driver/&quot;;
+    //return &quot;http://localhost&quot; + uniqueId + &quot;/selenium-server/driver/&quot;;
+}
+
+function logToRc(logLevel, message) {
+    if (debugMode) {
+        if (logLevel == null) {
+            logLevel = &quot;debug&quot;;
+        }
+        sendToRCAndForget(&quot;logLevel=&quot; + logLevel + &quot;:&quot; + message.replace(/[\n\r\015]/g, &quot; &quot;) + &quot;\n&quot;, &quot;logging=true&quot;);
+    }
+}
+
+function serializeString(name, s) {
+    return name + &quot;=unescape(\&quot;&quot; + escape(s) + &quot;\&quot;);&quot;;
+}
+
+function serializeObject(name, x)
+{
+    var s = '';
+
+    if (isArray(x))
+    {
+        s = name + &quot;=new Array(); &quot;;
+        var len = x[&quot;length&quot;];
+        for (var j = 0; j &lt; len; j++)
+        {
+            s += serializeString(name + &quot;[&quot; + j + &quot;]&quot;, x[j]);
+        }
+    }
+    else if (typeof x == &quot;string&quot;)
+    {
+        s = serializeString(name, x);
+    }
+    else
+    {
+        throw &quot;unrecognized object not encoded: &quot; + name + &quot;(&quot; + x + &quot;)&quot;;
+    }
+    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 = &quot;&quot;;
+        if (postResult == &quot;START&quot;) {
+            urlParms += &quot;seleniumStart=true&quot;;
+        }
+        this.xmlHttpForCommandsAndResults = XmlHttp.create();
+        sendToRC(postResult, urlParms, fnBind(this._HandleHttpResponse, this), this.xmlHttpForCommandsAndResults);
+    },
+
+    commandStarted : function(command) {
+        this.commandNode = document.createElement(&quot;div&quot;);
+        var cmdText = command.command + '(';
+        if (command.target != null &amp;&amp; command.target != &quot;&quot;) {
+            cmdText += command.target;
+            if (command.value != null &amp;&amp; command.value != &quot;&quot;) {
+                cmdText += ', ' + command.value;
+            }
+        }
+        cmdText += &quot;)&quot;;
+        if (cmdText.length &gt;40) {
+            cmdText = cmdText.substring(0,40);
+            cmdText += &quot;...&quot;;
+        }
+        this.commandNode.appendChild(document.createTextNode(cmdText));
+        this.commandNode.style.backgroundColor = workingColor;
+        if (document.getElementById(&quot;commandList&quot;) != null) {
+            document.getElementById(&quot;commandList&quot;).removeChild(cmd1);
+            document.getElementById(&quot;commandList&quot;).removeChild(cmd2);
+            document.getElementById(&quot;commandList&quot;).removeChild(cmd3);
+            document.getElementById(&quot;commandList&quot;).removeChild(cmd4);
+            cmd4 = cmd3;
+            cmd3 = cmd2;
+            cmd2 = cmd1;
+            cmd1 = this.commandNode;
+            document.getElementById(&quot;commandList&quot;).appendChild(cmd4);
+            document.getElementById(&quot;commandList&quot;).appendChild(cmd3);
+            document.getElementById(&quot;commandList&quot;).appendChild(cmd2);
+            document.getElementById(&quot;commandList&quot;).appendChild(cmd1);
+        }
+    },
+
+    commandComplete : function(result) {
+
+        if (result.failed) {
+            if (postResult == &quot;CONTINUATION&quot;) {
+                currentTest.aborted = true;
+            }
+            postResult = result.failureMessage;
+            this.commandNode.title = result.failureMessage;
+            this.commandNode.style.backgroundColor = failColor;
+        } else if (result.passed) {
+            postResult = &quot;OK&quot;;
+            this.commandNode.style.backgroundColor = passColor;
+        } else {
+            if (result.result == null) {
+                postResult = &quot;OK&quot;;
+            } else {
+                var actualResult = result.result;
+                actualResult = selArrayToString(actualResult);
+                postResult = &quot;OK,&quot; + actualResult;
+            }
+            this.commandNode.style.backgroundColor = doneColor;
+        }
+    },
+
+    commandError : function(message) {
+        postResult = &quot;ERROR: &quot; + message;
+        this.commandNode.style.backgroundColor = errorColor;
+        this.commandNode.title = message;
+    },
+
+    testComplete : function() {
+        window.status = &quot;Selenium Tests Complete, for this Test&quot;
+        // Continue checking for new results
+        this.continueTest();
+        postResult = &quot;START&quot;;
+    },
+
+    _HandleHttpResponse : function() {
+        // When request is completed
+        if (this.xmlHttpForCommandsAndResults.readyState == 4) {
+            // OK
+            if (this.xmlHttpForCommandsAndResults.status == 200) {
+            	if (this.xmlHttpForCommandsAndResults.responseText==&quot;&quot;) {
+                    LOG.error(&quot;saw blank string xmlHttpForCommandsAndResults.responseText&quot;);
+                    return;
+                }
+                var command = this._extractCommand(this.xmlHttpForCommandsAndResults);
+                this.currentCommand = command;
+                this.continueTestAtCurrentCommand();
+            }
+            // Not OK 
+            else {
+                var s = 'xmlHttp returned: ' + this.xmlHttpForCommandsAndResults.status + &quot;: &quot; + 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(&quot;^(.*?)\n((.|[\r\n])*)&quot;);
+            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 &gt; 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, &quot;%20&quot;);
+        // strip trailing spaces
+        var processed = processed.replace(/\s+$/, &quot;&quot;);
+        var vars = processed.split(&quot;&amp;&quot;);
+        var cmdArgs = new Object();
+        for (var i = 0; i &lt; vars.length; i++) {
+            var pair = vars[i].split(&quot;=&quot;);
+            cmdArgs[pair[0]] = pair[1];
+        }
+        var cmd = cmdArgs['cmd'];
+        var arg1 = cmdArgs['1'];
+        if (null == arg1) arg1 = &quot;&quot;;
+        arg1 = decodeURIComponent(arg1);
+        var arg2 = cmdArgs['2'];
+        if (null == arg2) arg2 = &quot;&quot;;
+        arg2 = decodeURIComponent(arg2);
+        if (cmd == null) {
+            throw new Error(&quot;Bad command request: &quot; + 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() + &quot;?&quot;
+    if (urlParms) {
+        url += urlParms;
+    }
+    url = addUrlParams(url);
+    url += &quot;&amp;sequenceNumber=&quot; + 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(&quot;*********** &quot; + command.command + &quot; | &quot; + command.target + &quot; | &quot; + command.value);
+                        if (command.command == 'retryLast') {
+                            retry = true;
+                        }
+                    }
+                    if (retry) {
+                        setTimeout(fnBind(function() {
+                            sendToRC(&quot;RETRY&quot;, &quot;retry=true&quot;, callback, xmlHttpObject, async);
+                        }, this), 1000);
+                    } else {
+                        callback();
+                    }
+                }
+            }
+        }
+    }
+    
+    var postedData = &quot;postedData=&quot; + encodeURIComponent(dataToBePosted);
+
+    //xmlHttpObject.setRequestHeader(&quot;Content-Type&quot;, &quot;application/x-www-form-urlencoded&quot;);
+    xmlHttpObject.open(&quot;POST&quot;, url, async);
+    xmlHttpObject.onreadystatechange = wrappingCallback;
+    xmlHttpObject.send(postedData);
+    return null;
+}
+
+function addUrlParams(url) {
+    return url + &quot;&amp;localFrameAddress=&quot; + (proxyInjectionMode ? makeAddressToAUTFrame() : &quot;top&quot;)
+    + getSeleniumWindowNameURLparameters()
+    + &quot;&amp;uniqueId=&quot; + 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 = &quot;http:&quot;;
+        if (window.location.protocol == &quot;https:&quot;) {
+            // DGF if we're in HTTPS, use another HTTPS url to avoid security warning
+            protocol = &quot;https:&quot;;
+        }
+        // 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 + &quot;//&quot; + Math.floor(Math.random()* 16 + 1) + &quot;.selenium.doesnotexist/selenium-server/driver/?&quot; + urlParams;
+    } else {
+        url = buildDriverUrl() + &quot;?&quot; + urlParams;
+    }
+    url = addUrlParams(url);
+    
+    var method = &quot;GET&quot;;
+    if (method == &quot;POST&quot;) {
+        // 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(&quot;form&quot;);
+        loggingForm.method = &quot;POST&quot;;
+        loggingForm.action = url;
+        loggingForm.target = &quot;seleniumLoggingFrame&quot;;
+        var postedDataInput = document.createElement(&quot;input&quot;);
+        postedDataInput.type = &quot;hidden&quot;;
+        postedDataInput.name = &quot;postedData&quot;;
+        postedDataInput.value = dataToBePosted;
+        loggingForm.appendChild(postedDataInput);
+        document.body.appendChild(loggingForm);
+        loggingForm.submit();
+        document.body.removeChild(loggingForm);
+    } else {
+        var postedData = &quot;&amp;postedData=&quot; + encodeURIComponent(dataToBePosted);
+        var scriptTag = document.createElement(&quot;script&quot;);
+        scriptTag.src = url + postedData;
+        document.body.appendChild(scriptTag);
+        document.body.removeChild(scriptTag);
+    }
+}
+
+function buildDriverParams() {
+    var params = &quot;&quot;;
+
+    var sessionId = runOptions.getSessionId();
+    if (sessionId == undefined) {
+        sessionId = injectedSessionId;
+    }
+    if (sessionId != undefined) {
+        params = params + &quot;&amp;sessionId=&quot; + sessionId;
+    }
+    return params;
+}
+
+function preventBrowserCaching() {
+    var t = (new Date()).getTime();
+    return &quot;&amp;counterToMakeURsUniqueAndSoStopPageCachingInTheBrowser=&quot; + 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[&quot;windowABC&quot;] = window.open(...)
+//
+function getSeleniumWindowNameURLparameters() {
+    var w = (proxyInjectionMode ? selenium.browserbot.getCurrentWindow() : window).top;
+    var s = &quot;&amp;seleniumWindowName=&quot;;
+    if (w.opener == null) {
+        return s;
+    }
+    if (w[&quot;seleniumWindowName&quot;] == null) {
+        if (w.name) {
+            w[&quot;seleniumWindowName&quot;] = w.name;
+        } else {
+    	    w[&quot;seleniumWindowName&quot;] = 'generatedSeleniumWindowName_' + Math.round(100000 * Math.random());
+    	}
+    }
+    s += w[&quot;seleniumWindowName&quot;];
+    var windowOpener = w.opener;
+    for (key in windowOpener) {
+        var val = null;
+        try {
+    	    val = windowOpener[key];
+        }
+        catch(e) {
+        }
+        if (val==w) {
+	    s += &quot;&amp;jsWindowNameVar=&quot; + 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 = &quot;top&quot;;
+    }
+
+    if (w == selenium.browserbot.getCurrentWindow())
+    {
+        return frameNavigationalJSexpression;
+    }
+    for (var j = 0; j &lt; w.frames.length; j++)
+    {
+        var t = makeAddressToAUTFrame(w.frames[j], frameNavigationalJSexpression + &quot;.frames[&quot; + j + &quot;]&quot;);
+        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(&quot;context&quot;);
+    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. &quot;c:\blah\screenshot.png&quot;
+    */
+    // 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</diff>
      <filename>testframework/selenium/scripts/selenium-remoterunner.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,264 +1,264 @@
-/*
-* Copyright 2005 ThoughtWorks, Inc
-*
-*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-*  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 &quot;AS IS&quot; 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 = &quot;#cfffcf&quot;;
-failColor = &quot;#ffcfcf&quot;;
-errorColor = &quot;#ffffff&quot;;
-workingColor = &quot;#DEE7EC&quot;;
-doneColor = &quot;#FFFFCC&quot;;
-
-slowMode = false;
-
-var cmd1 = document.createElement(&quot;div&quot;);
-var cmd2 = document.createElement(&quot;div&quot;);
-var cmd3 = document.createElement(&quot;div&quot;);
-var cmd4 = document.createElement(&quot;div&quot;);
-
-var postResult = &quot;START&quot;;
-
-function runTest() {
-    var testAppFrame = document.getElementById('myiframe');
-    selenium = Selenium.createForFrame(testAppFrame);
-
-    commandFactory = new CommandHandlerFactory();
-    commandFactory.registerAll(selenium);
-
-    testLoop = new TestLoop(commandFactory);
-
-    testLoop.nextCommand = nextCommand;
-    testLoop.commandStarted = commandStarted;
-    testLoop.commandComplete = commandComplete;
-    testLoop.commandError = commandError;
-    testLoop.requiresCallBack = true;
-    testLoop.testComplete = function() {
-    	window.status = &quot;Selenium Tests Complete, for this Test&quot;
-    	// Continue checking for new results
-    	testLoop.continueTest();
-    	postResult = &quot;START&quot;;
-    };
-
-    document.getElementById(&quot;commandList&quot;).appendChild(cmd4);
-    document.getElementById(&quot;commandList&quot;).appendChild(cmd3);
-    document.getElementById(&quot;commandList&quot;).appendChild(cmd2);
-    document.getElementById(&quot;commandList&quot;).appendChild(cmd1);
-    
-    var doContinue = getQueryVariable(&quot;continue&quot;);
-	if (doContinue != null) postResult = &quot;OK&quot;;
-
-    testLoop.start();
-}
-
-function getQueryVariable(variable) {
-    var query = window.location.search.substring(1);
-    var vars = query.split(&quot;&amp;&quot;);
-    for (var i=0;i&lt;vars.length;i++) {
-        var pair = vars[i].split(&quot;=&quot;);
-        if (pair[0] == variable) {
-            return pair[1];
-        }
-    }
-}
-
-function buildBaseUrl() {
-	var lastSlash = window.location.href.lastIndexOf('/');
-	var baseUrl = window.location.href.substring(0, lastSlash+1);
-	return baseUrl;
-}
-
-function buildDriverParams() {
-    var params = &quot;&quot;;
-
-    var host = getQueryVariable(&quot;driverhost&quot;);
-    var port = getQueryVariable(&quot;driverport&quot;);
-    if (host != undefined &amp;&amp; port != undefined) {
-        params = params + &quot;&amp;driverhost=&quot; + host + &quot;&amp;driverport=&quot; + port;
-    }
-
-    var sessionId = getQueryVariable(&quot;sessionId&quot;);
-    if (sessionId != undefined) {
-        params = params + &quot;&amp;sessionId=&quot; + sessionId;
-    }
-
-    return params;
-}
-
-function preventBrowserCaching() {
-    var t = (new Date()).getTime();
-    return &quot;&amp;counterToMakeURsUniqueAndSoStopPageCachingInTheBrowser=&quot; + t;
-}   
-
-function nextCommand() {
-    xmlHttp = XmlHttp.create();
-    try {
-    	
-    	var url = buildBaseUrl();
-        if (postResult == &quot;START&quot;) {
-        	url = url + &quot;driver/?seleniumStart=true&quot; + buildDriverParams() + preventBrowserCaching();
-        } else {
-        	url = url + &quot;driver/?&quot; + buildDriverParams() + preventBrowserCaching();
-        }
-        LOG.debug(&quot;XMLHTTPRequesting &quot; + url);
-        xmlHttp.open(&quot;POST&quot;, url, true);
-        xmlHttp.onreadystatechange=handleHttpResponse;
-        xmlHttp.send(postResult);
-    } catch(e) {
-       	var s = 'xmlHttp returned:\n'
-        for (key in e) {
-            s += &quot;\t&quot; + key + &quot; -&gt; &quot; + e[key] + &quot;\n&quot;
-        }
-        LOG.error(s);
-        return null;
-    }
-    return null;
-}
-
- function handleHttpResponse() {
- 	if (xmlHttp.readyState == 4) {
- 		if (xmlHttp.status == 200) {
- 			var command = extractCommand(xmlHttp);
- 			testLoop.currentCommand = command;
- 			testLoop.beginNextTest();
- 		} else {
- 			var s = 'xmlHttp returned: ' + xmlHttp.status + &quot;: &quot; + xmlHttp.statusText;
- 			LOG.error(s);
- 			testLoop.currentCommand = null;
- 			setTimeout(&quot;testLoop.beginNextTest();&quot;, 2000);
- 		}
- 		
- 	}
- }
-
-
-function extractCommand(xmlHttp) {
-    if (slowMode) {
-        delay(2000);
-    }
-
-    var command;
-    try {
-        command = xmlHttp.responseText;
-    } catch (e) {
-        alert('could not get responseText: ' + e.message);
-    }
-    if (command.substr(0,'|testComplete'.length)=='|testComplete') {
-        return null;
-    }
-
-    return createCommandFromRequest(command);
-}
-
-function commandStarted(command) {
-    commandNode = document.createElement(&quot;div&quot;);
-    innerHTML = command.command + '(';
-    if (command.target != null &amp;&amp; command.target != &quot;&quot;) {
-        innerHTML += command.target;
-        if (command.value != null &amp;&amp; command.value != &quot;&quot;) {
-            innerHTML += ', ' + command.value;
-        }
-    }
-    innerHTML += &quot;)&quot;;
-    commandNode.innerHTML = innerHTML;
-    commandNode.style.backgroundColor = workingColor;
-    document.getElementById(&quot;commandList&quot;).removeChild(cmd1);
-    document.getElementById(&quot;commandList&quot;).removeChild(cmd2);
-    document.getElementById(&quot;commandList&quot;).removeChild(cmd3);
-    document.getElementById(&quot;commandList&quot;).removeChild(cmd4);
-    cmd4 = cmd3;
-    cmd3 = cmd2;
-    cmd2 = cmd1;
-    cmd1 = commandNode;
-    document.getElementById(&quot;commandList&quot;).appendChild(cmd4);
-    document.getElementById(&quot;commandList&quot;).appendChild(cmd3);
-    document.getElementById(&quot;commandList&quot;).appendChild(cmd2);
-    document.getElementById(&quot;commandList&quot;).appendChild(cmd1);
-}
-
-function commandComplete(result) {
-    if (result.failed) {
-    	if (postResult == &quot;CONTINUATION&quot;) {
-    		testLoop.aborted = true;
-    	}
-        postResult = result.failureMessage;
-        commandNode.title = result.failureMessage;
-        commandNode.style.backgroundColor = failColor;
-    } else if (result.passed) {
-        postResult = &quot;OK&quot;;
-        commandNode.style.backgroundColor = passColor;
-    } else {
-    	if (result.result == null) {
-    		postResult = &quot;OK&quot;;
-    	} else {
-    		postResult = &quot;OK,&quot; + result.result;
-    	}
-        commandNode.style.backgroundColor = doneColor;
-    }
-}
-
-function commandError(message) {
-    postResult = &quot;ERROR: &quot; + message;
-    commandNode.style.backgroundColor = errorColor;
-    commandNode.title = message;
-}
-
-function slowClicked() {
-    slowMode = !slowMode;
-}
-
-function delay(millis) {
-    startMillis = new Date();
-    while (true) {
-        milli = new Date();
-        if (milli-startMillis &gt; millis) {
-            break;
-        }
-    }
-}
-
-function getIframeDocument(iframe) {
-    if (iframe.contentDocument) {
-        return iframe.contentDocument;
-    }
-    else {
-        return iframe.contentWindow.document;
-    }
-}
-
-// Parses a URI query string into a SeleniumCommand object
-function createCommandFromRequest(commandRequest) {
-	//decodeURIComponent doesn't strip plus signs
-	var processed = commandRequest.replace(/\+/g, &quot;%20&quot;);
-	// strip trailing spaces
-	var processed = processed.replace(/\s+$/, &quot;&quot;);
-    var vars = processed.split(&quot;&amp;&quot;);
-    var cmdArgs = new Object();
-    for (var i=0;i&lt;vars.length;i++) {
-        var pair = vars[i].split(&quot;=&quot;);
-        cmdArgs[pair[0]] = pair[1];
-    }
-    var cmd = cmdArgs['cmd'];
-    var arg1 = cmdArgs['1'];
-    if (null == arg1) arg1 = &quot;&quot;;
-    arg1 = decodeURIComponent(arg1);
-    var arg2 = cmdArgs['2'];
-    if (null == arg2) arg2 = &quot;&quot;;
-    arg2 = decodeURIComponent(arg2);
-    if (cmd == null) {
-    	throw new Error(&quot;Bad command request: &quot; + commandRequest);
-    }
-    return new SeleniumCommand(cmd, arg1, arg2);
-}
-
+/*
+* Copyright 2005 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+*  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 &quot;AS IS&quot; 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 = &quot;#cfffcf&quot;;
+failColor = &quot;#ffcfcf&quot;;
+errorColor = &quot;#ffffff&quot;;
+workingColor = &quot;#DEE7EC&quot;;
+doneColor = &quot;#FFFFCC&quot;;
+
+slowMode = false;
+
+var cmd1 = document.createElement(&quot;div&quot;);
+var cmd2 = document.createElement(&quot;div&quot;);
+var cmd3 = document.createElement(&quot;div&quot;);
+var cmd4 = document.createElement(&quot;div&quot;);
+
+var postResult = &quot;START&quot;;
+
+function runTest() {
+    var testAppFrame = document.getElementById('myiframe');
+    selenium = Selenium.createForFrame(testAppFrame);
+
+    commandFactory = new CommandHandlerFactory();
+    commandFactory.registerAll(selenium);
+
+    testLoop = new TestLoop(commandFactory);
+
+    testLoop.nextCommand = nextCommand;
+    testLoop.commandStarted = commandStarted;
+    testLoop.commandComplete = commandComplete;
+    testLoop.commandError = commandError;
+    testLoop.requiresCallBack = true;
+    testLoop.testComplete = function() {
+    	window.status = &quot;Selenium Tests Complete, for this Test&quot;
+    	// Continue checking for new results
+    	testLoop.continueTest();
+    	postResult = &quot;START&quot;;
+    };
+
+    document.getElementById(&quot;commandList&quot;).appendChild(cmd4);
+    document.getElementById(&quot;commandList&quot;).appendChild(cmd3);
+    document.getElementById(&quot;commandList&quot;).appendChild(cmd2);
+    document.getElementById(&quot;commandList&quot;).appendChild(cmd1);
+    
+    var doContinue = getQueryVariable(&quot;continue&quot;);
+	if (doContinue != null) postResult = &quot;OK&quot;;
+
+    testLoop.start();
+}
+
+function getQueryVariable(variable) {
+    var query = window.location.search.substring(1);
+    var vars = query.split(&quot;&amp;&quot;);
+    for (var i=0;i&lt;vars.length;i++) {
+        var pair = vars[i].split(&quot;=&quot;);
+        if (pair[0] == variable) {
+            return pair[1];
+        }
+    }
+}
+
+function buildBaseUrl() {
+	var lastSlash = window.location.href.lastIndexOf('/');
+	var baseUrl = window.location.href.substring(0, lastSlash+1);
+	return baseUrl;
+}
+
+function buildDriverParams() {
+    var params = &quot;&quot;;
+
+    var host = getQueryVariable(&quot;driverhost&quot;);
+    var port = getQueryVariable(&quot;driverport&quot;);
+    if (host != undefined &amp;&amp; port != undefined) {
+        params = params + &quot;&amp;driverhost=&quot; + host + &quot;&amp;driverport=&quot; + port;
+    }
+
+    var sessionId = getQueryVariable(&quot;sessionId&quot;);
+    if (sessionId != undefined) {
+        params = params + &quot;&amp;sessionId=&quot; + sessionId;
+    }
+
+    return params;
+}
+
+function preventBrowserCaching() {
+    var t = (new Date()).getTime();
+    return &quot;&amp;counterToMakeURsUniqueAndSoStopPageCachingInTheBrowser=&quot; + t;
+}   
+
+function nextCommand() {
+    xmlHttp = XmlHttp.create();
+    try {
+    	
+    	var url = buildBaseUrl();
+        if (postResult == &quot;START&quot;) {
+        	url = url + &quot;driver/?seleniumStart=true&quot; + buildDriverParams() + preventBrowserCaching();
+        } else {
+        	url = url + &quot;driver/?&quot; + buildDriverParams() + preventBrowserCaching();
+        }
+        LOG.debug(&quot;XMLHTTPRequesting &quot; + url);
+        xmlHttp.open(&quot;POST&quot;, url, true);
+        xmlHttp.onreadystatechange=handleHttpResponse;
+        xmlHttp.send(postResult);
+    } catch(e) {
+       	var s = 'xmlHttp returned:\n'
+        for (key in e) {
+            s += &quot;\t&quot; + key + &quot; -&gt; &quot; + e[key] + &quot;\n&quot;
+        }
+        LOG.error(s);
+        return null;
+    }
+    return null;
+}
+
+ function handleHttpResponse() {
+ 	if (xmlHttp.readyState == 4) {
+ 		if (xmlHttp.status == 200) {
+ 			var command = extractCommand(xmlHttp);
+ 			testLoop.currentCommand = command;
+ 			testLoop.beginNextTest();
+ 		} else {
+ 			var s = 'xmlHttp returned: ' + xmlHttp.status + &quot;: &quot; + xmlHttp.statusText;
+ 			LOG.error(s);
+ 			testLoop.currentCommand = null;
+ 			setTimeout(&quot;testLoop.beginNextTest();&quot;, 2000);
+ 		}
+ 		
+ 	}
+ }
+
+
+function extractCommand(xmlHttp) {
+    if (slowMode) {
+        delay(2000);
+    }
+
+    var command;
+    try {
+        command = xmlHttp.responseText;
+    } catch (e) {
+        alert('could not get responseText: ' + e.message);
+    }
+    if (command.substr(0,'|testComplete'.length)=='|testComplete') {
+        return null;
+    }
+
+    return createCommandFromRequest(command);
+}
+
+function commandStarted(command) {
+    commandNode = document.createElement(&quot;div&quot;);
+    innerHTML = command.command + '(';
+    if (command.target != null &amp;&amp; command.target != &quot;&quot;) {
+        innerHTML += command.target;
+        if (command.value != null &amp;&amp; command.value != &quot;&quot;) {
+            innerHTML += ', ' + command.value;
+        }
+    }
+    innerHTML += &quot;)&quot;;
+    commandNode.innerHTML = innerHTML;
+    commandNode.style.backgroundColor = workingColor;
+    document.getElementById(&quot;commandList&quot;).removeChild(cmd1);
+    document.getElementById(&quot;commandList&quot;).removeChild(cmd2);
+    document.getElementById(&quot;commandList&quot;).removeChild(cmd3);
+    document.getElementById(&quot;commandList&quot;).removeChild(cmd4);
+    cmd4 = cmd3;
+    cmd3 = cmd2;
+    cmd2 = cmd1;
+    cmd1 = commandNode;
+    document.getElementById(&quot;commandList&quot;).appendChild(cmd4);
+    document.getElementById(&quot;commandList&quot;).appendChild(cmd3);
+    document.getElementById(&quot;commandList&quot;).appendChild(cmd2);
+    document.getElementById(&quot;commandList&quot;).appendChild(cmd1);
+}
+
+function commandComplete(result) {
+    if (result.failed) {
+    	if (postResult == &quot;CONTINUATION&quot;) {
+    		testLoop.aborted = true;
+    	}
+        postResult = result.failureMessage;
+        commandNode.title = result.failureMessage;
+        commandNode.style.backgroundColor = failColor;
+    } else if (result.passed) {
+        postResult = &quot;OK&quot;;
+        commandNode.style.backgroundColor = passColor;
+    } else {
+    	if (result.result == null) {
+    		postResult = &quot;OK&quot;;
+    	} else {
+    		postResult = &quot;OK,&quot; + result.result;
+    	}
+        commandNode.style.backgroundColor = doneColor;
+    }
+}
+
+function commandError(message) {
+    postResult = &quot;ERROR: &quot; + message;
+    commandNode.style.backgroundColor = errorColor;
+    commandNode.title = message;
+}
+
+function slowClicked() {
+    slowMode = !slowMode;
+}
+
+function delay(millis) {
+    startMillis = new Date();
+    while (true) {
+        milli = new Date();
+        if (milli-startMillis &gt; millis) {
+            break;
+        }
+    }
+}
+
+function getIframeDocument(iframe) {
+    if (iframe.contentDocument) {
+        return iframe.contentDocument;
+    }
+    else {
+        return iframe.contentWindow.document;
+    }
+}
+
+// Parses a URI query string into a SeleniumCommand object
+function createCommandFromRequest(commandRequest) {
+	//decodeURIComponent doesn't strip plus signs
+	var processed = commandRequest.replace(/\+/g, &quot;%20&quot;);
+	// strip trailing spaces
+	var processed = processed.replace(/\s+$/, &quot;&quot;);
+    var vars = processed.split(&quot;&amp;&quot;);
+    var cmdArgs = new Object();
+    for (var i=0;i&lt;vars.length;i++) {
+        var pair = vars[i].split(&quot;=&quot;);
+        cmdArgs[pair[0]] = pair[1];
+    }
+    var cmd = cmdArgs['cmd'];
+    var arg1 = cmdArgs['1'];
+    if (null == arg1) arg1 = &quot;&quot;;
+    arg1 = decodeURIComponent(arg1);
+    var arg2 = cmdArgs['2'];
+    if (null == arg2) arg2 = &quot;&quot;;
+    arg2 = decodeURIComponent(arg2);
+    if (cmd == null) {
+    	throw new Error(&quot;Bad command request: &quot; + commandRequest);
+    }
+    return new SeleniumCommand(cmd, arg1, arg2);
+}
+</diff>
      <filename>testframework/selenium/scripts/selenium-seleneserunner.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,1333 +1,1333 @@
-/*
-* Copyright 2004 ThoughtWorks, Inc
-*
-*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-*  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 &quot;AS IS&quot; 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(&quot;link&quot;);
-        styleLink.rel = &quot;stylesheet&quot;;
-        styleLink.type = &quot;text/css&quot;;
-        if (browserVersion &amp;&amp; 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(&quot;link&quot;);
-            tempLink.href = &quot;selenium-test.css&quot;; // 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(/[^\/\\]+$/, &quot;selenium-test.css&quot;);
-            if (browserVersion.isIE &amp;&amp; window.location.protocol == &quot;file:&quot;) {
-                styleSheetPath = &quot;file:///&quot; + styleSheetPath;
-            }
-            styleLink.href = styleSheetPath;
-        }
-        // DGF You're only going to see this log message if you set defaultLogLevel=debug
-        LOG.debug(&quot;styleLink.href=&quot;+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(&quot;?&quot;) == -1 ? &quot;?&quot; : &quot;&amp;&quot;);
-        location += &quot;thisIsChrome=&quot; + isChrome + &quot;&amp;thisIsHTA=&quot; + 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 = &quot;about:blank&quot;;
-            this.frame.src = location;
-        } else {
-            this.frame.contentWindow.location.replace(location);
-        }
-    },
-
-    load: function(/* url, [callback] */) {
-        if (arguments.length &gt; 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(&quot;runInterval&quot;);
-        if (tempRunInterval) {
-            this.setRunInterval(tempRunInterval);
-        }
-        this.highlightOption.checked = this._getQueryParameter(&quot;highlight&quot;);
-    },
-
-    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 = &quot;cssPauseTest&quot;;
-        this.pauseButton.onclick = fnBindAsEventListener(this.pauseCurrentTest, this);
-    },
-
-    _switchPauseButtonToContinue: function() {
-        sel$('stepTest').disabled = false;
-        this.pauseButton.className = &quot;cssContinueTest&quot;;
-        this.pauseButton.onclick = fnBindAsEventListener(this.continueCurrentTest, this);
-    },
-
-    stepCurrentTest: function () {
-        this.setToPauseAtNextCommand();
-        currentTest.resume();
-    },
-
-    isAutomatedRun: function() {
-        return this._isQueryParameterTrue(&quot;auto&quot;);
-    },
-
-    shouldSaveResultsToFile: function() {
-        return this._isQueryParameterTrue(&quot;save&quot;);
-    },
-
-    closeAfterTests: function() {
-        return this._isQueryParameterTrue(&quot;close&quot;);
-    },
-
-    getTestSuiteName: function() {
-        return this._getQueryParameter(&quot;test&quot;);
-    },
-
-    getSingleTestName: function() {
-        return this._getQueryParameter(&quot;singletest&quot;);
-    },
-
-    getAutoUrl: function() {
-        return this._getQueryParameter(&quot;autoURL&quot;);
-    },
-    
-    getDefaultLogLevel: function() {
-        return this._getQueryParameter(&quot;defaultLogLevel&quot;);
-    },
-
-    getResultsUrl: function() {
-        return this._getQueryParameter(&quot;resultsUrl&quot;);
-    },
-
-    _acquireQueryString: function() {
-        if (this.queryString) return;
-        if (browserVersion.isHTA) {
-            var args = this._extractArgs();
-            if (args.length &lt; 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]+/, &quot;&quot;);
-        if (status) {
-            addClassName(this.trElement, &quot;status_&quot; + status);
-        }
-    },
-
-    select: function() {
-        addClassName(this.trElement, &quot;selected&quot;);
-        safeScrollIntoView(this.trElement);
-    },
-
-    unselect: function() {
-        removeClassName(this.trElement, &quot;selected&quot;);
-    },
-
-    markPassed: function() {
-        this.setStatus(&quot;passed&quot;);
-    },
-
-    markDone: function() {
-        this.setStatus(&quot;done&quot;);
-    },
-
-    markFailed: function() {
-        this.setStatus(&quot;failed&quot;);
-    }
-
-});
-
-var TitleRow = classCreate();
-objectExtend(TitleRow.prototype, AbstractResultAwareRow.prototype);
-objectExtend(TitleRow.prototype, {
-
-    initialize: function(trElement) {
-        this.trElement = trElement;
-        trElement.className = &quot;title&quot;;
-    }
-
-});
-
-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, &quot;breakpoint&quot;);
-        } else {
-            this.trElement.isBreakpoint = undefined;
-            removeClassName(this.trElement, &quot;breakpoint&quot;);
-        }
-    },
-
-    addBreakpointSupport: function() {
-        elementSetStyle(this.trElement, {&quot;cursor&quot; : &quot;pointer&quot;});
-        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(&quot;a&quot;)[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(&quot;div&quot;);
-        divElement.innerHTML = resultHTML;
-
-        var hiddenCell = this.trElement.ownerDocument.createElement(&quot;td&quot;);
-        hiddenCell.appendChild(divElement);
-        hiddenCell.style.display = &quot;none&quot;;
-
-        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 &lt; 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(&quot;table&quot;));
-        return tables[0];
-    },
-
-    isAvailable: function() {
-        return this.getTestTable() != null;
-    },
-
-    _collectSuiteRows: function () {
-        var result = [];
-        var tables = sel$A(this.suiteDocument.getElementsByTagName(&quot;table&quot;));
-        var testTable = tables[0];
-        for (rowNum = 1; rowNum &lt; 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 &lt; sel$A(this.suiteDocument.getElementsByTagName(&quot;table&quot;)).length; tableNum++) {
-            testTable = tables[tableNum];
-            for (rowNum = 1; rowNum &lt; 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 &gt;= 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 &gt;= 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 &quot;/postResults&quot;, but an alternative location
-// can be specified by providing a &quot;resultsUrl&quot; 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(&quot;form&quot;);
-        document.body.appendChild(form);
-
-        form.id = &quot;resultsForm&quot;;
-        form.method = &quot;post&quot;;
-        form.target = &quot;selenium_myiframe&quot;;
-
-        var resultsUrl = this.controlPanel.getResultsUrl();
-        if (!resultsUrl) {
-            resultsUrl = &quot;./postResults&quot;;
-        }
-
-        var actionAndParameters = resultsUrl.split('?', 2);
-        form.action = actionAndParameters[0];
-        var resultsUrlQueryString = actionAndParameters[1];
-
-        form.createHiddenField = function(name, value) {
-            input = document.createElement(&quot;input&quot;);
-            input.type = &quot;hidden&quot;;
-            input.name = name;
-            input.value = value;
-            this.appendChild(input);
-        };
-
-        if (resultsUrlQueryString) {
-            var clauses = resultsUrlQueryString.split('&amp;');
-            for (var i = 0; i &lt; 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(&quot;selenium.version&quot;, Selenium.version);
-        form.createHiddenField(&quot;selenium.revision&quot;, Selenium.revision);
-
-        form.createHiddenField(&quot;result&quot;, this.suiteFailed ? &quot;failed&quot; : &quot;passed&quot;);
-
-        form.createHiddenField(&quot;totalTime&quot;, Math.floor((this.metrics.currentTime - this.metrics.startTime) / 1000));
-        form.createHiddenField(&quot;numTestPasses&quot;, this.metrics.numTestPasses);
-        form.createHiddenField(&quot;numTestFailures&quot;, this.metrics.numTestFailures);
-        form.createHiddenField(&quot;numCommandPasses&quot;, this.metrics.numCommandPasses);
-        form.createHiddenField(&quot;numCommandFailures&quot;, this.metrics.numCommandFailures);
-        form.createHiddenField(&quot;numCommandErrors&quot;, this.metrics.numCommandErrors);
-
-        // Create an input for each test table.  The inputs are named
-        // testTable.1, testTable.2, etc.
-        for (rowNum = 1; rowNum &lt; this.suiteTable.rows.length; rowNum++) {
-            // If there is a second column, then add a new input
-            if (this.suiteTable.rows[rowNum].cells.length &gt; 1) {
-                var resultCell = this.suiteTable.rows[rowNum].cells[1];
-                form.createHiddenField(&quot;testTable.&quot; + rowNum, resultCell.innerHTML);
-                // remove the resultCell, so it's not included in the suite HTML
-                resultCell.parentNode.removeChild(resultCell);
-            }
-        }
-
-        form.createHiddenField(&quot;numTestTotal&quot;, rowNum-1);
-
-        // Add HTML for the suite itself
-        form.createHiddenField(&quot;suite&quot;, this.suiteTable.parentNode.innerHTML);
-
-        var logMessages = [];
-        while (LOG.pendingMessages.length &gt; 0) {
-            var msg = LOG.pendingMessages.shift();
-            logMessages.push(msg.type);
-            logMessages.push(&quot;: &quot;);
-            logMessages.push(msg.msg);
-            logMessages.push('\n');
-        }
-        var logOutput = logMessages.join(&quot;&quot;);
-        form.createHiddenField(&quot;log&quot;, 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 &lt; form.elements.length; i++) {
-            inputs[form.elements[i].name] = form.elements[i].value;
-        }
-        
-        var objFSO = new ActiveXObject(&quot;Scripting.FileSystemObject&quot;)
-        
-        // DGF get CSS
-        var styles = &quot;&quot;;
-        try {
-            var styleSheetPath = window.location.pathname.replace(/[^\/\\]+$/, &quot;selenium-test.css&quot;);
-            if (window.location.protocol == &quot;file:&quot;) {
-                var stylesFile = objFSO.OpenTextFile(styleSheetPath, 1);
-                styles = stylesFile.ReadAll();
-            } else {
-                var xhr = XmlHttp.create();
-                xhr.open(&quot;GET&quot;, styleSheetPath, false);
-                xhr.send(&quot;&quot;);
-                styles = xhr.responseText;
-            }
-        } catch (e) {}
-        
-        var scriptFile = objFSO.CreateTextFile(fileName);
-        
-        
-        scriptFile.WriteLine(&quot;&lt;html&gt;&lt;head&gt;&lt;title&gt;Test suite results&lt;/title&gt;&lt;style&gt;&quot;);
-        scriptFile.WriteLine(styles);
-        scriptFile.WriteLine(&quot;&lt;/style&gt;&quot;);
-        scriptFile.WriteLine(&quot;&lt;body&gt;\n&lt;h1&gt;Test suite results&lt;/h1&gt;&quot; +
-             &quot;\n\n&lt;table&gt;\n&lt;tr&gt;\n&lt;td&gt;result:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;result&quot;] + &quot;&lt;/td&gt;\n&quot; +
-             &quot;&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;totalTime:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;totalTime&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
-             &quot;&lt;tr&gt;\n&lt;td&gt;numTestTotal:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numTestTotal&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
-             &quot;&lt;tr&gt;\n&lt;td&gt;numTestPasses:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numTestPasses&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
-             &quot;&lt;tr&gt;\n&lt;td&gt;numTestFailures:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numTestFailures&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
-             &quot;&lt;tr&gt;\n&lt;td&gt;numCommandPasses:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numCommandPasses&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
-             &quot;&lt;tr&gt;\n&lt;td&gt;numCommandFailures:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numCommandFailures&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
-             &quot;&lt;tr&gt;\n&lt;td&gt;numCommandErrors:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numCommandErrors&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
-             &quot;&lt;tr&gt;\n&lt;td&gt;&quot; + inputs[&quot;suite&quot;] + &quot;&lt;/td&gt;\n&lt;td&gt;&amp;nbsp;&lt;/td&gt;\n&lt;/tr&gt;&lt;/table&gt;&lt;table&gt;&quot;);
-        var testNum = inputs[&quot;numTestTotal&quot;];
-        
-        for (var rowNum = 1; rowNum &lt;= testNum; rowNum++) {
-            scriptFile.WriteLine(&quot;&lt;tr&gt;\n&lt;td&gt;&quot; + inputs[&quot;testTable.&quot; + rowNum] + &quot;&lt;/td&gt;\n&lt;td&gt;&amp;nbsp;&lt;/td&gt;\n&lt;/tr&gt;&quot;);
-        }
-        scriptFile.WriteLine(&quot;&lt;/table&gt;&lt;pre&gt;&quot;);
-        var log = inputs[&quot;log&quot;];
-        log=log.replace(/&amp;/gm,&quot;&amp;amp;&quot;).replace(/&lt;/gm,&quot;&amp;lt;&quot;).replace(/&gt;/gm,&quot;&amp;gt;&quot;).replace(/&quot;/gm,&quot;&amp;quot;&quot;).replace(/'/gm,&quot;&amp;apos;&quot;);
-        scriptFile.WriteLine(log);
-        scriptFile.WriteLine(&quot;&lt;/pre&gt;&lt;/body&gt;&lt;/html&gt;&quot;);
-        scriptFile.Close();
-    }
-});
-
-/** HtmlTestCase encapsulates an HTML test document */
-var HtmlTestCase = classCreate();
-objectExtend(HtmlTestCase.prototype, {
-
-    initialize: function(testDocument, htmlTestSuiteRow) {
-        if (testDocument == null) {
-            throw &quot;testDocument should not be null&quot;;
-        }
-        if (htmlTestSuiteRow == null) {
-            throw &quot;htmlTestSuiteRow should not be null&quot;;
-        }
-        this.testDocument = testDocument;
-        this.htmlTestSuiteRow = htmlTestSuiteRow;
-        this.headerRow = new TitleRow(this.testDocument.getElementsByTagName(&quot;tr&quot;)[0]);
-        this.commandRows = this._collectCommandRows();
-        this.nextCommandRowIndex = 0;
-        this._addBreakpointSupport();
-    },
-
-    _collectCommandRows: function () {
-        var commandRows = [];
-        var tables = sel$A(this.testDocument.getElementsByTagName(&quot;table&quot;));
-        var self = this;
-        for (var i = 0; i &lt; tables.length; i++) {
-            var table = tables[i];
-            var tableRows = sel$A(table.rows);
-            for (var j = 0; j &lt; 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 &gt;= 3;
-    },
-
-    reset: function() {
-        /**
-         * reset the test to runnable state
-         */
-        this.nextCommandRowIndex = 0;
-
-        this.setStatus('');
-        for (var i = 0; i &lt; this.commandRows.length; i++) {
-            var row = this.commandRows[i];
-            row.reset();
-        }
-
-        // remove any additional fake &quot;error&quot; 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(&quot;failed&quot;);
-        this.htmlTestSuiteRow.markFailed();
-    },
-
-    markPassed: function() {
-        this.setStatus(&quot;passed&quot;);
-        this.htmlTestSuiteRow.markPassed();
-    },
-
-    addErrorMessage: function(errorMsg, currentRow) {
-        errorMsg = errorMsg.replace(/ /g, String.fromCharCode(160)).replace(&quot;\n&quot;, '\\n');
-        if (currentRow) {
-            currentRow.markFailed(errorMsg);
-        } else {
-            var errorElement = this.testDocument.createElement(&quot;p&quot;);
-            errorElement.id = &quot;error&quot;;
-            setText(errorElement, errorMsg);
-            this.testDocument.body.appendChild(errorElement);
-            errorElement.className = &quot;status_failed&quot;;
-        }
-    },
-
-    _addBreakpointSupport: function() {
-        for (var i = 0; i &lt; this.commandRows.length; i++) {
-            var row = this.commandRows[i];
-            row.addBreakpointSupport();
-        }
-    },
-
-    hasMoreCommandRows: function() {
-        return this.nextCommandRowIndex &lt; this.commandRows.length;
-    },
-
-    getNextCommandRow: function() {
-        if (this.hasMoreCommandRows()) {
-            return this.commandRows[this.nextCommandRowIndex++];
-        }
-        return null;
-    }
-
-});
-
-
-// TODO: split out an JavascriptTestCase class to handle the &quot;sejs&quot; stuff
-
-var get_new_rows = function() {
-    var row_array = new Array();
-    for (var i = 0; i &lt; 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 = '&lt;td style=&quot;display:none;&quot; class=&quot;js&quot;&gt;getEval&lt;/td&gt;' +
-                  '&lt;td style=&quot;display:none;&quot;&gt;currentTest.doNextCommand()&lt;/td&gt;' +
-                  '&lt;td style=&quot;white-space: pre;&quot;&gt;' + new_source + '&lt;/td&gt;' +
-                  '&lt;td&gt;&lt;/td&gt;'
-
-        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) + &quot;:&quot; + this._pad(seconds));
-    },
-
-// Puts a leading 0 on num if it is less than 10
-    _pad: function(num) {
-        return (num &gt; 9) ? num : &quot;0&quot; + 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(&quot;Starting test &quot; + 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 = &quot;Expected &quot; + this.expectedFailureType + &quot; did not occur.&quot;;
-            } else {
-                if (PatternMatcher.matches(this.expectedFailure, result.failureMessage)) {
-                    var failureType = result.error ? &quot;error&quot; : &quot;failure&quot;;
-                    if (failureType == this.expectedFailureType) {
-                        result.failed = false;
-                        result.passed = true;
-                    } else {
-                        result.failed = true;
-                        result.failureMessage = &quot;Expected &quot;+this.expectedFailureType+&quot;, but &quot;+failureType+&quot; occurred instead&quot;;
-                    }
-                } else {
-                    result.failed = true;
-                    result.failureMessage = &quot;Expected &quot; + this.expectedFailureType + &quot; message '&quot; + this.expectedFailure
-                                            + &quot;' but was '&quot; + result.failureMessage + &quot;'&quot;;
-                }
-            }
-            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(&quot;currentTest.recordFailure: &quot; + 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 &gt; 0) {
-            var the_table = this.htmlTestCase.testDocument.getElementById(&quot;se-js-table&quot;)
-            var loc = this.currentRowIndex
-            var new_rows = get_new_rows()
-
-            // make the new statements visible on screen...
-            for (var i = 0; i &lt; 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 &lt;a href=&quot;#storedVars&quot;&gt;variable&lt;/a&gt; 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.  &lt;i&gt;Note that this command is deprecated; you should use assertSelectedLabel, assertSelectedValue, assertSelectedIndex, or assertSelectedId instead.&lt;/i&gt;
-     *
-     * &lt;p&gt;See the select command for more information about option locators.&lt;/p&gt;
-     *
-     * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
-     * @param optionLocator an option locator, typically just an option label (e.g. &quot;John Smith&quot;)
-     */
-    var element = this.page().findElement(selectLocator);
-    var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
-    if (element.selectedIndex == -1)
-    {
-        Assert.fail(&quot;No option selected&quot;);
-    }
-    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(&quot;Message must be provided&quot;);
-    }
-
-    currentTest.expectedFailure = message;
-    currentTest.expectedFailureType = &quot;failure&quot;;
-    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(&quot;Message must be provided&quot;);
-    }
-
-    currentTest.expectedFailure = message;
-    currentTest.expectedFailureType = &quot;error&quot;;
-    currentTest.expectedFailureJustSet = true;
-};
-
+/*
+* Copyright 2004 ThoughtWorks, Inc
+*
+*  Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+*  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 &quot;AS IS&quot; 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(&quot;link&quot;);
+        styleLink.rel = &quot;stylesheet&quot;;
+        styleLink.type = &quot;text/css&quot;;
+        if (browserVersion &amp;&amp; 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(&quot;link&quot;);
+            tempLink.href = &quot;selenium-test.css&quot;; // 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(/[^\/\\]+$/, &quot;selenium-test.css&quot;);
+            if (browserVersion.isIE &amp;&amp; window.location.protocol == &quot;file:&quot;) {
+                styleSheetPath = &quot;file:///&quot; + styleSheetPath;
+            }
+            styleLink.href = styleSheetPath;
+        }
+        // DGF You're only going to see this log message if you set defaultLogLevel=debug
+        LOG.debug(&quot;styleLink.href=&quot;+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(&quot;?&quot;) == -1 ? &quot;?&quot; : &quot;&amp;&quot;);
+        location += &quot;thisIsChrome=&quot; + isChrome + &quot;&amp;thisIsHTA=&quot; + 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 = &quot;about:blank&quot;;
+            this.frame.src = location;
+        } else {
+            this.frame.contentWindow.location.replace(location);
+        }
+    },
+
+    load: function(/* url, [callback] */) {
+        if (arguments.length &gt; 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(&quot;runInterval&quot;);
+        if (tempRunInterval) {
+            this.setRunInterval(tempRunInterval);
+        }
+        this.highlightOption.checked = this._getQueryParameter(&quot;highlight&quot;);
+    },
+
+    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 = &quot;cssPauseTest&quot;;
+        this.pauseButton.onclick = fnBindAsEventListener(this.pauseCurrentTest, this);
+    },
+
+    _switchPauseButtonToContinue: function() {
+        sel$('stepTest').disabled = false;
+        this.pauseButton.className = &quot;cssContinueTest&quot;;
+        this.pauseButton.onclick = fnBindAsEventListener(this.continueCurrentTest, this);
+    },
+
+    stepCurrentTest: function () {
+        this.setToPauseAtNextCommand();
+        currentTest.resume();
+    },
+
+    isAutomatedRun: function() {
+        return this._isQueryParameterTrue(&quot;auto&quot;);
+    },
+
+    shouldSaveResultsToFile: function() {
+        return this._isQueryParameterTrue(&quot;save&quot;);
+    },
+
+    closeAfterTests: function() {
+        return this._isQueryParameterTrue(&quot;close&quot;);
+    },
+
+    getTestSuiteName: function() {
+        return this._getQueryParameter(&quot;test&quot;);
+    },
+
+    getSingleTestName: function() {
+        return this._getQueryParameter(&quot;singletest&quot;);
+    },
+
+    getAutoUrl: function() {
+        return this._getQueryParameter(&quot;autoURL&quot;);
+    },
+    
+    getDefaultLogLevel: function() {
+        return this._getQueryParameter(&quot;defaultLogLevel&quot;);
+    },
+
+    getResultsUrl: function() {
+        return this._getQueryParameter(&quot;resultsUrl&quot;);
+    },
+
+    _acquireQueryString: function() {
+        if (this.queryString) return;
+        if (browserVersion.isHTA) {
+            var args = this._extractArgs();
+            if (args.length &lt; 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]+/, &quot;&quot;);
+        if (status) {
+            addClassName(this.trElement, &quot;status_&quot; + status);
+        }
+    },
+
+    select: function() {
+        addClassName(this.trElement, &quot;selected&quot;);
+        safeScrollIntoView(this.trElement);
+    },
+
+    unselect: function() {
+        removeClassName(this.trElement, &quot;selected&quot;);
+    },
+
+    markPassed: function() {
+        this.setStatus(&quot;passed&quot;);
+    },
+
+    markDone: function() {
+        this.setStatus(&quot;done&quot;);
+    },
+
+    markFailed: function() {
+        this.setStatus(&quot;failed&quot;);
+    }
+
+});
+
+var TitleRow = classCreate();
+objectExtend(TitleRow.prototype, AbstractResultAwareRow.prototype);
+objectExtend(TitleRow.prototype, {
+
+    initialize: function(trElement) {
+        this.trElement = trElement;
+        trElement.className = &quot;title&quot;;
+    }
+
+});
+
+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, &quot;breakpoint&quot;);
+        } else {
+            this.trElement.isBreakpoint = undefined;
+            removeClassName(this.trElement, &quot;breakpoint&quot;);
+        }
+    },
+
+    addBreakpointSupport: function() {
+        elementSetStyle(this.trElement, {&quot;cursor&quot; : &quot;pointer&quot;});
+        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(&quot;a&quot;)[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(&quot;div&quot;);
+        divElement.innerHTML = resultHTML;
+
+        var hiddenCell = this.trElement.ownerDocument.createElement(&quot;td&quot;);
+        hiddenCell.appendChild(divElement);
+        hiddenCell.style.display = &quot;none&quot;;
+
+        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 &lt; 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(&quot;table&quot;));
+        return tables[0];
+    },
+
+    isAvailable: function() {
+        return this.getTestTable() != null;
+    },
+
+    _collectSuiteRows: function () {
+        var result = [];
+        var tables = sel$A(this.suiteDocument.getElementsByTagName(&quot;table&quot;));
+        var testTable = tables[0];
+        for (rowNum = 1; rowNum &lt; 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 &lt; sel$A(this.suiteDocument.getElementsByTagName(&quot;table&quot;)).length; tableNum++) {
+            testTable = tables[tableNum];
+            for (rowNum = 1; rowNum &lt; 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 &gt;= 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 &gt;= 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 &quot;/postResults&quot;, but an alternative location
+// can be specified by providing a &quot;resultsUrl&quot; 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(&quot;form&quot;);
+        document.body.appendChild(form);
+
+        form.id = &quot;resultsForm&quot;;
+        form.method = &quot;post&quot;;
+        form.target = &quot;selenium_myiframe&quot;;
+
+        var resultsUrl = this.controlPanel.getResultsUrl();
+        if (!resultsUrl) {
+            resultsUrl = &quot;./postResults&quot;;
+        }
+
+        var actionAndParameters = resultsUrl.split('?', 2);
+        form.action = actionAndParameters[0];
+        var resultsUrlQueryString = actionAndParameters[1];
+
+        form.createHiddenField = function(name, value) {
+            input = document.createElement(&quot;input&quot;);
+            input.type = &quot;hidden&quot;;
+            input.name = name;
+            input.value = value;
+            this.appendChild(input);
+        };
+
+        if (resultsUrlQueryString) {
+            var clauses = resultsUrlQueryString.split('&amp;');
+            for (var i = 0; i &lt; 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(&quot;selenium.version&quot;, Selenium.version);
+        form.createHiddenField(&quot;selenium.revision&quot;, Selenium.revision);
+
+        form.createHiddenField(&quot;result&quot;, this.suiteFailed ? &quot;failed&quot; : &quot;passed&quot;);
+
+        form.createHiddenField(&quot;totalTime&quot;, Math.floor((this.metrics.currentTime - this.metrics.startTime) / 1000));
+        form.createHiddenField(&quot;numTestPasses&quot;, this.metrics.numTestPasses);
+        form.createHiddenField(&quot;numTestFailures&quot;, this.metrics.numTestFailures);
+        form.createHiddenField(&quot;numCommandPasses&quot;, this.metrics.numCommandPasses);
+        form.createHiddenField(&quot;numCommandFailures&quot;, this.metrics.numCommandFailures);
+        form.createHiddenField(&quot;numCommandErrors&quot;, this.metrics.numCommandErrors);
+
+        // Create an input for each test table.  The inputs are named
+        // testTable.1, testTable.2, etc.
+        for (rowNum = 1; rowNum &lt; this.suiteTable.rows.length; rowNum++) {
+            // If there is a second column, then add a new input
+            if (this.suiteTable.rows[rowNum].cells.length &gt; 1) {
+                var resultCell = this.suiteTable.rows[rowNum].cells[1];
+                form.createHiddenField(&quot;testTable.&quot; + rowNum, resultCell.innerHTML);
+                // remove the resultCell, so it's not included in the suite HTML
+                resultCell.parentNode.removeChild(resultCell);
+            }
+        }
+
+        form.createHiddenField(&quot;numTestTotal&quot;, rowNum-1);
+
+        // Add HTML for the suite itself
+        form.createHiddenField(&quot;suite&quot;, this.suiteTable.parentNode.innerHTML);
+
+        var logMessages = [];
+        while (LOG.pendingMessages.length &gt; 0) {
+            var msg = LOG.pendingMessages.shift();
+            logMessages.push(msg.type);
+            logMessages.push(&quot;: &quot;);
+            logMessages.push(msg.msg);
+            logMessages.push('\n');
+        }
+        var logOutput = logMessages.join(&quot;&quot;);
+        form.createHiddenField(&quot;log&quot;, 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 &lt; form.elements.length; i++) {
+            inputs[form.elements[i].name] = form.elements[i].value;
+        }
+        
+        var objFSO = new ActiveXObject(&quot;Scripting.FileSystemObject&quot;)
+        
+        // DGF get CSS
+        var styles = &quot;&quot;;
+        try {
+            var styleSheetPath = window.location.pathname.replace(/[^\/\\]+$/, &quot;selenium-test.css&quot;);
+            if (window.location.protocol == &quot;file:&quot;) {
+                var stylesFile = objFSO.OpenTextFile(styleSheetPath, 1);
+                styles = stylesFile.ReadAll();
+            } else {
+                var xhr = XmlHttp.create();
+                xhr.open(&quot;GET&quot;, styleSheetPath, false);
+                xhr.send(&quot;&quot;);
+                styles = xhr.responseText;
+            }
+        } catch (e) {}
+        
+        var scriptFile = objFSO.CreateTextFile(fileName);
+        
+        
+        scriptFile.WriteLine(&quot;&lt;html&gt;&lt;head&gt;&lt;title&gt;Test suite results&lt;/title&gt;&lt;style&gt;&quot;);
+        scriptFile.WriteLine(styles);
+        scriptFile.WriteLine(&quot;&lt;/style&gt;&quot;);
+        scriptFile.WriteLine(&quot;&lt;body&gt;\n&lt;h1&gt;Test suite results&lt;/h1&gt;&quot; +
+             &quot;\n\n&lt;table&gt;\n&lt;tr&gt;\n&lt;td&gt;result:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;result&quot;] + &quot;&lt;/td&gt;\n&quot; +
+             &quot;&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;totalTime:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;totalTime&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
+             &quot;&lt;tr&gt;\n&lt;td&gt;numTestTotal:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numTestTotal&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
+             &quot;&lt;tr&gt;\n&lt;td&gt;numTestPasses:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numTestPasses&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
+             &quot;&lt;tr&gt;\n&lt;td&gt;numTestFailures:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numTestFailures&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
+             &quot;&lt;tr&gt;\n&lt;td&gt;numCommandPasses:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numCommandPasses&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
+             &quot;&lt;tr&gt;\n&lt;td&gt;numCommandFailures:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numCommandFailures&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
+             &quot;&lt;tr&gt;\n&lt;td&gt;numCommandErrors:&lt;/td&gt;\n&lt;td&gt;&quot; + inputs[&quot;numCommandErrors&quot;] + &quot;&lt;/td&gt;\n&lt;/tr&gt;\n&quot; +
+             &quot;&lt;tr&gt;\n&lt;td&gt;&quot; + inputs[&quot;suite&quot;] + &quot;&lt;/td&gt;\n&lt;td&gt;&amp;nbsp;&lt;/td&gt;\n&lt;/tr&gt;&lt;/table&gt;&lt;table&gt;&quot;);
+        var testNum = inputs[&quot;numTestTotal&quot;];
+        
+        for (var rowNum = 1; rowNum &lt;= testNum; rowNum++) {
+            scriptFile.WriteLine(&quot;&lt;tr&gt;\n&lt;td&gt;&quot; + inputs[&quot;testTable.&quot; + rowNum] + &quot;&lt;/td&gt;\n&lt;td&gt;&amp;nbsp;&lt;/td&gt;\n&lt;/tr&gt;&quot;);
+        }
+        scriptFile.WriteLine(&quot;&lt;/table&gt;&lt;pre&gt;&quot;);
+        var log = inputs[&quot;log&quot;];
+        log=log.replace(/&amp;/gm,&quot;&amp;amp;&quot;).replace(/&lt;/gm,&quot;&amp;lt;&quot;).replace(/&gt;/gm,&quot;&amp;gt;&quot;).replace(/&quot;/gm,&quot;&amp;quot;&quot;).replace(/'/gm,&quot;&amp;apos;&quot;);
+        scriptFile.WriteLine(log);
+        scriptFile.WriteLine(&quot;&lt;/pre&gt;&lt;/body&gt;&lt;/html&gt;&quot;);
+        scriptFile.Close();
+    }
+});
+
+/** HtmlTestCase encapsulates an HTML test document */
+var HtmlTestCase = classCreate();
+objectExtend(HtmlTestCase.prototype, {
+
+    initialize: function(testDocument, htmlTestSuiteRow) {
+        if (testDocument == null) {
+            throw &quot;testDocument should not be null&quot;;
+        }
+        if (htmlTestSuiteRow == null) {
+            throw &quot;htmlTestSuiteRow should not be null&quot;;
+        }
+        this.testDocument = testDocument;
+        this.htmlTestSuiteRow = htmlTestSuiteRow;
+        this.headerRow = new TitleRow(this.testDocument.getElementsByTagName(&quot;tr&quot;)[0]);
+        this.commandRows = this._collectCommandRows();
+        this.nextCommandRowIndex = 0;
+        this._addBreakpointSupport();
+    },
+
+    _collectCommandRows: function () {
+        var commandRows = [];
+        var tables = sel$A(this.testDocument.getElementsByTagName(&quot;table&quot;));
+        var self = this;
+        for (var i = 0; i &lt; tables.length; i++) {
+            var table = tables[i];
+            var tableRows = sel$A(table.rows);
+            for (var j = 0; j &lt; 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 &gt;= 3;
+    },
+
+    reset: function() {
+        /**
+         * reset the test to runnable state
+         */
+        this.nextCommandRowIndex = 0;
+
+        this.setStatus('');
+        for (var i = 0; i &lt; this.commandRows.length; i++) {
+            var row = this.commandRows[i];
+            row.reset();
+        }
+
+        // remove any additional fake &quot;error&quot; 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(&quot;failed&quot;);
+        this.htmlTestSuiteRow.markFailed();
+    },
+
+    markPassed: function() {
+        this.setStatus(&quot;passed&quot;);
+        this.htmlTestSuiteRow.markPassed();
+    },
+
+    addErrorMessage: function(errorMsg, currentRow) {
+        errorMsg = errorMsg.replace(/ /g, String.fromCharCode(160)).replace(&quot;\n&quot;, '\\n');
+        if (currentRow) {
+            currentRow.markFailed(errorMsg);
+        } else {
+            var errorElement = this.testDocument.createElement(&quot;p&quot;);
+            errorElement.id = &quot;error&quot;;
+            setText(errorElement, errorMsg);
+            this.testDocument.body.appendChild(errorElement);
+            errorElement.className = &quot;status_failed&quot;;
+        }
+    },
+
+    _addBreakpointSupport: function() {
+        for (var i = 0; i &lt; this.commandRows.length; i++) {
+            var row = this.commandRows[i];
+            row.addBreakpointSupport();
+        }
+    },
+
+    hasMoreCommandRows: function() {
+        return this.nextCommandRowIndex &lt; this.commandRows.length;
+    },
+
+    getNextCommandRow: function() {
+        if (this.hasMoreCommandRows()) {
+            return this.commandRows[this.nextCommandRowIndex++];
+        }
+        return null;
+    }
+
+});
+
+
+// TODO: split out an JavascriptTestCase class to handle the &quot;sejs&quot; stuff
+
+var get_new_rows = function() {
+    var row_array = new Array();
+    for (var i = 0; i &lt; 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 = '&lt;td style=&quot;display:none;&quot; class=&quot;js&quot;&gt;getEval&lt;/td&gt;' +
+                  '&lt;td style=&quot;display:none;&quot;&gt;currentTest.doNextCommand()&lt;/td&gt;' +
+                  '&lt;td style=&quot;white-space: pre;&quot;&gt;' + new_source + '&lt;/td&gt;' +
+                  '&lt;td&gt;&lt;/td&gt;'
+
+        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) + &quot;:&quot; + this._pad(seconds));
+    },
+
+// Puts a leading 0 on num if it is less than 10
+    _pad: function(num) {
+        return (num &gt; 9) ? num : &quot;0&quot; + 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(&quot;Starting test &quot; + 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 = &quot;Expected &quot; + this.expectedFailureType + &quot; did not occur.&quot;;
+            } else {
+                if (PatternMatcher.matches(this.expectedFailure, result.failureMessage)) {
+                    var failureType = result.error ? &quot;error&quot; : &quot;failure&quot;;
+                    if (failureType == this.expectedFailureType) {
+                        result.failed = false;
+                        result.passed = true;
+                    } else {
+                        result.failed = true;
+                        result.failureMessage = &quot;Expected &quot;+this.expectedFailureType+&quot;, but &quot;+failureType+&quot; occurred instead&quot;;
+                    }
+                } else {
+                    result.failed = true;
+                    result.failureMessage = &quot;Expected &quot; + this.expectedFailureType + &quot; message '&quot; + this.expectedFailure
+                                            + &quot;' but was '&quot; + result.failureMessage + &quot;'&quot;;
+                }
+            }
+            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(&quot;currentTest.recordFailure: &quot; + 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 &gt; 0) {
+            var the_table = this.htmlTestCase.testDocument.getElementById(&quot;se-js-table&quot;)
+            var loc = this.currentRowIndex
+            var new_rows = get_new_rows()
+
+            // make the new statements visible on screen...
+            for (var i = 0; i &lt; 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 &lt;a href=&quot;#storedVars&quot;&gt;variable&lt;/a&gt; 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.  &lt;i&gt;Note that this command is deprecated; you should use assertSelectedLabel, assertSelectedValue, assertSelectedIndex, or assertSelectedId instead.&lt;/i&gt;
+     *
+     * &lt;p&gt;See the select command for more information about option locators.&lt;/p&gt;
+     *
+     * @param selectLocator an &lt;a href=&quot;#locators&quot;&gt;element locator&lt;/a&gt; identifying a drop-down menu
+     * @param optionLocator an option locator, typically just an option label (e.g. &quot;John Smith&quot;)
+     */
+    var element = this.page().findElement(selectLocator);
+    var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
+    if (element.selectedIndex == -1)
+    {
+        Assert.fail(&quot;No option selected&quot;);
+    }
+    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(&quot;Message must be provided&quot;);
+    }
+
+    currentTest.expectedFailure = message;
+    currentTest.expectedFailureType = &quot;failure&quot;;
+    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(&quot;Message must be provided&quot;);
+    }
+
+    currentTest.expectedFailure = message;
+    currentTest.expectedFailureType = &quot;error&quot;;
+    currentTest.expectedFailureJustSet = true;
+};
+</diff>
      <filename>testframework/selenium/scripts/selenium-testrunner.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,4169 +1,4169 @@
-/*
- * xpath.js
- *
- * An XPath 1.0 library for JavaScript.
- *
- * Cameron McCormack &lt;cam (at) mcc.id.au&gt;
- *
- * This work is licensed under the Creative Commons Attribution-ShareAlike
- * License. To view a copy of this license, visit
- * 
- *   http://creativecommons.org/licenses/by-sa/2.0/
- *
- * or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford,
- * California 94305, USA.
- *
- * Revision 18: October 27, 2005
- *   DOM 3 XPath support.  Caveats:
- *     - namespace prefixes aren't resolved in XPathEvaluator.createExpression,
- *       but in XPathExpression.evaluate.
- *     - XPathResult.invalidIteratorState is not implemented.
- *
- * Revision 17: October 25, 2005
- *   Some core XPath function fixes and a patch to avoid crashing certain
- *   versions of MSXML in PathExpr.prototype.getOwnerElement, thanks to
- *   S&#233;bastien Cramatte &lt;contact (at) zeninteractif.com&gt;.
- *
- * Revision 16: September 22, 2005
- *   Workarounds for some IE 5.5 deficiencies.
- *   Fixed problem with prefix node tests on attribute nodes.
- *
- * Revision 15: May 21, 2005
- *   Fixed problem with QName node tests on elements with an xmlns=&quot;...&quot;.
- *
- * Revision 14: May 19, 2005
- *   Fixed QName node tests on attribute node regression.
- *
- * Revision 13: May 3, 2005
- *   Node tests are case insensitive now if working in an HTML DOM.
- *
- * Revision 12: April 26, 2005
- *   Updated licence.  Slight code changes to enable use of Dean
- *   Edwards' script compression, http://dean.edwards.name/packer/ .
- *
- * Revision 11: April 23, 2005
- *   Fixed bug with 'and' and 'or' operators, fix thanks to
- *   Sandy McArthur &lt;sandy (at) mcarthur.org&gt;.
- *
- * Revision 10: April 15, 2005
- *   Added support for a virtual root node, supposedly helpful for
- *   implementing XForms.  Fixed problem with QName node tests and
- *   the parent axis.
- *
- * Revision 9: March 17, 2005
- *   Namespace resolver tweaked so using the document node as the context
- *   for namespace lookups is equivalent to using the document element.
- *
- * Revision 8: February 13, 2005
- *   Handle implicit declaration of 'xmlns' namespace prefix.
- *   Fixed bug when comparing nodesets.
- *   Instance data can now be associated with a FunctionResolver, and
- *     workaround for MSXML not supporting 'localName' and 'getElementById',
- *     thanks to Grant Gongaware.
- *   Fix a few problems when the context node is the root node.
- *   
- * Revision 7: February 11, 2005
- *   Default namespace resolver fix from Grant Gongaware
- *   &lt;grant (at) gongaware.com&gt;.
- *
- * Revision 6: February 10, 2005
- *   Fixed bug in 'number' function.
- *
- * Revision 5: February 9, 2005
- *   Fixed bug where text nodes not getting converted to string values.
- *
- * Revision 4: January 21, 2005
- *   Bug in 'name' function, fix thanks to Bill Edney.
- *   Fixed incorrect processing of namespace nodes.
- *   Fixed NamespaceResolver to resolve 'xml' namespace.
- *   Implemented union '|' operator.
- *
- * Revision 3: January 14, 2005
- *   Fixed bug with nodeset comparisons, bug lexing &lt; and &gt;.
- *
- * Revision 2: October 26, 2004
- *   QName node test namespace handling fixed.  Few other bug fixes.
- *   
- * Revision 1: August 13, 2004
- *   Bug fixes from William J. Edney &lt;bedney (at) technicalpursuit.com&gt;.
- *   Added minimal licence.
- *
- * Initial version: June 14, 2004
- */
-
-// XPathParser ///////////////////////////////////////////////////////////////
-
-XPathParser.prototype = new Object();
-XPathParser.prototype.constructor = XPathParser;
-XPathParser.superclass = Object.prototype;
-
-function XPathParser() {
-	this.init();
-}
-
-XPathParser.prototype.init = function() {
-	this.reduceActions = [];
-
-	this.reduceActions[3] = function(rhs) {
-		return new OrOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[5] = function(rhs) {
-		return new AndOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[7] = function(rhs) {
-		return new EqualsOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[8] = function(rhs) {
-		return new NotEqualOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[10] = function(rhs) {
-		return new LessThanOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[11] = function(rhs) {
-		return new GreaterThanOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[12] = function(rhs) {
-		return new LessThanOrEqualOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[13] = function(rhs) {
-		return new GreaterThanOrEqualOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[15] = function(rhs) {
-		return new PlusOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[16] = function(rhs) {
-		return new MinusOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[18] = function(rhs) {
-		return new MultiplyOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[19] = function(rhs) {
-		return new DivOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[20] = function(rhs) {
-		return new ModOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[22] = function(rhs) {
-		return new UnaryMinusOperation(rhs[1]);
-	};
-	this.reduceActions[24] = function(rhs) {
-		return new BarOperation(rhs[0], rhs[2]);
-	};
-	this.reduceActions[25] = function(rhs) {
-		return new PathExpr(undefined, undefined, rhs[0]);
-	};
-	this.reduceActions[27] = function(rhs) {
-		rhs[0].locationPath = rhs[2];
-		return rhs[0];
-	};
-	this.reduceActions[28] = function(rhs) {
-		rhs[0].locationPath = rhs[2];
-		rhs[0].locationPath.steps.unshift(new Step(Step.DESCENDANTORSELF, new NodeTest(NodeTest.NODE, undefined), []));
-		return rhs[0];
-	};
-	this.reduceActions[29] = function(rhs) {
-		return new PathExpr(rhs[0], [], undefined);
-	};
-	this.reduceActions[30] = function(rhs) {
-		if (Utilities.instance_of(rhs[0], PathExpr)) {
-			if (rhs[0].filterPredicates == undefined) {
-				rhs[0].filterPredicates = [];
-			}
-			rhs[0].filterPredicates.push(rhs[1]);
-			return rhs[0];
-		} else {
-			return new PathExpr(rhs[0], [rhs[1]], undefined);
-		}
-	};
-	this.reduceActions[32] = function(rhs) {
-		return rhs[1];
-	};
-	this.reduceActions[33] = function(rhs) {
-		return new XString(rhs[0]);
-	};
-	this.reduceActions[34] = function(rhs) {
-		return new XNumber(rhs[0]);
-	};
-	this.reduceActions[36] = function(rhs) {
-		return new FunctionCall(rhs[0], []);
-	};
-	this.reduceActions[37] = function(rhs) {
-		return new FunctionCall(rhs[0], rhs[2]);
-	};
-	this.reduceActions[38] = function(rhs) {
-		return [ rhs[0] ];
-	};
-	this.reduceActions[39] = function(rhs) {
-		rhs[2].unshift(rhs[0]);
-		return rhs[2];
-	};
-	this.reduceActions[43] = function(rhs) {
-		return new LocationPath(true, []);
-	};
-	this.reduceActions[44] = function(rhs) {
-		rhs[1].absolute = true;
-		return rhs[1];
-	};
-	this.reduceActions[46] = function(rhs) {
-		return new LocationPath(false, [ rhs[0] ]);
-	};
-	this.reduceActions[47] = function(rhs) {
-		rhs[0].steps.push(rhs[2]);
-		return rhs[0];
-	};
-	this.reduceActions[49] = function(rhs) {
-		return new Step(rhs[0], rhs[1], []);
-	};
-	this.reduceActions[50] = function(rhs) {
-		return new Step(Step.CHILD, rhs[0], []);
-	};
-	this.reduceActions[51] = function(rhs) {
-		return new Step(rhs[0], rhs[1], rhs[2]);
-	};
-	this.reduceActions[52] = function(rhs) {
-		return new Step(Step.CHILD, rhs[0], rhs[1]);
-	};
-	this.reduceActions[54] = function(rhs) {
-		return [ rhs[0] ];
-	};
-	this.reduceActions[55] = function(rhs) {
-		rhs[1].unshift(rhs[0]);
-		return rhs[1];
-	};
-	this.reduceActions[56] = function(rhs) {
-		if (rhs[0] == &quot;ancestor&quot;) {
-			return Step.ANCESTOR;
-		} else if (rhs[0] == &quot;ancestor-or-self&quot;) {
-			return Step.ANCESTORORSELF;
-		} else if (rhs[0] == &quot;attribute&quot;) {
-			return Step.ATTRIBUTE;
-		} else if (rhs[0] == &quot;child&quot;) {
-			return Step.CHILD;
-		} else if (rhs[0] == &quot;descendant&quot;) {
-			return Step.DESCENDANT;
-		} else if (rhs[0] == &quot;descendant-or-self&quot;) {
-			return Step.DESCENDANTORSELF;
-		} else if (rhs[0] == &quot;following&quot;) {
-			return Step.FOLLOWING;
-		} else if (rhs[0] == &quot;following-sibling&quot;) {
-			return Step.FOLLOWINGSIBLING;
-		} else if (rhs[0] == &quot;namespace&quot;) {
-			return Step.NAMESPACE;
-		} else if (rhs[0] == &quot;parent&quot;) {
-			return Step.PARENT;
-		} else if (rhs[0] == &quot;preceding&quot;) {
-			return Step.PRECEDING;
-		} else if (rhs[0] == &quot;preceding-sibling&quot;) {
-			return Step.PRECEDINGSIBLING;
-		} else if (rhs[0] == &quot;self&quot;) {
-			return Step.SELF;
-		}
-		return -1;
-	};
-	this.reduceActions[57] = function(rhs) {
-		return Step.ATTRIBUTE;
-	};
-	this.reduceActions[59] = function(rhs) {
-		if (rhs[0] == &quot;comment&quot;) {
-			return new NodeTest(NodeTest.COMMENT, undefined);
-		} else if (rhs[0] == &quot;text&quot;) {
-			return new NodeTest(NodeTest.TEXT, undefined);
-		} else if (rhs[0] == &quot;processing-instruction&quot;) {
-			return new NodeTest(NodeTest.PI, undefined);
-		} else if (rhs[0] == &quot;node&quot;) {
-			return new NodeTest(NodeTest.NODE, undefined);
-		}
-		return new NodeTest(-1, undefined);
-	};
-	this.reduceActions[60] = function(rhs) {
-		return new NodeTest(NodeTest.PI, rhs[2]);
-	};
-	this.reduceActions[61] = function(rhs) {
-		return rhs[1];
-	};
-	this.reduceActions[63] = function(rhs) {
-		rhs[1].absolute = true;
-		rhs[1].steps.unshift(new Step(Step.DESCENDANTORSELF, new NodeTest(NodeTest.NODE, undefined), []));
-		return rhs[1];
-	};
-	this.reduceActions[64] = function(rhs) {
-		rhs[0].steps.push(new Step(Step.DESCENDANTORSELF, new NodeTest(NodeTest.NODE, undefined), []));
-		rhs[0].steps.push(rhs[2]);
-		return rhs[0];
-	};
-	this.reduceActions[65] = function(rhs) {
-		return new Step(Step.SELF, new NodeTest(NodeTest.NODE, undefined), []);
-	};
-	this.reduceActions[66] = function(rhs) {
-		return new Step(Step.PARENT, new NodeTest(NodeTest.NODE, undefined), []);
-	};
-	this.reduceActions[67] = function(rhs) {
-		return new VariableReference(rhs[1]);
-	};
-	this.reduceActions[68] = function(rhs) {
-		return new NodeTest(NodeTest.NAMETESTANY, undefined);
-	};
-	this.reduceActions[69] = function(rhs) {
-		var prefix = rhs[0].substring(0, rhs[0].indexOf(&quot;:&quot;));
-		return new NodeTest(NodeTest.NAMETESTPREFIXANY, prefix);
-	};
-	this.reduceActions[70] = function(rhs) {
-		return new NodeTest(NodeTest.NAMETESTQNAME, rhs[0]);
-	};
-};
-
-XPathParser.actionTable = [
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot;                 s                  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
-	&quot;                rrrrr               &quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot;rs  rrrrrrrr s  sssssrrrrrr  rrs rs &quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot;                            s       &quot;,
-	&quot;                            s       &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;  s                                 &quot;,
-	&quot;                            s       &quot;,
-	&quot; s           s  sssss          s  s &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
-	&quot;a                                   &quot;,
-	&quot;r       s                    rr  r  &quot;,
-	&quot;r      sr                    rr  r  &quot;,
-	&quot;r   s  rr            s       rr  r  &quot;,
-	&quot;r   rssrr            rss     rr  r  &quot;,
-	&quot;r   rrrrr            rrrss   rr  r  &quot;,
-	&quot;r   rrrrrsss         rrrrr   rr  r  &quot;,
-	&quot;r   rrrrrrrr         rrrrr   rr  r  &quot;,
-	&quot;r   rrrrrrrr         rrrrrs  rr  r  &quot;,
-	&quot;r   rrrrrrrr         rrrrrr  rr  r  &quot;,
-	&quot;r   rrrrrrrr         rrrrrr  rr  r  &quot;,
-	&quot;r  srrrrrrrr         rrrrrrs rr sr  &quot;,
-	&quot;r  srrrrrrrr         rrrrrrs rr  r  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;r   rrrrrrrr         rrrrrr  rr  r  &quot;,
-	&quot;r   rrrrrrrr         rrrrrr  rr  r  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
-	&quot;                sssss               &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr sr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;                             s      &quot;,
-	&quot;r  srrrrrrrr         rrrrrrs rr  r  &quot;,
-	&quot;r   rrrrrrrr         rrrrr   rr  r  &quot;,
-	&quot;              s                     &quot;,
-	&quot;                             s      &quot;,
-	&quot;                rrrrr               &quot;,
-	&quot; s s        sssssssss    s sss s  ss&quot;,
-	&quot;r  srrrrrrrr         rrrrrrs rr  r  &quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s s        sssssssss      ss  s  ss&quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot; s           s  sssss          s  s &quot;,
-	&quot; s           s  sssss          s  s &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot; s           s  sssss          s  s &quot;,
-	&quot; s           s  sssss          s  s &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr sr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr sr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;                             s      &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;                             rr     &quot;,
-	&quot;                             s      &quot;,
-	&quot;                             rs     &quot;,
-	&quot;r      sr                    rr  r  &quot;,
-	&quot;r   s  rr            s       rr  r  &quot;,
-	&quot;r   rssrr            rss     rr  r  &quot;,
-	&quot;r   rssrr            rss     rr  r  &quot;,
-	&quot;r   rrrrr            rrrss   rr  r  &quot;,
-	&quot;r   rrrrr            rrrss   rr  r  &quot;,
-	&quot;r   rrrrr            rrrss   rr  r  &quot;,
-	&quot;r   rrrrr            rrrss   rr  r  &quot;,
-	&quot;r   rrrrrsss         rrrrr   rr  r  &quot;,
-	&quot;r   rrrrrsss         rrrrr   rr  r  &quot;,
-	&quot;r   rrrrrrrr         rrrrr   rr  r  &quot;,
-	&quot;r   rrrrrrrr         rrrrr   rr  r  &quot;,
-	&quot;r   rrrrrrrr         rrrrr   rr  r  &quot;,
-	&quot;r   rrrrrrrr         rrrrrr  rr  r  &quot;,
-	&quot;                                 r  &quot;,
-	&quot;                                 s  &quot;,
-	&quot;r  srrrrrrrr         rrrrrrs rr  r  &quot;,
-	&quot;r  srrrrrrrr         rrrrrrs rr  r  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot; s s        sssssssss    s ss  s  ss&quot;,
-	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
-	&quot;                             r      &quot;
-];
-
-XPathParser.actionTableNumber = [
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot;                 J                  &quot;,
-	&quot;a  aaaaaaaaa         aaaaaaa aa  a  &quot;,
-	&quot;                YYYYY               &quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot;K1  KKKKKKKK .  +*)('KKKKKK  KK# K\&quot; &quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot;                            N       &quot;,
-	&quot;                            O       &quot;,
-	&quot;e  eeeeeeeee         eeeeeee ee ee  &quot;,
-	&quot;f  fffffffff         fffffff ff ff  &quot;,
-	&quot;d  ddddddddd         ddddddd dd dd  &quot;,
-	&quot;B  BBBBBBBBB         BBBBBBB BB BB  &quot;,
-	&quot;A  AAAAAAAAA         AAAAAAA AA AA  &quot;,
-	&quot;  P                                 &quot;,
-	&quot;                            Q       &quot;,
-	&quot; 1           .  +*)('          #  \&quot; &quot;,
-	&quot;b  bbbbbbbbb         bbbbbbb bb  b  &quot;,
-	&quot;                                    &quot;,
-	&quot;!       S                    !!  !  &quot;,
-	&quot;\&quot;      T\&quot;                    \&quot;\&quot;  \&quot;  &quot;,
-	&quot;$   V  $$            U       $$  $  &quot;,
-	&quot;&amp;   &amp;ZY&amp;&amp;            &amp;XW     &amp;&amp;  &amp;  &quot;,
-	&quot;)   )))))            )))\\[   ))  )  &quot;,
-	&quot;.   ....._^]         .....   ..  .  &quot;,
-	&quot;1   11111111         11111   11  1  &quot;,
-	&quot;5   55555555         55555`  55  5  &quot;,
-	&quot;7   77777777         777777  77  7  &quot;,
-	&quot;9   99999999         999999  99  9  &quot;,
-	&quot;:  c::::::::         ::::::b :: a:  &quot;,
-	&quot;I  fIIIIIIII         IIIIIIe II  I  &quot;,
-	&quot;=  =========         ======= == ==  &quot;,
-	&quot;?  ?????????         ??????? ?? ??  &quot;,
-	&quot;C  CCCCCCCCC         CCCCCCC CC CC  &quot;,
-	&quot;J   JJJJJJJJ         JJJJJJ  JJ  J  &quot;,
-	&quot;M   MMMMMMMM         MMMMMM  MM  M  &quot;,
-	&quot;N  NNNNNNNNN         NNNNNNN NN  N  &quot;,
-	&quot;P  PPPPPPPPP         PPPPPPP PP  P  &quot;,
-	&quot;                +*)('               &quot;,
-	&quot;R  RRRRRRRRR         RRRRRRR RR aR  &quot;,
-	&quot;U  UUUUUUUUU         UUUUUUU UU  U  &quot;,
-	&quot;Z  ZZZZZZZZZ         ZZZZZZZ ZZ ZZ  &quot;,
-	&quot;c  ccccccccc         ccccccc cc cc  &quot;,
-	&quot;                             j      &quot;,
-	&quot;L  fLLLLLLLL         LLLLLLe LL  L  &quot;,
-	&quot;6   66666666         66666   66  6  &quot;,
-	&quot;              k                     &quot;,
-	&quot;                             l      &quot;,
-	&quot;                XXXXX               &quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$m #  \&quot;!&quot;,
-	&quot;_  f________         ______e __  _  &quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('      %$  #  \&quot;!&quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot; 1           .  +*)('          #  \&quot; &quot;,
-	&quot; 1           .  +*)('          #  \&quot; &quot;,
-	&quot;&gt;  &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;         &gt;&gt;&gt;&gt;&gt;&gt;&gt; &gt;&gt; &gt;&gt;  &quot;,
-	&quot; 1           .  +*)('          #  \&quot; &quot;,
-	&quot; 1           .  +*)('          #  \&quot; &quot;,
-	&quot;Q  QQQQQQQQQ         QQQQQQQ QQ aQ  &quot;,
-	&quot;V  VVVVVVVVV         VVVVVVV VV aV  &quot;,
-	&quot;T  TTTTTTTTT         TTTTTTT TT  T  &quot;,
-	&quot;@  @@@@@@@@@         @@@@@@@ @@ @@  &quot;,
-	&quot;                             \x87      &quot;,
-	&quot;[  [[[[[[[[[         [[[[[[[ [[ [[  &quot;,
-	&quot;D  DDDDDDDDD         DDDDDDD DD DD  &quot;,
-	&quot;                             HH     &quot;,
-	&quot;                             \x88      &quot;,
-	&quot;                             F\x89     &quot;,
-	&quot;#      T#                    ##  #  &quot;,
-	&quot;%   V  %%            U       %%  %  &quot;,
-	&quot;'   'ZY''            'XW     ''  '  &quot;,
-	&quot;(   (ZY((            (XW     ((  (  &quot;,
-	&quot;+   +++++            +++\\[   ++  +  &quot;,
-	&quot;*   *****            ***\\[   **  *  &quot;,
-	&quot;-   -----            ---\\[   --  -  &quot;,
-	&quot;,   ,,,,,            ,,,\\[   ,,  ,  &quot;,
-	&quot;0   00000_^]         00000   00  0  &quot;,
-	&quot;/   /////_^]         /////   //  /  &quot;,
-	&quot;2   22222222         22222   22  2  &quot;,
-	&quot;3   33333333         33333   33  3  &quot;,
-	&quot;4   44444444         44444   44  4  &quot;,
-	&quot;8   88888888         888888  88  8  &quot;,
-	&quot;                                 ^  &quot;,
-	&quot;                                 \x8a  &quot;,
-	&quot;;  f;;;;;;;;         ;;;;;;e ;;  ;  &quot;,
-	&quot;&lt;  f&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;         &lt;&lt;&lt;&lt;&lt;&lt;e &lt;&lt;  &lt;  &quot;,
-	&quot;O  OOOOOOOOO         OOOOOOO OO  O  &quot;,
-	&quot;`  `````````         ``````` ``  `  &quot;,
-	&quot;S  SSSSSSSSS         SSSSSSS SS  S  &quot;,
-	&quot;W  WWWWWWWWW         WWWWWWW WW  W  &quot;,
-	&quot;\\  \\\\\\\\\\\\\\\\\\         \\\\\\\\\\\\\\ \\\\ \\\\  &quot;,
-	&quot;E  EEEEEEEEE         EEEEEEE EE EE  &quot;,
-	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
-	&quot;]  ]]]]]]]]]         ]]]]]]] ]] ]]  &quot;,
-	&quot;                             G      &quot;
-];
-
-XPathParser.gotoTable = [
-	&quot;3456789:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;L456789:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;            M        EFGH IJ &quot;,
-	&quot;       N;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;            S        EFGH IJ &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;              e              &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                        h  J &quot;,
-	&quot;              i          j   &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;o456789:;&lt;=&gt;?@ ABpqCDEFGH IJ &quot;,
-	&quot;                             &quot;,
-	&quot;  r6789:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;   s789:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;    t89:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;    u89:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;     v9:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;     w9:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;     x9:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;     y9:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;      z:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;      {:;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;       |;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;       };&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;       ~;&lt;=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;         \x7f=&gt;?@ AB  CDEFGH IJ &quot;,
-	&quot;\x80456789:;&lt;=&gt;?@ AB  CDEFGH IJ\x81&quot;,
-	&quot;            \x82        EFGH IJ &quot;,
-	&quot;            \x83        EFGH IJ &quot;,
-	&quot;                             &quot;,
-	&quot;                     \x84 GH IJ &quot;,
-	&quot;                     \x85 GH IJ &quot;,
-	&quot;              i          \x86   &quot;,
-	&quot;              i          \x87   &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;,
-	&quot;o456789:;&lt;=&gt;?@ AB\x8cqCDEFGH IJ &quot;,
-	&quot;                             &quot;,
-	&quot;                             &quot;
-];
-
-XPathParser.productions = [
-	[1, 1, 2],
-	[2, 1, 3],
-	[3, 1, 4],
-	[3, 3, 3, -9, 4],
-	[4, 1, 5],
-	[4, 3, 4, -8, 5],
-	[5, 1, 6],
-	[5, 3, 5, -22, 6],
-	[5, 3, 5, -5, 6],
-	[6, 1, 7],
-	[6, 3, 6, -23, 7],
-	[6, 3, 6, -24, 7],
-	[6, 3, 6, -6, 7],
-	[6, 3, 6, -7, 7],
-	[7, 1, 8],
-	[7, 3, 7, -25, 8],
-	[7, 3, 7, -26, 8],
-	[8, 1, 9],
-	[8, 3, 8, -12, 9],
-	[8, 3, 8, -11, 9],
-	[8, 3, 8, -10, 9],
-	[9, 1, 10],
-	[9, 2, -26, 9],
-	[10, 1, 11],
-	[10, 3, 10, -27, 11],
-	[11, 1, 12],
-	[11, 1, 13],
-	[11, 3, 13, -28, 14],
-	[11, 3, 13, -4, 14],
-	[13, 1, 15],
-	[13, 2, 13, 16],
-	[15, 1, 17],
-	[15, 3, -29, 2, -30],
-	[15, 1, -15],
-	[15, 1, -16],
-	[15, 1, 18],
-	[18, 3, -13, -29, -30],
-	[18, 4, -13, -29, 19, -30],
-	[19, 1, 20],
-	[19, 3, 20, -31, 19],
-	[20, 1, 2],
-	[12, 1, 14],
-	[12, 1, 21],
-	[21, 1, -28],
-	[21, 2, -28, 14],
-	[21, 1, 22],
-	[14, 1, 23],
-	[14, 3, 14, -28, 23],
-	[14, 1, 24],
-	[23, 2, 25, 26],
-	[23, 1, 26],
-	[23, 3, 25, 26, 27],
-	[23, 2, 26, 27],
-	[23, 1, 28],
-	[27, 1, 16],
-	[27, 2, 16, 27],
-	[25, 2, -14, -3],
-	[25, 1, -32],
-	[26, 1, 29],
-	[26, 3, -20, -29, -30],
-	[26, 4, -21, -29, -15, -30],
-	[16, 3, -33, 30, -34],
-	[30, 1, 2],
-	[22, 2, -4, 14],
-	[24, 3, 14, -4, 23],
-	[28, 1, -35],
-	[28, 1, -2],
-	[17, 2, -36, -18],
-	[29, 1, -17],
-	[29, 1, -19],
-	[29, 1, -18]
-];
-
-XPathParser.DOUBLEDOT = 2;
-XPathParser.DOUBLECOLON = 3;
-XPathParser.DOUBLESLASH = 4;
-XPathParser.NOTEQUAL = 5;
-XPathParser.LESSTHANOREQUAL = 6;
-XPathParser.GREATERTHANOREQUAL = 7;
-XPathParser.AND = 8;
-XPathParser.OR = 9;
-XPathParser.MOD = 10;
-XPathParser.DIV = 11;
-XPathParser.MULTIPLYOPERATOR = 12;
-XPathParser.FUNCTIONNAME = 13;
-XPathParser.AXISNAME = 14;
-XPathParser.LITERAL = 15;
-XPathParser.NUMBER = 16;
-XPathParser.ASTERISKNAMETEST = 17;
-XPathParser.QNAME = 18;
-XPathParser.NCNAMECOLONASTERISK = 19;
-XPathParser.NODETYPE = 20;
-XPathParser.PROCESSINGINSTRUCTIONWITHLITERAL = 21;
-XPathParser.EQUALS = 22;
-XPathParser.LESSTHAN = 23;
-XPathParser.GREATERTHAN = 24;
-XPathParser.PLUS = 25;
-XPathParser.MINUS = 26;
-XPathParser.BAR = 27;
-XPathParser.SLASH = 28;
-XPathParser.LEFTPARENTHESIS = 29;
-XPathParser.RIGHTPARENTHESIS = 30;
-XPathParser.COMMA = 31;
-XPathParser.AT = 32;
-XPathParser.LEFTBRACKET = 33;
-XPathParser.RIGHTBRACKET = 34;
-XPathParser.DOT = 35;
-XPathParser.DOLLAR = 36;
-
-XPathParser.prototype.tokenize = function(s1) {
-	var types = [];
-	var values = [];
-	var s = s1 + '\0';
-
-	var pos = 0;
-	var c = s.charAt(pos++);
-	while (1) {
-		while (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
-			c = s.charAt(pos++);
-		}
-		if (c == '\0' || pos &gt;= s.length) {
-			break;
-		}
-
-		if (c == '(') {
-			types.push(XPathParser.LEFTPARENTHESIS);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		if (c == ')') {
-			types.push(XPathParser.RIGHTPARENTHESIS);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		if (c == '[') {
-			types.push(XPathParser.LEFTBRACKET);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		if (c == ']') {
-			types.push(XPathParser.RIGHTBRACKET);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		if (c == '@') {
-			types.push(XPathParser.AT);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		if (c == ',') {
-			types.push(XPathParser.COMMA);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		if (c == '|') {
-			types.push(XPathParser.BAR);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		if (c == '+') {
-			types.push(XPathParser.PLUS);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		if (c == '-') {
-			types.push(XPathParser.MINUS);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		if (c == '=') {
-			types.push(XPathParser.EQUALS);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		if (c == '$') {
-			types.push(XPathParser.DOLLAR);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-		
-		if (c == '.') {
-			c = s.charAt(pos++);
-			if (c == '.') {
-				types.push(XPathParser.DOUBLEDOT);
-				values.push(&quot;..&quot;);
-				c = s.charAt(pos++);
-				continue;
-			}
-			if (c &gt;= '0' &amp;&amp; c &lt;= '9') {
-				var number = &quot;.&quot; + c;
-				c = s.charAt(pos++);
-				while (c &gt;= '0' &amp;&amp; c &lt;= '9') {
-					number += c;
-					c = s.charAt(pos++);
-				}
-				types.push(XPathParser.NUMBER);
-				values.push(number);
-				continue;
-			}
-			types.push(XPathParser.DOT);
-			values.push('.');
-			continue;
-		}
-
-		if (c == '\'' || c == '&quot;') {
-			var delimiter = c;
-			var literal = &quot;&quot;;
-			while ((c = s.charAt(pos++)) != delimiter) {
-				literal += c;
-			}
-			types.push(XPathParser.LITERAL);
-			values.push(literal);
-			c = s.charAt(pos++);
-			continue;
-		}
-
-		if (c &gt;= '0' &amp;&amp; c &lt;= '9') {
-			var number = c;
-			c = s.charAt(pos++);
-			while (c &gt;= '0' &amp;&amp; c &lt;= '9') {
-				number += c;
-				c = s.charAt(pos++);
-			}
-			if (c == '.') {
-				if (s.charAt(pos) &gt;= '0' &amp;&amp; s.charAt(pos) &lt;= '9') {
-					number += c;
-					number += s.charAt(pos++);
-					c = s.charAt(pos++);
-					while (c &gt;= '0' &amp;&amp; c &lt;= '9') {
-						number += c;
-						c = s.charAt(pos++);
-					}
-				}
-			}
-			types.push(XPathParser.NUMBER);
-			values.push(number);
-			continue;
-		}
-
-		if (c == '*') {
-			if (types.length &gt; 0) {
-				var last = types[types.length - 1];
-				if (last != XPathParser.AT
-						&amp;&amp; last != XPathParser.DOUBLECOLON
-						&amp;&amp; last != XPathParser.LEFTPARENTHESIS
-						&amp;&amp; last != XPathParser.LEFTBRACKET
-						&amp;&amp; last != XPathParser.AND
-						&amp;&amp; last != XPathParser.OR
-						&amp;&amp; last != XPathParser.MOD
-						&amp;&amp; last != XPathParser.DIV
-						&amp;&amp; last != XPathParser.MULTIPLYOPERATOR
-						&amp;&amp; last != XPathParser.SLASH
-						&amp;&amp; last != XPathParser.DOUBLESLASH
-						&amp;&amp; last != XPathParser.BAR
-						&amp;&amp; last != XPathParser.PLUS
-						&amp;&amp; last != XPathParser.MINUS
-						&amp;&amp; last != XPathParser.EQUALS
-						&amp;&amp; last != XPathParser.NOTEQUAL
-						&amp;&amp; last != XPathParser.LESSTHAN
-						&amp;&amp; last != XPathParser.LESSTHANOREQUAL
-						&amp;&amp; last != XPathParser.GREATERTHAN
-						&amp;&amp; last != XPathParser.GREATERTHANOREQUAL) {
-					types.push(XPathParser.MULTIPLYOPERATOR);
-					values.push(c);
-					c = s.charAt(pos++);
-					continue;
-				}
-			}
-			types.push(XPathParser.ASTERISKNAMETEST);
-			values.push(c);
-			c = s.charAt(pos++);
-			continue;
-		}
-
-		if (c == ':') {
-			if (s.charAt(pos) == ':') {
-				types.push(XPathParser.DOUBLECOLON);
-				values.push(&quot;::&quot;);
-				pos++;
-				c = s.charAt(pos++);
-				continue;
-			}
-		}
-
-		if (c == '/') {
-			c = s.charAt(pos++);
-			if (c == '/') {
-				types.push(XPathParser.DOUBLESLASH);
-				values.push(&quot;//&quot;);
-				c = s.charAt(pos++);
-				continue;
-			}
-			types.push(XPathParser.SLASH);
-			values.push('/');
-			continue;
-		}
-
-		if (c == '!') {
-			if (s.charAt(pos) == '=') {
-				types.push(XPathParser.NOTEQUAL);
-				values.push(&quot;!=&quot;);
-				pos++;
-				c = s.charAt(pos++);
-				continue;
-			}
-		}
-
-		if (c == '&lt;') {
-			if (s.charAt(pos) == '=') {
-				types.push(XPathParser.LESSTHANOREQUAL);
-				values.push(&quot;&lt;=&quot;);
-				pos++;
-				c = s.charAt(pos++);
-				continue;
-			}
-			types.push(XPathParser.LESSTHAN);
-			values.push('&lt;');
-			c = s.charAt(pos++);
-			continue;
-		}
-
-		if (c == '&gt;') {
-			if (s.charAt(pos) == '=') {
-				types.push(XPathParser.GREATERTHANOREQUAL);
-				values.push(&quot;&gt;=&quot;);
-				pos++;
-				c = s.charAt(pos++);
-				continue;
-			}
-			types.push(XPathParser.GREATERTHAN);
-			values.push('&gt;');
-			c = s.charAt(pos++);
-			continue;
-		}
-
-		if (c == '_' || Utilities.isLetter(c.charCodeAt(0))) {
-			var name = c;
-			c = s.charAt(pos++);
-			while (Utilities.isNCNameChar(c.charCodeAt(0))) {
-				name += c;
-				c = s.charAt(pos++);
-			}
-			if (types.length &gt; 0) {
-				var last = types[types.length - 1];
-				if (last != XPathParser.AT
-						&amp;&amp; last != XPathParser.DOUBLECOLON
-						&amp;&amp; last != XPathParser.LEFTPARENTHESIS
-						&amp;&amp; last != XPathParser.LEFTBRACKET
-						&amp;&amp; last != XPathParser.AND
-						&amp;&amp; last != XPathParser.OR
-						&amp;&amp; last != XPathParser.MOD
-						&amp;&amp; last != XPathParser.DIV
-						&amp;&amp; last != XPathParser.MULTIPLYOPERATOR
-						&amp;&amp; last != XPathParser.SLASH
-						&amp;&amp; last != XPathParser.DOUBLESLASH
-						&amp;&amp; last != XPathParser.BAR
-						&amp;&amp; last != XPathParser.PLUS
-						&amp;&amp; last != XPathParser.MINUS
-						&amp;&amp; last != XPathParser.EQUALS
-						&amp;&amp; last != XPathParser.NOTEQUAL
-						&amp;&amp; last != XPathParser.LESSTHAN
-						&amp;&amp; last != XPathParser.LESSTHANOREQUAL
-						&amp;&amp; last != XPathParser.GREATERTHAN
-						&amp;&amp; last != XPathParser.GREATERTHANOREQUAL) {
-					if (name == &quot;and&quot;) {
-						types.push(XPathParser.AND);
-						values.push(name);
-						continue;
-					}
-					if (name == &quot;or&quot;) {
-						types.push(XPathParser.OR);
-						values.push(name);
-						continue;
-					}
-					if (name == &quot;mod&quot;) {
-						types.push(XPathParser.MOD);
-						values.push(name);
-						continue;
-					}
-					if (name == &quot;div&quot;) {
-						types.push(XPathParser.DIV);
-						values.push(name);
-						continue;
-					}
-				}
-			}
-			if (c == ':') {
-				if (s.charAt(pos) == '*') {
-					types.push(XPathParser.NCNAMECOLONASTERISK);
-					values.push(name + &quot;:*&quot;);
-					pos++;
-					c = s.charAt(pos++);
-					continue;
-				}
-				if (s.charAt(pos) == '_' || Utilities.isLetter(s.charCodeAt(pos))) {
-					name += ':';
-					c = s.charAt(pos++);
-					while (Utilities.isNCNameChar(c.charCodeAt(0))) {
-						name += c;
-						c = s.charAt(pos++);
-					}
-					if (c == '(') {
-						types.push(XPathParser.FUNCTIONNAME);
-						values.push(name);
-						continue;
-					}
-					types.push(XPathParser.QNAME);
-					values.push(name);
-					continue;
-				}
-				if (s.charAt(pos) == ':') {
-					types.push(XPathParser.AXISNAME);
-					values.push(name);
-					continue;
-				}
-			}
-			if (c == '(') {
-				if (name == &quot;comment&quot; || name == &quot;text&quot; || name == &quot;node&quot;) {
-					types.push(XPathParser.NODETYPE);
-					values.push(name);
-					continue;
-				}
-				if (name == &quot;processing-instruction&quot;) {
-					if (s.charAt(pos) == ')') {
-						types.push(XPathParser.NODETYPE);
-					} else {
-						types.push(XPathParser.PROCESSINGINSTRUCTIONWITHLITERAL);
-					}
-					values.push(name);
-					continue;
-				}
-				types.push(XPathParser.FUNCTIONNAME);
-				values.push(name);
-				continue;
-			}
-			types.push(XPathParser.QNAME);
-			values.push(name);
-			continue;
-		}
-
-		throw new Error(&quot;Unexpected character &quot; + c);
-	}
-	types.push(1);
-	values.push(&quot;[EOF]&quot;);
-	return [types, values];
-};
-
-XPathParser.SHIFT = 's';
-XPathParser.REDUCE = 'r';
-XPathParser.ACCEPT = 'a';
-
-XPathParser.prototype.parse = function(s) {
-	var types;
-	var values;
-	var res = this.tokenize(s);
-	if (res == undefined) {
-		return undefined;
-	}
-	types = res[0];
-	values = res[1];
-	var tokenPos = 0;
-	var state = [];
-	var tokenType = [];
-	var tokenValue = [];
-	var s;
-	var a;
-	var t;
-
-	state.push(0);
-	tokenType.push(1);
-	tokenValue.push(&quot;_S&quot;);
-
-	a = types[tokenPos];
-	t = values[tokenPos++];
-	while (1) {
-		s = state[state.length - 1];
-		switch (XPathParser.actionTable[s].charAt(a - 1)) {
-			case XPathParser.SHIFT:
-				tokenType.push(-a);
-				tokenValue.push(t);
-				state.push(XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32);
-				a = types[tokenPos];
-				t = values[tokenPos++];
-				break;
-			case XPathParser.REDUCE:
-				var num = XPathParser.productions[XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32][1];
-				var rhs = [];
-				for (var i = 0; i &lt; num; i++) {
-					tokenType.pop();
-					rhs.unshift(tokenValue.pop());
-					state.pop();
-				}
-				var s_ = state[state.length - 1];
-				tokenType.push(XPathParser.productions[XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32][0]);
-				if (this.reduceActions[XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32] == undefined) {
-					tokenValue.push(rhs[0]);
-				} else {
-					tokenValue.push(this.reduceActions[XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32](rhs));
-				}
-				state.push(XPathParser.gotoTable[s_].charCodeAt(XPathParser.productions[XPathParser.actionTableNumber[s].charCodeAt(a - 1) - 32][0] - 2) - 33);
-				break;
-			case XPathParser.ACCEPT:
-				return new XPath(tokenValue.pop());
-			default:
-				throw new Error(&quot;XPath parse error&quot;);
-		}
-	}
-};
-
-// XPath /////////////////////////////////////////////////////////////////////
-
-XPath.prototype = new Object();
-XPath.prototype.constructor = XPath;
-XPath.superclass = Object.prototype;
-
-function XPath(e) {
-	this.expression = e;
-}
-
-XPath.prototype.toString = function() {
-	return this.expression.toString();
-};
-
-XPath.prototype.evaluate = function(c) {
-	c.contextNode = c.expressionContextNode;
-	c.contextSize = 1;
-	c.contextPosition = 1;
-	c.caseInsensitive = false;
-	if (c.contextNode != null) {
-		var doc = c.contextNode;
-		if (doc.nodeType != 9 /*Node.DOCUMENT_NODE*/) {
-			doc = doc.ownerDocument;
-		}
-		try {
-			c.caseInsensitive = doc.implementation.hasFeature(&quot;HTML&quot;, &quot;2.0&quot;);
-		} catch (e) {
-			c.caseInsensitive = true;
-		}
-	}
-	return this.expression.evaluate(c);
-};
-
-XPath.XML_NAMESPACE_URI = &quot;http://www.w3.org/XML/1998/namespace&quot;;
-XPath.XMLNS_NAMESPACE_URI = &quot;http://www.w3.org/2000/xmlns/&quot;;
-
-// Expression ////////////////////////////////////////////////////////////////
-
-Expression.prototype = new Object();
-Expression.prototype.constructor = Expression;
-Expression.superclass = Object.prototype;
-
-function Expression() {
-}
-
-Expression.prototype.init = function() {
-};
-
-Expression.prototype.toString = function() {
-	return &quot;&lt;Expression&gt;&quot;;
-};
-
-Expression.prototype.evaluate = function(c) {
-	throw new Error(&quot;Could not evaluate expression.&quot;);
-};
-
-// UnaryOperation ////////////////////////////////////////////////////////////
-
-UnaryOperation.prototype = new Expression();
-UnaryOperation.prototype.constructor = UnaryOperation;
-UnaryOperation.superclass = Expression.prototype;
-
-function UnaryOperation(rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(rhs);
-	}
-}
-
-UnaryOperation.prototype.init = function(rhs) {
-	this.rhs = rhs;
-};
-
-// UnaryMinusOperation ///////////////////////////////////////////////////////
-
-UnaryMinusOperation.prototype = new UnaryOperation();
-UnaryMinusOperation.prototype.constructor = UnaryMinusOperation;
-UnaryMinusOperation.superclass = UnaryOperation.prototype;
-
-function UnaryMinusOperation(rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(rhs);
-	}
-}
-
-UnaryMinusOperation.prototype.init = function(rhs) {
-	UnaryMinusOperation.superclass.init.call(this, rhs);
-};
-
-UnaryMinusOperation.prototype.evaluate = function(c) {
-	return this.rhs.evaluate(c).number().negate();
-};
-
-UnaryMinusOperation.prototype.toString = function() {
-	return &quot;-&quot; + this.rhs.toString();
-};
-
-// BinaryOperation ///////////////////////////////////////////////////////////
-
-BinaryOperation.prototype = new Expression();
-BinaryOperation.prototype.constructor = BinaryOperation;
-BinaryOperation.superclass = Expression.prototype;
-
-function BinaryOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-BinaryOperation.prototype.init = function(lhs, rhs) {
-	this.lhs = lhs;
-	this.rhs = rhs;
-};
-
-// OrOperation ///////////////////////////////////////////////////////////////
-
-OrOperation.prototype = new BinaryOperation();
-OrOperation.prototype.constructor = OrOperation;
-OrOperation.superclass = BinaryOperation.prototype;
-
-function OrOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-OrOperation.prototype.init = function(lhs, rhs) {
-	OrOperation.superclass.init.call(this, lhs, rhs);
-};
-
-OrOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; or &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-OrOperation.prototype.evaluate = function(c) {
-	var b = this.lhs.evaluate(c).bool();
-	if (b.booleanValue()) {
-		return b;
-	}
-	return this.rhs.evaluate(c).bool();
-};
-
-// AndOperation //////////////////////////////////////////////////////////////
-
-AndOperation.prototype = new BinaryOperation();
-AndOperation.prototype.constructor = AndOperation;
-AndOperation.superclass = BinaryOperation.prototype;
-
-function AndOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-AndOperation.prototype.init = function(lhs, rhs) {
-	AndOperation.superclass.init.call(this, lhs, rhs);
-};
-
-AndOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; and &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-AndOperation.prototype.evaluate = function(c) {
-	var b = this.lhs.evaluate(c).bool();
-	if (!b.booleanValue()) {
-		return b;
-	}
-	return this.rhs.evaluate(c).bool();
-};
-
-// EqualsOperation ///////////////////////////////////////////////////////////
-
-EqualsOperation.prototype = new BinaryOperation();
-EqualsOperation.prototype.constructor = EqualsOperation;
-EqualsOperation.superclass = BinaryOperation.prototype;
-
-function EqualsOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-EqualsOperation.prototype.init = function(lhs, rhs) {
-	EqualsOperation.superclass.init.call(this, lhs, rhs);
-};
-
-EqualsOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; = &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-EqualsOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).equals(this.rhs.evaluate(c));
-};
-
-// NotEqualOperation /////////////////////////////////////////////////////////
-
-NotEqualOperation.prototype = new BinaryOperation();
-NotEqualOperation.prototype.constructor = NotEqualOperation;
-NotEqualOperation.superclass = BinaryOperation.prototype;
-
-function NotEqualOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-NotEqualOperation.prototype.init = function(lhs, rhs) {
-	NotEqualOperation.superclass.init.call(this, lhs, rhs);
-};
-
-NotEqualOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; != &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-NotEqualOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).notequal(this.rhs.evaluate(c));
-};
-
-// LessThanOperation /////////////////////////////////////////////////////////
-
-LessThanOperation.prototype = new BinaryOperation();
-LessThanOperation.prototype.constructor = LessThanOperation;
-LessThanOperation.superclass = BinaryOperation.prototype;
-
-function LessThanOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-LessThanOperation.prototype.init = function(lhs, rhs) {
-	LessThanOperation.superclass.init.call(this, lhs, rhs);
-};
-
-LessThanOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).lessthan(this.rhs.evaluate(c));
-};
-
-LessThanOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; &lt; &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-// GreaterThanOperation //////////////////////////////////////////////////////
-
-GreaterThanOperation.prototype = new BinaryOperation();
-GreaterThanOperation.prototype.constructor = GreaterThanOperation;
-GreaterThanOperation.superclass = BinaryOperation.prototype;
-
-function GreaterThanOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-GreaterThanOperation.prototype.init = function(lhs, rhs) {
-	GreaterThanOperation.superclass.init.call(this, lhs, rhs);
-};
-
-GreaterThanOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).greaterthan(this.rhs.evaluate(c));
-};
-
-GreaterThanOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; &gt; &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-// LessThanOrEqualOperation //////////////////////////////////////////////////
-
-LessThanOrEqualOperation.prototype = new BinaryOperation();
-LessThanOrEqualOperation.prototype.constructor = LessThanOrEqualOperation;
-LessThanOrEqualOperation.superclass = BinaryOperation.prototype;
-
-function LessThanOrEqualOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-LessThanOrEqualOperation.prototype.init = function(lhs, rhs) {
-	LessThanOrEqualOperation.superclass.init.call(this, lhs, rhs);
-};
-
-LessThanOrEqualOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).lessthanorequal(this.rhs.evaluate(c));
-};
-
-LessThanOrEqualOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; &lt;= &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-// GreaterThanOrEqualOperation ///////////////////////////////////////////////
-
-GreaterThanOrEqualOperation.prototype = new BinaryOperation();
-GreaterThanOrEqualOperation.prototype.constructor = GreaterThanOrEqualOperation;
-GreaterThanOrEqualOperation.superclass = BinaryOperation.prototype;
-
-function GreaterThanOrEqualOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-GreaterThanOrEqualOperation.prototype.init = function(lhs, rhs) {
-	GreaterThanOrEqualOperation.superclass.init.call(this, lhs, rhs);
-};
-
-GreaterThanOrEqualOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).greaterthanorequal(this.rhs.evaluate(c));
-};
-
-GreaterThanOrEqualOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; &gt;= &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-// PlusOperation /////////////////////////////////////////////////////////////
-
-PlusOperation.prototype = new BinaryOperation();
-PlusOperation.prototype.constructor = PlusOperation;
-PlusOperation.superclass = BinaryOperation.prototype;
-
-function PlusOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-PlusOperation.prototype.init = function(lhs, rhs) {
-	PlusOperation.superclass.init.call(this, lhs, rhs);
-};
-
-PlusOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).number().plus(this.rhs.evaluate(c).number());
-};
-
-PlusOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; + &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-// MinusOperation ////////////////////////////////////////////////////////////
-
-MinusOperation.prototype = new BinaryOperation();
-MinusOperation.prototype.constructor = MinusOperation;
-MinusOperation.superclass = BinaryOperation.prototype;
-
-function MinusOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-MinusOperation.prototype.init = function(lhs, rhs) {
-	MinusOperation.superclass.init.call(this, lhs, rhs);
-};
-
-MinusOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).number().minus(this.rhs.evaluate(c).number());
-};
-
-MinusOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; - &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-// MultiplyOperation /////////////////////////////////////////////////////////
-
-MultiplyOperation.prototype = new BinaryOperation();
-MultiplyOperation.prototype.constructor = MultiplyOperation;
-MultiplyOperation.superclass = BinaryOperation.prototype;
-
-function MultiplyOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-MultiplyOperation.prototype.init = function(lhs, rhs) {
-	MultiplyOperation.superclass.init.call(this, lhs, rhs);
-};
-
-MultiplyOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).number().multiply(this.rhs.evaluate(c).number());
-};
-
-MultiplyOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; * &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-// DivOperation //////////////////////////////////////////////////////////////
-
-DivOperation.prototype = new BinaryOperation();
-DivOperation.prototype.constructor = DivOperation;
-DivOperation.superclass = BinaryOperation.prototype;
-
-function DivOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-DivOperation.prototype.init = function(lhs, rhs) {
-	DivOperation.superclass.init.call(this, lhs, rhs);
-};
-
-DivOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).number().div(this.rhs.evaluate(c).number());
-};
-
-DivOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; div &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-// ModOperation //////////////////////////////////////////////////////////////
-
-ModOperation.prototype = new BinaryOperation();
-ModOperation.prototype.constructor = ModOperation;
-ModOperation.superclass = BinaryOperation.prototype;
-
-function ModOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-ModOperation.prototype.init = function(lhs, rhs) {
-	ModOperation.superclass.init.call(this, lhs, rhs);
-};
-
-ModOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).number().mod(this.rhs.evaluate(c).number());
-};
-
-ModOperation.prototype.toString = function() {
-	return &quot;(&quot; + this.lhs.toString() + &quot; mod &quot; + this.rhs.toString() + &quot;)&quot;;
-};
-
-// BarOperation //////////////////////////////////////////////////////////////
-
-BarOperation.prototype = new BinaryOperation();
-BarOperation.prototype.constructor = BarOperation;
-BarOperation.superclass = BinaryOperation.prototype;
-
-function BarOperation(lhs, rhs) {
-	if (arguments.length &gt; 0) {
-		this.init(lhs, rhs);
-	}
-}
-
-BarOperation.prototype.init = function(lhs, rhs) {
-	BarOperation.superclass.init.call(this, lhs, rhs);
-};
-
-BarOperation.prototype.evaluate = function(c) {
-	return this.lhs.evaluate(c).nodeset().union(this.rhs.evaluate(c).nodeset());
-};
-
-BarOperation.prototype.toString = function() {
-	return this.lhs.toString() + &quot; | &quot; + this.rhs.toString();
-};
-
-// PathExpr //////////////////////////////////////////////////////////////////
-
-PathExpr.prototype = new Expression();
-PathExpr.prototype.constructor = PathExpr;
-PathExpr.superclass = Expression.prototype;
-
-function PathExpr(filter, filterPreds, locpath) {
-	if (arguments.length &gt; 0) {
-		this.init(filter, filterPreds, locpath);
-	}
-}
-
-PathExpr.prototype.init = function(filter, filterPreds, locpath) {
-	PathExpr.superclass.init.call(this);
-	this.filter = filter;
-	this.filterPredicates = filterPreds;
-	this.locationPath = locpath;
-};
-
-PathExpr.prototype.evaluate = function(c) {
-	var nodes;
-	var xpc = new XPathContext();
-	xpc.variableResolver = c.variableResolver;
-	xpc.functionResolver = c.functionResolver;
-	xpc.namespaceResolver = c.namespaceResolver;
-	xpc.expressionContextNode = c.expressionContextNode;
-	xpc.virtualRoot = c.virtualRoot;
-	xpc.caseInsensitive = c.caseInsensitive;
-	if (this.filter == null) {
-		nodes = [ c.contextNode ];
-	} else {
-		var ns = this.filter.evaluate(c);
-		if (!Utilities.instance_of(ns, XNodeSet)) {
-			if (this.filterPredicates != null &amp;&amp; this.filterPredicates.length &gt; 0 || this.locationPath != null) {
-				throw new Error(&quot;Path expression filter must evaluate to a nodset if predicates or location path are used&quot;);
-			}
-			return ns;
-		}
-		nodes = ns.toArray();
-		if (this.filterPredicates != null) {
-			// apply each of the predicates in turn
-			for (var j = 0; j &lt; this.filterPredicates.length; j++) {
-				var pred = this.filterPredicates[j];
-				var newNodes = [];
-				xpc.contextSize = nodes.length;
-				for (xpc.contextPosition = 1; xpc.contextPosition &lt;= xpc.contextSize; xpc.contextPosition++) {
-					xpc.contextNode = nodes[xpc.contextPosition - 1];
-					if (this.predicateMatches(pred, xpc)) {
-						newNodes.push(xpc.contextNode);
-					}
-				}
-				nodes = newNodes;
-			}
-		}
-	}
-	if (this.locationPath != null) {
-		if (this.locationPath.absolute) {
-			if (nodes[0].nodeType != 9 /*Node.DOCUMENT_NODE*/) {
-				if (xpc.virtualRoot != null) {
-					nodes = [ xpc.virtualRoot ];
-				} else {
-					if (nodes[0].ownerDocument == null) {
-						// IE 5.5 doesn't have ownerDocument?
-						var n = nodes[0];
-						while (n.parentNode != null) {
-							n = n.parentNode;
-						}
-						nodes = [ n ];
-					} else {
-						nodes = [ nodes[0].ownerDocument ];
-					}
-				}
-			} else {
-				nodes = [ nodes[0] ];
-			}
-		}
-		for (var i = 0; i &lt; this.locationPath.steps.length; i++) {
-			var step = this.locationPath.steps[i];
-			var newNodes = [];
-			for (var j = 0; j &lt; nodes.length; j++) {
-				xpc.contextNode = nodes[j];
-				switch (step.axis) {
-					case Step.ANCESTOR:
-						// look at all the ancestor nodes
-						if (xpc.contextNode === xpc.virtualRoot) {
-							break;
-						}
-						var m;
-						if (xpc.contextNode.nodeType == 2 /*Node.ATTRIBUTE_NODE*/) {
-							m = this.getOwnerElement(xpc.contextNode);
-						} else {
-							m = xpc.contextNode.parentNode;
-						}
-						while (m != null) {
-							if (step.nodeTest.matches(m, xpc)) {
-								newNodes.push(m);
-							}
-							if (m === xpc.virtualRoot) {
-								break;
-							}
-							m = m.parentNode;
-						}
-						break;
-
-					case Step.ANCESTORORSELF:
-						// look at all the ancestor nodes and the current node
-						for (var m = xpc.contextNode; m != null; m = m.nodeType == 2 /*Node.ATTRIBUTE_NODE*/ ? this.getOwnerElement(m) : m.parentNode) {
-							if (step.nodeTest.matches(m, xpc)) {
-								newNodes.push(m);
-							}
-							if (m === xpc.virtualRoot) {
-								break;
-							}
-						}
-						break;
-
-					case Step.ATTRIBUTE:
-						// look at the attributes
-						var nnm = xpc.contextNode.attributes;
-						if (nnm != null) {
-							for (var k = 0; k &lt; nnm.length; k++) {
-								var m = nnm.item(k);
-								if (step.nodeTest.matches(m, xpc)) {
-									newNodes.push(m);
-								}
-							}
-						}
-						break;
-
-					case Step.CHILD:
-						// look at all child elements
-						for (var m = xpc.contextNode.firstChild; m != null; m = m.nextSibling) {
-							if (step.nodeTest.matches(m, xpc)) {
-								newNodes.push(m);
-							}
-						}
-						break;
-
-					case Step.DESCENDANT:
-						// look at all descendant nodes
-						var st = [ xpc.contextNode.firstChild ];
-						while (st.length &gt; 0) {
-							for (var m = st.pop(); m != null; ) {
-								if (step.nodeTest.matches(m, xpc)) {
-									newNodes.push(m);
-								}
-								if (m.firstChild != null) {
-									st.push(m.nextSibling);
-									m = m.firstChild;
-								} else {
-									m = m.nextSibling;
-								}
-							}
-						}
-						break;
-
-					case Step.DESCENDANTORSELF:
-						// look at self
-						if (step.nodeTest.matches(xpc.contextNode, xpc)) {
-							newNodes.push(xpc.contextNode);
-						}
-						// look at all descendant nodes
-						var st = [ xpc.contextNode.firstChild ];
-						while (st.length &gt; 0) {
-							for (var m = st.pop(); m != null; ) {
-								if (step.nodeTest.matches(m, xpc)) {
-									newNodes.push(m);
-								}
-								if (m.firstChild != null) {
-									st.push(m.nextSibling);
-									m = m.firstChild;
-								} else {
-									m = m.nextSibling;
-								}
-							}
-						}
-						break;
-
-					case Step.FOLLOWING:
-						if (xpc.contextNode === xpc.virtualRoot) {
-							break;
-						}
-						var st = [];
-						if (xpc.contextNode.firstChild != null) {
-							st.unshift(xpc.contextNode.firstChild);
-						} else {
-							st.unshift(xpc.contextNode.nextSibling);
-						}
-						for (var m = xpc.contextNode.parentNode; m != null &amp;&amp; m.nodeType != 9 /*Node.DOCUMENT_NODE*/ &amp;&amp; m !== xpc.virtualRoot; m = m.parentNode) {
-							st.unshift(m.nextSibling);
-						}
-						do {
-							for (var m = st.pop(); m != null; ) {
-								if (step.nodeTest.matches(m, xpc)) {
-									newNodes.push(m);
-								}
-								if (m.firstChild != null) {
-									st.push(m.nextSibling);
-									m = m.firstChild;
-								} else {
-									m = m.nextSibling;
-								}
-							}
-						} while (st.length &gt; 0);
-						break;
-						
-					case Step.FOLLOWINGSIBLING:
-						if (xpc.contextNode === xpc.virtualRoot) {
-							break;
-						}
-						for (var m = xpc.contextNode.nextSibling; m != null; m = m.nextSibling) {
-							if (step.nodeTest.matches(m, xpc)) {
-								newNodes.push(m);
-							}
-						}
-						break;
-
-					case Step.NAMESPACE:
-						var n = {};
-						if (xpc.contextNode.nodeType == 1 /*Node.ELEMENT_NODE*/) {
-							n[&quot;xml&quot;] = XPath.XML_NAMESPACE_URI;
-							n[&quot;xmlns&quot;] = XPath.XMLNS_NAMESPACE_URI;
-							for (var m = xpc.contextNode; m != null &amp;&amp; m.nodeType == 1 /*Node.ELEMENT_NODE*/; m = m.parentNode) {
-								for (var k = 0; k &lt; m.attributes.length; k++) {
-									var attr = m.attributes.item(k);
-									var nm = String(attr.name);
-									if (nm == &quot;xmlns&quot;) {
-										if (n[&quot;&quot;] == undefined) {
-											n[&quot;&quot;] = attr.value;
-										}
-									} else if (nm.length &gt; 6 &amp;&amp; nm.substring(0, 6) == &quot;xmlns:&quot;) {
-										var pre = nm.substring(6, nm.length);
-										if (n[pre] == undefined) {
-											n[pre] = attr.value;
-										}
-									}
-								}
-							}
-							for (var pre in n) {
-								var nsn = new NamespaceNode(pre, n[pre], xpc.contextNode);
-								if (step.nodeTest.matches(nsn, xpc)) {
-									newNodes.push(nsn);
-								}
-							}
-						}
-						break;
-
-					case Step.PARENT:
-						m = null;
-						if (xpc.contextNode !== xpc.virtualRoot) {
-							if (xpc.contextNode.nodeType == 2 /*Node.ATTRIBUTE_NODE*/) {
-								m = this.getOwnerElement(xpc.contextNode);
-							} else {
-								m = xpc.contextNode.parentNode;
-							}
-						}
-						if (m != null &amp;&amp; step.nodeTest.matches(m, xpc)) {
-							newNodes.push(m);
-						}
-						break;
-
-					case Step.PRECEDING:
-						var st;
-						if (xpc.virtualRoot != null) {
-							st = [ xpc.virtualRoot ];
-						} else {
-							st = xpc.contextNode.nodeType == 9 /*Node.DOCUMENT_NODE*/
-								? [ xpc.contextNode ]
-								: [ xpc.contextNode.ownerDocument ];
-						}
-						outer: while (st.length &gt; 0) {
-							for (var m = st.pop(); m != null; ) {
-								if (m == xpc.contextNode) {
-									break outer;
-								}
-								if (step.nodeTest.matches(m, xpc)) {
-									newNodes.unshift(m);
-								}
-								if (m.firstChild != null) {
-									st.push(m.nextSibling);
-									m = m.firstChild;
-								} else {
-									m = m.nextSibling;
-								}
-							}
-						}
-						break;
-
-					case Step.PRECEDINGSIBLING:
-						if (xpc.contextNode === xpc.virtualRoot) {
-							break;
-						}
-						for (var m = xpc.contextNode.previousSibling; m != null; m = m.previousSibling) {
-							if (step.nodeTest.matches(m, xpc)) {
-								newNodes.push(m);
-							}
-						}
-						break;
-
-					case Step.SELF:
-						if (step.nodeTest.matches(xpc.contextNode, xpc)) {
-							newNodes.push(xpc.contextNode);
-						}
-						break;
-
-					default:
-				}
-			}
-			nodes = newNodes;
-			// apply each of the predicates in turn
-			for (var j = 0; j &lt; step.predicates.length; j++) {
-				var pred = step.predicates[j];
-				var newNodes = [];
-				xpc.contextSize = nodes.length;
-				for (xpc.contextPosition = 1; xpc.contextPosition &lt;= xpc.contextSize; xpc.contextPosition++) {
-					xpc.contextNode = nodes[xpc.contextPosition - 1];
-					if (this.predicateMatches(pred, xpc)) {
-						newNodes.push(xpc.contextNode);
-					} else {
-					}
-				}
-				nodes = newNodes;
-			}
-		}
-	}
-	var ns = new XNodeSet();
-	ns.addArray(nodes);
-	return ns;
-};
-
-PathExpr.prototype.predicateMatches = function(pred, c) {
-	var res = pred.evaluate(c);
-	if (Utilities.instance_of(res, XNumber)) {
-		return c.contextPosition == res.numberValue();
-	}
-	return res.booleanValue();
-};
-
-PathExpr.prototype.toString = function() {
-	if (this.filter != undefined) {
-		var s = this.filter.toString();
-		if (Utilities.instance_of(this.filter, XString)) {
-			s = &quot;'&quot; + s + &quot;'&quot;;
-		}
-		if (this.filterPredicates != undefined) {
-			for (var i = 0; i &lt; this.filterPredicates.length; i++) {
-				s = s + &quot;[&quot; + this.filterPredicates[i].toString() + &quot;]&quot;;
-			}
-		}
-		if (this.locationPath != undefined) {
-			if (!this.locationPath.absolute) {
-				s += &quot;/&quot;;
-			}
-			s += this.locationPath.toString();
-		}
-		return s;
-	}
-	return this.locationPath.toString();
-};
-
-PathExpr.prototype.getOwnerElement = function(n) {
-	// DOM 2 has ownerElement
-	if (n.ownerElement) {
-		return n.ownerElement;
-	}
-	// DOM 1 Internet Explorer can use selectSingleNode (ironically)
-	try {
-		if (n.selectSingleNode) {
-			return n.selectSingleNode(&quot;..&quot;);
-		}
-	} catch (e) {
-	}
-	// Other DOM 1 implementations must use this egregious search
-	var doc = n.nodeType == 9 /*Node.DOCUMENT_NODE*/
-			? n
-			: n.ownerDocument;
-	var elts = doc.getElementsByTagName(&quot;*&quot;);
-	for (var i = 0; i &lt; elts.length; i++) {
-		var elt = elts.item(i);
-		var nnm = elt.attributes;
-		for (var j = 0; j &lt; nnm.length; j++) {
-			var an = nnm.item(j);
-			if (an === n) {
-				return elt;
-			}
-		}
-	}
-	return null;
-};
-
-// LocationPath //////////////////////////////////////////////////////////////
-
-LocationPath.prototype = new Object();
-LocationPath.prototype.constructor = LocationPath;
-LocationPath.superclass = Object.prototype;
-
-function LocationPath(abs, steps) {
-	if (arguments.length &gt; 0) {
-		this.init(abs, steps);
-	}
-}
-
-LocationPath.prototype.init = function(abs, steps) {
-	this.absolute = abs;
-	this.steps = steps;
-};
-
-LocationPath.prototype.toString = function() {
-	var s;
-	if (this.absolute) {
-		s = &quot;/&quot;;
-	} else {
-		s = &quot;&quot;;
-	}
-	for (var i = 0; i &lt; this.steps.length; i++) {
-		if (i != 0) {
-			s += &quot;/&quot;;
-		}
-		s += this.steps[i].toString();
-	}
-	return s;
-};
-
-// Step //////////////////////////////////////////////////////////////////////
-
-Step.prototype = new Object();
-Step.prototype.constructor = Step;
-Step.superclass = Object.prototype;
-
-function Step(axis, nodetest, preds) {
-	if (arguments.length &gt; 0) {
-		this.init(axis, nodetest, preds);
-	}
-}
-
-Step.prototype.init = function(axis, nodetest, preds) {
-	this.axis = axis;
-	this.nodeTest = nodetest;
-	this.predicates = preds;
-};
-
-Step.prototype.toString = function() {
-	var s;
-	switch (this.axis) {
-		case Step.ANCESTOR:
-			s = &quot;ancestor&quot;;
-			break;
-		case Step.ANCESTORORSELF:
-			s = &quot;ancestor-or-self&quot;;
-			break;
-		case Step.ATTRIBUTE:
-			s = &quot;attribute&quot;;
-			break;
-		case Step.CHILD:
-			s = &quot;child&quot;;
-			break;
-		case Step.DESCENDANT:
-			s = &quot;descendant&quot;;
-			break;
-		case Step.DESCENDANTORSELF:
-			s = &quot;descendant-or-self&quot;;
-			break;
-		case Step.FOLLOWING:
-			s = &quot;following&quot;;
-			break;
-		case Step.FOLLOWINGSIBLING:
-			s = &quot;following-sibling&quot;;
-			break;
-		case Step.NAMESPACE:
-			s = &quot;namespace&quot;;
-			break;
-		case Step.PARENT:
-			s = &quot;parent&quot;;
-			break;
-		case Step.PRECEDING:
-			s = &quot;preceding&quot;;
-			break;
-		case Step.PRECEDINGSIBLING:
-			s = &quot;preceding-sibling&quot;;
-			break;
-		case Step.SELF:
-			s = &quot;self&quot;;
-			break;
-	}
-	s += &quot;::&quot;;
-	s += this.nodeTest.toString();
-	for (var i = 0; i &lt; this.predicates.length; i++) {
-		s += &quot;[&quot; + this.predicates[i].toString() + &quot;]&quot;;
-	}
-	return s;
-};
-
-Step.ANCESTOR = 0;
-Step.ANCESTORORSELF = 1;
-Step.ATTRIBUTE = 2;
-Step.CHILD = 3;
-Step.DESCENDANT = 4;
-Step.DESCENDANTORSELF = 5;
-Step.FOLLOWING = 6;
-Step.FOLLOWINGSIBLING = 7;
-Step.NAMESPACE = 8;
-Step.PARENT = 9;
-Step.PRECEDING = 10;
-Step.PRECEDINGSIBLING = 11;
-Step.SELF = 12;
-
-// NodeTest //////////////////////////////////////////////////////////////////
-
-NodeTest.prototype = new Object();
-NodeTest.prototype.constructor = NodeTest;
-NodeTest.superclass = Object.prototype;
-
-function NodeTest(type, value) {
-	if (arguments.length &gt; 0) {
-		this.init(type, value);
-	}
-}
-
-NodeTest.prototype.init = function(type, value) {
-	this.type = type;
-	this.value = value;
-};
-
-NodeTest.prototype.toString = function() {
-	switch (this.type) {
-		case NodeTest.NAMETESTANY:
-			return &quot;*&quot;;
-		case NodeTest.NAMETESTPREFIXANY:
-			return this.value + &quot;:*&quot;;
-		case NodeTest.NAMETESTRESOLVEDANY:
-			return &quot;{&quot; + this.value + &quot;}*&quot;;
-		case NodeTest.NAMETESTQNAME:
-			return this.value;
-		case NodeTest.NAMETESTRESOLVEDNAME:
-			return &quot;{&quot; + this.namespaceURI + &quot;}&quot; + this.value;
-		case NodeTest.COMMENT:
-			return &quot;comment()&quot;;
-		case NodeTest.TEXT:
-			return &quot;text()&quot;;
-		case NodeTest.PI:
-			if (this.value != undefined) {
-				return &quot;processing-instruction(\&quot;&quot; + this.value + &quot;\&quot;)&quot;;
-			}
-			return &quot;processing-instruction()&quot;;
-		case NodeTest.NODE:
-			return &quot;node()&quot;;
-	}
-	return &quot;&lt;unknown nodetest type&gt;&quot;;
-};
-
-NodeTest.prototype.matches = function(n, xpc) {
-	switch (this.type) {
-		case NodeTest.NAMETESTANY:
-			if (n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/
-					|| n.nodeType == 1 /*Node.ELEMENT_NODE*/
-					|| n.nodeType == XPathNamespace.XPATH_NAMESPACE_NODE) {
-				return true;
-			}
-			return false;
-		case NodeTest.NAMETESTPREFIXANY:
-			if ((n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/ || n.nodeType == 1 /*Node.ELEMENT_NODE*/)) {
-                var ns = xpc.namespaceResolver.getNamespace(this.value, xpc.expressionContextNode)
-                if (ns == null) {
-                    throw new Error(&quot;Cannot resolve QName &quot; + this.value);
-                }
-				return true;	
-			}
-			return false;
-		case NodeTest.NAMETESTQNAME:
-			if (n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/
-					|| n.nodeType == 1 /*Node.ELEMENT_NODE*/
-					|| n.nodeType == XPathNamespace.XPATH_NAMESPACE_NODE) {
-				var test = Utilities.resolveQName(this.value, xpc.namespaceResolver, xpc.expressionContextNode, false);
-                if (test[0] == null) {
-                    throw new Error(&quot;Cannot resolve QName &quot; + this.value);
-                }
-				test[0] = String(test[0]);
-				test[1] = String(test[1]);
-				if (test[0] == &quot;&quot;) {
-					test[0] = null;
-				}
-				var node = Utilities.resolveQName(n.nodeName, xpc.namespaceResolver, n, true);
-				node[0] = String(node[0]);
-				node[1] = String(node[1]);
-				if (node[0] == &quot;&quot;) {
-					node[0] = null;
-				}
-				if (xpc.caseInsensitive) {
-					return test[0] == node[0] &amp;&amp; String(test[1]).toLowerCase() == String(node[1]).toLowerCase();
-				}
-				return test[0] == node[0] &amp;&amp; test[1] == node[1];
-			}
-			return false;
-		case NodeTest.COMMENT:
-			return n.nodeType == 8 /*Node.COMMENT_NODE*/;
-		case NodeTest.TEXT:
-			return n.nodeType == 3 /*Node.TEXT_NODE*/ || n.nodeType == 4 /*Node.CDATA_SECTION_NODE*/;
-		case NodeTest.PI:
-			return n.nodeType == 7 /*Node.PROCESSING_INSTRUCTION_NODE*/
-				&amp;&amp; (this.value == null || n.nodeName == this.value);
-		case NodeTest.NODE:
-			return n.nodeType == 9 /*Node.DOCUMENT_NODE*/
-				|| n.nodeType == 1 /*Node.ELEMENT_NODE*/
-				|| n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/
-				|| n.nodeType == 3 /*Node.TEXT_NODE*/
-				|| n.nodeType == 4 /*Node.CDATA_SECTION_NODE*/
-				|| n.nodeType == 8 /*Node.COMMENT_NODE*/
-				|| n.nodeType == 7 /*Node.PROCESSING_INSTRUCTION_NODE*/;
-	}
-	return false;
-};
-
-NodeTest.NAMETESTANY = 0;
-NodeTest.NAMETESTPREFIXANY = 1;
-NodeTest.NAMETESTQNAME = 2;
-NodeTest.COMMENT = 3;
-NodeTest.TEXT = 4;
-NodeTest.PI = 5;
-NodeTest.NODE = 6;
-
-// VariableReference /////////////////////////////////////////////////////////
-
-VariableReference.prototype = new Expression();
-VariableReference.prototype.constructor = VariableReference;
-VariableReference.superclass = Expression.prototype;
-
-function VariableReference(v) {
-	if (arguments.length &gt; 0) {
-		this.init(v);
-	}
-}
-
-VariableReference.prototype.init = function(v) {
-	this.variable = v;
-};
-
-VariableReference.prototype.toString = function() {
-	return &quot;$&quot; + this.variable;
-};
-
-VariableReference.prototype.evaluate = function(c) {
-	return c.variableResolver.getVariable(this.variable, c);
-};
-
-// FunctionCall //////////////////////////////////////////////////////////////
-
-FunctionCall.prototype = new Expression();
-FunctionCall.prototype.constructor = FunctionCall;
-FunctionCall.superclass = Expression.prototype;
-
-function FunctionCall(fn, args) {
-	if (arguments.length &gt; 0) {
-		this.init(fn, args);
-	}
-}
-
-FunctionCall.prototype.init = function(fn, args) {
-	this.functionName = fn;
-	this.arguments = args;
-};
-
-FunctionCall.prototype.toString = function() {
-	var s = this.functionName + &quot;(&quot;;
-	for (var i = 0; i &lt; this.arguments.length; i++) {
-		if (i &gt; 0) {
-			s += &quot;, &quot;;
-		}
-		s += this.arguments[i].toString();
-	}
-	return s + &quot;)&quot;;
-};
-
-FunctionCall.prototype.evaluate = function(c) {
-	var f = c.functionResolver.getFunction(this.functionName, c);
-	if (f == undefined) {
-		throw new Error(&quot;Unknown function &quot; + this.functionName);
-	}
-	var a = [c].concat(this.arguments);
-	return f.apply(c.functionResolver.thisArg, a);
-};
-
-// XString ///////////////////////////////////////////////////////////////////
-
-XString.prototype = new Expression();
-XString.prototype.constructor = XString;
-XString.superclass = Expression.prototype;
-
-function XString(s) {
-	if (arguments.length &gt; 0) {
-		this.init(s);
-	}
-}
-
-XString.prototype.init = function(s) {
-	this.str = s;
-};
-
-XString.prototype.toString = function() {
-	return this.str;
-};
-
-XString.prototype.evaluate = function(c) {
-	return this;
-};
-
-XString.prototype.string = function() {
-	return this;
-};
-
-XString.prototype.number = function() {
-	return new XNumber(this.str);
-};
-
-XString.prototype.bool = function() {
-	return new XBoolean(this.str);
-};
-
-XString.prototype.nodeset = function() {
-	throw new Error(&quot;Cannot convert string to nodeset&quot;);
-};
-
-XString.prototype.stringValue = function() {
-	return this.str;
-};
-
-XString.prototype.numberValue = function() {
-	return this.number().numberValue();
-};
-
-XString.prototype.booleanValue = function() {
-	return this.bool().booleanValue();
-};
-
-XString.prototype.equals = function(r) {
-	if (Utilities.instance_of(r, XBoolean)) {
-		return this.bool().equals(r);
-	}
-	if (Utilities.instance_of(r, XNumber)) {
-		return this.number().equals(r);
-	}
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithString(this, Operators.equals);
-	}
-	return new XBoolean(this.str == r.str);
-};
-
-XString.prototype.notequal = function(r) {
-	if (Utilities.instance_of(r, XBoolean)) {
-		return this.bool().notequal(r);
-	}
-	if (Utilities.instance_of(r, XNumber)) {
-		return this.number().notequal(r);
-	}
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithString(this, Operators.notequal);
-	}
-	return new XBoolean(this.str != r.str);
-};
-
-XString.prototype.lessthan = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this.number(), Operators.greaterthanorequal);
-	}
-	return this.number().lessthan(r.number());
-};
-
-XString.prototype.greaterthan = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this.number(), Operators.lessthanorequal);
-	}
-	return this.number().greaterthan(r.number());
-};
-
-XString.prototype.lessthanorequal = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this.number(), Operators.greaterthan);
-	}
-	return this.number().lessthanorequal(r.number());
-};
-
-XString.prototype.greaterthanorequal = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this.number(), Operators.lessthan);
-	}
-	return this.number().greaterthanorequal(r.number());
-};
-
-// XNumber ///////////////////////////////////////////////////////////////////
-
-XNumber.prototype = new Expression();
-XNumber.prototype.constructor = XNumber;
-XNumber.superclass = Expression.prototype;
-
-function XNumber(n) {
-	if (arguments.length &gt; 0) {
-		this.init(n);
-	}
-}
-
-XNumber.prototype.init = function(n) {
-	this.num = Number(n);
-};
-
-XNumber.prototype.toString = function() {
-	return this.num;
-};
-
-XNumber.prototype.evaluate = function(c) {
-	return this;
-};
-
-XNumber.prototype.string = function() {
-	return new XString(this.num);
-};
-
-XNumber.prototype.number = function() {
-	return this;
-};
-
-XNumber.prototype.bool = function() {
-	return new XBoolean(this.num);
-};
-
-XNumber.prototype.nodeset = function() {
-	throw new Error(&quot;Cannot convert number to nodeset&quot;);
-};
-
-XNumber.prototype.stringValue = function() {
-	return this.string().stringValue();
-};
-
-XNumber.prototype.numberValue = function() {
-	return this.num;
-};
-
-XNumber.prototype.booleanValue = function() {
-	return this.bool().booleanValue();
-};
-
-XNumber.prototype.negate = function() {
-	return new XNumber(-this.num);
-};
-
-XNumber.prototype.equals = function(r) {
-	if (Utilities.instance_of(r, XBoolean)) {
-		return this.bool().equals(r);
-	}
-	if (Utilities.instance_of(r, XString)) {
-		return this.equals(r.number());
-	}
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this, Operators.equals);
-	}
-	return new XBoolean(this.num == r.num);
-};
-
-XNumber.prototype.notequal = function(r) {
-	if (Utilities.instance_of(r, XBoolean)) {
-		return this.bool().notequal(r);
-	}
-	if (Utilities.instance_of(r, XString)) {
-		return this.notequal(r.number());
-	}
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this, Operators.notequal);
-	}
-	return new XBoolean(this.num != r.num);
-};
-
-XNumber.prototype.lessthan = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this, Operators.greaterthanorequal);
-	}
-	if (Utilities.instance_of(r, XBoolean) || Utilities.instance_of(r, XString)) {
-		return this.lessthan(r.number());
-	}
-	return new XBoolean(this.num &lt; r.num);
-};
-
-XNumber.prototype.greaterthan = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this, Operators.lessthanorequal);
-	}
-	if (Utilities.instance_of(r, XBoolean) || Utilities.instance_of(r, XString)) {
-		return this.greaterthan(r.number());
-	}
-	return new XBoolean(this.num &gt; r.num);
-};
-
-XNumber.prototype.lessthanorequal = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this, Operators.greaterthan);
-	}
-	if (Utilities.instance_of(r, XBoolean) || Utilities.instance_of(r, XString)) {
-		return this.lessthanorequal(r.number());
-	}
-	return new XBoolean(this.num &lt;= r.num);
-};
-
-XNumber.prototype.greaterthanorequal = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this, Operators.lessthan);
-	}
-	if (Utilities.instance_of(r, XBoolean) || Utilities.instance_of(r, XString)) {
-		return this.greaterthanorequal(r.number());
-	}
-	return new XBoolean(this.num &gt;= r.num);
-};
-
-XNumber.prototype.plus = function(r) {
-	return new XNumber(this.num + r.num);
-};
-
-XNumber.prototype.minus = function(r) {
-	return new XNumber(this.num - r.num);
-};
-
-XNumber.prototype.multiply = function(r) {
-	return new XNumber(this.num * r.num);
-};
-
-XNumber.prototype.div = function(r) {
-	return new XNumber(this.num / r.num);
-};
-
-XNumber.prototype.mod = function(r) {
-	return new XNumber(this.num % r.num);
-};
-
-// XBoolean //////////////////////////////////////////////////////////////////
-
-XBoolean.prototype = new Expression();
-XBoolean.prototype.constructor = XBoolean;
-XBoolean.superclass = Expression.prototype;
-
-function XBoolean(b) {
-	if (arguments.length &gt; 0) {
-		this.init(b);
-	}
-}
-
-XBoolean.prototype.init = function(b) {
-	this.b = Boolean(b);
-};
-
-XBoolean.prototype.toString = function() {
-	return this.b.toString();
-};
-
-XBoolean.prototype.evaluate = function(c) {
-	return this;
-};
-
-XBoolean.prototype.string = function() {
-	return new XString(this.b);
-};
-
-XBoolean.prototype.number = function() {
-	return new XNumber(this.b);
-};
-
-XBoolean.prototype.bool = function() {
-	return this;
-};
-
-XBoolean.prototype.nodeset = function() {
-	throw new Error(&quot;Cannot convert boolean to nodeset&quot;);
-};
-
-XBoolean.prototype.stringValue = function() {
-	return this.string().stringValue();
-};
-
-XBoolean.prototype.numberValue = function() {
-	return this.num().numberValue();
-};
-
-XBoolean.prototype.booleanValue = function() {
-	return this.b;
-};
-
-XBoolean.prototype.not = function() {
-	return new XBoolean(!this.b);
-};
-
-XBoolean.prototype.equals = function(r) {
-	if (Utilities.instance_of(r, XString) || Utilities.instance_of(r, XNumber)) {
-		return this.equals(r.bool());
-	}
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithBoolean(this, Operators.equals);
-	}
-	return new XBoolean(this.b == r.b);
-};
-
-XBoolean.prototype.notequal = function(r) {
-	if (Utilities.instance_of(r, XString) || Utilities.instance_of(r, XNumber)) {
-		return this.notequal(r.bool());
-	}
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithBoolean(this, Operators.notequal);
-	}
-	return new XBoolean(this.b != r.b);
-};
-
-XBoolean.prototype.lessthan = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this.number(), Operators.greaterthanorequal);
-	}
-	return this.number().lessthan(r.number());
-};
-
-XBoolean.prototype.greaterthan = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this.number(), Operators.lessthanorequal);
-	}
-	return this.number().greaterthan(r.number());
-};
-
-XBoolean.prototype.lessthanorequal = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this.number(), Operators.greaterthan);
-	}
-	return this.number().lessthanorequal(r.number());
-};
-
-XBoolean.prototype.greaterthanorequal = function(r) {
-	if (Utilities.instance_of(r, XNodeSet)) {
-		return r.compareWithNumber(this.number(), Operators.lessthan);
-	}
-	return this.number().greaterthanorequal(r.number());
-};
-
-// XNodeSet //////////////////////////////////////////////////////////////////
-
-XNodeSet.prototype = new Expression();
-XNodeSet.prototype.constructor = XNodeSet;
-XNodeSet.superclass = Expression.prototype;
-
-function XNodeSet() {
-	this.init();
-}
-
-XNodeSet.prototype.init = function() {
-	this.tree = null;
-	this.size = 0;
-};
-
-XNodeSet.prototype.toString = function() {
-	var p = this.first();
-	if (p == null) {
-		return &quot;&quot;;
-	}
-	return this.stringForNode(p);
-};
-
-XNodeSet.prototype.evaluate = function(c) {
-	return this;
-};
-
-XNodeSet.prototype.string = function() {
-	return new XString(this.toString());
-};
-
-XNodeSet.prototype.stringValue = function() {
-	return this.toString();
-};
-
-XNodeSet.prototype.number = function() {
-	return new XNumber(this.string());
-};
-
-XNodeSet.prototype.numberValue = function() {
-	return Number(this.string());
-};
-
-XNodeSet.prototype.bool = function() {
-	return new XBoolean(this.tree != null);
-};
-
-XNodeSet.prototype.booleanValue = function() {
-	return this.tree != null;
-};
-
-XNodeSet.prototype.nodeset = function() {
-	return this;
-};
-
-XNodeSet.prototype.stringForNode = function(n) {
-	if (n.nodeType == 9 /*Node.DOCUMENT_NODE*/) {
-		n = n.documentElement;
-	}
-	if (n.nodeType == 1 /*Node.ELEMENT_NODE*/) {
-		return this.stringForNodeRec(n);
-	}
-	if (n.isNamespaceNode) {
-		return n.namespace;
-	}
-	return n.nodeValue;
-};
-
-XNodeSet.prototype.stringForNodeRec = function(n) {
-	var s = &quot;&quot;;
-	for (var n2 = n.firstChild; n2 != null; n2 = n2.nextSibling) {
-		if (n2.nodeType == 3 /*Node.TEXT_NODE*/) {
-			s += n2.nodeValue;
-		} else if (n2.nodeType == 1 /*Node.ELEMENT_NODE*/) {
-			s += this.stringForNodeRec(n2);
-		}
-	}
-	return s;
-};
-
-XNodeSet.prototype.first = function() {
-	var p = this.tree;
-	if (p == null) {
-		return null;
-	}
-	while (p.left != null) {
-		p = p.left;
-	}
-	return p.node;
-};
-
-XNodeSet.prototype.add = function(n) {
-	if (this.tree == null) {
-		this.tree = new Object();
-		this.tree.node = n;
-		this.tree.left = null;
-		this.tree.right = null;
-		this.size = 1;
-		return;
-	}
-
-	var p = this.tree;
-	while (1) {
-		var o = this.order(n, p.node);
-		if (o == 0) {
-			return;
-		}
-		if (o &gt; 0) {
-			if (p.right == null) {
-				p.right = new Object();
-				p.right.node = n;
-				p.right.left = null;
-				p.right.right = null;
-				this.size++;
-				return;
-			}
-			p = p.right;
-		} else {
-			if (p.left == null) {
-				p.left = new Object();
-				p.left.node = n;
-				p.left.left = null;
-				p.left.right = null;
-				this.size++;
-				return;
-			}
-			p = p.left;
-		}
-	}
-};
-
-XNodeSet.prototype.addArray = function(ns) {
-	for (var i = 0; i &lt; ns.length; i++) {
-		this.add(ns[i]);
-	}
-};
-
-XNodeSet.prototype.toArray = function() {
-	var a = [];
-	this.toArrayRec(this.tree, a);
-	return a;
-};
-
-XNodeSet.prototype.toArrayRec = function(t, a) {
-	if (t != null) {
-		this.toArrayRec(t.left, a);
-		a.push(t.node);
-		this.toArrayRec(t.right, a);
-	}
-};
-
-XNodeSet.prototype.order = function(n1, n2) {
-	if (n1 == n2) {
-		return 0;
-	}
-	var d1 = 0;
-	var d2 = 0;
-	for (var m1 = n1; m1 != null; m1 = m1.parentNode) {
-		d1++;
-	}
-	for (var m2 = n2; m2 != null; m2 = m2.parentNode) {
-		d2++;
-	}
-	if (d1 &gt; d2) {
-		while (d1 &gt; d2) {
-			n1 = n1.parentNode;
-			d1--;
-		}
-		if (n1 == n2) {
-			return 1;
-		}
-	} else if (d2 &gt; d1) {
-		while (d2 &gt; d1) {
-			n2 = n2.parentNode;
-			d2--;
-		}
-		if (n1 == n2) {
-			return -1;
-		}
-	}
-	while (n1.parentNode != n2.parentNode) {
-		n1 = n1.parentNode;
-		n2 = n2.parentNode;
-	}
-	while (n1.previousSibling != null &amp;&amp; n2.previousSibling != null) {
-		n1 = n1.previousSibling;
-		n2 = n2.previousSibling;
-	}
-	if (n1.previousSibling == null) {
-		return -1;
-	}
-	return 1;
-};
-
-XNodeSet.prototype.compareWithString = function(r, o) {
-	var a = this.toArray();
-	for (var i = 0; i &lt; a.length; i++) {
-		var n = a[i];
-		var l = new XString(this.stringForNode(n));
-		var res = o(l, r);
-		if (res.booleanValue()) {
-			return res;
-		}
-	}
-	return new XBoolean(false);
-};
-
-XNodeSet.prototype.compareWithNumber = function(r, o) {
-	var a = this.toArray();
-	for (var i = 0; i &lt; a.length; i++) {
-		var n = a[i];
-		var l = new XNumber(this.stringForNode(n));
-		var res = o(l, r);
-		if (res.booleanValue()) {
-			return res;
-		}
-	}
-	return new XBoolean(false);
-};
-
-XNodeSet.prototype.compareWithBoolean = function(r, o) {
-	return o(this.bool(), r);
-};
-
-XNodeSet.prototype.compareWithNodeSet = function(r, o) {
-	var a = this.toArray();
-	for (var i = 0; i &lt; a.length; i++) {
-		var n = a[i];
-		var l = new XString(this.stringForNode(n));
-		var b = r.toArray();
-		for (var j = 0; j &lt; b.length; j++) {
-			var n2 = b[j];
-			var r = new XString(this.stringForNode(n2));
-			var res = o(l, r);
-			if (res.booleanValue()) {
-				return res;
-			}
-		}
-	}
-	return new XBoolean(false);
-};
-
-XNodeSet.prototype.equals = function(r) {
-	if (Utilities.instance_of(r, XString)) {
-		return this.compareWithString(r, Operators.equals);
-	}
-	if (Utilities.instance_of(r, XNumber)) {
-		return this.compareWithNumber(r, Operators.equals);
-	}
-	if (Utilities.instance_of(r, XBoolean)) {
-		return this.compareWithBoolean(r, Operators.equals);
-	}
-	return this.compareWithNodeSet(r, Operators.equals);
-};
-
-XNodeSet.prototype.notequal = function(r) {
-	if (Utilities.instance_of(r, XString)) {
-		return this.compareWithString(r, Operators.notequal);
-	}
-	if (Utilities.instance_of(r, XNumber)) {
-		return this.compareWithNumber(r, Operators.notequal);
-	}
-	if (Utilities.instance_of(r, XBoolean)) {
-		return this.compareWithBoolean(r, Operators.notequal);
-	}
-	return this.compareWithNodeSet(r, Operators.notequal);
-};
-
-XNodeSet.prototype.lessthan = function(r) {
-	if (Utilities.instance_of(r, XString)) {
-		return this.compareWithNumber(r.number(), Operators.lessthan);
-	}
-	if (Utilities.instance_of(r, XNumber)) {
-		return this.compareWithNumber(r, Operators.lessthan);
-	}
-	if (Utilities.instance_of(r, XBoolean)) {
-		return this.compareWithBoolean(r, Operators.lessthan);
-	}
-	return this.compareWithNodeSet(r, Operators.lessthan);
-};
-
-XNodeSet.prototype.greaterthan = function(r) {
-	if (Utilities.instance_of(r, XString)) {
-		return this.compareWithNumber(r.number(), Operators.greaterthan);
-	}
-	if (Utilities.instance_of(r, XNumber)) {
-		return this.compareWithNumber(r, Operators.greaterthan);
-	}
-	if (Utilities.instance_of(r, XBoolean)) {
-		return this.compareWithBoolean(r, Operators.greaterthan);
-	}
-	return this.compareWithNodeSet(r, Operators.greaterthan);
-};
-
-XNodeSet.prototype.lessthanorequal = function(r) {
-	if (Utilities.instance_of(r, XString)) {
-		return this.compareWithNumber(r.number(), Operators.lessthanorequal);
-	}
-	if (Utilities.instance_of(r, XNumber)) {
-		return this.compareWithNumber(r, Operators.lessthanorequal);
-	}
-	if (Utilities.instance_of(r, XBoolean)) {
-		return this.compareWithBoolean(r, Operators.lessthanorequal);
-	}
-	return this.compareWithNodeSet(r, Operators.lessthanorequal);
-};
-
-XNodeSet.prototype.greaterthanorequal = function(r) {
-	if (Utilities.instance_of(r, XString)) {
-		return this.compareWithNumber(r.number(), Operators.greaterthanorequal);
-	}
-	if (Utilities.instance_of(r, XNumber)) {
-		return this.compareWithNumber(r, Operators.greaterthanorequal);
-	}
-	if (Utilities.instance_of(r, XBoolean)) {
-		return this.compareWithBoolean(r, Operators.greaterthanorequal);
-	}
-	return this.compareWithNodeSet(r, Operators.greaterthanorequal);
-};
-
-XNodeSet.prototype.union = function(r) {
-	var ns = new XNodeSet();
-	ns.tree = this.tree;
-	ns.size = this.size;
-	ns.addArray(r.toArray());
-	return ns;
-};
-
-// XPathNamespace ////////////////////////////////////////////////////////////
-
-XPathNamespace.prototype = new Object();
-XPathNamespace.prototype.constructor = XPathNamespace;
-XPathNamespace.superclass = Object.prototype;
-
-function XPathNamespace(pre, ns, p) {
-	this.isXPathNamespace = true;
-	this.ownerDocument = p.ownerDocument;
-	this.nodeName = &quot;#namespace&quot;;
-	this.prefix = pre;
-	this.localName = pre;
-	this.namespaceURI = ns;
-	this.nodeValue = ns;
-	this.ownerElement = p;
-	this.nodeType = XPathNamespace.XPATH_NAMESPACE_NODE;
-}
-
-XPathNamespace.prototype.toString = function() {
-	return &quot;{ \&quot;&quot; + this.prefix + &quot;\&quot;, \&quot;&quot; + this.namespaceURI + &quot;\&quot; }&quot;;
-};
-
-// Operators /////////////////////////////////////////////////////////////////
-
-var Operators = new Object();
-
-Operators.equals = function(l, r) {
-	return l.equals(r);
-};
-
-Operators.notequal = function(l, r) {
-	return l.notequal(r);
-};
-
-Operators.lessthan = function(l, r) {
-	return l.lessthan(r);
-};
-
-Operators.greaterthan = function(l, r) {
-	return l.greaterthan(r);
-};
-
-Operators.lessthanorequal = function(l, r) {
-	return l.lessthanorequal(r);
-};
-
-Operators.greaterthanorequal = function(l, r) {
-	return l.greaterthanorequal(r);
-};
-
-// XPathContext //////////////////////////////////////////////////////////////
-
-XPathContext.prototype = new Object();
-XPathContext.prototype.constructor = XPathContext;
-XPathContext.superclass = Object.prototype;
-
-function XPathContext(vr, nr, fr) {
-	this.variableResolver = vr != null ? vr : new VariableResolver();
-	this.namespaceResolver = nr != null ? nr : new NamespaceResolver();
-	this.functionResolver = fr != null ? fr : new FunctionResolver();
-}
-
-// VariableResolver //////////////////////////////////////////////////////////
-
-VariableResolver.prototype = new Object();
-VariableResolver.prototype.constructor = VariableResolver;
-VariableResolver.superclass = Object.prototype;
-
-function VariableResolver() {
-}
-
-VariableResolver.prototype.getVariable = function(vn, c) {
-	var parts = Utilities.splitQName(vn);
-	if (parts[0] != null) {
-		parts[0] = c.namespaceResolver.getNamespace(parts[0], c.expressionContextNode);
-        if (parts[0] == null) {
-            throw new Error(&quot;Cannot resolve QName &quot; + fn);
-        }
-	}
-	return this.getVariableWithName(parts[0], parts[1], c.expressionContextNode);
-};
-
-VariableResolver.prototype.getVariableWithName = function(ns, ln, c) {
-	return null;
-};
-
-// FunctionResolver //////////////////////////////////////////////////////////
-
-FunctionResolver.prototype = new Object();
-FunctionResolver.prototype.constructor = FunctionResolver;
-FunctionResolver.superclass = Object.prototype;
-
-function FunctionResolver(thisArg) {
-	this.thisArg = thisArg != null ? thisArg : Functions;
-	this.functions = new Object();
-	this.addStandardFunctions();
-}
-
-FunctionResolver.prototype.addStandardFunctions = function() {
-	this.functions[&quot;{}last&quot;] = Functions.last;
-	this.functions[&quot;{}position&quot;] = Functions.position;
-	this.functions[&quot;{}count&quot;] = Functions.count;
-	this.functions[&quot;{}id&quot;] = Functions.id;
-	this.functions[&quot;{}local-name&quot;] = Functions.localName;
-	this.functions[&quot;{}namespace-uri&quot;] = Functions.namespaceURI;
-	this.functions[&quot;{}name&quot;] = Functions.name;
-	this.functions[&quot;{}string&quot;] = Functions.string;
-	this.functions[&quot;{}concat&quot;] = Functions.concat;
-	this.functions[&quot;{}starts-with&quot;] = Functions.startsWith;
-	this.functions[&quot;{}contains&quot;] = Functions.contains;
-	this.functions[&quot;{}substring-before&quot;] = Functions.substringBefore;
-	this.functions[&quot;{}substring-after&quot;] = Functions.substringAfter;
-	this.functions[&quot;{}substring&quot;] = Functions.substring;
-	this.functions[&quot;{}string-length&quot;] = Functions.stringLength;
-	this.functions[&quot;{}normalize-space&quot;] = Functions.normalizeSpace;
-	this.functions[&quot;{}translate&quot;] = Functions.translate;
-	this.functions[&quot;{}boolean&quot;] = Functions.boolean_;
-	this.functions[&quot;{}not&quot;] = Functions.not;
-	this.functions[&quot;{}true&quot;] = Functions.true_;
-	this.functions[&quot;{}false&quot;] = Functions.false_;
-	this.functions[&quot;{}lang&quot;] = Functions.lang;
-	this.functions[&quot;{}number&quot;] = Functions.number;
-	this.functions[&quot;{}sum&quot;] = Functions.sum;
-	this.functions[&quot;{}floor&quot;] = Functions.floor;
-	this.functions[&quot;{}ceiling&quot;] = Functions.ceiling;
-	this.functions[&quot;{}round&quot;] = Functions.round;
-};
-
-FunctionResolver.prototype.addFunction = function(ns, ln, f) {
-	this.functions[&quot;{&quot; + ns + &quot;}&quot; + ln] = f;
-};
-
-FunctionResolver.prototype.getFunction = function(fn, c) {
-	var parts = Utilities.resolveQName(fn, c.namespaceResolver, c.contextNode, false);
-    if (parts[0] == null) {
-        throw new Error(&quot;Cannot resolve QName &quot; + fn);
-    }
-	return this.getFunctionWithName(parts[0], parts[1], c.contextNode);
-};
-
-FunctionResolver.prototype.getFunctionWithName = function(ns, ln, c) {
-	return this.functions[&quot;{&quot; + ns + &quot;}&quot; + ln];
-};
-
-// NamespaceResolver /////////////////////////////////////////////////////////
-
-NamespaceResolver.prototype = new Object();
-NamespaceResolver.prototype.constructor = NamespaceResolver;
-NamespaceResolver.superclass = Object.prototype;
-
-function NamespaceResolver() {
-}
-
-NamespaceResolver.prototype.getNamespace = function(prefix, n) {
-	if (prefix == &quot;xml&quot;) {
-		return XPath.XML_NAMESPACE_URI;
-	} else if (prefix == &quot;xmlns&quot;) {
-		return XPath.XMLNS_NAMESPACE_URI;
-	}
-	if (n.nodeType == 9 /*Node.DOCUMENT_NODE*/) {
-		n = n.documentElement;
-	} else if (n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/) {
-		n = PathExpr.prototype.getOwnerElement(n);
-	} else if (n.nodeType != 1 /*Node.ELEMENT_NODE*/) {
-		n = n.parentNode;
-	}
-	while (n != null &amp;&amp; n.nodeType == 1 /*Node.ELEMENT_NODE*/) {
-		var nnm = n.attributes;
-		for (var i = 0; i &lt; nnm.length; i++) {
-			var a = nnm.item(i);
-			var aname = a.nodeName;
-			if (aname == &quot;xmlns&quot; &amp;&amp; prefix == &quot;&quot;
-					|| aname == &quot;xmlns:&quot; + prefix) {
-				return String(a.nodeValue);
-			}
-		}
-		n = n.parentNode;
-	}
-	return null;
-};
-
-// Functions /////////////////////////////////////////////////////////////////
-
-Functions = new Object();
-
-Functions.last = function() {
-	var c = arguments[0];
-	if (arguments.length != 1) {
-		throw new Error(&quot;Function last expects ()&quot;);
-	}
-	return new XNumber(c.contextSize);
-};
-
-Functions.position = function() {
-	var c = arguments[0];
-	if (arguments.length != 1) {
-		throw new Error(&quot;Function position expects ()&quot;);
-	}
-	return new XNumber(c.contextPosition);
-};
-
-Functions.count = function() {
-	var c = arguments[0];
-	var ns;
-	if (arguments.length != 2 || !Utilities.instance_of(ns = arguments[1].evaluate(c), XNodeSet)) {
-		throw new Error(&quot;Function count expects (node-set)&quot;);
-	}
-	return new XNumber(ns.size);
-};
-
-Functions.id = function() {
-	var c = arguments[0];
-	var id;
-	if (arguments.length != 2) {
-		throw new Error(&quot;Function id expects (object)&quot;);
-	}
-	id = arguments[1].evaluate(c);
-	if (Utilities.instance_of(id, XNodeSet)) {
-		id = id.toArray().join(&quot; &quot;);
-	} else {
-		id = id.stringValue();
-	}
-	var ids = id.split(/[\x0d\x0a\x09\x20]+/);
-	var count = 0;
-	var ns = new XNodeSet();
-	var doc = c.contextNode.nodeType == 9 /*Node.DOCUMENT_NODE*/
-			? c.contextNode
-			: c.contextNode.ownerDocument;
-	for (var i = 0; i &lt; ids.length; i++) {
-		var n;
-		if (doc.getElementById) {
-			n = doc.getElementById(ids[i]);
-		} else {
-			n = Utilities.getElementById(doc, ids[i]);
-		}
-		if (n != null) {
-			ns.add(n);
-			count++;
-		}
-	}
-	return ns;
-};
-
-Functions.localName = function() {
-	var c = arguments[0];
-	var n;
-	if (arguments.length == 1) {
-		n = c.contextNode;
-	} else if (arguments.length == 2) {
-		n = arguments[1].evaluate(c).first();
-	} else {
-		throw new Error(&quot;Function local-name expects (node-set?)&quot;);
-	}
-	if (n == null) {
-		return new XString(&quot;&quot;);
-	}
-	return new XString(n.localName ? n.localName : n.baseName);
-};
-
-Functions.namespaceURI = function() {
-	var c = arguments[0];
-	var n;
-	if (arguments.length == 1) {
-		n = c.contextNode;
-	} else if (arguments.length == 2) {
-		n = arguments[1].evaluate(c).first();
-	} else {
-		throw new Error(&quot;Function namespace-uri expects (node-set?)&quot;);
-	}
-	if (n == null) {
-		return new XString(&quot;&quot;);
-	}
-	return new XString(n.namespaceURI);
-};
-
-Functions.name = function() {
-	var c = arguments[0];
-	var n;
-	if (arguments.length == 1) {
-		n = c.contextNode;
-	} else if (arguments.length == 2) {
-		n = arguments[1].evaluate(c).first();
-	} else {
-		throw new Error(&quot;Function name expects (node-set?)&quot;);
-	}
-	if (n == null) {
-		return new XString(&quot;&quot;);
-	}
-	if (n.nodeType == 1 /*Node.ELEMENT_NODE*/ || n.nodeType == 2 /*Node.ATTRIBUTE_NODE*/) {
-		return new XString(n.nodeName);
-	} else if (n.localName == null) {
-		return new XString(&quot;&quot;);
-	} else {
-		return new XString(n.localName);
-	}
-};
-
-Functions.string = function() {
-	var c = arguments[0];
-	if (arguments.length == 1) {
-		return XNodeSet.prototype.stringForNode(c.contextNode);
-	} else if (arguments.length == 2) {
-		return arguments[1].evaluate(c).string();
-	}
-	throw new Error(&quot;Function string expects (object?)&quot;);
-};
-
-Functions.concat = function() {
-	var c = arguments[0];
-	if (arguments.length &lt; 3) {
-		throw new Error(&quot;Function concat expects (string, string, string*)&quot;);
-	}
-	var s = &quot;&quot;;
-	for (var i = 1; i &lt; arguments.length; i++) {
-		s += arguments[i].evaluate(c).stringValue();
-	}
-	return new XString(s);
-};
-
-Functions.startsWith = function() {
-	var c = arguments[0];
-	if (arguments.length != 3) {
-		throw new Error(&quot;Function startsWith expects (string, string)&quot;);
-	}
-	var s1 = arguments[1].evaluate(c).stringValue();
-	var s2 = arguments[2].evaluate(c).stringValue();
-	return new XBoolean(s1.substring(0, s2.length) == s2);
-};
-
-Functions.contains = function() {
-	var c = arguments[0];
-	if (arguments.length != 3) {
-		throw new Error(&quot;Function contains expects (string, string)&quot;);
-	}
-	var s1 = arguments[1].evaluate(c).stringValue();
-	var s2 = arguments[2].evaluate(c).stringValue();
-	return new XBoolean(s1.indexOf(s2) != -1);
-};
-
-Functions.substringBefore = function() {
-	var c = arguments[0];
-	if (arguments.length != 3) {
-		throw new Error(&quot;Function substring-before expects (string, string)&quot;);
-	}
-	var s1 = arguments[1].evaluate(c).stringValue();
-	var s2 = arguments[2].evaluate(c).stringValue();
-	return new XString(s1.substring(0, s1.indexOf(s2)));
-};
-
-Functions.substringAfter = function() {
-	var c = arguments[0];
-	if (arguments.length != 3) {
-		throw new Error(&quot;Function substring-after expects (string, string)&quot;);
-	}
-	var s1 = arguments[1].evaluate(c).stringValue();
-	var s2 = arguments[2].evaluate(c).stringValue();
-	if (s2.length == 0) {
-		return new XString(s1);
-	}
-	var i = s1.indexOf(s2);
-	if (i == -1) {
-		return new XString(&quot;&quot;);
-	}
-	return new XString(s1.substring(s1.indexOf(s2) + 1));
-};
-
-Functions.substring = function() {
-	var c = arguments[0];
-	if (!(arguments.length == 3 || arguments.length == 4)) {
-		throw new Error(&quot;Function substring expects (string, number, number?)&quot;);
-	}
-	var s = arguments[1].evaluate(c).stringValue();
-	var n1 = Math.round(arguments[2].evaluate(c).numberValue()) - 1;
-	var n2 = arguments.length == 4 ? n1 + Math.round(arguments[3].evaluate(c).numberValue()) : undefined;
-	return new XString(s.substring(n1, n2));
-};
-
-Functions.stringLength = function() {
-	var c = arguments[0];
-	var s;
-	if (arguments.length == 1) {
-		s = XNodeSet.prototype.stringForNode(c.contextNode);
-	} else if (arguments.length == 2) {
-		s = arguments[1].evaluate(c).stringValue();
-	} else {
-		throw new Error(&quot;Function string-length expects (string?)&quot;);
-	}
-	return new XNumber(s.length);
-};
-
-Functions.normalizeSpace = function() {
-	var c = arguments[0];
-	var s;
-	if (arguments.length == 1) {
-		s = XNodeSet.prototype.stringForNode(c.contextNode);
-	} else if (arguments.length == 2) {
-		s = arguments[1].evaluate(c).stringValue();
-	} else {
-		throw new Error(&quot;Function normalize-space expects (string?)&quot;);
-	}
-	var i = 0;
-	var j = s.length - 1;
-	while (Utilities.isSpace(s.charCodeAt(j))) {
-		j--;
-	}
-	var t = &quot;&quot;;
-	while (i &lt;= j &amp;&amp; Utilities.isSpace(s.charCodeAt(i))) {
-		i++;
-	}
-	while (i &lt;= j) {
-		if (Utilities.isSpace(s.charCodeAt(i))) {
-			t += &quot; &quot;;
-			while (i &lt;= j &amp;&amp; Utilities.isSpace(s.charCodeAt(i))) {
-				i++;
-			}
-		} else {
-			t += s.charAt(i);
-			i++;
-		}
-	}
-	return new XString(t);
-};
-
-Functions.translate = function() {
-	var c = arguments[0];
-	if (arguments.length != 4) {
-		throw new Error(&quot;Function translate expects (string, string, string)&quot;);
-	}
-	var s1 = arguments[1].evaluate(c).stringValue();
-	var s2 = arguments[2].evaluate(c).stringValue();
-	var s3 = arguments[3].evaluate(c).stringValue();
-	var map = [];
-	for (var i = 0; i &lt; s2.length; i++) {
-		var j = s2.charCodeAt(i);
-		if (map[j] == undefined) {
-			var k = i &gt; s3.length ? &quot;&quot; : s3.charAt(i);
-			map[j] = k;
-		}
-	}
-	var t = &quot;&quot;;
-	for (var i = 0; i &lt; s1.length; i++) {
-		var c = s1.charCodeAt(i);
-		var r = map[c];
-		if (r == undefined) {
-			t += s1.charAt(i);
-		} else {
-			t += r;
-		}
-	}
-	return new XString(t);
-};
-
-Functions.boolean_ = function() {
-	var c = arguments[0];
-	if (arguments.length != 2) {
-		throw new Error(&quot;Function boolean expects (object)&quot;);
-	}
-	return arguments[1].evaluate(c).bool();
-};
-
-Functions.not = function() {
-	var c = arguments[0];
-	if (arguments.length != 2) {
-		throw new Error(&quot;Function not expects (object)&quot;);
-	}
-	return arguments[1].evaluate(c).bool().not();
-};
-
-Functions.true_ = function() {
-	if (arguments.length != 1) {
-		throw new Error(&quot;Function true expects ()&quot;);
-	}
-	return new XBoolean(true);
-};
-
-Functions.false_ = function() {
-	if (arguments.length != 1) {
-		throw new Error(&quot;Function false expects ()&quot;);
-	}
-	return new XBoolean(false);
-};
-
-Functions.lang = function() {
-	var c = arguments[0];
-	if (arguments.length != 2) {
-		throw new Error(&quot;Function lang expects (string)&quot;);
-	}
-	var lang;
-	for (var n = c.contextNode; n != null &amp;&amp; n.nodeType != 9 /*Node.DOCUMENT_NODE*/; n = n.parentNode) {
-		var a = n.getAttributeNS(XPath.XML_NAMESPACE_URI, &quot;lang&quot;);
-		if (a != null) {
-			lang = String(a);
-			break;
-		}
-	}
-	if (lang == null) {
-		return new XBoolean(false);
-	}
-	var s = arguments[1].evaluate(c).stringValue();
-	return new XBoolean(lang.substring(0, s.length) == s
-				&amp;&amp; (lang.length == s.length || lang.charAt(s.length) == '-'));
-};
-
-Functions.number = function() {
-	var c = arguments[0];
-	if (!(arguments.length == 1 || arguments.length == 2)) {
-		throw new Error(&quot;Function number expects (object?)&quot;);
-	}
-	if (arguments.length == 1) {
-		return new XNumber(XNodeSet.prototype.stringForNode(c.contextNode));
-	}
-	return arguments[1].evaluate(c).number();
-};
-
-Functions.sum = function() {
-	var c = arguments[0];
-	var ns;
-	if (arguments.length != 2 || !Utilities.instance_of((ns = arguments[1].evaluate(c)), XNodeSet)) {
-		throw new Error(&quot;Function sum expects (node-set)&quot;);
-	}
-	ns = ns.toArray();
-	var n = 0;
-	for (var i = 0; i &lt; ns.length; i++) {
-		n += new XNumber(XNodeSet.prototype.stringForNode(ns[i])).numberValue();
-	}
-	return new XNumber(n);
-};
-
-Functions.floor = function() {
-	var c = arguments[0];
-	if (arguments.length != 2) {
-		throw new Error(&quot;Function floor expects (number)&quot;);
-	}
-	return new XNumber(Math.floor(arguments[1].evaluate(c).numberValue()));
-};
-
-Functions.ceiling = function() {
-	var c = arguments[0];
-	if (arguments.length != 2) {
-		throw new Error(&quot;Function ceiling expects (number)&quot;);
-	}
-	return new XNumber(Math.ceil(arguments[1].evaluate(c).numberValue()));
-};
-
-Functions.round = function() {
-	var c = arguments[0];
-	if (arguments.length != 2) {
-		throw new Error(&quot;Function round expects (number)&quot;);
-	}
-	return new XNumber(Math.round(arguments[1].evaluate(c).numberValue()));
-};
-
-// Utilities /////////////////////////////////////////////////////////////////
-
-Utilities = new Object();
-
-Utilities.splitQName = function(qn) {
-	var i = qn.indexOf(&quot;:&quot;);
-	if (i == -1) {
-		return [ null, qn ];
-	}
-	return [ qn.substring(0, i), qn.substring(i + 1) ];
-};
-
-Utilities.resolveQName = function(qn, nr, n, useDefault) {
-	var parts = Utilities.splitQName(qn);
-	if (parts[0] != null) {
-		parts[0] = nr.getNamespace(parts[0], n);
-	} else {
-		if (useDefault) {
-			parts[0] = nr.getNamespace(&quot;&quot;, n);
-			if (parts[0] == null) {
-				parts[0] = &quot;&quot;;
-			}
-		} else {
-			parts[0] = &quot;&quot;;
-		}
-	}
-	return parts;
-};
-
-Utilities.isSpace = function(c) {
-	return c == 0x9 || c == 0xd || c == 0xa || c == 0x20;
-};
-
-Utilities.isLetter = function(c) {
-	return c &gt;= 0x0041 &amp;&amp; c &lt;= 0x005A ||
-		c &gt;= 0x0061 &amp;&amp; c &lt;= 0x007A ||
-		c &gt;= 0x00C0 &amp;&amp; c &lt;= 0x00D6 ||
-		c &gt;= 0x00D8 &amp;&amp; c &lt;= 0x00F6 ||
-		c &gt;= 0x00F8 &amp;&amp; c &lt;= 0x00FF ||
-		c &gt;= 0x0100 &amp;&amp; c &lt;= 0x0131 ||
-		c &gt;= 0x0134 &amp;&amp; c &lt;= 0x013E ||
-		c &gt;= 0x0141 &amp;&amp; c &lt;= 0x0148 ||
-		c &gt;= 0x014A &amp;&amp; c &lt;= 0x017E ||
-		c &gt;= 0x0180 &amp;&amp; c &lt;= 0x01C3 ||
-		c &gt;= 0x01CD &amp;&amp; c &lt;= 0x01F0 ||
-		c &gt;= 0x01F4 &amp;&amp; c &lt;= 0x01F5 ||
-		c &gt;= 0x01FA &amp;&amp; c &lt;= 0x0217 ||
-		c &gt;= 0x0250 &amp;&amp; c &lt;= 0x02A8 ||
-		c &gt;= 0x02BB &amp;&amp; c &lt;= 0x02C1 ||
-		c == 0x0386 ||
-		c &gt;= 0x0388 &amp;&amp; c &lt;= 0x038A ||
-		c == 0x038C ||
-		c &gt;= 0x038E &amp;&amp; c &lt;= 0x03A1 ||
-		c &gt;= 0x03A3 &amp;&amp; c &lt;= 0x03CE ||
-		c &gt;= 0x03D0 &amp;&amp; c &lt;= 0x03D6 ||
-		c == 0x03DA ||
-		c == 0x03DC ||
-		c == 0x03DE ||
-		c == 0x03E0 ||
-		c &gt;= 0x03E2 &amp;&amp; c &lt;= 0x03F3 ||
-		c &gt;= 0x0401 &amp;&amp; c &lt;= 0x040C ||
-		c &gt;= 0x040E &amp;&amp; c &lt;= 0x044F ||
-		c &gt;= 0x0451 &amp;&amp; c &lt;= 0x045C ||
-		c &gt;= 0x045E &amp;&amp; c &lt;= 0x0481 ||
-		c &gt;= 0x0490 &amp;&amp; c &lt;= 0x04C4 ||
-		c &gt;= 0x04C7 &amp;&amp; c &lt;= 0x04C8 ||
-		c &gt;= 0x04CB &amp;&amp; c &lt;= 0x04CC ||
-		c &gt;= 0x04D0 &amp;&amp; c &lt;= 0x04EB ||
-		c &gt;= 0x04EE &amp;&amp; c &lt;= 0x04F5 ||
-		c &gt;= 0x04F8 &amp;&amp; c &lt;= 0x04F9 ||
-		c &gt;= 0x0531 &amp;&amp; c &lt;= 0x0556 ||
-		c == 0x0559 ||
-		c &gt;= 0x0561 &amp;&amp; c &lt;= 0x0586 ||
-		c &gt;= 0x05D0 &amp;&amp; c &lt;= 0x05EA ||
-		c &gt;= 0x05F0 &amp;&amp; c &lt;= 0x05F2 ||
-		c &gt;= 0x0621 &amp;&amp; c &lt;= 0x063A ||
-		c &gt;= 0x0641 &amp;&amp; c &lt;= 0x064A ||
-		c &gt;= 0x0671 &amp;&amp; c &lt;= 0x06B7 ||
-		c &gt;= 0x06BA &amp;&amp; c &lt;= 0x06BE ||
-		c &gt;= 0x06C0 &amp;&amp; c &lt;= 0x06CE ||
-		c &gt;= 0x06D0 &amp;&amp; c &lt;= 0x06D3 ||
-		c == 0x06D5 ||
-		c &gt;= 0x06E5 &amp;&amp; c &lt;= 0x06E6 ||
-		c &gt;= 0x0905 &amp;&amp; c &lt;= 0x0939 ||
-		c == 0x093D ||
-		c &gt;= 0x0958 &amp;&amp; c &lt;= 0x0961 ||
-		c &gt;= 0x0985 &amp;&amp; c &lt;= 0x098C ||
-		c &gt;= 0x098F &amp;&amp; c &lt;= 0x0990 ||
-		c &gt;= 0x0993 &amp;&amp; c &lt;= 0x09A8 ||
-		c &gt;= 0x09AA &amp;&amp; c &lt;= 0x09B0 ||
-		c == 0x09B2 ||
-		c &gt;= 0x09B6 &amp;&amp; c &lt;= 0x09B9 ||
-		c &gt;= 0x09DC &amp;&amp; c &lt;= 0x09DD ||
-		c &gt;= 0x09DF &amp;&amp; c &lt;= 0x09E1 ||
-		c &gt;= 0x09F0 &amp;&amp; c &lt;= 0x09F1 ||
-		c &gt;= 0x0A05 &amp;&amp; c &lt;= 0x0A0A ||
-		c &gt;= 0x0A0F &amp;&amp; c &lt;= 0x0A10 ||
-		c &gt;= 0x0A13 &amp;&amp; c &lt;= 0x0A28 ||
-		c &gt;= 0x0A2A &amp;&amp; c &lt;= 0x0A30 ||
-		c &gt;= 0x0A32 &amp;&amp; c &lt;= 0x0A33 ||
-		c &gt;= 0x0A35 &amp;&amp; c &lt;= 0x0A36 ||
-		c &gt;= 0x0A38 &amp;&amp; c &lt;= 0x0A39 ||
-		c &gt;= 0x0A59 &amp;&amp; c &lt;= 0x0A5C ||
-		c == 0x0A5E ||
-		c &gt;= 0x0A72 &amp;&amp; c &lt;= 0x0A74 ||
-		c &gt;= 0x0A85 &amp;&amp; c &lt;= 0x0A8B ||
-		c == 0x0A8D ||
-		c &gt;= 0x0A8F &amp;&amp; c &lt;= 0x0A91 ||
-		c &gt;= 0x0A93 &amp;&amp; c &lt;= 0x0AA8 ||
-		c &gt;= 0x0AAA &amp;&amp; c &lt;= 0x0AB0 ||
-		c &gt;= 0x0AB2 &amp;&amp; c &lt;= 0x0AB3 ||
-		c &gt;= 0x0AB5 &amp;&amp; c &lt;= 0x0AB9 ||
-		c == 0x0ABD ||
-		c == 0x0AE0 ||
-		c &gt;= 0x0B05 &amp;&amp; c &lt;= 0x0B0C ||
-		c &gt;= 0x0B0F &amp;&amp; c &lt;= 0x0B10 ||
-		c &gt;= 0x0B13 &amp;&amp; c &lt;= 0x0B28 ||
-		c &gt;= 0x0B2A &amp;&amp; c &lt;= 0x0B30 ||
-		c &gt;= 0x0B32 &amp;&amp; c &lt;= 0x0B33 ||
-		c &gt;= 0x0B36 &amp;&amp; c &lt;= 0x0B39 ||
-		c == 0x0B3D ||
-		c &gt;= 0x0B5C &amp;&amp; c &lt;= 0x0B5D ||
-		c &gt;= 0x0B5F &amp;&amp; c &lt;= 0x0B61 ||
-		c &gt;= 0x0B85 &amp;&amp; c &lt;= 0x0B8A ||
-		c &gt;= 0x0B8E &amp;&amp; c &lt;= 0x0B90 ||
-		c &gt;= 0x0B92 &amp;&amp; c &lt;= 0x0B95 ||
-		c &gt;= 0x0B99 &amp;&amp; c &lt;= 0x0B9A ||
-		c == 0x0B9C ||
-		c &gt;= 0x0B9E &amp;&amp; c &lt;= 0x0B9F ||
-		c &gt;= 0x0BA3 &amp;&amp; c &lt;= 0x0BA4 ||
-		c &gt;= 0x0BA8 &amp;&amp; c &lt;= 0x0BAA ||
-		c &gt;= 0x0BAE &amp;&amp; c &lt;= 0x0BB5 ||
-		c &gt;= 0x0BB7 &amp;&amp; c &lt;= 0x0BB9 ||
-		c &gt;= 0x0C05 &amp;&amp; c &lt;= 0x0C0C ||
-		c &gt;= 0x0C0E &amp;&amp; c &lt;= 0x0C10 ||
-		c &gt;= 0x0C12 &amp;&amp; c &lt;= 0x0C28 ||
-		c &gt;= 0x0C2A &amp;&amp; c &lt;= 0x0C33 ||
-		c &gt;= 0x0C35 &amp;&amp; c &lt;= 0x0C39 ||
-		c &gt;= 0x0C60 &amp;&amp; c &lt;= 0x0C61 ||
-		c &gt;= 0x0C85 &amp;&amp; c &lt;= 0x0C8C ||
-		c &gt;= 0x0C8E &amp;&amp; c &lt;= 0x0C90 ||
-		c &gt;= 0x0C92 &amp;&amp; c &lt;= 0x0CA8 ||
-		c &gt;= 0x0CAA &amp;&amp; c &lt;= 0x0CB3 ||
-		c &gt;= 0x0CB5 &amp;&amp; c &lt;= 0x0CB9 ||
-		c == 0x0CDE ||
-		c &gt;= 0x0CE0 &amp;&amp; c &lt;= 0x0CE1 ||
-		c &gt;= 0x0D05 &amp;&amp; c &lt;= 0x0D0C ||
-		c &gt;= 0x0D0E &amp;&amp; c &lt;= 0x0D10 ||
-		c &gt;= 0x0D12 &amp;&amp; c &lt;= 0x0D28 ||
-		c &gt;= 0x0D2A &amp;&amp; c &lt;= 0x0D39 ||
-		c &gt;= 0x0D60 &amp;&amp; c &lt;= 0x0D61 ||
-		c &gt;= 0x0E01 &amp;&amp; c &lt;= 0x0E2E ||
-		c == 0x0E30 ||
-		c &gt;= 0x0E32 &amp;&amp; c &lt;= 0x0E33 ||
-		c &gt;= 0x0E40 &amp;&amp; c &lt;= 0x0E45 ||
-		c &gt;= 0x0E81 &amp;&amp; c &lt;= 0x0E82 ||
-		c == 0x0E84 ||
-		c &gt;= 0x0E87 &amp;&amp; c &lt;= 0x0E88 ||
-		c == 0x0E8A ||
-		c == 0x0E8D ||
-		c &gt;= 0x0E94 &amp;&amp; c &lt;= 0x0E97 ||
-		c &gt;= 0x0E99 &amp;&amp; c &lt;= 0x0E9F ||
-		c &gt;= 0x0EA1 &amp;&amp; c &lt;= 0x0EA3 ||
-		c == 0x0EA5 ||
-		c == 0x0EA7 ||
-		c &gt;= 0x0EAA &amp;&amp; c &lt;= 0x0EAB ||
-		c &gt;= 0x0EAD &amp;&amp; c &lt;= 0x0EAE ||
-		c == 0x0EB0 ||
-		c &gt;= 0x0EB2 &amp;&amp; c &lt;= 0x0EB3 ||
-		c == 0x0EBD ||
-		c &gt;= 0x0EC0 &amp;&amp; c &lt;= 0x0EC4 ||
-		c &gt;= 0x0F40 &amp;&amp; c &lt;= 0x0F47 ||
-		c &gt;= 0x0F49 &amp;&amp; c &lt;= 0x0F69 ||
-		c &gt;= 0x10A0 &amp;&amp; c &lt;= 0x10C5 ||
-		c &gt;= 0x10D0 &amp;&amp; c &lt;= 0x10F6 ||
-		c == 0x1100 ||
-		c &gt;= 0x1102 &amp;&amp; c &lt;= 0x1103 ||
-		c &gt;= 0x1105 &amp;&amp; c &lt;= 0x1107 ||
-		c == 0x1109 ||
-		c &gt;= 0x110B &amp;&amp; c &lt;= 0x110C ||
-		c &gt;= 0x110E &amp;&amp; c &lt;= 0x1112 ||
-		c == 0x113C ||
-		c == 0x113E ||
-		c == 0x1140 ||
-		c == 0x114C ||
-		c == 0x114E ||
-		c == 0x1150 ||
-		c &gt;= 0x1154 &amp;&amp; c &lt;= 0x1155 ||
-		c == 0x1159 ||
-		c &gt;= 0x115F &amp;&amp; c &lt;= 0x1161 ||
-		c == 0x1163 ||
-		c == 0x1165 ||
-		c == 0x1167 ||
-		c == 0x1169 ||
-		c &gt;= 0x116D &amp;&amp; c &lt;= 0x116E ||
-		c &gt;= 0x1172 &amp;&amp; c &lt;= 0x1173 ||
-		c == 0x1175 ||
-		c == 0x119E ||
-		c == 0x11A8 ||
-		c == 0x11AB ||
-		c &gt;= 0x11AE &amp;&amp; c &lt;= 0x11AF ||
-		c &gt;= 0x11B7 &amp;&amp; c &lt;= 0x11B8 ||
-		c == 0x11BA ||
-		c &gt;= 0x11BC &amp;&amp; c &lt;= 0x11C2 ||
-		c == 0x11EB ||
-		c == 0x11F0 ||
-		c == 0x11F9 ||
-		c &gt;= 0x1E00 &amp;&amp; c &lt;= 0x1E9B ||
-		c &gt;= 0x1EA0 &amp;&amp; c &lt;= 0x1EF9 ||
-		c &gt;= 0x1F00 &amp;&amp; c &lt;= 0x1F15 ||
-		c &gt;= 0x1F18 &amp;&amp; c &lt;= 0x1F1D ||
-		c &gt;= 0x1F20 &amp;&amp; c &lt;= 0x1F45 ||
-		c &gt;= 0x1F48 &amp;&amp; c &lt;= 0x1F4D ||
-		c &gt;= 0x1F50 &amp;&amp; c &lt;= 0x1F57 ||
-		c == 0x1F59 ||
-		c == 0x1F5B ||
-		c == 0x1F5D ||
-		c &gt;= 0x1F5F &amp;&amp; c &lt;= 0x1F7D ||
-		c &gt;= 0x1F80 &amp;&amp; c &lt;= 0x1FB4 ||
-		c &gt;= 0x1FB6 &amp;&amp; c &lt;= 0x1FBC ||
-		c == 0x1FBE ||
-		c &gt;= 0x1FC2 &amp;&amp; c &lt;= 0x1FC4 ||
-		c &gt;= 0x1FC6 &amp;&amp; c &lt;= 0x1FCC ||
-		c &gt;= 0x1FD0 &amp;&amp; c &lt;= 0x1FD3 ||
-		c &gt;= 0x1FD6 &amp;&amp; c &lt;= 0x1FDB ||
-		c &gt;= 0x1FE0 &amp;&amp; c &lt;= 0x1FEC ||
-		c &gt;= 0x1FF2 &amp;&amp; c &lt;= 0x1FF4 ||
-		c &gt;= 0x1FF6 &amp;&amp; c &lt;= 0x1FFC ||
-		c == 0x2126 ||
-		c &gt;= 0x212A &amp;&amp; c &lt;= 0x212B ||
-		c == 0x212E ||
-		c &gt;= 0x2180 &amp;&amp; c &lt;= 0x2182 ||
-		c &gt;= 0x3041 &amp;&amp; c &lt;= 0x3094 ||
-		c &gt;= 0x30A1 &amp;&amp; c &lt;= 0x30FA ||
-		c &gt;= 0x3105 &amp;&amp; c &lt;= 0x312C ||
-		c &gt;= 0xAC00 &amp;&amp; c &lt;= 0xD7A3 ||
-		c &gt;= 0x4E00 &amp;&amp; c &lt;= 0x9FA5 ||
-		c == 0x3007 ||
-		c &gt;= 0x3021 &amp;&amp; c &lt;= 0x3029;
-};
-
-Utilities.isNCNameChar = function(c) {
-	return c &gt;= 0x0030 &amp;&amp; c &lt;= 0x0039 
-		|| c &gt;= 0x0660 &amp;&amp; c &lt;= 0x0669 
-		|| c &gt;= 0x06F0 &amp;&amp; c &lt;= 0x06F9 
-		|| c &gt;= 0x0966 &amp;&amp; c &lt;= 0x096F 
-		|| c &gt;= 0x09E6 &amp;&amp; c &lt;= 0x09EF 
-		|| c &gt;= 0x0A66 &amp;&amp; c &lt;= 0x0A6F 
-		|| c &gt;= 0x0AE6 &amp;&amp; c &lt;= 0x0AEF 
-		|| c &gt;= 0x0B66 &amp;&amp; c &lt;= 0x0B6F 
-		|| c &gt;= 0x0BE7 &amp;&amp; c &lt;= 0x0BEF 
-		|| c &gt;= 0x0C66 &amp;&amp; c &lt;= 0x0C6F 
-		|| c &gt;= 0x0CE6 &amp;&amp; c &lt;= 0x0CEF 
-		|| c &gt;= 0x0D66 &amp;&amp; c &lt;= 0x0D6F 
-		|| c &gt;= 0x0E50 &amp;&amp; c &lt;= 0x0E59 
-		|| c &gt;= 0x0ED0 &amp;&amp; c &lt;= 0x0ED9 
-		|| c &gt;= 0x0F20 &amp;&amp; c &lt;= 0x0F29
-		|| c == 0x002E
-		|| c == 0x002D
-		|| c == 0x005F
-		|| Utilities.isLetter(c)
-		|| c &gt;= 0x0300 &amp;&amp; c &lt;= 0x0345 
-		|| c &gt;= 0x0360 &amp;&amp; c &lt;= 0x0361 
-		|| c &gt;= 0x0483 &amp;&amp; c &lt;= 0x0486 
-		|| c &gt;= 0x0591 &amp;&amp; c &lt;= 0x05A1 
-		|| c &gt;= 0x05A3 &amp;&amp; c &lt;= 0x05B9 
-		|| c &gt;= 0x05BB &amp;&amp; c &lt;= 0x05BD 
-		|| c == 0x05BF 
-		|| c &gt;= 0x05C1 &amp;&amp; c &lt;= 0x05C2 
-		|| c == 0x05C4 
-		|| c &gt;= 0x064B &amp;&amp; c &lt;= 0x0652 
-		|| c == 0x0670 
-		|| c &gt;= 0x06D6 &amp;&amp; c &lt;= 0x06DC 
-		|| c &gt;= 0x06DD &amp;&amp; c &lt;= 0x06DF 
-		|| c &gt;= 0x06E0 &amp;&amp; c &lt;= 0x06E4 
-		|| c &gt;= 0x06E7 &amp;&amp; c &lt;= 0x06E8 
-		|| c &gt;= 0x06EA &amp;&amp; c &lt;= 0x06ED 
-		|| c &gt;= 0x0901 &amp;&amp; c &lt;= 0x0903 
-		|| c == 0x093C 
-		|| c &gt;= 0x093E &amp;&amp; c &lt;= 0x094C 
-		|| c == 0x094D 
-		|| c &gt;= 0x0951 &amp;&amp; c &lt;= 0x0954 
-		|| c &gt;= 0x0962 &amp;&amp; c &lt;= 0x0963 
-		|| c &gt;= 0x0981 &amp;&amp; c &lt;= 0x0983 
-		|| c == 0x09BC 
-		|| c == 0x09BE 
-		|| c == 0x09BF 
-		|| c &gt;= 0x09C0 &amp;&amp; c &lt;= 0x09C4 
-		|| c &gt;= 0x09C7 &amp;&amp; c &lt;= 0x09C8 
-		|| c &gt;= 0x09CB &amp;&amp; c &lt;= 0x09CD 
-		|| c == 0x09D7 
-		|| c &gt;= 0x09E2 &amp;&amp; c &lt;= 0x09E3 
-		|| c == 0x0A02 
-		|| c == 0x0A3C 
-		|| c == 0x0A3E 
-		|| c == 0x0A3F 
-		|| c &gt;= 0x0A40 &amp;&amp; c &lt;= 0x0A42 
-		|| c &gt;= 0x0A47 &amp;&amp; c &lt;= 0x0A48 
-		|| c &gt;= 0x0A4B &amp;&amp; c &lt;= 0x0A4D 
-		|| c &gt;= 0x0A70 &amp;&amp; c &lt;= 0x0A71 
-		|| c &gt;= 0x0A81 &amp;&amp; c &lt;= 0x0A83 
-		|| c == 0x0ABC 
-		|| c &gt;= 0x0ABE &amp;&amp; c &lt;= 0x0AC5 
-		|| c &gt;= 0x0AC7 &amp;&amp; c &lt;= 0x0AC9 
-		|| c &gt;= 0x0ACB &amp;&amp; c &lt;= 0x0ACD 
-		|| c &gt;= 0x0B01 &amp;&amp; c &lt;= 0x0B03 
-		|| c == 0x0B3C 
-		|| c &gt;= 0x0B3E &amp;&amp; c &lt;= 0x0B43 
-		|| c &gt;= 0x0B47 &amp;&amp; c &lt;= 0x0B48 
-		|| c &gt;= 0x0B4B &amp;&amp; c &lt;= 0x0B4D 
-		|| c &gt;= 0x0B56 &amp;&amp; c &lt;= 0x0B57 
-		|| c &gt;= 0x0B82 &amp;&amp; c &lt;= 0x0B83 
-		|| c &gt;= 0x0BBE &amp;&amp; c &lt;= 0x0BC2 
-		|| c &gt;= 0x0BC6 &amp;&amp; c &lt;= 0x0BC8 
-		|| c &gt;= 0x0BCA &amp;&amp; c &lt;= 0x0BCD 
-		|| c == 0x0BD7 
-		|| c &gt;= 0x0C01 &amp;&amp; c &lt;= 0x0C03 
-		|| c &gt;= 0x0C3E &amp;&amp; c &lt;= 0x0C44 
-		|| c &gt;= 0x0C46 &amp;&amp; c &lt;= 0x0C48 
-		|| c &gt;= 0x0C4A &amp;&amp; c &lt;= 0x0C4D 
-		|| c &gt;= 0x0C55 &amp;&amp; c &lt;= 0x0C56 
-		|| c &gt;= 0x0C82 &amp;&amp; c &lt;= 0x0C83 
-		|| c &gt;= 0x0CBE &amp;&amp; c &lt;= 0x0CC4 
-		|| c &gt;= 0x0CC6 &amp;&amp; c &lt;= 0x0CC8 
-		|| c &gt;= 0x0CCA &amp;&amp; c &lt;= 0x0CCD 
-		|| c &gt;= 0x0CD5 &amp;&amp; c &lt;= 0x0CD6 
-		|| c &gt;= 0x0D02 &amp;&amp; c &lt;= 0x0D03 
-		|| c &gt;= 0x0D3E &amp;&amp; c &lt;= 0x0D43 
-		|| c &gt;= 0x0D46 &amp;&amp; c &lt;= 0x0D48 
-		|| c &gt;= 0x0D4A &amp;&amp; c &lt;= 0x0D4D 
-		|| c == 0x0D57 
-		|| c == 0x0E31 
-		|| c &gt;= 0x0E34 &amp;&amp; c &lt;= 0x0E3A 
-		|| c &gt;= 0x0E47 &amp;&amp; c &lt;= 0x0E4E 
-		|| c == 0x0EB1 
-		|| c &gt;= 0x0EB4 &amp;&amp; c &lt;= 0x0EB9 
-		|| c &gt;= 0x0EBB &amp;&amp; c &lt;= 0x0EBC 
-		|| c &gt;= 0x0EC8 &amp;&amp; c &lt;= 0x0ECD 
-		|| c &gt;= 0x0F18 &amp;&amp; c &lt;= 0x0F19 
-		|| c == 0x0F35 
-		|| c == 0x0F37 
-		|| c == 0x0F39 
-		|| c == 0x0F3E 
-		|| c == 0x0F3F 
-		|| c &gt;= 0x0F71 &amp;&amp; c &lt;= 0x0F84 
-		|| c &gt;= 0x0F86 &amp;&amp; c &lt;= 0x0F8B 
-		|| c &gt;= 0x0F90 &amp;&amp; c &lt;= 0x0F95 
-		|| c == 0x0F97 
-		|| c &gt;= 0x0F99 &amp;&amp; c &lt;= 0x0FAD 
-		|| c &gt;= 0x0FB1 &amp;&amp; c &lt;= 0x0FB7 
-		|| c == 0x0FB9 
-		|| c &gt;= 0x20D0 &amp;&amp; c &lt;= 0x20DC 
-		|| c == 0x20E1 
-		|| c &gt;= 0x302A &amp;&amp; c &lt;= 0x302F 
-		|| c == 0x3099 
-		|| c == 0x309A
-		|| c == 0x00B7 
-		|| c == 0x02D0 
-		|| c == 0x02D1 
-		|| c == 0x0387 
-		|| c == 0x0640 
-		|| c == 0x0E46 
-		|| c == 0x0EC6 
-		|| c == 0x3005 
-		|| c &gt;= 0x3031 &amp;&amp; c &lt;= 0x3035 
-		|| c &gt;= 0x309D &amp;&amp; c &lt;= 0x309E 
-		|| c &gt;= 0x30FC &amp;&amp; c &lt;= 0x30FE;
-};
-
-Utilities.coalesceText = function(n) {
-	for (var m = n.firstChild; m != null; m = m.nextSibling) {
-		if (m.nodeType == 3 /*Node.TEXT_NODE*/ || m.nodeType == 4 /*Node.CDATA_SECTION_NODE*/) {
-			var s = m.nodeValue;
-			var first = m;
-			m = m.nextSibling;
-			while (m != null &amp;&amp; (m.nodeType == 3 /*Node.TEXT_NODE*/ || m.nodeType == 4 /*Node.CDATA_SECTION_NODE*/)) {
-				s += m.nodeValue;
-				var del = m;
-				m = m.nextSibling;
-				del.parentNode.removeChild(del);
-			}
-			if (first.nodeType == 4 /*Node.CDATA_SECTION_NODE*/) {
-				var p = first.parentNode;
-				if (first.nextSibling == null) {
-					p.removeChild(first);
-					p.appendChild(p.ownerDocument.createTextNode(s));
-				} else {
-					var next = first.nextSibling;
-					p.removeChild(first);
-					p.insertBefore(p.ownerDocument.createTextNode(s), next);
-				}
-			} else {
-				first.nodeValue = s;
-			}
-			if (m == null) {
-				break;
-			}
-		} else if (m.nodeType == 1 /*Node.ELEMENT_NODE*/) {
-			Utilities.coalesceText(m);
-		}
-	}
-};
-
-Utilities.instance_of = function(o, c) {
-	while (o != null) {
-		if (o.constructor === c) {
-			return true;
-		}
-		if (o === Object) {
-			return false;
-		}
-		o = o.constructor.superclass;
-	}
-	return false;
-};
-
-Utilities.getElementById = function(n, id) {
-	// Note that this does not check the DTD to check for actual
-	// attributes of type ID, so this may be a bit wrong.
-	if (n.nodeType == 1 /*Node.ELEMENT_NODE*/) {
-		if (n.getAttribute(&quot;id&quot;) == id
-				|| n.getAttributeNS(null, &quot;id&quot;) == id) {
-			return n;
-		}
-	}
-	for (var m = n.firstChild; m != null; m = m.nextSibling) {
-		var res = Utilities.getElementById(m, id);
-		if (res != null) {
-			return res;
-		}
-	}
-	return null;
-};
-
-// XPathException ////////////////////////////////////////////////////////////
-
-XPathException.prototype = {};
-XPathException.prototype.constructor = XPathException;
-XPathException.superclass = Object.prototype;
-
-function XPathException(c, e) {
-	this.code = c;
-	this.exception = e;
-}
-
-XPathException.prototype.toString = function() {
-	var msg = this.exception ? &quot;: &quot; + this.exception.toString() : &quot;&quot;;
-	switch (this.code) {
-		case XPathException.INVALID_EXPRESSION_ERR:
-			return &quot;Invalid expression&quot; + msg;
-		case XPathException.TYPE_ERR:
-			return &quot;Type error&quot; + msg;
-	}
-};
-
-XPathException.INVALID_EXPRESSION_ERR = 51;
-XPathException.TYPE_ERR = 52;
-
-// XPathExpression ///////////////////////////////////////////////////////////
-
-XPathExpression.prototype = {};
-XPathExpression.prototype.constructor = XPathExpression;
-XPathExpression.superclass = Object.prototype;
-
-function XPathExpression(e, r, p) {
-	this.xpath = p.parse(e);
-	this.context = new XPathContext();
-	this.context.namespaceResolver = new XPathNSResolverWrapper(r);
-}
-
-XPathExpression.prototype.evaluate = function(n, t, res) {
-	this.context.expressionContextNode = n;
-	var result = this.xpath.evaluate(this.context);
-	return new XPathResult(result, t);
-}
-
-// XPathNSResolverWrapper ////////////////////////////////////////////////////
-
-XPathNSResolverWrapper.prototype = {};
-XPathNSResolverWrapper.prototype.constructor = XPathNSResolverWrapper;
-XPathNSResolverWrapper.superclass = Object.prototype;
-
-function XPathNSResolverWrapper(r) {
-	this.xpathNSResolver = r;
-}
-
-XPathNSResolverWrapper.prototype.getNamespace = function(prefix, n) {
-    if (this.xpathNSResolver == null) {
-        return null;
-    }
-	return this.xpathNSResolver.lookupNamespaceURI(prefix);
-};
-
-// NodeXPathNSResolver ///////////////////////////////////////////////////////
-
-NodeXPathNSResolver.prototype = {};
-NodeXPathNSResolver.prototype.constructor = NodeXPathNSResolver;
-NodeXPathNSResolver.superclass = Object.prototype;
-
-function NodeXPathNSResolver(n) {
-	this.node = n;
-	this.namespaceResolver = new NamespaceResolver();
-}
-
-NodeXPathNSResolver.prototype.lookupNamespaceURI = function(prefix) {
-	return this.namespaceResolver.getNamespace(prefix, this.node);
-};
-
-// XPathResult ///////////////////////////////////////////////////////////////
-
-XPathResult.prototype = {};
-XPathResult.prototype.constructor = XPathResult;
-XPathResult.superclass = Object.prototype;
-
-function XPathResult(v, t) {
-	if (t == XPathResult.ANY_TYPE) {
-		if (v.constructor === XString) {
-			t = XPathResult.STRING_TYPE;
-		} else if (v.constructor === XNumber) {
-			t = XPathResult.NUMBER_TYPE;
-		} else if (v.constructor === XBoolean) {
-			t = XPathResult.BOOLEAN_TYPE;
-		} else if (v.constructor === XNodeSet) {
-			t = XPathResult.UNORDERED_NODE_ITERATOR_TYPE;
-		}
-	}
-	this.resultType = t;
-	switch (t) {
-		case XPathResult.NUMBER_TYPE:
-			this.numberValue = v.numberValue();
-			return;
-		case XPathResult.STRING_TYPE:
-			this.stringValue = v.stringValue();
-			return;
-		case XPathResult.BOOLEAN_TYPE:
-			this.booleanValue = v.booleanValue();
-			return;
-		case XPathResult.ANY_UNORDERED_NODE_TYPE:
-		case XPathResult.FIRST_UNORDERED_NODE_TYPE:
-			if (v.constructor === XNodeSet) {
-				this.singleNodeValue = v.first();
-				return;
-			}
-			break;
-		case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
-		case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
-			if (v.constructor === XNodeSet) {
-				this.invalidIteratorState = false;
-				this.nodes = v.toArray();
-				this.iteratorIndex = 0;
-				return;
-			}
-			break;
-		case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE:
-		case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE:
-			if (v.constructor === XNodeSet) {
-				this.nodes = v.toArray();
-				this.snapshotLength = this.nodes.length;
-				return;
-			}
-			break;
-	}
-	throw new XPathException(XPathException.TYPE_ERR);
-};
-
-XPathResult.prototype.iterateNext = function() {
-	if (this.resultType != XPathResult.UNORDERED_NODE_ITERATOR_TYPE
-			&amp;&amp; this.resultType != XPathResult.ORDERED_NODE_ITERATOR_TYPE) {
-		throw new XPathException(XPathException.TYPE_ERR);
-	}
-	return this.nodes[this.iteratorIndex++];
-};
-
-XPathResult.prototype.snapshotItem = function(i) {
-	if (this.resultType != XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE
-			&amp;&amp; this.resultType != XPathResult.ORDERED_NODE_SNAPSHOT_TYPE) {
-		throw new XPathException(XPathException.TYPE_ERR);
-	}
-	return this.nodes[i];
-};
-
-XPathResult.ANY_TYPE = 0;
-XPathResult.NUMBER_TYPE = 1;
-XPathResult.STRING_TYPE = 2;
-XPathResult.BOOLEAN_TYPE = 3;
-XPathResult.UNORDERED_NODE_ITERATOR_TYPE = 4;
-XPathResult.ORDERED_NODE_ITERATOR_TYPE = 5;
-XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE = 6;
-XPathResult.ORDERED_NODE_SNAPSHOT_TYPE = 7;
-XPathResult.ANY_UNORDERED_NODE_TYPE = 8;
-XPathResult.FIRST_ORDERED_NODE_TYPE = 9;
-
-// DOM 3 XPath support ///////////////////////////////////////////////////////
-
-function installDOM3XPathSupport(doc, p) {
-	doc.createExpression = function(e, r) {
-		try {
-			return new XPathExpression(e, r, p);
-		} catch (e) {
-			throw new XPathException(XPathException.INVALID_EXPRESSION_ERR, e);
-		}
-	};
-	doc.createNSResolver = function(n) {
-		return new NodeXPathNSResolver(n);
-	};
-	doc.evaluate = function(e, cn, r, t, res) {
-		if (t &lt; 0 || t &gt; 9) {
-			throw { code: 0, toString: function() { return &quot;Request type not supported&quot;; } };
-		}
-        return doc.createExpression(e, r, p).evaluate(cn, t, res);
-	};
-};
-
-// ---------------------------------------------------------------------------
-
-// Install DOM 3 XPath support for the current document.
-try {
-	var shouldInstall = true;
-	try {
-		if (document.implementation
-				&amp;&amp; document.implementation.hasFeature
-				&amp;&amp; document.implementation.hasFeature(&quot;XPath&quot;, null)) {
-			shouldInstall = false;
-		}
-	} catch (e) {
-	}
-	if (shouldInstall) {
-		installDOM3XPathSupport(document, new XPathParser());
-	}
-} catch (e) {
-}
+/*
+ * xpath.js
+ *
+ * An XPath 1.0 library for JavaScript.
+ *
+ * Cameron McCormack &lt;cam (at) mcc.id.au&gt;
+ *
+ * This work is licensed under the Creative Commons Attribution-ShareAlike
+ * License. To view a copy of this license, visit
+ * 
+ *   http://creativecommons.org/licenses/by-sa/2.0/
+ *
+ * or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford,
+ * California 94305, USA.
+ *
+ * Revision 18: October 27, 2005
+ *   DOM 3 XPath support.  Caveats:
+ *     - namespace prefixes aren't resolved in XPathEvaluator.createExpression,
+ *       but in XPathExpression.evaluate.
+ *     - XPathResult.invalidIteratorState is not implemented.
+ *
+ * Revision 17: October 25, 2005
+ *   Some core XPath function fixes and a patch to avoid crashing certain
+ *   versions of MSXML in PathExpr.prototype.getOwnerElement, thanks to
+ *   S&#233;bastien Cramatte &lt;contact (at) zeninteractif.com&gt;.
+ *
+ * Revision 16: September 22, 2005
+ *   Workarounds for some IE 5.5 deficiencies.
+ *   Fixed problem with prefix node tests on attribute nodes.
+ *
+ * Revision 15: May 21, 2005
+ *   Fixed problem with QName node tests on elements with an xmlns=&quot;...&quot;.
+ *
+ * Revision 14: May 19, 2005
+ *   Fixed QName node tests on attribute node regression.
+ *
+ * Revision 13: May 3, 2005
+ *   Node tests are case insensitive now if working in an HTML DOM.
+ *
+ * Revision 12: April 26, 2005
+ *   Updated licence.  Slight code changes to enable use of Dean
+ *   Edwards' script compression, http://dean.edwards.name/packer/ .
+ *
+ * Revision 11: April 23, 2005
+ *   Fixed bug with 'and' and 'or' operators, fix thanks to
+ *   Sandy McArthur &lt;sandy (at) mcarthur.org&gt;.
+ *
+ * Revision 10: April 15, 2005
+ *   Added support for a virtual root node, supposedly helpful for
+ *   implementing XForms.  Fixed problem with QName node tests and
+ *   the parent axis.
+ *
+ * Revision 9: March 17, 2005
+ *   Namespace resolver tweaked so using the document node as the context
+ *   for namespace lookups is equivalent to using the document element.
+ *
+ * Revision 8: February 13, 2005
+ *   Handle implicit declaration of 'xmlns' namespace prefix.
+ *   Fixed bug when comparing nodesets.
+ *   Instance data can now be associated with a FunctionResolver, and
+ *     workaround for MSXML not supporting 'localName' and 'getElementById',
+ *     thanks to Grant Gongaware.
+ *   Fix a few problems when the context node is the root node.
+ *   
+ * Revision 7: February 11, 2005
+ *   Default namespace resolver fix from Grant Gongaware
+ *   &lt;grant (at) gongaware.com&gt;.
+ *
+ * Revision 6: February 10, 2005
+ *   Fixed bug in 'number' function.
+ *
+ * Revision 5: February 9, 2005
+ *   Fixed bug where text nodes not getting converted to string values.
+ *
+ * Revision 4: January 21, 2005
+ *   Bug in 'name' function, fix thanks to Bill Edney.
+ *   Fixed incorrect processing of namespace nodes.
+ *   Fixed NamespaceResolver to resolve 'xml' namespace.
+ *   Implemented union '|' operator.
+ *
+ * Revision 3: January 14, 2005
+ *   Fixed bug with nodeset comparisons, bug lexing &lt; and &gt;.
+ *
+ * Revision 2: October 26, 2004
+ *   QName node test namespace handling fixed.  Few other bug fixes.
+ *   
+ * Revision 1: August 13, 2004
+ *   Bug fixes from William J. Edney &lt;bedney (at) technicalpursuit.com&gt;.
+ *   Added minimal licence.
+ *
+ * Initial version: June 14, 2004
+ */
+
+// XPathParser ///////////////////////////////////////////////////////////////
+
+XPathParser.prototype = new Object();
+XPathParser.prototype.constructor = XPathParser;
+XPathParser.superclass = Object.prototype;
+
+function XPathParser() {
+	this.init();
+}
+
+XPathParser.prototype.init = function() {
+	this.reduceActions = [];
+
+	this.reduceActions[3] = function(rhs) {
+		return new OrOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[5] = function(rhs) {
+		return new AndOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[7] = function(rhs) {
+		return new EqualsOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[8] = function(rhs) {
+		return new NotEqualOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[10] = function(rhs) {
+		return new LessThanOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[11] = function(rhs) {
+		return new GreaterThanOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[12] = function(rhs) {
+		return new LessThanOrEqualOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[13] = function(rhs) {
+		return new GreaterThanOrEqualOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[15] = function(rhs) {
+		return new PlusOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[16] = function(rhs) {
+		return new MinusOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[18] = function(rhs) {
+		return new MultiplyOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[19] = function(rhs) {
+		return new DivOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[20] = function(rhs) {
+		return new ModOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[22] = function(rhs) {
+		return new UnaryMinusOperation(rhs[1]);
+	};
+	this.reduceActions[24] = function(rhs) {
+		return new BarOperation(rhs[0], rhs[2]);
+	};
+	this.reduceActions[25] = function(rhs) {
+		return new PathExpr(undefined, undefined, rhs[0]);
+	};
+	this.reduceActions[27] = function(rhs) {
+		rhs[0].locationPath = rhs[2];
+		return rhs[0];
+	};
+	this.reduceActions[28] = function(rhs) {
+		rhs[0].locationPath = rhs[2];
+		rhs[0].locationPath.steps.unshift(new Step(Step.DESCENDANTORSELF, new NodeTest(NodeTest.NODE, undefined), []));
+		return rhs[0];
+	};
+	this.reduceActions[29] = function(rhs) {
+		return new PathExpr(rhs[0], [], undefined);
+	};
+	this.reduceActions[30] = function(rhs) {
+		if (Utilities.instance_of(rhs[0], PathExpr)) {
+			if (rhs[0].filterPredicates == undefined) {
+				rhs[0].filterPredicates = [];
+			}
+			rhs[0].filterPredicates.push(rhs[1]);
+			return rhs[0];
+		} else {
+			return new PathExpr(rhs[0], [rhs[1]], undefined);
+		}
+	};
+	this.reduceActions[32] = function(rhs) {
+		return rhs[1];
+	};
+	this.reduceActions[33] = function(rhs) {
+		return new XString(rhs[0]);
+	};
+	this.reduceActions[34] = function(rhs) {
+		return new XNumber(rhs[0]);
+	};
+	this.reduceActions[36] = function(rhs) {
+		return new FunctionCall(rhs[0], []);
+	};
+	this.reduceActions[37] = function(rhs) {
+		return new FunctionCall(rhs[0], rhs[2]);
+	};
+	this.reduceActions[38] = function(rhs) {
+		return [ rhs[0] ];
+	};
+	this.reduceActions[39] = function(rhs) {
+		rhs[2].unshift(rhs[0]);
+		return rhs[2];
+	};
+	this.reduceActions[43] = function(rhs) {
+		return new LocationPath(true, []);
+	};
+	this.reduceActions[44] = function(rhs) {
+		rhs[1].absolute = true;
+		return rhs[1];
+	};
+	this.reduceActions[46] = function(rhs) {
+		return new LocationPath(false, [ rhs[0] ]);
+	};
+	this.reduceActions[47] = function(rhs) {
+		rhs[0].steps.push(rhs[2]);
+		return rhs[0];
+	};
+	this.reduceActions[49] = function(rhs) {
+		return new Step(rhs[0], rhs[1], []);
+	};
+	this.reduceActions[50] = function(rhs) {
+		return new Step(Step.CHILD, rhs[0], []);
+	};
+	this.reduceActions[51] = function(rhs) {
+		return new Step(rhs[0], rhs[1], rhs[2]);
+	};
+	this.reduceActions[52] = function(rhs) {
+		return new Step(Step.CHILD, rhs[0], rhs[1]);
+	};
+	this.reduceActions[54] = function(rhs) {
+		return [ rhs[0] ];
+	};
+	this.reduceActions[55] = function(rhs) {
+		rhs[1].unshift(rhs[0]);
+		return rhs[1];
+	};
+	this.reduceActions[56] = function(rhs) {
+		if (rhs[0] == &quot;ancestor&quot;) {
+			return Step.ANCESTOR;
+		} else if (rhs[0] == &quot;ancestor-or-self&quot;) {
+			return Step.ANCESTORORSELF;
+		} else if (rhs[0] == &quot;attribute&quot;) {
+			return Step.ATTRIBUTE;
+		} else if (rhs[0] == &quot;child&quot;) {
+			return Step.CHILD;
+		} else if (rhs[0] == &quot;descendant&quot;) {
+			return Step.DESCENDANT;
+		} else if (rhs[0] == &quot;descendant-or-self&quot;) {
+			return Step.DESCENDANTORSELF;
+		} else if (rhs[0] == &quot;following&quot;) {
+			return Step.FOLLOWING;
+		} else if (rhs[0] == &quot;following-sibling&quot;) {
+			return Step.FOLLOWINGSIBLING;
+		} else if (rhs[0] == &quot;namespace&quot;) {
+			return Step.NAMESPACE;
+		} else if (rhs[0] == &quot;parent&quot;) {
+			return Step.PARENT;
+		} else if (rhs[0] == &quot;preceding&quot;) {
+			return Step.PRECEDING;
+		} else if (rhs[0] == &quot;preceding-sibling&quot;) {
+			return Step.PRECEDINGSIBLING;
+		} else if (rhs[0] == &quot;self&quot;) {
+			return Step.SELF;
+		}
+		return -1;
+	};
+	this.reduceActions[57] = function(rhs) {
+		return Step.ATTRIBUTE;
+	};
+	this.reduceActions[59] = function(rhs) {
+		if (rhs[0] == &quot;comment&quot;) {
+			return new NodeTest(NodeTest.COMMENT, undefined);
+		} else if (rhs[0] == &quot;text&quot;) {
+			return new NodeTest(NodeTest.TEXT, undefined);
+		} else if (rhs[0] == &quot;processing-instruction&quot;) {
+			return new NodeTest(NodeTest.PI, undefined);
+		} else if (rhs[0] == &quot;node&quot;) {
+			return new NodeTest(NodeTest.NODE, undefined);
+		}
+		return new NodeTest(-1, undefined);
+	};
+	this.reduceActions[60] = function(rhs) {
+		return new NodeTest(NodeTest.PI, rhs[2]);
+	};
+	this.reduceActions[61] = function(rhs) {
+		return rhs[1];
+	};
+	this.reduceActions[63] = function(rhs) {
+		rhs[1].absolute = true;
+		rhs[1].steps.unshift(new Step(Step.DESCENDANTORSELF, new NodeTest(NodeTest.NODE, undefined), []));
+		return rhs[1];
+	};
+	this.reduceActions[64] = function(rhs) {
+		rhs[0].steps.push(new Step(Step.DESCENDANTORSELF, new NodeTest(NodeTest.NODE, undefined), []));
+		rhs[0].steps.push(rhs[2]);
+		return rhs[0];
+	};
+	this.reduceActions[65] = function(rhs) {
+		return new Step(Step.SELF, new NodeTest(NodeTest.NODE, undefined), []);
+	};
+	this.reduceActions[66] = function(rhs) {
+		return new Step(Step.PARENT, new NodeTest(NodeTest.NODE, undefined), []);
+	};
+	this.reduceActions[67] = function(rhs) {
+		return new VariableReference(rhs[1]);
+	};
+	this.reduceActions[68] = function(rhs) {
+		return new NodeTest(NodeTest.NAMETESTANY, undefined);
+	};
+	this.reduceActions[69] = function(rhs) {
+		var prefix = rhs[0].substring(0, rhs[0].indexOf(&quot;:&quot;));
+		return new NodeTest(NodeTest.NAMETESTPREFIXANY, prefix);
+	};
+	this.reduceActions[70] = function(rhs) {
+		return new NodeTest(NodeTest.NAMETESTQNAME, rhs[0]);
+	};
+};
+
+XPathParser.actionTable = [
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot;                 s                  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
+	&quot;                rrrrr               &quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot;rs  rrrrrrrr s  sssssrrrrrr  rrs rs &quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot;                            s       &quot;,
+	&quot;                            s       &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;  s                                 &quot;,
+	&quot;                            s       &quot;,
+	&quot; s           s  sssss          s  s &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
+	&quot;a                                   &quot;,
+	&quot;r       s                    rr  r  &quot;,
+	&quot;r      sr                    rr  r  &quot;,
+	&quot;r   s  rr            s       rr  r  &quot;,
+	&quot;r   rssrr            rss     rr  r  &quot;,
+	&quot;r   rrrrr            rrrss   rr  r  &quot;,
+	&quot;r   rrrrrsss         rrrrr   rr  r  &quot;,
+	&quot;r   rrrrrrrr         rrrrr   rr  r  &quot;,
+	&quot;r   rrrrrrrr         rrrrrs  rr  r  &quot;,
+	&quot;r   rrrrrrrr         rrrrrr  rr  r  &quot;,
+	&quot;r   rrrrrrrr         rrrrrr  rr  r  &quot;,
+	&quot;r  srrrrrrrr         rrrrrrs rr sr  &quot;,
+	&quot;r  srrrrrrrr         rrrrrrs rr  r  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;r   rrrrrrrr         rrrrrr  rr  r  &quot;,
+	&quot;r   rrrrrrrr         rrrrrr  rr  r  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
+	&quot;                sssss               &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr sr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;                             s      &quot;,
+	&quot;r  srrrrrrrr         rrrrrrs rr  r  &quot;,
+	&quot;r   rrrrrrrr         rrrrr   rr  r  &quot;,
+	&quot;              s                     &quot;,
+	&quot;                             s      &quot;,
+	&quot;                rrrrr               &quot;,
+	&quot; s s        sssssssss    s sss s  ss&quot;,
+	&quot;r  srrrrrrrr         rrrrrrs rr  r  &quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s s        sssssssss      ss  s  ss&quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot; s           s  sssss          s  s &quot;,
+	&quot; s           s  sssss          s  s &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot; s           s  sssss          s  s &quot;,
+	&quot; s           s  sssss          s  s &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr sr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr sr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;                             s      &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;                             rr     &quot;,
+	&quot;                             s      &quot;,
+	&quot;                             rs     &quot;,
+	&quot;r      sr                    rr  r  &quot;,
+	&quot;r   s  rr            s       rr  r  &quot;,
+	&quot;r   rssrr            rss     rr  r  &quot;,
+	&quot;r   rssrr            rss     rr  r  &quot;,
+	&quot;r   rrrrr            rrrss   rr  r  &quot;,
+	&quot;r   rrrrr            rrrss   rr  r  &quot;,
+	&quot;r   rrrrr            rrrss   rr  r  &quot;,
+	&quot;r   rrrrr            rrrss   rr  r  &quot;,
+	&quot;r   rrrrrsss         rrrrr   rr  r  &quot;,
+	&quot;r   rrrrrsss         rrrrr   rr  r  &quot;,
+	&quot;r   rrrrrrrr         rrrrr   rr  r  &quot;,
+	&quot;r   rrrrrrrr         rrrrr   rr  r  &quot;,
+	&quot;r   rrrrrrrr         rrrrr   rr  r  &quot;,
+	&quot;r   rrrrrrrr         rrrrrr  rr  r  &quot;,
+	&quot;                                 r  &quot;,
+	&quot;                                 s  &quot;,
+	&quot;r  srrrrrrrr         rrrrrrs rr  r  &quot;,
+	&quot;r  srrrrrrrr         rrrrrrs rr  r  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr  r  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot; s s        sssssssss    s ss  s  ss&quot;,
+	&quot;r  rrrrrrrrr         rrrrrrr rr rr  &quot;,
+	&quot;                             r      &quot;
+];
+
+XPathParser.actionTableNumber = [
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot;                 J                  &quot;,
+	&quot;a  aaaaaaaaa         aaaaaaa aa  a  &quot;,
+	&quot;                YYYYY               &quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot;K1  KKKKKKKK .  +*)('KKKKKK  KK# K\&quot; &quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot;                            N       &quot;,
+	&quot;                            O       &quot;,
+	&quot;e  eeeeeeeee         eeeeeee ee ee  &quot;,
+	&quot;f  fffffffff         fffffff ff ff  &quot;,
+	&quot;d  ddddddddd         ddddddd dd dd  &quot;,
+	&quot;B  BBBBBBBBB         BBBBBBB BB BB  &quot;,
+	&quot;A  AAAAAAAAA         AAAAAAA AA AA  &quot;,
+	&quot;  P                                 &quot;,
+	&quot;                            Q       &quot;,
+	&quot; 1           .  +*)('          #  \&quot; &quot;,
+	&quot;b  bbbbbbbbb         bbbbbbb bb  b  &quot;,
+	&quot;                                    &quot;,
+	&quot;!       S                    !!  !  &quot;,
+	&quot;\&quot;      T\&quot;                    \&quot;\&quot;  \&quot;  &quot;,
+	&quot;$   V  $$            U       $$  $  &quot;,
+	&quot;&amp;   &amp;ZY&amp;&amp;            &amp;XW     &amp;&amp;  &amp;  &quot;,
+	&quot;)   )))))            )))\\[   ))  )  &quot;,
+	&quot;.   ....._^]         .....   ..  .  &quot;,
+	&quot;1   11111111         11111   11  1  &quot;,
+	&quot;5   55555555         55555`  55  5  &quot;,
+	&quot;7   77777777         777777  77  7  &quot;,
+	&quot;9   99999999         999999  99  9  &quot;,
+	&quot;:  c::::::::         ::::::b :: a:  &quot;,
+	&quot;I  fIIIIIIII         IIIIIIe II  I  &quot;,
+	&quot;=  =========         ======= == ==  &quot;,
+	&quot;?  ?????????         ??????? ?? ??  &quot;,
+	&quot;C  CCCCCCCCC         CCCCCCC CC CC  &quot;,
+	&quot;J   JJJJJJJJ         JJJJJJ  JJ  J  &quot;,
+	&quot;M   MMMMMMMM         MMMMMM  MM  M  &quot;,
+	&quot;N  NNNNNNNNN         NNNNNNN NN  N  &quot;,
+	&quot;P  PPPPPPPPP         PPPPPPP PP  P  &quot;,
+	&quot;                +*)('               &quot;,
+	&quot;R  RRRRRRRRR         RRRRRRR RR aR  &quot;,
+	&quot;U  UUUUUUUUU         UUUUUUU UU  U  &quot;,
+	&quot;Z  ZZZZZZZZZ         ZZZZZZZ ZZ ZZ  &quot;,
+	&quot;c  ccccccccc         ccccccc cc cc  &quot;,
+	&quot;                             j      &quot;,
+	&quot;L  fLLLLLLLL         LLLLLLe LL  L  &quot;,
+	&quot;6   66666666         66666   66  6  &quot;,
+	&quot;              k                     &quot;,
+	&quot;                             l      &quot;,
+	&quot;                XXXXX               &quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$m #  \&quot;!&quot;,
+	&quot;_  f________         ______e __  _  &quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('      %$  #  \&quot;!&quot;,
+	&quot; 1 0        /.-,+*)('    &amp; %$  #  \&quot;!&quot;,
+	&quot; 1           .  +*)('          #  \&quot; &quot;,
+	&quot; 1           .  +*)('          #  \&quot; &quot;,
+	&quot;&gt;  &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;         &gt;&gt;&gt;&gt;&gt;&gt;&gt; &gt;&gt; &gt;&gt;  &quot;,
+	&quot; 1           .  +*)('          #  \&quot; &quot;,
+	&quot; 1           .  +*)('          #  \&quot; &quot;,
+	&quot;Q  QQQQQQQQQ         QQQQQQQ QQ aQ  &quot;,
+	&quot;V  VVVVVVVVV         VVVVVVV VV aV  &quot;,
+	&quot;T  TTTTTTTTT         TTTTTTT TT  T  &quot;,
+	&quot;@  @@@@@@@@@         @@@@@@@ @@ @@  &quot;,
+	&quot;                             \x87      &quot;,
+	&quot;[  [[[[[[[[[         [[[[[[[ [[ [[  &quot;,
+	&quot;D  DDDDDDDDD         DDDDDDD DD DD  &quot;,
+	&quot;                             HH     &quot;,
+	&quot;                             \x88      &quot;,
+	&quot;                             F\x89     &quot;,
+	&quot;#      T#                    ##  #  &quot;,
+	&quot;%   V  %%            U       %%  %  &quot;,
+	&quot;'   'ZY''            'XW     ''  '  &quot;,
+	&quot;(   (ZY((            (XW     ((  (  &quot;,
+	&quot;+   +++++            +++\\[   ++  +  &quot;,
+	&quot;*   *****            ***\\[   **  *  &quot;,
+	&quot;-   -----            ---\\[   --  -  &quot;,
+	&quot;,   ,,,,,            ,,,\\[   ,,  ,  &quot;,
+	&quot;0   00000_^]         00000   00  0  &quot;,
+	&quot;/   /////_^]         /////   //  /  &quot;,
+	&quot;2   22222222         22222   22  2  &quot;,
+	&