diff options
118 files changed, 10403 insertions, 13099 deletions
diff --git a/.gitattributes b/.gitattributes index 08cd6622..a18d9820 100644 --- a/.gitattributes +++ b/.gitattributes @@ -130,6 +130,10 @@ demos/quickstart/index.php -text demos/quickstart/index2.php -text demos/quickstart/protected/.htaccess -text demos/quickstart/protected/application.xml -text +demos/quickstart/protected/comments/CommentList.php -text +demos/quickstart/protected/comments/CommentList.tpl -text +demos/quickstart/protected/comments/QuickStartComments.php -text +demos/quickstart/protected/comments/comments.db -text demos/quickstart/protected/controls/DocLink.php -text demos/quickstart/protected/controls/Layout.php -text demos/quickstart/protected/controls/Layout.tpl -text @@ -233,6 +237,8 @@ demos/quickstart/protected/pages/Advanced/mastercontent.gif -text demos/quickstart/protected/pages/Advanced/mastercontent.vsd -text demos/quickstart/protected/pages/Advanced/pcrelation.gif -text demos/quickstart/protected/pages/Advanced/pcrelation.vsd -text +demos/quickstart/protected/pages/Comments.page -text +demos/quickstart/protected/pages/Comments.php -text demos/quickstart/protected/pages/Configurations/AppConfig.page -text demos/quickstart/protected/pages/Configurations/Overview.page -text demos/quickstart/protected/pages/Configurations/PageConfig.page -text @@ -476,6 +482,8 @@ docs/sqlmap/latex/tut3.tex -text docs/sqlmap/sqlmap.pdf -text docs/sqlmap/sqlmap_tut.pdf -text framework/.htaccess -text +framework/3rdParty/Markdown/License.text -text +framework/3rdParty/Markdown/MarkdownParser.php -text framework/3rdParty/SafeHtml/HTMLSax3.php -text framework/3rdParty/SafeHtml/HTMLSax3/Decorators.php -text framework/3rdParty/SafeHtml/HTMLSax3/States.php -text @@ -1074,6 +1082,7 @@ framework/Web/UI/WebControls/TListControl.php -text framework/Web/UI/WebControls/TListControlValidator.php -text framework/Web/UI/WebControls/TListItem.php -text framework/Web/UI/WebControls/TLiteral.php -text +framework/Web/UI/WebControls/TMarkdown.php -text framework/Web/UI/WebControls/TMultiView.php -text framework/Web/UI/WebControls/TOutputCache.php -text framework/Web/UI/WebControls/TPanel.php -text @@ -1189,76 +1198,39 @@ tests/FunctionalTests/quickstart/Controls/Wizard3TestCase.php -text tests/FunctionalTests/quickstart/Controls/Wizard4TestCase.php -text tests/FunctionalTests/quickstart/Controls/Wizard5TestCase.php -text tests/FunctionalTests/quickstart/Fundamentals/HangmanTestCase.php -text -tests/FunctionalTests/selenium/SeleneseRunner.html -text -tests/FunctionalTests/selenium/SeleniumLog.html -text -tests/FunctionalTests/selenium/TestRunner-splash.html -text -tests/FunctionalTests/selenium/TestRunner.hta -text -tests/FunctionalTests/selenium/TestRunner.html -text -tests/FunctionalTests/selenium/VERSION.txt -text -tests/FunctionalTests/selenium/build.xml -text -tests/FunctionalTests/selenium/doc/FAQ.html -text -tests/FunctionalTests/selenium/doc/FAQ.txt -text -tests/FunctionalTests/selenium/doc/contact.html -text -tests/FunctionalTests/selenium/doc/developingdrivers.html -text -tests/FunctionalTests/selenium/doc/driven.html -text -tests/FunctionalTests/selenium/doc/home-page.html -text -tests/FunctionalTests/selenium/doc/images/Adjacent.png -text -tests/FunctionalTests/selenium/doc/images/Embedded.png -text -tests/FunctionalTests/selenium/doc/images/SmallAdjacent.png -text -tests/FunctionalTests/selenium/doc/images/SmallEmbedded.png -text -tests/FunctionalTests/selenium/doc/images/SmallStandalone.png -text -tests/FunctionalTests/selenium/doc/images/Standalone.png -text -tests/FunctionalTests/selenium/doc/images/localhostAut.png -text -tests/FunctionalTests/selenium/doc/images/localhostDriver.png -text -tests/FunctionalTests/selenium/doc/images/localhostSelenium.png -text -tests/FunctionalTests/selenium/doc/images/stockmeister.png -text -tests/FunctionalTests/selenium/doc/images/tested-with-selenium.png -text -tests/FunctionalTests/selenium/doc/index.html -text -tests/FunctionalTests/selenium/doc/jsrmi.html -text -tests/FunctionalTests/selenium/doc/release-notes.html -text -tests/FunctionalTests/selenium/doc/rst2html.bat -text -tests/FunctionalTests/selenium/doc/seleniumReference.html -text -tests/FunctionalTests/selenium/doc/seleniumReference.txt -text -tests/FunctionalTests/selenium/doc/testRunner.txt -text -tests/FunctionalTests/selenium/doc/testrunner.html -text -tests/FunctionalTests/selenium/doc/usage.html -text -tests/FunctionalTests/selenium/dom-images/butmin.gif -text -tests/FunctionalTests/selenium/dom-images/butplus.gif -text -tests/FunctionalTests/selenium/dom-styles/default.css -text -tests/FunctionalTests/selenium/domviewer.html -text -tests/FunctionalTests/selenium/html-xpath/carnation.jpg -text -tests/FunctionalTests/selenium/html-xpath/example.html -text -tests/FunctionalTests/selenium/html-xpath/html-xpath-patched.js -text -tests/FunctionalTests/selenium/html-xpath/html-xpath.js -text -tests/FunctionalTests/selenium/html-xpath/license.txt -text -tests/FunctionalTests/selenium/html-xpath/rainbow.jpg -text -tests/FunctionalTests/selenium/htmlutils.js -text -tests/FunctionalTests/selenium/index.html -text -tests/FunctionalTests/selenium/install-readme.txt -text -tests/FunctionalTests/selenium/jsmock/mock-tests.html -text -tests/FunctionalTests/selenium/jsmock/mock.js -text +tests/FunctionalTests/selenium/core/SeleneseRunner.html -text +tests/FunctionalTests/selenium/core/SeleniumLog.html -text +tests/FunctionalTests/selenium/core/TestPrompt.html -text +tests/FunctionalTests/selenium/core/TestRunner-splash.html -text +tests/FunctionalTests/selenium/core/TestRunner.html -text +tests/FunctionalTests/selenium/core/domviewer/butmin.gif -text +tests/FunctionalTests/selenium/core/domviewer/butplus.gif -text +tests/FunctionalTests/selenium/core/domviewer/domviewer.css -text +tests/FunctionalTests/selenium/core/domviewer/domviewer.html -text +tests/FunctionalTests/selenium/core/domviewer/selenium-domviewer.js -text +tests/FunctionalTests/selenium/core/scripts/find_matching_child.js -text +tests/FunctionalTests/selenium/core/scripts/htmlutils.js -text +tests/FunctionalTests/selenium/core/scripts/prototype-1.4.0.js -text +tests/FunctionalTests/selenium/core/scripts/selenium-api.js -text +tests/FunctionalTests/selenium/core/scripts/selenium-browserbot.js -text +tests/FunctionalTests/selenium/core/scripts/selenium-browserdetect.js -text +tests/FunctionalTests/selenium/core/scripts/selenium-commandhandlers.js -text +tests/FunctionalTests/selenium/core/scripts/selenium-executionloop.js -text +tests/FunctionalTests/selenium/core/scripts/selenium-logging.js -text +tests/FunctionalTests/selenium/core/scripts/selenium-seleneserunner.js -text +tests/FunctionalTests/selenium/core/scripts/selenium-testrunner.js -text +tests/FunctionalTests/selenium/core/scripts/selenium-version.js -text +tests/FunctionalTests/selenium/core/scripts/user-extensions.js.sample -text +tests/FunctionalTests/selenium/core/scripts/xmlextras.js -text +tests/FunctionalTests/selenium/core/selenium-logo.png -text +tests/FunctionalTests/selenium/core/selenium.css -text +tests/FunctionalTests/selenium/core/xpath/dom.js -text +tests/FunctionalTests/selenium/core/xpath/misc.js -text +tests/FunctionalTests/selenium/core/xpath/xpath.js -text tests/FunctionalTests/selenium/php/TestRunner.php -text tests/FunctionalTests/selenium/php/results.php -text tests/FunctionalTests/selenium/php/selenium.php -text tests/FunctionalTests/selenium/prado-functional-test.js -text -tests/FunctionalTests/selenium/readme-selenium-fitrunner.txt -text -tests/FunctionalTests/selenium/readme-selenium-rpcrunner.txt -text -tests/FunctionalTests/selenium/selenium-api.js -text -tests/FunctionalTests/selenium/selenium-browserbot.js -text -tests/FunctionalTests/selenium/selenium-commandhandlers.js -text -tests/FunctionalTests/selenium/selenium-domviewer.js -text -tests/FunctionalTests/selenium/selenium-executionloop.js -text -tests/FunctionalTests/selenium/selenium-fitrunner.js -text -tests/FunctionalTests/selenium/selenium-logging.js -text -tests/FunctionalTests/selenium/selenium-logo.graffle -text -tests/FunctionalTests/selenium/selenium-logo.png -text -tests/FunctionalTests/selenium/selenium-seleneserunner.js -text -tests/FunctionalTests/selenium/selenium-tableparser.js -text -tests/FunctionalTests/selenium/selenium-testrunner.js -text -tests/FunctionalTests/selenium/selenium.css -text -tests/FunctionalTests/selenium/user-extensions.js.sample -text -tests/FunctionalTests/selenium/xmlextras.js -text -tests/FunctionalTests/selenium/xpath.js -text tests/FunctionalTests/tickets.php -text tests/FunctionalTests/tickets/index.php -text tests/FunctionalTests/tickets/protected/pages/Layout.php -text @@ -31,6 +31,7 @@ ENH: added TTheme.BaseUrl and TTheme.BasePath property (Qiang) ENH: added TListControl.SelectedValues property (Qiang)
ENH: added TThemeManager.AvailableThemes property (Qiang)
ENH: refactored TUserManager and TAuthManager so that they are easier to be extended (Qiang)
+ENH: template syntax now supports setting event handler via subproperties (Qiang)
CHG: Ticket#151 - URL format is modified to handle empty GET values (Qiang)
CHG: Ticket#153 - TAssetManager now ignores .svn directories (Qiang)
NEW: TTableHeaderRow, TTableFooterRow and table section support (Qiang)
diff --git a/demos/blog/protected/Pages/Admin/ConfigMan.page b/demos/blog/protected/Pages/Admin/ConfigMan.page index 5c6cd53d..b959551e 100644 --- a/demos/blog/protected/Pages/Admin/ConfigMan.page +++ b/demos/blog/protected/Pages/Admin/ConfigMan.page @@ -1,3 +1,5 @@ +<%@ Title="System Configuration" %>
+
<com:TContent ID="Main">
<h2>Administration Center</h2>
diff --git a/demos/blog/protected/Pages/Admin/PostMan.page b/demos/blog/protected/Pages/Admin/PostMan.page index 68ac44f8..9d96af42 100644 --- a/demos/blog/protected/Pages/Admin/PostMan.page +++ b/demos/blog/protected/Pages/Admin/PostMan.page @@ -1,3 +1,5 @@ +<%@ Title="Post Management" %>
+
<com:TContent ID="Main">
<h2>Administration Center</h2>
diff --git a/demos/blog/protected/Pages/Admin/UserMan.page b/demos/blog/protected/Pages/Admin/UserMan.page index a8b634c6..02b1a069 100644 --- a/demos/blog/protected/Pages/Admin/UserMan.page +++ b/demos/blog/protected/Pages/Admin/UserMan.page @@ -1,3 +1,5 @@ +<%@ Title="User Management" %>
+
<com:TContent ID="Main">
<h2>Administration Center</h2>
diff --git a/demos/blog/protected/Pages/Posts/MyPost.page b/demos/blog/protected/Pages/Posts/MyPost.page index ca153166..69ba78e4 100644 --- a/demos/blog/protected/Pages/Posts/MyPost.page +++ b/demos/blog/protected/Pages/Posts/MyPost.page @@ -1,3 +1,5 @@ +<%@ Title="My Posts" %>
+
<com:TContent ID="Main">
<h2>My Posts</h2>
@@ -25,7 +27,7 @@ <com:TBoundColumn
HeaderText="Status"
DataField="Status"
- DataFormatString="#{0}===0?'Published':({0}===1?'Draft':'Pending')"
+ DataFormatString="#{0}===0?'Published':({0}===1?'Draft':({0}===2?'Pending':'Sticky'))"
ItemStyle.Width="70px"
/>
<com:TBoundColumn
diff --git a/demos/blog/themes/Fall/style.css b/demos/blog/themes/Fall/style.css index 9b18b151..ac4e40e7 100644 --- a/demos/blog/themes/Fall/style.css +++ b/demos/blog/themes/Fall/style.css @@ -30,6 +30,11 @@ a img { border: 0px none;
}
+div {
+ overflow: hidden;
+ word-wrap: break-word;
+}
+
#page {
background-color:#fff;
margin:0 auto;
diff --git a/demos/blog/themes/Spring/style.css b/demos/blog/themes/Spring/style.css index 6a675c0f..19b8f5ba 100644 --- a/demos/blog/themes/Spring/style.css +++ b/demos/blog/themes/Spring/style.css @@ -30,6 +30,11 @@ a img { border: 0px none;
}
+div {
+ overflow: hidden;
+ word-wrap: break-word;
+}
+
#page {
background-color:#fff;
margin:0 auto;
diff --git a/demos/blog/themes/Summer/style.css b/demos/blog/themes/Summer/style.css index d7218f7d..939b36ef 100644 --- a/demos/blog/themes/Summer/style.css +++ b/demos/blog/themes/Summer/style.css @@ -30,6 +30,11 @@ a img { border: 0px none;
}
+div {
+ overflow: hidden;
+ word-wrap: break-word;
+}
+
#page {
background-color:#fff;
margin:0 auto;
diff --git a/demos/blog/themes/Winter/style.css b/demos/blog/themes/Winter/style.css index bfa488fa..1d36859e 100644 --- a/demos/blog/themes/Winter/style.css +++ b/demos/blog/themes/Winter/style.css @@ -30,6 +30,11 @@ a img { border: 0px none;
}
+div {
+ overflow: hidden;
+ word-wrap: break-word;
+}
+
#page {
background:#fff;
margin:0 auto;
diff --git a/demos/quickstart/protected/application.xml b/demos/quickstart/protected/application.xml index fed0ec22..7ed4a749 100644 --- a/demos/quickstart/protected/application.xml +++ b/demos/quickstart/protected/application.xml @@ -19,6 +19,7 @@ </modules>
<paths>
<using namespace="Application.controls.*" />
+ <using namespace="Application.comments.*" />
</paths>
<services>
<service id="page" class="TPageService" DefaultPage="GettingStarted.Introduction" />
diff --git a/demos/quickstart/protected/comments/CommentList.php b/demos/quickstart/protected/comments/CommentList.php new file mode 100644 index 00000000..4328991f --- /dev/null +++ b/demos/quickstart/protected/comments/CommentList.php @@ -0,0 +1,59 @@ +<?php + +Prado::using('System.I18N.*'); + +/** + * CommentList class. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version : $ Sat May 27 17:53:15 AZOST 2006 $ + * @package Demo.Quickstart.comments + * @since 3.0 + */ +class CommentList extends TTemplateControl +{ + private $_exclude = array( + 'Comments', + 'Markdown', + 'Search', + 'GettingStarted.Introduction'); + + private $_quickstart; + + public function onLoad($param) + { + parent::onLoad($param); + + $this->_quickstart = new QuickStartComments(); + + $page = $this->getService()->getRequestedPagePath(); + + $this->listComments($page); + } + + protected function listComments($page) + { + $this->comments->setDataSource($this->_quickstart->getComments($page)); + $this->comments->dataBind(); + } + + public function addComment_Clicked($sender, $param) + { + $page = $this->getService()->getRequestedPagePath(); + $this->_quickstart->addNewComment($page, + $this->email->getText(), $this->content->getText()); + $this->multiView1->setActiveViewIndex(1); + $this->listComments($page); + } + + public function setVisible($value) + { + $page = $this->getService()->getRequestedPagePath(); + if(in_array($page, $this->_exclude)) + parent::setVisible(false); + else + parent::setVisible($value); + } +} + +?>
\ No newline at end of file diff --git a/demos/quickstart/protected/comments/CommentList.tpl b/demos/quickstart/protected/comments/CommentList.tpl new file mode 100644 index 00000000..0aeb2c81 --- /dev/null +++ b/demos/quickstart/protected/comments/CommentList.tpl @@ -0,0 +1,102 @@ +<div id="comments"> + +<com:TRepeater ID="comments"> + <prop:HeaderTemplate> + <h2 class="comment_header">Comments + <span style="font-size:0.8em">( <a href="#add_comments">Add your comments</a> )</span></h2> + </prop:HeaderTemplate> + <prop:ItemTemplate> + <div class="comment_item comment_item<%# $this->ItemIndex%2 %>"> + <span class="number" id="comment_<%# $this->ItemIndex+1 %>"><%# $this->ItemIndex+1 %></span> + <span class="email"> + <%# str_replace(array('@','.'),array(' at ',' dot '), strtoupper($this->DataItem['email'])) %> + </span> + <span class="date"> + <com:TDateFormat Value=<%# intval($this->DataItem['date_added']) %> /> + </span> + <div class="comment"> + <com:TSafeHtml> + <com:TMarkdown TextHighlighter.CssClass="source"> + <%# $this->DataItem['comment'] %> + </com:TMarkdown> + </com:TSafeHtml> + </div> + </div> + </prop:ItemTemplate> +</com:TRepeater> + +<com:TMultiView ID="multiView1" ActiveViewIndex="0"> + <com:TView ID="view1"> + <div id="add_comments" class="add_comments"> + <h3>Post a comment</h3> + <p><strong>Note:</strong> + Please only use the comments in relation to this page for + <ul> + <li>questions/critcisms/suggestions on the documentation,</li> + <li>small notes that can solve or clarify a particular problem or task.</li> + </ul> + If you experience errors please <a href="http://trac.pradosoft.com/newticket">file a ticket</a> + or <a href="http://www.pradosoft.com/forum/">ask at the forum</a>. + Please use the <a href="http://pradosoft.com/wiki/index.php/Main_Page">Prado wiki</a> for longer pieces and detailed solutions</a>. + </p> + <p>Comments will be periodically reviewed, integrated into the documentation and removed. + You may use <a href="?page=Markdown">markdown syntax</a> in your comment. </p> + + <div class="comment_email"> + <com:TLabel ID="email_label" Text="Email:" ForControl="email"/> + <com:TTextBox ID="email" /> + <com:TRequiredFieldValidator + ControlToValidate="email" + Display="Dynamic" + ErrorMessage="An email address is required." /> + <com:TEmailAddressValidator + ControlToValidate="email" + CheckMXRecord="false" + Display="Dynamic" + ErrorMessage="Please provide your email address."/> + </div> + <div class="comment_content"> + <com:TLabel ID="content_label" Text="Comment:" ForControl="content"/> + <com:TTextBox ID="content" TextMode="MultiLine"/> + <div class="please_add"> + <com:TRequiredFieldValidator + ControlToValidate="content" + Display="Dynamic" + ErrorMessage="Please add your comment." /> + </div> + </div> + <com:TPlaceHolder Visible=<%= strlen($this->content->Text) %> > + <div class="comment_preview"> + <h3 style="margin:0">Preview comment</h3> + <div class="comment"> + <com:TSafeHtml> + <com:TMarkdown TextHighlighter.CssClass="source"> + <%= $this->content->Text %> + </com:TMarkdown> + </com:TSafeHtml> + </div> + </div> + </com:TPlaceHolder> + <div class="add_comment"> + <com:TButton ID="previewComment" Text="Preview Comment" /> + <com:TButton ID="addComment" Text="Add Comment" OnClick="addComment_Clicked"/> + </div> + </div> + </com:TView> + <com:TView ID="view2"> + <div class="comment_added"> + <div class="thank">Thank you, your comment is quequed for moderation.</div> + <div class="comment_preview"> + <h3 style="margin:0">Preview comment</h3> + <div class="comment"> + <com:TSafeHtml> + <com:TMarkdown TextHighlighter.CssClass="source"> + <%= $this->content->Text %> + </com:TMarkdown> + </com:TSafeHtml> + </div> + </div> + </div> + </com:TView> +</com:TMultiView> +</div>
\ No newline at end of file diff --git a/demos/quickstart/protected/comments/QuickStartComments.php b/demos/quickstart/protected/comments/QuickStartComments.php new file mode 100644 index 00000000..c138bd94 --- /dev/null +++ b/demos/quickstart/protected/comments/QuickStartComments.php @@ -0,0 +1,175 @@ +<?php + +/** + * QuickStartComments class. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version : $ Sat May 27 16:49:19 AZOST 2006 $ + * @package Demos.QuickStart.comments + * @since 3.0 + */ +class QuickStartComments +{ + /** + * @var string sqlite database source. + */ + private $_database; + /** + * @var sqlite connection. + */ + private $_connection; + + /** + * Sets the sqlite comment database file. + */ + public function __construct() + { + $this->_database = realpath(dirname(__FILE__).'/comments.db'); + } + + /** + * Closed the database connection. + */ + public function __destruct() + { + if(!is_null($this->_connection)) + sqlite_close($this->_connection); + } + + /** + * @return resource sqlite database connection. + */ + protected function getConnection() + { + if(is_null($this->_connection)) + $this->_connection = sqlite_open($this->_database); + return $this->_connection; + } + + /** + * Quote database input data. + */ + protected function quote($value) + { + return sqlite_escape_string($value); + } + + /** + * Executes an sqlite query. + * @param string SQL + * @return mixed query results. + */ + protected function query($sql) + { + return sqlite_query($this->getConnection(), $sql); + } + + /** + * Returns a row from the sqlite result. + * @param resource sqlite result + * @return array database record. + */ + protected function fetch($resource) + { + if($resource !== false) + return sqlite_fetch_array($resource); + else + return false; + } + + /** + * Fetch all the records for given SQL query. + * @param string SQL query. + * @return array result set. + */ + protected function fetchAll($sql) + { + $rs = $this->query($sql); + $rows = array(); + while($row = $this->fetch($rs)) + $rows[] = $row; + return $rows; + } + + /** + * Returns all the comments for a given page. + * @param string specific page comments + * @return array list of comments + */ + public function getComments($pageID) + { + $page = $this->quote($pageID); + $sql = "SELECT * FROM comments WHERE page=\"$page\" AND approved = 1 ORDER BY date_added ASC"; + return $this->fetchAll($sql); + } + + /** + * Adds a new comment for moderation. + * @param string ID of the page to comment belongs + * @param string email address of the commenter + * @param string comment contents + */ + public function addNewComment($pageID, $email, $comment) + { + $page = $this->quote($pageID); + $email = $this->quote($email); + $comment = $this->quote($comment); + $date_added = time(); + $sql = <<<EOD + INSERT INTO comments(page, email, comment, date_added) + VALUES ("$page", "$email", "$comment", "$date_added") +EOD; + return $this->query($sql); + } + + /** + * Update an existing comment. + * @param string comment ID + * @param string page ID + * @param string email address + * @param string updated comment. + */ + public function updateComment($commentID, $page, $email, $content) + { + $ID = intval($commentID); + $email = $this->quote($email); + $comment = $this->quote($content); + $page = $this->quote($page); + $sql = <<<EOD + UPDATE comments SET + email = "$email", comment = "$comment", page = "$page" + WHERE id = $ID; +EOD; + $this->query($sql); + } + + /** + * Delete a comment. + * @param string comment ID + */ + public function deleteComment($commentID) + { + $ID = intval($commentID); + $this->query("DELETE FROM comments WHERE id=$ID"); + } + + /** + * @return array all the quequed comments. + */ + public function getQuequedComments() + { + return $this->fetchAll("SELECT * FROM comments WHERE approved != 1"); + } + + /** + * Approve a quequed comment. + * @param string comment ID. + */ + public function approveComment($commentID) + { + $ID = intval($commentID); + $this->query("UPDATE comments SET approved = 1 WHERE id=$ID"); + } +} + +?>
\ No newline at end of file diff --git a/demos/quickstart/protected/comments/comments.db b/demos/quickstart/protected/comments/comments.db Binary files differnew file mode 100644 index 00000000..b81d7123 --- /dev/null +++ b/demos/quickstart/protected/comments/comments.db diff --git a/demos/quickstart/protected/controls/Layout.tpl b/demos/quickstart/protected/controls/Layout.tpl index 6fbd1380..d4a6a7e5 100644 --- a/demos/quickstart/protected/controls/Layout.tpl +++ b/demos/quickstart/protected/controls/Layout.tpl @@ -32,6 +32,7 @@ <div id="content">
<com:TContentPlaceHolder ID="body" />
</div>
+<com:CommentList Visible="true" />
</td>
</tr>
</table>
diff --git a/demos/quickstart/protected/pages/Advanced/I18N.page b/demos/quickstart/protected/pages/Advanced/I18N.page index 5b1fafa0..6c86a6c9 100644 --- a/demos/quickstart/protected/pages/Advanced/I18N.page +++ b/demos/quickstart/protected/pages/Advanced/I18N.page @@ -148,9 +148,11 @@ To translate a message or string in the template, use <tt>TTranslate</tt>.</p> <com:TTranslate Text="Goodbye" />
</com:TTextHighlighter>
-<p><tt>TTranslate</tt> can also perform string substitution. Any attributes of <tt>TTranslate</tt> will be substituted with <tt>{attribute name}</tt> in the translation. E.g.</p>
+<p><tt>TTranslate</tt> can also perform string substitution.
+The <tt>Parameters</tt> property can be use to add name values pairs for substitution. Substrings in the translation enclosed with "{" and "}" are consider as the
+ parameter names during substitution lookup. The following example will substitute the substring "{time}" with the value of the parameter attribute "<tt>Parameters.time=<%= time() %></tt>".
<com:TTextHighlighter Language="prado" CssClass="source">
-<com:TTranslate time="late">
+<com:TTranslate Parameters.time=<%= time() %> >
The time is {time}.
</com:TTranslate>
</com:TTextHighlighter>
diff --git a/demos/quickstart/protected/pages/Comments.page b/demos/quickstart/protected/pages/Comments.page new file mode 100644 index 00000000..32c7bcae --- /dev/null +++ b/demos/quickstart/protected/pages/Comments.page @@ -0,0 +1,46 @@ +<com:TContent ID="body"> + <com:TDataList + ID="comments" + DataKeyField="id" + OnEditCommand="editComment" + OnCancelCommand="cancelEdit" + OnUpdateCommand="updateComment" + OnDeleteCommand="deleteComment" + OnSelectedIndexChanged="approveComment" + ItemStyle.BackColor="#BFCFFF" + AlternatingItemStyle.BackColor="#E6ECFF" + EditItemStyle.BackColor="lightgreen"> + + <prop:HeaderTemplate> + Comments awaiting approval + </prop:HeaderTemplate> + + <prop:ItemTemplate> + <span class="page"><%# $this->DataItem['page'] %></span> + <span class="date"> + <com:TDateFormat Value=<%# intval($this->DataItem['date_added']) %> /> + </span> + <span class="email"> + <%# $this->DataItem['email'] %> + </span> + <div class="comment"> + <com:TSafeHtml> + <%# $this->DataItem['comment']%> + </com:TSafeHtml> + </div> + <com:TLinkButton Text="Edit" CommandName="edit" /> + <com:TLinkButton Text="Delete" CommandName="delete" + Attributes.onclick="if(!confirm('Are you sure?')) return false;" /> + <com:TLinkButton Text="Approve" CommandName="select" /> + </prop:ItemTemplate> + + <prop:EditItemTemplate> + <com:TTextBox ID="email" Text=<%# $this->DataItem['email'] %> /> + <com:TTextBox ID="page" Text=<%# $this->DataItem['page'] %> /> + <com:TTextBox ID="content" Text=<%# $this->DataItem['comment'] %> TextMode="MultiLine"/> + <com:TLinkButton Text="Save" CommandName="update" /> + <com:TLinkButton Text="Cancel" CommandName="cancel" /> + </prop:EditItemTemplate> + + </com:TDataList> +</com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/Comments.php b/demos/quickstart/protected/pages/Comments.php new file mode 100644 index 00000000..7af70ece --- /dev/null +++ b/demos/quickstart/protected/pages/Comments.php @@ -0,0 +1,76 @@ +<?php + +Prado::using('System.I18N.*'); + +/** + * Comments class. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version : $ Sat May 27 20:23:00 AZOST 2006 $ + * @package Demo.Quickstart + * @since 3.0 + */ +class Comments extends TPage +{ + private $_quickstart; + + public function onLoad($param) + { + parent::onLoad($param); + $this->_quickstart = new QuickStartComments; + if(!$this->getIsPostBack()) + $this->refreshData(); + } + + protected function refreshData() + { + $this->comments->setDataSource($this->_quickstart->getQuequedComments()); + $this->comments->dataBind(); + } + + public function approveComment($sender, $param) + { + $ID = $this->comments->DataKeys[$this->comments->SelectedItemIndex]; + $this->_quickstart->approveComment($ID); + $this->refreshData(); + $this->comments->SelectedItemIndex=-1; + } + + public function editComment($sender, $param) + { + $this->comments->SelectedItemIndex=-1; + $this->comments->EditItemIndex=$param->Item->ItemIndex; + $this->refreshData(); + } + + public function cancelEdit($sender, $param) + { + $this->comments->SelectedItemIndex=-1; + $this->comments->EditItemIndex=-1; + $this->refreshData(); + } + + public function deleteComment($sender, $param) + { + $ID = $this->comments->DataKeys[$param->Item->ItemIndex]; + $this->_quickstart->deleteComment($ID); + $this->comments->SelectedItemIndex=-1; + $this->comments->EditItemIndex=-1; + $this->refreshData(); + } + + public function updateComment($sender, $param) + { + $item=$param->Item; + $this->_quickstart->updateComment( + $this->comments->DataKeys[$item->ItemIndex], + $item->page->Text, + $item->email->Text, + $item->content->Text); + + $this->comments->EditItemIndex=-1; + $this->refreshData(); + } +} + +?>
\ No newline at end of file diff --git a/demos/quickstart/themes/PradoSoft/style.css b/demos/quickstart/themes/PradoSoft/style.css index 0fed7813..782697ae 100644 --- a/demos/quickstart/themes/PradoSoft/style.css +++ b/demos/quickstart/themes/PradoSoft/style.css @@ -379,4 +379,142 @@ dd {
text-align: center;
margin: 30px;
+}
+
+/** Comments **/
+#comments
+{
+ margin: 10px;
+}
+
+.comment_header
+{
+ border-bottom: 1px solid silver;
+}
+
+.comment_item
+{
+ padding: 10px;
+ border: 1px solid silver;
+ margin: 0 10px 10px 10px;
+}
+
+.comment_item .number
+{
+ float: right;
+ font-size: 1.5em;
+ font-weight: bold;
+ color: silver;
+}
+
+.comment_item1
+{
+ background-color: #EDEDED;
+}
+
+.comment_item .email
+{
+ font-weight: bold;
+ display: block;
+}
+
+.comment_item .date
+{
+ font-size: 0.85em;
+ display: block;
+ border-bottom: 1px dotted silver;
+}
+
+.comment_item .comment
+{
+ padding: 10px;
+}
+
+.add_comments
+{
+ margin-top: 2em;
+}
+
+.add_comments .comment_email
+{
+ margin-bottom: 5px;
+}
+
+.add_comments .comment_email input
+{
+ width: 25em;
+}
+
+.add_comments .comment_content textarea
+{
+ width: 75%;
+ height: 200px;
+ padding: 5px;
+}
+
+.add_comments .comment_email label, .add_comments .comment_content label
+{
+ width: 8em;
+ float: left;
+ text-align: right;
+ padding-right: 5px;
+}
+
+.add_comments .add_comment
+{
+ padding-left: 8.2em;
+ padding-top: 0.5em;
+}
+
+.add_comments .please_add
+{
+ padding-left: 8.2em;
+}
+
+.add_comments h3, .comment_added h3
+{
+ border-bottom: 1px solid silver;
+}
+
+.comment_preview
+{
+ margin: 10px;
+ padding: 10px;
+ border: 1px solid silver;
+}
+
+.comment_preview .comment
+{
+ padding: 10px;
+}
+
+.comment_added
+{
+ margin-top: 3em;
+ border-top: 1px dotted silver;
+ padding: 10px;
+}
+
+.comment_added .comment
+{
+ padding: 10px;
+ margin-top: 10px;
+}
+
+.comment_added .thank
+{
+ background-color: #ffffcc;
+ padding: 20px;
+}
+
+pre code
+{
+ display: block;
+ padding: 0.5em;
+ border-style:solid;
+ border-width:1px;
+ border-color:#eeeeee;
+ background-color:#ffffee;
+ font-family: "Courier New", Courier, mono;
+ margin: 0.2em;
}
\ No newline at end of file diff --git a/framework/3rdParty/Markdown/License.text b/framework/3rdParty/Markdown/License.text new file mode 100644 index 00000000..ea6a6a1a --- /dev/null +++ b/framework/3rdParty/Markdown/License.text @@ -0,0 +1,34 @@ +Copyright (c) 2004-2005, John Gruber +<http://daringfireball.net/> +All rights reserved. + +Copyright (c) 2004-2005, Michel Fortin +<http://www.michelf.com/> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. diff --git a/framework/3rdParty/Markdown/MarkdownParser.php b/framework/3rdParty/Markdown/MarkdownParser.php new file mode 100644 index 00000000..c0d2becf --- /dev/null +++ b/framework/3rdParty/Markdown/MarkdownParser.php @@ -0,0 +1,1257 @@ +<?php + +# +# Markdown - A text-to-HTML conversion tool for web writers +# +# Copyright (c) 2004-2005 John Gruber +# <http://daringfireball.net/projects/markdown/> +# +# Copyright (c) 2004-2005 Michel Fortin - PHP Port +# <http://www.michelf.com/projects/php-markdown/> +# + +/** + * PHP5 version of the markdown parser. + * Usage: + * <code> + * $markdown = new MarkdownParser; + * echo $markdown->parse($text); + * </code> + */ +class MarkdownParser +{ + private static $md_nested_brackets; + private static $md_escape_table = array(); + private static $md_backslash_escape_table = array(); + private static $md_nested_brackets_depth = 6; + + protected $md_empty_element_suffix = " />"; # Change to ">" for HTML output + protected $md_tab_width = 4; + + private $md_list_level = 0; + private $md_urls = array(); + private $md_titles = array(); + private $md_html_blocks = array(); + + public function __construct() + { + if(is_null(self::$md_nested_brackets)) + $this->initialize(); + } + + private function initialize() + { + self::$md_nested_brackets = + str_repeat('(?>[^\[\]]+|\[', self::$md_nested_brackets_depth). + str_repeat('\])*', self::$md_nested_brackets_depth); + + self::$md_escape_table = array( + "\\" => md5("\\"), + "`" => md5("`"), + "*" => md5("*"), + "_" => md5("_"), + "{" => md5("{"), + "}" => md5("}"), + "[" => md5("["), + "]" => md5("]"), + "(" => md5("("), + ")" => md5(")"), + ">" => md5(">"), + "#" => md5("#"), + "+" => md5("+"), + "-" => md5("-"), + "." => md5("."), + "!" => md5("!") + ); + + # Table of hash values for escaped characters: + # Create an identical table but for escaped characters. + foreach (self::$md_escape_table as $key => $char) + self::$md_backslash_escape_table["\\$key"] = $char; + } + + public function parse($text) + { + # + # Main function. The order in which other subs are called here is + # essential. Link and image substitutions need to happen before + # _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a> + # and <img> tags get encoded. + # + # Clear the hashes. If we don't clear these, you get conflicts + # from other articles when generating a page which contains more than + # one article (e.g. an index page that shows the N most recent + # articles): + $this->md_urls = array(); + $this->md_titles = array(); + $this->md_html_blocks = array(); + + # Standardize line endings: + # DOS to Unix and Mac to Unix + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # Make sure $text ends with a couple of newlines: + $text .= "\n\n"; + + # Convert all tabs to spaces. + $text = $this->_Detab($text); + + # Strip any lines consisting only of spaces and tabs. + # This makes subsequent regexen easier to write, because we can + # match consecutive blank lines with /\n+/ instead of something + # contorted like /[ \t]*\n+/ . + $text = preg_replace('/^[ \t]+$/m', '', $text); + + # Turn block-level HTML blocks into hash entries + $text = $this->_HashHTMLBlocks($text); + + # Strip link definitions, store in hashes. + $text = $this->_StripLinkDefinitions($text); + + $text = $this->_RunBlockGamut($text); + + $text = $this->_UnescapeSpecialChars($text); + + return $text . "\n"; + } + + + private function _StripLinkDefinitions($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->md_tab_width - 1; + + # Link defs are in the form: ^[id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[(.+)\]: # id = $1 + [ \t]* + \n? # maybe *one* newline + [ \t]* + <?(\S+?)>? # url = $2 + [ \t]* + \n? # maybe one newline + [ \t]* + (?: + (?<=\s) # lookbehind for whitespace + ["(] + (.+?) # title = $3 + [")] + [ \t]* + )? # title is optional + (?:\n+|\Z) + }xm', + array($this,'_StripLinkDefinitions_callback'), + $text); + return $text; + } + + private function _StripLinkDefinitions_callback($matches) { + $link_id = strtolower($matches[1]); + $this->md_urls[$link_id] = $this->_EncodeAmpsAndAngles($matches[2]); + if (isset($matches[3])) + $this->md_titles[$link_id] = str_replace('"', '"', $matches[3]); + return ''; # String that will replace the block + } + + + private function _HashHTMLBlocks($text) { + $less_than_tab = $this->md_tab_width - 1; + + # Hashify HTML blocks: + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap <p>s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded: + $block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|'. + 'script|noscript|form|fieldset|iframe|math|ins|del'; + $block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|'. + 'script|noscript|form|fieldset|iframe|math'; + + # First, look for nested blocks, e.g.: + # <div> + # <div> + # tags for inner block must be indented. + # </div> + # </div> + # + # The outermost tags must start at the left margin for this to match, and + # the inner nested divs must be indented. + # We need to do this before the next, more liberal match, because the next + # match will start at the first `<div>` and stop at the first `</div>`. + $text = preg_replace_callback("{ + ( # save in $1 + ^ # start of line (with /m) + <($block_tags_a) # start tag = $2 + \\b # word break + (.*\\n)*? # any number of lines, minimally matching + </\\2> # the matching end tag + [ \\t]* # trailing spaces/tabs + (?=\\n+|\\Z) # followed by a newline or end of document + ) + }xm", + array($this,'_HashHTMLBlocks_callback'), + $text); + + # + # Now match more liberally, simply from `\n<tag>` to `</tag>\n` + # + $text = preg_replace_callback("{ + ( # save in $1 + ^ # start of line (with /m) + <($block_tags_b) # start tag = $2 + \\b # word break + (.*\\n)*? # any number of lines, minimally matching + .*</\\2> # the matching end tag + [ \\t]* # trailing spaces/tabs + (?=\\n+|\\Z) # followed by a newline or end of document + ) + }xm", + array($this,'_HashHTMLBlocks_callback'), + $text); + + # Special case just for <hr />. It was easier to make a special case than + # to make the other regex more complicated. + $text = preg_replace_callback('{ + (?: + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + [ ]{0,'.$less_than_tab.'} + <(hr) # start tag = $2 + \b # word break + ([^<>])*? # + /?> # the matching end tag + [ \t]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + ) + }x', + array($this,'_HashHTMLBlocks_callback'), + $text); + + # Special case for standalone HTML comments: + $text = preg_replace_callback('{ + (?: + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + [ ]{0,'.$less_than_tab.'} + (?s: + <! + (--.*?--\s*)+ + > + ) + [ \t]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + ) + }x', + array($this,'_HashHTMLBlocks_callback'), + $text); + + return $text; + } + private function _HashHTMLBlocks_callback($matches) { + $text = $matches[1]; + $key = md5($text); + $this->md_html_blocks[$key] = $text; + return "\n\n$key\n\n"; # String that will replace the block + } + + + private function _RunBlockGamut($text) { + # + # These are all the transformations that form block-level + # tags like paragraphs, headers, and list items. + # + $text = $this->_DoHeaders($text); + + # Do Horizontal Rules: + $text = preg_replace( + array('{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}mx', + '{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}mx', + '{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}mx'), + "\n<hr{$this->md_empty_element_suffix}\n", + $text); + + $text = $this->_DoLists($text); + $text = $this->_DoCodeBlocks($text); + $text = $this->_DoBlockQuotes($text); + + # We already ran _HashHTMLBlocks() before, in Markdown(), but that + # was to escape raw HTML in the original Markdown source. This time, + # we're escaping the markup we've just created, so that we don't wrap + # <p> tags around block-level tags. + $text = $this->_HashHTMLBlocks($text); + $text = $this->_FormParagraphs($text); + + return $text; + } + + + private function _RunSpanGamut($text) { + # + # These are all the transformations that occur *within* block-level + # tags like paragraphs, headers, and list items. + # + + $text = $this->_DoCodeSpans($text); + + $text = $this->_EscapeSpecialChars($text); + + # Process anchor and image tags. Images must come first, + # because ![foo][f] looks like an anchor. + $text = $this->_DoImages($text); + $text = $this->_DoAnchors($text); + + # Make links out of things like `<http://example.com/>` + # Must come after _DoAnchors(), because you can use < and > + # delimiters in inline links like [this](<url>). + $text = $this->_DoAutoLinks($text); + $text = $this->_EncodeAmpsAndAngles($text); + $text = $this->_DoItalicsAndBold($text); + + # Do hard breaks: + $text = preg_replace('/ {2,}\n/', "<br{$this->md_empty_element_suffix}\n", $text); + + return $text; + } + + + private function _EscapeSpecialChars($text) { + $tokens = $this->_TokenizeHTML($text); + + $text = ''; # rebuild $text from the tokens + # $in_pre = 0; # Keep track of when we're inside <pre> or <code> tags. + # $tags_to_skip = "!<(/?)(?:pre|code|kbd|script|math)[\s>]!"; + + foreach ($tokens as $cur_token) { + if ($cur_token[0] == 'tag') { + # Within tags, encode * and _ so they don't conflict + # with their use in Markdown for italics and strong. + # We're replacing each such character with its + # corresponding MD5 checksum value; this is likely + # overkill, but it should prevent us from colliding + # with the escape values by accident. + $cur_token[1] = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $cur_token[1]); + $text .= $cur_token[1]; + } else { + $t = $cur_token[1]; + $t = $this->_EncodeBackslashEscapes($t); + $text .= $t; + } + } + return $text; + } + + + private function _DoAnchors($text) { + # + # Turn Markdown link shortcuts into XHTML <a> tags. + # + # + # First, handle reference-style links: [link text] [id] + # + $bracket = self::$md_nested_brackets; + $text = preg_replace_callback("{ + ( # wrap whole match in $1 + \\[ + ({$bracket}) # link text = $2 + \\] + + [ ]? # one optional space + (?:\\n[ ]*)? # one optional newline followed by spaces + + \\[ + (.*?) # id = $3 + \\] + ) + }xs", + array($this,'_DoAnchors_reference_callback'), $text); + + # + # Next, inline-style links: [link text](url "optional title") + # + $text = preg_replace_callback("{ + ( # wrap whole match in $1 + \\[ + ({$bracket}) # link text = $2 + \\] + \\( # literal paren + [ \\t]* + <?(.*?)>? # href = $3 + [ \\t]* + ( # $4 + (['\"]) # quote char = $5 + (.*?) # Title = $6 + \\5 # matching quote + )? # title is optional + \\) + ) + }xs", + array($this,'_DoAnchors_inline_callback'), $text); + + return $text; + } + private function _DoAnchors_reference_callback($matches) { + $whole_match = $matches[1]; + $link_text = $matches[2]; + $link_id = strtolower($matches[3]); + + if ($link_id == "") { + $link_id = strtolower($link_text); # for shortcut links like [this][]. + } + + if (isset($this->md_urls[$link_id])) { + $url = $this->md_urls[$link_id]; + # We've got to encode these to avoid conflicting with italics/bold. + $url = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $url); + $result = "<a href=\"$url\""; + if ( isset( $this->md_titles[$link_id] ) ) { + $title = $this->md_titles[$link_id]; + $title = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], + self::$md_escape_table['_']), $title); + $result .= " title=\"$title\""; + } + $result .= ">$link_text</a>"; + } + else { + $result = $whole_match; + } + return $result; + } + private function _DoAnchors_inline_callback($matches) { + $whole_match = $matches[1]; + $link_text = $matches[2]; + $url = $matches[3]; + $title =& $matches[6]; + + # We've got to encode these to avoid conflicting with italics/bold. + $url = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $url); + $result = "<a href=\"$url\""; + if (isset($title)) { + $title = str_replace('"', '"', $title); + $title = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $title); + $result .= " title=\"$title\""; + } + + $result .= ">$link_text</a>"; + + return $result; + } + + + private function _DoImages($text) { + # + # Turn Markdown image shortcuts into <img> tags. + # + # + # First, handle reference-style labeled images: ![alt text][id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.self::$md_nested_brackets.') # alt text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + + ) + }xs', + array($this,'_DoImages_reference_callback'), $text); + + # + # Next, handle inline images: ![alt text](url "optional title") + # Don't forget: encode * and _ + + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.self::$md_nested_brackets.') # alt text = $2 + \] + \( # literal paren + [ \t]* + <?(\S+?)>? # src url = $3 + [ \t]* + ( # $4 + ([\'"]) # quote char = $5 + (.*?) # title = $6 + \5 # matching quote + [ \t]* + )? # title is optional + \) + ) + }xs', + array($this,'_DoImages_inline_callback'), $text); + + return $text; + } + private function _DoImages_reference_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $link_id = strtolower($matches[3]); + + if ($link_id == "") { + $link_id = strtolower($alt_text); # for shortcut links like ![this][]. + } + + $alt_text = str_replace('"', '"', $alt_text); + if (isset($this->md_urls[$link_id])) { + $url = $this->md_urls[$link_id]; + # We've got to encode these to avoid conflicting with italics/bold. + $url = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $url); + $result = "<img src=\"$url\" alt=\"$alt_text\""; + if (isset($this->md_titles[$link_id])) { + $title = $this->md_titles[$link_id]; + $title = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], + self::$md_escape_table['_']), $title); + $result .= " title=\"$title\""; + } + $result .= $this->md_empty_element_suffix; + } + else { + # If there's no such link ID, leave intact: + $result = $whole_match; + } + + return $result; + } + private function _DoImages_inline_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $url = $matches[3]; + $title = ''; + if (isset($matches[6])) { + $title = $matches[6]; + } + + $alt_text = str_replace('"', '"', $alt_text); + $title = str_replace('"', '"', $title); + # We've got to encode these to avoid conflicting with italics/bold. + $url = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $url); + $result = "<img src=\"$url\" alt=\"$alt_text\""; + if (isset($title)) { + $title = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $title); + $result .= " title=\"$title\""; # $title already quoted + } + $result .= $this->md_empty_element_suffix; + + return $result; + } + + + private function _DoHeaders($text) { + # Setext-style headers: + # Header 1 + # ======== + # + # Header 2 + # -------- + # + $text = preg_replace( + array('{ ^(.+)[ \t]*\n=+[ \t]*\n+ }emx', + '{ ^(.+)[ \t]*\n-+[ \t]*\n+ }emx'), + array("'<h1>'.\$this->_RunSpanGamut(\$this->_UnslashQuotes('\\1')).'</h1>\n\n'", + "'<h2>'.\$this->_RunSpanGamut(\$this->_UnslashQuotes('\\1')).'</h2>\n\n'"), + $text); + + # atx-style headers: + # # Header 1 + # ## Header 2 + # ## Header 2 with closing hashes ## + # ... + # ###### Header 6 + # + $text = preg_replace("{ + ^(\\#{1,6}) # $1 = string of #'s + [ \\t]* + (.+?) # $2 = Header text + [ \\t]* + \\#* # optional closing #'s (not counted) + \\n+ + }xme", + "'<h'.strlen('\\1').'>'.\$this->_RunSpanGamut(\$this->_UnslashQuotes('\\2')).'</h'.strlen('\\1').'>\n\n'", + $text); + + return $text; + } + + + private function _DoLists($text) { + # + # Form HTML ordered (numbered) and unordered (bulleted) lists. + # + $less_than_tab = $this->md_tab_width - 1; + + # Re-usable patterns to match list item bullets and number markers: + $marker_ul = '[*+-]'; + $marker_ol = '\d+[.]'; + $marker_any = "(?:$marker_ul|$marker_ol)"; + + $markers = array($marker_ul, $marker_ol); + + foreach ($markers as $marker) { + # Re-usable pattern to match any entirel ul or ol list: + $whole_list = ' + ( # $1 = whole list + ( # $2 + [ ]{0,'.$less_than_tab.'} + ('.$marker.') # $3 = first list item marker + [ \t]+ + ) + (?s:.+?) + ( # $4 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another list item marker + [ \t]* + '.$marker.'[ \t]+ + ) + ) + ) + '; // mx + + # We use a different prefix before nested lists than top-level lists. + # See extended comment in _ProcessListItems(). + + if ($this->md_list_level) { + $text = preg_replace_callback('{ + ^ + '.$whole_list.' + }mx', + array($this,'_DoLists_callback_top'), $text); + } + else { + $text = preg_replace_callback('{ + (?:(?<=\n\n)|\A\n?) + '.$whole_list.' + }mx', + array($this,'_DoLists_callback_nested'), $text); + } + } + + return $text; + } + private function _DoLists_callback_top($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul = '[*+-]'; + $marker_ol = '\d+[.]'; + $marker_any = "(?:$marker_ul|$marker_ol)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul/", $matches[3]) ? "ul" : "ol"; + + $marker_any = ( $list_type == "ul" ? $marker_ul : $marker_ol ); + + # Turn double returns into triple returns, so that we can make a + # paragraph for the last item in a list, if necessary: + $list = preg_replace("/\n{2,}/", "\n\n\n", $list); + $result = $this->_ProcessListItems($list, $marker_any); + + # Trim any trailing whitespace, to put the closing `</$list_type>` + # up on the preceding line, to get it past the current stupid + # HTML block parser. This is a hack to work around the terrible + # hack that is the HTML block parser. + $result = rtrim($result); + $result = "<$list_type>" . $result . "</$list_type>\n"; + return $result; + } + private function _DoLists_callback_nested($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul = '[*+-]'; + $marker_ol = '\d+[.]'; + $marker_any = "(?:$marker_ul|$marker_ol)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul/", $matches[3]) ? "ul" : "ol"; + + $marker_any = ( $list_type == "ul" ? $marker_ul : $marker_ol ); + + # Turn double returns into triple returns, so that we can make a + # paragraph for the last item in a list, if necessary: + $list = preg_replace("/\n{2,}/", "\n\n\n", $list); + $result = $this->_ProcessListItems($list, $marker_any); + $result = "<$list_type>\n" . $result . "</$list_type>\n"; + return $result; + } + + + private function _ProcessListItems($list_str, $marker_any) { + # + # Process the contents of a single ordered or unordered list, splitting it + # into individual list items. + # + + # The $md_list_level keeps track of when we're inside a list. + # Each time we enter a list, we increment it; when we leave a list, + # we decrement. If it's zero, we're not in a list anymore. + # + # We do this because when we're not inside a list, we want to treat + # something like this: + # + # I recommend upgrading to version + # 8. Oops, now this line is treated + # as a sub-list. + # + # As a single paragraph, despite the fact that the second line starts + # with a digit-period-space sequence. + # + # Whereas when we're inside a list (or sub-list), that line will be + # treated as the start of a sub-list. What a kludge, huh? This is + # an aspect of Markdown's syntax that's hard to parse perfectly + # without resorting to mind-reading. Perhaps the solution is to + # change the syntax rules such that sub-lists must start with a + # starting cardinal number; e.g. "1." or "a.". + + $this->md_list_level++; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + $list_str = preg_replace_callback('{ + (\n)? # leading line = $1 + (^[ \t]*) # leading whitespace = $2 + ('.$marker_any.') [ \t]+ # list marker = $3 + ((?s:.+?) # list item text = $4 + (\n{1,2})) + (?= \n* (\z | \2 ('.$marker_any.') [ \t]+)) + }xm', + array($this,'_ProcessListItems_callback'), $list_str); + + $this->md_list_level--; + return $list_str; + } + private function _ProcessListItems_callback($matches) { + $item = $matches[4]; + $leading_line =& $matches[1]; + $leading_space =& $matches[2]; + + if ($leading_line || preg_match('/\n{2,}/', $item)) { + $item = $this->_RunBlockGamut($this->_Outdent($item)); + } + else { + # Recursion for sub-lists: + $item = $this->_DoLists($this->_Outdent($item)); + $item = preg_replace('/\n+$/', '', $item); + $item = $this->_RunSpanGamut($item); + } + + return "<li>" . $item . "</li>\n"; + } + + + private function _DoCodeBlocks($text) { + # + # Process Markdown `<pre><code>` blocks. + # + $text = preg_replace_callback('{ + (?:\n\n|\A) + ( # $1 = the code block -- one or more lines, starting with a space/tab + (?: + (?:[ ]{'.$this->md_tab_width.'} | \t) # Lines must start with a tab or a tab-width of spaces + .*\n+ + )+ + ) + ((?=^[ ]{0,'.$this->md_tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc + }xm', + array($this,'_DoCodeBlocks_callback'), $text); + + return $text; + } + private function _DoCodeBlocks_callback($matches) { + $codeblock = $matches[1]; + + $codeblock = $this->_EncodeCode($this->_Outdent($codeblock)); + // $codeblock = _Detab($codeblock); + # trim leading newlines and trailing whitespace + $codeblock = preg_replace(array('/\A\n+/', '/\s+\z/'), '', $codeblock); + + $result = "\n\n<pre><code>" . $codeblock . "\n</code></pre>\n\n"; + + return $result; + } + + + private function _DoCodeSpans($text) { + # + # * Backtick quotes are used for <code></code> spans. + # + # * You can use multiple backticks as the delimiters if you want to + # include literal backticks in the code span. So, this input: + # + # Just type ``foo `bar` baz`` at the prompt. + # + # Will translate to: + # + # <p>Just type <code>foo `bar` baz</code> at the prompt.</p> + # + # There's no arbitrary limit to the number of backticks you + # can use as delimters. If you need three consecutive backticks + # in your code, use four for delimiters, etc. + # + # * You can use spaces to get literal backticks at the edges: + # + # ... type `` `bar` `` ... + # + # Turns to: + # + # ... type <code>`bar`</code> ... + # + $text = preg_replace_callback('@ + (?<!\\\) # Character before opening ` can\'t be a backslash + (`+) # $1 = Opening run of ` + (.+?) # $2 = The code block + (?<!`) + \1 # Matching closer + (?!`) + @xs', + array($this,'_DoCodeSpans_callback'), $text); + + return $text; + } + private function _DoCodeSpans_callback($matches) { + $c = $matches[2]; + $c = preg_replace('/^[ \t]*/', '', $c); # leading whitespace + $c = preg_replace('/[ \t]*$/', '', $c); # trailing whitespace + $c = $this->_EncodeCode($c); + return "<code>$c</code>"; + } + + + private function _EncodeCode($_) { + # + # Encode/escape certain characters inside Markdown code runs. + # The point is that in code, these characters are literals, + # and lose their special Markdown meanings. + # + # Encode all ampersands; HTML entities are not + # entities within a Markdown code span. + $_ = str_replace('&', '&', $_); + + # Do the angle bracket song and dance: + $_ = str_replace(array('<', '>'), + array('<', '>'), $_); + + # Now, escape characters that are magic in Markdown: + $_ = str_replace(array_keys(self::$md_escape_table), + array_values(self::$md_escape_table), $_); + + return $_; + } + + + private function _DoItalicsAndBold($text) { + # <strong> must go first: + $text = preg_replace('{ + ( # $1: Marker + (?<!\*\*) \*\* | # (not preceded by two chars of + (?<!__) __ # the same marker) + ) + (?=\S) # Not followed by whitespace + (?!\1) # or two others marker chars. + ( # $2: Content + (?: + [^*_]+? # Anthing not em markers. + | + # Balence any regular emphasis inside. + ([*_]) (?=\S) .+? (?<=\S) \3 # $3: em char (* or _) + | + (?! \1 ) . # Allow unbalenced * and _. + )+? + ) + (?<=\S) \1 # End mark not preceded by whitespace. + }sx', + '<strong>\2</strong>', $text); + # Then <em>: + $text = preg_replace( + '{ ( (?<!\*)\* | (?<!_)_ ) (?=\S) (?! \1) (.+?) (?<=\S) \1 }sx', + '<em>\2</em>', $text); + + return $text; + } + + + private function _DoBlockQuotes($text) { + $text = preg_replace_callback('/ + ( # Wrap whole match in $1 + ( + ^[ \t]*>[ \t]? # ">" at the start of a line + .+\n # rest of the first line + (.+\n)* # subsequent consecutive lines + \n* # blanks + )+ + ) + /xm', + array($this,'_DoBlockQuotes_callback'), $text); + + return $text; + } + private function _DoBlockQuotes_callback($matches) { + $bq = $matches[1]; + # trim one level of quoting - trim whitespace-only lines + $bq = preg_replace(array('/^[ \t]*>[ \t]?/m', '/^[ \t]+$/m'), '', $bq); + $bq = $this->_RunBlockGamut($bq); # recurse + + $bq = preg_replace('/^/m', " ", $bq); + # These leading spaces screw with <pre> content, so we need to fix that: + $bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx', + array($this,'_DoBlockQuotes_callback2'), $bq); + + return "<blockquote>\n$bq\n</blockquote>\n\n"; + } + private function _DoBlockQuotes_callback2($matches) { + $pre = $matches[1]; + $pre = preg_replace('/^ /m', '', $pre); + return $pre; + } + + + private function _FormParagraphs($text) { + # + # Params: + # $text - string to process with html <p> tags + # + # Strip leading and trailing lines: + $text = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap <p> tags. + # + foreach ($grafs as $key => $value) { + if (!isset( $this->md_html_blocks[$value] )) { + $value = $this->_RunSpanGamut($value); + $value = preg_replace('/^([ \t]*)/', '<p>', $value); + $value .= "</p>"; + $grafs[$key] = $value; + } + } + + # + # Unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + if (isset( $this->md_html_blocks[$value] )) { + $grafs[$key] = $this->md_html_blocks[$value]; + } + } + + return implode("\n\n", $grafs); + } + + + private function _EncodeAmpsAndAngles($text) { + # Smart processing for ampersands and angle brackets that need to be encoded. + + # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: + # http://bumppo.net/projects/amputator/ + $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', + '&', $text);; + + # Encode naked <'s + $text = preg_replace('{<(?![a-z/?\$!])}i', '<', $text); + + return $text; + } + + + private function _EncodeBackslashEscapes($text) { + # + # Parameter: String. + # Returns: The string, with after processing the following backslash + # escape sequences. + # + # Must process escaped backslashes first. + return str_replace(array_keys(self::$md_backslash_escape_table), + array_values(self::$md_backslash_escape_table), $text); + } + + + private function _DoAutoLinks($text) { + $text = preg_replace("!<((https?|ftp):[^'\">\\s]+)>!", + '<a href="\1">\1</a>', $text); + + # Email addresses: <address@domain.foo> + $text = preg_replace('{ + < + (?:mailto:)? + ( + [-.\w]+ + \@ + [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ + ) + > + }exi', + "\$this->_EncodeEmailAddress(\$this->_UnescapeSpecialChars(\$this->_UnslashQuotes('\\1')))", + $text); + + return $text; + } + + + private function _EncodeEmailAddress($addr) { + # + # Input: an email address, e.g. "foo@example.com" + # + # Output: the email address as a mailto link, with each character + # of the address encoded as either a decimal or hex entity, in + # the hopes of foiling most address harvesting spam bots. E.g.: + # + # <a href="mailto:foo@e + # xample.com">foo + # @example.com</a> + # + # Based by a filter by Matthew Wickline, posted to the BBEdit-Talk + # mailing list: <http://tinyurl.com/yu7ue> + # + $addr = "mailto:" . $addr; + $length = strlen($addr); + + # leave ':' alone (to spot mailto: later) + $addr = preg_replace_callback('/([^\:])/', + array($this,'_EncodeEmailAddress_callback'), $addr); + + $addr = "<a href=\"$addr\">$addr</a>"; + # strip the mailto: from the visible part + $addr = preg_replace('/">.+?:/', '">', $addr); + + return $addr; + } + private function _EncodeEmailAddress_callback($matches) { + $char = $matches[1]; + $r = rand(0, 100); + # roughly 10% raw, 45% hex, 45% dec + # '@' *must* be encoded. I insist. + if ($r > 90 && $char != '@') return $char; + if ($r < 45) return '&#x'.dechex(ord($char)).';'; + return '&#'.ord($char).';'; + } + + + private function _UnescapeSpecialChars($text) { + # + # Swap back in all the special characters we've hidden. + # + return str_replace(array_values(self::$md_escape_table), + array_keys(self::$md_escape_table), $text); + } + + + # _TokenizeHTML is shared between PHP Markdown and PHP SmartyPants. + # We only define it if it is not already defined. + + private function _TokenizeHTML($str) { + # + # Parameter: String containing HTML markup. + # Returns: An array of the tokens comprising the input + # string. Each token is either a tag (possibly with nested, + # tags contained therein, such as <a href="<MTFoo>">, or a + # run of text between tags. Each element of the array is a + # two-element array; the first is either 'tag' or 'text'; + # the second is the actual value. + # + # + # Regular expression derived from the _tokenize() subroutine in + # Brad Choate's MTRegex plugin. + # <http://www.bradchoate.com/past/mtregex.php> + # + $index = 0; + $tokens = array(); + + $match = '(?s:<!(?:--.*?--\s*)+>)|'. # comment + '(?s:<\?.*?\?>)|'. # processing instruction + # regular tags + '(?:<[/!$]?[-a-zA-Z0-9:]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)'; + + $parts = preg_split("{($match)}", $str, -1, PREG_SPLIT_DELIM_CAPTURE); + + foreach ($parts as $part) { + if (++$index % 2 && $part != '') + $tokens[] = array('text', $part); + else + $tokens[] = array('tag', $part); + } + + return $tokens; + } + + private function _Outdent($text) { + # + # Remove one level of line-leading tabs or spaces + # + return preg_replace("/^(\\t|[ ]{1,".$this->md_tab_width."})/m", "", $text); + } + + + private function _Detab($text) { + # + # Replace tabs with the appropriate amount of space. + # + # For each line we separate the line in blocks delemited by + # tab characters. Then we reconstruct every line by adding the + # appropriate number of space between each blocks. + + $lines = explode("\n", $text); + $text = ""; + + foreach ($lines as $line) { + # Split in blocks. + $blocks = explode("\t", $line); + # Add each blocks to the line. + $line = $blocks[0]; + unset($blocks[0]); # Do not add first block twice. + foreach ($blocks as $block) { + # Calculate amount of space, insert spaces, insert block. + $amount = $this->md_tab_width - strlen($line) % $this->md_tab_width; + $line .= str_repeat(" ", $amount) . $block; + } + $text .= "$line\n"; + } + return $text; + } + + + private function _UnslashQuotes($text) { + # + # This function is useful to remove automaticaly slashed double quotes + # when using preg_replace and evaluating an expression. + # Parameter: String. + # Returns: The string with any slash-double-quote (\") sequence replaced + # by a single double quote. + # + return str_replace('\"', '"', $text); + } +} + +/* + +PHP Markdown +============ + +Description +----------- + +This is a PHP translation of the original Markdown formatter written in +Perl by John Gruber. + +Markdown is a text-to-HTML filter; it translates an easy-to-read / +easy-to-write structured text format into HTML. Markdown's text format +is most similar to that of plain text email, and supports features such +as headers, *emphasis*, code blocks, blockquotes, and links. + +Markdown's syntax is designed not as a generic markup language, but +specifically to serve as a front-end to (X)HTML. You can use span-level +HTML tags anywhere in a Markdown document, and you can use block level +HTML tags (like <div> and <table> as well). + +For more information about Markdown's syntax, see: + +<http://daringfireball.net/projects/markdown/> + + +Bugs +---- + +To file bug reports please send email to: + +<michel.fortin@michelf.com> + +Please include with your report: (1) the example input; (2) the output you +expected; (3) the output Markdown actually produced. + + +Version History +--------------- + +See the readme file for detailed release notes for this version. + +1.0.1c - 9 Dec 2005 + +1.0.1b - 6 Jun 2005 + +1.0.1a - 15 Apr 2005 + +1.0.1 - 16 Dec 2004 + +1.0 - 21 Aug 2004 + + +Author & Contributors +--------------------- + +Original Perl version by John Gruber +<http://daringfireball.net/> + +PHP port and other contributions by Michel Fortin +<http://www.michelf.com/> + + +Copyright and License +--------------------- + +Copyright (c) 2004-2005 Michel Fortin +<http://www.michelf.com/> +All rights reserved. + +Copyright (c) 2003-2004 John Gruber +<http://daringfireball.net/> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. + +*/ +?> diff --git a/framework/3rdParty/readme.html b/framework/3rdParty/readme.html index f67526ae..590f1174 100644 --- a/framework/3rdParty/readme.html +++ b/framework/3rdParty/readme.html @@ -133,7 +133,15 @@ projects. <td>Slightly modified to work nicely with E_STRICT in php 5.</td>
</tr>
+<tr>
+ <td><a href="Markdown">Markdown</a></td>
+ <td><a href="http://www.michelf.com/projects/php-markdown/">PHP Markdown</a></td>
+ <td><a href="Markdown/License.text">BSD</a></td>
+ <td>System.Web.UI.WebControls.TMarkdown</td>
+ <td>PHP5 class implementation of the PHP Markdown.</td>
+</tr>
+
</table>
</p>
</body>
-</html>
\ No newline at end of file +</html>
diff --git a/framework/I18N/TTranslate.php b/framework/I18N/TTranslate.php index 6a30f3a6..da5876d4 100644 --- a/framework/I18N/TTranslate.php +++ b/framework/I18N/TTranslate.php @@ -28,19 +28,19 @@ Prado::using('System.I18N.TI18NControl'); * Depending on the culture set on the page, the phrase "Goodbye" will
* be translated.
*
- * The values of any attribute in TTranslate are consider as values for
- * substitution. Strings enclosed with "{" and "}" are consider as the
- * parameters. The following example will substitution the string
- * "{time}" with the value of the attribute "time="#time()". Note that
- * the value of the attribute time is evaluated.
+ * The {@link getParameters Parameters} property can be use to add name values pairs for
+ * substitution. Substrings enclosed with "{" and "}" in the translation message are consider as the
+ * parameter names during substitution lookup. The following example will substitute the substring
+ * "{time}" with the value of the parameter attribute "Parameters.time=<%= time() %>. Note that
+ * the value of the parameter named "time" is evaluated.
* <code>
- * <com:TTranslate time="#time()">
+ * <com:TTranslate Parameters.time=<%= time() %> >
* The unix-time is "{time}".
* </com:TTranslate>
* </code>
*
* More complex string substitution can be applied using the
- * TParam component.
+ * TTranslateParameter component.
*
* Namespace: System.I18N
*
@@ -134,6 +134,68 @@ class TTranslate extends TI18NControl }
/**
+ * Returns the list of custom parameters.
+ * Custom parameters are name-value pairs that may subsititute translation
+ * place holders during rendering.
+ * @return TAttributeCollection the list of custom parameters
+ */
+ public function getParameters()
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters;
+ else
+ {
+ $parameters=new TAttributeCollection;
+ $this->setViewState('Parameters',$parameters,null);
+ return $parameters;
+ }
+ }
+
+ /**
+ * @return boolean whether the named parameter exists
+ */
+ public function hasParameter($name)
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters->contains($name);
+ else
+ return false;
+ }
+
+ /**
+ * @return string parameter value, null if parameter does not exist
+ */
+ public function getParameter($name)
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters->itemAt($name);
+ else
+ return null;
+ }
+
+ /**
+ * @param string parameter name
+ * @param string value of the parameter
+ */
+ public function setParameter($name,$value)
+ {
+ $this->getParameters()->add($name,$value);
+ }
+
+ /**
+ * Removes the named parameter.
+ * @param string the name of the parameter to be removed.
+ * @return string parameter value removed, null if parameter does not exist.
+ */
+ public function removeParameter($name)
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters->remove($name);
+ else
+ return null;
+ }
+
+ /**
* renders the translated string.
*/
public function render($writer)
@@ -141,6 +203,8 @@ class TTranslate extends TI18NControl $textWriter=new TTextWriter;
$htmlWriter=new THtmlWriter($textWriter);
$subs = array();
+ foreach($this->getParameters() as $key => $value)
+ $subs['{'.$key.'}'] = $value;
foreach($this->getControls() as $control)
{
if($control instanceof TTranslateParameter)
diff --git a/framework/Web/Javascripts/datepicker/datepicker.js b/framework/Web/Javascripts/datepicker/datepicker.js index d5d9496c..e704e950 100644 --- a/framework/Web/Javascripts/datepicker/datepicker.js +++ b/framework/Web/Javascripts/datepicker/datepicker.js @@ -282,8 +282,11 @@ Prado.WebUI.TDatePicker.prototype = Event.observe(this._calDiv, "mousewheel", this.mouseWheelChange.bindEvent(this));
Event.observe(calendarBody, "click", this.selectDate.bindEvent(this));
+
+ Event.observe(this.control, "blur", this.hide.bind(this));
+ Prado.Element.focus(this.control);
- },
+ },A
ieHack : function(cleanup)
{
@@ -305,7 +308,7 @@ Prado.WebUI.TDatePicker.prototype = if (!ev) ev = document.parentWindow.event;
var kc = ev.keyCode != null ? ev.keyCode : ev.charCode;
- if(kc == Event.KEY_RETURN)
+ if(kc == Event.KEY_RETURN || kc == Event.KEY_SPACEBAR)
{
this.setSelectedDate(this.selectedDate);
Event.stop(ev);
@@ -526,6 +529,22 @@ Prado.WebUI.TDatePicker.prototype = this.setMonth(this.selectedDate.getMonth()-1);
},
+ getDatePickerOffsetHeight : function()
+ {
+ if(this.options.InputMode == "TextBox")
+ return this.control.offsetHeight;
+
+ var control = Prado.WebUI.TDatePicker.getDayListControl(this.control);
+ if(control) return control.offsetHeight;
+
+ var control = Prado.WebUI.TDatePicker.getMonthListControl(this.control);
+ if(control) return control.offsetHeight;
+
+ var control = Prado.WebUI.TDatePicker.getYearListControl(this.control);
+ if(control) return control.offsetHeight;
+ return 0;
+ },
+
show : function()
{
this.create();
@@ -534,15 +553,8 @@ Prado.WebUI.TDatePicker.prototype = {
var pos = Position.cumulativeOffset(this.control);
- if(this.options.InputMode == "TextBox")
- pos[1] += this.control.offsetHeight;
- else
- {
- var dayList = Prado.WebUI.TDatePicker.getDayListControl(this.control);
- if(dayList)
- pos[1] += dayList.offsetHeight-1;
- }
-
+ pos[1] += this.getDatePickerOffsetHeight();
+
this._calDiv.style.display = "block";
this._calDiv.style.top = (pos[1]-1) + "px";
this._calDiv.style.left = pos[0] + "px";
diff --git a/framework/Web/Javascripts/js/datepicker.js b/framework/Web/Javascripts/js/datepicker.js index a81bbeb4..e0bba502 100644 --- a/framework/Web/Javascripts/js/datepicker.js +++ b/framework/Web/Javascripts/js/datepicker.js @@ -23,10 +23,10 @@ div=document.createElement("div");div.className="calendarFooter";this._calDiv.ap this.iePopUp.scrolling="no" this.iePopUp.frameBorder="0" document.body.appendChild(this.iePopUp);} -document.body.appendChild(this._calDiv);this.update();this.updateHeader();this.ieHack(true);previousMonth.hideFocus=true;nextMonth.hideFocus=true;todayButton.hideFocus=true;Event.observe(previousMonth,"click",this.prevMonth.bindEvent(this));Event.observe(nextMonth,"click",this.nextMonth.bindEvent(this));Event.observe(todayButton,"click",this.selectToday.bindEvent(this));Event.observe(this._monthSelect,"change",this.monthSelect.bindEvent(this));Event.observe(this._yearSelect,"change",this.yearSelect.bindEvent(this));Event.observe(this._calDiv,"mousewheel",this.mouseWheelChange.bindEvent(this));Event.observe(calendarBody,"click",this.selectDate.bindEvent(this));},ieHack:function(cleanup) +document.body.appendChild(this._calDiv);this.update();this.updateHeader();this.ieHack(true);previousMonth.hideFocus=true;nextMonth.hideFocus=true;todayButton.hideFocus=true;Event.observe(previousMonth,"click",this.prevMonth.bindEvent(this));Event.observe(nextMonth,"click",this.nextMonth.bindEvent(this));Event.observe(todayButton,"click",this.selectToday.bindEvent(this));Event.observe(this._monthSelect,"change",this.monthSelect.bindEvent(this));Event.observe(this._yearSelect,"change",this.yearSelect.bindEvent(this));Event.observe(this._calDiv,"mousewheel",this.mouseWheelChange.bindEvent(this));Event.observe(calendarBody,"click",this.selectDate.bindEvent(this));Event.observe(this.control,"blur",this.hide.bind(this));Prado.Element.focus(this.control);},ieHack:function(cleanup) {if(this.iePopUp) {this.iePopUp.style.display="block";this.iePopUp.style.top=(this._calDiv.offsetTop-1)+"px";this.iePopUp.style.left=(this._calDiv.offsetLeft-1)+"px";this.iePopUp.style.width=Math.abs(this._calDiv.offsetWidth-2)+"px";this.iePopUp.style.height=(this._calDiv.offsetHeight+1)+"px";if(cleanup)this.iePopUp.style.display="none";}},keyPressed:function(ev) -{if(!this.showing)return;if(!ev)ev=document.parentWindow.event;var kc=ev.keyCode!=null?ev.keyCode:ev.charCode;if(kc==Event.KEY_RETURN) +{if(!this.showing)return;if(!ev)ev=document.parentWindow.event;var kc=ev.keyCode!=null?ev.keyCode:ev.charCode;if(kc==Event.KEY_RETURN||kc==Event.KEY_SPACEBAR) {this.setSelectedDate(this.selectedDate);Event.stop(ev);this.hide();} if(kc==Event.KEY_ESC) {Event.stop(ev);this.hide();} @@ -88,13 +88,11 @@ this.onChange();},getElement:function() {var d=this.newDate(this.selectedDate);d.setFullYear(year);this.setSelectedDate(d);},setMonth:function(month) {var d=this.newDate(this.selectedDate);d.setMonth(month);this.setSelectedDate(d);},nextMonth:function() {this.setMonth(this.selectedDate.getMonth()+1);},prevMonth:function() -{this.setMonth(this.selectedDate.getMonth()-1);},show:function() +{this.setMonth(this.selectedDate.getMonth()-1);},getDatePickerOffsetHeight:function() +{if(this.options.InputMode=="TextBox") +return this.control.offsetHeight;var control=Prado.WebUI.TDatePicker.getDayListControl(this.control);if(control)return control.offsetHeight;var control=Prado.WebUI.TDatePicker.getMonthListControl(this.control);if(control)return control.offsetHeight;var control=Prado.WebUI.TDatePicker.getYearListControl(this.control);if(control)return control.offsetHeight;return 0;},show:function() {this.create();if(!this.showing) -{var pos=Position.cumulativeOffset(this.control);if(this.options.InputMode=="TextBox") -pos[1]+=this.control.offsetHeight;else -{var dayList=Prado.WebUI.TDatePicker.getDayListControl(this.control);if(dayList) -pos[1]+=dayList.offsetHeight-1;} -this._calDiv.style.display="block";this._calDiv.style.top=(pos[1]-1)+"px";this._calDiv.style.left=pos[0]+"px";this.ieHack(false);this.documentClickEvent=this.hideOnClick.bindEvent(this);this.documentKeyDownEvent=this.keyPressed.bindEvent(this);Event.observe(document.body,"click",this.documentClickEvent);var date=this.getDateFromInput();if(date) +{var pos=Position.cumulativeOffset(this.control);pos[1]+=this.getDatePickerOffsetHeight();this._calDiv.style.display="block";this._calDiv.style.top=(pos[1]-1)+"px";this._calDiv.style.left=pos[0]+"px";this.ieHack(false);this.documentClickEvent=this.hideOnClick.bindEvent(this);this.documentKeyDownEvent=this.keyPressed.bindEvent(this);Event.observe(document.body,"click",this.documentClickEvent);var date=this.getDateFromInput();if(date) {this.selectedDate=date;this.setSelectedDate(date);} Event.observe(document,"keydown",this.documentKeyDownEvent);this.showing=true;}},getDateFromInput:function() {if(this.options.InputMode=="TextBox") diff --git a/framework/Web/Javascripts/js/prado.js b/framework/Web/Javascripts/js/prado.js index aa02e98e..87fd69a6 100644 --- a/framework/Web/Javascripts/js/prado.js +++ b/framework/Web/Javascripts/js/prado.js @@ -111,7 +111,7 @@ this.registerFormCallbacks();else this.registerCallback(this.element);},onElementEvent:function(){var value=this.getValue();if(this.lastValue!=value){this.callback(this.element,value);this.lastValue=value;}},registerFormCallbacks:function(){var elements=Form.getElements(this.element);for(var i=0;i<elements.length;i++) this.registerCallback(elements[i]);},registerCallback:function(element){if(element.type){switch(element.type.toLowerCase()){case'checkbox':case'radio':Event.observe(element,'click',this.onElementEvent.bind(this));break;case'password':case'text':case'textarea':case'select-one':case'select-multiple':Event.observe(element,'change',this.onElementEvent.bind(this));break;}}}} Form.Element.EventObserver=Class.create();Form.Element.EventObserver.prototype=Object.extend(new Abstract.EventObserver(),{getValue:function(){return Form.Element.getValue(this.element);}});Form.EventObserver=Class.create();Form.EventObserver.prototype=Object.extend(new Abstract.EventObserver(),{getValue:function(){return Form.serialize(this.element);}});if(!window.Event){var Event=new Object();} -Object.extend(Event,{KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},pointerX:function(event){return event.pageX||(event.clientX+ +Object.extend(Event,{KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,KEY_SPACEBAR:32,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},pointerX:function(event){return event.pageX||(event.clientX+ (document.documentElement.scrollLeft||document.body.scrollLeft));},pointerY:function(event){return event.pageY||(event.clientY+ (document.documentElement.scrollTop||document.body.scrollTop));},stop:function(event){if(event.preventDefault){event.preventDefault();event.stopPropagation();}else{event.returnValue=false;event.cancelBubble=true;}},findElement:function(event,tagName){var element=Event.element(event);while(element.parentNode&&(!element.tagName||(element.tagName.toUpperCase()!=tagName.toUpperCase()))) element=element.parentNode;return element;},observers:false,_observeAndCache:function(element,name,observer,useCapture){if(!this.observers)this.observers=[];if(element.addEventListener){this.observers.push([element,name,observer,useCapture]);element.addEventListener(name,observer,useCapture);}else if(element.attachEvent){this.observers.push([element,name,observer,useCapture]);element.attachEvent('on'+name,observer);}},unloadCache:function(){if(!Event.observers)return;for(var i=0;i<Event.observers.length;i++){Event.stopObserving.apply(this,Event.observers[i]);Event.observers[i][0]=null;} diff --git a/framework/Web/Javascripts/prototype/event.js b/framework/Web/Javascripts/prototype/event.js index 51b9010e..4def0e4a 100644 --- a/framework/Web/Javascripts/prototype/event.js +++ b/framework/Web/Javascripts/prototype/event.js @@ -12,6 +12,7 @@ Object.extend(Event, { KEY_RIGHT: 39, KEY_DOWN: 40, KEY_DELETE: 46, + KEY_SPACEBAR: 32, element: function(event) { return event.target || event.srcElement; diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php index 60ce11ca..cb05fa35 100644 --- a/framework/Web/UI/TTemplateManager.php +++ b/framework/Web/UI/TTemplateManager.php @@ -383,16 +383,37 @@ class TTemplate extends TApplicationComponent implements ITemplate * @param string property name
* @param mixed property initial value
*/
+ protected function configureControl2($control,$name,$value)
+ {
+ if(strncasecmp($name,'on',2)===0) // is an event
+ $this->configureEvent($control,$name,$value,$control);
+ else if(($pos=strrpos($name,'.'))===false) // is a simple property or custom attribute
+ $this->configureProperty($control,$name,$value);
+ else // is a subproperty
+ {
+ $subName=substr($name,$pos+1);
+ if(strncasecmp($subName,'on',2)===0) // is an event: XXX.YYY.OnZZZ
+ {
+ $object=$control->getSubProperty(substr($name,0,$pos));
+ if(($object instanceof TControl))
+ $this->configureEvent($object,$subName,$value,$control);
+ else
+ $this->configureSubProperty($control,$name,$value);
+ }
+ else
+ $this->configureSubProperty($control,$name,$value);
+ }
+ }
+
protected function configureControl($control,$name,$value)
{
if(strncasecmp($name,'on',2)===0) // is an event
- $this->configureEvent($control,$name,$value);
- else if(strpos($name,'.')===false) // is a simple property or custom attribute
+ $this->configureEvent($control,$name,$value,$control);
+ else if(($pos=strrpos($name,'.'))===false) // is a simple property or custom attribute
$this->configureProperty($control,$name,$value);
else // is a subproperty
$this->configureSubProperty($control,$name,$value);
}
-
/**
* Configures a property of a non-control component.
* @param TComponent component to be configured
@@ -412,13 +433,14 @@ class TTemplate extends TApplicationComponent implements ITemplate * @param TControl control to be configured
* @param string event name
* @param string event handler
+ * @param TControl context control
*/
- protected function configureEvent($component,$name,$value)
+ protected function configureEvent($control,$name,$value,$contextControl)
{
if(strpos($value,'.')===false)
- $component->attachEventHandler($name,array($component,'TemplateControl.'.$value));
+ $control->attachEventHandler($name,array($contextControl,'TemplateControl.'.$value));
else
- $component->attachEventHandler($name,array($component,$value));
+ $control->attachEventHandler($name,array($contextControl,$value));
}
/**
diff --git a/framework/Web/UI/WebControls/TDataBoundControl.php b/framework/Web/UI/WebControls/TDataBoundControl.php index 0bb771bf..9e6ecbf3 100644 --- a/framework/Web/UI/WebControls/TDataBoundControl.php +++ b/framework/Web/UI/WebControls/TDataBoundControl.php @@ -59,7 +59,7 @@ abstract class TDataBoundControl extends TWebControl */
public function setDataSource($value)
{
- $this->_dataSource=$this->validateDataSource($value);;
+ $this->_dataSource=$this->validateDataSource($value);
$this->onDataSourceChanged();
}
diff --git a/framework/Web/UI/WebControls/TMarkdown.php b/framework/Web/UI/WebControls/TMarkdown.php new file mode 100644 index 00000000..49660b4e --- /dev/null +++ b/framework/Web/UI/WebControls/TMarkdown.php @@ -0,0 +1,113 @@ +<?php
+/**
+ * TMarkdown class file
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TMarkdown class
+ *
+ * TMarkdown is a control that produces HTML from code with markdown syntax.
+ *
+ * Markdown is a text-to-HTML conversion tool for web writers. Markdown allows
+ * you to write using an easy-to-read, easy-to-write plain text format, then
+ * convert it to structurally valid XHTML (or HTML).
+ * Further documentation regarding Markdown can be found at
+ * http://daringfireball.net/projects/markdown/
+ *
+ * To use TMarkdown, simply enclose the content to be rendered within
+ * the body of TMarkdown in a template.
+ *
+ * See http://www.pradosoft.com/demos/quickstart/?page=Markdown for
+ * details on the Markdown syntax usage.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TMarkdown extends TControl
+{
+ /**
+ * @var TTextHighlighter
+ */
+ private $_highlighter;
+
+ /**
+ * Renders body content.
+ * This method overrides parent implementation by removing
+ * malicious javascript code from the body content
+ * @param THtmlWriter writer
+ */
+ public function render($writer)
+ {
+ $textWriter=new TTextWriter;
+ parent::render(new THtmlWriter($textWriter));
+ $writer->write($this->renderMarkdown($textWriter->flush()));
+ }
+
+ /**
+ * Use MarkdownParser to render the HTML content.
+ * @param string markdown content
+ * @return string HTML content
+ */
+ protected function renderMarkdown($text)
+ {
+ $renderer = Prado::createComponent('System.3rdParty.Markdown.MarkdownParser');
+ $result = $renderer->parse($text);
+ return preg_replace_callback(
+ '/<pre><code>\[\s*(\w+)\s*\]\n+((.|\n)*?)\s*<\\/code><\\/pre>/im',
+ array($this, 'highlightCode'), $result);
+ }
+
+ /**
+ * @return TTextHighlighter source code highlighter
+ */
+ public function getTextHighlighter()
+ {
+ if(is_null($this->_highlighter))
+ $this->_highlighter = new TTextHighlighter;
+ return $this->_highlighter;
+ }
+
+
+ /**
+ * Highlights source code using TTextHighlighter
+ * @param array matches of code blocks
+ * @return string highlighted code.
+ */
+ protected function highlightCode($matches)
+ {
+ $text = new TTextWriter;
+ $writer = new THtmlWriter($text);
+ $hi = $this->getTextHighlighter();
+ if($hi->getControls()->getCount() > 0)
+ $hi->getControls()->removeAt(0);
+ $hi->addParsedObject(html_entity_decode($matches[2]));
+ $hi->setLanguage($matches[1]);
+ $hi->render($writer);
+ return $text->flush();
+ }
+
+ /**
+ * Registers css style for the highlighted result.
+ * This method overrides parent implementation.
+ * @param THtmlWriter writer
+ */
+ public function onPreRender($writer)
+ {
+ parent::onPreRender($writer);
+ $hi = $this->getTextHighlighter();
+ $this->getControls()->insertAt(0,$hi);
+ $hi->onPreRender($writer);
+ $this->getControls()->removeAt(0);
+ }
+}
+
+?>
\ No newline at end of file diff --git a/framework/Web/UI/WebControls/TRepeater.php b/framework/Web/UI/WebControls/TRepeater.php index 1acdc766..9aa7af8d 100644 --- a/framework/Web/UI/WebControls/TRepeater.php +++ b/framework/Web/UI/WebControls/TRepeater.php @@ -11,9 +11,10 @@ */
/**
- * Using TDataBoundControl cass
+ * Using TDataBoundControl and TDataFieldAccessor cass
*/
Prado::using('System.Web.UI.WebControls.TDataBoundControl');
+Prado::using('System.Util.TDataFieldAccessor');
/**
* TRepeater class
diff --git a/framework/Web/UI/WebControls/TValidationSummary.php b/framework/Web/UI/WebControls/TValidationSummary.php index 8148036b..cfb57c5b 100644 --- a/framework/Web/UI/WebControls/TValidationSummary.php +++ b/framework/Web/UI/WebControls/TValidationSummary.php @@ -223,9 +223,24 @@ class TValidationSummary extends TWebControl {
if(!$this->getEnabled(true) || !$this->getEnableClientScript())
return;
+ $cs = $this->getPage()->getClientScript();
+ $cs->registerPradoScript('validator');
+
+ //need to register the validation manager is validation summary is alone.
+ $formID=$this->getPage()->getForm()->getClientID();
+ $scriptKey = "TBaseValidator:$formID";
+ if($this->getEnableClientScript() && !$cs->isEndScriptRegistered($scriptKey))
+ {
+ $manager['FormID'] = $formID;
+ $options = TJavaScript::encode($manager);
+ $cs->registerPradoScript('validator');
+ $cs->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});");
+ }
+
+
$options=TJavaScript::encode($this->getClientScriptOptions());
$script = "new Prado.WebUI.TValidationSummary({$options});";
- $this->getPage()->getClientScript()->registerEndScript($this->getClientID(), $script);
+ $cs->registerEndScript($this->getClientID(), $script);
}
/**
diff --git a/framework/Web/UI/WebControls/TWizard.php b/framework/Web/UI/WebControls/TWizard.php index 2d2815ba..811c4e76 100644 --- a/framework/Web/UI/WebControls/TWizard.php +++ b/framework/Web/UI/WebControls/TWizard.php @@ -622,7 +622,7 @@ class TWizard extends TWebControl implements INamingContainer }
/**
- * @var TWizardNavigationContainer container of the start navigation
+ * @return TWizardNavigationContainer container of the start navigation
*/
public function getStartNavigation()
{
@@ -630,7 +630,7 @@ class TWizard extends TWebControl implements INamingContainer }
/**
- * @var TWizardNavigationContainer container of the step navigation
+ * @return TWizardNavigationContainer container of the step navigation
*/
public function getStepNavigation()
{
@@ -638,7 +638,7 @@ class TWizard extends TWebControl implements INamingContainer }
/**
- * @var TWizardNavigationContainer container of the finish navigation
+ * @return TWizardNavigationContainer container of the finish navigation
*/
public function getFinishNavigation()
{
diff --git a/tests/FunctionalTests/quickstart/Controls/ListBoxTestCase.php b/tests/FunctionalTests/quickstart/Controls/ListBoxTestCase.php index c031c5fd..7ed2b10f 100644 --- a/tests/FunctionalTests/quickstart/Controls/ListBoxTestCase.php +++ b/tests/FunctionalTests/quickstart/Controls/ListBoxTestCase.php @@ -42,12 +42,13 @@ class ListBoxTestCase extends SeleniumTestCase $this->verifySelectOptions("ctl0\$body\$ctl7[]","item 1,item 2,item 3,item 4");
// multiselection list box's behavior upon postback
- $this->select("ctl0\$body\$ListBox2[]", "label=item 3");
+ $this->addSelection("ctl0\$body\$ListBox2[]", "label=item 3");
$this->clickAndWait("name=ctl0\$body\$ctl8", "");
$this->verifyText("ctl0_body_MultiSelectionResult2","Your selection is: (Index: 1, Value: value 2, Text: item 2)(Index: 2, Value: value 3, Text: item 3)(Index: 3, Value: value 4, Text: item 4)");
// Auto postback multiselection list box
- $this->selectAndWait("ctl0\$body\$ctl9[]", "label=item 1");
+ $this->addSelection("ctl0\$body\$ctl9[]", "label=item 1");
+ $this->waitForPageToLoad(1000);
$this->verifyText("ctl0_body_MultiSelectionResult","Your selection is: (Index: 0, Value: value 1, Text: item 1)(Index: 1, Value: value 2, Text: item 2)(Index: 3, Value: value 4, Text: item 4)");
// Databind to an integer-indexed array
@@ -73,7 +74,6 @@ class ListBoxTestCase extends SeleniumTestCase // List box causing validation
$this->verifyNotVisible('ctl0_body_ctl12');
$this->select("ctl0\$body\$VListBox2", "label=Agree");
-// $this->pause(1000);
$this->verifyVisible('ctl0_body_ctl12');
$this->type("ctl0\$body\$TextBox", "test");
$this->selectAndWait("ctl0\$body\$VListBox2", "label=Disagree");
diff --git a/tests/FunctionalTests/quickstart/Controls/MultiViewTestCase.php b/tests/FunctionalTests/quickstart/Controls/MultiViewTestCase.php index c94a325c..90f67901 100644 --- a/tests/FunctionalTests/quickstart/Controls/MultiViewTestCase.php +++ b/tests/FunctionalTests/quickstart/Controls/MultiViewTestCase.php @@ -9,8 +9,8 @@ class MultiViewTestCase extends SeleniumTestCase $this->verifyTitle("PRADO QuickStart Sample", "");
// view 1 : type in a string
- $this->verifyNotVisible('ctl0_body_Result1');
- $this->verifyNotVisible('ctl0_body_Result2');
+ $this->verifyElementNotPresent('ctl0_body_Result1');
+ $this->verifyElementNotPresent('ctl0_body_Result2');
$this->type('ctl0_body_Memo','test');
$this->clickAndWait('ctl0$body$ctl1');
@@ -20,8 +20,8 @@ class MultiViewTestCase extends SeleniumTestCase $this->clickAndWait('ctl0$body$ctl7');
// view 2 : update dropdownlist
- $this->verifyNotVisible('ctl0_body_Result1');
- $this->verifyNotVisible('ctl0_body_Result2');
+ $this->verifyElementNotPresent('ctl0_body_Result1');
+ $this->verifyElementNotPresent('ctl0_body_Result2');
$this->select('ctl0$body$DropDownList', "label=Blue");
$this->clickAndWait('ctl0$body$ctl4');
diff --git a/tests/FunctionalTests/selenium/TestRunner.hta b/tests/FunctionalTests/selenium/TestRunner.hta deleted file mode 100644 index e4516243..00000000 --- a/tests/FunctionalTests/selenium/TestRunner.hta +++ /dev/null @@ -1,146 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> -<html> -<head> -<HTA:APPLICATION ID="SeleniumTestRunner" APPLICATIONNAME="Selenium" > -<!-- the previous line is only relevant if you rename this - file to "TestRunner.hta" --> - -<!-- The copyright notice and other comments have been moved to after the HTA declaration, - to work-around a bug in IE on Win2K whereby the HTA application doesn't function correctly --> -<!-- -Copyright 2004 ThoughtWorks, Inc - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type" /> - -<title>Selenium Functional Test Runner</title> -<link rel="stylesheet" type="text/css" href="selenium.css" /> -<script language="JavaScript" type="text/javascript" src="html-xpath/html-xpath-patched.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-browserbot.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-api.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-commandhandlers.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-executionloop.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-fitrunner.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-logging.js"></script> -<script language="JavaScript" type="text/javascript" src="htmlutils.js"></script> -<script language="JavaScript" type="text/javascript" src="xpath.js"></script> -<script language="JavaScript" type="text/javascript" src="user-extensions.js"></script> -<script language="JavaScript" type="text/javascript"> - function openDomViewer() { - var autFrame = document.getElementById('myiframe'); - var autFrameDocument = getIframeDocument(autFrame); - this.rootDocument = autFrameDocument; - var domViewer = window.open('domviewer.html'); - return false; - } -</script> -</head> - -<body onload="start();"> - - <table class="layout"> - <form action="" name="controlPanel"> - - <!-- Suite, Test, Control Panel --> - - <tr class="selenium"> - <td width="25%" height="30%" rowspan="2"><iframe name="testSuiteFrame" id="testSuiteFrame" src="./tests/TestSuite.html"></iframe></td> - <td width="50%" height="30%" rowspan="2"><iframe name="testFrame" id="testFrame"></iframe></td> - <th width="25%" height="1" class="header"> - <h1><a href="http://selenium.thoughtworks.com" title="The Selenium Project">Selenium</a> TestRunner</h1> - </th> - </tr> - - <tr class="selenium"> - <td width="25%" height="30%" id="controlPanel"> - - <fieldset> - <legend>Execute Tests</legend> - - <div> - <input id="modeRun" type="radio" name="runMode" value="0" checked="checked"/><label for="modeRun">Run</label> - <input id="modeWalk" type="radio" name="runMode" value="500" /><label for="modeWalk">Walk</label> - <input id="modeStep" type="radio" name="runMode" value="-1" /><label for="modeStep">Step</label> - </div> - - <div> - <button type="button" id="runSuite" onclick="startTestSuite();" - title="Run the entire Test-Suite"> - <strong>All</strong> - </button> - <button type="button" id="runTest" onclick="runSingleTest();" - title="Run the current Test"> - <em>Selected</em> - </button> - <button type="button" id="continueTest" disabled="disabled" - title="Continue the Test"> - Continue - </button> - </div> - - </fieldset> - - <table id="stats" align="center"> - <tr> - <td colspan="2" align="right">Elapsed:</td> - <td id="elapsedTime" colspan="2">00.00</td> - </tr> - <tr> - <th colspan="2">Tests</th> - <th colspan="2">Commands</th> - </tr> - <tr> - <td class="count" id="testRuns">0</td> - <td>run</td> - <td class="count" id="commandPasses">0</td> - <td>passed</td> - </tr> - <tr> - <td class="count" id="testFailures">0</td> - <td>failed</td> - <td class="count" id="commandFailures">0</td> - <td>failed</td> - </tr> - <tr> - <td colspan="2"></td> - <td class="count" id="commandErrors">0</td> - <td>incomplete</td> - </tr> - </table> - - <fieldset> - <legend>Tools</legend> - - <button type="button" id="domViewer1" onclick="openDomViewer();"> - View DOM - </button> - <button type="button" onclick="LOG.show();"> - Show Log - </button> - - </fieldset> - - </td> - </tr> - - <!-- AUT --> - - <tr> - <td colspan="3" height="70%"><iframe name="myiframe" id="myiframe" src="TestRunner-splash.html"></iframe></td> - </tr> - </form> - </table> - -</body> -</html> diff --git a/tests/FunctionalTests/selenium/TestRunner.html b/tests/FunctionalTests/selenium/TestRunner.html deleted file mode 100644 index c476f25e..00000000 --- a/tests/FunctionalTests/selenium/TestRunner.html +++ /dev/null @@ -1,146 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> -<html> -<head> -<HTA:APPLICATION ID="SeleniumTestRunner" APPLICATIONNAME="Selenium" > -<!-- the previous line is only relevant if you rename this - file to "TestRunner.hta" --> - -<!-- The copyright notice and other comments have been moved to after the HTA declaration, - to work-around a bug in IE on Win2K whereby the HTA application doesn't function correctly --> -<!-- -Copyright 2004 ThoughtWorks, Inc - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type" /> - -<title>Selenium Functional Test Runner</title> -<link rel="stylesheet" type="text/css" href="selenium.css" /> -<script language="JavaScript" type="text/javascript" src="html-xpath/html-xpath-patched.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-browserbot.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-api.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-commandhandlers.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-executionloop.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-testrunner.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-logging.js"></script> -<script language="JavaScript" type="text/javascript" src="htmlutils.js"></script> -<script language="JavaScript" type="text/javascript" src="xpath.js"></script> -<script language="JavaScript" type="text/javascript" src="user-extensions.js"></script> -<script language="JavaScript" type="text/javascript"> - function openDomViewer() { - var autFrame = document.getElementById('myiframe'); - var autFrameDocument = getIframeDocument(autFrame); - this.rootDocument = autFrameDocument; - var domViewer = window.open('domviewer.html'); - return false; - } -</script> -</head> - -<body onload="start();"> - - <table class="layout"> - <form action="" name="controlPanel"> - - <!-- Suite, Test, Control Panel --> - - <tr class="selenium"> - <td width="25%" height="30%" rowspan="2"><iframe name="testSuiteFrame" id="testSuiteFrame" src="./tests/TestSuite.html"></iframe></td> - <td width="50%" height="30%" rowspan="2"><iframe name="testFrame" id="testFrame"></iframe></td> - <th width="25%" height="1" class="header"> - <h1><a href="http://selenium.thoughtworks.com" title="The Selenium Project">Selenium</a> TestRunner</h1> - </th> - </tr> - - <tr class="selenium"> - <td width="25%" height="30%" id="controlPanel"> - - <fieldset> - <legend>Execute Tests</legend> - - <div> - <input id="modeRun" type="radio" name="runMode" value="0" checked="checked"/><label for="modeRun">Run</label> - <input id="modeWalk" type="radio" name="runMode" value="500" /><label for="modeWalk">Walk</label> - <input id="modeStep" type="radio" name="runMode" value="-1" /><label for="modeStep">Step</label> - </div> - - <div> - <button type="button" id="runSuite" onclick="startTestSuite();" - title="Run the entire Test-Suite"> - <strong>All</strong> - </button> - <button type="button" id="runTest" onclick="runSingleTest();" - title="Run the current Test"> - <em>Selected</em> - </button> - <button type="button" id="continueTest" disabled="disabled" - title="Continue the Test"> - Continue - </button> - </div> - - </fieldset> - - <table id="stats" align="center"> - <tr> - <td colspan="2" align="right">Elapsed:</td> - <td id="elapsedTime" colspan="2">00.00</td> - </tr> - <tr> - <th colspan="2">Tests</th> - <th colspan="2">Commands</th> - </tr> - <tr> - <td class="count" id="testRuns">0</td> - <td>run</td> - <td class="count" id="commandPasses">0</td> - <td>passed</td> - </tr> - <tr> - <td class="count" id="testFailures">0</td> - <td>failed</td> - <td class="count" id="commandFailures">0</td> - <td>failed</td> - </tr> - <tr> - <td colspan="2"></td> - <td class="count" id="commandErrors">0</td> - <td>incomplete</td> - </tr> - </table> - - <fieldset> - <legend>Tools</legend> - - <button type="button" id="domViewer1" onclick="openDomViewer();"> - View DOM - </button> - <button type="button" onclick="LOG.show();"> - Show Log - </button> - - </fieldset> - - </td> - </tr> - - <!-- AUT --> - - <tr> - <td colspan="3" height="70%"><iframe name="myiframe" id="myiframe" src="TestRunner-splash.html"></iframe></td> - </tr> - </form> - </table> - -</body> -</html> diff --git a/tests/FunctionalTests/selenium/VERSION.txt b/tests/FunctionalTests/selenium/VERSION.txt deleted file mode 100644 index 2d589f48..00000000 --- a/tests/FunctionalTests/selenium/VERSION.txt +++ /dev/null @@ -1 +0,0 @@ -trunk (SVN/UNRELEASED)
\ No newline at end of file diff --git a/tests/FunctionalTests/selenium/build.xml b/tests/FunctionalTests/selenium/build.xml deleted file mode 100644 index 73376a1e..00000000 --- a/tests/FunctionalTests/selenium/build.xml +++ /dev/null @@ -1,109 +0,0 @@ -<project name="Selenium Javascript Build" default="run_jsunit_tests" basedir="."> - - <property name="version" value="0.2"/> - - <!-- global properties --> - <property name="JSUnitHome" value="${basedir}/jsunit"/> - <property name="WebServerPort" value="8000"/> <!-- this must match the hardcoded value in tinyWebServer.py --> - - <path id="JSUnitClassPath"> - <fileset dir="${JSUnitHome}/java/lib"> - <include name="*.jar"/> - </fileset> - <fileset dir="${JSUnitHome}/java/bin"> - <include name="jsunit.jar"/> - </fileset> - </path> - - - <!-- Private independant targets --> - <target name="-start_web_server"> - <exec executable="startWebServer.bat" spawn="true"/> - <waitfor maxwaitunit="second" maxwait="5" timeoutproperty="webserver.timeout"> - <http url="http://localhost:${WebServerPort}"/> - </waitfor> - <fail message="Timed out waiting for http://localhost:${WebServerPort}" if="webserver.timeout"/> - </target> - - <target name="-stop_web_server" description="Stops a web server"> - <exec executable="stopWebServer.bat"/> - </target> - - <target name="-execute_jsunit_tests"> - - <property name="JSUnitServerPort" value="8080"/> - - <!-- TODO : Make this locally configurable without needing to hack build.xml --> - <property name="browserFileNames" value="c:\program files\Mozilla Firefox\firefox.exe,c:\program files\internet explorer\iexplore.exe"/> - - <property name="JSUnitSuitePath" value="/tests/jsUnitSuite.html"/> - <property name="JSUnitTestRunnerPath" value="/jsunit/testRunner.html"/> - - <!-- TODO: Theres a lot of moving parts here! - - The JSUnit testrunner and the tests themselves are served to the browser by the (python) webserver - - The Browser communicates with the build via a JSUnit server running on jetty - - consolidate to one webserver? - - Get runner and tests from the filesystem instead? - --> - <property name="testUrl" value="http://localhost:${WebServerPort}${JSUnitTestRunnerPath}?testPage=http://localhost:${WebServerPort}${JSUnitSuitePath}&autoRun=true&submitresults=localhost:${JSUnitServerPort}/jsunit/acceptor"/> - - <junit showoutput="true" failureproperty="JSUnitTestsFailed"> - <classpath refid="JSUnitClassPath"/> - <sysproperty key="browserFileNames" value="${browserFileNames}"/> - <sysproperty key="url" value="${testUrl}"/> - <sysproperty key="port" value="${JSUnitServerPort}"/> - <sysproperty key="logsDirectory" value="jsunit-logs"/> - <test name="net.jsunit.StandaloneTest"/> - </junit> - </target> - - <target name="-check_for_jsunit_failure"> - <fail message="One or more JSUnit Tests Failed" if="JSUnitTestsFailed"/> - </target> - - <!-- Dependancy Targets --> - <target name="--run_jsunit_tests" depends=" -start_web_server, -execute_jsunit_tests, -stop_web_server, -check_for_jsunit_failure"/> - - <!-- Public Targets --> - <target name="run_jsunit_tests" description="Runs selenium JSUnit tests" depends="--run_jsunit_tests"/> - - <target name="dist"> - - <mkdir dir="dist"/> - - <echo file="dist/readme.txt">Create a TestSuite.html page, and a number of test pages here. Read the docs!</echo> - - <zip destfile="dist/selenium-${version}-inbrowser.zip"> - <zipfileset dir="dist" prefix="tests/html"> - <include name="readme.txt"/> - </zipfileset> - <fileset dir="."> - <exclude name="dist/**"/> - <exclude name="SeleneseRunner.html"/> - <exclude name="jsmock/**"/> - <exclude name="jsunit/**"/> - <exclude name="jsunit-logs/**"/> - <exclude name="tests/**"/> - <exclude name="build.xml"/> - <exclude name="**/web.xml"/> - </fileset> - </zip> - - <zip destfile="dist/selenium-${version}-inbrowser-src.zip"> - <fileset dir="."> - <exclude name="dist/**"/> - <include name="**"/> - </fileset> - </zip> - <delete file="dist/readme.txt"/> - - </target> - <target name="publish-dist" depends="dist"> - - <copy todir="../../website/webapps/ROOT" file="dist/selenium-${version}-inbrowser-src.zip"/> - <copy todir="../../website/webapps/ROOT" file="dist/selenium-${version}-inbrowser.zip"/> - - </target> - - -</project> diff --git a/tests/FunctionalTests/selenium/SeleneseRunner.html b/tests/FunctionalTests/selenium/core/SeleneseRunner.html index 4ace34c7..49eef6f6 100644..100755 --- a/tests/FunctionalTests/selenium/SeleneseRunner.html +++ b/tests/FunctionalTests/selenium/core/SeleneseRunner.html @@ -1,3 +1,5 @@ +<html> + <!-- Copyright 2004 ThoughtWorks, Inc @@ -13,38 +15,50 @@ Copyright 2004 ThoughtWorks, Inc See the License for the specific language governing permissions and limitations under the License. --> -<html> +<HTA:APPLICATION ID="SeleniumHTARunner" APPLICATIONNAME="Selenium" > <head> <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> <title>Selenium Functional Test Runner</title> <link rel="stylesheet" type="text/css" href="selenium.css" /> <script language="JavaScript" type="text/javascript" src="jsunit/app/jsUnitCore.js"></script> -<script type="text/javascript" src="xmlextras.js"></script> - -<script language="JavaScript" type="text/javascript" src="selenium-browserbot.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-api.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-commandhandlers.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-executionloop.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-seleneserunner.js"></script> -<script language="JavaScript" type="text/javascript" src="selenium-logging.js"></script> -<script language="JavaScript" type="text/javascript" src="htmlutils.js"></script> -<script language="JavaScript" type="text/javascript" src="xpath.js"></script> -<script language="JavaScript" type="text/javascript" src="user-extensions.js"></script> +<script type="text/javascript" src="scripts/xmlextras.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-browserdetect.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-browserbot.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/prototype-1.4.0.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/find_matching_child.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-api.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-commandhandlers.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-executionloop.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-seleneserunner.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-logging.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-version.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/htmlutils.js"></script> +<script language="JavaScript" type="text/javascript" src="xpath/misc.js"></script> +<script language="JavaScript" type="text/javascript" src="xpath/dom.js"></script> +<script language="JavaScript" type="text/javascript" src="xpath/xpath.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/user-extensions.js"></script> <script language="JavaScript" type="text/javascript"> function openDomViewer() { var autFrame = document.getElementById('myiframe'); var autFrameDocument = getIframeDocument(autFrame); - var domViewer = window.open('domviewer.html'); + var domViewer = window.open(getDocumentBase(document) + 'domviewer/domviewer.html'); domViewer.rootDocument = autFrameDocument; return false; } + + function cleanUp() { + if (LOG != null) { + LOG.close(); + } + } + </script> <script> </script> </head> -<body onLoad="runTest()"> +<body onLoad="runTest()" onUnload="cleanUp()"> <table border="1" style="height: 100%;"> <tr> @@ -81,7 +95,7 @@ http-equiv="content-type"> </form> </td> <td width="50%" height="30%"> - <b>Last Four Commands</b><br/> + <b>Last Four Test Commands:</b><br/> <div id="commandList"></div> </td> </tr> diff --git a/tests/FunctionalTests/selenium/SeleniumLog.html b/tests/FunctionalTests/selenium/core/SeleniumLog.html index 01a5a9fe..291e1f77 100644..100755 --- a/tests/FunctionalTests/selenium/SeleniumLog.html +++ b/tests/FunctionalTests/selenium/core/SeleniumLog.html @@ -16,6 +16,8 @@ var logLevels = { error: 3 }; +var logLevelThreshold = null; + function getThresholdLevel() { var buttons = document.getElementById('logLevelChooser').level; for (var i = 0; i < buttons.length; i++) { @@ -25,8 +27,24 @@ function getThresholdLevel() { } } +function setThresholdLevel(logLevel) { + logLevelThreshold = logLevel; + var buttons = document.getElementById('logLevelChooser').level; + for (var i = 0; i < buttons.length; i++) { + if (buttons[i].value==logLevel) { + buttons[i].checked = true; + } + else { + buttons[i].checked = false; + } + } +} + function append(message, logLevel) { - if (logLevels[logLevel] < logLevels[getThresholdLevel()]) { + if (logLevelThreshold==null) { + logLevelThreshold = getThresholdLevel(); + } + if (logLevels[logLevel] < logLevels[logLevelThreshold]) { return; } var log = document.getElementById('log'); diff --git a/tests/FunctionalTests/selenium/core/TestPrompt.html b/tests/FunctionalTests/selenium/core/TestPrompt.html new file mode 100755 index 00000000..f62bad87 --- /dev/null +++ b/tests/FunctionalTests/selenium/core/TestPrompt.html @@ -0,0 +1,93 @@ +<html>
+<!--
+Copyright 2004 ThoughtWorks, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<head>
+<meta content="text/html; charset=ISO-8859-1"
+http-equiv="content-type">
+<title>Select a Test Suite</title>
+<script language="JavaScript" type="text/javascript" src="scripts/selenium-browserdetect.js"></script>
+<script>
+
+function load() {
+ if (browserVersion.isHTA) {
+ document.getElementById("save-div").style.display = "inline";
+ }
+}
+
+function autoCheck() {
+ var auto = document.getElementById("auto");
+ var autoDiv = document.getElementById("auto-div");
+ if (auto.checked) {
+ autoDiv.style.display = "inline";
+
+ } else {
+ autoDiv.style.display = "none";
+ }
+}
+
+function saveCheck() {
+ var results = document.getElementById("results");
+ var check = document.getElementById("save").checked;
+ if (check) {
+ results.firstChild.nodeValue = "Results file ";
+ document.getElementById("resultsUrl").value = "results.html";
+ } else {
+ results.firstChild.nodeValue = "Results URL ";
+ document.getElementById("resultsUrl").value = "../postResults";
+ }
+}
+
+function go() {
+
+ var inputs = document.getElementsByTagName("input");
+ var queryString = "";
+ for (var i = 0; i < inputs.length; i++) {
+ var elem = inputs[i];
+ var name = elem.name;
+ var value = elem.value;
+ if (elem.checked) {
+ value = "true";
+ }
+ queryString += escape(name) + "=" + escape(value);
+ if (i < (inputs.length - 1)) {
+ queryString += "&";
+ }
+ }
+
+ window.parent.queryString = queryString;
+ window.parent.loadSuiteFrame();
+ return false;
+}
+</script>
+</head>
+<body onload="load()" style="font-size: x-small">
+<form id="prompt" target="_top" method="GET" onsubmit="return go();" action="TestRunner.html">
+<p>Select an HTML test suite:</p>
+<input id="test" name="test" size="30" value="../tests/TestSuite.html"/>
+<p><label for="auto">Auto-run </label><input id="auto" type="checkbox" name="auto" value="false" onclick="autoCheck();"/>
+<div id="auto-div" style="display: none">
+<label for="close" >Close afterwards </label><input id="close" type="checkbox" name="close" />
+<div id="save-div" style="display: none">
+ <br/><label for="save">Save to file </label><input id="save" type="checkbox" name="save" onclick="saveCheck();"/>
+</div>
+</p>
+<p id="results" >Results URL <input id="resultsUrl" name="resultsUrl" value="../postResults"/></p>
+</div>
+<p><input type="submit" value="go"/></p>
+</form>
+</body>
+</html>
\ No newline at end of file diff --git a/tests/FunctionalTests/selenium/TestRunner-splash.html b/tests/FunctionalTests/selenium/core/TestRunner-splash.html index 3abd1ec8..da2acdce 100644..100755 --- a/tests/FunctionalTests/selenium/TestRunner-splash.html +++ b/tests/FunctionalTests/selenium/core/TestRunner-splash.html @@ -44,7 +44,7 @@ Copyright 2005 ThoughtWorks, Inc For more information on Selenium, visit
<pre>
- <a href="http://selenium.thoughtworks.com" target="_blank">http://selenium.thoughtworks.com</a>
+ <a href="http://selenium.openqa.org" target="_blank">http://selenium.openqa.org</a>
</pre>
</td>
diff --git a/tests/FunctionalTests/selenium/core/TestRunner.html b/tests/FunctionalTests/selenium/core/TestRunner.html new file mode 100755 index 00000000..f933ab9f --- /dev/null +++ b/tests/FunctionalTests/selenium/core/TestRunner.html @@ -0,0 +1,157 @@ +<html> + +<head> +<HTA:APPLICATION ID="SeleniumHTARunner" APPLICATIONNAME="Selenium" > +<!-- the previous line is only relevant if you rename this + file to "TestRunner.hta" --> + +<!-- The copyright notice and other comments have been moved to after the HTA declaration, + to work-around a bug in IE on Win2K whereby the HTA application doesn't function correctly --> +<!-- +Copyright 2004 ThoughtWorks, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type" /> + +<title>Selenium Functional Test Runner</title> +<link rel="stylesheet" type="text/css" href="selenium.css" /> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-browserdetect.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-browserbot.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/prototype-1.4.0.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/find_matching_child.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-api.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-commandhandlers.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-executionloop.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-testrunner.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-logging.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/selenium-version.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/htmlutils.js"></script> +<script language="JavaScript" type="text/javascript" src="xpath/misc.js"></script> +<script language="JavaScript" type="text/javascript" src="xpath/dom.js"></script> +<script language="JavaScript" type="text/javascript" src="xpath/xpath.js"></script> +<script language="JavaScript" type="text/javascript" src="scripts/user-extensions.js"></script> +<script language="JavaScript" type="text/javascript"> + function openDomViewer() { + var autFrame = document.getElementById('myiframe'); + var autFrameDocument = getIframeDocument(autFrame); + this.rootDocument = autFrameDocument; + var domViewer = window.open(getDocumentBase(document) + 'domviewer/domviewer.html'); + return false; + } +</script> +</head> + +<body onLoad="start();"> + + <table class="layout"> + <form action="" name="controlPanel"> + + <!-- Suite, Test, Control Panel --> + + <tr class="selenium"> + <td width="25%" height="30%" ><iframe name="testSuiteFrame" id="testSuiteFrame" src="./TestPrompt.html" application="yes"></iframe></td> + <td width="50%" height="30%" ><iframe name="testFrame" id="testFrame" application="yes"></iframe></td> + + <td width="25%"> + <table class="layout"> + <tr class="selenium"> + <th width="25%" height="1" class="header"> + <h1><a href="http://selenium.thoughtworks.com" title="The Selenium Project">Selenium</a> TestRunner</h1> + </th> + </tr> + <tr> + <td width="25%" height="30%" id="controlPanel"> + + <fieldset> + <legend>Execute Tests</legend> + + <div> + <input id="modeRun" type="radio" name="runMode" value="0" checked="checked"/><label for="modeRun">Run</label> + <input id="modeWalk" type="radio" name="runMode" value="500" /><label for="modeWalk">Walk</label> + <input id="modeStep" type="radio" name="runMode" value="-1" /><label for="modeStep">Step</label> + </div> + + <div> + <button type="button" id="runSuite" onClick="startTestSuite();" + title="Run the entire Test-Suite"> + <strong>All</strong> + </button> + <button type="button" id="runTest" onClick="runSingleTest();" + title="Run the current Test"> + <em>Selected</em> + </button> + <button type="button" id="continueTest" disabled="disabled" + title="Continue the Test"> + Continue + </button> + </div> + + </fieldset> + + <table id="stats" align="center"> + <tr> + <td colspan="2" align="right">Elapsed:</td> + <td id="elapsedTime" colspan="2">00.00</td> + </tr> + <tr> + <th colspan="2">Tests</th> + <th colspan="2">Commands</th> + </tr> + <tr> + <td class="count" id="testRuns">0</td> + <td>run</td> + <td class="count" id="commandPasses">0</td> + <td>passed</td> + </tr> + <tr> + <td class="count" id="testFailures">0</td> + <td>failed</td> + <td class="count" id="commandFailures">0</td> + <td>failed</td> + </tr> + <tr> + <td colspan="2"></td> + <td class="count" id="commandErrors">0</td> + <td>incomplete</td> + </tr> + </table> + + <fieldset> + <legend>Tools</legend> + + <button type="button" id="domViewer1" onClick="openDomViewer();"> + View DOM + </button> + <button type="button" onClick="LOG.show();"> + Show Log + </button> + + </fieldset> + + </td> + </tr> + </table> + </td> + </tr> + + <!-- AUT --> + + <tr> + <td colspan="3" height="70%"><iframe name="myiframe" id="myiframe" src="TestRunner-splash.html"></iframe></td> + </tr> + </form> + </table> + +</body> +</html> diff --git a/tests/FunctionalTests/selenium/dom-images/butmin.gif b/tests/FunctionalTests/selenium/core/domviewer/butmin.gif Binary files differindex 7b7cefd5..7b7cefd5 100644..100755 --- a/tests/FunctionalTests/selenium/dom-images/butmin.gif +++ b/tests/FunctionalTests/selenium/core/domviewer/butmin.gif diff --git a/tests/FunctionalTests/selenium/dom-images/butplus.gif b/tests/FunctionalTests/selenium/core/domviewer/butplus.gif Binary files differindex 6d68cfa9..6d68cfa9 100644..100755 --- a/tests/FunctionalTests/selenium/dom-images/butplus.gif +++ b/tests/FunctionalTests/selenium/core/domviewer/butplus.gif diff --git a/tests/FunctionalTests/selenium/dom-styles/default.css b/tests/FunctionalTests/selenium/core/domviewer/domviewer.css index b64b2435..b64b2435 100644..100755 --- a/tests/FunctionalTests/selenium/dom-styles/default.css +++ b/tests/FunctionalTests/selenium/core/domviewer/domviewer.css diff --git a/tests/FunctionalTests/selenium/domviewer.html b/tests/FunctionalTests/selenium/core/domviewer/domviewer.html index cf10cc6f..9158a50f 100644..100755 --- a/tests/FunctionalTests/selenium/domviewer.html +++ b/tests/FunctionalTests/selenium/core/domviewer/domviewer.html @@ -4,7 +4,7 @@ <head> <title>DOM Viewer</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> - <link rel="stylesheet" type="text/css" href="dom-styles/default.css"/> + <link rel="stylesheet" type="text/css" href="domviewer.css"/> <script type="text/javascript" src="selenium-domviewer.js"></script> </head> <body onload="loadDomViewer();"> diff --git a/tests/FunctionalTests/selenium/selenium-domviewer.js b/tests/FunctionalTests/selenium/core/domviewer/selenium-domviewer.js index 01a384f5..941aab16 100644..100755 --- a/tests/FunctionalTests/selenium/selenium-domviewer.js +++ b/tests/FunctionalTests/selenium/core/domviewer/selenium-domviewer.js @@ -1,7 +1,7 @@ var HIDDEN="hidden"; var LEVEL = "level"; -var PLUS_SRC="dom-images/butplus.gif"; -var MIN_SRC="dom-images/butmin.gif"; +var PLUS_SRC="butplus.gif"; +var MIN_SRC="butmin.gif"; var newRoot; var maxColumns=1; @@ -106,6 +106,7 @@ function displayNode(element, level, isLink){ } function getNodeContent(element) { + str = ""; id =""; if (element.id != null && element.id != "") { @@ -123,17 +124,33 @@ function getNodeContent(element) { if (element.href != null && element.href != "") { href = " HREF(" + element.href + ")"; } + clazz = ""; + if (element.className != null && element.className != "") { + clazz = " CLASS(" + element.className + ")"; + } + src = ""; + if (element.src != null && element.src != "") { + src = " SRC(" + element.src + ")"; + } + alt = ""; + if (element.alt != null && element.alt != "") { + alt = " ALT(" + element.alt + ")"; + } + type = ""; + if (element.type != null && element.type != "") { + type = " TYPE(" + element.type + ")"; + } text =""; if (element.text != null && element.text != "" && element.text != "undefined") { text = " #TEXT(" + trim(element.text) +")"; } - str+=" <b>"+ element.nodeName + id + name + value + href + text + "</b>"; + str+=" <b>"+ element.nodeName + id + alt + type + clazz + name + value + href + src + text + "</b>"; return str; } function trim(val) { - val2 = val.substring(0,20) + " "; + val2 = val.substring(0,40) + " "; var spaceChr = String.fromCharCode(32); var length = val2.length; var retVal = ""; @@ -147,7 +164,7 @@ function trim(val) { } ix = ix-1; } - if (val.length > 20) { + if (val.length > 40) { retVal += "..."; } return retVal; diff --git a/tests/FunctionalTests/selenium/core/scripts/find_matching_child.js b/tests/FunctionalTests/selenium/core/scripts/find_matching_child.js new file mode 100755 index 00000000..5b635a27 --- /dev/null +++ b/tests/FunctionalTests/selenium/core/scripts/find_matching_child.js @@ -0,0 +1,69 @@ +/*
+ * Copyright 2004 ThoughtWorks, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+Element.findMatchingChildren = function(element, selector) {
+ var matches = $A([]);
+
+ var childCount = element.childNodes.length;
+ for (var i=0; i<childCount; i++) {
+ var child = element.childNodes[i];
+ if (selector(child)) {
+ matches.push(child);
+ } else {
+ childMatches = Element.findMatchingChildren(child, selector);
+ matches.push(childMatches);
+ }
+ }
+
+ return matches.flatten();
+}
+
+ELEMENT_NODE_TYPE = 1;
+
+Element.findFirstMatchingChild = function(element, selector) {
+
+ var childCount = element.childNodes.length;
+ for (var i=0; i<childCount; i++) {
+ var child = element.childNodes[i];
+ if (child.nodeType == ELEMENT_NODE_TYPE) {
+ if (selector(child)) {
+ return child;
+ }
+ result = Element.findFirstMatchingChild(child, selector);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ return null;
+}
+
+Element.findFirstMatchingParent = function(element, selector) {
+ var current = element.parentNode;
+ while (current != null) {
+ if (selector(current)) {
+ break;
+ }
+ current = current.parentNode;
+ }
+ return current;
+}
+
+Element.findMatchingChildById = function(element, id) {
+ return Element.findFirstMatchingChild(element, function(element){return element.id==id} );
+}
+
diff --git a/tests/FunctionalTests/selenium/htmlutils.js b/tests/FunctionalTests/selenium/core/scripts/htmlutils.js index 73922b13..fcb1ee44 100644..100755 --- a/tests/FunctionalTests/selenium/htmlutils.js +++ b/tests/FunctionalTests/selenium/core/scripts/htmlutils.js @@ -35,19 +35,130 @@ String.prototype.startsWith = function(str) { // Returns the text in this element function getText(element) { - text = ""; + var text = ""; - if(element.textContent) { + if(browserVersion.isFirefox && browserVersion.firefoxVersion >= "1.5") + { + var dummyElement = element.cloneNode(true); + renderWhitespaceInTextContent(dummyElement); + text = dummyElement.textContent; + } else if (browserVersion.isOpera) { + var dummyElement = element.cloneNode(true); + renderWhitespaceInTextContent(dummyElement); + text = dummyElement.innerText; + text = xmlDecode(text); + } + else if(element.textContent) + { text = element.textContent; - } else if(element.innerText) { + } + else if(element.innerText) + { text = element.innerText; } - // Replace with a space - // TODO - should this be in the match() code instead? - text = text.replace(/\240/g, " "); + + text = normalizeNewlines(text); + text = normalizeSpaces(text); + return text.trim(); } +function renderWhitespaceInTextContent(element) { + // Remove non-visible newlines in text nodes + if (element.nodeType == Node.TEXT_NODE) + { + element.data = element.data.replace(/\n|\r|\t/g, " "); + return; + } + + if (element.nodeType == Node.COMMENT_NODE) + { + element.data = ""; + return; + } + + // Don't modify PRE elements + if (element.tagName == "PRE") + { + return; + } + + // Handle inline element that force newlines + if (tagIs(element, ["BR", "HR"])) + { + // Replace this element with a newline text element + element.parentNode.replaceChild(element.ownerDocument.createTextNode("\n"), element) + } + + for (var i = 0; i < element.childNodes.length; i++) + { + var child = element.childNodes.item(i) + renderWhitespaceInTextContent(child); + } + + // Handle block elements that introduce newlines +// -- From HTML spec: +//<!ENTITY % block +// "P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT | +// BLOCKQUOTE | FORM | HR | TABLE | FIELDSET | ADDRESS"> + if (tagIs(element, ["P", "DIV"])) + { + element.appendChild(element.ownerDocument.createTextNode("\n"), element) + } + +} + +function tagIs(element, tags) +{ + var tag = element.tagName; + for (var i = 0; i < tags.length; i++) + { + if (tags[i] == tag) + { + return true; + } + } + return false; +} + +/** + * Convert all newlines to \m + */ +function normalizeNewlines(text) +{ + return text.replace(/\r\n|\r/g, "\n"); +} + +/** + * Replace multiple sequential spaces with a single space, and then convert to space. + */ +function normalizeSpaces(text) +{ + // IE has already done this conversion, so doing it again will remove multiple nbsp + if (browserVersion.isIE) + { + return text; + } + + // Replace multiple spaces with a single space + // TODO - this shouldn't occur inside PRE elements + text = text.replace(/\ +/g, " "); + + // Replace with a space + var pat = String.fromCharCode(160); // Opera doesn't like /\240/g + var re = new RegExp(pat, "g"); + return text.replace(re, " "); +} + +function xmlDecode(text) { + text = text.replace(/"/g, '"'); + text = text.replace(/'/g, "'"); + text = text.replace(/</g, "<"); + text = text.replace(/>/g, ">"); + text = text.replace(/&/g, "&"); + return text; +} + // Sets the text in this element function setText(element, text) { if(element.textContent) { @@ -59,8 +170,8 @@ function setText(element, text) { // Get the value of an <input> element function getInputValue(inputElement) { - if (inputElement.type.toUpperCase() == 'CHECKBOX' || - inputElement.type.toUpperCase() == 'RADIO') + if (inputElement.type.toUpperCase() == 'CHECKBOX' || + inputElement.type.toUpperCase() == 'RADIO') { return (inputElement.checked ? 'on' : 'off'); } @@ -80,6 +191,28 @@ function triggerEvent(element, eventType, canBubble) { } } +function triggerKeyEvent(element, eventType, keycode, canBubble) { + canBubble = (typeof(canBubble) == undefined) ? true : canBubble; + if (element.fireEvent) { + keyEvent = parent.frames['myiframe'].document.createEventObject(); + keyEvent.keyCode=keycode; + element.fireEvent('on' + eventType, keyEvent); + } + else { + var evt; + if( window.KeyEvent ) { + evt = document.createEvent('KeyEvents'); + evt.initKeyEvent(eventType, true, true, window, false, false, false, false, keycode, keycode); + } else { + evt = document.createEvent('UIEvents'); + evt.initUIEvent( eventType, true, true, window, 1 ); + evt.keyCode = keycode; + } + + element.dispatchEvent(evt); + } +} + /* Fire a mouse event in a browser-compatible manner */ function triggerMouseEvent(element, eventType, canBubble) { canBubble = (typeof(canBubble) == undefined) ? true : canBubble; @@ -88,7 +221,16 @@ function triggerMouseEvent(element, eventType, canBubble) { } else { var evt = document.createEvent('MouseEvents'); - evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null); + if (evt.initMouseEvent) + { + evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null) + } + else + { + // Safari + // TODO we should be initialising other mouse-event related attributes here + evt.initEvent(eventType, canBubble, true); + } element.dispatchEvent(evt); } } @@ -101,7 +243,7 @@ function removeLoadListener(element, command) { } function addLoadListener(element, command) { - if (window.addEventListener) + if (window.addEventListener && !browserVersion.isOpera) element.addEventListener("load",command, true); else if (window.attachEvent) element.attachEvent("onload",command); @@ -126,6 +268,14 @@ function getFunctionName(aFunction) { return 'anonymous'; } +function getDocumentBase(doc) { + var bases = document.getElementsByTagName("base"); + if (bases && bases.length && bases[0].href) { + return bases[0].href; + } + return ""; +} + function describe(object, delimiter) { var props = new Array(); for (var prop in object) { @@ -134,11 +284,11 @@ function describe(object, delimiter) { return props.join(delimiter || '\n'); } -PatternMatcher = function(pattern) { +var PatternMatcher = function(pattern) { this.selectStrategy(pattern); }; PatternMatcher.prototype = { - + selectStrategy: function(pattern) { this.pattern = pattern; var strategyName = 'glob'; // by default @@ -150,9 +300,10 @@ PatternMatcher.prototype = { if (!matchStrategy) { throw new SeleniumError("cannot find PatternMatcher.strategies." + strategyName); } + this.strategy = matchStrategy; this.matcher = new matchStrategy(pattern); }, - + matches: function(actual) { return this.matcher.matches(actual + ''); // Note: appending an empty string avoids a Konqueror bug @@ -190,6 +341,25 @@ PatternMatcher.strategies = { }, /** + * "globContains" (aka "wildmat") patterns, e.g. "glob:one,two,*", + * but don't require a perfect match; instead succeed if actual + * contains something that matches globString. + * Making this distinction is motivated by a bug in IE6 which + * leads to the browser hanging if we implement *TextPresent tests + * by just matching against a regular expression beginning and + * ending with ".*". The globcontains strategy allows us to satisfy + * the functional needs of the *TextPresent ops more efficiently + * and so avoid running into this IE6 freeze. + */ + globContains: function(globString) { + this.regexp = new RegExp(PatternMatcher.regexpFromGlobContains(globString)); + this.matches = function(actual) { + return this.regexp.test(actual); + }; + }, + + + /** * "glob" (aka "wildmat") patterns, e.g. "glob:one,two,*" */ glob: function(globString) { @@ -198,15 +368,23 @@ PatternMatcher.strategies = { return this.regexp.test(actual); }; } - + }; -PatternMatcher.regexpFromGlob = function(glob) { +PatternMatcher.convertGlobMetaCharsToRegexpMetaChars = function(glob) { var re = glob; re = re.replace(/([.^$+(){}\[\]\\|])/g, "\\$1"); re = re.replace(/\?/g, "(.|[\r\n])"); re = re.replace(/\*/g, "(.|[\r\n])*"); - return "^" + re + "$"; + return re; +}; + +PatternMatcher.regexpFromGlobContains = function(globContains) { + return PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(globContains); +}; + +PatternMatcher.regexpFromGlob = function(glob) { + return "^" + PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(glob) + "$"; }; var Assert = { @@ -223,8 +401,8 @@ var Assert = { if (args.expected === args.actual) { return; } - Assert.fail(args.comment + - "Expected '" + args.expected + + Assert.fail(args.comment + + "Expected '" + args.expected + "' but was '" + args.actual + "'"); }, @@ -236,11 +414,11 @@ var Assert = { if (PatternMatcher.matches(args.expected, args.actual)) { return; } - Assert.fail(args.comment + - "Actual value '" + args.actual + + Assert.fail(args.comment + + "Actual value '" + args.actual + "' did not match '" + args.expected + "'"); }, - + /* * Assert.notMtches(comment?, pattern, actual) */ @@ -249,8 +427,8 @@ var Assert = { if (!PatternMatcher.matches(args.expected, args.actual)) { return; } - Assert.fail(args.comment + - "Actual value '" + args.actual + + Assert.fail(args.comment + + "Actual value '" + args.actual + "' did match '" + args.expected + "'"); } @@ -273,6 +451,8 @@ function AssertionArguments(args) { function AssertionFailedError(message) { this.isAssertionFailedError = true; + this.isSeleniumError = true; + this.message = message; this.failureMessage = message; } diff --git a/tests/FunctionalTests/selenium/core/scripts/prototype-1.4.0.js b/tests/FunctionalTests/selenium/core/scripts/prototype-1.4.0.js new file mode 100755 index 00000000..0e85338b --- /dev/null +++ b/tests/FunctionalTests/selenium/core/scripts/prototype-1.4.0.js @@ -0,0 +1,1781 @@ +/* Prototype JavaScript framework, version 1.4.0 + * (c) 2005 Sam Stephenson <sam@conio.net> + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://prototype.conio.net/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.4.0', + ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', + + emptyFunction: function() {}, + K: function(x) {return x} +} + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +} + +var Abstract = new Object(); + +Object.extend = function(destination, source) { + for (property in source) { + destination[property] = source[property]; + } + return destination; +} + +Object.inspect = function(object) { + try { + if (object == undefined) return 'undefined'; + if (object == null) return 'null'; + return object.inspect ? object.inspect() : object.toString(); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } +} + +Function.prototype.bind = function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } +} + +Function.prototype.bindAsEventListener = function(object) { + var __method = this; + return function(event) { + return __method.call(object, event || window.event); + } +} + +Object.extend(Number.prototype, { + toColorPart: function() { + var digits = this.toString(16); + if (this < 16) return '0' + digits; + return digits; + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + } +}); + +var Try = { + these: function() { + var returnValue; + + for (var i = 0; i < arguments.length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) {} + } + + return returnValue; + } +} + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create(); +PeriodicalExecuter.prototype = { + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.callback(); + } finally { + this.currentlyExecuting = false; + } + } + } +} + +/*--------------------------------------------------------------------------*/ + +function $() { + var elements = new Array(); + + for (var i = 0; i < arguments.length; i++) { + var element = arguments[i]; + if (typeof element == 'string') + element = document.getElementById(element); + + if (arguments.length == 1) + return element; + + elements.push(element); + } + + return elements; +} +Object.extend(String.prototype, { + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(eval); + }, + + escapeHTML: function() { + var div = document.createElement('div'); + var text = document.createTextNode(this); + div.appendChild(text); + return div.innerHTML; + }, + + unescapeHTML: function() { + var div = document.createElement('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; + }, + + toQueryParams: function() { + var pairs = this.match(/^\??(.*)$/)[1].split('&'); + return pairs.inject({}, function(params, pairString) { + var pair = pairString.split('='); + params[pair[0]] = pair[1]; + return params; + }); + }, + + toArray: function() { + return this.split(''); + }, + + camelize: function() { + var oStringList = this.split('-'); + if (oStringList.length == 1) return oStringList[0]; + + var camelizedString = this.indexOf('-') == 0 + ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) + : oStringList[0]; + + for (var i = 1, len = oStringList.length; i < len; i++) { + var s = oStringList[i]; + camelizedString += s.charAt(0).toUpperCase() + s.substring(1); + } + + return camelizedString; + }, + + inspect: function() { + return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'"; + } +}); + +String.prototype.parseQuery = String.prototype.toQueryParams; + +var $break = new Object(); +var $continue = new Object(); + +var Enumerable = { + each: function(iterator) { + var index = 0; + try { + this._each(function(value) { + try { + iterator(value, index++); + } catch (e) { + if (e != $continue) throw e; + } + }); + } catch (e) { + if (e != $break) throw e; + } + }, + + all: function(iterator) { + var result = true; + this.each(function(value, index) { + result = result && !!(iterator || Prototype.K)(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator) { + var result = true; + this.each(function(value, index) { + if (result = !!(iterator || Prototype.K)(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator) { + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function (iterator) { + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator) { + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(pattern, iterator) { + var results = []; + this.each(function(value, index) { + var stringValue = value.toString(); + if (stringValue.match(pattern)) + results.push((iterator || Prototype.K)(value, index)); + }) + return results; + }, + + include: function(object) { + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inject: function(memo, iterator) { + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.collect(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (value >= (result || value)) + result = value; + }); + return result; + }, + + min: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (value <= (result || value)) + result = value; + }); + return result; + }, + + partition: function(iterator) { + var trues = [], falses = []; + this.each(function(value, index) { + ((iterator || Prototype.K)(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value, index) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator) { + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator) { + return this.collect(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.collect(Prototype.K); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (typeof args.last() == 'function') + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + iterator(value = collections.pluck(index)); + return value; + }); + }, + + inspect: function() { + return '#<Enumerable:' + this.toArray().inspect() + '>'; + } +} + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray +}); +var $A = Array.from = function(iterable) { + if (!iterable) return []; + if (iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0; i < iterable.length; i++) + results.push(iterable[i]); + return results; + } +} + +Object.extend(Array.prototype, Enumerable); + +Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0; i < this.length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != undefined || value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(value.constructor == Array ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + indexOf: function(object) { + for (var i = 0; i < this.length; i++) + if (this[i] == object) return i; + return -1; + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + shift: function() { + var result = this[0]; + for (var i = 0; i < this.length - 1; i++) + this[i] = this[i + 1]; + this.length--; + return result; + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } +}); +var Hash = { + _each: function(iterator) { + for (key in this) { + var value = this[key]; + if (typeof value == 'function') continue; + + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + merge: function(hash) { + return $H(hash).inject($H(this), function(mergedHash, pair) { + mergedHash[pair.key] = pair.value; + return mergedHash; + }); + }, + + toQueryString: function() { + return this.map(function(pair) { + return pair.map(encodeURIComponent).join('='); + }).join('&'); + }, + + inspect: function() { + return '#<Hash:{' + this.map(function(pair) { + return pair.map(Object.inspect).join(': '); + }).join(', ') + '}>'; + } +} + +function $H(object) { + var hash = Object.extend({}, object || {}); + Object.extend(hash, Enumerable); + Object.extend(hash, Hash); + return hash; +} +ObjectRange = Class.create(); +Object.extend(ObjectRange.prototype, Enumerable); +Object.extend(ObjectRange.prototype, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + do { + iterator(value); + value = value.succ(); + } while (this.include(value)); + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')}, + function() {return new XMLHttpRequest()} + ) || false; + }, + + activeRequestCount: 0 +} + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responderToAdd) { + if (!this.include(responderToAdd)) + this.responders.push(responderToAdd); + }, + + unregister: function(responderToRemove) { + this.responders = this.responders.without(responderToRemove); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (responder[callback] && typeof responder[callback] == 'function') { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) {} + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { + Ajax.activeRequestCount++; + }, + + onComplete: function() { + Ajax.activeRequestCount--; + } +}); + +Ajax.Base = function() {}; +Ajax.Base.prototype = { + setOptions: function(options) { + this.options = { + method: 'post', + asynchronous: true, + parameters: '' + } + Object.extend(this.options, options || {}); + }, + + responseIsSuccess: function() { + return this.transport.status == undefined + || this.transport.status == 0 + || (this.transport.status >= 200 && this.transport.status < 300); + }, + + responseIsFailure: function() { + return !this.responseIsSuccess(); + } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { + initialize: function(url, options) { + this.transport = Ajax.getTransport(); + this.setOptions(options); + this.request(url); + }, + + request: function(url) { + var parameters = this.options.parameters || ''; + if (parameters.length > 0) parameters += '&_='; + + try { + this.url = url; + if (this.options.method == 'get' && parameters.length > 0) + this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; + + Ajax.Responders.dispatch('onCreate', this, this.transport); + + this.transport.open(this.options.method, this.url, + this.options.asynchronous); + + if (this.options.asynchronous) { + this.transport.onreadystatechange = this.onStateChange.bind(this); + setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); + } + + this.setRequestHeaders(); + + var body = this.options.postBody ? this.options.postBody : parameters; + this.transport.send(this.options.method == 'post' ? body : null); + + } catch (e) { + this.dispatchException(e); + } + }, + + setRequestHeaders: function() { + var requestHeaders = + ['X-Requested-With', 'XMLHttpRequest', + 'X-Prototype-Version', Prototype.Version]; + + if (this.options.method == 'post') { + requestHeaders.push('Content-type', + 'application/x-www-form-urlencoded'); + + /* Force "Connection: close" for Mozilla browsers to work around + * a bug where XMLHttpReqeuest sends an incorrect Content-length + * header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType) + requestHeaders.push('Connection', 'close'); + } + + if (this.options.requestHeaders) + requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); + + for (var i = 0; i < requestHeaders.length; i += 2) + this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState != 1) + this.respondToReadyState(this.transport.readyState); + }, + + header: function(name) { + try { + return this.transport.getResponseHeader(name); + } catch (e) {} + }, + + evalJSON: function() { + try { + return eval(this.header('X-JSON')); + } catch (e) {} + }, + + evalResponse: function() { + try { + return eval(this.transport.responseText); + } catch (e) { + this.dispatchException(e); + } + }, + + respondToReadyState: function(readyState) { + var event = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.evalJSON(); + + if (event == 'Complete') { + try { + (this.options['on' + this.transport.status] + || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(transport, json); + } catch (e) { + this.dispatchException(e); + } + + if ((this.header('Content-type') || '').match(/^text\/javascript/i)) + this.evalResponse(); + } + + try { + (this.options['on' + event] || Prototype.emptyFunction)(transport, json); + Ajax.Responders.dispatch('on' + event, this, transport, json); + } catch (e) { + this.dispatchException(e); + } + + /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ + if (event == 'Complete') + this.transport.onreadystatechange = Prototype.emptyFunction; + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Updater = Class.create(); + +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { + initialize: function(container, url, options) { + this.containers = { + success: container.success ? $(container.success) : $(container), + failure: container.failure ? $(container.failure) : + (container.success ? null : $(container)) + } + + this.transport = Ajax.getTransport(); + this.setOptions(options); + + var onComplete = this.options.onComplete || Prototype.emptyFunction; + this.options.onComplete = (function(transport, object) { + this.updateContent(); + onComplete(transport, object); + }).bind(this); + + this.request(url); + }, + + updateContent: function() { + var receiver = this.responseIsSuccess() ? + this.containers.success : this.containers.failure; + var response = this.transport.responseText; + + if (!this.options.evalScripts) + response = response.stripScripts(); + + if (receiver) { + if (this.options.insertion) { + new this.options.insertion(receiver, response); + } else { + Element.update(receiver, response); + } + } + + if (this.responseIsSuccess()) { + if (this.onComplete) + setTimeout(this.onComplete.bind(this), 10); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { + initialize: function(container, url, options) { + this.setOptions(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = {}; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(request) { + if (this.options.decay) { + this.decay = (request.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = request.responseText; + } + this.timer = setTimeout(this.onTimerEvent.bind(this), + this.decay * this.frequency * 1000); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +document.getElementsByClassName = function(className, parentElement) { + var children = ($(parentElement) || document.body).getElementsByTagName('*'); + return $A(children).inject([], function(elements, child) { + if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) + elements.push(child); + return elements; + }); +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Element) { + var Element = new Object(); +} + +Object.extend(Element, { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + Element[Element.visible(element) ? 'hide' : 'show'](element); + } + }, + + hide: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = 'none'; + } + }, + + show: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = ''; + } + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + }, + + update: function(element, html) { + $(element).innerHTML = html.stripScripts(); + setTimeout(function() {html.evalScripts()}, 10); + }, + + getHeight: function(element) { + element = $(element); + return element.offsetHeight; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).include(className); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).add(className); + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).remove(className); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + for (var i = 0; i < element.childNodes.length; i++) { + var node = element.childNodes[i]; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + Element.remove(node); + } + }, + + empty: function(element) { + return $(element).innerHTML.match(/^\s*$/); + }, + + scrollTo: function(element) { + element = $(element); + var x = element.x ? element.x : element.offsetLeft, + y = element.y ? element.y : element.offsetTop; + window.scrollTo(x, y); + }, + + getStyle: function(element, style) { + element = $(element); + var value = element.style[style.camelize()]; + if (!value) { + if (document.defaultView && document.defaultView.getComputedStyle) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css.getPropertyValue(style) : null; + } else if (element.currentStyle) { + value = element.currentStyle[style.camelize()]; + } + } + + if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) + if (Element.getStyle(element, 'position') == 'static') value = 'auto'; + + return value == 'auto' ? null : value; + }, + + setStyle: function(element, style) { + element = $(element); + for (name in style) + element.style[name.camelize()] = style[name]; + }, + + getDimensions: function(element) { + element = $(element); + if (Element.getStyle(element, 'display') != 'none') + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = ''; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = 'none'; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return; + element._overflow = element.style.overflow; + if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') + element.style.overflow = 'hidden'; + }, + + undoClipping: function(element) { + element = $(element); + if (element._overflow) return; + element.style.overflow = element._overflow; + element._overflow = undefined; + } +}); + +var Toggle = new Object(); +Toggle.display = Element.toggle; + +/*--------------------------------------------------------------------------*/ + +Abstract.Insertion = function(adjacency) { + this.adjacency = adjacency; +} + +Abstract.Insertion.prototype = { + initialize: function(element, content) { + this.element = $(element); + this.content = content.stripScripts(); + + if (this.adjacency && this.element.insertAdjacentHTML) { + try { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } catch (e) { + if (this.element.tagName.toLowerCase() == 'tbody') { + this.insertContent(this.contentFromAnonymousTable()); + } else { + throw e; + } + } + } else { + this.range = this.element.ownerDocument.createRange(); + if (this.initializeRange) this.initializeRange(); + this.insertContent([this.range.createContextualFragment(this.content)]); + } + + setTimeout(function() {content.evalScripts()}, 10); + }, + + contentFromAnonymousTable: function() { + var div = document.createElement('div'); + div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>'; + return $A(div.childNodes[0].childNodes[0].childNodes); + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, this.element); + }).bind(this)); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function(fragments) { + fragments.reverse(false).each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.appendChild(fragment); + }).bind(this)); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, + this.element.nextSibling); + }).bind(this)); + } +}); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set(this.toArray().concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set(this.select(function(className) { + return className != classNameToRemove; + }).join(' ')); + }, + + toString: function() { + return this.toArray().join(' '); + } +} + +Object.extend(Element.ClassNames.prototype, Enumerable); +var Field = { + clear: function() { + for (var i = 0; i < arguments.length; i++) + $(arguments[i]).value = ''; + }, + + focus: function(element) { + $(element).focus(); + }, + + present: function() { + for (var i = 0; i < arguments.length; i++) + if ($(arguments[i]).value == '') return false; + return true; + }, + + select: function(element) { + $(element).select(); + }, + + activate: function(element) { + element = $(element); + element.focus(); + if (element.select) + element.select(); + } +} + +/*--------------------------------------------------------------------------*/ + +var Form = { + serialize: function(form) { + var elements = Form.getElements($(form)); + var queryComponents = new Array(); + + for (var i = 0; i < elements.length; i++) { + var queryComponent = Form.Element.serialize(elements[i]); + if (queryComponent) + queryComponents.push(queryComponent); + } + + return queryComponents.join('&'); + }, + + getElements: function(form) { + form = $(form); + var elements = new Array(); + + for (tagName in Form.Element.Serializers) { + var tagElements = form.getElementsByTagName(tagName); + for (var j = 0; j < tagElements.length; j++) + elements.push(tagElements[j]); + } + return elements; + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) + return inputs; + + var matchingInputs = new Array(); + for (var i = 0; i < inputs.length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || + (name && input.name != name)) + continue; + matchingInputs.push(input); + } + + return matchingInputs; + }, + + disable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.blur(); + element.disabled = 'true'; + } + }, + + enable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.disabled = ''; + } + }, + + findFirstElement: function(form) { + return Form.getElements(form).find(function(element) { + return element.type != 'hidden' && !element.disabled && + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + Field.activate(Form.findFirstElement(form)); + }, + + reset: function(form) { + $(form).reset(); + } +} + +Form.Element = { + serialize: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) { + var key = encodeURIComponent(parameter[0]); + if (key.length == 0) return; + + if (parameter[1].constructor != Array) + parameter[1] = [parameter[1]]; + + return parameter[1].map(function(value) { + return key + '=' + encodeURIComponent(value); + }).join('&'); + } + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return parameter[1]; + } +} + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'submit': + case 'hidden': + case 'password': + case 'text': + return Form.Element.Serializers.textarea(element); + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + } + return false; + }, + + inputSelector: function(element) { + if (element.checked) + return [element.name, element.value]; + }, + + textarea: function(element) { + return [element.name, element.value]; + }, + + select: function(element) { + return Form.Element.Serializers[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + }, + + selectOne: function(element) { + var value = '', opt, index = element.selectedIndex; + if (index >= 0) { + opt = element.options[index]; + value = opt.value; + if (!value && !('value' in opt)) + value = opt.text; + } + return [element.name, value]; + }, + + selectMany: function(element) { + var value = new Array(); + for (var i = 0; i < element.length; i++) { + var opt = element.options[i]; + if (opt.selected) { + var optValue = opt.value; + if (!optValue && !('value' in opt)) + optValue = opt.text; + value.push(optValue); + } + } + return [element.name, value]; + } +} + +/*--------------------------------------------------------------------------*/ + +var $F = Form.Element.getValue; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + var elements = Form.getElements(this.element); + for (var i = 0; i < elements.length; i++) + this.registerCallback(elements[i]); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + case 'password': + case 'text': + case 'textarea': + case 'select-one': + case 'select-multiple': + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + + element: function(event) { + return event.target || event.srcElement; + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + event.cancelBubble = true; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0; i < Event.observers.length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.attachEvent)) + name = 'keydown'; + + this._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.detachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + element.detachEvent('on' + name, observer); + } + } +}); + +/* prevent memory leaks in IE */ +Event.observe(window, 'unload', Event.unloadCache, false); +var Position = { + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + p = Element.getStyle(element, 'position'); + if (p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; + }, + + offsetParent: function(element) { + if (element.offsetParent) return element.offsetParent; + if (element == document.body) return element; + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return element; + + return document.body; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + clone: function(source, target) { + source = $(source); + target = $(target); + target.style.position = 'absolute'; + var offsets = this.cumulativeOffset(source); + target.style.top = offsets[1] + 'px'; + target.style.left = offsets[0] + 'px'; + target.style.width = source.offsetWidth + 'px'; + target.style.height = source.offsetHeight + 'px'; + }, + + page: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent==document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } while (element = element.parentNode); + + return [valueL, valueT]; + }, + + clone: function(source, target) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || {}) + + // find page position of source + source = $(source); + var p = Position.page(source); + + // find coordinate system to use + target = $(target); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(target,'position') == 'absolute') { + parent = Position.offsetParent(target); + delta = Position.page(parent); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if(options.setWidth) target.style.width = source.offsetWidth + 'px'; + if(options.setHeight) target.style.height = source.offsetHeight + 'px'; + }, + + absolutize: function(element) { + element = $(element); + if (element.style.position == 'absolute') return; + Position.prepare(); + + var offsets = Position.positionedOffset(element); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px';; + element.style.left = left + 'px';; + element.style.width = width + 'px';; + element.style.height = height + 'px';; + }, + + relativize: function(element) { + element = $(element); + if (element.style.position == 'relative') return; + Position.prepare(); + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + } +} + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned. For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + Position.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return [valueL, valueT]; + } +}
\ No newline at end of file diff --git a/tests/FunctionalTests/selenium/core/scripts/selenium-api.js b/tests/FunctionalTests/selenium/core/scripts/selenium-api.js new file mode 100755 index 00000000..ad0509ee --- /dev/null +++ b/tests/FunctionalTests/selenium/core/scripts/selenium-api.js @@ -0,0 +1,1402 @@ +/* + * Copyright 2004 ThoughtWorks, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +var storedVars = new Object(); + +function Selenium(browserbot) { + /** + * Defines an object that runs Selenium commands. + * + * <h3><a name="locators"></a>Element Locators</h3> + * <p> + * Element Locators tell Selenium which HTML element a command refers to. + * The format of a locator is:</p> + * <blockquote> + * <em>locatorType</em><strong>=</strong><em>argument</em> + * </blockquote> + * + * <p> + * We support the following strategies for locating elements: + * </p> + * <blockquote> + * <dl> + * <dt><strong>identifier</strong>=<em>id</em></dt> + * <dd>Select the element with the specified @id attribute. If no match is + * found, select the first element whose @name attribute is <em>id</em>. + * (This is normally the default; see below.)</dd> + * <dt><strong>id</strong>=<em>id</em></dt> + * <dd>Select the element with the specified @id attribute.</dd> + * + * <dt><strong>name</strong>=<em>name</em></dt> + * <dd>Select the first element with the specified @name attribute.</dd> + * <dd><ul class="first last simple"> + * <li>username</li> + * <li>name=username</li> + * </ul> + * </dd> + * <dd>The name may optionally be followed by one or more <em>element-filters</em>, separated from the name by whitespace. If the <em>filterType</em> is not specified, <strong>value</strong> is assumed.</dd> + * + * <dd><ul class="first last simple"> + * <li>name=flavour value=chocolate</li> + * </ul> + * </dd> + * <dt><strong>dom</strong>=<em>javascriptExpression</em></dt> + * + * <dd> + * + * <dd>Find an element using JavaScript traversal of the HTML Document Object + * Model. DOM locators <em>must</em> begin with "document.". + * <ul class="first last simple"> + * <li>dom=document.forms['myForm'].myDropdown</li> + * <li>dom=document.images[56]</li> + * </ul> + * </dd> + * + * </dd> + * + * <dt><strong>xpath</strong>=<em>xpathExpression</em></dt> + * <dd>Locate an element using an XPath expression. + * <ul class="first last simple"> + * <li>xpath=//img[@alt='The image alt text']</li> + * <li>xpath=//table[@id='table1']//tr[4]/td[2]</li> + * + * </ul> + * </dd> + * <dt><strong>link</strong>=<em>textPattern</em></dt> + * <dd>Select the link (anchor) element which contains text matching the + * specified <em>pattern</em>. + * <ul class="first last simple"> + * <li>link=The link text</li> + * </ul> + * + * </dd> + * </dl> + * </blockquote> + * <p> + * Without an explicit locator prefix, Selenium uses the following default + * strategies: + * </p> + * + * <ul class="simple"> + * <li><strong>dom</strong>, for locators starting with "document."</li> + * <li><strong>xpath</strong>, for locators starting with "//"</li> + * <li><strong>identifier</strong>, otherwise</li> + * </ul> + * + * <h3><a name="element-filters">Element Filters</a></h3> + * <blockquote> + * <p>Element filters can be used with a locator to refine a list of candidate elements. They are currently used only in the 'name' element-locator.</p> + * <p>Filters look much like locators, ie.</p> + * <blockquote> + * <em>filterType</em><strong>=</strong><em>argument</em></blockquote> + * + * <p>Supported element-filters are:</p> + * <p><strong>value=</strong><em>valuePattern</em></p> + * <blockquote> + * Matches elements based on their values. This is particularly useful for refining a list of similarly-named toggle-buttons.</blockquote> + * <p><strong>index=</strong><em>index</em></p> + * <blockquote> + * Selects a single element based on its position in the list (offset from zero).</blockquote> + * </blockquote> + * + * <h3><a name="patterns"></a>String-match Patterns</h3> + * + * <p> + * Various Pattern syntaxes are available for matching string values: + * </p> + * <blockquote> + * <dl> + * <dt><strong>glob:</strong><em>pattern</em></dt> + * <dd>Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a + * kind of limited regular-expression syntax typically used in command-line + * shells. In a glob pattern, "*" represents any sequence of characters, and "?" + * represents any single character. Glob patterns match against the entire + * string.</dd> + * <dt><strong>regexp:</strong><em>regexp</em></dt> + * <dd>Match a string using a regular-expression. The full power of JavaScript + * regular-expressions is available.</dd> + * <dt><strong>exact:</strong><em>string</em></dt> + * + * <dd>Match a string exactly, verbatim, without any of that fancy wildcard + * stuff.</dd> + * </dl> + * </blockquote> + * <p> + * If no pattern prefix is specified, Selenium assumes that it's a "glob" + * pattern. + * </p> + */ + this.browserbot = browserbot; + this.optionLocatorFactory = new OptionLocatorFactory(); + this.page = function() { + return browserbot.getCurrentPage(); + }; +} + +Selenium.createForFrame = function(frame) { + return new Selenium(BrowserBot.createForFrame(frame)); +}; + +Selenium.prototype.reset = function() { + /** + * Clear out all stored variables and select the null (starting) window + */ + storedVars = new Object(); + this.browserbot.selectWindow("null"); +}; + +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.page().findElement(locator); + this.page().clickElement(element); +}; + +Selenium.prototype.doFireEvent = function(locator, eventName) { + /** + * Explicitly simulate an event, to trigger the corresponding "on<em>event</em>" + * handler. + * + * @param locator an <a href="#locators">element locator</a> + * @param eventName the event name, e.g. "focus" or "blur" + */ + var element = this.page().findElement(locator); + triggerEvent(element, eventName, false); +}; + +Selenium.prototype.doKeyPress = function(locator, keycode) { + /** + * Simulates a user pressing and releasing a key. + * + * @param locator an <a href="#locators">element locator</a> + * @param keycode the numeric keycode of the key to be pressed, normally the + * ASCII value of that key. + */ + var element = this.page().findElement(locator); + triggerKeyEvent(element, 'keypress', keycode, true); +}; + +Selenium.prototype.doKeyDown = function(locator, keycode) { + /** + * Simulates a user pressing a key (without releasing it yet). + * + * @param locator an <a href="#locators">element locator</a> + * @param keycode the numeric keycode of the key to be pressed, normally the + * ASCII value of that key. + */ + var element = this.page().findElement(locator); + triggerKeyEvent(element, 'keydown', keycode, true); +}; + +Selenium.prototype.doKeyUp = function(locator, keycode) { + /** + * Simulates a user releasing a key. + * + * @param locator an <a href="#locators">element locator</a> + * @param keycode the numeric keycode of the key to be released, normally the + * ASCII value of that key. + */ + var element = this.page().findElement(locator); + triggerKeyEvent(element, 'keyup', keycode, true); +}; + +Selenium.prototype.doMouseOver = function(locator) { + /** + * Simulates a user hovering a mouse over the specified element. + * + * @param locator an <a href="#locators">element locator</a> + */ + var element = this.page().findElement(locator); + triggerMouseEvent(element, 'mouseover', true); +}; + +Selenium.prototype.doMouseDown = function(locator) { + /** + * Simulates a user pressing the mouse button (without releasing it yet) on + * the specified element. + * + * @param locator an <a href="#locators">element locator</a> + */ + var element = this.page().findElement(locator); + triggerMouseEvent(element, 'mousedown', true); +}; + +Selenium.prototype.doType = function(locator, value) { + /** + * Sets the value of an input field, as though you typed it in. + * + * <p>Can also be used to set the value of combo boxes, check boxes, etc. In these cases, + * value should be the value of the option selected, not the visible text.</p> + * + * @param locator an <a href="#locators">element locator</a> + * @param value the value to type + */ + // TODO fail if it can't be typed into. + var element = this.page().findElement(locator); + this.page().replaceText(element, value); +}; + +Selenium.prototype.findToggleButton = function(locator) { + var element = this.page().findElement(locator); + if (element.checked == null) { + Assert.fail("Element " + locator + " is not a toggle-button."); + } + return element; +} + +Selenium.prototype.doCheck = function(locator) { + /** + * Check a toggle-button (checkbox/radio) + * + * @param locator an <a href="#locators">element locator</a> + */ + this.findToggleButton(locator).checked = true; +}; + +Selenium.prototype.doUncheck = function(locator) { + /** + * Uncheck a toggle-button (checkbox/radio) + * + * @param locator an <a href="#locators">element locator</a> + */ + this.findToggleButton(locator).checked = false; +}; + +Selenium.prototype.doSelect = function(selectLocator, optionLocator) { + /** + * Select an option from a drop-down using an option locator. + * + * <p> + * Option locators provide different ways of specifying options of an HTML + * Select element (e.g. for selecting a specific option, or for asserting + * that the selected option satisfies a specification). There are several + * forms of Select Option Locator. + * </p> + * <dl> + * <dt><strong>label</strong>=<em>labelPattern</em></dt> + * <dd>matches options based on their labels, i.e. the visible text. (This + * is the default.) + * <ul class="first last simple"> + * <li>label=regexp:^[Oo]ther</li> + * </ul> + * </dd> + * <dt><strong>value</strong>=<em>valuePattern</em></dt> + * <dd>matches options based on their values. + * <ul class="first last simple"> + * <li>value=other</li> + * </ul> + * + * + * </dd> + * <dt><strong>id</strong>=<em>id</em></dt> + * + * <dd>matches options based on their ids. + * <ul class="first last simple"> + * <li>id=option1</li> + * </ul> + * </dd> + * <dt><strong>index</strong>=<em>index</em></dt> + * <dd>matches an option based on its index (offset from zero). + * <ul class="first last simple"> + * + * <li>index=2</li> + * </ul> + * </dd> + * </dl> + * <p> + * If no option locator prefix is provided, the default behaviour is to match on <strong>label</strong>. + * </p> + * + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @param optionLocator an option locator (a label by default) + */ + var element = this.page().findElement(selectLocator); + if (!("options" in element)) { + throw new SeleniumError("Specified element is not a Select (has no options)"); + } + var locator = this.optionLocatorFactory.fromLocatorString(optionLocator); + var option = locator.findOption(element); + this.page().selectOption(element, option); +}; + +Selenium.prototype.doAddSelection = function(locator, optionLocator) { + /** + * Add a selection to the set of selected options in a multi-select element using an option locator. + * + * @see #doSelect for details of option locators + * + * @param locator an <a href="#locators">element locator</a> identifying a multi-select box + * @param optionLocator an option locator (a label by default) + */ + var element = this.page().findElement(locator); + if (!("options" in element)) { + throw new SeleniumError("Specified element is not a Select (has no options)"); + } + var locator = this.optionLocatorFactory.fromLocatorString(optionLocator); + var option = locator.findOption(element); + this.page().addSelection(element, option); +}; + +Selenium.prototype.doRemoveSelection = function(locator, optionLocator) { + /** + * Remove a selection from the set of selected options in a multi-select element using an option locator. + * + * @see #doSelect for details of option locators + * + * @param locator an <a href="#locators">element locator</a> identifying a multi-select box + * @param optionLocator an option locator (a label by default) + */ + + var element = this.page().findElement(locator); + if (!("options" in element)) { + throw new SeleniumError("Specified element is not a Select (has no options)"); + } + var locator = this.optionLocatorFactory.fromLocatorString(optionLocator); + var option = locator.findOption(element); + this.page().removeSelection(element, option); +}; + +Selenium.prototype.doSubmit = function(formLocator) { + /** + * Submit the specified form. This is particularly useful for forms without + * submit buttons, e.g. single-input "Search" forms. + * + * @param formLocator an <a href="#locators">element locator</a> for the form you want to submit + */ + var form = this.page().findElement(formLocator); + var actuallySubmit = true; + if (form.onsubmit) { + // apply this to the correct window so alerts are properly handled, even in IE HTA mode + actuallySubmit = form.onsubmit.apply(this.browserbot.getContentWindow()); + } + if (actuallySubmit) { + form.submit(); + } + +}; + +Selenium.prototype.doOpen = function(url) { + /** + * Opens an URL in the test frame. This accepts both relative and absolute + * URLs. + * + * The "open" command waits for the page to load before proceeding, + * ie. the "AndWait" suffix is implicit. + * + * <em>Note</em>: The URL must be on the same domain as the runner HTML + * due to security restrictions in the browser (Same Origin Policy). If you + * need to open an URL on another domain, use the Selenium Server to start a + * new browser session on that domain. + * + * @param url the URL to open; may be relative or absolute + */ + this.browserbot.openLocation(url); + return SELENIUM_PROCESS_WAIT; +}; + +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. + * + * @param windowID the JavaScript window ID of the window to select + */ + this.browserbot.selectWindow(windowID); +}; + +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 + */ + if (isNaN(timeout)) { + throw new SeleniumError("Timeout is not a number: " + timeout); + } + + testLoop.waitForCondition = function () { + var targetWindow = selenium.browserbot.getTargetWindow(windowID); + if (!targetWindow) return false; + if (!targetWindow.location) return false; + if ("about:blank" == targetWindow.location) return false; + if (!targetWindow.document) return false; + if (!targetWindow.document.readyState) return true; + if ('complete' != targetWindow.document.readyState) return false; + return true; + }; + + testLoop.waitForConditionStart = new Date().getTime(); + testLoop.waitForConditionTimeout = 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. + * + */ + this.browserbot.cancelNextConfirmation(); +}; + + +Selenium.prototype.doAnswerOnNextPrompt = function(answer) { + /** + * Instructs Selenium to return the specified answer string in response to + * the next JavaScript prompt [window.prompt()]. + * + * + * @param answer the answer to give in response to the prompt pop-up + */ + this.browserbot.setNextPromptResult(answer); +}; + +Selenium.prototype.doGoBack = function() { + /** + * Simulates the user clicking the "back" button on their browser. + * + */ + this.page().goBack(); +}; + +Selenium.prototype.doRefresh = function() { + /** + * Simulates the user clicking the "Refresh" button on their browser. + * + */ + this.page().refresh(); +}; + +Selenium.prototype.doClose = function() { + /** + * Simulates the user clicking the "close" button in the titlebar of a popup + * window or tab. + */ + this.page().close(); +}; + +Selenium.prototype.isAlertPresent = function() { + /** + * Has an alert occurred? + * + * <p> + * This function never throws an exception + * </p> + * @return boolean true if there is an alert + */ + return this.browserbot.hasAlerts(); +}; +Selenium.prototype.isPromptPresent = function() { + /** + * Has a prompt occurred? + * + * <p> + * This function never throws an exception + * </p> + * @return boolean true if there is a pending prompt + */ + return this.browserbot.hasPrompts(); +}; +Selenium.prototype.isConfirmationPresent = function() { + /** + * Has confirm() been called? + * + * <p> + * This function never throws an exception + * </p> + * @return boolean true if there is a pending confirmation + */ + return this.browserbot.hasConfirmations(); +}; +Selenium.prototype.getAlert = function() { + /** + * Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts. + * + * <p>Getting an alert has the same effect as manually clicking OK. If an + * alert is generated but you do not get/verify it, the next Selenium action + * will fail.</p> + * + * <p>NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert + * dialog.</p> + * + * <p>NOTE: Selenium does NOT support JavaScript alerts that are generated in a + * page's onload() event handler. In this case a visible dialog WILL be + * generated and Selenium will hang until someone manually clicks OK.</p> + * @return string The message of the most recent JavaScript alert + */ + if (!this.browserbot.hasAlerts()) { + Assert.fail("There were no alerts"); + } + return this.browserbot.getNextAlert(); +}; +Selenium.prototype.getAlert.dontCheckAlertsAndConfirms = true; + +Selenium.prototype.getConfirmation = function() { + /** + * Retrieves the message of a JavaScript confirmation dialog generated during + * the previous action. + * + * <p> + * By default, the confirm function will return true, having the same effect + * as manually clicking OK. This can be changed by prior execution of the + * chooseCancelOnNextConfirmation command. If an confirmation is generated + * but you do not get/verify it, the next Selenium action will fail. + * </p> + * + * <p> + * NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible + * dialog. + * </p> + * + * <p> + * NOTE: Selenium does NOT support JavaScript confirmations that are + * generated in a page's onload() event handler. In this case a visible + * dialog WILL be generated and Selenium will hang until you manually click + * OK. + * </p> + * + * @return string the message of the most recent JavaScript confirmation dialog + */ + if (!this.browserbot.hasConfirmations()) { + Assert.fail("There were no confirmations"); + } + return this.browserbot.getNextConfirmation(); +}; +Selenium.prototype.getConfirmation.dontCheckAlertsAndConfirms = true; + +Selenium.prototype.getPrompt = function() { + /** + * Retrieves the message of a JavaScript question prompt dialog generated during + * the previous action. + * + * <p>Successful handling of the prompt requires prior execution of the + * answerOnNextPrompt command. If a prompt is generated but you + * do not get/verify it, the next Selenium action will fail.</p> + * + * <p>NOTE: under Selenium, JavaScript prompts will NOT pop up a visible + * dialog.</p> + * + * <p>NOTE: Selenium does NOT support JavaScript prompts that are generated in a + * page's onload() event handler. In this case a visible dialog WILL be + * generated and Selenium will hang until someone manually clicks OK.</p> + * @return string the message of the most recent JavaScript question prompt + */ + if (! this.browserbot.hasPrompts()) { + Assert.fail("There were no prompts"); + } + return this.browserbot.getNextPrompt(); +}; + +Selenium.prototype.getLocation = function() { + /** Gets the absolute URL of the current page. + * + * @return string the absolute URL of the current page + */ + return this.page().location; +}; + +Selenium.prototype.getTitle = function() { + /** Gets the title of the current page. + * + * @return string the title of the current page + */ + return this.page().title(); +}; + + +Selenium.prototype.getBodyText = function() { + /** + * Gets the entire text of the page. + * @return string the entire text of the page + */ + return this.page().bodyText(); +}; + + +Selenium.prototype.getValue = function(locator) { + /** + * Gets the (whitespace-trimmed) value of an input field (or anything else with a value parameter). + * For checkbox/radio elements, the value will be "on" or "off" depending on + * whether the element is checked or not. + * + * @param locator an <a href="#locators">element locator</a> + * @return string the element value, or "on/off" for checkbox/radio elements + */ + var element = this.page().findElement(locator) + return getInputValue(element).trim(); +} + +Selenium.prototype.getText = function(locator) { + /** + * Gets the text of an element. This works for any element that contains + * text. This command uses either the textContent (Mozilla-like browsers) or + * the innerText (IE-like browsers) of the element, which is the rendered + * text shown to the user. + * + * @param locator an <a href="#locators">element locator</a> + * @return string the text of the element + */ + var element = this.page().findElement(locator); + return getText(element).trim(); +}; + +Selenium.prototype.getEval = function(script) { + /** Gets the result of evaluating the specified JavaScript snippet. The snippet may + * have multiple lines, but only the result of the last line will be returned. + * + * <p>Note that, by default, the snippet will run in the context of the "selenium" + * object itself, so <code>this</code> will refer to the Selenium object, and <code>window</code> will + * refer to the top-level runner test window, not the window of your application.</p> + * + * <p>If you need a reference to the window of your application, you can refer + * to <code>this.browserbot.getCurrentWindow()</code> and if you need to use + * a locator to refer to a single element in your application page, you can + * use <code>this.page().findElement("foo")</code> where "foo" is your locator.</p> + * + * @param script the JavaScript snippet to run + * @return string the results of evaluating the snippet + */ + try { + var result = eval(script); + // Selenium RC doesn't allow returning null + if (null == result) return "null"; + return result; + } catch (e) { + throw new SeleniumError("Threw an exception: " + e.message); + } +}; + +Selenium.prototype.isChecked = function(locator) { + /** + * Gets whether a toggle-button (checkbox/radio) is checked. Fails if the specified element doesn't exist or isn't a toggle-button. + * @param locator an <a href="#locators">element locator</a> pointing to a checkbox or radio button + * @return string either "true" or "false" depending on whether the checkbox is checked + */ + var element = this.page().findElement(locator); + if (element.checked == null) { + throw new SeleniumError("Element " + locator + " is not a toggle-button."); + } + return element.checked; +}; + +Selenium.prototype.getTable = function(tableCellAddress) { + /** + * Gets the text from a cell of a table. The cellAddress syntax + * tableLocator.row.column, where row and column start at 0. + * + * @param tableCellAddress a cell address, e.g. "foo.1.4" + * @return string the text from the specified cell + */ + // This regular expression matches "tableName.row.column" + // For example, "mytable.3.4" + pattern = /(.*)\.(\d+)\.(\d+)/; + + if(!pattern.test(tableCellAddress)) { + throw new SeleniumError("Invalid target format. Correct format is tableName.rowNum.columnNum"); + } + + pieces = tableCellAddress.match(pattern); + + tableName = pieces[1]; + row = pieces[2]; + col = pieces[3]; + + var table = this.page().findElement(tableName); + if (row > table.rows.length) { + Assert.fail("Cannot access row " + row + " - table has " + table.rows.length + " rows"); + } + else if (col > table.rows[row].cells.length) { + Assert.fail("Cannot access column " + col + " - table row has " + table.rows[row].cells.length + " columns"); + } + else { + actualContent = getText(table.rows[row].cells[col]); + return actualContent.trim(); + } + return null; +}; + +Selenium.prototype.assertSelected = function(selectLocator, optionLocator) { + /** + * Verifies that the selected option of a drop-down satisfies the optionSpecifier. + * + * <p>See the select command for more information about option locators.</p> + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @param optionLocator an option locator, typically just an option label (e.g. "John Smith") + */ + var element = this.page().findElement(selectLocator); + var locator = this.optionLocatorFactory.fromLocatorString(optionLocator); + if (element.selectedIndex == -1) + { + Assert.fail("No option selected"); + } + locator.assertSelected(element); +}; + +Selenium.prototype.getSelectedLabels = function(selectLocator) { + /** Gets all option labels (visible text) for selected options in the specified select or multi-select element. + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @return string[] an array of all selected option labels in the specified select drop-down + */ + return this.findSelectedOptionProperties(selectLocator, "text").join(","); +} + +Selenium.prototype.getSelectedLabel = function(selectLocator) { + /** Gets option label (visible text) for selected option in the specified select element. + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @return string the selected option label in the specified select drop-down + */ + return this.findSelectedOptionProperty(selectLocator, "text"); +} + +Selenium.prototype.getSelectedValues = function(selectLocator) { + /** Gets all option values (value attributes) for selected options in the specified select or multi-select element. + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @return string[] an array of all selected option values in the specified select drop-down + */ + return this.findSelectedOptionProperties(selectLocator, "value").join(","); +} + +Selenium.prototype.getSelectedValue = function(selectLocator) { + /** Gets option value (value attribute) for selected option in the specified select element. + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @return string the selected option value in the specified select drop-down + */ + return this.findSelectedOptionProperty(selectLocator, "value"); +} + +Selenium.prototype.getSelectedIndexes = function(selectLocator) { + /** Gets all option indexes (option number, starting at 0) for selected options in the specified select or multi-select element. + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @return string[] an array of all selected option indexes in the specified select drop-down + */ + return this.findSelectedOptionProperties(selectLocator, "index").join(","); +} + +Selenium.prototype.getSelectedIndex = function(selectLocator) { + /** Gets option index (option number, starting at 0) for selected option in the specified select element. + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @return string the selected option index in the specified select drop-down + */ + return this.findSelectedOptionProperty(selectLocator, "index"); +} + +Selenium.prototype.getSelectedIds = function(selectLocator) { + /** Gets all option element IDs for selected options in the specified select or multi-select element. + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @return string[] an array of all selected option IDs in the specified select drop-down + */ + return this.findSelectedOptionProperties(selectLocator, "id").join(","); +} + +Selenium.prototype.getSelectedId = function(selectLocator) { + /** Gets option element ID for selected option in the specified select element. + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @return string the selected option ID in the specified select drop-down + */ + return this.findSelectedOptionProperty(selectLocator, "id"); +} + +Selenium.prototype.isSomethingSelected = function(selectLocator) { + /** Determines whether some option in a drop-down menu is selected. + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @return boolean true if some option has been selected, false otherwise + */ + var element = this.page().findElement(selectLocator); + if (!("options" in element)) { + throw new SeleniumError("Specified element is not a Select (has no options)"); + } + + var selectedOptions = []; + + for (var i = 0; i < element.options.length; i++) { + if (element.options[i].selected) + { + return true; + } + } + return false; +} + +Selenium.prototype.findSelectedOptionProperties = function(locator, property) { + var element = this.page().findElement(locator); + if (!("options" in element)) { + throw new SeleniumError("Specified element is not a Select (has no options)"); + } + + var selectedOptions = []; + + for (var i = 0; i < element.options.length; i++) { + if (element.options[i].selected) + { + var propVal = element.options[i][property]; + if (propVal.replace) { + propVal.replace(/,/g, "\\,"); + } + selectedOptions.push(propVal); + } + } + if (selectedOptions.length == 0) Assert.fail("No option selected"); + return selectedOptions; +} + +Selenium.prototype.findSelectedOptionProperty = function(locator, property) { + var selectedOptions = this.findSelectedOptionProperties(locator, property); + if (selectedOptions.length > 1) { + Assert.fail("More than one selected option!"); + } + return selectedOptions[0]; +} + +Selenium.prototype.getSelectOptions = function(selectLocator) { + /** Gets all option labels in the specified select drop-down. + * + * @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu + * @return string[] an array of all option labels in the specified select drop-down + */ + var element = this.page().findElement(selectLocator); + + var selectOptions = []; + + for (var i = 0; i < element.options.length; i++) { + var option = element.options[i].text.replace(/,/g, "\\,"); + selectOptions.push(option); + } + + return selectOptions.join(","); +}; + + +Selenium.prototype.getAttribute = function(attributeLocator) { + /** + * Gets the value of an element attribute. + * @param attributeLocator an element locator followed by an @ sign and then the name of the attribute, e.g. "foo@bar" + * @return string the value of the specified attribute + */ + var result = this.page().findAttribute(attributeLocator); + if (result == null) { + throw new SeleniumError("Could not find element attribute: " + attributeLocator); + } + return result; +}; + +Selenium.prototype.isTextPresent = function(pattern) { + /** + * Verifies that the specified text pattern appears somewhere on the rendered page shown to the user. + * @param pattern a <a href="#patterns">pattern</a> to match with the text of the page + * @return boolean true if the pattern matches the text, false otherwise + */ + var allText = this.page().bodyText(); + + if(allText == "") { + Assert.fail("Page text not found"); + } else { + var patternMatcher = new PatternMatcher(pattern); + if (patternMatcher.strategy == PatternMatcher.strategies.glob) { + patternMatcher.matcher = new PatternMatcher.strategies.globContains(pattern); + } + return patternMatcher.matches(allText); + } +}; + +Selenium.prototype.isElementPresent = function(locator) { + /** + * Verifies that the specified element is somewhere on the page. + * @param locator an <a href="#locators">element locator</a> + * @return boolean true if the element is present, false otherwise + */ + try { + this.page().findElement(locator); + } catch (e) { + return false; + } + return true; +}; + +Selenium.prototype.isVisible = function(locator) { + /** + * Determines if the specified element is visible. An + * element can be rendered invisible by setting the CSS "visibility" + * property to "hidden", or the "display" property to "none", either for the + * element itself or one if its ancestors. This method will fail if + * the element is not present. + * + * @param locator an <a href="#locators">element locator</a> + * @return boolean true if the specified element is visible, false otherwise + */ + var element; + element = this.page().findElement(locator); + + if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + var visibility = element.style["visibility"]; + else + var visibility = this.findEffectiveStyleProperty(element, "visibility"); + + var _isDisplayed = this._isDisplayed(element); + return (visibility != "hidden" && _isDisplayed); +}; + +Selenium.prototype.findEffectiveStyleProperty = function(element, property) { + var effectiveStyle = this.findEffectiveStyle(element); + var propertyValue = effectiveStyle[property]; + if (propertyValue == 'inherit' && element.parentNode.style) { + return this.findEffectiveStyleProperty(element.parentNode, property); + } + return propertyValue; +}; + +Selenium.prototype._isDisplayed = function(element) { + if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + var display = element.style["display"]; + else + var display = this.findEffectiveStyleProperty(element, "display"); + if (display == "none") return false; + if (element.parentNode.style) { + return this._isDisplayed(element.parentNode); + } + return true; +}; + +Selenium.prototype.findEffectiveStyle = function(element) { + if (element.style == undefined) { + return undefined; // not a styled element + } + var window = this.browserbot.getContentWindow(); + if (window.getComputedStyle) { + // DOM-Level-2-CSS + return window.getComputedStyle(element, null); + } + if (element.currentStyle) { + // non-standard IE alternative + return element.currentStyle; + // TODO: this won't really work in a general sense, as + // currentStyle is not identical to getComputedStyle() + // ... but it's good enough for "visibility" + } + throw new SeleniumError("cannot determine effective stylesheet in this browser"); +}; + +Selenium.prototype.isEditable = function(locator) { + /** + * Determines whether the specified input element is editable, ie hasn't been disabled. + * This method will fail if the specified element isn't an input element. + * + * @param locator an <a href="#locators">element locator</a> + * @return boolean true if the input element is editable, false otherwise + */ + var element = this.page().findElement(locator); + if (element.value == undefined) { + Assert.fail("Element " + locator + " is not an input."); + } + return !element.disabled; +}; + +Selenium.prototype.getAllButtons = function() { + /** Returns the IDs of all buttons on the page. + * + * <p>If a given button has no ID, it will appear as "" in this array.</p> + * + * @return string[] the IDs of all buttons on the page + */ + return this.page().getAllButtons(); +}; + +Selenium.prototype.getAllLinks = function() { + /** Returns the IDs of all links on the page. + * + * <p>If a given link has no ID, it will appear as "" in this array.</p> + * + * @return string[] the IDs of all links on the page + */ + return this.page().getAllLinks(); +}; + +Selenium.prototype.getAllFields = function() { + /** Returns the IDs of all input fields on the page. + * + * <p>If a given field has no ID, it will appear as "" in this array.</p> + * + * @return string[] the IDs of all field on the page + */ + return this.page().getAllFields(); +}; + +Selenium.prototype.getHtmlSource = function() { + /** Returns the entire HTML source between the opening and + * closing "html" tags. + * + * @return string the entire HTML source + */ + return this.page().currentDocument.getElementsByTagName("html")[0].innerHTML; +}; + +Selenium.prototype.doSetCursorPosition = function(locator, position) { + /** + * Moves the text cursor to the specified position in the given input element or textarea. + * This method will fail if the specified element isn't an input element or textarea. + * + * @param locator an <a href="#locators">element locator</a> pointing to an input element or textarea + * @param position the numerical position of the cursor in the field; position should be 0 to move the position to the beginning of the field. You can also set the cursor to -1 to move it to the end of the field. + */ + var element = this.page().findElement(locator); + if (element.value == undefined) { + Assert.fail("Element " + locator + " is not an input."); + } + if (position == -1) { + position = element.value.length; + } + + if( element.setSelectionRange && !browserVersion.isOpera) { + element.focus(); + element.setSelectionRange(/*start*/position,/*end*/position); + } + else if( element.createTextRange ) { + triggerEvent(element, 'focus', false); + var range = element.createTextRange(); + range.collapse(true); + range.moveEnd('character',position); + range.moveStart('character',position); + range.select(); + } +} + +Selenium.prototype.getCursorPosition = function(locator) { + /** + * Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers. + * + * <p>Specifically, if the cursor/selection has been cleared by JavaScript, this command will tend to + * return the position of the last location of the cursor, even though the cursor is now gone from the page. This is filed as <a href="http://jira.openqa.org/browse/SEL-243">SEL-243</a>.</p> + * This method will fail if the specified element isn't an input element or textarea, or there is no cursor in the element. + * + * @param locator an <a href="#locators">element locator</a> pointing to an input element or textarea + * @return number the numerical position of the cursor in the field + */ + var element = this.page().findElement(locator); + var doc = this.page().currentDocument; + var win = this.browserbot.getCurrentWindow(); + if( doc.selection && !browserVersion.isOpera){ + + var selectRange = doc.selection.createRange().duplicate(); + var elementRange = element.createTextRange(); + selectRange.move("character",0); + elementRange.move("character",0); + var inRange1 = selectRange.inRange(elementRange); + var inRange2 = elementRange.inRange(selectRange); + try { + elementRange.setEndPoint("EndToEnd", selectRange); + } catch (e) { + Assert.fail("There is no cursor on this page!"); + } + var answer = String(elementRange.text).replace(/\r/g,"").length; + return answer; + } else { + if (typeof(element.selectionStart) != undefined) { + if (win.getSelection && typeof(win.getSelection().rangeCount) != undefined && win.getSelection().rangeCount == 0) { + Assert.fail("There is no cursor on this page!"); + } + return element.selectionStart; + } + } + throw new Error("Couldn't detect cursor position on this browser!"); +} + + +Selenium.prototype.doSetContext = function(context, logLevelThreshold) { + /** + * Writes a message to the status bar and adds a note to the browser-side + * log. + * + * <p>If logLevelThreshold is specified, set the threshold for logging + * to that level (debug, info, warn, error).</p> + * + * <p>(Note that the browser-side logs will <i>not</i> be sent back to the + * server, and are invisible to the Client Driver.)</p> + * + * @param context + * the message to be sent to the browser + * @param logLevelThreshold one of "debug", "info", "warn", "error", sets the threshold for browser-side logging + */ + if (logLevelThreshold==null || logLevelThreshold=="") { + return this.page().setContext(context); + } + return this.page().setContext(context, logLevelThreshold); +}; + +Selenium.prototype.getExpression = function(expression) { + /** + * Returns the specified expression. + * + * <p>This is useful because of JavaScript preprocessing. + * It is used to generate commands like assertExpression and storeExpression.</p> + * + * @param expression the value to return + * @return string the value passed in + */ + return expression; +} + +Selenium.prototype.doWaitForCondition = function(script, timeout) { + /** + * Runs the specified JavaScript snippet repeatedly until it evaluates to "true". + * The snippet may have multiple lines, but only the result of the last line + * will be considered. + * + * <p>Note that, by default, the snippet will be run in the runner's test window, not in the window + * of your application. To get the window of your application, you can use + * the JavaScript snippet <code>selenium.browserbot.getCurrentWindow()</code>, and then + * run your JavaScript in there</p> + * @param script the JavaScript snippet to run + * @param timeout a timeout in milliseconds, after which this command will return with an error + */ + if (isNaN(timeout)) { + throw new SeleniumError("Timeout is not a number: " + timeout); + } + + testLoop.waitForCondition = function () { + return eval(script); + }; + + testLoop.waitForConditionStart = new Date().getTime(); + testLoop.waitForConditionTimeout = timeout; +}; + +Selenium.prototype.doWaitForCondition.dontCheckAlertsAndConfirms = true; + +Selenium.prototype.doSetTimeout = function(timeout) { + /** + * Specifies the amount of time that Selenium will wait for actions to complete. + * + * <p>Actions that require waiting include "open" and the "waitFor*" actions.</p> + * The default timeout is 30 seconds. + * @param timeout a timeout in milliseconds, after which the action will return with an error + */ + testLoop.waitForConditionTimeout = timeout; +} + +Selenium.prototype.doWaitForPageToLoad = function(timeout) { + /** + * Waits for a new page to load. + * + * <p>You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc. + * (which are only available in the JS API).</p> + * + * <p>Selenium constantly keeps track of new pages loading, and sets a "newPageLoaded" + * flag when it first notices a page load. Running any other Selenium command after + * turns the flag to false. Hence, if you want to wait for a page to load, you must + * wait immediately after a Selenium command that caused a page-load.</p> + * @param timeout a timeout in milliseconds, after which this command will return with an error + */ + this.doWaitForCondition("selenium.browserbot.isNewPageLoaded()", timeout); +}; + +Selenium.prototype.doWaitForPageToLoad.dontCheckAlertsAndConfirms = true; + +/** + * Evaluate a parameter, performing JavaScript evaluation and variable substitution. + * If the string matches the pattern "javascript{ ... }", evaluate the string between the braces. + */ +Selenium.prototype.preprocessParameter = function(value) { + var match = value.match(/^javascript\{((.|\r?\n)+)\}$/); + if (match && match[1]) { + return eval(match[1]).toString(); + } + return this.replaceVariables(value); +}; + +/* + * Search through str and replace all variable references ${varName} with their + * value in storedVars. + */ +Selenium.prototype.replaceVariables = function(str) { + var stringResult = str; + + // Find all of the matching variable references + var match = stringResult.match(/\$\{\w+\}/g); + if (!match) { + return stringResult; + } + + // For each match, lookup the variable value, and replace if found + for (var i = 0; match && i < match.length; i++) { + var variable = match[i]; // The replacement variable, with ${} + var name = variable.substring(2, variable.length - 1); // The replacement variable without ${} + var replacement = storedVars[name]; + if (replacement != undefined) { + stringResult = stringResult.replace(variable, replacement); + } + } + return stringResult; +}; + + +/** + * Factory for creating "Option Locators". + * An OptionLocator is an object for dealing with Select options (e.g. for + * finding a specified option, or asserting that the selected option of + * Select element matches some condition. + * The type of locator returned by the factory depends on the locator string: + * label=<exp> (OptionLocatorByLabel) + * value=<exp> (OptionLocatorByValue) + * index=<exp> (OptionLocatorByIndex) + * id=<exp> (OptionLocatorById) + * <exp> (default is OptionLocatorByLabel). + */ +function OptionLocatorFactory() { +} + +OptionLocatorFactory.prototype.fromLocatorString = function(locatorString) { + var locatorType = 'label'; + var locatorValue = locatorString; + // If there is a locator prefix, use the specified strategy + var result = locatorString.match(/^([a-zA-Z]+)=(.*)/); + if (result) { + locatorType = result[1]; + locatorValue = result[2]; + } + if (this.optionLocators == undefined) { + this.registerOptionLocators(); + } + if (this.optionLocators[locatorType]) { + return new this.optionLocators[locatorType](locatorValue); + } + throw new SeleniumError("Unkown option locator type: " + locatorType); +}; + +/** + * To allow for easy extension, all of the option locators are found by + * searching for all methods of OptionLocatorFactory.prototype that start + * with "OptionLocatorBy". + * TODO: Consider using the term "Option Specifier" instead of "Option Locator". + */ +OptionLocatorFactory.prototype.registerOptionLocators = function() { + this.optionLocators={}; + for (var functionName in this) { + var result = /OptionLocatorBy([A-Z].+)$/.exec(functionName); + if (result != null) { + var locatorName = result[1].lcfirst(); + this.optionLocators[locatorName] = this[functionName]; + } + } +}; + +/** + * OptionLocator for options identified by their labels. + */ +OptionLocatorFactory.prototype.OptionLocatorByLabel = function(label) { + this.label = label; + this.labelMatcher = new PatternMatcher(this.label); + this.findOption = function(element) { + for (var i = 0; i < element.options.length; i++) { + if (this.labelMatcher.matches(element.options[i].text)) { + return element.options[i]; + } + } + throw new SeleniumError("Option with label '" + this.label + "' not found"); + }; + + this.assertSelected = function(element) { + var selectedLabel = element.options[element.selectedIndex].text; + Assert.matches(this.label, selectedLabel) + }; +}; + +/** + * OptionLocator for options identified by their values. + */ +OptionLocatorFactory.prototype.OptionLocatorByValue = function(value) { + this.value = value; + this.valueMatcher = new PatternMatcher(this.value); + this.findOption = function(element) { + for (var i = 0; i < element.options.length; i++) { + if (this.valueMatcher.matches(element.options[i].value)) { + return element.options[i]; + } + } + throw new SeleniumError("Option with value '" + this.value + "' not found"); + }; + + this.assertSelected = function(element) { + var selectedValue = element.options[element.selectedIndex].value; + Assert.matches(this.value, selectedValue) + }; +}; + +/** + * OptionLocator for options identified by their index. + */ +OptionLocatorFactory.prototype.OptionLocatorByIndex = function(index) { + this.index = Number(index); + if (isNaN(this.index) || this.index < 0) { + throw new SeleniumError("Illegal Index: " + index); + } + + this.findOption = function(element) { + if (element.options.length <= this.index) { + throw new SeleniumError("Index out of range. Only " + element.options.length + " options available"); + } + return element.options[this.index]; + }; + + this.assertSelected = function(element) { + Assert.equals(this.index, element.selectedIndex); + }; +}; + +/** + * OptionLocator for options identified by their id. + */ +OptionLocatorFactory.prototype.OptionLocatorById = function(id) { + this.id = id; + this.idMatcher = new PatternMatcher(this.id); + this.findOption = function(element) { + for (var i = 0; i < element.options.length; i++) { + if (this.idMatcher.matches(element.options[i].id)) { + return element.options[i]; + } + } + throw new SeleniumError("Option with id '" + this.id + "' not found"); + }; + + this.assertSelected = function(element) { + var selectedId = element.options[element.selectedIndex].id; + Assert.matches(this.id, selectedId) + }; +}; + + diff --git a/tests/FunctionalTests/selenium/selenium-browserbot.js b/tests/FunctionalTests/selenium/core/scripts/selenium-browserbot.js index bf605f01..8df46865 100644..100755 --- a/tests/FunctionalTests/selenium/selenium-browserbot.js +++ b/tests/FunctionalTests/selenium/core/scripts/selenium-browserbot.js @@ -27,22 +27,7 @@ // 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. - -// Although it's generally better web development practice not to use browser-detection -// (feature detection is better), the subtle browser differences that Selenium has to -// work around seem to make it necessary. Maybe as we learn more about what we need, -// we can do this in a more "feature-centric" rather than "browser-centric" way. -// TODO we should probably reuse an available browser-detection library -var browserName=navigator.appName; -var isIE = (browserName =="Microsoft Internet Explorer"); -var isKonqueror = (browserName == "Konqueror"); -var isSafari = (navigator.userAgent.indexOf('Safari') != -1); - -// Get the Gecko version as an 8 digit date. -var geckoResult = /^Mozilla\/5\.0 .*Gecko\/(\d{8}).*$/.exec(navigator.userAgent); -var geckoVersion = geckoResult == null ? null : geckoResult[1]; - -BrowserBot = function(frame) { +var BrowserBot = function(frame) { this.frame = frame; this.currentPage = null; this.currentWindowName = null; @@ -55,31 +40,42 @@ BrowserBot = function(frame) { this.nextConfirmResult = true; this.nextPromptResult = ''; this.newPageLoaded = false; + this.pageLoadError = null; var self = this; this.recordPageLoad = function() { - LOG.debug("Page load detected, location=" + self.getCurrentWindow().location); + LOG.debug("Page load detected"); + try { + LOG.debug("Page load location=" + self.getCurrentWindow().location); + } catch (e) { + self.pageLoadError = e; + return; + } self.currentPage = null; self.newPageLoaded = true; }; this.isNewPageLoaded = function() { + if (this.pageLoadError) throw this.pageLoadError; return self.newPageLoaded; }; }; BrowserBot.createForFrame = function(frame) { var browserbot; - if (isIE) { + LOG.debug("browserName: " + browserVersion.name); + LOG.debug("userAgent: " + navigator.userAgent); + if (browserVersion.isIE) { browserbot = new IEBrowserBot(frame); } - else if (isKonqueror) { + else if (browserVersion.isKonqueror) { browserbot = new KonquerorBrowserBot(frame); } - else if (isSafari) { + else if (browserVersion.isSafari) { browserbot = new SafariBrowserBot(frame); } else { + LOG.info("Using MozillaBrowserBot") // Use mozilla by default browserbot = new MozillaBrowserBot(frame); } @@ -146,13 +142,17 @@ BrowserBot.prototype.openLocation = function(target) { this.currentPage = null; this.newPageLoaded = false; - this.setIFrameLocation(this.getFrame(), target); + this.setOpenLocation(target); }; BrowserBot.prototype.setIFrameLocation = function(iframe, location) { iframe.src = location; }; +BrowserBot.prototype.setOpenLocation = function(location) { + this.getCurrentWindow().location.href = location; +}; + BrowserBot.prototype.getCurrentPage = function() { if (this.currentPage == null) { var testWindow = this.getCurrentWindow(); @@ -210,21 +210,66 @@ BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(window /** * Call the supplied function when a the current page unloads and a new one loads. - * This is done with an "unload" handler which attaches a "load" handler. + * This is done by polling continuously until the document changes and is fully loaded. */ BrowserBot.prototype.callOnWindowPageTransition = function(loadFunction, windowObject) { - var attachLoadListener = function() { - if (windowObject && !windowObject.closed) { - addLoadListener(windowObject, loadFunction); - } - }; + // Since the unload event doesn't fire in Safari 1.3, we start polling immediately + if (windowObject && !windowObject.closed) { + LOG.debug("Starting pollForLoad: " + windowObject.document.location); + this.pollingForLoad = true; + this.pollForLoad(loadFunction, windowObject, windowObject.location, windowObject.location.href); + } +}; - var unloadFunction = function() { - window.setTimeout(attachLoadListener, 0); - }; - addUnloadListener(windowObject, unloadFunction); +/** + * 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, originalLocation, originalHref) { + var windowClosed = true; + try { + windowClosed = windowObject.closed; + } catch (e) { + LOG.debug("exception detecting closed window (I guess it must be closed)"); + LOG.exception(e); + // swallow exceptions which may occur in HTA mode when the window is closed + } + if (null == windowClosed) windowClosed = true; + if (windowClosed) { + this.pollingForLoad = false; + return; + } + + LOG.debug("pollForLoad original: " + originalHref); + try { + + var currentLocation = windowObject.location; + var currentHref = currentLocation.href + + var sameLoc = (originalLocation === currentLocation); + var sameHref = (originalHref === currentHref); + var rs = windowObject.document.readyState; + + if (rs == null) rs = 'complete'; + + if (!(sameLoc && sameHref) && rs == 'complete') { + LOG.debug("pollForLoad complete: " + rs + " (" + currentHref + ")"); + loadFunction(); + this.pollingForLoad = false; + return; + } + var self = this; + LOG.debug("pollForLoad continue: " + currentHref); + window.setTimeout(function() {self.pollForLoad(loadFunction, windowObject, originalLocation, originalHref);}, 500); + } catch (e) { + LOG.error("Exception during pollForLoad; this should get noticed soon!"); + LOG.exception(e); + this.pageLoadError = e; + } }; + BrowserBot.prototype.getContentWindow = function() { return this.getFrame().contentWindow || frames[this.getFrame().id]; }; @@ -268,41 +313,11 @@ KonquerorBrowserBot.prototype.setIFrameLocation = function(iframe, location) { iframe.src = location; }; -/** - * 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. - */ -KonquerorBrowserBot.prototype.callOnWindowPageTransition = function(loadFunction, windowObject) { - // Since the unload event doesn't fire in Safari 1.3, we start polling immediately - // This works in Konqueror as well - if (windowObject && !windowObject.closed) { - LOG.debug("Starting pollForLoad"); - this.pollForLoad(loadFunction, windowObject, windowObject.document); - } -}; - -/** - * For Konqueror (and Safari), we can't catch the onload event for a separate window (as opposed to an IFrame) - * So we 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 check to see that the completed document - * is different from the original one. - */ -KonquerorBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument) { - if (windowObject.closed) { - return; - } - - var sameDoc = (originalDocument === windowObject.document); - var rs = windowObject.document.readyState; - - if (!sameDoc && rs == 'complete') { - LOG.debug("pollForLoad complete: " + rs + " (" + sameDoc + ")"); - loadFunction(); - return; - } - var self = this; - LOG.debug("pollForLoad continue"); - window.setTimeout(function() {self.pollForLoad(loadFunction, windowObject, originalDocument);}, 500); +KonquerorBrowserBot.prototype.setOpenLocation = function(location) { + // Window doesn't fire onload event when setting src to the current value, + // so we set it to blank first. + this.getCurrentWindow().location.href = "about:blank"; + this.getCurrentWindow().location.href = location; }; function SafariBrowserBot(frame) { @@ -311,15 +326,11 @@ function SafariBrowserBot(frame) { SafariBrowserBot.prototype = new BrowserBot; SafariBrowserBot.prototype.setIFrameLocation = KonquerorBrowserBot.prototype.setIFrameLocation; -SafariBrowserBot.prototype.callOnWindowPageTransition = KonquerorBrowserBot.prototype.callOnWindowPageTransition; -SafariBrowserBot.prototype.pollForLoad = KonquerorBrowserBot.prototype.pollForLoad; function IEBrowserBot(frame) { BrowserBot.call(this, frame); } IEBrowserBot.prototype = new BrowserBot; -IEBrowserBot.prototype.callOnWindowPageTransition = KonquerorBrowserBot.prototype.callOnWindowPageTransition; -IEBrowserBot.prototype.pollForLoad = KonquerorBrowserBot.prototype.pollForLoad; IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) { BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot); @@ -370,7 +381,7 @@ SafariBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToM }; }; -PageBot = function(pageWindow) { +var PageBot = function(pageWindow) { if (pageWindow) { this.currentWindow = pageWindow; this.currentDocument = pageWindow.document; @@ -399,7 +410,7 @@ PageBot = function(pageWindow) { * Find a locator based on a prefix. */ this.findElementBy = function(locatorType, locator, inDocument) { - var locatorFunction = this.locationStrategies[locatorType]; + var locatorFunction = this.locationStrategies[locatorType]; if (! locatorFunction) { throw new SeleniumError("Unrecognised locator type: '" + locatorType + "'"); } @@ -412,58 +423,67 @@ PageBot = function(pageWindow) { this.locationStrategies['implicit'] = function(locator, inDocument) { if (locator.startsWith('//')) { return this.locateElementByXPath(locator, inDocument); - } + } if (locator.startsWith('document.')) { return this.locateElementByDomTraversal(locator, inDocument); } return this.locateElementByIdentifier(locator, inDocument); }; - + }; PageBot.createForWindow = function(windowObject) { - if (isIE) { + if (browserVersion.isIE) { return new IEPageBot(windowObject); } - else if (isKonqueror) { + else if (browserVersion.isKonqueror) { return new KonquerorPageBot(windowObject); } - else if (isSafari) { + else if (browserVersion.isSafari) { return new SafariPageBot(windowObject); } + else if (browserVersion.isOpera) { + return new OperaPageBot(windowObject); + } else { + LOG.info("Using MozillaPageBot") // Use mozilla by default return new MozillaPageBot(windowObject); } }; -MozillaPageBot = function(pageWindow) { +var MozillaPageBot = function(pageWindow) { PageBot.call(this, pageWindow); }; MozillaPageBot.prototype = new PageBot(); -KonquerorPageBot = function(pageWindow) { +var KonquerorPageBot = function(pageWindow) { PageBot.call(this, pageWindow); }; KonquerorPageBot.prototype = new PageBot(); -SafariPageBot = function(pageWindow) { +var SafariPageBot = function(pageWindow) { PageBot.call(this, pageWindow); }; SafariPageBot.prototype = new PageBot(); -IEPageBot = function(pageWindow) { +var IEPageBot = function(pageWindow) { PageBot.call(this, pageWindow); }; IEPageBot.prototype = new PageBot(); +OperaPageBot = function(pageWindow) { + PageBot.call(this, pageWindow); +}; +OperaPageBot.prototype = new PageBot(); + /* * Finds an element on the current page, using various lookup protocols */ PageBot.prototype.findElement = function(locator) { 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) { @@ -572,15 +592,15 @@ PageBot.prototype.locateElementByXPath = function(xpath, inDocument) { } // Handle //tag - var match = xpath.match(/^\/\/(\w+|\*)$/); + var match = xpath.match(/^\/\/(\w+|\*)$/); if (match) { - var elements = inDocument.getElementsByTagName(match[1].toUpperCase()); + 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+)=('([^\']+)'|"([^\"]+)")\]$/); + var match = xpath.match(/^\/\/(\w+|\*)\[@(\w+)=('([^\']+)'|"([^\"]+)")\]$/); if (match) { return this.findElementByTagNameAndAttributeValue( inDocument, @@ -591,7 +611,7 @@ PageBot.prototype.locateElementByXPath = function(xpath, inDocument) { } // Handle //tag[text()='value'] - var match = xpath.match(/^\/\/(\w+|\*)\[text\(\)=('([^\']+)'|"([^\"]+)")\]$/); + var match = xpath.match(/^\/\/(\w+|\*)\[text\(\)=('([^\']+)'|"([^\"]+)")\]$/); if (match) { return this.findElementByTagNameAndText( inDocument, @@ -606,47 +626,43 @@ PageBot.prototype.locateElementByXPath = function(xpath, inDocument) { PageBot.prototype.findElementByTagNameAndAttributeValue = function( inDocument, tagName, attributeName, attributeValue ) { - if (isIE && attributeName == "class") { + if (browserVersion.isIE && attributeName == "class") { attributeName = "className"; } - var elements = inDocument.getElementsByTagName(tagName); - for (var i = 0; i < elements.length; i++) { - var elementAttr = elements[i].getAttribute(attributeName); - if (elementAttr == attributeValue) { - return elements[i]; - } - } + var elements = inDocument.getElementsByTagName(tagName); + for (var i = 0; i < elements.length; i++) { + var elementAttr = elements[i].getAttribute(attributeName); + if (elementAttr == attributeValue) { + return elements[i]; + } + } return null; }; PageBot.prototype.findElementByTagNameAndText = function( inDocument, tagName, text ) { - var elements = inDocument.getElementsByTagName(tagName); - for (var i = 0; i < elements.length; i++) { - if (getText(elements[i]) == text) { - return elements[i]; - } - } + var elements = inDocument.getElementsByTagName(tagName); + for (var i = 0; i < elements.length; i++) { + if (getText(elements[i]) == text) { + return elements[i]; + } + } return null; }; PageBot.prototype.findElementUsingFullXPath = function(xpath, inDocument) { - if (isIE && !inDocument.evaluate) { - addXPathSupport(inDocument); - } - // Use document.evaluate() if it's available if (inDocument.evaluate) { return inDocument.evaluate(xpath, inDocument, null, 0, null).iterateNext(); } // If not, fall back to slower JavaScript implementation - var context = new XPathContext(); - context.expressionContextNode = inDocument; - var xpathResult = new XPathParser().parse(xpath).evaluate(context); - if (xpathResult && xpathResult.toArray) { - return xpathResult.toArray()[0]; + var context = new ExprContext(inDocument); + var xpathObj = xpathParse(xpath); + var xpathResult = xpathObj.evaluate(context); + if (xpathResult && xpathResult.value) { + return xpathResult.value[0]; } return null; }; @@ -681,7 +697,7 @@ PageBot.prototype.findAttribute = function(locator) { var element = this.findElement(elementLocator); // Handle missing "class" attribute in IE. - if (isIE && attributeName == "class") { + if (browserVersion.isIE && attributeName == "class") { attributeName = "className"; } @@ -694,7 +710,32 @@ PageBot.prototype.findAttribute = function(locator) { /* * Select the specified option and trigger the relevant events of the element. */ -PageBot.prototype.selectOption = function(element, option) { +PageBot.prototype.selectOption = function(element, optionToSelect) { + triggerEvent(element, 'focus', false); + var changed = false; + for (var i = 0; i < element.options.length; i++) { + var option = element.options[i]; + if (option.selected && option != optionToSelect) { + option.selected = false; + changed = true; + } + else if (!option.selected && option == optionToSelect) { + option.selected = true; + changed = true; + } + } + + if (changed) { + triggerEvent(element, 'change', true); + } + triggerEvent(element, 'blur', false); +}; + +/* +* Select the specified option and trigger the relevant events of the element. +*/ +PageBot.prototype.addSelection = function(element, option) { + this.checkMultiselect(element); triggerEvent(element, 'focus', false); if (!option.selected) { option.selected = true; @@ -703,11 +744,35 @@ PageBot.prototype.selectOption = function(element, option) { triggerEvent(element, 'blur', false); }; +/* +* Select the specified option and trigger the relevant events of the element. +*/ +PageBot.prototype.removeSelection = function(element, option) { + this.checkMultiselect(element); + triggerEvent(element, 'focus', false); + if (option.selected) { + option.selected = false; + triggerEvent(element, 'change', true); + } + triggerEvent(element, 'blur', false); +}; + +PageBot.prototype.checkMultiselect = function(element) { + if (!element.multiple) + { + throw new SeleniumError("Not a multi-select"); + } + +}; + PageBot.prototype.replaceText = function(element, stringValue) { triggerEvent(element, 'focus', false); triggerEvent(element, 'select', true); element.value=stringValue; - triggerEvent(element, 'change', true); + if (!browserVersion.isChrome) { + // In chrome URL, The change event is already fired by setting the value. + triggerEvent(element, 'change', true); + } triggerEvent(element, 'blur', false); }; @@ -718,18 +783,15 @@ MozillaPageBot.prototype.clickElement = function(element) { // Add an event listener that detects if the default action has been prevented. // (This is caused by a javascript onclick handler returning false) var preventDefault = false; - if (geckoVersion) { - element.addEventListener("click", function(evt) {preventDefault = evt.getPreventDefault();}, false); - } - + + element.addEventListener("click", function(evt) {preventDefault = evt.getPreventDefault();}, false); + // Trigger the click event. triggerMouseEvent(element, 'click', true); - // In FireFox < 1.0 Final, and Mozilla <= 1.7.3, just sending the click event is enough. - // But in newer versions, we need to do it ourselves. - var needsProgrammaticClick = (geckoVersion > '20041025'); // Perform the link action if preventDefault was set. - if (needsProgrammaticClick && !preventDefault) { + // In chrome URL, the link action is already executed by triggerMouseEvent. + if (!browserVersion.isChrome && !preventDefault) { // Try the element itself, as well as it's parent - this handles clicking images inside links. if (element.href) { this.currentWindow.location.href = element.href; @@ -746,6 +808,30 @@ MozillaPageBot.prototype.clickElement = function(element) { triggerEvent(element, 'blur', false); }; +OperaPageBot.prototype.clickElement = function(element) { + + triggerEvent(element, 'focus', false); + + // Trigger the click event. + triggerMouseEvent(element, 'click', true); + + if (isDefined(element.checked)) { + // In Opera, clicking won't check/uncheck + if (element.type == "checkbox") { + element.checked = !element.checked; + } else { + element.checked = true; + } + } + + if (this.windowClosed()) { + return; + } + + triggerEvent(element, 'blur', false); +}; + + KonquerorPageBot.prototype.clickElement = function(element) { triggerEvent(element, 'focus', false); @@ -769,14 +855,14 @@ SafariPageBot.prototype.clickElement = function(element) { triggerEvent(element, 'focus', false); var wasChecked = element.checked; - + // For form element it is simple. if (element.click) { element.click(); } // For links and other elements, event emulation is required. else { - triggerEvent(element, 'click', true); + triggerMouseEvent(element, 'click', true); // Unfortunately, triggering the event doesn't seem to activate onclick handlers. // We currently call onclick for the link, but I'm guessing that the onclick for containing @@ -809,11 +895,6 @@ SafariPageBot.prototype.clickElement = function(element) { } } - // Onchange event is not triggered automatically in Safari. - if (isDefined(element.checked) && wasChecked != element.checked) { - triggerEvent(element, 'change', true); - } - if (this.windowClosed()) { return; } @@ -914,9 +995,12 @@ PageBot.prototype.getAllLinks = function() { return result; }; -PageBot.prototype.setContext = function(strContext) { +PageBot.prototype.setContext = function(strContext, logLevel) { //set the current test title - context.innerHTML=strContext; + document.getElementById("context").innerHTML=strContext; + if (logLevel!=null) { + LOG.setLogLevelThreshold(logLevel); + } }; function isDefined(value) { @@ -925,6 +1009,10 @@ function isDefined(value) { PageBot.prototype.goBack = function() { this.currentWindow.history.back(); + if (browserVersion.isOpera && !selenium.browserbot.pollingForLoad) { + // DGF On Opera, goBack doesn't re-trigger a load event, so we have to poll for it + selenium.browserbot.callOnWindowPageTransition(selenium.browserbot.recordPageLoad, this.currentWindow); + } }; PageBot.prototype.goForward = function() { @@ -932,7 +1020,15 @@ PageBot.prototype.goForward = function() { }; PageBot.prototype.close = function() { - this.currentWindow.close(); + if (browserVersion.isChrome) { + this.currentWindow.close(); + } else { + this.currentWindow.eval("window.close();"); + } +}; + +PageBot.prototype.refresh = function() { + this.currentWindow.location.reload(true); }; /** @@ -994,3 +1090,25 @@ PageBot.prototype.selectElements = function(filterExpr, elements, defaultFilterT return this.selectElementsBy(filterType, filterExpr, elements); }; +/** + * Find an element by class + */ +PageBot.prototype.locateElementByClass = function(locator, document) { + return Element.findFirstMatchingChild(document, + function(element) { + return element.className == locator + } + ); +} + +/** + * Find an element by alt + */ +PageBot.prototype.locateElementByAlt = function(locator, document) { + return Element.findFirstMatchingChild(document, + function(element) { + return element.alt == locator + } + ); +} + diff --git a/tests/FunctionalTests/selenium/core/scripts/selenium-browserdetect.js b/tests/FunctionalTests/selenium/core/scripts/selenium-browserdetect.js new file mode 100755 index 00000000..137a1518 --- /dev/null +++ b/tests/FunctionalTests/selenium/core/scripts/selenium-browserdetect.js @@ -0,0 +1,115 @@ +/* +* Copyright 2004 ThoughtWorks, Inc +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*/ + +// Although it's generally better web development practice not to use browser-detection +// (feature detection is better), the subtle browser differences that Selenium has to +// work around seem to make it necessary. Maybe as we learn more about what we need, +// we can do this in a more "feature-centric" rather than "browser-centric" way. + +BrowserVersion = function() { + this.name = navigator.appName; + + if (window.opera != null) + { + this.browser = BrowserVersion.OPERA; + this.isOpera = true; + return; + } + + var self = this; + + var checkChrome = function() { + var loc = window.document.location.href; + try { + loc = window.top.document.location.href; + } catch (e) { + // can't see the top (that means we might be chrome, but it's impossible to be sure) + self.isChromeDetectable = "no, top location couldn't be read in this window"; + } + + if (/^chrome:\/\//.test(loc)) { + self.isChrome = true; + } else { + self.isChrome = false; + } + } + + if (this.name == "Microsoft Internet Explorer") + { + this.browser = BrowserVersion.IE; + this.isIE = true; + if (window.top.SeleniumHTARunner && window.top.document.location.pathname.match(/.hta$/i)) { + this.isHTA = true; + } + if ("0" == navigator.appMinorVersion) { + this.preSV1 = true; + } + return; + } + + if (navigator.userAgent.indexOf('Safari') != -1) + { + this.browser = BrowserVersion.SAFARI; + this.isSafari = true; + this.khtml = true; + return; + } + + if (navigator.userAgent.indexOf('Konqueror') != -1) + { + this.browser = BrowserVersion.KONQUEROR; + this.isKonqueror = true; + this.khtml = true; + return; + } + + if (navigator.userAgent.indexOf('Firefox') != -1) + { + this.browser = BrowserVersion.FIREFOX; + this.isFirefox = true; + this.isGecko = true; + var result = /.*Firefox\/([\d\.]+).*/.exec(navigator.userAgent); + if (result) + { + this.firefoxVersion = result[1]; + } + checkChrome(); + return; + } + + if (navigator.userAgent.indexOf('Gecko') != -1) + { + this.browser = BrowserVersion.MOZILLA; + this.isMozilla = true; + this.isGecko = true; + checkChrome(); + return; + } + + this.browser = BrowserVersion.UNKNOWN; +} + +BrowserVersion.OPERA = "Opera"; +BrowserVersion.IE = "IE"; +BrowserVersion.KONQUEROR = "Konqueror"; +BrowserVersion.SAFARI = "Safari"; +BrowserVersion.FIREFOX = "Firefox"; +BrowserVersion.MOZILLA = "Mozilla"; +BrowserVersion.UNKNOWN = "Unknown"; + +browserVersion = new BrowserVersion(); + diff --git a/tests/FunctionalTests/selenium/core/scripts/selenium-commandhandlers.js b/tests/FunctionalTests/selenium/core/scripts/selenium-commandhandlers.js new file mode 100755 index 00000000..ee01ea76 --- /dev/null +++ b/tests/FunctionalTests/selenium/core/scripts/selenium-commandhandlers.js @@ -0,0 +1,371 @@ +/* +* Copyright 2004 ThoughtWorks, Inc +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*/ +function CommandHandlerFactory() { + this.actions = {}; + this.asserts = {}; + this.accessors = {}; + + var self = this; + + this.registerAction = function(name, action, wait, dontCheckAlertsAndConfirms) { + var handler = new ActionHandler(action, wait, dontCheckAlertsAndConfirms); + this.actions[name] = handler; + }; + + this.registerAccessor = function(name, accessor) { + var handler = new AccessorHandler(accessor); + this.accessors[name] = handler; + }; + + this.registerAssert = function(name, assertion, haltOnFailure) { + var handler = new AssertHandler(assertion, haltOnFailure); + this.asserts[name] = handler; + }; + + this.getCommandHandler = function(name) { + return this.actions[name] || this.accessors[name] || this.asserts[name] || null; + }; + + this.registerAll = function(commandObject) { + registerAllAccessors(commandObject); + registerAllActions(commandObject); + registerAllAsserts(commandObject); + }; + + var registerAllActions = function(commandObject) { + for (var functionName in commandObject) { + var result = /^do([A-Z].+)$/.exec(functionName); + if (result != null) { + var actionName = result[1].lcfirst(); + + // Register the action without the wait flag. + var action = commandObject[functionName]; + self.registerAction(actionName, action, false, action.dontCheckAlertsAndConfirms); + + // Register actionName + "AndWait" with the wait flag; + var waitActionName = actionName + "AndWait"; + self.registerAction(waitActionName, action, true, action.dontCheckAlertsAndConfirms); + } + } + }; + + + var registerAllAsserts = function(commandObject) { + for (var functionName in commandObject) { + var result = /^assert([A-Z].+)$/.exec(functionName); + if (result != null) { + var assert = commandObject[functionName]; + + // Register the assert with the "assert" prefix, and halt on failure. + var assertName = functionName; + self.registerAssert(assertName, assert, true); + + // Register the assert with the "verify" prefix, and do not halt on failure. + var verifyName = "verify" + result[1]; + self.registerAssert(verifyName, assert, false); + } + } + }; + + + // Given an accessor function getBlah(target), + // return a "predicate" equivalient to isBlah(target, value) that + // is true when the value returned by the accessor matches the specified value. + this.createPredicateFromSingleArgAccessor = function(accessor) { + return function(target, value) { + var accessorResult = accessor.call(this, target); + if (PatternMatcher.matches(value, accessorResult)) { + return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'"); + } else { + return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'"); + } + }; + }; + + // Given a (no-arg) accessor function getBlah(), + // return a "predicate" equivalient to isBlah(value) that + // is true when the value returned by the accessor matches the specified value. + this.createPredicateFromNoArgAccessor = function(accessor) { + return function(value) { + var accessorResult = accessor.call(this); + if (PatternMatcher.matches(value, accessorResult)) { + return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'"); + } else { + return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'"); + } + }; + }; + + // Given a boolean accessor function isBlah(), + // return a "predicate" equivalient to isBlah() that + // returns an appropriate PredicateResult value. + this.createPredicateFromBooleanAccessor = function(accessor) { + return function() { + var accessorResult; + if (arguments.length > 2) throw new SeleniumError("Too many arguments! " + arguments.length); + if (arguments.length == 2) { + accessorResult = accessor.call(this, arguments[0], arguments[1]); + } else if (arguments.length == 1) { + accessorResult = accessor.call(this, arguments[0]); + } else { + accessorResult = accessor.call(this); + } + if (accessorResult) { + return new PredicateResult(true, "true"); + } else { + return new PredicateResult(false, "false"); + } + }; + }; + + // Given an accessor fuction getBlah([target]) (target is optional) + // return a predicate equivalent to isBlah([target,] value) that + // is true when the value returned by the accessor matches the specified value. + this.createPredicateFromAccessor = function(accessor) { + if (accessor.length == 0) { + return self.createPredicateFromNoArgAccessor(accessor); + } + return self.createPredicateFromSingleArgAccessor(accessor); + }; + + // Given a predicate, return the negation of that predicate. + // Leaves the message unchanged. + // Used to create assertNot, verifyNot, and waitForNot commands. + this.invertPredicate = function(predicate) { + return function(target, value) { + var result = predicate.call(this, target, value); + result.isTrue = ! result.isTrue; + return result; + }; + }; + + // Convert an isBlahBlah(target, value) function into an assertBlahBlah(target, value) function. + this.createAssertionFromPredicate = function(predicate) { + return function(target, value) { + var result = predicate.call(this, target, value); + if (!result.isTrue) { + Assert.fail(result.message); + } + }; + }; + + // Register an assertion, a verification, a negative assertion, + // and a negative verification based on the specified accessor. + this.registerAssertionsBasedOnAccessor = function(accessor, baseName, predicate) { + if (predicate==null) { + predicate = self.createPredicateFromAccessor(accessor); + } + var assertion = self.createAssertionFromPredicate(predicate); + // Register an assert with the "assert" prefix, and halt on failure. + self.registerAssert("assert" + baseName, assertion, true); + // Register a verify with the "verify" prefix, and do not halt on failure. + self.registerAssert("verify" + baseName, assertion, false); + + var invertedPredicate = self.invertPredicate(predicate); + var negativeAssertion = self.createAssertionFromPredicate(invertedPredicate); + + var result = /^(.*)Present$/.exec(baseName); + if (result==null) { + // Register an assertNot with the "assertNot" prefix, and halt on failure. + self.registerAssert("assertNot"+baseName, negativeAssertion, true); + // Register a verifyNot with the "verifyNot" prefix, and do not halt on failure. + self.registerAssert("verifyNot"+baseName, negativeAssertion, false); + } + else { + var invertedBaseName = result[1] + "NotPresent"; + + // Register an assertNot ending w/ "NotPresent", and halt on failure. + self.registerAssert("assert"+invertedBaseName, negativeAssertion, true); + // Register an assertNot ending w/ "NotPresent", and do not halt on failure. + self.registerAssert("verify"+invertedBaseName, negativeAssertion, false); + } + }; + + + // Convert an isBlahBlah(target, value) function into a waitForBlahBlah(target, value) function. + this.createWaitForActionFromPredicate = function(predicate) { + var action = function(target, value) { + var seleniumApi = this; + testLoop.waitForCondition = function () { + try { + return predicate.call(seleniumApi, 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 action; + }; + + // Register a waitForBlahBlah and waitForNotBlahBlah based on the specified accessor. + this.registerWaitForCommandsBasedOnAccessor = function(accessor, baseName, predicate) { + if (predicate==null) { + predicate = self.createPredicateFromAccessor(accessor); + } + var waitForAction = self.createWaitForActionFromPredicate(predicate); + self.registerAction("waitFor"+baseName, waitForAction, false, true); + var invertedPredicate = self.invertPredicate(predicate); + var waitForNotAction = self.createWaitForActionFromPredicate(invertedPredicate); + self.registerAction("waitForNot"+baseName, waitForNotAction, false, true); + } + + // Register a storeBlahBlah based on the specified accessor. + this.registerStoreCommandBasedOnAccessor = function(accessor, baseName) { + var action; + if (accessor.length == 1) { + action = function(target, varName) { + storedVars[varName] = accessor.call(this, target); + }; + } else { + action = function(varName) { + storedVars[varName] = accessor.call(this); + }; + } + self.registerAction("store"+baseName, action, false, accessor.dontCheckAlertsAndConfirms); + }; + + // Methods of the form getFoo(target) result in commands: + // getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo + // storeFoo, waitForFoo, and waitForNotFoo. + var registerAllAccessors = function(commandObject) { + for (var functionName in commandObject) { + var match = /^get([A-Z].+)$/.exec(functionName); + if (match != null) { + var accessor = commandObject[functionName]; + var baseName = match[1]; + self.registerAccessor(functionName, accessor); + self.registerAssertionsBasedOnAccessor(accessor, baseName); + self.registerStoreCommandBasedOnAccessor(accessor, baseName); + self.registerWaitForCommandsBasedOnAccessor(accessor, baseName); + } + var match = /^is([A-Z].+)$/.exec(functionName); + if (match != null) { + var accessor = commandObject[functionName]; + var baseName = match[1]; + var predicate = self.createPredicateFromBooleanAccessor(accessor); + self.registerAccessor(functionName, accessor); + self.registerAssertionsBasedOnAccessor(accessor, baseName, predicate); + self.registerStoreCommandBasedOnAccessor(accessor, baseName); + self.registerWaitForCommandsBasedOnAccessor(accessor, baseName, predicate); + } + } + }; + + +} + +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, executor) { + this.type = type; + this.haltOnFailure = haltOnFailure; + this.executor = executor; +} + +// 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(action, wait, dontCheckAlerts) { + CommandHandler.call(this, "action", true, action); + if (wait) { + this.wait = true; + } + // note that dontCheckAlerts could be undefined!!! + this.checkAlerts = (dontCheckAlerts) ? false : true; +} +ActionHandler.prototype = new CommandHandler; +ActionHandler.prototype.execute = function(seleniumApi, command) { + if (this.checkAlerts && (null==/(Alert|Confirmation)(Not)?Present/.exec(command.command))) { + this.checkForAlerts(seleniumApi); + } + var processState = this.executor.call(seleniumApi, 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 (processState == undefined && this.wait) { + processState = SELENIUM_PROCESS_WAIT; + } + return new CommandResult(processState); +}; +ActionHandler.prototype.checkForAlerts = function(seleniumApi) { + if ( seleniumApi.browserbot.hasAlerts() ) { + throw new SeleniumError("There was an unexpected Alert! [" + seleniumApi.browserbot.getNextAlert() + "]"); + } + if ( seleniumApi.browserbot.hasConfirmations() ) { + throw new SeleniumError("There was an unexpected Confirmation! [" + seleniumApi.browserbot.getNextConfirmation() + "]"); + } +}; + +function AccessorHandler(accessor) { + CommandHandler.call(this, "accessor", true, accessor); +} +AccessorHandler.prototype = new CommandHandler; +AccessorHandler.prototype.execute = function(seleniumApi, command) { + var returnValue = this.executor.call(seleniumApi, command.target, command.value); + var result = new CommandResult(); + result.result = returnValue; + return result; +}; + +/** + * Handler for assertions and verifications. + */ +function AssertHandler(assertion, haltOnFailure) { + CommandHandler.call(this, "assert", haltOnFailure || false, assertion); +} +AssertHandler.prototype = new CommandHandler; +AssertHandler.prototype.execute = function(seleniumApi, command) { + var result = new CommandResult(); + try { + this.executor.call(seleniumApi, command.target, command.value); + result.passed = true; + } 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.failed = true; + result.failureMessage = e.failureMessage; + } + return result; +}; + + +function CommandResult(processState) { + this.processState = processState; + this.result = null; +} + +function SeleniumCommand(command, target, value) { + this.command = command; + this.target = target; + this.value = value; +} diff --git a/tests/FunctionalTests/selenium/selenium-executionloop.js b/tests/FunctionalTests/selenium/core/scripts/selenium-executionloop.js index c9e1fd9c..14c1a07a 100644..100755 --- a/tests/FunctionalTests/selenium/selenium-executionloop.js +++ b/tests/FunctionalTests/selenium/core/scripts/selenium-executionloop.js @@ -19,9 +19,11 @@ SELENIUM_PROCESS_WAIT = "wait"; function TestLoop(commandFactory) { this.commandFactory = commandFactory; + this.waitForConditionTimeout = 30 * 1000; // 30 seconds this.start = function() { selenium.reset(); + LOG.debug("testLoop.start()"); this.continueTest(); }; @@ -29,17 +31,25 @@ function TestLoop(commandFactory) { * Select the next command and continue the test. */ this.continueTest = function() { + LOG.debug("testLoop.continueTest() - acquire the next command"); if (! this.aborted) { this.currentCommand = this.nextCommand(); } - if (this.currentCommand) { + if (! this.requiresCallBack) { + this.beginNextTest(); + } // otherwise, just finish and let the callback invoke beginNextTest() + }; + + this.beginNextTest = function() { + LOG.debug("testLoop.beginNextTest()"); + if (this.currentCommand) { // TODO: rename commandStarted to commandSelected, OR roll it into nextCommand this.commandStarted(this.currentCommand); this.resumeAfterDelay(); } else { this.testComplete(); } - }; + } /** * Pause, then execute the current command. @@ -63,8 +73,10 @@ function TestLoop(commandFactory) { * Select the next command and continue the test. */ this.resume = function() { + LOG.debug("testLoop.resume() - actually execute"); try { this.executeCurrentCommand(); + this.waitForConditionStart = new Date().getTime(); this.continueTestWhenConditionIsTrue(); } catch (e) { this.handleCommandError(e); @@ -86,17 +98,19 @@ function TestLoop(commandFactory) { var handler = this.commandFactory.getCommandHandler(command.command); if (handler == null) { - throw new Error("Unknown command: '" + command.command + "'"); + throw new SeleniumError("Unknown command: '" + command.command + "'"); } command.target = selenium.preprocessParameter(command.target); command.value = selenium.preprocessParameter(command.value); + LOG.debug("Command found, going to execute " + command.command); var result = handler.execute(selenium, command); - + LOG.debug("Command complete"); this.commandComplete(result); if (result.processState == SELENIUM_PROCESS_WAIT) { this.waitForCondition = function() { + LOG.debug("Checking condition: isNewPageLoaded?"); return selenium.browserbot.isNewPageLoaded(); }; } @@ -105,7 +119,7 @@ function TestLoop(commandFactory) { this.handleCommandError = function(e) { if (!e.isSeleniumError) { LOG.exception(e); - var msg = "Selenium failure. Please report to selenium-devel@lists.public.thoughtworks.org, with details from the logs at the base of the page."; + var msg = "Selenium failure. Please report to selenium-dev@openqa.org, with error details from the log window."; if (e.message) { msg += " The error message is: " + e.message; } @@ -118,15 +132,33 @@ function TestLoop(commandFactory) { /** * Busy wait for waitForCondition() to become true, and then carry on - * with test. + * with test. Fail the current test if there's a timeout or an exception. */ this.continueTestWhenConditionIsTrue = function () { - if (this.waitForCondition == null || this.waitForCondition()) { - this.waitForCondition = null; - this.continueTest(); - } else { - window.setTimeout("testLoop.continueTestWhenConditionIsTrue()", 10); - } + LOG.debug("testLoop.continueTestWhenConditionIsTrue()"); + try { + if (this.waitForCondition == null || this.waitForCondition()) { + LOG.debug("condition satisfied; let's continueTest()"); + this.waitForCondition = null; + this.waitForConditionStart = null; + this.continueTest(); + } else { + LOG.debug("waitForCondition was false; keep waiting!"); + if (this.waitForConditionTimeout != null) { + var now = new Date(); + if ((now - this.waitForConditionStart) > this.waitForConditionTimeout) { + throw new SeleniumError("Timed out after " + this.waitForConditionTimeout + "ms"); + } + } + window.setTimeout("testLoop.continueTestWhenConditionIsTrue()", 10); + } + } catch (e) { + var lastResult = new CommandResult(); + lastResult.failed = true; + lastResult.failureMessage = e.message; + this.commandComplete(lastResult); + this.testComplete(); + } }; } @@ -163,7 +195,7 @@ Selenium.prototype.assertFailureOnNext = function(message) { } var expectFailureCommandFactory = - new ExpectFailureCommandFactory(testLoop.commandFactory, message); + new ExpectFailureCommandFactory(testLoop.commandFactory, message, "failure"); expectFailureCommandFactory.baseExecutor = executeCommandAndReturnFailureMessage; testLoop.commandFactory = expectFailureCommandFactory; }; @@ -179,12 +211,12 @@ Selenium.prototype.assertErrorOnNext = function(message) { } var expectFailureCommandFactory = - new ExpectFailureCommandFactory(testLoop.commandFactory, message); + new ExpectFailureCommandFactory(testLoop.commandFactory, message, "error"); expectFailureCommandFactory.baseExecutor = executeCommandAndReturnErrorMessage; testLoop.commandFactory = expectFailureCommandFactory; }; -function ExpectFailureCommandFactory(originalCommandFactory, expectedErrorMessage) { +function ExpectFailureCommandFactory(originalCommandFactory, expectedErrorMessage, errorType) { this.getCommandHandler = function(name) { var baseHandler = originalCommandFactory.getCommandHandler(name); var baseExecutor = this.baseExecutor; @@ -194,12 +226,12 @@ function ExpectFailureCommandFactory(originalCommandFactory, expectedErrorMessag var result = new CommandResult(); if (!baseFailureMessage) { result.failed = true; - result.failureMessage = "Command should have failed."; + result.failureMessage = "Expected " + errorType + " did not occur."; } else { if (! PatternMatcher.matches(expectedErrorMessage, baseFailureMessage)) { result.failed = true; - result.failureMessage = "Expected failure message '" + expectedErrorMessage + result.failureMessage = "Expected " + errorType + " message '" + expectedErrorMessage + "' but was '" + baseFailureMessage + "'"; } else { diff --git a/tests/FunctionalTests/selenium/selenium-logging.js b/tests/FunctionalTests/selenium/core/scripts/selenium-logging.js index d0305c08..b0fc67e4 100644..100755 --- a/tests/FunctionalTests/selenium/selenium-logging.js +++ b/tests/FunctionalTests/selenium/core/scripts/selenium-logging.js @@ -19,16 +19,36 @@ var Logger = function() { } Logger.prototype = { + setLogLevelThreshold: function(logLevel) { + this.pendingLogLevelThreshold = logLevel; + this.show(); + // + // The following message does not show up in the log -- _unless_ I step along w/ the debugger + // down to the append call. I believe this is because the new log window has not yet loaded, + // and therefore the log msg is discarded; but if I step through the debugger, this changes + // the scheduling so as to load that window and make it ready. + // this.info("Log level programmatically set to " + logLevel + " (presumably by driven-mode test code)"); + }, + getLogWindow: function() { if (this.logWindow && this.logWindow.closed) { this.logWindow = null; } + if (this.logWindow && this.pendingLogLevelThreshold && this.logWindow.setThresholdLevel) { + this.logWindow.setThresholdLevel(this.pendingLogLevelThreshold); + + // can't just directly log because that action would loop back to this code infinitely + this.pendingInfoMessage = "Log level programmatically set to " + this.pendingLogLevelThreshold + " (presumably by driven-mode test code)"; + + this.pendingLogLevelThreshold = null; // let's only go this way one time + } + return this.logWindow; }, openLogWindow: function() { this.logWindow = window.open( - "SeleniumLog.html", "SeleniumLog", + getDocumentBase(document) + "SeleniumLog.html", "SeleniumLog", "width=600,height=250,bottom=0,right=0,status,scrollbars,resizable" ); return this.logWindow; @@ -44,11 +64,27 @@ Logger.prototype = { var logWindow = this.getLogWindow(); if (logWindow) { if (logWindow.append) { - logWindow.append(message, className); + if (this.pendingInfoMessage) { + logWindow.append("info: " + this.pendingInfoMessage, "info"); + this.pendingInfoMessage = null; + } + logWindow.append(className + ": " + message, className); } } }, + 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(message, "debug"); }, @@ -74,15 +110,3 @@ Logger.prototype = { var LOG = new Logger(); -function noop() {}; - -var DummyLogger = function() {}; -DummyLogger.prototype = { - show: noop, - log: noop, - debug: noop, - info: noop, - warn: noop, - error: noop -}; - diff --git a/tests/FunctionalTests/selenium/core/scripts/selenium-seleneserunner.js b/tests/FunctionalTests/selenium/core/scripts/selenium-seleneserunner.js new file mode 100755 index 00000000..041b3bf9 --- /dev/null +++ b/tests/FunctionalTests/selenium/core/scripts/selenium-seleneserunner.js @@ -0,0 +1,300 @@ +/* +* Copyright 2005 ThoughtWorks, Inc +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*/ + +passColor = "#cfffcf"; +failColor = "#ffcfcf"; +errorColor = "#ffffff"; +workingColor = "#DEE7EC"; +doneColor = "#FFFFCC"; + +slowMode = false; + +var cmd1 = document.createElement("div"); +var cmd2 = document.createElement("div"); +var cmd3 = document.createElement("div"); +var cmd4 = document.createElement("div"); + +var postResult = "START"; + +queryString = null; + +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 = "Selenium Tests Complete, for this Test" + // Continue checking for new results + testLoop.continueTest(); + postResult = "START"; + }; + + document.getElementById("commandList").appendChild(cmd4); + document.getElementById("commandList").appendChild(cmd3); + document.getElementById("commandList").appendChild(cmd2); + document.getElementById("commandList").appendChild(cmd1); + + var doContinue = getQueryVariable("continue"); + if (doContinue != null) postResult = "OK"; + + testLoop.start(); +} + +function getQueryString() { + if (queryString != null) return queryString; + if (browserVersion.isHTA) { + var args = extractArgs(); + if (args.length < 2) return null; + queryString = args[1]; + return queryString; + } else { + return location.search.substr(1); + } +} + +function extractArgs() { + var str = SeleniumHTARunner.commandLine; + if (str == null || str == "") return new Array(); + var matches = str.match(/(?:"([^"]+)"|(?!"([^"]+)")(\S+))/g); + // We either want non quote stuff ([^"]+) surrounded by quotes + // or we want to look-ahead, see that the next character isn't + // a quoted argument, and then grab all the non-space stuff + // this will return for the line: "foo" bar + // the results "\"foo\"" and "bar" + + // So, let's unquote the quoted arguments: + var args = new Array; + for (var i = 0; i < matches.length; i++) { + args[i] = matches[i]; + args[i] = args[i].replace(/^"(.*)"$/, "$1"); + } + return args; +} + +function getQueryVariable(variable) { + var query = getQueryString(); + if (query == null) return null; + var vars = query.split("&"); + for (var i=0;i<vars.length;i++) { + var pair = vars[i].split("="); + if (pair[0] == variable) { + return pair[1]; + } + } +} + +function buildBaseUrl() { + var baseUrl = getQueryVariable("baseUrl"); + if (baseUrl != null) return baseUrl; + var lastSlash = window.location.href.lastIndexOf('/'); + baseUrl = window.location.href.substring(0, lastSlash+1); + return baseUrl; +} + +function buildDriverParams() { + var params = ""; + + var host = getQueryVariable("driverhost"); + var port = getQueryVariable("driverport"); + if (host != undefined && port != undefined) { + params = params + "&driverhost=" + host + "&driverport=" + port; + } + + var sessionId = getQueryVariable("sessionId"); + if (sessionId != undefined) { + params = params + "&sessionId=" + sessionId; + } + + return params; +} + +function preventBrowserCaching() { + var t = (new Date()).getTime(); + return "&counterToMakeURsUniqueAndSoStopPageCachingInTheBrowser=" + t; +} + +function nextCommand() { + xmlHttp = XmlHttp.create(); + try { + + var url = buildBaseUrl(); + if (postResult == "START") { + url = url + "driver/?seleniumStart=true" + buildDriverParams() + preventBrowserCaching(); + } else { + url = url + "driver/?" + buildDriverParams() + preventBrowserCaching(); + } + LOG.debug("XMLHTTPRequesting " + url); + xmlHttp.open("POST", url, true); + xmlHttp.onreadystatechange=handleHttpResponse; + xmlHttp.send(postResult); + } catch(e) { + var s = 'xmlHttp returned:\n' + for (key in e) { + s += "\t" + key + " -> " + e[key] + "\n" + } + 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 + ": " + xmlHttp.statusText; + LOG.error(s); + testLoop.currentCommand = null; + setTimeout("testLoop.beginNextTest();", 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("div"); + innerHTML = command.command + '('; + if (command.target != null && command.target != "") { + innerHTML += command.target; + if (command.value != null && command.value != "") { + innerHTML += ', ' + command.value; + } + } + innerHTML += ")"; + commandNode.innerHTML = innerHTML; + commandNode.style.backgroundColor = workingColor; + document.getElementById("commandList").removeChild(cmd1); + document.getElementById("commandList").removeChild(cmd2); + document.getElementById("commandList").removeChild(cmd3); + document.getElementById("commandList").removeChild(cmd4); + cmd4 = cmd3; + cmd3 = cmd2; + cmd2 = cmd1; + cmd1 = commandNode; + document.getElementById("commandList").appendChild(cmd4); + document.getElementById("commandList").appendChild(cmd3); + document.getElementById("commandList").appendChild(cmd2); + document.getElementById("commandList").appendChild(cmd1); +} + +function commandComplete(result) { + if (result.failed) { + if (postResult == "CONTINUATION") { + testLoop.aborted = true; + } + postResult = result.failureMessage; + commandNode.title = result.failureMessage; + commandNode.style.backgroundColor = failColor; + } else if (result.passed) { + postResult = "OK"; + commandNode.style.backgroundColor = passColor; + } else { + if (result.result == null) { + postResult = "OK"; + } else { + postResult = "OK," + result.result; + } + commandNode.style.backgroundColor = doneColor; + } +} + +function commandError(message) { + postResult = "ERROR: " + 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 > 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, "%20"); + // strip trailing spaces + var processed = processed.replace(/\s+$/, ""); + var vars = processed.split("&"); + var cmdArgs = new Object(); + for (var i=0;i<vars.length;i++) { + var pair = vars[i].split("="); + cmdArgs[pair[0]] = pair[1]; + } + var cmd = cmdArgs['cmd']; + var arg1 = cmdArgs['1']; + if (null == arg1) arg1 = ""; + arg1 = decodeURIComponent(arg1); + var arg2 = cmdArgs['2']; + if (null == arg2) arg2 = ""; + arg2 = decodeURIComponent(arg2); + if (cmd == null) { + throw new Error("Bad command request: " + commandRequest); + } + return new SeleniumCommand(cmd, arg1, arg2); +} + diff --git a/tests/FunctionalTests/selenium/selenium-testrunner.js b/tests/FunctionalTests/selenium/core/scripts/selenium-testrunner.js index e786b29b..1ced0a11 100644..100755 --- a/tests/FunctionalTests/selenium/selenium-testrunner.js +++ b/tests/FunctionalTests/selenium/core/scripts/selenium-testrunner.js @@ -18,10 +18,8 @@ // The current row in the list of tests (test suite) currentRowInSuite = 0; -// Where are we at in the current test -testTableRows = null; -currentRowInTest = -1; -currentCommandRow = null; +// An object representing the current test +currentTest = null; // Whether or not the jsFT should run all tests in the suite runAllTests = false; @@ -66,6 +64,8 @@ FAILURE = 1; runInterval = 0; +queryString = null; + function setRunInterval() { // Get the value of the checked runMode option. // There should be a way of getting the value of the "group", but I don't know how. @@ -100,6 +100,7 @@ function loadAndRunIfAuto() { } function start() { + queryString = null; setRunInterval(); loadSuiteFrame(); } @@ -149,25 +150,32 @@ function getIframeDocument(iframe) function onloadTestSuite() { removeLoadListener(getSuiteFrame(), onloadTestSuite); - suiteTable = getIframeDocument(getSuiteFrame()).getElementsByTagName("table")[0]; - - // Add an onclick function to each link in the suite table - for(rowNum = 1;rowNum < suiteTable.rows.length; rowNum++) { - addOnclick(suiteTable, rowNum); + + // Add an onclick function to each link in all suite tables + var allTables = getIframeDocument(getSuiteFrame()).getElementsByTagName("table"); + for (var tableNum = 0; tableNum < allTables.length; tableNum++) + { + var skippedTable = allTables[tableNum]; + for(rowNum = 1;rowNum < skippedTable.rows.length; rowNum++) { + addOnclick(skippedTable, rowNum); + } } + suiteTable = getIframeDocument(getSuiteFrame()).getElementsByTagName("table")[0]; + if (suiteTable!=null) { - if (isAutomatedRun()) { - startTestSuite(); - } else if (getQueryParameter("autoURL")) { + if (isAutomatedRun()) { + startTestSuite(); + } else if (getQueryParameter("autoURL")) { - addLoadListener(getApplicationFrame(), startSingleTest); + addLoadListener(getApplicationFrame(), startSingleTest); - getApplicationFrame().src = getQueryParameter("autoURL"); + getApplicationFrame().src = getQueryParameter("autoURL"); - } else { - testLink = suiteTable.rows[currentRowInSuite+1].cells[0].getElementsByTagName("a")[0]; - getTestFrame().src = testLink.href; + } else { + testLink = suiteTable.rows[currentRowInSuite+1].cells[0].getElementsByTagName("a")[0]; + getTestFrame().src = testLink.href; + } } } @@ -220,8 +228,41 @@ function isQueryParameterTrue(name) { return (parameterValue != null && parameterValue.toLowerCase() == "true"); } +function getQueryString() { + if (queryString != null) return queryString; + if (browserVersion.isHTA) { + var args = extractArgs(); + if (args.length < 2) return null; + queryString = args[1]; + return queryString; + } else { + return location.search.substr(1); + } +} + +function extractArgs() { + var str = SeleniumHTARunner.commandLine; + if (str == null || str == "") return new Array(); + var matches = str.match(/(?:"([^"]+)"|(?!"([^"]+)")(\S+))/g); + // We either want non quote stuff ([^"]+) surrounded by quotes + // or we want to look-ahead, see that the next character isn't + // a quoted argument, and then grab all the non-space stuff + // this will return for the line: "foo" bar + // the results "\"foo\"" and "bar" + + // So, let's unquote the quoted arguments: + var args = new Array; + for (var i = 0; i < matches.length; i++) { + args[i] = matches[i]; + args[i] = args[i].replace(/^"(.*)"$/, "$1"); + } + return args; +} + function getQueryParameter(searchKey) { - var clauses = location.search.substr(1).split('&'); + var str = getQueryString(); + if (str == null) return null; + var clauses = str.split('&'); for (var i = 0; i < clauses.length; i++) { var keyValuePair = clauses[i].split('=',2); var key = unescape(keyValuePair[0]); @@ -266,25 +307,59 @@ function startTest() { frames['testFrame'].scrollTo(0,0); } - testTable = getIframeDocument(getTestFrame()).getElementsByTagName("table")[0]; - testTableRows = testTable.rows; - currentRowInTest = -1; - currentCommandRow = null; - + currentTest = new HtmlTest(getIframeDocument(getTestFrame())); + testFailed = false; storedVars = new Object(); - resetTestTable(); - testLoop = initialiseTestLoop(); testLoop.start(); } -function resetTestTable() { - for (var i = 0; i <= testTableRows.length - 1; i++) { - testTableRows[i].bgColor = "white"; +function HtmlTest(testDocument) { + this.init(testDocument); +} + +HtmlTest.prototype = { + + init: function(testDocument) { + this.document = testDocument; + this.document.bgColor = ""; + this.currentRow = null; + this.commandRows = new Array(); + this.headerRow = null; + var tables = this.document.getElementsByTagName("table"); + for (var i = 0; i < tables.length; i++) { + var candidateRows = tables[i].rows; + for (var j = 0; j < candidateRows.length; j++) { + if (!this.headerRow) { + this.headerRow = candidateRows[j]; + } + if (candidateRows[j].cells.length >= 3) { + this.addCommandRow(candidateRows[j]); + } + } + } + }, + + addCommandRow: function(row) { + if (row.cells[2] && row.cells[2].originalHTML) { + row.cells[2].innerHTML = row.cells[2].originalHTML; + } + row.bgColor = ""; + this.commandRows.push(row); + }, + + nextCommand: function() { + if (this.commandRows.length > 0) { + this.currentRow = this.commandRows.shift(); + } else { + this.currentRow = null; + } + return this.currentRow; } -} + +}; function startTestSuite() { resetMetrics(); @@ -309,7 +384,7 @@ function runNextTest() { } else { setCellColor(suiteTable.rows, currentRowInSuite, 0, passColor); } - + // Set the results from the previous test run setResultsData(suiteTable, currentRowInSuite); } @@ -362,7 +437,7 @@ function setResultsData(suiteTable, row) { new_column.appendChild(tableNode); // Set the column to be invisible - new_column.style.cssText = "display: none;"; + new_column.style.display = "none"; // Add the invisible column suiteTable.rows[row].appendChild(new_column); @@ -422,6 +497,9 @@ function postTestResults(suiteFailed, suiteTable) { } } + form.createHiddenField("selenium.version", Selenium.version); + form.createHiddenField("selenium.revision", Selenium.revision); + form.createHiddenField("result", suiteFailed == true ? "failed" : "passed"); form.createHiddenField("totalTime", Math.floor((currentTime - startTime) / 1000)); @@ -442,13 +520,46 @@ function postTestResults(suiteFailed, suiteTable) { resultCell.parentNode.removeChild(resultCell); } } + + form.createHiddenField("numTestTotal", rowNum); // Add HTML for the suite itself form.createHiddenField("suite", suiteTable.parentNode.innerHTML); - form.submit(); + if (isQueryParameterTrue("save")) { + saveToFile(resultsUrl, form); + } else { + form.submit(); + } document.body.removeChild(form); + if (isQueryParameterTrue("close")) { + window.top.close(); + } +} +function saveToFile(fileName, form) { + // This only works when run as an IE HTA + var inputs = new Object(); + for (var i = 0; i < form.elements.length; i++) { + inputs[form.elements[i].name] = form.elements[i].value; + } + var objFSO = new ActiveXObject("Scripting.FileSystemObject") + var scriptFile = objFSO.CreateTextFile(fileName); + scriptFile.WriteLine("<html><body>\n<h1>Test suite results </h1>" + + "\n\n<table>\n<tr>\n<td>result:</td>\n<td>" + inputs["result"] + "</td>\n" + + "</tr>\n<tr>\n<td>totalTime:</td>\n<td>" + inputs["totalTime"] + "</td>\n</tr>\n" + + "<tr>\n<td>numTestPasses:</td>\n<td>" + inputs["numTestPasses"] + "</td>\n</tr>\n" + + "<tr>\n<td>numTestFailures:</td>\n<td>" + inputs["numTestFailures"] + "</td>\n</tr>\n" + + "<tr>\n<td>numCommandPasses:</td>\n<td>" + inputs["numCommandPasses"] + "</td>\n</tr>\n" + + "<tr>\n<td>numCommandFailures:</td>\n<td>" + inputs["numCommandFailures"] + "</td>\n</tr>\n" + + "<tr>\n<td>numCommandErrors:</td>\n<td>" + inputs["numCommandErrors"] + "</td>\n</tr>\n" + + "<tr>\n<td>" + inputs["suite"] + "</td>\n<td> </td>\n</tr>"); + var testNum = inputs["numTestTotal"]; + for (var rowNum = 1; rowNum < testNum; rowNum++) { + scriptFile.WriteLine("<tr>\n<td>" + inputs["testTable." + rowNum] + "</td>\n<td> </td>\n</tr>"); + } + scriptFile.WriteLine("</table></body></html>"); + scriptFile.Close(); } function printMetrics() { @@ -499,30 +610,15 @@ function initialiseTestLoop() { return testLoop; } -function findNextCommandRow() { - // Return the next row with >= 3 cells - while (currentRowInTest < testTableRows.length - 1) { - currentRowInTest++; - currentCommandRow = testTableRows[currentRowInTest]; - if (currentCommandRow.cells.length >= 3) { - return; - } - } - currentCommandRow = null; -} - function nextCommand() { - findNextCommandRow(); - if (currentCommandRow == null) { + var row = currentTest.nextCommand(); + if (row == null) { return null; } - var row = currentCommandRow; - if (! row.cachedValue) { - row.cachedValue = removeNbsp(getText(row.cells[2])); - } + row.cells[2].originalHTML = row.cells[2].innerHTML; return new SeleniumCommand(getText(row.cells[0]), getText(row.cells[1]), - row.cachedValue); + getText(row.cells[2])); } function removeNbsp(value) { @@ -539,8 +635,8 @@ function scrollIntoView(element) { } function commandStarted() { - currentCommandRow.bgColor = workingColor; - scrollIntoView(currentCommandRow.cells[0]); + currentTest.currentRow.bgColor = workingColor; + scrollIntoView(currentTest.currentRow.cells[0]); printMetrics(); } @@ -550,9 +646,9 @@ function commandComplete(result) { recordFailure(result.failureMessage); } else if (result.passed) { numCommandPasses += 1; - currentCommandRow.bgColor = passColor; + currentTest.currentRow.bgColor = passColor; } else { - currentCommandRow.bgColor = doneColor; + currentTest.currentRow.bgColor = doneColor; } } @@ -562,26 +658,30 @@ function commandError(errorMessage) { } function recordFailure(errorMsg) { + LOG.warn("recordFailure: " + errorMsg); // Set cell background to red - currentCommandRow.bgColor = failColor; + currentTest.currentRow.bgColor = failColor; // Set error message - currentCommandRow.cells[2].innerHTML = errorMsg; - currentCommandRow.title = errorMsg; + currentTest.currentRow.cells[2].innerHTML = errorMsg; + currentTest.currentRow.title = errorMsg; testFailed = true; suiteFailed = true; } function testComplete() { - if(testFailed) { - testTableRows[0].bgColor = failColor; + var resultColor = passColor; + if (testFailed) { + resultColor = failColor; numTestFailures += 1; - } - else { - testTableRows[0].bgColor = passColor; + } else { numTestPasses += 1; } + if (currentTest.headerRow) { + currentTest.headerRow.bgColor = resultColor; + } + printMetrics(); window.setTimeout("runNextTest()", 1); @@ -591,7 +691,58 @@ Selenium.prototype.doPause = function(waitTime) { testLoop.pauseInterval = waitTime; }; +Selenium.prototype.doPause.dontCheckAlertsAndConfirms = true; + Selenium.prototype.doBreak = function() { document.getElementById('modeStep').checked = true; runInterval = -1; }; + +/* + * Click on the located element, and attach a callback to notify + * when the page is reloaded. + */ +Selenium.prototype.doModalDialogTest = function(returnValue) { + this.browserbot.doModalDialogTest(returnValue); +}; + +/* + * Store the value of a form input in a variable + */ +Selenium.prototype.doStoreValue = function(target, varName) { + if (!varName) { + // Backward compatibility mode: read the ENTIRE text of the page + // and stores it in a variable with the name of the target + value = this.page().bodyText(); + storedVars[target] = value; + return; + } + var element = this.page().findElement(target); + storedVars[varName] = getInputValue(element); +}; + +/* + * Store the text of an element in a variable + */ +Selenium.prototype.doStoreText = function(target, varName) { + var element = this.page().findElement(target); + storedVars[varName] = getText(element); +}; + +/* + * Store the value of an element attribute in a variable + */ +Selenium.prototype.doStoreAttribute = function(target, varName) { + storedVars[varName] = this.page().findAttribute(target); +}; + +/* + * Store the result of a literal value + */ +Selenium.prototype.doStore = function(value, varName) { + storedVars[varName] = value; +}; + +Selenium.prototype.doEcho = function(msg) { + currentTest.currentRow.cells[2].innerHTML = msg; +} diff --git a/tests/FunctionalTests/selenium/core/scripts/selenium-version.js b/tests/FunctionalTests/selenium/core/scripts/selenium-version.js new file mode 100755 index 00000000..1fee837b --- /dev/null +++ b/tests/FunctionalTests/selenium/core/scripts/selenium-version.js @@ -0,0 +1,5 @@ +Selenium.version = "@VERSION@"; +Selenium.revision = "@REVISION@"; + +window.top.document.title += " v" + Selenium.version + " [" + Selenium.revision + "]"; + diff --git a/tests/FunctionalTests/selenium/user-extensions.js.sample b/tests/FunctionalTests/selenium/core/scripts/user-extensions.js.sample index f234c29e..0f0ca840 100644..100755 --- a/tests/FunctionalTests/selenium/user-extensions.js.sample +++ b/tests/FunctionalTests/selenium/core/scripts/user-extensions.js.sample @@ -7,6 +7,8 @@ * By default, this file does not exist. Users can create this file and place their extension code * in this common location, removing the need to modify the Selenium sources, and hopefully assisting * with the upgrade process. + * + * You can find contributed extensions at http://wiki.openqa.org/display/SEL/Contributed%20User-Extensions */ // The following examples try to give an indication of how Selenium can be extended with javascript. @@ -40,7 +42,16 @@ Selenium.prototype.assertValueRepeated = function(locator, text) { var actualValue = element.value; // Make sure the actual value matches the expected - this.assertMatches(expectedValue, actualValue); + Assert.matches(expectedValue, actualValue); +}; + +// All get* methods on the Selenium prototype result in +// store, assert, assertNot, verify, verifyNot, waitFor, and waitForNot commands. +// E.g. add a getTextLength method that returns the length of the text +// of a specified element. +// Will result in support for storeTextLength, assertTextLength, etc. +Selenium.prototype.getTextLength = function(locator) { + return this.getText(locator).length; }; // All locateElementBy* methods are added as locator-strategies. @@ -60,3 +71,5 @@ PageBot.prototype.locateElementByValueRepeated = function(text, inDocument) { } return null; }; + + diff --git a/tests/FunctionalTests/selenium/xmlextras.js b/tests/FunctionalTests/selenium/core/scripts/xmlextras.js index 267aa058..267aa058 100644..100755 --- a/tests/FunctionalTests/selenium/xmlextras.js +++ b/tests/FunctionalTests/selenium/core/scripts/xmlextras.js diff --git a/tests/FunctionalTests/selenium/selenium-logo.png b/tests/FunctionalTests/selenium/core/selenium-logo.png Binary files differindex 1de09290..1de09290 100644..100755 --- a/tests/FunctionalTests/selenium/selenium-logo.png +++ b/tests/FunctionalTests/selenium/core/selenium-logo.png diff --git a/tests/FunctionalTests/selenium/selenium.css b/tests/FunctionalTests/selenium/core/selenium.css index 6e9f3f30..6e9f3f30 100644..100755 --- a/tests/FunctionalTests/selenium/selenium.css +++ b/tests/FunctionalTests/selenium/core/selenium.css diff --git a/tests/FunctionalTests/selenium/core/xpath/dom.js b/tests/FunctionalTests/selenium/core/xpath/dom.js new file mode 100755 index 00000000..5e49748c --- /dev/null +++ b/tests/FunctionalTests/selenium/core/xpath/dom.js @@ -0,0 +1,428 @@ +// Copyright 2005 Google Inc.
+// All Rights Reserved
+//
+// An XML parse and a minimal DOM implementation that just supportes
+// the subset of the W3C DOM that is used in the XSLT implementation.
+//
+// References:
+//
+// [DOM] W3C DOM Level 3 Core Specification
+// <http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/>.
+//
+//
+// Author: Steffen Meschkat <mesch@google.com>
+
+// NOTE: The split() method in IE omits empty result strings. This is
+// utterly annoying. So we don't use it here.
+
+// Resolve entities in XML text fragments. According to the DOM
+// specification, the DOM is supposed to resolve entity references at
+// the API level. I.e. no entity references are passed through the
+// API. See "Entities and the DOM core", p.12, DOM 2 Core
+// Spec. However, different browsers actually pass very different
+// values at the API.
+//
+function xmlResolveEntities(s) {
+
+ var parts = stringSplit(s, '&');
+
+ var ret = parts[0];
+ for (var i = 1; i < parts.length; ++i) {
+ var rp = stringSplit(parts[i], ';');
+ if (rp.length == 1) {
+ // no entity reference: just a & but no ;
+ ret += parts[i];
+ continue;
+ }
+
+ var ch;
+ switch (rp[0]) {
+ case 'lt':
+ ch = '<';
+ break;
+ case 'gt':
+ ch = '>';
+ break;
+ case 'amp':
+ ch = '&';
+ break;
+ case 'quot':
+ ch = '"';
+ break;
+ case 'apos':
+ ch = '\'';
+ break;
+ case 'nbsp':
+ ch = String.fromCharCode(160);
+ break;
+ default:
+ // Cool trick: let the DOM do the entity decoding. We assign
+ // the entity text through non-W3C DOM properties and read it
+ // through the W3C DOM. W3C DOM access is specified to resolve
+ // entities.
+ var span = window.document.createElement('span');
+ span.innerHTML = '&' + rp[0] + '; ';
+ ch = span.childNodes[0].nodeValue.charAt(0);
+ }
+ ret += ch + rp[1];
+ }
+
+ return ret;
+}
+
+
+// Parses the given XML string with our custom, JavaScript XML parser. Written
+// by Steffen Meschkat (mesch@google.com).
+function xmlParse(xml) {
+ Timer.start('xmlparse');
+ var regex_empty = /\/$/;
+
+ // See also <http://www.w3.org/TR/REC-xml/#sec-common-syn> for
+ // allowed chars in a tag and attribute name. TODO(mesch): the
+ // following is still not completely correct.
+
+ var regex_tagname = /^([\w:-]*)/;
+ var regex_attribute = /([\w:-]+)\s?=\s?('([^\']*)'|"([^\"]*)")/g;
+
+ var xmldoc = new XDocument();
+ var root = xmldoc;
+
+ // For the record: in Safari, we would create native DOM nodes, but
+ // in Opera that is not possible, because the DOM only allows HTML
+ // element nodes to be created, so we have to do our own DOM nodes.
+
+ // xmldoc = document.implementation.createDocument('','',null);
+ // root = xmldoc; // .createDocumentFragment();
+ // NOTE(mesch): using the DocumentFragment instead of the Document
+ // crashes my Safari 1.2.4 (v125.12).
+ var stack = [];
+
+ var parent = root;
+ stack.push(parent);
+
+ var x = stringSplit(xml, '<');
+ for (var i = 1; i < x.length; ++i) {
+ var xx = stringSplit(x[i], '>');
+ var tag = xx[0];
+ var text = xmlResolveEntities(xx[1] || '');
+
+ if (tag.charAt(0) == '/') {
+ stack.pop();
+ parent = stack[stack.length-1];
+
+ } else if (tag.charAt(0) == '?') {
+ // Ignore XML declaration and processing instructions
+ } else if (tag.charAt(0) == '!') {
+ // Ignore notation and comments
+ } else {
+ var empty = tag.match(regex_empty);
+ var tagname = regex_tagname.exec(tag)[1];
+ var node = xmldoc.createElement(tagname);
+
+ var att;
+ while (att = regex_attribute.exec(tag)) {
+ var val = xmlResolveEntities(att[3] || att[4] || '');
+ node.setAttribute(att[1], val);
+ }
+
+ if (empty) {
+ parent.appendChild(node);
+ } else {
+ parent.appendChild(node);
+ parent = node;
+ stack.push(node);
+ }
+ }
+
+ if (text && parent != root) {
+ parent.appendChild(xmldoc.createTextNode(text));
+ }
+ }
+
+ Timer.end('xmlparse');
+ return root;
+}
+
+
+// Our W3C DOM Node implementation. Note we call it XNode because we
+// can't define the identifier Node. We do this mostly for Opera,
+// where we can't reuse the HTML DOM for parsing our own XML, and for
+// Safari, where it is too expensive to have the template processor
+// operate on native DOM nodes.
+function XNode(type, name, value, owner) {
+ this.attributes = [];
+ this.childNodes = [];
+
+ XNode.init.call(this, type, name, value, owner);
+}
+
+// Don't call as method, use apply() or call().
+XNode.init = function(type, name, value, owner) {
+ this.nodeType = type - 0;
+ this.nodeName = '' + name;
+ this.nodeValue = '' + value;
+ this.ownerDocument = owner;
+
+ this.firstChild = null;
+ this.lastChild = null;
+ this.nextSibling = null;
+ this.previousSibling = null;
+ this.parentNode = null;
+}
+
+XNode.unused_ = [];
+
+XNode.recycle = function(node) {
+ if (!node) {
+ return;
+ }
+
+ if (node.constructor == XDocument) {
+ XNode.recycle(node.documentElement);
+ return;
+ }
+
+ if (node.constructor != this) {
+ return;
+ }
+
+ XNode.unused_.push(node);
+ for (var a = 0; a < node.attributes.length; ++a) {
+ XNode.recycle(node.attributes[a]);
+ }
+ for (var c = 0; c < node.childNodes.length; ++c) {
+ XNode.recycle(node.childNodes[c]);
+ }
+ node.attributes.length = 0;
+ node.childNodes.length = 0;
+ XNode.init.call(node, 0, '', '', null);
+}
+
+XNode.create = function(type, name, value, owner) {
+ if (XNode.unused_.length > 0) {
+ var node = XNode.unused_.pop();
+ XNode.init.call(node, type, name, value, owner);
+ return node;
+ } else {
+ return new XNode(type, name, value, owner);
+ }
+}
+
+XNode.prototype.appendChild = function(node) {
+ // firstChild
+ if (this.childNodes.length == 0) {
+ this.firstChild = node;
+ }
+
+ // previousSibling
+ node.previousSibling = this.lastChild;
+
+ // nextSibling
+ node.nextSibling = null;
+ if (this.lastChild) {
+ this.lastChild.nextSibling = node;
+ }
+
+ // parentNode
+ node.parentNode = this;
+
+ // lastChild
+ this.lastChild = node;
+
+ // childNodes
+ this.childNodes.push(node);
+}
+
+
+XNode.prototype.replaceChild = function(newNode, oldNode) {
+ if (oldNode == newNode) {
+ return;
+ }
+
+ for (var i = 0; i < this.childNodes.length; ++i) {
+ if (this.childNodes[i] == oldNode) {
+ this.childNodes[i] = newNode;
+
+ var p = oldNode.parentNode;
+ oldNode.parentNode = null;
+ newNode.parentNode = p;
+
+ p = oldNode.previousSibling;
+ oldNode.previousSibling = null;
+ newNode.previousSibling = p;
+ if (newNode.previousSibling) {
+ newNode.previousSibling.nextSibling = newNode;
+ }
+
+ p = oldNode.nextSibling;
+ oldNode.nextSibling = null;
+ newNode.nextSibling = p;
+ if (newNode.nextSibling) {
+ newNode.nextSibling.previousSibling = newNode;
+ }
+
+ if (this.firstChild == oldNode) {
+ this.firstChild = newNode;
+ }
+
+ if (this.lastChild == oldNode) {
+ this.lastChild = newNode;
+ }
+
+ break;
+ }
+ }
+}
+
+XNode.prototype.insertBefore = function(newNode, oldNode) {
+ if (oldNode == newNode) {
+ return;
+ }
+
+ if (oldNode.parentNode != this) {
+ return;
+ }
+
+ if (newNode.parentNode) {
+ newNode.parentNode.removeChild(newNode);
+ }
+
+ var newChildren = [];
+ for (var i = 0; i < this.childNodes.length; ++i) {
+ var c = this.childNodes[i];
+ if (c == oldNode) {
+ newChildren.push(newNode);
+
+ newNode.parentNode = this;
+
+ newNode.previousSibling = oldNode.previousSibling;
+ oldNode.previousSibling = newNode;
+ if (newNode.previousSibling) {
+ newNode.previousSibling.nextSibling = newNode;
+ }
+
+ newNode.nextSibling = oldNode;
+
+ if (this.firstChild == oldNode) {
+ this.firstChild = newNode;
+ }
+ }
+ newChildren.push(c);
+ }
+ this.childNodes = newChildren;
+}
+
+XNode.prototype.removeChild = function(node) {
+ var newChildren = [];
+ for (var i = 0; i < this.childNodes.length; ++i) {
+ var c = this.childNodes[i];
+ if (c != node) {
+ newChildren.push(c);
+ } else {
+ if (c.previousSibling) {
+ c.previousSibling.nextSibling = c.nextSibling;
+ }
+ if (c.nextSibling) {
+ c.nextSibling.previousSibling = c.previousSibling;
+ }
+ if (this.firstChild == c) {
+ this.firstChild = c.nextSibling;
+ }
+ if (this.lastChild == c) {
+ this.lastChild = c.previousSibling;
+ }
+ }
+ }
+ this.childNodes = newChildren;
+}
+
+
+XNode.prototype.hasAttributes = function() {
+ return this.attributes.length > 0;
+}
+
+
+XNode.prototype.setAttribute = function(name, value) {
+ for (var i = 0; i < this.attributes.length; ++i) {
+ if (this.attributes[i].nodeName == name) {
+ this.attributes[i].nodeValue = '' + value;
+ return;
+ }
+ }
+ this.attributes.push(new XNode(DOM_ATTRIBUTE_NODE, name, value));
+}
+
+
+XNode.prototype.getAttribute = function(name) {
+ for (var i = 0; i < this.attributes.length; ++i) {
+ if (this.attributes[i].nodeName == name) {
+ return this.attributes[i].nodeValue;
+ }
+ }
+ return null;
+}
+
+XNode.prototype.removeAttribute = function(name) {
+ var a = [];
+ for (var i = 0; i < this.attributes.length; ++i) {
+ if (this.attributes[i].nodeName != name) {
+ a.push(this.attributes[i]);
+ }
+ }
+ this.attributes = a;
+}
+
+
+function XDocument() {
+ XNode.call(this, DOM_DOCUMENT_NODE, '#document', null, this);
+ this.documentElement = null;
+}
+
+XDocument.prototype = new XNode(DOM_DOCUMENT_NODE, '#document');
+
+XDocument.prototype.clear = function() {
+ XNode.recycle(this.documentElement);
+ this.documentElement = null;
+}
+
+XDocument.prototype.appendChild = function(node) {
+ XNode.prototype.appendChild.call(this, node);
+ this.documentElement = this.childNodes[0];
+}
+
+XDocument.prototype.createElement = function(name) {
+ return XNode.create(DOM_ELEMENT_NODE, name, null, this);
+}
+
+XDocument.prototype.createDocumentFragment = function() {
+ return XNode.create(DOM_DOCUMENT_FRAGMENT_NODE, '#document-fragment',
+ null, this);
+}
+
+XDocument.prototype.createTextNode = function(value) {
+ return XNode.create(DOM_TEXT_NODE, '#text', value, this);
+}
+
+XDocument.prototype.createAttribute = function(name) {
+ return XNode.create(DOM_ATTRIBUTE_NODE, name, null, this);
+}
+
+XDocument.prototype.createComment = function(data) {
+ return XNode.create(DOM_COMMENT_NODE, '#comment', data, this);
+}
+
+XNode.prototype.getElementsByTagName = function(name, list) {
+ if (!list) {
+ list = [];
+ }
+
+ if (this.nodeName == name) {
+ list.push(this);
+ }
+
+ for (var i = 0; i < this.childNodes.length; ++i) {
+ this.childNodes[i].getElementsByTagName(name, list);
+ }
+
+ return list;
+}
diff --git a/tests/FunctionalTests/selenium/core/xpath/misc.js b/tests/FunctionalTests/selenium/core/xpath/misc.js new file mode 100755 index 00000000..9017542e --- /dev/null +++ b/tests/FunctionalTests/selenium/core/xpath/misc.js @@ -0,0 +1,255 @@ +// Copyright 2005 Google Inc.
+// All Rights Reserved
+//
+// Miscellania that support the ajaxslt implementation.
+//
+// Author: Steffen Meschkat <mesch@google.com>
+//
+
+function el(i) {
+ return document.getElementById(i);
+}
+
+function px(x) {
+ return x + 'px';
+}
+
+// Split a string s at all occurrences of character c. This is like
+// the split() method of the string object, but IE omits empty
+// strings, which violates the invariant (s.split(x).join(x) == s).
+function stringSplit(s, c) {
+ var a = s.indexOf(c);
+ if (a == -1) {
+ return [ s ];
+ }
+
+ var parts = [];
+ parts.push(s.substr(0,a));
+ while (a != -1) {
+ var a1 = s.indexOf(c, a + 1);
+ if (a1 != -1) {
+ parts.push(s.substr(a + 1, a1 - a - 1));
+ } else {
+ parts.push(s.substr(a + 1));
+ }
+ a = a1;
+ }
+
+ return parts;
+}
+
+// Returns the text value if a node; for nodes without children this
+// is the nodeValue, for nodes with children this is the concatenation
+// of the value of all children.
+function xmlValue(node) {
+ if (!node) {
+ return '';
+ }
+
+ var ret = '';
+ if (node.nodeType == DOM_TEXT_NODE ||
+ node.nodeType == DOM_CDATA_SECTION_NODE ||
+ node.nodeType == DOM_ATTRIBUTE_NODE) {
+ ret += node.nodeValue;
+
+ } else if (node.nodeType == DOM_ELEMENT_NODE ||
+ node.nodeType == DOM_DOCUMENT_NODE ||
+ node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
+ for (var i = 0; i < node.childNodes.length; ++i) {
+ ret += arguments.callee(node.childNodes[i]);
+ }
+ }
+ return ret;
+}
+
+// Returns the representation of a node as XML text.
+function xmlText(node) {
+ var ret = '';
+ if (node.nodeType == DOM_TEXT_NODE) {
+ ret += xmlEscapeText(node.nodeValue);
+
+ } else if (node.nodeType == DOM_ELEMENT_NODE) {
+ ret += '<' + node.nodeName;
+ for (var i = 0; i < node.attributes.length; ++i) {
+ var a = node.attributes[i];
+ if (a && a.nodeName && a.nodeValue) {
+ ret += ' ' + a.nodeName;
+ ret += '="' + xmlEscapeAttr(a.nodeValue) + '"';
+ }
+ }
+
+ if (node.childNodes.length == 0) {
+ ret += '/>';
+
+ } else {
+ ret += '>';
+ for (var i = 0; i < node.childNodes.length; ++i) {
+ ret += arguments.callee(node.childNodes[i]);
+ }
+ ret += '</' + node.nodeName + '>';
+ }
+
+ } else if (node.nodeType == DOM_DOCUMENT_NODE ||
+ node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
+ for (var i = 0; i < node.childNodes.length; ++i) {
+ ret += arguments.callee(node.childNodes[i]);
+ }
+ }
+
+ return ret;
+}
+
+// Applies the given function to each element of the array.
+function mapExec(array, func) {
+ for (var i = 0; i < array.length; ++i) {
+ func(array[i]);
+ }
+}
+
+// Returns an array that contains the return value of the given
+// function applied to every element of the input array.
+function mapExpr(array, func) {
+ var ret = [];
+ for (var i = 0; i < array.length; ++i) {
+ ret.push(func(array[i]));
+ }
+ return ret;
+};
+
+// Reverses the given array in place.
+function reverseInplace(array) {
+ for (var i = 0; i < array.length / 2; ++i) {
+ var h = array[i];
+ var ii = array.length - i - 1;
+ array[i] = array[ii];
+ array[ii] = h;
+ }
+}
+
+// Shallow-copies an array.
+function copyArray(dst, src) {
+ for (var i = 0; i < src.length; ++i) {
+ dst.push(src[i]);
+ }
+}
+
+function assert(b) {
+ if (!b) {
+ throw 'assertion failed';
+ }
+}
+
+// Based on
+// <http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247>
+var DOM_ELEMENT_NODE = 1;
+var DOM_ATTRIBUTE_NODE = 2;
+var DOM_TEXT_NODE = 3;
+var DOM_CDATA_SECTION_NODE = 4;
+var DOM_ENTITY_REFERENCE_NODE = 5;
+var DOM_ENTITY_NODE = 6;
+var DOM_PROCESSING_INSTRUCTION_NODE = 7;
+var DOM_COMMENT_NODE = 8;
+var DOM_DOCUMENT_NODE = 9;
+var DOM_DOCUMENT_TYPE_NODE = 10;
+var DOM_DOCUMENT_FRAGMENT_NODE = 11;
+var DOM_NOTATION_NODE = 12;
+
+
+var xpathdebug = false; // trace xpath parsing
+var xsltdebug = false; // trace xslt processing
+
+
+// Escape XML special markup chracters: tag delimiter < > and entity
+// reference start delimiter &. The escaped string can be used in XML
+// text portions (i.e. between tags).
+function xmlEscapeText(s) {
+ return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
+}
+
+// Escape XML special markup characters: tag delimiter < > entity
+// reference start delimiter & and quotes ". The escaped string can be
+// used in double quoted XML attribute value portions (i.e. in
+// attributes within start tags).
+function xmlEscapeAttr(s) {
+ return xmlEscapeText(s).replace(/\"/g, '"');
+}
+
+// Escape markup in XML text, but don't touch entity references. The
+// escaped string can be used as XML text (i.e. between tags).
+function xmlEscapeTags(s) {
+ return s.replace(/</g, '<').replace(/>/g, '>');
+}
+
+// An implementation of the debug log.
+
+var logging__ = false;
+
+function Log() {};
+
+Log.lines = [];
+
+Log.write = function(s) {
+ if (logging__) {
+ this.lines.push(xmlEscapeText(s));
+ this.show();
+ }
+};
+
+// Writes the given XML with every tag on a new line.
+Log.writeXML = function(xml) {
+ if (logging__) {
+ var s0 = xml.replace(/</g, '\n<');
+ var s1 = xmlEscapeText(s0);
+ var s2 = s1.replace(/\s*\n(\s|\n)*/g, '<br/>');
+ this.lines.push(s2);
+ this.show();
+ }
+}
+
+// Writes without any escaping
+Log.writeRaw = function(s) {
+ if (logging__) {
+ this.lines.push(s);
+ this.show();
+ }
+}
+
+Log.clear = function() {
+ if (logging__) {
+ var l = this.div();
+ l.innerHTML = '';
+ this.lines = [];
+ }
+}
+
+Log.show = function() {
+ var l = this.div();
+ l.innerHTML += this.lines.join('<br/>') + '<br/>';
+ this.lines = [];
+ l.scrollTop = l.scrollHeight;
+}
+
+Log.div = function() {
+ var l = document.getElementById('log');
+ if (!l) {
+ l = document.createElement('div');
+ l.id = 'log';
+ l.style.position = 'absolute';
+ l.style.right = '5px';
+ l.style.top = '5px';
+ l.style.width = '250px';
+ l.style.height = '150px';
+ l.style.overflow = 'auto';
+ l.style.backgroundColor = '#f0f0f0';
+ l.style.border = '1px solid gray';
+ l.style.fontSize = '10px';
+ l.style.padding = '5px';
+ document.body.appendChild(l);
+ }
+ return l;
+}
+
+
+function Timer() {}
+Timer.start = function() {}
+Timer.end = function() {}
diff --git a/tests/FunctionalTests/selenium/core/xpath/xpath.js b/tests/FunctionalTests/selenium/core/xpath/xpath.js new file mode 100755 index 00000000..ce784585 --- /dev/null +++ b/tests/FunctionalTests/selenium/core/xpath/xpath.js @@ -0,0 +1,2182 @@ +// Copyright 2005 Google Inc.
+// All Rights Reserved
+//
+// An XPath parser and evaluator written in JavaScript. The
+// implementation is complete except for functions handling
+// namespaces.
+//
+// Reference: [XPATH] XPath Specification
+// <http://www.w3.org/TR/1999/REC-xpath-19991116>.
+//
+//
+// The API of the parser has several parts:
+//
+// 1. The parser function xpathParse() that takes a string and returns
+// an expession object.
+//
+// 2. The expression object that has an evaluate() method to evaluate the
+// XPath expression it represents. (It is actually a hierarchy of
+// objects that resembles the parse tree, but an application will call
+// evaluate() only on the top node of this hierarchy.)
+//
+// 3. The context object that is passed as an argument to the evaluate()
+// method, which represents the DOM context in which the expression is
+// evaluated.
+//
+// 4. The value object that is returned from evaluate() and represents
+// values of the different types that are defined by XPath (number,
+// string, boolean, and node-set), and allows to convert between them.
+//
+// These parts are near the top of the file, the functions and data
+// that are used internally follow after them.
+//
+//
+// TODO(mesch): add jsdoc comments. Use more coherent naming.
+//
+//
+// Author: Steffen Meschkat <mesch@google.com>
+
+
+// The entry point for the parser.
+//
+// @param expr a string that contains an XPath expression.
+// @return an expression object that can be evaluated with an
+// expression context.
+
+function xpathParse(expr) {
+ if (xpathdebug) {
+ Log.write('XPath parse ' + expr);
+ }
+ xpathParseInit();
+
+ var cached = xpathCacheLookup(expr);
+ if (cached) {
+ if (xpathdebug) {
+ Log.write(' ... cached');
+ }
+ return cached;
+ }
+
+ // Optimize for a few common cases: simple attribute node tests
+ // (@id), simple element node tests (page), variable references
+ // ($address), numbers (4), multi-step path expressions where each
+ // step is a plain element node test
+ // (page/overlay/locations/location).
+
+ if (expr.match(/^(\$|@)?\w+$/i)) {
+ var ret = makeSimpleExpr(expr);
+ xpathParseCache[expr] = ret;
+ if (xpathdebug) {
+ Log.write(' ... simple');
+ }
+ return ret;
+ }
+
+ if (expr.match(/^\w+(\/\w+)*$/i)) {
+ var ret = makeSimpleExpr2(expr);
+ xpathParseCache[expr] = ret;
+ if (xpathdebug) {
+ Log.write(' ... simple 2');
+ }
+ return ret;
+ }
+
+ var cachekey = expr; // expr is modified during parse
+ if (xpathdebug) {
+ Timer.start('XPath parse', cachekey);
+ }
+
+ var stack = [];
+ var ahead = null;
+ var previous = null;
+ var done = false;
+
+ var parse_count = 0;
+ var lexer_count = 0;
+ var reduce_count = 0;
+
+ while (!done) {
+ parse_count++;
+ expr = expr.replace(/^\s*/, '');
+ previous = ahead;
+ ahead = null;
+
+ var rule = null;
+ var match = '';
+ for (var i = 0; i < xpathTokenRules.length; ++i) {
+ var result = xpathTokenRules[i].re.exec(expr);
+ lexer_count++;
+ if (result && result.length > 0 && result[0].length > match.length) {
+ rule = xpathTokenRules[i];
+ match = result[0];
+ break;
+ }
+ }
+
+ // Special case: allow operator keywords to be element and
+ // variable names.
+
+ // NOTE(mesch): The parser resolves conflicts by looking ahead,
+ // and this is the only case where we look back to
+ // disambiguate. So this is indeed something different, and
+ // looking back is usually done in the lexer (via states in the
+ // general case, called "start conditions" in flex(1)). Also,the
+ // conflict resolution in the parser is not as robust as it could
+ // be, so I'd like to keep as much off the parser as possible (all
+ // these precedence values should be computed from the grammar
+ // rules and possibly associativity declarations, as in bison(1),
+ // and not explicitly set.
+
+ if (rule &&
+ (rule == TOK_DIV ||
+ rule == TOK_MOD ||
+ rule == TOK_AND ||
+ rule == TOK_OR) &&
+ (!previous ||
+ previous.tag == TOK_AT ||
+ previous.tag == TOK_DSLASH ||
+ previous.tag == TOK_SLASH ||
+ previous.tag == TOK_AXIS ||
+ previous.tag == TOK_DOLLAR)) {
+ rule = TOK_QNAME;
+ }
+
+ if (rule) {
+ expr = expr.substr(match.length);
+ if (xpathdebug) {
+ Log.write('token: ' + match + ' -- ' + rule.label);
+ }
+ ahead = {
+ tag: rule,
+ match: match,
+ prec: rule.prec ? rule.prec : 0, // || 0 is removed by the compiler
+ expr: makeTokenExpr(match)
+ };
+
+ } else {
+ if (xpathdebug) {
+ Log.write('DONE');
+ }
+ done = true;
+ }
+
+ while (xpathReduce(stack, ahead)) {
+ reduce_count++;
+ if (xpathdebug) {
+ Log.write('stack: ' + stackToString(stack));
+ }
+ }
+ }
+
+ if (xpathdebug) {
+ Log.write(stackToString(stack));
+ }
+
+ if (stack.length != 1) {
+ throw 'XPath parse error ' + cachekey + ':\n' + stackToString(stack);
+ }
+
+ var result = stack[0].expr;
+ xpathParseCache[cachekey] = result;
+
+ if (xpathdebug) {
+ Timer.end('XPath parse', cachekey);
+ }
+
+ if (xpathdebug) {
+ Log.write('XPath parse: ' + parse_count + ' / ' +
+ lexer_count + ' / ' + reduce_count);
+ }
+
+ return result;
+}
+
+var xpathParseCache = {};
+
+function xpathCacheLookup(expr) {
+ return xpathParseCache[expr];
+}
+
+function xpathReduce(stack, ahead) {
+ var cand = null;
+
+ if (stack.length > 0) {
+ var top = stack[stack.length-1];
+ var ruleset = xpathRules[top.tag.key];
+
+ if (ruleset) {
+ for (var i = 0; i < ruleset.length; ++i) {
+ var rule = ruleset[i];
+ var match = xpathMatchStack(stack, rule[1]);
+ if (match.length) {
+ cand = {
+ tag: rule[0],
+ rule: rule,
+ match: match
+ };
+ cand.prec = xpathGrammarPrecedence(cand);
+ break;
+ }
+ }
+ }
+ }
+
+ var ret;
+ if (cand && (!ahead || cand.prec > ahead.prec ||
+ (ahead.tag.left && cand.prec >= ahead.prec))) {
+ for (var i = 0; i < cand.match.matchlength; ++i) {
+ stack.pop();
+ }
+
+ if (xpathdebug) {
+ Log.write('reduce ' + cand.tag.label + ' ' + cand.prec +
+ ' ahead ' + (ahead ? ahead.tag.label + ' ' + ahead.prec +
+ (ahead.tag.left ? ' left' : '')
+ : ' none '));
+ }
+
+ var matchexpr = mapExpr(cand.match, function(m) { return m.expr; });
+ cand.expr = cand.rule[3].apply(null, matchexpr);
+
+ stack.push(cand);
+ ret = true;
+
+ } else {
+ if (ahead) {
+ if (xpathdebug) {
+ Log.write('shift ' + ahead.tag.label + ' ' + ahead.prec +
+ (ahead.tag.left ? ' left' : '') +
+ ' over ' + (cand ? cand.tag.label + ' ' +
+ cand.prec : ' none'));
+ }
+ stack.push(ahead);
+ }
+ ret = false;
+ }
+ return ret;
+}
+
+function xpathMatchStack(stack, pattern) {
+
+ // NOTE(mesch): The stack matches for variable cardinality are
+ // greedy but don't do backtracking. This would be an issue only
+ // with rules of the form A* A, i.e. with an element with variable
+ // cardinality followed by the same element. Since that doesn't
+ // occur in the grammar at hand, all matches on the stack are
+ // unambiguous.
+
+ var S = stack.length;
+ var P = pattern.length;
+ var p, s;
+ var match = [];
+ match.matchlength = 0;
+ var ds = 0;
+ for (p = P - 1, s = S - 1; p >= 0 && s >= 0; --p, s -= ds) {
+ ds = 0;
+ var qmatch = [];
+ if (pattern[p] == Q_MM) {
+ p -= 1;
+ match.push(qmatch);
+ while (s - ds >= 0 && stack[s - ds].tag == pattern[p]) {
+ qmatch.push(stack[s - ds]);
+ ds += 1;
+ match.matchlength += 1;
+ }
+
+ } else if (pattern[p] == Q_01) {
+ p -= 1;
+ match.push(qmatch);
+ while (s - ds >= 0 && ds < 2 && stack[s - ds].tag == pattern[p]) {
+ qmatch.push(stack[s - ds]);
+ ds += 1;
+ match.matchlength += 1;
+ }
+
+ } else if (pattern[p] == Q_1M) {
+ p -= 1;
+ match.push(qmatch);
+ if (stack[s].tag == pattern[p]) {
+ while (s - ds >= 0 && stack[s - ds].tag == pattern[p]) {
+ qmatch.push(stack[s - ds]);
+ ds += 1;
+ match.matchlength += 1;
+ }
+ } else {
+ return [];
+ }
+
+ } else if (stack[s].tag == pattern[p]) {
+ match.push(stack[s]);
+ ds += 1;
+ match.matchlength += 1;
+
+ } else {
+ return [];
+ }
+
+ reverseInplace(qmatch);
+ qmatch.expr = mapExpr(qmatch, function(m) { return m.expr; });
+ }
+
+ reverseInplace(match);
+
+ if (p == -1) {
+ return match;
+
+ } else {
+ return [];
+ }
+}
+
+function xpathTokenPrecedence(tag) {
+ return tag.prec || 2;
+}
+
+function xpathGrammarPrecedence(frame) {
+ var ret = 0;
+
+ if (frame.rule) { /* normal reduce */
+ if (frame.rule.length >= 3 && frame.rule[2] >= 0) {
+ ret = frame.rule[2];
+
+ } else {
+ for (var i = 0; i < frame.rule[1].length; ++i) {
+ var p = xpathTokenPrecedence(frame.rule[1][i]);
+ ret = Math.max(ret, p);
+ }
+ }
+ } else if (frame.tag) { /* TOKEN match */
+ ret = xpathTokenPrecedence(frame.tag);
+
+ } else if (frame.length) { /* Q_ match */
+ for (var j = 0; j < frame.length; ++j) {
+ var p = xpathGrammarPrecedence(frame[j]);
+ ret = Math.max(ret, p);
+ }
+ }
+
+ return ret;
+}
+
+function stackToString(stack) {
+ var ret = '';
+ for (var i = 0; i < stack.length; ++i) {
+ if (ret) {
+ ret += '\n';
+ }
+ ret += stack[i].tag.label;
+ }
+ return ret;
+}
+
+
+// XPath expression evaluation context. An XPath context consists of a
+// DOM node, a list of DOM nodes that contains this node, a number
+// that represents the position of the single node in the list, and a
+// current set of variable bindings. (See XPath spec.)
+//
+// The interface of the expression context:
+//
+// Constructor -- gets the node, its position, the node set it
+// belongs to, and a parent context as arguments. The parent context
+// is used to implement scoping rules for variables: if a variable
+// is not found in the current context, it is looked for in the
+// parent context, recursively. Except for node, all arguments have
+// default values: default position is 0, default node set is the
+// set that contains only the node, and the default parent is null.
+//
+// Notice that position starts at 0 at the outside interface;
+// inside XPath expressions this shows up as position()=1.
+//
+// clone() -- creates a new context with the current context as
+// parent. If passed as argument to clone(), the new context has a
+// different node, position, or node set. What is not passed is
+// inherited from the cloned context.
+//
+// setVariable(name, expr) -- binds given XPath expression to the
+// name.
+//
+// getVariable(name) -- what the name says.
+//
+// setNode(node, position) -- sets the context to the new node and
+// its corresponding position. Needed to implement scoping rules for
+// variables in XPath. (A variable is visible to all subsequent
+// siblings, not only to its children.)
+
+function ExprContext(node, position, nodelist, parent) {
+ this.node = node;
+ this.position = position || 0;
+ this.nodelist = nodelist || [ node ];
+ this.variables = {};
+ this.parent = parent || null;
+ this.root = parent ? parent.root : node.ownerDocument;
+}
+
+ExprContext.prototype.clone = function(node, position, nodelist) {
+ return new
+ ExprContext(node || this.node,
+ typeof position != 'undefined' ? position : this.position,
+ nodelist || this.nodelist, this);
+};
+
+ExprContext.prototype.setVariable = function(name, value) {
+ this.variables[name] = value;
+};
+
+ExprContext.prototype.getVariable = function(name) {
+ if (typeof this.variables[name] != 'undefined') {
+ return this.variables[name];
+
+ } else if (this.parent) {
+ return this.parent.getVariable(name);
+
+ } else {
+ return null;
+ }
+}
+
+ExprContext.prototype.setNode = function(node, position) {
+ this.node = node;
+ this.position = position;
+}
+
+
+// XPath expression values. They are what XPath expressions evaluate
+// to. Strangely, the different value types are not specified in the
+// XPath syntax, but only in the semantics, so they don't show up as
+// nonterminals in the grammar. Yet, some expressions are required to
+// evaluate to particular types, and not every type can be coerced
+// into every other type. Although the types of XPath values are
+// similar to the types present in JavaScript, the type coercion rules
+// are a bit peculiar, so we explicitly model XPath types instead of
+// mapping them onto JavaScript types. (See XPath spec.)
+//
+// The four types are:
+//
+// StringValue
+//
+// NumberValue
+//
+// BooleanValue
+//
+// NodeSetValue
+//
+// The common interface of the value classes consists of methods that
+// implement the XPath type coercion rules:
+//
+// stringValue() -- returns the value as a JavaScript String,
+//
+// numberValue() -- returns the value as a JavaScript Number,
+//
+// booleanValue() -- returns the value as a JavaScript Boolean,
+//
+// nodeSetValue() -- returns the value as a JavaScript Array of DOM
+// Node objects.
+//
+
+function StringValue(value) {
+ this.value = value;
+ this.type = 'string';
+}
+
+StringValue.prototype.stringValue = function() {
+ return this.value;
+}
+
+StringValue.prototype.booleanValue = function() {
+ return this.value.length > 0;
+}
+
+StringValue.prototype.numberValue = function() {
+ return this.value - 0;
+}
+
+StringValue.prototype.nodeSetValue = function() {
+ throw this + ' ' + Error().stack;
+}
+
+function BooleanValue(value) {
+ this.value = value;
+ this.type = 'boolean';
+}
+
+BooleanValue.prototype.stringValue = function() {
+ return '' + this.value;
+}
+
+BooleanValue.prototype.booleanValue = function() {
+ return this.value;
+}
+
+BooleanValue.prototype.numberValue = function() {
+ return this.value ? 1 : 0;
+}
+
+BooleanValue.prototype.nodeSetValue = function() {
+ throw this + ' ' + Error().stack;
+}
+
+function NumberValue(value) {
+ this.value = value;
+ this.type = 'number';
+}
+
+NumberValue.prototype.stringValue = function() {
+ return '' + this.value;
+}
+
+NumberValue.prototype.booleanValue = function() {
+ return !!this.value;
+}
+
+NumberValue.prototype.numberValue = function() {
+ return this.value - 0;
+}
+
+NumberValue.prototype.nodeSetValue = function() {
+ throw this + ' ' + Error().stack;
+}
+
+function NodeSetValue(value) {
+ this.value = value;
+ this.type = 'node-set';
+}
+
+NodeSetValue.prototype.stringValue = function() {
+ if (this.value.length == 0) {
+ return '';
+ } else {
+ return xmlValue(this.value[0]);
+ }
+}
+
+NodeSetValue.prototype.booleanValue = function() {
+ return this.value.length > 0;
+}
+
+NodeSetValue.prototype.numberValue = function() {
+ return this.stringValue() - 0;
+}
+
+NodeSetValue.prototype.nodeSetValue = function() {
+ return this.value;
+};
+
+// XPath expressions. They are used as nodes in the parse tree and
+// possess an evaluate() method to compute an XPath value given an XPath
+// context. Expressions are returned from the parser. Teh set of
+// expression classes closely mirrors the set of non terminal symbols
+// in the grammar. Every non trivial nonterminal symbol has a
+// corresponding expression class.
+//
+// The common expression interface consists of the following methods:
+//
+// evaluate(context) -- evaluates the expression, returns a value.
+//
+// toString() -- returns the XPath text representation of the
+// expression (defined in xsltdebug.js).
+//
+// parseTree(indent) -- returns a parse tree representation of the
+// expression (defined in xsltdebug.js).
+
+function TokenExpr(m) {
+ this.value = m;
+}
+
+TokenExpr.prototype.evaluate = function() {
+ return new StringValue(this.value);
+};
+
+function LocationExpr() {
+ this.absolute = false;
+ this.steps = [];
+}
+
+LocationExpr.prototype.appendStep = function(s) {
+ this.steps.push(s);
+}
+
+LocationExpr.prototype.prependStep = function(s) {
+ var steps0 = this.steps;
+ this.steps = [ s ];
+ for (var i = 0; i < steps0.length; ++i) {
+ this.steps.push(steps0[i]);
+ }
+};
+
+LocationExpr.prototype.evaluate = function(ctx) {
+ var start;
+ if (this.absolute) {
+ start = ctx.root;
+
+ } else {
+ start = ctx.node;
+ }
+
+ var nodes = [];
+ xPathStep(nodes, this.steps, 0, start, ctx);
+ return new NodeSetValue(nodes);
+};
+
+function xPathStep(nodes, steps, step, input, ctx) {
+ var s = steps[step];
+ var ctx2 = ctx.clone(input);
+ var nodelist = s.evaluate(ctx2).nodeSetValue();
+
+ for (var i = 0; i < nodelist.length; ++i) {
+ if (step == steps.length - 1) {
+ nodes.push(nodelist[i]);
+ } else {
+ xPathStep(nodes, steps, step + 1, nodelist[i], ctx);
+ }
+ }
+}
+
+function StepExpr(axis, nodetest, predicate) {
+ this.axis = axis;
+ this.nodetest = nodetest;
+ this.predicate = predicate || [];
+}
+
+StepExpr.prototype.appendPredicate = function(p) {
+ this.predicate.push(p);
+}
+
+StepExpr.prototype.evaluate = function(ctx) {
+ var input = ctx.node;
+ var nodelist = [];
+
+ // NOTE(mesch): When this was a switch() statement, it didn't work
+ // in Safari/2.0. Not sure why though; it resulted in the JavaScript
+ // console output "undefined" (without any line number or so).
+
+ if (this.axis == xpathAxis.ANCESTOR_OR_SELF) {
+ nodelist.push(input);
+ for (var n = input.parentNode; n; n = input.parentNode) {
+ nodelist.push(n);
+ }
+
+ } else if (this.axis == xpathAxis.ANCESTOR) {
+ for (var n = input.parentNode; n; n = input.parentNode) {
+ nodelist.push(n);
+ }
+
+ } else if (this.axis == xpathAxis.ATTRIBUTE) {
+ copyArray(nodelist, input.attributes);
+
+ } else if (this.axis == xpathAxis.CHILD) {
+ copyArray(nodelist, input.childNodes);
+
+ } else if (this.axis == xpathAxis.DESCENDANT_OR_SELF) {
+ nodelist.push(input);
+ xpathCollectDescendants(nodelist, input);
+
+ } else if (this.axis == xpathAxis.DESCENDANT) {
+ xpathCollectDescendants(nodelist, input);
+
+ } else if (this.axis == xpathAxis.FOLLOWING) {
+ for (var n = input.parentNode; n; n = n.parentNode) {
+ for (var nn = n.nextSibling; nn; nn = nn.nextSibling) {
+ nodelist.push(nn);
+ xpathCollectDescendants(nodelist, nn);
+ }
+ }
+
+ } else if (this.axis == xpathAxis.FOLLOWING_SIBLING) {
+ for (var n = input.nextSibling; n; n = input.nextSibling) {
+ nodelist.push(n);
+ }
+
+ } else if (this.axis == xpathAxis.NAMESPACE) {
+ alert('not implemented: axis namespace');
+
+ } else if (this.axis == xpathAxis.PARENT) {
+ if (input.parentNode) {
+ nodelist.push(input.parentNode);
+ }
+
+ } else if (this.axis == xpathAxis.PRECEDING) {
+ for (var n = input.parentNode; n; n = n.parentNode) {
+ for (var nn = n.previousSibling; nn; nn = nn.previousSibling) {
+ nodelist.push(nn);
+ xpathCollectDescendantsReverse(nodelist, nn);
+ }
+ }
+
+ } else if (this.axis == xpathAxis.PRECEDING_SIBLING) {
+ for (var n = input.previousSibling; n; n = input.previousSibling) {
+ nodelist.push(n);
+ }
+
+ } else if (this.axis == xpathAxis.SELF) {
+ nodelist.push(input);
+
+ } else {
+ throw 'ERROR -- NO SUCH AXIS: ' + this.axis;
+ }
+
+ // process node test
+ var nodelist0 = nodelist;
+ nodelist = [];
+ for (var i = 0; i < nodelist0.length; ++i) {
+ var n = nodelist0[i];
+ if (this.nodetest.evaluate(ctx.clone(n, i, nodelist0)).booleanValue()) {
+ nodelist.push(n);
+ }
+ }
+
+ // process predicates
+ for (var i = 0; i < this.predicate.length; ++i) {
+ var nodelist0 = nodelist;
+ nodelist = [];
+ for (var ii = 0; ii < nodelist0.length; ++ii) {
+ var n = nodelist0[ii];
+ if (this.predicate[i].evaluate(ctx.clone(n, ii, nodelist0)).booleanValue()) {
+ nodelist.push(n);
+ }
+ }
+ }
+
+ return new NodeSetValue(nodelist);
+};
+
+function NodeTestAny() {
+ this.value = new BooleanValue(true);
+}
+
+NodeTestAny.prototype.evaluate = function(ctx) {
+ return this.value;
+};
+
+function NodeTestElement() {}
+
+NodeTestElement.prototype.evaluate = function(ctx) {
+ return new BooleanValue(ctx.node.nodeType == DOM_ELEMENT_NODE);
+}
+
+function NodeTestText() {}
+
+NodeTestText.prototype.evaluate = function(ctx) {
+ return new BooleanValue(ctx.node.nodeType == DOM_TEXT_NODE);
+}
+
+function NodeTestComment() {}
+
+NodeTestComment.prototype.evaluate = function(ctx) {
+ return new BooleanValue(ctx.node.nodeType == DOM_COMMENT_NODE);
+}
+
+function NodeTestPI(target) {
+ this.target = target;
+}
+
+NodeTestPI.prototype.evaluate = function(ctx) {
+ return new
+ BooleanValue(ctx.node.nodeType == DOM_PROCESSING_INSTRUCTION_NODE &&
+ (!this.target || ctx.node.nodeName == this.target));
+}
+
+function NodeTestNC(nsprefix) {
+ this.regex = new RegExp("^" + nsprefix + ":");
+ this.nsprefix = nsprefix;
+}
+
+NodeTestNC.prototype.evaluate = function(ctx) {
+ var n = ctx.node;
+ return new BooleanValue(this.regex.match(n.nodeName));
+}
+
+function NodeTestName(name) {
+ this.name = name;
+}
+
+NodeTestName.prototype.evaluate = function(ctx) {
+ var n = ctx.node;
+ // NOTE (Patrick Lightbody): this change allows node selection to be case-insensitive
+ return new BooleanValue(n.nodeName.toUpperCase() == this.name.toUpperCase());
+}
+
+function PredicateExpr(expr) {
+ this.expr = expr;
+}
+
+PredicateExpr.prototype.evaluate = function(ctx) {
+ var v = this.expr.evaluate(ctx);
+ if (v.type == 'number') {
+ // NOTE(mesch): Internally, position is represented starting with
+ // 0, however in XPath position starts with 1. See functions
+ // position() and last().
+ return new BooleanValue(ctx.position == v.numberValue() - 1);
+ } else {
+ return new BooleanValue(v.booleanValue());
+ }
+};
+
+function FunctionCallExpr(name) {
+ this.name = name;
+ this.args = [];
+}
+
+FunctionCallExpr.prototype.appendArg = function(arg) {
+ this.args.push(arg);
+};
+
+FunctionCallExpr.prototype.evaluate = function(ctx) {
+ var fn = '' + this.name.value;
+ var f = this.xpathfunctions[fn];
+ if (f) {
+ return f.call(this, ctx);
+ } else {
+ Log.write('XPath NO SUCH FUNCTION ' + fn);
+ return new BooleanValue(false);
+ }
+};
+
+FunctionCallExpr.prototype.xpathfunctions = {
+ 'last': function(ctx) {
+ assert(this.args.length == 0);
+ // NOTE(mesch): XPath position starts at 1.
+ return new NumberValue(ctx.nodelist.length);
+ },
+
+ 'position': function(ctx) {
+ assert(this.args.length == 0);
+ // NOTE(mesch): XPath position starts at 1.
+ return new NumberValue(ctx.position + 1);
+ },
+
+ 'count': function(ctx) {
+ assert(this.args.length == 1);
+ var v = this.args[0].evaluate(ctx);
+ return new NumberValue(v.nodeSetValue().length);
+ },
+
+ 'id': function(ctx) {
+ assert(this.args.length == 1);
+ var e = this.args.evaluate(ctx);
+ var ret = [];
+ var ids;
+ if (e.type == 'node-set') {
+ ids = [];
+ for (var i = 0; i < e.length; ++i) {
+ var v = xmlValue(e[i]).split(/\s+/);
+ for (var ii = 0; ii < v.length; ++ii) {
+ ids.push(v[ii]);
+ }
+ }
+ } else {
+ ids = e.split(/\s+/);
+ }
+ var d = ctx.node.ownerDocument;
+ for (var i = 0; i < ids.length; ++i) {
+ var n = d.getElementById(ids[i]);
+ if (n) {
+ ret.push(n);
+ }
+ }
+ return new NodeSetValue(ret);
+ },
+
+ 'local-name': function(ctx) {
+ alert('not implmented yet: XPath function local-name()');
+ },
+
+ 'namespace-uri': function(ctx) {
+ alert('not implmented yet: XPath function namespace-uri()');
+ },
+
+ 'name': function(ctx) {
+ assert(this.args.length == 1 || this.args.length == 0);
+ var n;
+ if (this.args.length == 0) {
+ n = [ ctx.node ];
+ } else {
+ n = this.args[0].evaluate(ctx).nodeSetValue();
+ }
+
+ if (n.length == 0) {
+ return new StringValue('');
+ } else {
+ return new StringValue(n[0].nodeName);
+ }
+ },
+
+ 'string': function(ctx) {
+ assert(this.args.length == 1 || this.args.length == 0);
+ if (this.args.length == 0) {
+ return new StringValue(new NodeSetValue([ ctx.node ]).stringValue());
+ } else {
+ return new StringValue(this.args[0].evaluate(ctx).stringValue());
+ }
+ },
+
+ 'concat': function(ctx) {
+ var ret = '';
+ for (var i = 0; i < this.args.length; ++i) {
+ ret += this.args[i].evaluate(ctx).stringValue();
+ }
+ return new StringValue(ret);
+ },
+
+ 'starts-with': function(ctx) {
+ assert(this.args.length == 2);
+ var s0 = this.args[0].evaluate(ctx).stringValue();
+ var s1 = this.args[1].evaluate(ctx).stringValue();
+ return new BooleanValue(s0.indexOf(s1) == 0);
+ },
+
+ 'contains': function(ctx) {
+ assert(this.args.length == 2);
+ var s0 = this.args[0].evaluate(ctx).stringValue();
+ var s1 = this.args[1].evaluate(ctx).stringValue();
+ return new BooleanValue(s0.indexOf(s1) != -1);
+ },
+
+ 'substring-before': function(ctx) {
+ assert(this.args.length == 2);
+ var s0 = this.args[0].evaluate(ctx).stringValue();
+ var s1 = this.args[1].evaluate(ctx).stringValue();
+ var i = s0.indexOf(s1);
+ var ret;
+ if (i == -1) {
+ ret = '';
+ } else {
+ ret = s0.substr(0,i);
+ }
+ return new StringValue(ret);
+ },
+
+ 'substring-after': function(ctx) {
+ assert(this.args.length == 2);
+ var s0 = this.args[0].evaluate(ctx).stringValue();
+ var s1 = this.args[1].evaluate(ctx).stringValue();
+ var i = s0.indexOf(s1);
+ var ret;
+ if (i == -1) {
+ ret = '';
+ } else {
+ ret = s0.substr(i + s1.length);
+ }
+ return new StringValue(ret);
+ },
+
+ 'substring': function(ctx) {
+ // NOTE: XPath defines the position of the first character in a
+ // string to be 1, in JavaScript this is 0 ([XPATH] Section 4.2).
+ assert(this.args.length == 2 || this.args.length == 3);
+ var s0 = this.args[0].evaluate(ctx).stringValue();
+ var s1 = this.args[1].evaluate(ctx).numberValue();
+ var ret;
+ if (this.args.length == 2) {
+ var i1 = Math.max(0, Math.round(s1) - 1);
+ ret = s0.substr(i1);
+
+ } else {
+ var s2 = this.args[2].evaluate(ctx).numberValue();
+ var i0 = Math.round(s1) - 1;
+ var i1 = Math.max(0, i0);
+ var i2 = Math.round(s2) - Math.max(0, -i0);
+ ret = s0.substr(i1, i2);
+ }
+ return new StringValue(ret);
+ },
+
+ 'string-length': function(ctx) {
+ var s;
+ if (this.args.length > 0) {
+ s = this.args[0].evaluate(ctx).stringValue();
+ } else {
+ s = new NodeSetValue([ ctx.node ]).stringValue();
+ }
+ return new NumberValue(s.length);
+ },
+
+ 'normalize-space': function(ctx) {
+ var s;
+ if (this.args.length > 0) {
+ s = this.args[0].evaluate(ctx).stringValue();
+ } else {
+ s = new NodeSetValue([ ctx.node ]).stringValue();
+ }
+ s = s.replace(/^\s*/,'').replace(/\s*$/,'').replace(/\s+/g, ' ');
+ return new StringValue(s);
+ },
+
+ 'translate': function(ctx) {
+ assert(this.args.length == 3);
+ var s0 = this.args[0].evaluate(ctx).stringValue();
+ var s1 = this.args[1].evaluate(ctx).stringValue();
+ var s2 = this.args[2].evaluate(ctx).stringValue();
+
+ for (var i = 0; i < s1.length; ++i) {
+ s0 = s0.replace(new RegExp(s1.charAt(i), 'g'), s2.charAt(i));
+ }
+ return new StringValue(s0);
+ },
+
+ 'boolean': function(ctx) {
+ assert(this.args.length == 1);
+ return new BooleanValue(this.args[0].evaluate(ctx).booleanValue());
+ },
+
+ 'not': function(ctx) {
+ assert(this.args.length == 1);
+ var ret = !this.args[0].evaluate(ctx).booleanValue();
+ return new BooleanValue(ret);
+ },
+
+ 'true': function(ctx) {
+ assert(this.args.length == 0);
+ return new BooleanValue(true);
+ },
+
+ 'false': function(ctx) {
+ assert(this.args.length == 0);
+ return new BooleanValue(false);
+ },
+
+ 'lang': function(ctx) {
+ assert(this.args.length == 1);
+ var lang = this.args[0].evaluate(ctx).stringValue();
+ var xmllang;
+ var n = ctx.node;
+ while (n && n != n.parentNode /* just in case ... */) {
+ xmllang = n.getAttribute('xml:lang');
+ if (xmllang) {
+ break;
+ }
+ n = n.parentNode;
+ }
+ if (!xmllang) {
+ return new BooleanValue(false);
+ } else {
+ var re = new RegExp('^' + lang + '$', 'i');
+ return new BooleanValue(xmllang.match(re) ||
+ xmllang.replace(/_.*$/,'').match(re));
+ }
+ },
+
+ 'number': function(ctx) {
+ assert(this.args.length == 1 || this.args.length == 0);
+
+ if (this.args.length == 1) {
+ return new NumberValue(this.args[0].evaluate(ctx).numberValue());
+ } else {
+ return new NumberValue(new NodeSetValue([ ctx.node ]).numberValue());
+ }
+ },
+
+ 'sum': function(ctx) {
+ assert(this.args.length == 1);
+ var n = this.args[0].evaluate(ctx).nodeSetValue();
+ var sum = 0;
+ for (var i = 0; i < n.length; ++i) {
+ sum += xmlValue(n[i]) - 0;
+ }
+ return new NumberValue(sum);
+ },
+
+ 'floor': function(ctx) {
+ assert(this.args.length == 1);
+ var num = this.args[0].evaluate(ctx).numberValue();
+ return new NumberValue(Math.floor(num));
+ },
+
+ 'ceiling': function(ctx) {
+ assert(this.args.length == 1);
+ var num = this.args[0].evaluate(ctx).numberValue();
+ return new NumberValue(Math.ceil(num));
+ },
+
+ 'round': function(ctx) {
+ assert(this.args.length == 1);
+ var num = this.args[0].evaluate(ctx).numberValue();
+ return new NumberValue(Math.round(num));
+ },
+
+ // TODO(mesch): The following functions are custom. There is a
+ // standard that defines how to add functions, which should be
+ // applied here.
+
+ 'ext-join': function(ctx) {
+ assert(this.args.length == 2);
+ var nodes = this.args[0].evaluate(ctx).nodeSetValue();
+ var delim = this.args[1].evaluate(ctx).stringValue();
+ var ret = '';
+ for (var i = 0; i < nodes.length; ++i) {
+ if (ret) {
+ ret += delim;
+ }
+ ret += xmlValue(nodes[i]);
+ }
+ return new StringValue(ret);
+ },
+
+ // ext-if() evaluates and returns its second argument, if the
+ // boolean value of its first argument is true, otherwise it
+ // evaluates and returns its third argument.
+
+ 'ext-if': function(ctx) {
+ assert(this.args.length == 3);
+ if (this.args[0].evaluate(ctx).booleanValue()) {
+ return this.args[1].evaluate(ctx);
+ } else {
+ return this.args[2].evaluate(ctx);
+ }
+ },
+
+ 'ext-sprintf': function(ctx) {
+ assert(this.args.length >= 1);
+ var args = [];
+ for (var i = 0; i < this.args.length; ++i) {
+ args.push(this.args[i].evaluate(ctx).stringValue());
+ }
+ return new StringValue(sprintf.apply(null, args));
+ },
+
+ // ext-cardinal() evaluates its single argument as a number, and
+ // returns the current node that many times. It can be used in the
+ // select attribute to iterate over an integer range.
+
+ 'ext-cardinal': function(ctx) {
+ assert(this.args.length >= 1);
+ var c = this.args[0].evaluate(ctx).numberValue();
+ var ret = [];
+ for (var i = 0; i < c; ++i) {
+ ret.push(ctx.node);
+ }
+ return new NodeSetValue(ret);
+ }
+};
+
+function UnionExpr(expr1, expr2) {
+ this.expr1 = expr1;
+ this.expr2 = expr2;
+}
+
+UnionExpr.prototype.evaluate = function(ctx) {
+ var nodes1 = this.expr1.evaluate(ctx).nodeSetValue();
+ var nodes2 = this.expr2.evaluate(ctx).nodeSetValue();
+ var I1 = nodes1.length;
+ for (var i2 = 0; i2 < nodes2.length; ++i2) {
+ for (var i1 = 0; i1 < I1; ++i1) {
+ if (nodes1[i1] == nodes2[i2]) {
+ // break inner loop and continue outer loop, labels confuse
+ // the js compiler, so we don't use them here.
+ i1 = I1;
+ }
+ }
+ nodes1.push(nodes2[i2]);
+ }
+ return new NodeSetValue(nodes2);
+};
+
+function PathExpr(filter, rel) {
+ this.filter = filter;
+ this.rel = rel;
+}
+
+PathExpr.prototype.evaluate = function(ctx) {
+ var nodes = this.filter.evaluate(ctx).nodeSetValue();
+ var nodes1 = [];
+ for (var i = 0; i < nodes.length; ++i) {
+ var nodes0 = this.rel.evaluate(ctx.clone(nodes[i], i, nodes)).nodeSetValue();
+ for (var ii = 0; ii < nodes0.length; ++ii) {
+ nodes1.push(nodes0[ii]);
+ }
+ }
+ return new NodeSetValue(nodes1);
+};
+
+function FilterExpr(expr, predicate) {
+ this.expr = expr;
+ this.predicate = predicate;
+}
+
+FilterExpr.prototype.evaluate = function(ctx) {
+ var nodes = this.expr.evaluate(ctx).nodeSetValue();
+ for (var i = 0; i < this.predicate.length; ++i) {
+ var nodes0 = nodes;
+ nodes = [];
+ for (var j = 0; j < nodes0.length; ++j) {
+ var n = nodes0[j];
+ if (this.predicate[i].evaluate(ctx.clone(n, j, nodes0)).booleanValue()) {
+ nodes.push(n);
+ }
+ }
+ }
+
+ return new NodeSetValue(nodes);
+}
+
+function UnaryMinusExpr(expr) {
+ this.expr = expr;
+}
+
+UnaryMinusExpr.prototype.evaluate = function(ctx) {
+ return new NumberValue(-this.expr.evaluate(ctx).numberValue());
+};
+
+function BinaryExpr(expr1, op, expr2) {
+ this.expr1 = expr1;
+ this.expr2 = expr2;
+ this.op = op;
+}
+
+BinaryExpr.prototype.evaluate = function(ctx) {
+ var ret;
+ switch (this.op.value) {
+ case 'or':
+ ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() ||
+ this.expr2.evaluate(ctx).booleanValue());
+ break;
+
+ case 'and':
+ ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() &&
+ this.expr2.evaluate(ctx).booleanValue());
+ break;
+
+ case '+':
+ ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() +
+ this.expr2.evaluate(ctx).numberValue());
+ break;
+
+ case '-':
+ ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() -
+ this.expr2.evaluate(ctx).numberValue());
+ break;
+
+ case '*':
+ ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() *
+ this.expr2.evaluate(ctx).numberValue());
+ break;
+
+ case 'mod':
+ ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() %
+ this.expr2.evaluate(ctx).numberValue());
+ break;
+
+ case 'div':
+ ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() /
+ this.expr2.evaluate(ctx).numberValue());
+ break;
+
+ case '=':
+ ret = this.compare(ctx, function(x1, x2) { return x1 == x2; });
+ break;
+
+ case '!=':
+ ret = this.compare(ctx, function(x1, x2) { return x1 != x2; });
+ break;
+
+ case '<':
+ ret = this.compare(ctx, function(x1, x2) { return x1 < x2; });
+ break;
+
+ case '<=':
+ ret = this.compare(ctx, function(x1, x2) { return x1 <= x2; });
+ break;
+
+ case '>':
+ ret = this.compare(ctx, function(x1, x2) { return x1 > x2; });
+ break;
+
+ case '>=':
+ ret = this.compare(ctx, function(x1, x2) { return x1 >= x2; });
+ break;
+
+ default:
+ alert('BinaryExpr.evaluate: ' + this.op.value);
+ }
+ return ret;
+};
+
+BinaryExpr.prototype.compare = function(ctx, cmp) {
+ var v1 = this.expr1.evaluate(ctx);
+ var v2 = this.expr2.evaluate(ctx);
+
+ var ret;
+ if (v1.type == 'node-set' && v2.type == 'node-set') {
+ var n1 = v1.nodeSetValue();
+ var n2 = v2.nodeSetValue();
+ ret = false;
+ for (var i1 = 0; i1 < n1.length; ++i1) {
+ for (var i2 = 0; i2 < n2.length; ++i2) {
+ if (cmp(xmlValue(n1[i1]), xmlValue(n2[i2]))) {
+ ret = true;
+ // Break outer loop. Labels confuse the jscompiler and we
+ // don't use them.
+ i2 = n2.length;
+ i1 = n1.length;
+ }
+ }
+ }
+
+ } else if (v1.type == 'node-set' || v2.type == 'node-set') {
+
+ if (v1.type == 'number') {
+ var s = v1.numberValue();
+ var n = v2.nodeSetValue();
+
+ ret = false;
+ for (var i = 0; i < n.length; ++i) {
+ var nn = xmlValue(n[i]) - 0;
+ if (cmp(s, nn)) {
+ ret = true;
+ break;
+ }
+ }
+
+ } else if (v2.type == 'number') {
+ var n = v1.nodeSetValue();
+ var s = v2.numberValue();
+
+ ret = false;
+ for (var i = 0; i < n.length; ++i) {
+ var nn = xmlValue(n[i]) - 0;
+ if (cmp(nn, s)) {
+ ret = true;
+ break;
+ }
+ }
+
+ } else if (v1.type == 'string') {
+ var s = v1.stringValue();
+ var n = v2.nodeSetValue();
+
+ ret = false;
+ for (var i = 0; i < n.length; ++i) {
+ var nn = xmlValue(n[i]);
+ if (cmp(s, nn)) {
+ ret = true;
+ break;
+ }
+ }
+
+ } else if (v2.type == 'string') {
+ var n = v1.nodeSetValue();
+ var s = v2.stringValue();
+
+ ret = false;
+ for (var i = 0; i < n.length; ++i) {
+ var nn = xmlValue(n[i]);
+ if (cmp(nn, s)) {
+ ret = true;
+ break;
+ }
+ }
+
+ } else {
+ ret = cmp(v1.booleanValue(), v2.booleanValue());
+ }
+
+ } else if (v1.type == 'boolean' || v2.type == 'boolean') {
+ ret = cmp(v1.booleanValue(), v2.booleanValue());
+
+ } else if (v1.type == 'number' || v2.type == 'number') {
+ ret = cmp(v1.numberValue(), v2.numberValue());
+
+ } else {
+ ret = cmp(v1.stringValue(), v2.stringValue());
+ }
+
+ return new BooleanValue(ret);
+}
+
+function LiteralExpr(value) {
+ this.value = value;
+}
+
+LiteralExpr.prototype.evaluate = function(ctx) {
+ return new StringValue(this.value);
+};
+
+function NumberExpr(value) {
+ this.value = value;
+}
+
+NumberExpr.prototype.evaluate = function(ctx) {
+ return new NumberValue(this.value);
+};
+
+function VariableExpr(name) {
+ this.name = name;
+}
+
+VariableExpr.prototype.evaluate = function(ctx) {
+ return ctx.getVariable(this.name);
+}
+
+// Factory functions for semantic values (i.e. Expressions) of the
+// productions in the grammar. When a production is matched to reduce
+// the current parse state stack, the function is called with the
+// semantic values of the matched elements as arguments, and returns
+// another semantic value. The semantic value is a node of the parse
+// tree, an expression object with an evaluate() method that evaluates the
+// expression in an actual context. These factory functions are used
+// in the specification of the grammar rules, below.
+
+function makeTokenExpr(m) {
+ return new TokenExpr(m);
+}
+
+function passExpr(e) {
+ return e;
+}
+
+function makeLocationExpr1(slash, rel) {
+ rel.absolute = true;
+ return rel;
+}
+
+function makeLocationExpr2(dslash, rel) {
+ rel.absolute = true;
+ rel.prependStep(makeAbbrevStep(dslash.value));
+ return rel;
+}
+
+function makeLocationExpr3(slash) {
+ var ret = new LocationExpr();
+ ret.appendStep(makeAbbrevStep('.'));
+ ret.absolute = true;
+ return ret;
+}
+
+function makeLocationExpr4(dslash) {
+ var ret = new LocationExpr();
+ ret.absolute = true;
+ ret.appendStep(makeAbbrevStep(dslash.value));
+ return ret;
+}
+
+function makeLocationExpr5(step) {
+ var ret = new LocationExpr();
+ ret.appendStep(step);
+ return ret;
+}
+
+function makeLocationExpr6(rel, slash, step) {
+ rel.appendStep(step);
+ return rel;
+}
+
+function makeLocationExpr7(rel, dslash, step) {
+ rel.appendStep(makeAbbrevStep(dslash.value));
+ return rel;
+}
+
+function makeStepExpr1(dot) {
+ return makeAbbrevStep(dot.value);
+}
+
+function makeStepExpr2(ddot) {
+ return makeAbbrevStep(ddot.value);
+}
+
+function makeStepExpr3(axisname, axis, nodetest) {
+ return new StepExpr(axisname.value, nodetest);
+}
+
+function makeStepExpr4(at, nodetest) {
+ return new StepExpr('attribute', nodetest);
+}
+
+function makeStepExpr5(nodetest) {
+ return new StepExpr('child', nodetest);
+}
+
+function makeStepExpr6(step, predicate) {
+ step.appendPredicate(predicate);
+ return step;
+}
+
+function makeAbbrevStep(abbrev) {
+ switch (abbrev) {
+ case '//':
+ return new StepExpr('descendant-or-self', new NodeTestAny);
+
+ case '.':
+ return new StepExpr('self', new NodeTestAny);
+
+ case '..':
+ return new StepExpr('parent', new NodeTestAny);
+ }
+}
+
+function makeNodeTestExpr1(asterisk) {
+ return new NodeTestElement;
+}
+
+function makeNodeTestExpr2(ncname, colon, asterisk) {
+ return new NodeTestNC(ncname.value);
+}
+
+function makeNodeTestExpr3(qname) {
+ return new NodeTestName(qname.value);
+}
+
+function makeNodeTestExpr4(typeo, parenc) {
+ var type = typeo.value.replace(/\s*\($/, '');
+ switch(type) {
+ case 'node':
+ return new NodeTestAny;
+
+ case 'text':
+ return new NodeTestText;
+
+ case 'comment':
+ return new NodeTestComment;
+
+ case 'processing-instruction':
+ return new NodeTestPI;
+ }
+}
+
+function makeNodeTestExpr5(typeo, target, parenc) {
+ var type = typeo.replace(/\s*\($/, '');
+ if (type != 'processing-instruction') {
+ throw type + ' ' + Error().stack;
+ }
+ return new NodeTestPI(target.value);
+}
+
+function makePredicateExpr(pareno, expr, parenc) {
+ return new PredicateExpr(expr);
+}
+
+function makePrimaryExpr(pareno, expr, parenc) {
+ return expr;
+}
+
+function makeFunctionCallExpr1(name, pareno, parenc) {
+ return new FunctionCallExpr(name);
+}
+
+function makeFunctionCallExpr2(name, pareno, arg1, args, parenc) {
+ var ret = new FunctionCallExpr(name);
+ ret.appendArg(arg1);
+ for (var i = 0; i < args.length; ++i) {
+ ret.appendArg(args[i]);
+ }
+ return ret;
+}
+
+function makeArgumentExpr(comma, expr) {
+ return expr;
+}
+
+function makeUnionExpr(expr1, pipe, expr2) {
+ return new UnionExpr(expr1, expr2);
+}
+
+function makePathExpr1(filter, slash, rel) {
+ return new PathExpr(filter, rel);
+}
+
+function makePathExpr2(filter, dslash, rel) {
+ rel.prependStep(makeAbbrevStep(dslash.value));
+ return new PathExpr(filter, rel);
+}
+
+function makeFilterExpr(expr, predicates) {
+ if (predicates.length > 0) {
+ return new FilterExpr(expr, predicates);
+ } else {
+ return expr;
+ }
+}
+
+function makeUnaryMinusExpr(minus, expr) {
+ return new UnaryMinusExpr(expr);
+}
+
+function makeBinaryExpr(expr1, op, expr2) {
+ return new BinaryExpr(expr1, op, expr2);
+}
+
+function makeLiteralExpr(token) {
+ // remove quotes from the parsed value:
+ var value = token.value.substring(1, token.value.length - 1);
+ return new LiteralExpr(value);
+}
+
+function makeNumberExpr(token) {
+ return new NumberExpr(token.value);
+}
+
+function makeVariableReference(dollar, name) {
+ return new VariableExpr(name.value);
+}
+
+// Used before parsing for optimization of common simple cases. See
+// the begin of xpathParse() for which they are.
+function makeSimpleExpr(expr) {
+ if (expr.charAt(0) == '$') {
+ return new VariableExpr(expr.substr(1));
+ } else if (expr.charAt(0) == '@') {
+ var a = new NodeTestName(expr.substr(1));
+ var b = new StepExpr('attribute', a);
+ var c = new LocationExpr();
+ c.appendStep(b);
+ return c;
+ } else if (expr.match(/^[0-9]+$/)) {
+ return new NumberExpr(expr);
+ } else {
+ var a = new NodeTestName(expr);
+ var b = new StepExpr('child', a);
+ var c = new LocationExpr();
+ c.appendStep(b);
+ return c;
+ }
+}
+
+function makeSimpleExpr2(expr) {
+ var steps = expr.split('/');
+ var c = new LocationExpr();
+ for (var i in steps) {
+ var a = new NodeTestName(steps[i]);
+ var b = new StepExpr('child', a);
+ c.appendStep(b);
+ }
+ return c;
+}
+
+// The axes of XPath expressions.
+
+var xpathAxis = {
+ ANCESTOR_OR_SELF: 'ancestor-or-self',
+ ANCESTOR: 'ancestor',
+ ATTRIBUTE: 'attribute',
+ CHILD: 'child',
+ DESCENDANT_OR_SELF: 'descendant-or-self',
+ DESCENDANT: 'descendant',
+ FOLLOWING_SIBLING: 'following-sibling',
+ FOLLOWING: 'following',
+ NAMESPACE: 'namespace',
+ PARENT: 'parent',
+ PRECEDING_SIBLING: 'preceding-sibling',
+ PRECEDING: 'preceding',
+ SELF: 'self'
+};
+
+var xpathAxesRe = [
+ xpathAxis.ANCESTOR_OR_SELF,
+ xpathAxis.ANCESTOR,
+ xpathAxis.ATTRIBUTE,
+ xpathAxis.CHILD,
+ xpathAxis.DESCENDANT_OR_SELF,
+ xpathAxis.DESCENDANT,
+ xpathAxis.FOLLOWING_SIBLING,
+ xpathAxis.FOLLOWING,
+ xpathAxis.NAMESPACE,
+ xpathAxis.PARENT,
+ xpathAxis.PRECEDING_SIBLING,
+ xpathAxis.PRECEDING,
+ xpathAxis.SELF
+].join('|');
+
+
+// The tokens of the language. The label property is just used for
+// generating debug output. The prec property is the precedence used
+// for shift/reduce resolution. Default precedence is 0 as a lookahead
+// token and 2 on the stack. TODO(mesch): this is certainly not
+// necessary and too complicated. Simplify this!
+
+// NOTE: tabular formatting is the big exception, but here it should
+// be OK.
+
+var TOK_PIPE = { label: "|", prec: 17, re: new RegExp("^\\|") };
+var TOK_DSLASH = { label: "//", prec: 19, re: new RegExp("^//") };
+var TOK_SLASH = { label: "/", prec: 30, re: new RegExp("^/") };
+var TOK_AXIS = { label: "::", prec: 20, re: new RegExp("^::") };
+var TOK_COLON = { label: ":", prec: 1000, re: new RegExp("^:") };
+var TOK_AXISNAME = { label: "[axis]", re: new RegExp('^(' + xpathAxesRe + ')') };
+var TOK_PARENO = { label: "(", prec: 34, re: new RegExp("^\\(") };
+var TOK_PARENC = { label: ")", re: new RegExp("^\\)") };
+var TOK_DDOT = { label: "..", prec: 34, re: new RegExp("^\\.\\.") };
+var TOK_DOT = { label: ".", prec: 34, re: new RegExp("^\\.") };
+var TOK_AT = { label: "@", prec: 34, re: new RegExp("^@") };
+
+var TOK_COMMA = { label: ",", re: new RegExp("^,") };
+
+var TOK_OR = { label: "or", prec: 10, re: new RegExp("^or\\b") };
+var TOK_AND = { label: "and", prec: 11, re: new RegExp("^and\\b") };
+var TOK_EQ = { label: "=", prec: 12, re: new RegExp("^=") };
+var TOK_NEQ = { label: "!=", prec: 12, re: new RegExp("^!=") };
+var TOK_GE = { label: ">=", prec: 13, re: new RegExp("^>=") };
+var TOK_GT = { label: ">", prec: 13, re: new RegExp("^>") };
+var TOK_LE = { label: "<=", prec: 13, re: new RegExp("^<=") };
+var TOK_LT = { label: "<", prec: 13, re: new RegExp("^<") };
+var TOK_PLUS = { label: "+", prec: 14, re: new RegExp("^\\+"), left: true };
+var TOK_MINUS = { label: "-", prec: 14, re: new RegExp("^\\-"), left: true };
+var TOK_DIV = { label: "div", prec: 15, re: new RegExp("^div\\b"), left: true };
+var TOK_MOD = { label: "mod", prec: 15, re: new RegExp("^mod\\b"), left: true };
+
+var TOK_BRACKO = { label: "[", prec: 32, re: new RegExp("^\\[") };
+var TOK_BRACKC = { label: "]", re: new RegExp("^\\]") };
+var TOK_DOLLAR = { label: "$", re: new RegExp("^\\$") };
+
+var TOK_NCNAME = { label: "[ncname]", re: new RegExp('^[a-z][-\\w]*','i') };
+
+var TOK_ASTERISK = { label: "*", prec: 15, re: new RegExp("^\\*"), left: true };
+var TOK_LITERALQ = { label: "[litq]", prec: 20, re: new RegExp("^'[^\\']*'") };
+var TOK_LITERALQQ = {
+ label: "[litqq]",
+ prec: 20,
+ re: new RegExp('^"[^\\"]*"')
+};
+
+var TOK_NUMBER = {
+ label: "[number]",
+ prec: 35,
+ re: new RegExp('^\\d+(\\.\\d*)?') };
+
+var TOK_QNAME = {
+ label: "[qname]",
+ re: new RegExp('^([a-z][-\\w]*:)?[a-z][-\\w]*','i')
+};
+
+var TOK_NODEO = {
+ label: "[nodetest-start]",
+ re: new RegExp('^(processing-instruction|comment|text|node)\\(')
+};
+
+// The table of the tokens of our grammar, used by the lexer: first
+// column the tag, second column a regexp to recognize it in the
+// input, third column the precedence of the token, fourth column a
+// factory function for the semantic value of the token.
+//
+// NOTE: order of this list is important, because the first match
+// counts. Cf. DDOT and DOT, and AXIS and COLON.
+
+var xpathTokenRules = [
+ TOK_DSLASH,
+ TOK_SLASH,
+ TOK_DDOT,
+ TOK_DOT,
+ TOK_AXIS,
+ TOK_COLON,
+ TOK_AXISNAME,
+ TOK_NODEO,
+ TOK_PARENO,
+ TOK_PARENC,
+ TOK_BRACKO,
+ TOK_BRACKC,
+ TOK_AT,
+ TOK_COMMA,
+ TOK_OR,
+ TOK_AND,
+ TOK_NEQ,
+ TOK_EQ,
+ TOK_GE,
+ TOK_GT,
+ TOK_LE,
+ TOK_LT,
+ TOK_PLUS,
+ TOK_MINUS,
+ TOK_ASTERISK,
+ TOK_PIPE,
+ TOK_MOD,
+ TOK_DIV,
+ TOK_LITERALQ,
+ TOK_LITERALQQ,
+ TOK_NUMBER,
+ TOK_QNAME,
+ TOK_NCNAME,
+ TOK_DOLLAR
+];
+
+// All the nonterminals of the grammar. The nonterminal objects are
+// identified by object identity; the labels are used in the debug
+// output only.
+var XPathLocationPath = { label: "LocationPath" };
+var XPathRelativeLocationPath = { label: "RelativeLocationPath" };
+var XPathAbsoluteLocationPath = { label: "AbsoluteLocationPath" };
+var XPathStep = { label: "Step" };
+var XPathNodeTest = { label: "NodeTest" };
+var XPathPredicate = { label: "Predicate" };
+var XPathLiteral = { label: "Literal" };
+var XPathExpr = { label: "Expr" };
+var XPathPrimaryExpr = { label: "PrimaryExpr" };
+var XPathVariableReference = { label: "Variablereference" };
+var XPathNumber = { label: "Number" };
+var XPathFunctionCall = { label: "FunctionCall" };
+var XPathArgumentRemainder = { label: "ArgumentRemainder" };
+var XPathPathExpr = { label: "PathExpr" };
+var XPathUnionExpr = { label: "UnionExpr" };
+var XPathFilterExpr = { label: "FilterExpr" };
+var XPathDigits = { label: "Digits" };
+
+var xpathNonTerminals = [
+ XPathLocationPath,
+ XPathRelativeLocationPath,
+ XPathAbsoluteLocationPath,
+ XPathStep,
+ XPathNodeTest,
+ XPathPredicate,
+ XPathLiteral,
+ XPathExpr,
+ XPathPrimaryExpr,
+ XPathVariableReference,
+ XPathNumber,
+ XPathFunctionCall,
+ XPathArgumentRemainder,
+ XPathPathExpr,
+ XPathUnionExpr,
+ XPathFilterExpr,
+ XPathDigits
+];
+
+// Quantifiers that are used in the productions of the grammar.
+var Q_01 = { label: "?" };
+var Q_MM = { label: "*" };
+var Q_1M = { label: "+" };
+
+// Tag for left associativity (right assoc is implied by undefined).
+var ASSOC_LEFT = true;
+
+// The productions of the grammar. Columns of the table:
+//
+// - target nonterminal,
+// - pattern,
+// - precedence,
+// - semantic value factory
+//
+// The semantic value factory is a function that receives parse tree
+// nodes from the stack frames of the matched symbols as arguments and
+// returns an a node of the parse tree. The node is stored in the top
+// stack frame along with the target object of the rule. The node in
+// the parse tree is an expression object that has an evaluate() method
+// and thus evaluates XPath expressions.
+//
+// The precedence is used to decide between reducing and shifting by
+// comparing the precendence of the rule that is candidate for
+// reducing with the precedence of the look ahead token. Precedence of
+// -1 means that the precedence of the tokens in the pattern is used
+// instead. TODO: It shouldn't be necessary to explicitly assign
+// precedences to rules.
+
+var xpathGrammarRules =
+ [
+ [ XPathLocationPath, [ XPathRelativeLocationPath ], 18,
+ passExpr ],
+ [ XPathLocationPath, [ XPathAbsoluteLocationPath ], 18,
+ passExpr ],
+
+ [ XPathAbsoluteLocationPath, [ TOK_SLASH, XPathRelativeLocationPath ], 18,
+ makeLocationExpr1 ],
+ [ XPathAbsoluteLocationPath, [ TOK_DSLASH, XPathRelativeLocationPath ], 18,
+ makeLocationExpr2 ],
+
+ [ XPathAbsoluteLocationPath, [ TOK_SLASH ], 0,
+ makeLocationExpr3 ],
+ [ XPathAbsoluteLocationPath, [ TOK_DSLASH ], 0,
+ makeLocationExpr4 ],
+
+ [ XPathRelativeLocationPath, [ XPathStep ], 31,
+ makeLocationExpr5 ],
+ [ XPathRelativeLocationPath,
+ [ XPathRelativeLocationPath, TOK_SLASH, XPathStep ], 31,
+ makeLocationExpr6 ],
+ [ XPathRelativeLocationPath,
+ [ XPathRelativeLocationPath, TOK_DSLASH, XPathStep ], 31,
+ makeLocationExpr7 ],
+
+ [ XPathStep, [ TOK_DOT ], 33,
+ makeStepExpr1 ],
+ [ XPathStep, [ TOK_DDOT ], 33,
+ makeStepExpr2 ],
+ [ XPathStep,
+ [ TOK_AXISNAME, TOK_AXIS, XPathNodeTest ], 33,
+ makeStepExpr3 ],
+ [ XPathStep, [ TOK_AT, XPathNodeTest ], 33,
+ makeStepExpr4 ],
+ [ XPathStep, [ XPathNodeTest ], 33,
+ makeStepExpr5 ],
+ [ XPathStep, [ XPathStep, XPathPredicate ], 33,
+ makeStepExpr6 ],
+
+ [ XPathNodeTest, [ TOK_ASTERISK ], 33,
+ makeNodeTestExpr1 ],
+ [ XPathNodeTest, [ TOK_NCNAME, TOK_COLON, TOK_ASTERISK ], 33,
+ makeNodeTestExpr2 ],
+ [ XPathNodeTest, [ TOK_QNAME ], 33,
+ makeNodeTestExpr3 ],
+ [ XPathNodeTest, [ TOK_NODEO, TOK_PARENC ], 33,
+ makeNodeTestExpr4 ],
+ [ XPathNodeTest, [ TOK_NODEO, XPathLiteral, TOK_PARENC ], 33,
+ makeNodeTestExpr5 ],
+
+ [ XPathPredicate, [ TOK_BRACKO, XPathExpr, TOK_BRACKC ], 33,
+ makePredicateExpr ],
+
+ [ XPathPrimaryExpr, [ XPathVariableReference ], 33,
+ passExpr ],
+ [ XPathPrimaryExpr, [ TOK_PARENO, XPathExpr, TOK_PARENC ], 33,
+ makePrimaryExpr ],
+ [ XPathPrimaryExpr, [ XPathLiteral ], 30,
+ passExpr ],
+ [ XPathPrimaryExpr, [ XPathNumber ], 30,
+ passExpr ],
+ [ XPathPrimaryExpr, [ XPathFunctionCall ], 30,
+ passExpr ],
+
+ [ XPathFunctionCall, [ TOK_QNAME, TOK_PARENO, TOK_PARENC ], -1,
+ makeFunctionCallExpr1 ],
+ [ XPathFunctionCall,
+ [ TOK_QNAME, TOK_PARENO, XPathExpr, XPathArgumentRemainder, Q_MM,
+ TOK_PARENC ], -1,
+ makeFunctionCallExpr2 ],
+ [ XPathArgumentRemainder, [ TOK_COMMA, XPathExpr ], -1,
+ makeArgumentExpr ],
+
+ [ XPathUnionExpr, [ XPathPathExpr ], 20,
+ passExpr ],
+ [ XPathUnionExpr, [ XPathUnionExpr, TOK_PIPE, XPathPathExpr ], 20,
+ makeUnionExpr ],
+
+ [ XPathPathExpr, [ XPathLocationPath ], 20,
+ passExpr ],
+ [ XPathPathExpr, [ XPathFilterExpr ], 19,
+ passExpr ],
+ [ XPathPathExpr,
+ [ XPathFilterExpr, TOK_SLASH, XPathRelativeLocationPath ], 20,
+ makePathExpr1 ],
+ [ XPathPathExpr,
+ [ XPathFilterExpr, TOK_DSLASH, XPathRelativeLocationPath ], 20,
+ makePathExpr2 ],
+
+ [ XPathFilterExpr, [ XPathPrimaryExpr, XPathPredicate, Q_MM ], 20,
+ makeFilterExpr ],
+
+ [ XPathExpr, [ XPathPrimaryExpr ], 16,
+ passExpr ],
+ [ XPathExpr, [ XPathUnionExpr ], 16,
+ passExpr ],
+
+ [ XPathExpr, [ TOK_MINUS, XPathExpr ], -1,
+ makeUnaryMinusExpr ],
+
+ [ XPathExpr, [ XPathExpr, TOK_OR, XPathExpr ], -1,
+ makeBinaryExpr ],
+ [ XPathExpr, [ XPathExpr, TOK_AND, XPathExpr ], -1,
+ makeBinaryExpr ],
+
+ [ XPathExpr, [ XPathExpr, TOK_EQ, XPathExpr ], -1,
+ makeBinaryExpr ],
+ [ XPathExpr, [ XPathExpr, TOK_NEQ, XPathExpr ], -1,
+ makeBinaryExpr ],
+
+ [ XPathExpr, [ XPathExpr, TOK_LT, XPathExpr ], -1,
+ makeBinaryExpr ],
+ [ XPathExpr, [ XPathExpr, TOK_LE, XPathExpr ], -1,
+ makeBinaryExpr ],
+ [ XPathExpr, [ XPathExpr, TOK_GT, XPathExpr ], -1,
+ makeBinaryExpr ],
+ [ XPathExpr, [ XPathExpr, TOK_GE, XPathExpr ], -1,
+ makeBinaryExpr ],
+
+ [ XPathExpr, [ XPathExpr, TOK_PLUS, XPathExpr ], -1,
+ makeBinaryExpr, ASSOC_LEFT ],
+ [ XPathExpr, [ XPathExpr, TOK_MINUS, XPathExpr ], -1,
+ makeBinaryExpr, ASSOC_LEFT ],
+
+ [ XPathExpr, [ XPathExpr, TOK_ASTERISK, XPathExpr ], -1,
+ makeBinaryExpr, ASSOC_LEFT ],
+ [ XPathExpr, [ XPathExpr, TOK_DIV, XPathExpr ], -1,
+ makeBinaryExpr, ASSOC_LEFT ],
+ [ XPathExpr, [ XPathExpr, TOK_MOD, XPathExpr ], -1,
+ makeBinaryExpr, ASSOC_LEFT ],
+
+ [ XPathLiteral, [ TOK_LITERALQ ], -1,
+ makeLiteralExpr ],
+ [ XPathLiteral, [ TOK_LITERALQQ ], -1,
+ makeLiteralExpr ],
+
+ [ XPathNumber, [ TOK_NUMBER ], -1,
+ makeNumberExpr ],
+
+ [ XPathVariableReference, [ TOK_DOLLAR, TOK_QNAME ], 200,
+ makeVariableReference ]
+ ];
+
+// That function computes some optimizations of the above data
+// structures and will be called right here. It merely takes the
+// counter variables out of the global scope.
+
+var xpathRules = [];
+
+function xpathParseInit() {
+ if (xpathRules.length) {
+ return;
+ }
+
+ // Some simple optimizations for the xpath expression parser: sort
+ // grammar rules descending by length, so that the longest match is
+ // first found.
+
+ xpathGrammarRules.sort(function(a,b) {
+ var la = a[1].length;
+ var lb = b[1].length;
+ if (la < lb) {
+ return 1;
+ } else if (la > lb) {
+ return -1;
+ } else {
+ return 0;
+ }
+ });
+
+ var k = 1;
+ for (var i = 0; i < xpathNonTerminals.length; ++i) {
+ xpathNonTerminals[i].key = k++;
+ }
+
+ for (i = 0; i < xpathTokenRules.length; ++i) {
+ xpathTokenRules[i].key = k++;
+ }
+
+ Log.write('XPath parse INIT: ' + k + ' rules');
+
+ // Another slight optimization: sort the rules into bins according
+ // to the last element (observing quantifiers), so we can restrict
+ // the match against the stack to the subest of rules that match the
+ // top of the stack.
+ //
+ // TODO(mesch): What we actually want is to compute states as in
+ // bison, so that we don't have to do any explicit and iterated
+ // match against the stack.
+
+ function push_(array, position, element) {
+ if (!array[position]) {
+ array[position] = [];
+ }
+ array[position].push(element);
+ }
+
+ for (i = 0; i < xpathGrammarRules.length; ++i) {
+ var rule = xpathGrammarRules[i];
+ var pattern = rule[1];
+
+ for (var j = pattern.length - 1; j >= 0; --j) {
+ if (pattern[j] == Q_1M) {
+ push_(xpathRules, pattern[j-1].key, rule);
+ break;
+
+ } else if (pattern[j] == Q_MM || pattern[j] == Q_01) {
+ push_(xpathRules, pattern[j-1].key, rule);
+ --j;
+
+ } else {
+ push_(xpathRules, pattern[j].key, rule);
+ break;
+ }
+ }
+ }
+
+ Log.write('XPath parse INIT: ' + xpathRules.length + ' rule bins');
+
+ var sum = 0;
+ mapExec(xpathRules, function(i) {
+ if (i) {
+ sum += i.length;
+ }
+ });
+
+ Log.write('XPath parse INIT: ' + (sum / xpathRules.length) + ' average bin size');
+}
+
+// Local utility functions that are used by the lexer or parser.
+
+function xpathCollectDescendants(nodelist, node) {
+ for (var n = node.firstChild; n; n = n.nextSibling) {
+ nodelist.push(n);
+ arguments.callee(nodelist, n);
+ }
+}
+
+function xpathCollectDescendantsReverse(nodelist, node) {
+ for (var n = node.lastChild; n; n = n.previousSibling) {
+ nodelist.push(n);
+ arguments.callee(nodelist, n);
+ }
+}
+
+
+// The entry point for the library: match an expression against a DOM
+// node. Returns an XPath value.
+function xpathDomEval(expr, node) {
+ var expr1 = xpathParse(expr);
+ var ret = expr1.evaluate(new ExprContext(node));
+ return ret;
+}
+
+// Utility function to sort a list of nodes. Used by xsltSort() and
+// nxslSelect().
+function xpathSort(input, sort) {
+ if (sort.length == 0) {
+ return;
+ }
+
+ var sortlist = [];
+
+ for (var i = 0; i < input.nodelist.length; ++i) {
+ var node = input.nodelist[i];
+ var sortitem = { node: node, key: [] };
+ var context = input.clone(node, 0, [ node ]);
+
+ for (var j = 0; j < sort.length; ++j) {
+ var s = sort[j];
+ var value = s.expr.evaluate(context);
+
+ var evalue;
+ if (s.type == 'text') {
+ evalue = value.stringValue();
+ } else if (s.type == 'number') {
+ evalue = value.numberValue();
+ }
+ sortitem.key.push({ value: evalue, order: s.order });
+ }
+
+ // Make the sort stable by adding a lowest priority sort by
+ // id. This is very convenient and furthermore required by the
+ // spec ([XSLT] - Section 10 Sorting).
+ sortitem.key.push({ value: i, order: 'ascending' });
+
+ sortlist.push(sortitem);
+ }
+
+ sortlist.sort(xpathSortByKey);
+
+ var nodes = [];
+ for (var i = 0; i < sortlist.length; ++i) {
+ nodes.push(sortlist[i].node);
+ }
+ input.nodelist = nodes;
+ input.setNode(nodes[0], 0);
+}
+
+
+// Sorts by all order criteria defined. According to the JavaScript
+// spec ([ECMA] Section 11.8.5), the compare operators compare strings
+// as strings and numbers as numbers.
+//
+// NOTE: In browsers which do not follow the spec, this breaks only in
+// the case that numbers should be sorted as strings, which is very
+// uncommon.
+
+function xpathSortByKey(v1, v2) {
+ // NOTE: Sort key vectors of different length never occur in
+ // xsltSort.
+
+ for (var i = 0; i < v1.key.length; ++i) {
+ var o = v1.key[i].order == 'descending' ? -1 : 1;
+ if (v1.key[i].value > v2.key[i].value) {
+ return +1 * o;
+ } else if (v1.key[i].value < v2.key[i].value) {
+ return -1 * o;
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/FunctionalTests/selenium/doc/FAQ.html b/tests/FunctionalTests/selenium/doc/FAQ.html deleted file mode 100644 index 90299653..00000000 --- a/tests/FunctionalTests/selenium/doc/FAQ.html +++ /dev/null @@ -1,128 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> -<meta name="generator" content="Docutils 0.3.5: http://docutils.sourceforge.net/" /> -<title>Selenium Frequently Asked Questions</title> -<link rel="stylesheet" href="default.css" type="text/css" /> -</head> -<body> -<h1 class="title">Selenium Frequently Asked Questions</h1> -<div class="document" id="selenium-frequently-asked-questions"> -<!-- Please note that until there's a Q&A-specific construct available, -this FAQ will use section titles for questions. Therefore -questions must fit on one line. The title may be a summary of the -question, with the full question in the section body. --> -<p>This is a work in progress. Please feel free to ask questions and/or -provide answers; send email to the Selenium users email address at <a class="reference" href="mailto:selenium-users@lists.public.thoughtworks.org">selenium-users@lists.public.thoughtworks.org</a>.</p> -<div class="contents topic" id="contents"> -<p class="topic-title first"><a name="contents">Contents</a></p> -<ul class="auto-toc simple"> -<li><a class="reference" href="#selenium" id="id2" name="id2">1Â Â Â Selenium</a><ul class="auto-toc"> -<li><a class="reference" href="#what-is-selenium-used-for" id="id3" name="id3">1.1Â Â Â What is Selenium used for?</a></li> -<li><a class="reference" href="#why-can-t-i-script-google-com" id="id4" name="id4">1.2Â Â Â Why can't I script google.com?</a></li> -<li><a class="reference" href="#how-can-i-run-my-test-against-a-foreign-or-remote-server-and-get-around-cross-site-scripting-security" id="id5" name="id5">1.3Â Â Â How can I run my test against a foreign or remote server and get around cross-site scripting security?</a></li> -<li><a class="reference" href="#how-do-you-create-test-tables" id="id6" name="id6">1.4Â Â Â How do you create test tables?</a></li> -</ul> -</li> -</ul> -</div> -<div class="section" id="selenium"> -<h1><a class="toc-backref" href="#id2" name="selenium">1Â Â Â Selenium</a></h1> -<div class="section" id="what-is-selenium-used-for"> -<h2><a class="toc-backref" href="#id3" name="what-is-selenium-used-for">1.1Â Â Â What is Selenium used for?</a></h2> -<p>It is used for functional or system testing web applications. These tests -are also sometimes called acceptance, customer, or integration tests. Selenium is not meant for unit testing.</p> -</div> -<div class="section" id="why-can-t-i-script-google-com"> -<h2><a class="toc-backref" href="#id4" name="why-can-t-i-script-google-com">1.2Â Â Â Why can't I script google.com?</a></h2> -<p>Question: -<em>I was trying to write a simple script that does a google search. -I have been running into all sorts of problems. Does this work for you? -Here is my test:</em> <table cellpadding="1" cellspacing="1" border="1"> - <tbody> - <tr> - <td rowspan="1" colspan="3">Test Type<br> - </td> - </tr> - <tr> - <td>open</td> - <td>http://www.google.com/</td> - <td> </td> - </tr> - <tr> - <td>type</td> - <td>q</td> - <td>testing tools</td> - </tr> - <tr> - <td>click</td> - <td>submitButton</td> - <td> </td> - </tr> - </tbody> -</table>.</p> -<p>Answer: -The quick answer is that because of cross-site scripting security built into -JavaScript engines in all browsers, you can't edit the content of a web page -from another domain. The foreign page will probably load correctly and be visible -in the test runner window, but Selenium won't be able to query or edit its contents. -In other words, you can't run selenium on "foo.com" and -run a test that edits values and clicks buttons against "bar.com". So, in -its current form, you can't "script" google.com because your script isn't -currently hosted on google.com. When Selenium and the application you are -testing is hosted on the same domain, however, you do not run into the -cross-site scripting security feature/limitation.</p> -<p>You read more about cross-site scripting here: <a class="reference" href="http://www.devarticles.com/c/a/JavaScript/JavaScript-Security/">http://www.devarticles.com/c/a/JavaScript/JavaScript-Security/</a></p> -<p>Also, if cross-site scripting security didn't exist, be careful about your -field and button references in your tests. The current version -of Selenium uses the "id" attribute of the object you are referring to in your -test. The search field and submit button at google.com have "name" attributes, -but not not "id" attributes. Therefore, Selenium wouldn't be able to find the objects. -Future versions of Selenium will be able to search for objects by more than -just the id attribute, though.</p> -</div> -<div class="section" id="how-can-i-run-my-test-against-a-foreign-or-remote-server-and-get-around-cross-site-scripting-security"> -<h2><a class="toc-backref" href="#id5" name="how-can-i-run-my-test-against-a-foreign-or-remote-server-and-get-around-cross-site-scripting-security">1.3Â Â Â How can I run my test against a foreign or remote server and get around cross-site scripting security?</a></h2> -<p>There are a few ways around cross-site scripting to access a remote server. -You could use a combination of proxying and URL rewriting in Apache to -trick the browser into the thinking the application and the testing tool -are coming from the same domain.</p> -<p>Another option is to run Selenium as an "HTA" application, or "HTML -Application" in Internet Explorer. HTA applications run in the security -context of any trusted application on the client, so there is no cross-site -scripting limitation. (You can find out more here: -<a class="reference" href="http://msdn.microsoft.com/workshop/author/hta/overview/htaoverview.asp">http://msdn.microsoft.com/workshop/author/hta/overview/htaoverview.asp</a>) The -equivalent to this "security-free" client on the Mozilla side of the fence -would be to write a XUL wrapper/extension.</p> -<p>Also, please see the answer to the related question: "Why can't I script google.com".</p> -</div> -<div class="section" id="how-do-you-create-test-tables"> -<h2><a class="toc-backref" href="#id6" name="how-do-you-create-test-tables">1.4Â Â Â How do you create test tables?</a></h2> -<p>The developers on the Selenium project use Mozilla Composer to -create plain HTML text files for their tests. -By default, Mozilla Composer writes very clean HTML without any extra, unnecessary markup.</p> -<p>Future versions of Selenium may support RST (ReStructred Text), or wiki-table -syntax, natively. However, you are free to use another format now, -as long as you remember to generate the HTML files from your source files, -either during your build process or dynamically at run-time.</p> -<table class="field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field"><th class="field-name">Author:</th><td class="field-body">Jason Huggins</td> -</tr> -<tr class="field"><th class="field-name">Created Date:</th><td class="field-body">11/05/2004</td> -</tr> -<tr class="field"><th class="field-name">Modified Date:</th><td class="field-body">11/05/2004</td> -</tr> -<tr class="field"><th class="field-name">Created With:</th><td class="field-body">reStructuredText: <a class="reference" href="http://docutils.sourceforge.net/rst.html">http://docutils.sourceforge.net/rst.html</a></td> -</tr> -</tbody> -</table> -</div> -</div> -</div> -</body> -</html> diff --git a/tests/FunctionalTests/selenium/doc/FAQ.txt b/tests/FunctionalTests/selenium/doc/FAQ.txt deleted file mode 100644 index ad9894ee..00000000 --- a/tests/FunctionalTests/selenium/doc/FAQ.txt +++ /dev/null @@ -1,127 +0,0 @@ -=========================================== - Selenium Frequently Asked Questions -=========================================== - -.. Please note that until there's a Q&A-specific construct available, - this FAQ will use section titles for questions. Therefore - questions must fit on one line. The title may be a summary of the - question, with the full question in the section body. - -This is a work in progress. Please feel free to ask questions and/or -provide answers; send email to the Selenium users email address at `selenium-users@lists.public.thoughtworks.org`__. - -.. _let us know: -__ mailto:selenium-users@lists.public.thoughtworks.org - - -.. contents:: -.. sectnum:: - - - - -Selenium -======== - -What is Selenium used for? --------------------------- - -It is used for functional or system testing web applications. These tests -are also sometimes called acceptance, customer, or integration tests. Selenium is not meant for unit testing. - - - -Why can't I script google.com? ------------------------------- -Question: -*I was trying to write a simple script that does a google search. -I have been running into all sorts of problems. Does this work for you? -Here is my test:* |test|. - -.. |test| raw:: html - - <table cellpadding="1" cellspacing="1" border="1"> - <tbody> - <tr> - <td rowspan="1" colspan="3">Test Type<br> - </td> - </tr> - <tr> - <td>open</td> - <td>http://www.google.com/</td> - <td> </td> - </tr> - <tr> - <td>type</td> - <td>q</td> - <td>testing tools</td> - </tr> - <tr> - <td>click</td> - <td>submitButton</td> - <td> </td> - </tr> - </tbody> - </table> - -Answer: -The quick answer is that because of cross-site scripting security built into -JavaScript engines in all browsers, you can't edit the content of a web page -from another domain. The foreign page will probably load correctly and be visible -in the test runner window, but Selenium won't be able to query or edit its contents. -In other words, you can't run selenium on "foo.com" and -run a test that edits values and clicks buttons against "bar.com". So, in -its current form, you can't "script" google.com because your script isn't -currently hosted on google.com. When Selenium and the application you are -testing is hosted on the same domain, however, you do not run into the -cross-site scripting security feature/limitation. - -You read more about cross-site scripting here: http://www.devarticles.com/c/a/JavaScript/JavaScript-Security/ - -Also, if cross-site scripting security didn't exist, be careful about your -field and button references in your tests. The current version -of Selenium uses the "id" attribute of the object you are referring to in your -test. The search field and submit button at google.com have "name" attributes, -but not not "id" attributes. Therefore, Selenium wouldn't be able to find the objects. -Future versions of Selenium will be able to search for objects by more than -just the id attribute, though. - - -How can I run my test against a foreign or remote server and get around cross-site scripting security? ------------------------------------------------------------------------------------------------------- - -There are a few ways around cross-site scripting to access a remote server. -You could use a combination of proxying and URL rewriting in Apache to -trick the browser into the thinking the application and the testing tool -are coming from the same domain. - -Another option is to run Selenium as an "HTA" application, or "HTML -Application" in Internet Explorer. HTA applications run in the security -context of any trusted application on the client, so there is no cross-site -scripting limitation. (You can find out more here: -http://msdn.microsoft.com/workshop/author/hta/overview/htaoverview.asp) The -equivalent to this "security-free" client on the Mozilla side of the fence -would be to write a XUL wrapper/extension. - -Also, please see the answer to the related question: "Why can't I script google.com". - - -How do you create test tables? ------------------------------- - -The developers on the Selenium project use Mozilla Composer to -create plain HTML text files for their tests. -By default, Mozilla Composer writes very clean HTML without any extra, unnecessary markup. - -Future versions of Selenium may support RST (ReStructred Text), or wiki-table -syntax, natively. However, you are free to use another format now, -as long as you remember to generate the HTML files from your source files, -either during your build process or dynamically at run-time. - - - -:Author: - Jason Huggins -:Created Date: 11/05/2004 -:Modified Date: 11/05/2004 -:Created With: reStructuredText: http://docutils.sourceforge.net/rst.html
\ No newline at end of file diff --git a/tests/FunctionalTests/selenium/doc/contact.html b/tests/FunctionalTests/selenium/doc/contact.html deleted file mode 100644 index b109444d..00000000 --- a/tests/FunctionalTests/selenium/doc/contact.html +++ /dev/null @@ -1,23 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <meta content="text/html; charset=ISO-8859-1" - http-equiv="content-type"> - <title>Selenium Contact</title> -</head> -<body> -<p> -For more information about Selenium, please use Confluence and the standard mailing lists below. -<p> -<h3>Selenium Project Wiki</h3> -[ <a href="http://confluence.public.thoughtworks.org/display/SEL/Home">Selenium Confluence</a> ] -<p> -<h3>Selenium Development</h3><p> -selenium-devel-subscribe@lists.public.thoughtworks.org<br> -[ <a href="http://lists.public.thoughtworks.org/mailman/listinfo/selenium-devel">Subscribe/Unsubscribe</a> | <a href="http://lists.public.thoughtworks.org/pipermail/selenium-devel/">Message Archives</a> ] -<p> -<h3>Selenium Users</h3><p> -selenium-users-subscribe@lists.public.thoughtworks.org<br> -[ <a href="http://lists.public.thoughtworks.org/mailman/listinfo/selenium-users">Subscribe/Unsubscribe</a> | <a href="http://lists.public.thoughtworks.org/pipermail/selenium-users/">Message Archives</a> ] -</body> -</html> diff --git a/tests/FunctionalTests/selenium/doc/developingdrivers.html b/tests/FunctionalTests/selenium/doc/developingdrivers.html deleted file mode 100644 index 4c2594d9..00000000 --- a/tests/FunctionalTests/selenium/doc/developingdrivers.html +++ /dev/null @@ -1,134 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <meta content="text/html; charset=ISO-8859-1" - http-equiv="content-type"> - <title>Developing Drivers</title> -</head> -<body> -<h2>Overview</h2> -This page details important information for people developing drivers -for Selenium.<br> -<h2>Same Origin Policy</h2> -This is a security issue that affects all modern browsers. It is well -described <a - href="http://www.mozilla.org/projects/security/components/same-origin.html">here</a>, -but for our purposes, it constrains the way that a -JavaScript in a browser may interoperate with other open frames and -windows. In short, the Selenium JavaScript app must come from (or -appear to) the same origin as the AUT.<br> -<h2>The Driver</h2> -The driver has a number of duties. These are typically.. <br> -<ul> - <li>Instantiating the web server with a dynamic app (servlets for -Java) </li> - <li>Launching a browser with a with a URL that makes sense for -connecting to that server </li> - <li>providing an API to such that commands can be routed though the -dynamic app to the browser</li> -</ul> -<h2>Local, Remote and URLs<br> -</h2> -An application may be testable in a remote location by humans, but for -scripted testing and the need for the driver to dynamically drive the -browser, it is optimal for the driver to be on the same machine as that -serving the AUT and the browser testing it. <br> -<br> -Thus, an application as deployed ...<br> -<br> -<img alt="http://localhost:8080/" src="images/stockmeister.png" - style="width: 361px; height: 68px;"><br> -<br> -... would appear like so for the purposes of testing ...<br> -<br> -<img alt="http://localhost:8080/" src="images/localhostAut.png" - style="width: 346px; height: 43px;"><br> -<br> -As with the standalone version of Selenium, there are a number of files -(HTML and JavaScript) that comprise the bulk of the testing framework -and sit in the browser as testing occurs. It makes most sense to put -these in a virtual directory on the same server ...<br> -<br> -<img alt="http://localhost:8080/selenium/" - src="images/localhostSelenium.png" - style="width: 326px; height: 57px;"><br> -<br> -The dynamic webapp needs to be similarly mounted ...<br> -<br> -<img alt="http://localhost:8080/selenium/driver?..." - src="images/localhostDriver.png" style="width: 318px; height: 52px;"><br> -<br> -As the dynamic is the link between what is happening in the browser and -the driving process, it we need to somehow have an instance reference -to it. This is easier in some languages and web servers than -others. Also full programatic start/stop control over the web -server is not always possible for some larger web servers -implementations.<br> -<h2>Reply/Request Architecture</h2> -Because a browser cannot open a socket and listen on it, we must -effectively initiate communication thru the driver on the browser side. -Ignoring the possibilities granted by keep-alive, we simply poll from -the browser to the server and pick up commands to process inside the -browser. Results of commands are also passed back to the dynamic -hander over the same mechanism. These are in fact done in the same HTTP -request. The results from the <span style="font-style: italic;">previous</span> -command go back as query string parameters, and the <span - style="font-style: italic;">next</span> command is communicated in a -text/plain document<br> -<br> -The previous/next business introduces some complexity on the driver -side of the design. Namely hidden behind the API, the driver must -manage queues for the outgoing commands and the (some time later) -incoming responses. Java, until 1.5, did not have a blocking -queue to make this easy. Most other languages did.<br> -<h2><span style="font-weight: bold;">Selenese</span></h2> -<table style="width: 100%;" border="1" cellpadding="2" cellspacing="2"> - <tbody> - <tr> - <td>Selenese is the contrived (and mostly hidden) wire language -that the -driver uses to speak to the browser-bot through the dynamic -handler. It uses HTTP for its transport, and is quite -simple. <br> - <br> -Responses come from the browser to the driver in a query -string like <br> - <br> - <span style="font-family: monospace;">commandResult=OK</span>, <br> - <br> -Commands go from the driver to the -browser-bot in a text/plain document:<br> - <br> - <span style="font-family: monospace;">| open | /foo/bar.html | |</span><br - style="font-family: monospace;"> - <br> -This two way communication is of course invisible to the observer.</td> - <td style="width: 30%; background-color: rgb(255, 153, 102);">The -BrowserBot, by understanding Selenese, allows a process other than the -browser itsself to direct events on the Application Under Test. <br> - <br> -The Selenese language is simple enough to be commandable by any -language that has an API that can handle HTTP requests. <br> - <br> -Thus, Selenese allows many different open, free or closed license -drivers to interoperate with the BrowserBot.</td> - </tr> - </tbody> -</table> -<h2>Choregraphy</h2> -The driver cleary has some fairly heavy things to do. It is -important for some robustness to be built into the design. For example -it may take a long time for the browser to be instantiated on a -particular platform. It is appropriate for wait timeouts to be -generous here. Similarly whilst issuing individual commands it is -important for the driver to wait a sufficient amount of time for a -command to execute (and its result to come back). For the most -part on a localhost setup, it will be because the AUT is slow, but it -could be because some break in the app means that there will be no -subsequent response. Some timeout regime should be able to -recover from, mark a test as failed (for reasons of timeout), and start -again with the next test.<br> -<br> -<br> -</body> -</html> diff --git a/tests/FunctionalTests/selenium/doc/driven.html b/tests/FunctionalTests/selenium/doc/driven.html deleted file mode 100644 index a5f33dff..00000000 --- a/tests/FunctionalTests/selenium/doc/driven.html +++ /dev/null @@ -1,206 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - <meta name="generator" - content="Docutils 0.3.6: http://docutils.sourceforge.net/"> - <title>Driven Selenium Reference</title> - <link rel="stylesheet" href="default.css" type="text/css"> -</head> -<body> -<div class="document" id="selenium-reference"> -<div class="section" id="test-tables"> -<h2><a name="test-tables"></a>Overview</h2> -Driven Selenium is where the browser is under the the control of an -adjacent process. That process is either a Java, .Net, Ruby or Python -application and it is typically run in conjunction with a unit testing -framework like JUnit or NUnit. Also possible, is a console application -driving a browser interactively.<br> -<h2>Selenium & Selenese</h2> -The key to this mode of operation is manner in which the browset-bot -takes instruction from the driver. If it were possible, the -browser-bot's javascript would open a server socket and await requests -from the driver. It is against the rules for browser embedded -javascript, to open ports for incoking requests as it would be a -general breach of security for the client-OS that the browser is -running on. What a browser can do is open addition requests to -the same server that its source came from. See <a - href="http://www.mozilla.org/projects/security/components/same-origin.html">http://www.mozilla.org/projects/security/components/same-origin.html</a> -for more info.<br> -<br> -To overcome the limitations of Javascript in a browser page is the page -continuously requests pages from the driver (which has conveniently -opened a web server). The pages which are retrieved from the server are -in fact plain text and each is an individual instruction from the -driver for what the browser-bot is to do next. E.g. - <br> -<br> - | open | /foo/bar.html | |<br> -<br> -We refer to this architecture are reply/request rather than the more -ususal request/reply.<br> -<h2><a name="test-tables">Sample test method<br> -</a></h2> -The test script is one that would be recognisable to people adept with -unit test frameworks :<br> -<br> -For Java -<br> -<br> - public void testOKClick() {<br> - selenium.verifyTitle("First Page");<br> - selenium.open("/TestPage.html");<br> - selenium.click("OKButton");<br> - selenium.verifyTitle("Another Page");<br> - }<br> -<br> -The difference from normal unit testing is that as part of the startup, -three major things have to happen:<br> -<ol> - <li>The test framework needs to publish a fresh copy of the -Application Under Test (AUT). -Selenium prefers to mount its own web server temporarily for the -purposes of testing.</li> - <li>The test framework needs to publish the static Selenium pages -(refer selenium dir in TestRunner mode above) in an apparent directory -on the same web server as (1).</li> - <li>The test framework needs to open a browser instance and point it -to Selenium.html served in (2) above.</li> -</ol> -As each of these isa fairly time consuming operation, it is best that -all three of those happen in a one time setup mode. As such, and -even though these leverage a unit testing framework, this is definately -for acceptance or functional testing.<br> -<h2>Example Setup<br> -</h2> -</div> -<div class="section" id="continuous-integration">For Java -<br> -<br> - selenium = new DefaultSelenium("c:\foo\bar-web-app\");<br> -<br> -The above will instantiate a web server using <a - href="http://jetty.mortbay.com/jetty/index.html">Jetty</a>, and -publish it at http://localhost:8080. The Selenium pages will appear to -be run from http://localhost:8080/selenium-driver. The default browser -for Windows, Linux or Mac will be instantiated and directed to accept -test instructions from the driver.<br> -<br> -The above would ususally be done in a setup method if under unit test -control. See <a - href="http://junit.sourceforge.net/doc/faq/faq.htm#organize_3">http://junit.sourceforge.net/doc/faq/faq.htm#organize_3</a> -for advice on one time setup for Java.<br> - <br> -A more complex case could be -<br> -<br> - selenium = new DefaultSelenium(new -TomcatCommandProcessor("c:\foo\bar-web-app"), new -MyOperaBrowserLauncher()), <br> -<h2>Command Reference</h2> - void chooseCancelOnNextConfirmation();<br> - void click(String field);<br> - void clickAndWait(String field);<br> - void open(String path);<br> - void pause(int duration);<br> - void selectAndWait(String field, String value);<br> - void selectWindow(String window);<br> - void setTextField(String field, String value);<br> - void storeText(String element, String value);<br> - void storeValue(String field, String value);<br> - void testComplete();<br> - void type(String field, String value);<br> - void typeAndWait(String field, String value);<br> - void verifyAlert(String alert);<br> - void verifyAttribute(String element, String value);<br> - void verifyConfirmation(String confirmation);<br> - void verifyElementNotPresent(String type);<br> - void verifyElementPresent(String type);<br> - void verifyLocation(String location);<br> - void verifySelectOptions(String field, String[] -values);<br> - void verifySelected(String field, String value);<br> - void verifyTable(String table, String value);<br> - void verifyText(String type, String text);<br> - void verifyTextPresent(String type, String text);<br> - void verifyTitle(String title);<br> - void verifyValue(String field, String value);<br> -<h2>Deployment Choices</h2> -<h3>Embedded Web Server</h3> -<p> -<img alt="Picture of Browser and Driving process" src="images/Embedded.png" - style="width: 518px; height: 302px;" align="top"><br> -The best way to deply the driven form of Selenium is where an embedded -web server is used. With the Java version, this could be <a - href="http://jetty.mortbay.com/jetty/index.html">Jetty</a> or <a - href="http://jakarta.apache.org/tomcat/">Tomcat</a>. <br> -<br> -In advance of a series of selenese instructions being issued to the -browser, a web server containing the AUT and some static pages for -Selenium itself will be programmatically started and used to -communicate selenese instructions to the browser. When the driver -process is complete the web server will be programmatically stopped. <br> -</p> -<p style="color: rgb(255, 0, 0);">[ For release 0.2 - this is the only -mode that really works. Those below will be fine for 0.3 and above ]<br> -</p> -<h3>Adjacent Web Server</h3> -<img alt="diagram of adjacent config" src="images/Adjacent.png" - style="width: 534px; height: 572px;"><br> -By adjacent we mean a process on the same machine as the driver. As -such it would appear as localhost to browsers. <br> -<br> -For the .Net driver embedded is very unlikely as Internet Information -Server is running in its own process. For the Java driver, this could -simple be a necessary choice - i.e. the deployment target is WebLogic -or -WebSphere which are not too embeddable. <br> -<br> -In this scenario we suggest you deploy a small web-app alongside the -AUT that will liase between the driver process and the browser. Of -course, there is less fine grained control over the starting and -stopping of the server and indeed the version of the AUT. If the web -server supports it, it is best to copy a fresh version of the AUT to -the underlying directory that the web-app is mapped to. We call the -small web-app the selenese proxy. It does of course slow things down a -fraction.<br> -<span style="font-weight: bold;"><br> -Selenese-proxy</span><br style="font-weight: bold;"> -<br> -If you can deploy a copy of the selenese proxy to remote web server, -and configure it to forward requests to your machine, then you can -essentially script that remote web app. The downside of this is that -that remote machine can essentially only be driven from the machine -that is configured to drive it. i.e. it would need to be reconfigured -to be driven from elsewhere. The upside is that you can to a great -extent mix and match your technologies to achieve this proxying (a Java -driver could use a Python selenese-proxy script a web-app).<br> -<h3>Nearby Web Server <br> -</h3> -This is where the AUT is running on a nearby testing stack or dedicated -development box (not the developer's own workstation).<br> -<br> -To achieve this the selenese proxy needs to be deployed again, this -time to that nearby machine. It will need to be reconfigured to -indicate that selenese traffic is either forwarded to a particular -machine.<span style="font-weight: bold;"><br> -</span> -<h3>Remote Web Server <br> -</h3> -This is where the AUT is running on a remote machine, which you have no -control over. A good example would be www.google.com. It is -worth pointing out that this is of more interest to hackers or data -harvesters than testing professionals, as what self respecting -development group would prevent you from deploying at least the -Selenese Proxy webapp to a testing stack.<br> -<span style="font-weight: bold;"><br> -Funnel</span><br style="font-weight: bold;"> -<br> -We are writing an application called the funnel that can help us -overcome the <a - href="http://www.mozilla.org/projects/security/components/same-origin.html">same -origin</a> issue that is key to Selenium. It essentially makes a -selenium-driver/ directory appear on a remote web site for the purposes -of the browser.<br> -<br> -<br> -</div> -</div> -</body> -</html> diff --git a/tests/FunctionalTests/selenium/doc/home-page.html b/tests/FunctionalTests/selenium/doc/home-page.html deleted file mode 100644 index 3806386b..00000000 --- a/tests/FunctionalTests/selenium/doc/home-page.html +++ /dev/null @@ -1,161 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd"> -<html> -<head> - <title>Selenium</title> - <meta http-equiv="Content-Type" - content="text/html; charset=windows-1252"> - <link href="index_files/selenium.css" type="text/css" rel="stylesheet"> -</head> -<body> - -<fieldset> - <legend><b>News</b></legend> - - <dl> - <dt>September ??, 2005</dt> - <dd>Selenium-0.6 is available - (<a href="release-notes.html">release notes</a>) - - <a href="http://selenium.thoughtworks.com/download.html">download now</a> - </dd> - </dl> - -</fieldset> - -<p> -<b>Selenium</b> is a test tool for web applications. Selenium tests run -<b>directly in a browsers</b>, just as real users do. And they run in -Internet Explorer, Mozilla and Firefox on Windows, Linux and Macintosh. No -other test tool covers such a wide array of platforms. -</p> - -<ul> - <li><span style="font-weight: bold;">Browser compatability testing.</span> -Test your application to see if it works correctly on different -browsers and operating systems. The same script can run on any Selenium -platform.<br> - </li> - <li><span style="font-weight: bold;">System functional testing.</span> -Create regression tests to verify application functionality and user -acceptance.<br> - </li> -</ul> -<span style="font-weight: bold;"><span style="font-weight: bold;"></span></span>Selenium -uses a unique mechanism which allows it to run on so multiple -platforms. Installed with your application webserver, Selenium -automatically deploys it's JavaScript automation engine -- the Browser -Bot -- to your browser when you point it at the Selenium install point -on your webserver. Thus, you must have write access to the machine your -web application server is running on to install Selenium.<br> -<br> -<div style="text-align: left; margin-right: 120px; margin-left: 80px;"><span - style="font-style: italic;">"Considering the simplicity of it, it is -almost surprising that no one has thought of doing this previously. The -framework is simple and the code is neat and very maintainable. -Sometimes it takes a work of genius to find the uncomplicated solution -to a potentially complicated problem.</span><span - style="font-style: italic;">" - </span>Antony Marcano<span - style="font-style: italic;"></span><span style="font-style: italic;"></span><br> -<span style="font-style: italic;"></span></div> -<div style="text-align: left; margin-right: 120px; margin-left: 120px;"><span - style="font-style: italic;"><br> -</span></div> -<div style="text-align: left; margin-right: 120px;">Selenium was -developed by team -of programmers and testers at -ThoughtWorks. It is -open-source software and can -be downloaded and used without charge. It is currently under active -development by our team. Stay tuned for updates and further -announcements.<br> -</div> -<br> -ThoughtWorks is a leader in Agile development methods for enterprise -software development. Selenium is designed specifically for the -acceptance testing requirements of Agile teams. However, teams -using more traditional development will also find it useful.<br> -<h2>Supported Browsers and Platforms</h2> -<div style="text-align: left; margin-left: 80px;"> -<table style="text-align: left;" border="1" - cellpadding="2" cellspacing="2" height=""> - <tbody> - <tr> - <td style="vertical-align: top;"><br> - </td> - <td style="vertical-align: top; font-weight: bold;">Internet -Explorer<br> - </td> - <td style="vertical-align: top; font-weight: bold;">Mozilla<br> - </td> - <td style="vertical-align: top; font-weight: bold;">Firefox<br> - </td> - <td style="vertical-align: top;"><span style="font-weight: bold;">Safari</span><br> - </td> - </tr> - <tr> - <td style="vertical-align: top; font-weight: bold;">Windows XP<br> - </td> - <td style="vertical-align: top;">6.0<br> - </td> - <td style="vertical-align: top;">1.6+, 1.7+<br> - </td> - <td style="vertical-align: top;">0.8+, 0.9+, 1.0<br> - </td> - <td style="vertical-align: top;"><br> - </td> - </tr> - <tr> - <td style="vertical-align: top; font-weight: bold;">Red Hat Linux<br> - </td> - <td style="vertical-align: top;"><br> - </td> - <td style="vertical-align: top;">1.6+, 1.7+<br> - </td> - <td style="vertical-align: top;">0.8+, 0.9+, 1.0+<br> - </td> - <td style="vertical-align: top;"><br> - </td> - </tr> - <tr> - <td style="vertical-align: top; font-weight: bold;">Mac OS X 10.3<br> - </td> - <td style="vertical-align: top;"><span style="font-style: italic;">not supported</span><br> - </td> - <td style="vertical-align: top;">1.6+, 1.7+<br> - </td> - <td style="vertical-align: top;">0.8+, 0.9+, 1.0+<br> - </td> - <td style="vertical-align: top;">1.3+<br> - </td> - </tr> - </tbody> -</table> -</div> -<span style="font-weight: bold;"><br> -</span> -<h2>How does Selenium Work? <br> -</h2> -Selenium uses JavaScript and Iframes to embed a test automation -engine in your browser. This technique should work with any -JavaScript-enabled browser. Because different browsers handle -JavaScript somewhat differently, we usually have to tweak the engine to -support new browsers. -<h2>Where did Selenium Come From?</h2> -Selenium grew out of a testing framework that was -developed to acceptance-test the functionality of <a - href="https://www.thoughtworks.com">ThoughtWorks</a>' new -web-based time & expense reporting application. It was written by -Jason Huggins, Paul Gross and Jie Tina Wang.<br> -<p class="paragraph">Jason -started demoing the test framework for various colleagues. Many were -excited about its immediate and intuitive visual feedback, as well as -its potential to grow as a reusable testing framework for other web -applications.</p> -And Selenium was born. -<br> -<h2>Having Trouble?</h2> -Check out our <a - href="http://confluence.public.thoughtworks.org/display/SEL/FAQ">Frequently -Asked Questions</a> page -for more information. -</body> -</html> diff --git a/tests/FunctionalTests/selenium/doc/images/Adjacent.png b/tests/FunctionalTests/selenium/doc/images/Adjacent.png Binary files differdeleted file mode 100644 index 6da4ad66..00000000 --- a/tests/FunctionalTests/selenium/doc/images/Adjacent.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/images/Embedded.png b/tests/FunctionalTests/selenium/doc/images/Embedded.png Binary files differdeleted file mode 100644 index 8e5967d7..00000000 --- a/tests/FunctionalTests/selenium/doc/images/Embedded.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/images/SmallAdjacent.png b/tests/FunctionalTests/selenium/doc/images/SmallAdjacent.png Binary files differdeleted file mode 100644 index 3cf28aec..00000000 --- a/tests/FunctionalTests/selenium/doc/images/SmallAdjacent.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/images/SmallEmbedded.png b/tests/FunctionalTests/selenium/doc/images/SmallEmbedded.png Binary files differdeleted file mode 100644 index 399acbf5..00000000 --- a/tests/FunctionalTests/selenium/doc/images/SmallEmbedded.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/images/SmallStandalone.png b/tests/FunctionalTests/selenium/doc/images/SmallStandalone.png Binary files differdeleted file mode 100644 index f04266a0..00000000 --- a/tests/FunctionalTests/selenium/doc/images/SmallStandalone.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/images/Standalone.png b/tests/FunctionalTests/selenium/doc/images/Standalone.png Binary files differdeleted file mode 100644 index f9b670c0..00000000 --- a/tests/FunctionalTests/selenium/doc/images/Standalone.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/images/localhostAut.png b/tests/FunctionalTests/selenium/doc/images/localhostAut.png Binary files differdeleted file mode 100644 index 25204654..00000000 --- a/tests/FunctionalTests/selenium/doc/images/localhostAut.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/images/localhostDriver.png b/tests/FunctionalTests/selenium/doc/images/localhostDriver.png Binary files differdeleted file mode 100644 index a904d7d4..00000000 --- a/tests/FunctionalTests/selenium/doc/images/localhostDriver.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/images/localhostSelenium.png b/tests/FunctionalTests/selenium/doc/images/localhostSelenium.png Binary files differdeleted file mode 100644 index af527be5..00000000 --- a/tests/FunctionalTests/selenium/doc/images/localhostSelenium.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/images/stockmeister.png b/tests/FunctionalTests/selenium/doc/images/stockmeister.png Binary files differdeleted file mode 100644 index b07aabc6..00000000 --- a/tests/FunctionalTests/selenium/doc/images/stockmeister.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/images/tested-with-selenium.png b/tests/FunctionalTests/selenium/doc/images/tested-with-selenium.png Binary files differdeleted file mode 100644 index fa80b414..00000000 --- a/tests/FunctionalTests/selenium/doc/images/tested-with-selenium.png +++ /dev/null diff --git a/tests/FunctionalTests/selenium/doc/index.html b/tests/FunctionalTests/selenium/doc/index.html deleted file mode 100644 index 8f7fed80..00000000 --- a/tests/FunctionalTests/selenium/doc/index.html +++ /dev/null @@ -1,30 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd"> - -<HTML> -<head> - <title>Selenium</title> -</head> -<body> - <DIV id="menu"> - <UL id="menulist"> - <LI><A href="index.html">home</A></LI> - <LI><A href="download.html">download</A></LI> - <LI><A href="release-notes.html"> - release notes</A></LI> - <LI><A href="usage.html">usage</A></LI> - <LI><A href="seleniumReference.html"> - commands</A></LI> - <LI><A href="testrunner.html"> - test runner</A></LI> - <LI><A href="driven.html"> - driven</A></LI> - <LI><A href="developingdrivers.html">developing drivers</A></LI> - <LI><A href="contact.html">contact</A></LI> - <LI><A href="demos.html">demos</A></LI> - <LI><A href="http://confluence.public.thoughtworks.org/display/SEL">wiki</A></LI> - <LI><A href="http://confluence.public.thoughtworks.org/display/SEL/FAQ">faq</A></LI> - </UL> - </DIV> -</body> -</HTML> - - - <!-- Do not edit this page beyond its list of links current form --> - <!-- the real front page is home-page.html --> - <!-- which is rendered as index.html on the live website --> diff --git a/tests/FunctionalTests/selenium/doc/jsrmi.html b/tests/FunctionalTests/selenium/doc/jsrmi.html deleted file mode 100644 index 035f4a4e..00000000 --- a/tests/FunctionalTests/selenium/doc/jsrmi.html +++ /dev/null @@ -1,151 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <meta content="text/html; charset=ISO-8859-1" - http-equiv="content-type"> - <title>JSRMI</title> -</head> -<body> -<p>JSRMI (Javascript Remote Method Invocation) is a portable browser-neutral Javascript library that makes it possible to execute -Javascript functions in a browser from a process external to the browser.</p> -<p>JSRMI is not in any way tied to Java's RMI, but provides a similar mechanism. JSRMI is completely decoupled from the core Selenium Javascript API, but it can -be used to access the Selenium API from outside the browser. </p> -<p>All of the -browser-side JSRMI code resides in the rmi.js script - available in the Selenium -distribution.</p> -<h2>Browser support</h2> -<ul> - <li>IE 5</li> - <li>Mozilla 1</li> - <li>Netscape 7</li> - <li>Firefox 0.92</li> - <li>Safari 1.2</li> -</ul> -<h2>Language support</h2> -<ul> - <li>Ruby</li> -</ul> -<p>Libraries for other languages are under way.</p> -<h1>How do I use JSRMI from an external process?</h1> -<h2>Ruby</h2> -<p>Just include the jsrmi script in your own:</p> -<p><font face="Courier New">require "jsrmi"<br> -<br> -browser = Selenium::Browser.new.proxy<br> -someArea = browser.document.getElementById("someArea")<br> -someArea.value = "Hello from Ruby #{Time.new}"</font></p> -<p>This will modify the text of a text area in the browser. Looks strangely -familiar to Javascript doesn't it? You can of course call the selenium API too -if the browser has loaded the main Selenium page (which also includes the rmi.js -script - at least that is the plan - I hope (Aslak)).</p> -<h1>How does it work?</h1> -<p>(You can safely skip this section if you don't care - this is gory details)</p> -<h2>Browser side</h2> -<p>The rmi.js script uses the -<a href="http://developer.apple.com/internet/webcontent/xmlhttpreq.html"> -XMLHttpRequest</a> object (available in all compatible browsers) to communicate -with the external process. It executes a GET-POST loop which is repeated ad -infinitum - pulling JSRMI invocations from the external process with GET and -POSTing the results back. This all happens in a separate thread, thereby having -minimal impact on the rest of the web page - without causing a page refresh.</p> -<p>The rmi.js script will do a HTTP GET to a URL on the same host/port as the rmi.js -script was loaded from. The content returned from the GET (which must comply -with the <a href="#protocol">JSRMI protocol</a>) is then translated into -Javascript and dynamically executed via Javascript's <font face="Courier New"> -eval()</font> function.</p> -<p>The result of the function call (typically a Javascript object) is translated -back into the JSRMI protocol format and POSTed back to the same URL as the GET.</p> -<h2>External process side</h2> -<p>The external process typically consists of a library that embeds the -following functionality:</p> -<ul> - <li>A HTTP server (should be light to ensure fast startup)</li> - <li>An API that translates local invocations into the JSRMI protocol</li> - <li>Two blocking queues:</li> - <ul> - <li>Output queue - HTTP GET invocations will take JSRMI protocol strings - (representing browser side invocations) from this queue and block until - something is available. These strings are returned to the HTTP client (The - rmi.js script in the browser). This means a blocking GET for the JSRMI - browser side).</li> - <li>Input queue - HTTP POST data from the browser side JSRMI will be enqued - here. This data represents results of browser side Javascript invocations.</li> - </ul> -</ul> -<p>A local invocation should translate the invocation to a JSRMI protocol string -and put it on the output queue (which jsrmi will GET). It should then wait for -the result of the browser side invocation to be put back on the input queue via -a POST from jsrmi. Finally it should translate the return value (another JSRMI -protocol string) into a native object and return it to the caller.</p> -<p>At any given point in time there should only be one single JSRMI protocol -string in one of the queues - depending on the where the invocation is in its -lifecycle.</p> -<h2>Reference objects</h2> -<p>JSRMI allows objects (such as browser side HTMLDocument, HTMLTextField etc) -to be transferred back and forth. This is based on a simple mechanism where each -object is given a unique id and maintained in a pool on each side. this pool is -used to reference and dereference native objects back and forth from the JSRMI -protocol strings.</p> -<h1>Why would I use JSRMI?</h1> -<h2>With Selenium</h2> -<p>The Selenium browser runtime will load both selenium test scripts and the web -pages from the web application you're testing into the browser. Modern browsers -don't allow content to be loaded from different hosts (cross browsing security -restrictions). A web application being tested will typically be deployed on a -server, and therefore the selenium test scripts must be loaded from the same web -server. Depending on who's writing the Selenium scripts and executing them, this -may or may not be a restriction to its usage.</p> -<p>Under some circumstances it is desirable to keep Selenium test scripts on -your local machine (the same as the one running the browser with the Selenium -runtime) rather than on a remote web server hosting the web application being -tested. Some examples are:</p> -<ul> - <li>The edit/run cycle of selenium scripts can be cumbersome if the script has - to be deployed to a server each time it is modified. Being able to keep the - scripts on a different machine (such as the one on your desk) can - significantly improve the ease of use and rapid development of tests. JSRMI - lets you do just that.</li> - <li>Putting in place a deployment routine for selenium script requires - technical knowledge of the web application's build process as well as the web - server hosting it. Many users of Selenium will not have easy access to this - expertise. JSRMI lets these users use Selenium nevertheless.</li> -</ul> -<p><i>It is important to emphasise that hosting the Selenium scripts on a local -machine would also require that the browser loads the web site being tested from -the local machine. Aren't we creating new problems by requiring testers to -install a full web server environment on their machines? Actually no. The JSRMI -libraries in Ruby and Java (and those who may follow) will soon provide a light -HTTP proxy server. The local browser will load the remote web site through this -HTTP proxy. The browser's security restrictions are satisfied since all content -(Selenium runtime, Selenium scripts and the remote website) is loaded through -localhost.</i></p> -<h2>Scripting of existing web applications</h2> -<p>Think of all boring web form tasks you could automate with JSRMI....</p> -<h1><a name="protocol">The JSRMI protocol</a></h1> -<p>TODO: describe the format.</p> -<h1>How do I implement a JSRMI client library for language X?</h1> -<p>Start by understand the inner workings - look at the ruby implementation and -study the javascript. (When the JSRMI protocol is better described, studying -this code should not be necessary). </p> -<p>It will be to implement a JSRMI client -library in a dynamic language than a static language, because dynamic languages -allow arbitrary messages (method invocations) to be sent to an object. Most -dynamic languages (Ruby, Python, Groovy, Smalltalk) have a generic mechanism to -intercept any message and an object message->JSRMI protocol translation logic is -trivial to implement based on this.</p> -<h2>Guidelines for static languages such as Java and C#</h2> -<p>JSRMI clients for static languages such as Java or C# will either have to -choose a subset of the Javascript functions and objects that you want to access, -or implement some generic invocation mechanism that allows raw JSRMI protocol -strings.</p> -<p>The former is more easy to use from a user perspective, but will be -restricted in terms of flexibility. The latter is completely generic, but -awkward to deal with from a user perspective.</p> -<p>The recommendation is to implement a raw interface to the JSRMI protocol and -have a generic dynamic proxy implementation on top of that. This way the API -support can easily be extended simply by implementing new interfaces for the -Javascript counterparts and generate dynamic proxies on the fly as needed. </p> -<h2>Calling functions/methods in an external process from the browser using JSRMI</h2> -<p>This is currently not possible.</p> -</body> -</html>
\ No newline at end of file diff --git a/tests/FunctionalTests/selenium/doc/release-notes.html b/tests/FunctionalTests/selenium/doc/release-notes.html deleted file mode 100644 index f7b7971b..00000000 --- a/tests/FunctionalTests/selenium/doc/release-notes.html +++ /dev/null @@ -1,97 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd"> -<html> -<head> - <title>Release Notes</title> - <meta http-equiv="Content-Type" - content="text/html; charset=windows-1252"> - <link href="index_files/selenium.css" type="text/css" rel="stylesheet"> -</head> -<body> - -Release information for different distributions of Selenium - -<h2>Selenium 0.6.0</h2> - -September 24, 2005 - -<dl> - <dt>pattern-matching</dt> <dd>Support for regular-expression and exact matching. Allow patterns to be used to match alert and confirmation messages.</dd> - <dt>support for Javascript prompts</dt> <dd>Capture and verify prompt-messages, set up return values.</dd> - <dt>fireEvent command</dt> <dd>Allows arbitrary events to be raised.</dd> - <dt>logging</dt> <dd>New "pop up" log window.</dd> - - <dt>error-handling</dt> <dd>Better handling and reporting of internal errors.</dd> - <dt>bug-fixes</dt> <dd>Various defects fixed.</dd> -</dl> - -<p> -See also: <a href="http://jira.public.thoughtworks.org/secure/ReleaseNote.jspa?projectId=10030&styleName=Html&version=10150">JIRA release-summary</a>. -</p> - -<h2>Selenium 0.5.0</h2> - -June 19, 2005 - -<dl> - <dt>"waitForValue" command</dt><dd>Tells Selenium to wait until an input element has a specified value.</dd> - <dt>"close" command</dt><dd>Closes an open popup window</dd> - <dt>DOM Viewer</dt><dd>Now works in IE, show/hide works in all browsers</dd> - <dt>Option locators</dt><dd>Can now select options based on index, value or id, as well as label</dd> - <dt>verifyLocation</dt><dd>Now handles querystring in the url</dd> - <dt>selectWindow</dt><dd>Can now select windows that aren't assigned to a global variable</dd> - <dt>TestRunner hanging</dt><dd>Fixed problem found in some IE installations</dd> -</dl> - -<p> -See also: <a href="http://jira.public.thoughtworks.org/secure/ReleaseNote.jspa?projectId=10030&styleName=Html&version=10140">JIRA release-summary</a>. - -<h2>Selenium 0.4.0</h2> - -May 20, 2005 - -<p style="font-weight: bold;">This release breaks backward compatibility in 3 ways: - <ol> - <li>The 'click' command now requires the 'AndWait' suffix in order to wait for a page to reload. The 'nowait' parameter is no longer supported.</li> - <li>The previously undocumented 'link:' locator is now specified as 'link=the link text'</li> - <li>The previously undocumented 'setVariable' command has been renamed to 'store', with slightly different semantics.</li> - </ol> -</p> - -<dl> - <dt>Safari support</dt><dd>Most Selenium features now work in Safari!</dd> - <dt>Click command doesn't wait</dt><dd>Use 'clickAndWait' to wait for a page reload</dd> - <dt>Locator specificity</dt><dd>Can use prefixes ('id=', 'xpath=' etc) to specify exactly which locator strategy to use.</dd> - <dt>Better XPath support</dt><dd>Works in Konqueror, and is faster on other platforms.</dd> - <dt>Javascript parameters</dt><dd>All parameters can now take a javascript{...} syntax, constructing the parameter value from javascript.</dd> - <dt>Handle 'AndWait' commands in popup windows</dt><dd>This bug has been fixed</dd> - <dt>Bug fixes</dt><dd>Many broken things fixed.</dd> -</dl> - -<p> -See also: <a href="http://jira.public.thoughtworks.org/secure/ReleaseNote.jspa?projectId=10030&styleName=Html&version=10120">JIRA release-summary</a>. - -<h2>Selenium 0.3.0</h2> - -May 2, 2005 - -<ul> - <li><span style="font-weight: bold;">Standalone server for testing remote sites</span><br></li> - <li><span style="font-weight: bold;">Improved in-browser user interface</span></li> - <li><span style="font-weight: bold;">Zope product plug-in for Plone Content Management System</span></li> - <li><span style="font-weight: bold;">Many, many bug fixes</span></li> -</ul> - - -<h2>Selenium 0.2</h2> - -Jan 20, 2005 - -<ul> - <li><span style="font-weight: bold;">Java and Ruby drivers</span><br> - </li> - <li><span style="font-weight: bold;">Cross browser capability improved</span></li> -</ul> -<br> -<br> -</body> -</html> diff --git a/tests/FunctionalTests/selenium/doc/rst2html.bat b/tests/FunctionalTests/selenium/doc/rst2html.bat deleted file mode 100644 index 8fc9043f..00000000 --- a/tests/FunctionalTests/selenium/doc/rst2html.bat +++ /dev/null @@ -1,3 +0,0 @@ -rem - Uses Python docutils-0.3.5 -rem - URL: http://docutils.sourceforge.net -c:/python23/python.exe c:/python23/Scripts/rst2html.py ./seleniumReference.txt ./seleniumReference.html
\ No newline at end of file diff --git a/tests/FunctionalTests/selenium/doc/seleniumReference.html b/tests/FunctionalTests/selenium/doc/seleniumReference.html deleted file mode 100644 index b9b186b9..00000000 --- a/tests/FunctionalTests/selenium/doc/seleniumReference.html +++ /dev/null @@ -1,1148 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> -<meta name="generator" content="Docutils 0.3.5: http://docutils.sourceforge.net/" /> -<title>Selenium Reference</title> -<link rel="stylesheet" href="default.css" type="text/css" /> -</head> -<body> -<h1 class="title">Selenium Reference</h1> -<div class="document" id="selenium-reference"> -<blockquote> -<p>A <strong>command</strong> is what tells Selenium what to do. Selenium commands come in two 'flavors', <strong>Actions</strong> and <strong>Assertions</strong>. -Each command call is one line in the test table of the form:</p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="39%" /> -<col width="33%" /> -<col width="28%" /> -</colgroup> -<tbody valign="top"> -<tr><td>command</td> -<td>target</td> -<td>value</td> -</tr> -</tbody> -</table> -</blockquote> -<p><strong>Actions</strong> are commands that generally manipulate the state of the application. They do things like "click this link" and "select that option". If an Action fails, or has an error, the execution of the current test is stopped.</p> -<p><strong>Checks</strong> verify the state of the application conforms to what is expected. Examples include "make sure the page title is X" and "check that this checkbox is checked". It is possible to tell Selenium to stop the test when an Assertion fails, or to simply record the failure and continue.</p> -<p><strong>Element Locators</strong> tell Selenium which HTML element a command refers to. Many commands require an Element Locator as the "target" attribute. Examples of Element Locators include "elementId" and "document.forms[0].element". These are described more clearly in the next section.</p> -<p><strong>Patterns</strong> are used for various reasons, e.g. to specify the expected value of an input field, or identify a select option. Selenium supports various types of pattern, including regular-expressions, all of which are described in more detail below.</p> -</blockquote> -<div class="section" id="element-locators"> -<h1><a name="element-locators">Element Locators</a></h1> -<blockquote> -<p>Element Locators allow Selenium to identify which HTML element a -command refers to. We support the following strategies for locating -elements:</p> -<dl> -<dt><strong>id=</strong><em>id</em></dt> -<dd>Select the element with the specified @id attribute.</dd> -<dt><strong>name=</strong><em>name</em></dt> -<dd>Select the first element with the specified @name attribute.</dd> -<dt><strong>identifier=</strong><em>id</em></dt> -<dd>Select the element with the specified @id attribute. If no match is found, select the first element whose @name attribute is <em>id</em>.</dd> -<dt><strong>dom=</strong><em>javascriptExpression</em></dt> -<dd><dl class="first last"> -<dt>Find an element using JavaScript traversal of the HTML Document Object Model. DOM locators <em>must</em> begin with "document.".</dt> -<dd><ul class="first last simple"> -<li>dom=document.forms['myForm'].myDropdown</li> -<li>dom=document.images[56]</li> -</ul> -</dd> -</dl> -</dd> -<dt><strong>xpath=</strong><em>xpathExpression</em></dt> -<dd><dl class="first last"> -<dt>Locate an element using an XPath expression. XPath locators <em>must</em> begin with "//".</dt> -<dd><ul class="first last simple"> -<li>xpath=//img[@alt='The image alt text']</li> -<li>xpath=//table[@id='table1']//tr[4]/td[2]</li> -</ul> -</dd> -</dl> -</dd> -<dt><strong>link=</strong><em>textPattern</em></dt> -<dd><dl class="first last"> -<dt>Select the link (anchor) element which contains text matching the specified <em>pattern</em>.</dt> -<dd><ul class="first last simple"> -<li>link=The link text</li> -</ul> -</dd> -</dl> -</dd> -</dl> -<p>Without a locator prefix, Selenium uses:</p> -<ul class="simple"> -<li><strong>dom</strong>, for locators starting with "document."</li> -<li><strong>xpath</strong>, for locators starting with "//"</li> -<li><strong>identifier</strong>, otherwise</li> -</ul> -</blockquote> -</div> -<div class="section" id="select-option-specifiers"> -<h1><a name="select-option-specifiers">Select Option Specifiers</a></h1> -<blockquote> -<p>Select Option Specifiers 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 Specifier.</p> -<dl> -<dt><strong>label=</strong><em>labelPattern</em></dt> -<dd><dl class="first last"> -<dt>matches options based on their labels, i.e. the visible text. </dt> -<dd><ul class="first last simple"> -<li>label=regexp:^[Oo]ther</li> -</ul> -</dd> -</dl> -</dd> -<dt><strong>value=</strong><em>valuePattern</em></dt> -<dd><dl class="first last"> -<dt>matches options based on their values. </dt> -<dd><ul class="first last simple"> -<li>value=other</li> -</ul> -</dd> -</dl> -</dd> -<dt><strong>id=</strong><em>id</em></dt> -<dd><dl class="first last"> -<dt>matches options based on their ids. </dt> -<dd><ul class="first last simple"> -<li>id=option1</li> -</ul> -</dd> -</dl> -</dd> -<dt><strong>index=</strong><em>index</em></dt> -<dd><dl class="first last"> -<dt>matches an option based on its index (offset from zero).</dt> -<dd><ul class="first last simple"> -<li>index=2</li> -</ul> -</dd> -</dl> -</dd> -</dl> -<p>Without a prefix, the default behaviour is to only match on labels.</p> -</blockquote> -</div> -<div class="section" id="string-match-patterns"> -<h1><a name="string-match-patterns">String-match Patterns</a></h1> -<blockquote> -<p>Various Pattern syntaxes are available for matching string values:</p> -<dl> -<dt><strong>glob:</strong><em>pattern</em></dt> -<dd>Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a kind of limited regular-expression syntax typically used in command-line shells. In a glob pattern, "*" represents any sequence of characters, and "?" represents any single character. Glob patterns match against the entire string.</dd> -<dt><strong>regexp:</strong><em>regexp</em></dt> -<dd>Match a string using a regular-expression. The full power of JavaScript regular-expressions is available.</dd> -<dt><strong>exact:</strong><em>string</em></dt> -<dd>Match a string exactly, verbatim, without any of that fancy wildcard stuff.</dd> -</dl> -<p>If no pattern prefix is specified, Selenium assumes that it's a "glob" pattern.</p> -</blockquote> -</div> -<div class="section" id="selenium-actions"> -<h1><a name="selenium-actions">Selenium Actions</a></h1> -<blockquote> -<p>Actions tell Selenium to do something in the application. They generally represent something a user would do.</p> -<p>Many <strong>Actions</strong> can be called with the "AndWait" suffix. This suffix tells Selenium that the action will cause the browser to make a call to the server, and that Selenium should wait for a new page to load. -The exceptions to this pattern are the "open" and "click" actions, which will both wait for a page to load by default.</p> -<p><strong>open</strong>( <em>url</em> )</p> -<blockquote> -<p>Opens a URL in the test frame. This accepts both relative and absolute URLs.</p> -<p><em>Note</em>: The URL must be on the same site as Selenium due to security restrictions in the browser (Cross Site Scripting).</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="15%" /> -<col width="65%" /> -<col width="19%" /> -</colgroup> -<tbody valign="top"> -<tr><td>open</td> -<td>/mypage</td> -<td> </td> -</tr> -<tr><td>open</td> -<td><a class="reference" href="http://localhost/">http://localhost/</a></td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>click</strong>( <em>elementLocator</em> )</p> -<blockquote> -<p>Clicks on a link, button, checkbox or radio button. -If the click action causes a new page to load (like a link usually does), use "clickAndWait".</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="34%" /> -<col width="51%" /> -<col width="14%" /> -</colgroup> -<tbody valign="top"> -<tr><td>click</td> -<td>aCheckbox</td> -<td> </td> -</tr> -<tr><td>clickAndWait</td> -<td>submitButton</td> -<td> </td> -</tr> -<tr><td>clickAndWait</td> -<td>anyLink</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -<dl> -<dt><strong>note:</strong></dt> -<dd>Selenium will <em>always</em> automatically click on a popup dialog raised by the alert() or confirm() -methods. (The exception is those raised during 'onload', which are not yet handled by Selenium). -You <em>must</em> use [verify|assert]Alert or [verify|assert]Confirmation to tell Selenium that you expect the -popup dialog. You may use chooseCancelOnNextConfirmation to click 'cancel' on the next confirmation -dialog instead of clicking 'OK'.</dd> -</dl> -</blockquote> -<p><strong>type</strong>( <em>inputLocator</em>, <em>value</em> )</p> -<blockquote> -<p>Sets the <em>value</em> of an input field, as though you typed it in.</p> -<p>Can also be used to set the value of combo boxes, check boxes, etc. In these cases, <em>value</em> should be the value of the option selected, not the visible text.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="23%" /> -<col width="55%" /> -<col width="21%" /> -</colgroup> -<tbody valign="top"> -<tr><td>type</td> -<td>nameField</td> -<td>John Smith</td> -</tr> -<tr><td>typeAndWait</td> -<td>textBoxThatSubmitsOnChange</td> -<td>newValue</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>select</strong>( <em>dropDownLocator</em>, <em>optionSpecifier</em> )</p> -<blockquote> -<p>Select an option from a drop-down, based on the <em>optionSpecifier</em>. If more than one option matches the specifier (e.g. due to the use of globs like "f*b*", or due to more than one option having the same label or value), then the first matches is selected.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="27%" /> -<col width="33%" /> -<col width="40%" /> -</colgroup> -<tbody valign="top"> -<tr><td>select</td> -<td>dropDown</td> -<td>Australian Dollars</td> -</tr> -<tr><td>select</td> -<td>dropDown</td> -<td>index=0</td> -</tr> -<tr><td>selectAndWait</td> -<td>currencySelector</td> -<td>value=AUD</td> -</tr> -<tr><td>selectAndWait</td> -<td>currencySelector</td> -<td>label=Aus*lian D*rs</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>selectWindow</strong>( <em>windowId</em> )</p> -<blockquote> -<p>Selects a popup window. Once a popup window has been selected, all commands go to that window. To select the main window again, use "null" as the target.</p> -<p><strong>target:</strong> The id of the window to select.</p> -<p><strong>value:</strong> <em>ignored</em></p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="40%" /> -<col width="43%" /> -<col width="17%" /> -</colgroup> -<tbody valign="top"> -<tr><td>selectWindow</td> -<td>myPopupWindow</td> -<td> </td> -</tr> -<tr><td>selectWindow</td> -<td>null</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>goBack</strong>()</p> -<blockquote> -<p>Simulates the user clicking the "back" button on their browser.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="40%" /> -<col width="27%" /> -<col width="33%" /> -</colgroup> -<tbody valign="top"> -<tr><td>goBack</td> -<td> </td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>close</strong>()</p> -<blockquote> -<p>Simulates the user clicking the "close" button in the titlebar of a popup window.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="40%" /> -<col width="27%" /> -<col width="33%" /> -</colgroup> -<tbody valign="top"> -<tr><td>close</td> -<td> </td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>pause</strong>( <em>milliseconds</em> )</p> -<blockquote> -<p>Pauses the execution of the test script for a specified amount of time. This is useful for debugging a script or pausing to wait for some server side action.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="36%" /> -<col width="29%" /> -<col width="36%" /> -</colgroup> -<tbody valign="top"> -<tr><td>pause</td> -<td>5000</td> -<td> </td> -</tr> -<tr><td>pause</td> -<td>2000</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>fireEvent</strong>( <em>elementLocator</em>, <em>eventName</em> )</p> -<blockquote> -<p>Explicitly simulate an event, to trigger the corresponding "on<em>event</em>" handler.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="32%" /> -<col width="39%" /> -<col width="29%" /> -</colgroup> -<tbody valign="top"> -<tr><td>fireEvent</td> -<td>textField</td> -<td>focus</td> -</tr> -<tr><td>fireEvent</td> -<td>dropDown</td> -<td>blur</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>waitForValue</strong>( <em>inputLocator</em>, <em>value</em> )</p> -<blockquote> -<p>Waits for a specified input (e.g. a hidden field) to have a specified <em>value</em>. Will succeed immediately if the input already has the value. This is implemented by polling for the value. Warning: can block indefinitely if the input never has the specified value.</p> -<p><strong>example:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="32%" /> -<col width="42%" /> -<col width="26%" /> -</colgroup> -<tbody valign="top"> -<tr><td>waitForValue</td> -<td>finishIndication</td> -<td>isfinished</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>store</strong>( <em>valueToStore</em>, <em>variableName</em> )</p> -<blockquote> -<p>Stores a value into a variable. The value can be constructed using either variable substitution or javascript evaluation, as detailed in 'Parameter construction and Variables' (below).</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="16%" /> -<col width="70%" /> -<col width="14%" /> -</colgroup> -<tbody valign="top"> -<tr><td>store</td> -<td>Mr John Smith</td> -<td>fullname</td> -</tr> -<tr><td>store</td> -<td>${title} ${firstname} ${surname}</td> -<td>fullname</td> -</tr> -<tr><td>store</td> -<td>javascript{Math.round(Math.PI * 100) / 100}</td> -<td>PI</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>storeValue</strong>( <em>inputLocator</em>, <em>variableName</em> )</p> -<blockquote> -<p>Stores the value of an input field into a variable.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="36%" /> -<col width="32%" /> -<col width="32%" /> -</colgroup> -<tbody valign="top"> -<tr><td>storeValue</td> -<td>userName</td> -<td>userID</td> -</tr> -<tr><td>type</td> -<td>userName</td> -<td>${userID}</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>storeText</strong>( <em>elementLocator</em>, <em>variableName</em> )</p> -<blockquote> -<p>Stores the text of an element into a variable.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="26%" /> -<col width="26%" /> -<col width="48%" /> -</colgroup> -<tbody valign="top"> -<tr><td>storeText</td> -<td>currentDate</td> -<td>expectedStartDate</td> -</tr> -<tr><td>verifyValue</td> -<td>startDate</td> -<td>${expectedStartDate}</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>storeAttribute</strong>( <em>elementLocator</em>@<em>attributeName</em>, <em>variableName</em> )</p> -<blockquote> -<p>Stores the value of an element attribute into a variable.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="31%" /> -<col width="29%" /> -<col width="41%" /> -</colgroup> -<tbody valign="top"> -<tr><td>storeAttribute</td> -<td>input1@class</td> -<td>classOfInput1</td> -</tr> -<tr><td>verifyAttribute</td> -<td>input2@class</td> -<td>${classOfInput1}</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>chooseCancelOnNextConfirmation</strong>()</p> -<blockquote> -<p>Instructs Selenium to click <strong>Cancel</strong> on the next javascript confirmation dialog to be raised. By default, the confirm function will return true, having the same effect as manually clicking OK. After running this command, the next confirmation will behave as if the user had clicked Cancel.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="75%" /> -<col width="13%" /> -<col width="13%" /> -</colgroup> -<tbody valign="top"> -<tr><td>chooseCancelOnNextConfirmation</td> -<td> </td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>answerOnNextPrompt</strong>( <em>answerString</em> )</p> -<blockquote> -<p>Instructs Selenium to return the specified <em>answerString</em> in response to the next prompt.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="65%" /> -<col width="23%" /> -<col width="13%" /> -</colgroup> -<tbody valign="top"> -<tr><td>answerOnNextPrompt</td> -<td>Kangaroo</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -</blockquote> -</div> -<div class="section" id="selenium-checks"> -<h1><a name="selenium-checks">Selenium Checks</a></h1> -<blockquote> -<p>Checks are used to verify the state of the application. They can be used to check the value of a form field, the presense of some text, or the URL of the current page.</p> -<p>All Selenium Checks can be used in 2 modes, "assert" and "verify". These behave identically, except that when an "assert" check fails, the test is aborted. When a "verify" check fails, the test will continue execution. -This allows a single "assert" to ensure that the application is on the correct page, followed by a bunch of "verify" checks to test form field values, labels, etc.</p> -<p><strong>assertLocation</strong>( <em>relativeLocation</em> )</p> -<blockquote> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="54%" /> -<col width="27%" /> -<col width="19%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyLocation</td> -<td>/mypage</td> -<td> </td> -</tr> -<tr><td>assertLocation</td> -<td>/mypage</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertTitle</strong>( <em>titlePattern</em> )</p> -<blockquote> -<p>Verifies the title of the current page.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="48%" /> -<col width="30%" /> -<col width="22%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyTitle</td> -<td>My Page</td> -<td> </td> -</tr> -<tr><td>assertTitle</td> -<td>My Page</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertValue</strong>( <em>inputLocator</em>, <em>valuePattern</em> )</p> -<blockquote> -<p>Verifies the value of an input field (or anything else with a value parameter). For checkbox/radio elements, the value will be "on" or "off" depending on whether the element is checked or not.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="23%" /> -<col width="56%" /> -<col width="21%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyValue</td> -<td>nameField</td> -<td>John Smith</td> -</tr> -<tr><td>assertValue</td> -<td>document.forms[2].nameField</td> -<td>John Smith</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertSelected</strong>( <em>selectLocator</em>, <em>optionSpecifier</em> )</p> -<blockquote> -<p>Verifies that the selected option of a drop-down satisfies the <em>optionSpecifier</em>.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="25%" /> -<col width="49%" /> -<col width="25%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifySelected</td> -<td>dropdown2</td> -<td>John Smith</td> -</tr> -<tr><td>verifySelected</td> -<td>dropdown2</td> -<td>value=js*123</td> -</tr> -<tr><td>assertSelected</td> -<td>document.forms[2].dropDown</td> -<td>label=J* Smith</td> -</tr> -<tr><td>assertSelected</td> -<td>document.forms[2].dropDown</td> -<td>index=0</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertSelectOptions</strong>( <em>selectLocator</em>, <em>optionLabelList</em> )</p> -<blockquote> -<p>Verifies the labels of all options in a drop-down against a comma-separated list. Commas in an expected option can be escaped as ",".</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="29%" /> -<col width="41%" /> -<col width="30%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifySelectOptions</td> -<td>dropdown2</td> -<td>John Smith,Dave Bird</td> -</tr> -<tr><td>assertSelectOptions</td> -<td>document.forms[2].dropDown</td> -<td>Smith\, J,Bird\, D</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertText</strong>( <em>elementLocator</em>, <em>textPattern</em> )</p> -<blockquote> -<p>Verifies the text of an element. This works for any element that contains text. This command uses either the textContent (Mozilla-like browsers) or the innerText (IE-like browsers) of the element, which is the rendered text shown to the user.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="25%" /> -<col width="50%" /> -<col width="25%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyText</td> -<td>statusMessage</td> -<td>Successful</td> -</tr> -<tr><td>assertText</td> -<td>//div[@id='foo']//h1</td> -<td>Successful</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertAttribute</strong>( <em>elementLocator</em>@<em>attributeName</em>, <em>valuePattern</em> )</p> -<blockquote> -<p>Verifies the value of an element attribute.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="32%" /> -<col width="47%" /> -<col width="21%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyAttribute</td> -<td>txt1@class</td> -<td>bigAndBold</td> -</tr> -<tr><td>assertAttribute</td> -<td>document.images[0]@alt</td> -<td>alt-text</td> -</tr> -<tr><td>verifyAttribute</td> -<td>//img[@id='foo']/@alt</td> -<td>alt-text</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertTextPresent</strong>( <em>text</em> )</p> -<blockquote> -<p>Verifies that the specified text appears somewhere on the rendered page shown to the user.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="39%" /> -<col width="50%" /> -<col width="11%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyTextPresent</td> -<td>You are now logged in.</td> -<td> </td> -</tr> -<tr><td>assertTextPresent</td> -<td>You are now logged in.</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertTextNotPresent</strong>( <em>text</em> )</p> -<blockquote> -Verifies that the specified text does NOT appear anywhere on the rendered page.</blockquote> -<p><strong>assertElementPresent</strong>( <em>elementLocator</em> )</p> -<blockquote> -<p>Verifies that the specified element is somewhere on the page.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="48%" /> -<col width="40%" /> -<col width="12%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyElementPresent</td> -<td>submitButton</td> -<td> </td> -</tr> -<tr><td>assertElementPresent</td> -<td>//img[@alt='foo']</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertElementNotPresent</strong>( <em>elementLocator</em> )</p> -<blockquote> -<p>Verifies that the specified element is NOT on the page.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="58%" /> -<col width="30%" /> -<col width="13%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyElementNotPresent</td> -<td>cancelButton</td> -<td> </td> -</tr> -<tr><td>assertElementNotPresent</td> -<td>cancelButton</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertTable</strong>( <em>cellAddress</em>, <em>valuePattern</em> )</p> -<blockquote> -<p>Verifies the text in a cell of a table. The <em>cellAddress</em> syntax <em>tableName.row.column</em>, where row and column start at 0.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="35%" /> -<col width="35%" /> -<col width="29%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyTable</td> -<td>myTable.1.6</td> -<td>Submitted</td> -</tr> -<tr><td>assertTable</td> -<td>results.0.2</td> -<td>13</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertVisible</strong>( <em>elementLocator</em> )</p> -<blockquote> -<p>Verifies that the specified element is both present <em>and</em> visible. An element can be rendered invisible by setting the CSS "visibility" property to "hidden", or the "display" property to "none", either for the element itself or one if its ancestors.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="50%" /> -<col width="31%" /> -<col width="19%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyVisible</td> -<td>postcode</td> -<td> </td> -</tr> -<tr><td>assertVisible</td> -<td>postcode</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertNotVisible</strong>( <em>elementLocator</em> )</p> -<blockquote> -<p>Verifies that the specified element is NOT visible. Elements that are simply not present are also considered invisible.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="55%" /> -<col width="28%" /> -<col width="17%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyNotVisible</td> -<td>postcode</td> -<td> </td> -</tr> -<tr><td>assertNotVisible</td> -<td>postcode</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>verifyEditable / assertEditable</strong>( <em>inputLocator</em> )</p> -<blockquote> -<p>Verifies that the specified element is editable, ie. it's an input element, and hasn't been disabled.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="52%" /> -<col width="30%" /> -<col width="19%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyEditable</td> -<td>shape</td> -<td> </td> -</tr> -<tr><td>assertEditable</td> -<td>colour</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertNotEditable</strong>( <em>inputLocator</em> )</p> -<blockquote> -Verifies that the specified element is NOT editable, ie. it's NOT an input element, or has been disabled.</blockquote> -<p><strong>assertAlert</strong>( <em>messagePattern</em> )</p> -<blockquote> -<p>Verifies that a javascript alert with the specified message was generated. Alerts must be verified in the same order that they were generated.</p> -<p>Verifying an alert has the same effect as manually clicking OK. If an alert is generated but you do not verify it, the next Selenium action will fail.</p> -<p>NOTE: under Selenium, javascript alerts will NOT pop up a visible alert dialog.</p> -<p>NOTE: Selenium does NOT support javascript alerts that are generated in a page's onload() event handler. In this case a visible dialog WILL be generated and Selenium will hang until you manually click OK.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="36%" /> -<col width="51%" /> -<col width="13%" /> -</colgroup> -<tbody valign="top"> -<tr><td>verifyAlert</td> -<td>Invalid Phone Number</td> -<td> </td> -</tr> -<tr><td>assertAlert</td> -<td>Invalid Phone Number</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertConfirmation</strong>( <em>messagePattern</em> )</p> -<blockquote> -<p>Verifies that a javascript confirmation dialog with the specified message was generated. Like alerts, confirmations must be verified in the same order that they were generated.</p> -<p>By default, the confirm function will return true, having the same effect as manually clicking OK. This can be changed by prior execution of the <strong>chooseCancelOnNextConfirmation</strong> command (see above). If an confirmation is generated but you do not verify it, the next Selenium action will fail.</p> -<p>NOTE: under Selenium, javascript confirmations will NOT pop up a visible dialog.</p> -<p>NOTE: Selenium does NOT support javascript confirmations that are generated in a page's onload() event handler. In this case a visible dialog WILL be generated and Selenium will hang until you manually click OK.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="42%" /> -<col width="47%" /> -<col width="12%" /> -</colgroup> -<tbody valign="top"> -<tr><td>assertConfirmation</td> -<td>Remove this user?</td> -<td> </td> -</tr> -<tr><td>verifyConfirmation</td> -<td>Are you sure?</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -<p><strong>assertPrompt</strong>( <em>messagePattern</em> )</p> -<blockquote> -<p>Verifies that a javascript prompt dialog with the specified message was generated. Like alerts, prompts must be verified in the same order that they were generated.</p> -<p>Successful handling of the prompt requires prior execution of the <strong>answerOnNextPrompt</strong> command (see above). If a prompt is generated but you do not verify it, the next Selenium action will fail.</p> -<p><strong>examples:</strong></p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="35%" /> -<col width="56%" /> -<col width="10%" /> -</colgroup> -<tbody valign="top"> -<tr><td>answerOnNextPrompt</td> -<td>Joe</td> -<td> </td> -</tr> -<tr><td>click</td> -<td>id=delegate</td> -<td> </td> -</tr> -<tr><td>verifyPrompt</td> -<td>Delegate to who?</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -</blockquote> -</div> -<div class="section" id="parameter-construction-and-variables"> -<h1><a name="parameter-construction-and-variables">Parameter construction and Variables</a></h1> -<blockquote> -<p>All Selenium command parameters can be constructed using both simple -variable substitution as well as full javascript. Both of these -mechanisms can access previously stored variables, but do so using -different syntax.</p> -<p><strong>Stored Variables</strong></p> -<p>The commands <em>store</em>, <em>storeValue</em> and <em>storeText</em> can be used to store a variable -value for later access. Internally, these variables are stored in a map called "storedVars", -with values keyed by the variable name. These commands are documented in the command reference.</p> -<p><strong>Variable substitution</strong></p> -<p>Variable substitution provides a simple way to include a previously stored variable in a -command parameter. This is a simple mechanism, by which the variable to substitute is indicated -by ${variableName}. Multiple variables can be substituted, and intermixed with static text.</p> -<p>Example:</p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="18%" /> -<col width="36%" /> -<col width="45%" /> -</colgroup> -<tbody valign="top"> -<tr><td>store</td> -<td>Mr</td> -<td>title</td> -</tr> -<tr><td>storeValue</td> -<td>nameField</td> -<td>surname</td> -</tr> -<tr><td>store</td> -<td>${title} ${surname}</td> -<td>fullname</td> -</tr> -<tr><td>type</td> -<td>textElement</td> -<td>Full name is: ${fullname}</td> -</tr> -</tbody> -</table> -</blockquote> -<p><strong>Javascript evaluation</strong></p> -<p>Javascript evaluation provides the full power of javascript in constructing a command parameter. -To use this mechanism, the <em>entire</em> parameter value must be prefixed by -'javascript{' with a trailing '}'. The text inside the braces is evaluated as a javascript expression, -and can access previously stored variables using the <em>storedVars</em> map detailed above. -Note that variable substitution cannot be combined with javascript evaluation.</p> -<p>Example:</p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="9%" /> -<col width="44%" /> -<col width="46%" /> -</colgroup> -<tbody valign="top"> -<tr><td>store</td> -<td>javascript{'merchant' + (new Date()).getTime()}</td> -<td>merchantId</td> -</tr> -<tr><td>type</td> -<td>textElement</td> -<td>javascript{storedVars['merchantId'].toUpperCase()}</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -</div> -<div class="section" id="extending-selenium"> -<h1><a name="extending-selenium">Extending Selenium</a></h1> -<blockquote> -<p>It can be quite simple to extend Selenium, adding your own actions, checks and locator-strategies. -This is done with javascript by adding methods to the Selenium object prototype, and the PageBot -object prototype. On startup, Selenium will automatically look through methods on these prototypes, -using name patterns to recognise which ones are actions, checks and locators.</p> -<p>The following examples try to give an indication of how Selenium can be extended with javascript.</p> -</blockquote> -<p><strong>Actions</strong></p> -<blockquote> -<p>All <em>doFoo</em> methods on the Selenium prototype are added as actions. For each action <em>foo</em> there -is also an action <em>fooAndWait</em> registered. An action method can take up to 2 parameters, which -will be passed the second and third column values in the test.</p> -<p>Example: Add a "typeRepeated" action to Selenium, which types the text twice into a text box.</p> -<pre class="literal-block"> -Selenium.prototype.doTypeRepeated = function(locator, text) { - // All locator-strategies are automatically handled by "findElement" - var element = this.page().findElement(locator); - - // Create the text to type - var valueToType = text + text; - - // Replace the element text with the new text - this.page().replaceText(element, valueToType); -}; -</pre> -</blockquote> -<p><strong>Checks</strong></p> -<blockquote> -<p>All <em>assertFoo</em> methods on the Selenium prototype are added as checks. For each check <em>foo</em> there -is an <em>assertFoo</em> and <em>verifyFoo</em> registered. An assert method can take up to 2 parameters, which -will be passed the second and third column values in the test.</p> -<p>Example: Add a <em>valueRepeated</em> check, that makes sure that the element value -consists of the supplied text repeated. The 2 commands that would be available in tests would be -<em>assertValueRepeated</em> and <em>verifyValueRepeated</em>.</p> -<pre class="literal-block"> -Selenium.prototype.assertValueRepeated = function(locator, text) { - // All locator-strategies are automatically handled by "findElement" - var element = this.page().findElement(locator); - - // Create the text to verify - var expectedValue = text + text; - - // Get the actual element value - var actualValue = element.value; - - // Make sure the actual value matches the expected - this.assertMatches(expectedValue, actualValue); -}; -</pre> -</blockquote> -<p><strong>Locator Strategies</strong></p> -<blockquote> -<p>All <em>locateElementByFoo</em> methods on the PageBot prototype are added as locator-strategies. A locator strategy takes 2 parameters, the first being the locator string (minus the prefix), and the second being the document in which to search.</p> -<p>Example: Add a "valuerepeated=" locator, that finds the first element a value attribute equal to the the supplied value repeated.</p> -<pre class="literal-block"> -// The "inDocument" is a the document you are searching. -PageBot.prototype.locateElementByValueRepeated = function(text, inDocument) { - // Create the text to search for - var expectedValue = text + text; - - // Loop through all elements, looking for ones that have - // a value === our expected value - var allElements = inDocument.getElementsByTagName("*"); - for (var i = 0; i < allElements.length; i++) { - var testElement = allElements[i]; - if (testElement.value && testElement.value === expectedValue) { - return testElement; - } - } - return null; -}; -</pre> -</blockquote> -<p><strong>user-extensions.js</strong></p> -<blockquote> -<p>By default, Selenium looks for a file called "user-extensions.js", and loads the javascript code found in that file. This file provides a convenient location for adding features to Selenium, without needing to modify the core Selenium sources.</p> -<p>In the standard distibution, this file does not exist. Users can create this file and place their extension code in this common location, removing the need to modify the Selenium sources, and hopefully assisting with the upgrade process.</p> -</blockquote> -<hr /> -<p>:</p> -</div> -</div> -</body> -</html> diff --git a/tests/FunctionalTests/selenium/doc/seleniumReference.txt b/tests/FunctionalTests/selenium/doc/seleniumReference.txt deleted file mode 100644 index e7819869..00000000 --- a/tests/FunctionalTests/selenium/doc/seleniumReference.txt +++ /dev/null @@ -1,771 +0,0 @@ -========================= -Selenium Reference -========================= - --------- -Concepts --------- - - A **command** is what tells Selenium what to do. Selenium commands come in two 'flavors', **Actions** and **Assertions**. - Each command call is one line in the test table of the form: - - ======= ====== ===== - command target value - ======= ====== ===== - - **Actions** are commands that generally manipulate the state of the application. They do things like "click this link" and "select that option". If an Action fails, or has an error, the execution of the current test is stopped. - - **Assertions** verify the state of the application conforms to what is expected. Examples include "make sure the page title is X" and "verify that this checkbox is checked". It is possible to tell Selenium to stop the test when an Assertion fails, or to simply record the failure and continue. - - **Element Locators** tell Selenium which HTML element a command refers to. Many commands require an Element Locator as the "target" attribute. Examples of Element Locators include "elementId" and "document.forms[0].element". These are described more clearly in the next section. - - **Patterns** are used for various reasons, e.g. to specify the expected value of an input field, or identify a select option. Selenium supports various types of pattern, including regular-expressions, all of which are described in more detail below. - ------------------ -Element Locators ------------------ - - Element Locators allow Selenium to identify which HTML element a - command refers to. The format of a locator is: - - *locatorType*\ **=**\ *argument* - - We support the following strategies for locating - elements: - - **id=**\ *id* - - Select the element with the specified @id attribute. - - **name=**\ *name* - - Select the first element with the specified @name attribute. - - username - - name=username - - The name may optionally be following by one or more *element-filters*, separated from the name by whitespace. If the *filterType* is not specified, **value** is assumed. - - name=flavour value=chocolate - - **identifier=**\ *id* - - Select the element with the specified @id attribute. If no match is found, select the first element whose @name attribute is *id*. - - **dom=**\ *javascriptExpression* - - Find an element using JavaScript traversal of the HTML Document Object Model. DOM locators *must* begin with "document.". - - dom=document.forms['myForm'].myDropdown - - dom=document.images[56] - - **xpath=**\ *xpathExpression* - - Locate an element using an XPath expression. - - xpath=//img[@alt='The image alt text'] - - xpath=//table[@id='table1']//tr[4]/td[2] - - **link=**\ *textPattern* - - Select the link (anchor) element which contains text matching the specified *pattern*. - - link=The link text - - - If no *locatorType* is specified, Selenium uses: - - * **dom**, for locators starting with "document." - * **xpath**, for locators starting with "//" - * **identifier**, otherwise - ---------------- -Element Filters ---------------- - - 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. - - Filters look much like locators, ie. - - *filterType*\ **=**\ *argument* - - Supported element-filters are: - - **value=**\ *valuePattern* - - Matches elements based on their values. This is particularly useful for refining a list of similarly-named toggle-buttons. - - **index=**\ *index* - - Selects a single element based on its position in the list offset from zero). - ------------------------- -Select Option Specifiers ------------------------- - - Select Option Specifiers 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 Specifier. - - **label=**\ *labelPattern* - matches options based on their labels, i.e. the visible text. - - label=regexp:^[Oo]ther - - **value=**\ *valuePattern* - matches options based on their values. - - value=other - - **id=**\ *id* - matches options based on their ids. - - id=option1 - - **index=**\ *index* - matches an option based on its index (offset from zero). - - index=2 - - If no optionSpecifierType prefix is provided, the default behaviour is to match on **label**. - ------------------------- -String-match Patterns ------------------------- - - Various Pattern syntaxes are available for matching string values: - - **glob:**\ *pattern* - Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a kind of limited regular-expression syntax typically used in command-line shells. In a glob pattern, "*" represents any sequence of characters, and "?" represents any single character. Glob patterns match against the entire string. - - **regexp:**\ *regexp* - Match a string using a regular-expression. The full power of JavaScript regular-expressions is available. - - **exact:**\ *string* - Match a string exactly, verbatim, without any of that fancy wildcard stuff. - - If no pattern prefix is specified, Selenium assumes that it's a "glob" pattern. - ----------------- -Selenium Actions ----------------- - - Actions tell Selenium to do something in the application. They generally represent something a user would do. - - Many **Actions** can be called with the "AndWait" suffix. This suffix tells Selenium that the action will cause the browser to make a call to the server, and that Selenium should wait for a new page to load. - - **open**\ ( *url* ) - - Opens a URL in the test frame. This accepts both relative and absolute URLs. - - The "open" command waits for the page to load before proceeding, - ie. the "AndWait" suffix is implicit. - - *Note*: The URL must be on the same site as Selenium due to security restrictions in the browser (Cross Site Scripting). - - **examples:** - - ==== ================= ===== - open /mypage - open http://localhost/ - ==== ================= ===== - - **click**\ ( *elementLocator* ) - - Clicks on a link, button, checkbox or radio button. - If the click action causes a new page to load (like a link usually does), use "clickAndWait". - - **examples:** - - ============ ================== ===== - click aCheckbox - clickAndWait submitButton - clickAndWait anyLink - ============ ================== ===== - - **type**\ ( *inputLocator*, *value* ) - - Sets the *value* of an input field, as though you typed it in. - - 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. - - **examples:** - - =========== ========================== ========== - type nameField John Smith - typeAndWait textBoxThatSubmitsOnChange newValue - =========== ========================== ========== - - **select**\ ( *dropDownLocator*, *optionSpecifier* ) - - Select an option from a drop-down, based on the *optionSpecifier*. If more than one option matches the specifier (e.g. due to the use of globs like "f*b*", or due to more than one option having the same label or value), then the first matches is selected. - - **examples:** - - ============= ================ ========== - select dropDown Australian Dollars - select dropDown index=0 - selectAndWait currencySelector value=AUD - selectAndWait currencySelector label=Aus*lian D*rs - ============= ================ ========== - - **check**\ ( *toggleButtonLocator* ) - - Check a toggle-button (ie. a check-box or radio-button). - - Note: if addressing the toggle-button element by name, you'll need to append an element-filter (e.g. typically by value), since toggle-button groups consist of input-elements with the same name. - - **examples:** - - ============= ======================== ========== - check name=flavour value=honey - check flavour honey - ============= ======================== ========== - - **uncheck**\ ( *toggleButtonLocator* ) - - Un-check a toggle-button. - - **selectWindow**\ ( *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. - - **target:** The id of the window to select. - - **value:** *ignored* - - **examples:** - - ============ ============= ===== - selectWindow myPopupWindow - selectWindow null - ============ ============= ===== - - **goBack**\ () - - Simulates the user clicking the "back" button on their browser. - - **examples:** - - ============= ==== ===== - goBackAndWait - ============= ==== ===== - - **close**\ () - - Simulates the user clicking the "close" button in the titlebar of a popup window. - - **examples:** - - ====== ==== ===== - close - ====== ==== ===== - - **pause**\ ( *milliseconds* ) - - Pauses the execution of the test script for a specified amount of time. This is useful for debugging a script or pausing to wait for some server side action. - - **examples:** - - ===== ==== ===== - pause 5000 - pause 2000 - ===== ==== ===== - - **fireEvent**\ ( *elementLocator*, *eventName* ) - - Explicitly simulate an event, to trigger the corresponding "on\ *event*" handler. - - **examples:** - - ========= =========== ======== - fireEvent textField focus - fireEvent dropDown blur - ========= =========== ======== - - **waitForValue**\ ( *inputLocator*, *value* ) - - Waits for a specified input (e.g. a hidden field) to have a specified *value*. Will succeed immediately if the input already has the value. This is implemented by polling for the value. Warning: can block indefinitely if the input never has the specified value. - - **example:** - - ============ ================ ========== - waitForValue finishIndication isfinished - ============ ================ ========== - - **store**\ ( *valueToStore*, *variableName* ) - - Stores a value into a variable. The value can be constructed using either variable substitution or JavaScript evaluation, as detailed in 'Parameter construction and Variables' (below). - - **examples:** - - ========== ============================================ ========= - store Mr John Smith fullname - store ${title} ${firstname} ${surname} fullname - store javascript{Math.round(Math.PI * 100) / 100} PI - ========== ============================================ ========= - - **storeValue**\ ( *inputLocator*, *variableName* ) - - Stores the value of an input field into a variable. - - **examples:** - - ========== ========= ========= - storeValue userName userID - type userName ${userID} - ========== ========= ========= - - **storeText**\ ( *elementLocator*, *variableName* ) - - Stores the text of an element into a variable. - - **examples:** - - =========== =========== ==================== - storeText currentDate expectedStartDate - verifyValue startDate ${expectedStartDate} - =========== =========== ==================== - - **storeAttribute**\ ( *elementLocator*\ @\ *attributeName*, *variableName* ) - - Stores the value of an element attribute into a variable. - - **examples:** - - =============== ============== ==================== - storeAttribute input1@\ class classOfInput1 - verifyAttribute input2@\ class ${classOfInput1} - =============== ============== ==================== - - **chooseCancelOnNextConfirmation**\ () - - 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. - - **examples:** - - ============================== ===== ===== - chooseCancelOnNextConfirmation - ============================== ===== ===== - - **answerOnNextPrompt**\ ( *answerString* ) - - Instructs Selenium to return the specified *answerString* in - response to the next call to window.prompt(). - - **examples:** - - ========================== ========= ===== - answerOnNextPrompt Kangaroo - ========================== ========= ===== - -------------------- -Selenium Assertions -------------------- - - Assertions are used to verify the state of the application. They can be used to check the value of a form field, the presense of some text, or the URL of the current page. - - All Selenium assertions can be used in 2 modes, "assert" and "verify". These behave identically, except that when an "assert" fails, the test is aborted. When a "verify" fails, the test will continue execution. This allows a single "assert" to ensure that the application is on the correct page, followed by a bunch of "verify" assertions to test form field values, labels, etc. - - A growing number of assertions have a negative version. In most cases, except where indicated, if the positive assertion is of the form **assertXYZ**, then the negative cases will be of the form **assertNotXYZ**. - - **assertLocation**\ ( *relativeLocation* ) - - **examples:** - - ============== ======= ===== - verifyLocation /mypage - assertLocation /mypage - ============== ======= ===== - - **assertTitle**\ ( *titlePattern* ) - - Verifies the title of the current page. - - **examples:** - - =========== ======= ===== - verifyTitle My Page - assertTitle My Page - =========== ======= ===== - - **assertNotTitle**\ ( *titlePattern* ) - - Verifies that the title of the current page does not match the specified pattern. - - **assertValue**\ ( *inputLocator*, *valuePattern* ) - - Verifies the value of an input field (or anything else with a value parameter). For checkbox/radio elements, the value will be "on" or "off" depending on whether the element is checked or not. - - **examples:** - - =========== =========================== ========== - verifyValue nameField John Smith - assertValue document.forms[2].nameField John Smith - =========== =========================== ========== - - **assertNotValue**\ ( *inputLocator*, *valuePattern* ) - - Verifies the value of an input field does not match the specified pattern. - - **assertSelected**\ ( *selectLocator*, *optionSpecifier* ) - - Verifies that the selected option of a drop-down satisfies the *optionSpecifier*. - - **examples:** - - ============== =========================== ========== - verifySelected dropdown2 John Smith - verifySelected dropdown2 value=js*123 - assertSelected document.forms[2].dropDown label=J* Smith - assertSelected document.forms[2].dropDown index=0 - ============== =========================== ========== - - **assertSelectOptions**\ ( *selectLocator*, *optionLabelList* ) - - Verifies the labels of all options in a drop-down against a comma-separated list. Commas in an expected option can be escaped as "\,". - - **examples:** - - =================== =========================== ==================== - verifySelectOptions dropdown2 John Smith,Dave Bird - assertSelectOptions document.forms[2].dropDown Smith\\, J,Bird\\, D - =================== =========================== ==================== - - **assertChecked**\ ( *toggleButtonLocator* ) - - Verifies that the specified toggle-button element is checked. - - **examples:** - - ============= ======================== ========== - verifyChecked flavour honey - ============= ======================== ========== - - **assertText**\ ( *elementLocator*, *textPattern* ) - - Verifies 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. - - **examples:** - - ========== ==================== ========== - verifyText statusMessage Successful - assertText //div[@id='foo']//h1 Successful - ========== ==================== ========== - - **assertNotText**\ ( *elementLocator*, *textPattern* ) - - Verifies that the text of an element does not match the specified pattern. - - **assertAttribute**\ ( *elementLocator*\ @\ *attributeName*, *valuePattern* ) - - Verifies the value of an element attribute. - - **examples:** - - =============== ====================== ========== - verifyAttribute txt1@\ class bigAndBold - assertAttribute document.images[0]@alt alt-text - verifyAttribute //img[@id='foo']/@alt alt-text - =============== ====================== ========== - - **assertNotAttribute**\ ( *elementLocator*\ @\ *attributeName*, *valuePattern* ) - - Verifies that the value of an element attribute does not match the specified pattern. - - **assertTextPresent**\ ( *text* ) - - Verifies that the specified text appears somewhere on the rendered page shown to the user. - - **examples:** - - ================= ====================== ===== - verifyTextPresent You are now logged in. - assertTextPresent You are now logged in. - ================= ====================== ===== - - **assertTextNotPresent**\ ( *text* ) - - Verifies that the specified text does NOT appear anywhere on the rendered page. - - **assertElementPresent**\ ( *elementLocator* ) - - Verifies that the specified element is somewhere on the page. - - **examples:** - - ==================== ================= ===== - verifyElementPresent submitButton - assertElementPresent //img[@alt='foo'] - ==================== ================= ===== - - **assertElementNotPresent**\ ( *elementLocator* ) - - Verifies that the specified element is NOT on the page. - - **examples:** - - ======================= ============ ===== - verifyElementNotPresent cancelButton - assertElementNotPresent cancelButton - ======================= ============ ===== - - **assertTable**\( *cellAddress*, *valuePattern* ) - - Verifies the text in a cell of a table. The *cellAddress* syntax *tableName.row.column*, where row and column start at 0. - - **examples:** - - =========== =========== ========= - verifyTable myTable.1.6 Submitted - assertTable results.0.2 13 - =========== =========== ========= - - **assertNotTable**\( *cellAddress*, *valuePattern* ) - - Verifies that the text in a cell of a table does not match the specified pattern. Note that this will fail if the table cell does not exist. - - **assertVisible**\ ( *elementLocator* ) - - Verifies that the specified element is both present *and* visible. An element can be rendered invisible by setting the CSS "visibility" property to "hidden", or the "display" property to "none", either for the element itself or one if its ancestors. - - **examples:** - - ============= ======== ===== - verifyVisible postcode - assertVisible postcode - ============= ======== ===== - - **assertNotVisible**\ ( *elementLocator* ) - - Verifies that the specified element is NOT visible. Elements that are simply not present are also considered invisible. - - **examples:** - - ================ ======== ===== - verifyNotVisible postcode - assertNotVisible postcode - ================ ======== ===== - - **verifyEditable / assertEditable**\ ( *inputLocator* ) - - Verifies that the specified element is editable, ie. it's an input element, and hasn't been disabled. - - **examples:** - - ============== ======== ===== - verifyEditable shape - assertEditable colour - ============== ======== ===== - - **assertNotEditable**\ ( *inputLocator* ) - - Verifies that the specified element is NOT editable, ie. it's NOT an input element, or has been disabled. - - **assertAlert**\ ( *messagePattern* ) - - Verifies that a JavaScript alert was generated, with the specified - message. - - If an alert is generated but you do not verify it, the next - Selenium action will fail. Alerts must be verified in the order - that they were generated. - - **examples:** - - ============== ==================== ===== - verifyAlert Invalid Phone Number - assertAlert Invalid Phone Number - ============== ==================== ===== - - **assertConfirmation**\ ( *messagePattern* ) - - Verifies that a JavaScript confirmation dialog was generated, with - the specified message. - - 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 (see - above). - - Like alerts, any unexpected confirmation will cause the test to - fail, and confirmations must be verified in the order that they - were generated. - - **examples:** - - ================== ==================== ===== - assertConfirmation Remove this user? - verifyConfirmation Are you sure? - ================== ==================== ===== - - **assertPrompt**\ ( *messagePattern* ) - - Verifies that a JavaScript prompt dialog was generated, with the - specified message. - - Successful handling of the prompt requires prior execution of the - **answerOnNextPrompt** command (see above). - - Like alerts, unexpected prompts will cause the test to fail, and - they must be verified in the order that they were generated. - - **examples:** - - ================== ============================= ===== - answerOnNextPrompt Joe - click id=delegate - verifyPrompt Delegate to who? - ================== ============================= ===== - -------------------------------------------- -Handling of alert(), confirm() and prompt() -------------------------------------------- - - Selenium overrides the default implementations of the JavaScript - window.alert(), window.confirm() and window.prompt() functions, - enabling tests to simulate the actions of the user when these occur. - Under normal condition, no visible JavaScript dialog-box will appear. - - If your application generates alerts, confirmations, or prompts, you - *must* use assertAlert, assertConfirmation and assertPrompt (or their - "verify" equivalents) to handle them. Any unhandled alerts will result - in the test failing. - - *PROVISO:* Selenium is unable to handle alerts, confirmations, or - prompts raised during processing of the 'onload' event. In such cases - a visible dialog-box WILL appear, and Selenium will hang until you - manually handle it. This is an unfortunate restriction, but at this - time we have no solution. - ------------------------------------- -Parameter construction and Variables ------------------------------------- - - All Selenium command parameters can be constructed using both simple - variable substitution as well as full JavaScript. Both of these - mechanisms can access previously stored variables, but do so using - different syntax. - - **Stored Variables** - - The commands *store*, *storeValue* and *storeText* can be used to store a variable - value for later access. Internally, these variables are stored in a map called "storedVars", - with values keyed by the variable name. These commands are documented in the command reference. - - **Variable substitution** - - Variable substitution provides a simple way to include a previously stored variable in a - command parameter. This is a simple mechanism, by which the variable to substitute is indicated - by ${variableName}. Multiple variables can be substituted, and intermixed with static text. - - Example: - - ========== ==================== ========== - store Mr title - storeValue nameField surname - store ${title} ${surname} fullname - type textElement Full name is: ${fullname} - ========== ==================== ========== - - **JavaScript evaluation** - - JavaScript evaluation provides the full power of JavaScript in constructing a command parameter. - To use this mechanism, the *entire* parameter value must be prefixed by - 'javascript{' with a trailing '}'. The text inside the braces is evaluated as a JavaScript expression, - and can access previously stored variables using the *storedVars* map detailed above. - Note that variable substitution cannot be combined with JavaScript evaluation. - - Example: - - ========== ================================================ ========== - store javascript{'merchant' + (new Date()).getTime()} merchantId - type textElement javascript{storedVars['merchantId'].toUpperCase()} - ========== ================================================ ========== - ------------------- -Extending Selenium ------------------- - - It can be quite simple to extend Selenium, adding your own actions, assertions and locator-strategies. - This is done with JavaScript by adding methods to the Selenium object prototype, and the PageBot - object prototype. On startup, Selenium will automatically look through methods on these prototypes, - using name patterns to recognise which ones are actions, assertions and locators. - - The following examples try to give an indication of how Selenium can be extended with JavaScript. - -**Actions** - - All *doFoo* methods on the Selenium prototype are added as actions. For each action *foo* there - is also an action *fooAndWait* registered. An action method can take up to 2 parameters, which - will be passed the second and third column values in the test. - - Example: Add a "typeRepeated" action to Selenium, which types the text twice into a text box. - - :: - - Selenium.prototype.doTypeRepeated = function(locator, text) { - // All locator-strategies are automatically handled by "findElement" - var element = this.page().findElement(locator); - - // Create the text to type - var valueToType = text + text; - - // Replace the element text with the new text - this.page().replaceText(element, valueToType); - }; - -**Assertions** - - All *assertFoo* methods on the Selenium prototype are added as - assertions. For each assertion *foo* there is an *assertFoo* and - *verifyFoo* registered. An assert method can take up to 2 parameters, - which will be passed the second and third column values in the test. - - Example: Add a *valueRepeated* assertion, that makes sure that the - element value consists of the supplied text repeated. The 2 commands - that would be available in tests would be *assertValueRepeated* and - *verifyValueRepeated*. - - :: - - Selenium.prototype.assertValueRepeated = function(locator, text) { - // All locator-strategies are automatically handled by "findElement" - var element = this.page().findElement(locator); - - // Create the text to verify - var expectedValue = text + text; - - // Get the actual element value - var actualValue = element.value; - - // Make sure the actual value matches the expected - Assert.matches(expectedValue, actualValue); - }; - -**Locator Strategies** - - All *locateElementByFoo* methods on the PageBot prototype are added as locator-strategies. A locator strategy takes 2 parameters, the first being the locator string (minus the prefix), and the second being the document in which to search. - - Example: Add a "valuerepeated=" locator, that finds the first element a value attribute equal to the the supplied value repeated. - - :: - - // The "inDocument" is a the document you are searching. - PageBot.prototype.locateElementByValueRepeated = function(text, inDocument) { - // Create the text to search for - var expectedValue = text + text; - - // Loop through all elements, looking for ones that have - // a value === our expected value - var allElements = inDocument.getElementsByTagName("*"); - for (var i = 0; i < allElements.length; i++) { - var testElement = allElements[i]; - if (testElement.value && testElement.value === expectedValue) { - return testElement; - } - } - return null; - }; - -**user-extensions.js** - - By default, Selenium looks for a file called "user-extensions.js", and loads the JavaScript code found in that file. This file provides a convenient location for adding features to Selenium, without needing to modify the core Selenium sources. - - In the standard distibution, this file does not exist. Users can create this file and place their extension code in this common location, removing the need to modify the Selenium sources, and hopefully assisting with the upgrade process. - ------------------- - -: diff --git a/tests/FunctionalTests/selenium/doc/testRunner.txt b/tests/FunctionalTests/selenium/doc/testRunner.txt deleted file mode 100644 index 56b949d2..00000000 --- a/tests/FunctionalTests/selenium/doc/testRunner.txt +++ /dev/null @@ -1,99 +0,0 @@ -========================= -TestRunner Reference -========================= ------------ -Test Suites ------------ - - A test suite is represented by an HTML document containing a single-column table. Each entry in the table should be a hyperlink to a test-case document. The first row will be ignored by Selenium, so this can be used for a title, and is typically used to hold a title. - - By default Selenium will attempt to load the test-suite from "tests/TestSuite.html". An alternative test-suite source can be specified by appending a "test" parameter to the TestRunner.html URL, e.g.:: - - http://localhost:8000/TestRunner.html?test=AllTests.php - - The "test" URL is interpreted relative to the location of TestRunner.html. - ----------- -Test Cases ----------- - - A test-case is represented by an HTML document, containing a table with 3 columns: *command*, *target*, *value*. Not all commands take a value, however. In this case either leave the column blank or use a to make the table look better. - - The first row will be ignored by Selenium, so this can be used for a title or any other information. - - Example: - - ========== ============ ========== - Simple Test Table - -------------------------------------- - open /mypage - type nameField John Smith - click submitButton True - verifyText name John Smith - ========== ============ ========== - ------------------ -SetUp / TearDown ------------------ - - There are no setUp and tearDown commands in Selenium, but there is a way to handle these common testing operations. On the site being tested, create URLs for setUp and tearDown. Then, when the test runner opens these URLs, the server can do whatever setUp or tearDown is necessary. - - Example: - - For the T&E project, we wanted the functional tests to run as a dummy user. Therefore, we made a /setUpFT URL that would create a dummy user and write the userID to the page. Then, we can store this value (using the command storeValue) and use it in the script. Finally, we made a /tearDownFT URL which takes the dummy userID as a parameter and deletes the user. Therefore, our tests look like this: - - ========== ============================ ========== - Setup and Teardown - ------------------------------------------------------ - open /setUpFT - storeValue userid - open /login - type userID ${userid} - click submit - open /tearDownFT?userid=${userid} - ========== ============================ ========== - - ----------------------- -Continuous Integration ----------------------- - - Selenium can be integrated with an automated build. When the parameter "auto=true" is added to the URL, Selenium will run the entire suite of tests, and then post the results to a handling URL. The default URL is "/postResults", but an alternative handler location can be provided by specifying a "resultsUrl" parameter. - - The fields of the post are: - - ================== ====================================================================================================== - Parameter Description - ================== ====================================================================================================== - result the word "passed" or "failed" depending on whether the whole suite passed or at least one test failed. - totalTime the time in seconds for the whole suite to run - numTestPasses tht 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 the first test table - testTable.2 the second test table - ... ... - testTable.N The Nth test table - ================== ====================================================================================================== - - Therefore, the steps for continuous integration are: - 1. Create a servlet-type application at the url /postResults which can read the parameters above and write them to a file - 2. Create a script which can start up a brower and send to to the URL: selenium?auto=true - - Generally, this can be done by merely calling the browser with the URL as an argument: - firefox.exe http://localhost/selenium?auto=true - 3. Make your continuous build: - - Call the script from step 2, preferably using more than one browser - - Wait for it to finish, possibly by checking for the existence of the file(s) from step 1 - - Parse these files to determine whether the build passed or failed - - Act accordingly (send emails, update a build web page, etc.) - - ------------------- - -:Authors: Paul Gross, Jason Huggins -:Created Date: 08/23/2004 -:Modified Date: 28/01/2005 -:Created With: reStructuredText: http://docutils.sourceforge.net/rst.html diff --git a/tests/FunctionalTests/selenium/doc/testrunner.html b/tests/FunctionalTests/selenium/doc/testrunner.html deleted file mode 100644 index 86cac0cb..00000000 --- a/tests/FunctionalTests/selenium/doc/testrunner.html +++ /dev/null @@ -1,213 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> -<meta name="generator" content="Docutils 0.3.5: http://docutils.sourceforge.net/" /> -<title>TestRunner Reference</title> -<link rel="stylesheet" href="default.css" type="text/css" /> -</head> -<body> -<h1 class="title">TestRunner Reference</h1> -<div class="document" id="testrunner-reference"> -<div class="section" id="test-suites"> -<h1><a name="test-suites">Test Suites</a></h1> -<blockquote> -<p>A test suite is represented by an HTML document containing a single-column table. Each entry in the table should be a hyperlink to a test-case document. The first row will be ignored by Selenium, so this can be used for a title, and is typically used to hold a title.</p> -<p>By default Selenium will attempt to load the test-suite from "tests/TestSuite.html". An alternative test-suite source can be specified by appending a "test" parameter to the TestRunner.html URL, e.g.:</p> -<pre class="literal-block"> -http://localhost:8000/TestRunner.html?test=AllTests.php -</pre> -<p>The "test" URL is interpreted relative to the location of TestRunner.html.</p> -</blockquote> -</div> -<div class="section" id="test-cases"> -<h1><a name="test-cases">Test Cases</a></h1> -<blockquote> -<p>A test-case is represented by an HTML document, containing a table with 3 columns: <em>command</em>, <em>target</em>, <em>value</em>. Not all commands take a value, however. In this case either leave the column blank or use a &nbsp; to make the table look better.</p> -<p>The first row will be ignored by Selenium, so this can be used for a title or any other information.</p> -<p>Example:</p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="31%" /> -<col width="38%" /> -<col width="31%" /> -</colgroup> -<tbody valign="top"> -<tr><td colspan="3">Simple Test Table</td> -</tr> -<tr><td>open</td> -<td>/mypage</td> -<td> </td> -</tr> -<tr><td>type</td> -<td>nameField</td> -<td>John Smith</td> -</tr> -<tr><td>click</td> -<td>submitButton</td> -<td>True</td> -</tr> -<tr><td>verifyText</td> -<td>name</td> -<td>John Smith</td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -</div> -<div class="section" id="setup-teardown"> -<h1><a name="setup-teardown">SetUp / TearDown</a></h1> -<blockquote> -<p>There are no setUp and tearDown commands in Selenium, but there is a way to handle these common testing operations. On the site being tested, create URLs for setUp and tearDown. Then, when the test runner opens these URLs, the server can do whatever setUp or tearDown is necessary.</p> -<p>Example:</p> -<blockquote> -<p>For the T&E project, we wanted the functional tests to run as a dummy user. Therefore, we made a /setUpFT URL that would create a dummy user and write the userID to the page. Then, we can store this value (using the command storeValue) and use it in the script. Finally, we made a /tearDownFT URL which takes the dummy userID as a parameter and deletes the user. Therefore, our tests look like this:</p> -<table border="1" class="table"> -<colgroup> -<col width="21%" /> -<col width="58%" /> -<col width="21%" /> -</colgroup> -<tbody valign="top"> -<tr><td colspan="3">Setup and Teardown</td> -</tr> -<tr><td>open</td> -<td>/setUpFT</td> -<td> </td> -</tr> -<tr><td>storeValue</td> -<td>userid</td> -<td> </td> -</tr> -<tr><td>open</td> -<td>/login</td> -<td> </td> -</tr> -<tr><td>type</td> -<td>userID</td> -<td>${userid}</td> -</tr> -<tr><td>click</td> -<td>submit</td> -<td> </td> -</tr> -<tr><td>open</td> -<td>/tearDownFT?userid=${userid}</td> -<td> </td> -</tr> -</tbody> -</table> -</blockquote> -</blockquote> -</div> -<div class="section" id="continuous-integration"> -<h1><a name="continuous-integration">Continuous Integration</a></h1> -<blockquote> -<p>Selenium can be integrated with an automated build. When the parameter "auto=true" is added to the URL, Selenium will run the entire suite of tests, and then post the results to a handling URL. The default URL is "/postResults", but an alternative handler location can be provided by specifying a "resultsUrl" parameter.</p> -<p>The fields of the post are:</p> -<blockquote> -<table border="1" class="table"> -<colgroup> -<col width="15%" /> -<col width="85%" /> -</colgroup> -<thead valign="bottom"> -<tr><th>Parameter</th> -<th>Description</th> -</tr> -</thead> -<tbody valign="top"> -<tr><td>result</td> -<td>the word "passed" or "failed" depending on whether the whole suite passed or at least one test failed.</td> -</tr> -<tr><td>totalTime</td> -<td>the time in seconds for the whole suite to run</td> -</tr> -<tr><td>numTestPasses</td> -<td>tht total number of tests which passed</td> -</tr> -<tr><td>numTestFailures</td> -<td>the total number of tests which failed.</td> -</tr> -<tr><td>numCommandPasses</td> -<td>the total number of commands which passed.</td> -</tr> -<tr><td>numCommandFailures</td> -<td>the total number of commands which failed.</td> -</tr> -<tr><td>numCommandErrors</td> -<td>the total number of commands which errored.</td> -</tr> -<tr><td>suite</td> -<td>the suite table, including the hidden column of test results</td> -</tr> -<tr><td>testTable.1</td> -<td>the first test table</td> -</tr> -<tr><td>testTable.2</td> -<td>the second test table</td> -</tr> -<tr><td>...</td> -<td>...</td> -</tr> -<tr><td>testTable.N</td> -<td>The Nth test table</td> -</tr> -</tbody> -</table> -</blockquote> -<dl> -<dt>Therefore, the steps for continuous integration are:</dt> -<dd><ol class="first last arabic"> -<li><p class="first">Create a servlet-type application at the url /postResults which can read the parameters above and write them to a file</p> -</li> -<li><dl class="first"> -<dt>Create a script which can start up a brower and send to to the URL: selenium?auto=true</dt> -<dd><ul class="first last"> -<li><dl class="first"> -<dt>Generally, this can be done by merely calling the browser with the URL as an argument:</dt> -<dd><p class="first last">firefox.exe <a class="reference" href="http://localhost/selenium?auto=true">http://localhost/selenium?auto=true</a></p> -</dd> -</dl> -</li> -</ul> -</dd> -</dl> -</li> -<li><dl class="first"> -<dt>Make your continuous build:</dt> -<dd><ul class="first last simple"> -<li>Call the script from step 2, preferably using more than one browser</li> -<li>Wait for it to finish, possibly by checking for the existence of the file(s) from step 1</li> -<li>Parse these files to determine whether the build passed or failed</li> -<li>Act accordingly (send emails, update a build web page, etc.)</li> -</ul> -</dd> -</dl> -</li> -</ol> -</dd> -</dl> -</blockquote> -<hr /> -<table class="field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field"><th class="field-name">Authors:</th><td class="field-body">Paul Gross, Jason Huggins</td> -</tr> -<tr class="field"><th class="field-name">Created Date:</th><td class="field-body">08/23/2004</td> -</tr> -<tr class="field"><th class="field-name">Modified Date:</th><td class="field-body">28/01/2005</td> -</tr> -<tr class="field"><th class="field-name">Created With:</th><td class="field-body">reStructuredText: <a class="reference" href="http://docutils.sourceforge.net/rst.html">http://docutils.sourceforge.net/rst.html</a></td> -</tr> -</tbody> -</table> -</div> -</div> -</body> -</html> diff --git a/tests/FunctionalTests/selenium/doc/usage.html b/tests/FunctionalTests/selenium/doc/usage.html deleted file mode 100644 index 81ee7152..00000000 --- a/tests/FunctionalTests/selenium/doc/usage.html +++ /dev/null @@ -1,84 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - <meta name="generator" - content="Docutils 0.3.6: http://docutils.sourceforge.net/"> - <title>Usage</title> - <link rel="stylesheet" href="default.css" type="text/css"> -</head> -<body> -<div class="document" id="selenium-reference"> -<div class="section" id="test-tables"> -<h2><a name="test-tables">Overview</a> </h2> -This document details how Selenium can be deployed to test or script -web -applications.<br> -</div> -<div class="section" id="command-reference"> -<h2><a name="test-tables">Modes of Operation</a></h2> -Broadly speaking there are two modes of operation for Selenium -TestRunner and Driven<br> -<h3>TestRunner</h3> -<img alt="standalone pic" src="images/SmallStandalone.png" - style="width: 266px; height: 113px;"><br> -<br> -The TestRunner mode of operation for Selenium is where its HTML & -Javascript -and the test suite are deployed alongside the Application Under Test -(AUT) on a arbitrary web server. The test suite is coded as tables in a -HTML page for each test.<br> -<br> -See <a href="testrunner.html">test runner documentation</a> for more -information.<br> -<h3>Driven</h3> -<img alt="embedded pic" src="images/SmallEmbedded.png" - style="width: 248px; height: 113px;"> <br> -Driven Selenium is where the browser is under the the control of a -process on the same machine. That process is either a Java, .Net, Ruby -or Python -application and it is typically run in conjunction with a unit testing -framework like JUnit or NUnit. Also possible, is a console application -driving a browser interactively. <br> -<br> -The test script is one that would be recognisable to people adept with -unit test frameworks :<br> -<br> - public void testOKClick() {<br> - selenium.verifyTitle("First Page");<br> - selenium.open("/TestPage.html");<br> - selenium.click("OKButton");<br> - selenium.verifyTitle("Another Page");<br> - }<br> -<br> -The difference from normal unit testing is that as part of the startup, -three major things have to happen:<br> -<ol> - <li>The test framework needs to publish a fresh copy of the AUT. -Selenium prefers to mount its own web server temporarily for the -purposes of testing.</li> - <li>The test framework needs to publish the static Selenium's HTML -pages and Javascript in an apparent directory -on the same web server as (1).</li> - <li>The test framework needs to open a browser instance and point it -to Selenium.html served in (2) above.</li> -</ol> -As each of these is a fairly time consuming operation, it is best that -all three of those happen in a one-time setup mode. As such, and -even though these leverage a unit testing framework, this is definately -for acceptance or functional rather than unit-testing.<br> -<br> -Some variations in the accesibility of the the webserver in question -for testing purposes or its scriptablity mean a more complex setup is -required:<br> -<br> -<img alt="Adjacent pic" src="images/SmallAdjacent.png" - style="width: 239px; height: 229px;"><br> -<br> -See the <a href="driven.html">driven documentation</a> for more -information.<br> -<br> -</div> -</div> -</body> -</html> diff --git a/tests/FunctionalTests/selenium/html-xpath/carnation.jpg b/tests/FunctionalTests/selenium/html-xpath/carnation.jpg Binary files differdeleted file mode 100644 index 6b1e16b3..00000000 --- a/tests/FunctionalTests/selenium/html-xpath/carnation.jpg +++ /dev/null diff --git a/tests/FunctionalTests/selenium/html-xpath/example.html b/tests/FunctionalTests/selenium/html-xpath/example.html deleted file mode 100644 index 3d7c62af..00000000 --- a/tests/FunctionalTests/selenium/html-xpath/example.html +++ /dev/null @@ -1,75 +0,0 @@ -<html> -<head> - <title>DOM Level 3 XPath Example</title> - <script language="javascript" type="text/javascript" src="html-xpath.js"></script> -</head> -<body> -<div class="Test"> - Test Node <span class="Number">1</span> -</div> - -<span id="TestingGround"> - <div class="SomeOtherClass"> - <div class="Test" id="ContextNodeTest"> - Test Node <span class="Number">2</span> - <p>Test Node <span class="Number">3</span>: Unclosed Paragraph tag - <div class="Test"> - Test Node <span class="Number">4</span>: Nesting - </div> - <img src="rainbow.jpg" alt="Something I found in my backyard" width="100" height="200" alt="Test image" someOtherAttribute="someOtherValue" /> - <img src="carnation.jpg" alt="Carnation" width="99" height="74"> - </div> - </div> -</span> - -<script language="javascript"> - - function showIterator(title, result) - { - var s = title; - var item; - while(item = result.iterateNext()) - { - s += "<div class=\"XPathResultItem\">" + (item.nodeType == 1 ? item.innerHTML.replace(/</g, "<").replace(/>/g, ">") : item.nodeValue) + "</div>"; - } - document.write(s); - } - - // Evaluate 1 - var result = document.evaluate("//div[@class='Test']", document, null, XPathResult.STRING_TYPE, null); - document.write("<h1>\"//div[@class='Test']\" as String</h1>" + result.getStringValue()); - - // Evaluate 2 - document.evaluate("//img[@width < 100]/@height", document, null, XPathResult.NUMBER_TYPE, result); - document.write("<h1>\"//img[@width < 100]/@height\" as Number</h1>" + result.getNumberValue()); - - // Evaluate 3 - showIterator - ( - "<h1>\"//div[@class='Test']\" in context of document</h1>", - document.evaluate("//div[@class='Test']", document, null, XPathResult.ANY_TYPE, result) - ); - - // Evaluate 4 - showIterator - ( - "<h1>\"div[@class='Test']\" in context of a specific node (id=\"ContextNodeTest\")</h1>", - document.evaluate("div[@class='Test']", document.getElementById("ContextNodeTest"), null, XPathResult.ANY_TYPE, result) - ); - - // Evaluate 5 - showIterator - ( - "<h1>\"//div/@class\": Retrieve Attribute Values</h1>", - document.evaluate("//div/@class", document, null, 0, result) - ); - - // Evaluate 6 - var result = document.evaluate("count(//span[@class='Number'])", document, null, XPathResult.NUMBER_TYPE, null); - document.write("<h1>\"count(//span[@class='Number'])\" as Number</h1>" + result.getNumberValue()); - -</script> - - -</body> -</html> diff --git a/tests/FunctionalTests/selenium/html-xpath/html-xpath-patched.js b/tests/FunctionalTests/selenium/html-xpath/html-xpath-patched.js deleted file mode 100644 index d135e879..00000000 --- a/tests/FunctionalTests/selenium/html-xpath/html-xpath-patched.js +++ /dev/null @@ -1,657 +0,0 @@ -/* - 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) && (navigator.platform == "Win32"); - -// Mozilla has support by default, we don't have an implementation for the rest -if (isIe) -{ - // release number - document.DomL3XPathRelease = "0.0.3.0"; - - // 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, "This functionality is not implemented.", message); - } - - this.ThrowInvalidExpression = function(message) - { - ThrowError(this.INVALID_EXPRESSION_ERR, "Invalid expression", message); - } - - this.ThrowType = function(message) - { - ThrowError(this.TYPE_ERR, "Type error", message); - } - - this.Throw = function(message) - { - ThrowError(this.RUNTIME_ERR, "Run-time error", message); - } - - function ThrowError(code, description, message) - { - var error = new Error(code, "DOM-L3-XPath " + document.DomL3XPathRelease + ": " + description + (message ? ", \"" + message + "\"": "")); - error.code = code; - error.name = "XPathException"; - 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, "The state of the object is no longer valid", message); - } - - function ThrowError(code, description, message) - { - var error = new Error(code, "DOM : " + description + (message ? ", \"" + message + "\"": "")); - error.code = code; - error.name = "DOMException"; - 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 && result.constructor == XPathResult ? result.initialize(this, contextNode, resolver, type) : new XPathResult(this, contextNode, resolver, type)); - } - - this.toString = function() - { - return "[XPathExpression]"; - } - } - - // 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 "[XPathNSResolver]"; - } - } - - // 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 > 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("Snapshot is not an expected result type"); - } - activateResult(this); - // return Number - return this._domResult.length; - } - - // Node iterateNext() - this.iterateNext = function() - // raises XPathException, DOMException - { - if (!this._isIterator) - { - XPathException.ThrowType("Iterator is not an expected result type"); - } - activateResult(this); - if (documentChangeDetected()) - { - DOMException.ThrowInvalidState("iterateNext"); - } - // return Node - return getNextNode(this); - } - - // Node snapshotItem(Number index) - this.snapshotItem = function(index) - // raises XPathException - { - if (!this._isSnapshot) - { - XPathException.ThrowType("Snapshot is not an expected result type"); - } - // return Node - return getItemNode(this, index); - } - - this.toString = function() - { - return "[XPathResult]"; - } - - // 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("The expression can not be converted to return String"); - } - 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("The expression can not be converted to return Number"); - } - var number = parseInt(getNodeText(this)); - if (isNaN(number)) - { - XPathException.ThrowType("The result can not be converted to Number"); - } - 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("The expression can not be converted to return Boolean"); - } - - var - text = getNodeText(this); - bool = (text ? text.toLowerCase() : null); - if (bool == "false" || bool == "true") - { - return bool; - } - XPathException.ThrowType("The result can not be converted to Boolean"); - } - - // 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 && - this.resultType != XPathResult.FIRST_ORDERED_NODE_TYPE) - { - XPathException.ThrowType("The expression can not be converted to return single Node value"); - } - 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("id"); - if (id) - { - return document.getElementById(id.value); - } - XPathException.Throw("unable to locate element in XML tree"); - case 2: // NODE_ATTRIBUTE - var id = current.selectSingleNode("..").attributes.getNamedItem("id"); - if (id) - { - var node = document.getElementById(id.text); - if (node) - { - return node.attributes.getNamedItem(current.nodeName); - } - } - XPathException.Throw("unable to locate attribute in XML tree"); - case 3: // NODE_TEXT - var id = current.selectSingleNode("..").attributes.getNamedItem("id"); - if (id) - { - var node = document.getElementById(id.value); - if (node) - { - for(child in node.childNodes) - { - if (child.nodeType == 3 && child.nodeValue == current.nodeValue) - { - return child; - } - } - } - } - XPathException.Throw("unable to locate text in XML tree"); - } - XPathException.Throw("unknown node type"); - } - - function activateResult(result) - { - if (!result._domResult) - { - try - { - var expression = result._expression.expressionString; - - // adjust expression if contextNode is not a document - if (result._contextNode != document && expression.indexOf("//") != 0) - { - - expression = "//*[@id = '" + result._contextNode.id + "']" + - (expression.indexOf("/") == 0 ? "" : "/") + 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(/</g, "<").replace(/>/g, ">").replace(/"/g, "\""); - var xslText = "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">" + - "<xsl:output method=\"text\"/><xsl:template match=\"*\"><xsl:value-of select=\"" + expression + "\"/>" + - "</xsl:template></xsl:stylesheet>"; - var xsl = new ActiveXObject("Msxml2.DOMDocument"); - xsl.loadXML(xslText); - try - { - var result = this.getDom().transformNode(xsl); - } - catch(error) - { - alert("Error: " + 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("Msxml2.DOMDocument"); -/** SELENIUM:PATCH TO ALLOW PROVIDE FULL XPATH SUPPORT */ - dom.setProperty("SelectionLanguage", "XPath"); -/** 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(""); - 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("/") > -1 - || node.nodeName == "" - || node.nodeName == "#document" - || node.nodeName == "#document-fragment" - || node.nodeName == "#cdata-section" - || node.nodeName == "#xml-declaration" - || node.nodeName == "#whitespace" - || node.nodeName == "#significat-whitespace" - ) - { - return; - } - - // #comment is a <!-- comment -->, which must be created with createComment() - if (node.nodeName == "#comment") - { - 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 < length; i ++ ) - { - loadNode(dom, domNode, node.childNodes[i], helper); - } - node.attachEvent("onpropertychange", onPropertyChangeEventHandler); - } - } -/** END SELENIUM:PATCH */ - - function loadAttributes(dom, domParentNode, node) - { - for (var i = 0; i < node.attributes.length; i ++ ) - { - var attribute = node.attributes[i]; - var attributeValue = attribute.nodeValue; - if (attributeValue && (attribute.specified || attribute.nodeName == 'value')) - { - 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 --git a/tests/FunctionalTests/selenium/html-xpath/html-xpath.js b/tests/FunctionalTests/selenium/html-xpath/html-xpath.js deleted file mode 100644 index acad8b93..00000000 --- a/tests/FunctionalTests/selenium/html-xpath/html-xpath.js +++ /dev/null @@ -1,610 +0,0 @@ -/* - 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 - -*/ - -var isIe = /MSIE [56789]/.test(navigator.userAgent) && (navigator.platform == "Win32"); - -// Mozilla has support by default, we don't have an implementation for the rest -if (isIe) -{ - // release number - document.DomL3XPathRelease = "0.0.3.0"; - - // 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, "This functionality is not implemented.", message); - } - - this.ThrowInvalidExpression = function(message) - { - ThrowError(this.INVALID_EXPRESSION_ERR, "Invalid expression", message); - } - - this.ThrowType = function(message) - { - ThrowError(this.TYPE_ERR, "Type error", message); - } - - this.Throw = function(message) - { - ThrowError(this.RUNTIME_ERR, "Run-time error", message); - } - - function ThrowError(code, description, message) - { - var error = new Error(code, "DOM-L3-XPath " + document.DomL3XPathRelease + ": " + description + (message ? ", \"" + message + "\"": "")); - error.code = code; - error.name = "XPathException"; - 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, "The state of the object is no longer valid", message); - } - - function ThrowError(code, description, message) - { - var error = new Error(code, "DOM : " + description + (message ? ", \"" + message + "\"": "")); - error.code = code; - error.name = "DOMException"; - 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 && result.constructor == XPathResult ? result.initialize(this, contextNode, resolver, type) : new XPathResult(this, contextNode, resolver, type)); - } - - this.toString = function() - { - return "[XPathExpression]"; - } - } - - // 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 "[XPathNSResolver]"; - } - } - - // 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 > 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("Snapshot is not an expected result type"); - } - activateResult(this); - // return Number - return this._domResult.length; - } - - // Node iterateNext() - this.iterateNext = function() - // raises XPathException, DOMException - { - if (!this._isIterator) - { - XPathException.ThrowType("Iterator is not an expected result type"); - } - activateResult(this); - if (documentChangeDetected()) - { - DOMException.ThrowInvalidState("iterateNext"); - } - // return Node - return getNextNode(this); - } - - // Node snapshotItem(Number index) - this.snapshotItem = function(index) - // raises XPathException - { - if (!this._isSnapshot) - { - XPathException.ThrowType("Snapshot is not an expected result type"); - } - // return Node - return getItemNode(this, index); - } - - this.toString = function() - { - return "[XPathResult]"; - } - - // 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("The expression can not be converted to return String"); - } - 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("The expression can not be converted to return Number"); - } - var number = parseInt(getNodeText(this)); - if (isNaN(number)) - { - XPathException.ThrowType("The result can not be converted to Number"); - } - 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("The expression can not be converted to return Boolean"); - } - - var - text = getNodeText(this); - bool = (text ? text.toLowerCase() : null); - if (bool == "false" || bool == "true") - { - return bool; - } - XPathException.ThrowType("The result can not be converted to Boolean"); - } - - // 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 && - this.resultType != XPathResult.FIRST_ORDERED_NODE_TYPE) - { - XPathException.ThrowType("The expression can not be converted to return single Node value"); - } - 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("id"); - if (id) - { - return document.getElementById(id.value); - } - XPathException.Throw("unable to locate element in XML tree"); - case 2: // NODE_ATTRIBUTE - var id = current.selectSingleNode("..").attributes.getNamedItem("id"); - if (id) - { - var node = document.getElementById(id.text); - if (node) - { - return node.attributes.getNamedItem(current.nodeName); - } - } - XPathException.Throw("unable to locate attribute in XML tree"); - case 3: // NODE_TEXT - var id = current.selectSingleNode("..").attributes.getNamedItem("id"); - if (id) - { - var node = document.getElementById(id.value); - if (node) - { - for(child in node.childNodes) - { - if (child.nodeType == 3 && child.nodeValue == current.nodeValue) - { - return child; - } - } - } - } - XPathException.Throw("unable to locate text in XML tree"); - } - XPathException.Throw("unknown node type"); - } - - function activateResult(result) - { - if (!result._domResult) - { - try - { - var expression = result._expression.expressionString; - - // adjust expression if contextNode is not a document - if (result._contextNode != document && expression.indexOf("//") != 0) - { - - expression = "//*[@id = '" + result._contextNode.id + "']" + - (expression.indexOf("/") == 0 ? "" : "/") + 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(/</g, "<").replace(/>/g, ">").replace(/"/g, "\""); - var xslText = "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">" + - "<xsl:output method=\"text\"/><xsl:template match=\"*\"><xsl:value-of select=\"" + expression + "\"/>" + - "</xsl:template></xsl:stylesheet>"; - var xsl = new ActiveXObject("Msxml2.DOMDocument"); - xsl.loadXML(xslText); - try - { - var result = this.getDom().transformNode(xsl); - } - catch(error) - { - alert("Error: " + 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("Msxml2.DOMDocument"); - 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(""); - loadDocument(dom, helper); - helper._currentElementCount = document.all.length; - document._propertyChangeDetected = false; - } - } - } - - function loadDocument(dom, helper) - { - return loadNode(dom, dom, document.body, helper); - } - - function loadNode(dom, domParentNode, node, helper) - { - 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 < length; i ++ ) - { - loadNode(dom, domNode, node.childNodes[i], helper); - } - node.attachEvent("onpropertychange", onPropertyChangeEventHandler); - } - } - - function loadAttributes(dom, domParentNode, node) - { - for (var i = 0; i < node.attributes.length; i ++ ) - { - var attribute = node.attributes[i]; - var attributeValue = attribute.nodeValue; - if (attributeValue && attribute.specified) - { - 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; - } -}
\ No newline at end of file diff --git a/tests/FunctionalTests/selenium/html-xpath/license.txt b/tests/FunctionalTests/selenium/html-xpath/license.txt deleted file mode 100644 index b1e3f5a2..00000000 --- a/tests/FunctionalTests/selenium/html-xpath/license.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - <one line to give the library's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - <signature of Ty Coon>, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/tests/FunctionalTests/selenium/html-xpath/rainbow.jpg b/tests/FunctionalTests/selenium/html-xpath/rainbow.jpg Binary files differdeleted file mode 100644 index 757995b6..00000000 --- a/tests/FunctionalTests/selenium/html-xpath/rainbow.jpg +++ /dev/null diff --git a/tests/FunctionalTests/selenium/index.html b/tests/FunctionalTests/selenium/index.html deleted file mode 100644 index eaaaa308..00000000 --- a/tests/FunctionalTests/selenium/index.html +++ /dev/null @@ -1,60 +0,0 @@ -<html> -<head> -<title>Selenium starting points</title> -<script language="Javascript"> -function fixUnitTestLink() { - baseurl = document.location + ""; - baseurl = baseurl.replace(/[^\/]*$/, ''); - jsunitLink = document.getElementById("unitTestLink"); - jsunitLink['href'] = jsunitLink['href'].replace('BASEURL', baseurl); -} -</script> -<style> -li { margin-top: 6pt; } -</style> -</head> -<body onload="fixUnitTestLink();"> - -<h1>Selenium</h1> - -<p> -<b>Acceptance tests:</b> These test-suites demonstrate/exercise the -functionality of Selenium. -</p> - -<ul> - - <li> - <a href="TestRunner.html">Mozilla/Firefox TestSuite</a> - tests that currently pass on Firefox - </li> - <li> - <a href="TestRunner.html?test=tests/TestSuite-InternetExplorer.html">Internet Explorer TestSuite</a> - tests that currently pass on IE - </li> - <li> - <a href="TestRunner.html?test=tests/TestSuite-Konqueror.html">Konqueror TestSuite</a> - tests that currently pass on Konqueror - </li> - <li> - <a href="TestRunner.html?test=tests/TestSuite-Safari.html">Safari TestSuite</a> - tests that currently pass on Safari - </li> - <li> - <a href="TestRunner.html?test=devtests/TestSuite.html"><i>devtests</i></a> - - experimental features - </li> - <li> - <a href="TestRunner.html?test=tests/ErrorCheckingTestSuite.html">Error Checking TestSuite</a> - tests for the error verification commands - </li> -</ul> - -<p><b>Unit-tests:</b> Use JsUnit to test Selenium internals.</p> - -<ul> - - <li> - <a id="unitTestLink" href="jsunit/testRunner.html?testPage=BASEURLtests/JsUnitSuite.html">BrowserBot unit-tests</a> - <br /> - </li> - -</ul> - -</body> -</html> diff --git a/tests/FunctionalTests/selenium/install-readme.txt b/tests/FunctionalTests/selenium/install-readme.txt deleted file mode 100644 index 0946f4c4..00000000 --- a/tests/FunctionalTests/selenium/install-readme.txt +++ /dev/null @@ -1,9 +0,0 @@ -Copy the "selenium" folder to a web accessible directory in the same web server as the application you want to test. -In Apache, this would mean a subdirectory of "htdocs". - -Because of javascript security settings standard in most browsers, Selenium needs to be available on the same host and port as your application. - -Once deployed to the server, to run Selenium's self-tests, check out: -http://<webservername>:<port>/selenium/TestRunner.html - -Read the website for more details. (http://selenium.thoughtworks.com)
\ No newline at end of file diff --git a/tests/FunctionalTests/selenium/jsmock/mock-tests.html b/tests/FunctionalTests/selenium/jsmock/mock-tests.html deleted file mode 100644 index f0cc6758..00000000 --- a/tests/FunctionalTests/selenium/jsmock/mock-tests.html +++ /dev/null @@ -1,205 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" -"http://www.w3.org/TR/html4/loose.dtd"> - -<!-- -Copyright 2004 ThoughtWorks, Inc - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> - <title>JsMock Tests</title> - <link rel="stylesheet" type="text/css" href="/jsunit/css/jsUnitStyle.css"> -<script language="JavaScript" type="text/javascript" src="/jsunit/app/jsUnitCore.js"></script> -<script language="JavaScript" type="text/javascript" src="/jsmock/mock.js"></script> -<script language="JavaScript" type="text/javascript"> - -function testCallingExpectedFunctionShouldPass() { - var myMock = new Mock() - myMock.expects("aslak") - myMock.aslak() - myMock.verify() -} - -function testAccessingExpectedPropertyShouldPass() { - var myMock = new Mock() - myMock.expectsProperty("hello").returns("world") - assertEquals("world", myMock.hello) -} - -function testAccessingExpectedPropertyWithObjectShouldPass() { - var myMock = new Mock() - ob = [1,2] - myMock.expectsProperty("hello").returns(ob) - assertEquals(ob, myMock.hello) -} - -function testCallingUnexpectedFunctionShouldFail() { - var myMock = new Mock() - try { - myMock.someMethod() - } catch(expected) { - return - } - fail("Should fail because someMethod wasn't expected!") -} - -function testNotCallingExpectedFunctionShouldFail() { - var myMock = new Mock() - myMock.expects("someMethod") - try { - myMock.verify() - } catch(expected) { - return - } - fail("Should fail because someMethod wasn't called!") -} - -function testCallingExpectedFunctionWithBadArgumentsShouldFail() { - var myMock = new Mock() - myMock.expects("someMethod", "foo") - try { - myMock.someMethod("bar") - } catch(expected) { - return - } - fail("Should fail because bar wasn't the expected arg!") -} - -function testCallingExpectedFunctionWithExpectedArgumentsShouldPass() { - var myMock = new Mock() - myMock.expects("someMethod", "foo") - myMock.expects("anotherMethod", "bar", "zap") - assertUndefined(myMock.someMethod("foo")) - assertUndefined(myMock.anotherMethod("bar", "zap")) -} - -function testCallingExpectedFunctionWithTooFewArgumentsShouldFail() { - var myMock = new Mock() - myMock.expects("someMethod", "foo", "bar") - try { - myMock.someMethod("foo") - } catch(expected) { - return - } - fail("Should fail because too few arguments were passed!") -} - -function testCallingExpectedFunctionWithTooManyArgumentsShouldFail() { - var myMock = new Mock() - myMock.expects("someMethod", "foo") - try { - myMock.someMethod("foo", "bar") - } catch(expected) { - return - } - fail("Should fail because too many arguments were passed!") -} - -function testShouldCreateMockInstancesWithoutSideffects() { - var foo = new Mock() - var bar = new Mock() - - foo.expects("foo") - bar.expects("bar") - - try { - bar.foo() - } catch(expected) { - return - } - fail("Should fail because an unexpected bar was called!") -} - -function testCallingExpectedFunctionWithReturnShouldReturnValue() { - var myMock = new Mock() - myMock.expects("someMethod", "bar").returns("foo") - myMock.expects("theOtherMethod", "zap", "ping", "pong").returns("bang") - assertEquals("foo", myMock.someMethod("bar")) - assertEquals("bang", myMock.theOtherMethod("zap", "ping", "pong")) - myMock.verify() -} - -function testCallingExpectedFunctionWithThrowsShouldThrowError() { - var myMock = new Mock(); - myMock.expects("someMethod", "bar").andThrows("failure") - try { - myMock.someMethod("bar") - } catch (e) { - assertEquals("failure", e.message); - return - } - fail("Mock did not throw exception when required"); -} - -function testSettingExpectedPropertyShouldPass() { - var myMock = new Mock() - myMock.expectsProperty("foo", "bar") - myMock.foo = "bar" - myMock.verify() -} - -function TODO_testSettingUnexpectedPropertyShouldFail() { - var myMock = new Mock() - myMock.foo = "bar" - - try { - myMock.verify() - } catch(expected) { - return - } - fail("Should fail because an unexpected property was set!") -} - -function TODO_testShouldAllowExpectationOfSameFunctionWithDifferentArguments() { - var myMock = new Mock() - myMock.expects("aslak", "hello").returns("world") - myMock.expects("aslak", "bonjour").returns("monde") - assertEquals("world", myMock.aslak("hello")) - assertEquals("monde", myMock.aslak("bonjour")) - myMock.verify() -} - -function TODO_testNotSettingExpectedPropertyShouldFail() { - var myMock = new Mock() - myMock.expectsProperty("foo", "bar") - try { - myMock.verify() - } catch(expected) { - return - } - fail("Should fail because an expected property was not set!") -} - -function TODO_testSettingExpectedPropertyWithUnexpectedValueShouldFail() { - var myMock = new Mock() - myMock.expectsProperty("foo", "bar") - myMock.foo="zap" - try { - myMock.verify() - } catch(expected) { - return - } - fail("Should fail because an expected property was set with unexpected value!") -} - -</script> - </head> - - <body> - <h1>JsMock Tests</h1> - - <p>This page contains tests for JsMock. To see them, take a look at the source. To run them, load this file via JsUnit's testRunner.html</p> - </body> -</html> diff --git a/tests/FunctionalTests/selenium/jsmock/mock.js b/tests/FunctionalTests/selenium/jsmock/mock.js deleted file mode 100644 index 8c0b4b9f..00000000 --- a/tests/FunctionalTests/selenium/jsmock/mock.js +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2004 ThoughtWorks, Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// A simple mock library for Javascript -// -// Original code by Aslak Hellesoy and Ji Wang - -Mock = function() { - this.expectedInvocations = {}; - this.expectedArgs = {}; - this.returnValues = {}; - this.attrs = []; - this.expectedProperties = {}; - - this.expects = function() { - functionName = arguments[0]; - this.expectedArgs[functionName] = []; - for(i = 1; i < arguments.length; i++) { - this.expectedArgs[functionName][i-1] = arguments[i]; - } - javascriptCode = "this." + functionName + " = function() {\n" + - " // mark this function as \"executed\"\n" + - " this.expectedInvocations[\"" + functionName + "\"] = true;\n" + - " assertEquals(\"" + functionName + ": Wrong number of arguments.\", " + this.expectedArgs[functionName].length + ", arguments.length);\n" + - " for(i = 0; i < arguments.length; i++) {\n" + - " assertEquals(this.expectedArgs[\"" + functionName + "\"][i], arguments[i]);\n" + - " };\n" + - " var returnValue = this.returnValues[\"" + functionName + "\"];\n" + - " if (returnValue && returnValue.isMockError) { throw returnValue };\n" + - " return returnValue;\n" + - "}"; - eval(javascriptCode); - // initially mark this function as "not yet executed" - this.expectedInvocations[functionName] = false; - this.attrs[this.attrs.length] = "dummy"; - return new Returner(this, functionName); - }; - - this.expectsProperty = function() { - var propertyName = arguments[0]; - if(arguments.length == 2) { - expectedPropertyValue = arguments[1]; - this.expectedProperties[propertyName] = expectedPropertyValue; - this.attrs[this.attrs.length] = "dummy"; - } else { - return new PropertySetter(this, propertyName); - } - }; - - this.verify = function() { - // loop over all expected invocations and see if they were called - for(var functionName in this.expectedInvocations) { - var wasCalled = this.expectedInvocations[functionName]; - if(!wasCalled) { - fail("Expected function not called:" + functionName); - } - } - var currentAttrs = []; - var currentAttrCount = 0; - - // verify that all expected properties are set -// for(var attr in this) { -// currentAttrs[currentAttrCount] = attr; -// currentAttrCount++; -// } -// if(this.attrs.length < currentAttrCount) { -// unexpectedAttr = currentAttrs[this.attrs.length] -// fail("Unexpected property was set: " + unexpectedAttr + "=" + eval("this." + unexpectedAttr)) -// } - - // verify that all expected properties are set with the right value -// for(var attr in this.expectedProperties) { -// if(this.expectedProperties[attr] != eval("this." + attr)) { -// fail("Expected property was not set: " + attr + "=" + this.expectedProperties[attr]) -// } -// } - }; - - var attrCount = 0; - for(var attr in this) { - this.attrs[attrCount] = attr; - attrCount++; - } -}; - -Returner = function(mock, functionName) { - this.mock = mock; - this.functionName = functionName; -}; - -Returner.prototype.returns = function(returnValue) { - this.mock.returnValues[this.functionName] = returnValue; -}; - -Returner.prototype.andThrows = function(message) { - var error = new Error(message); - error.isMockError = true; - this.mock.returnValues[this.functionName] = error; -}; - -PropertySetter = function(mock, propertyName) { - this.mock = mock; - this.propertyName = propertyName; -}; - -PropertySetter.prototype.returns = function(returnValue) { - var ref = new Object(); - ref.value = returnValue; - eval("this.mock." + this.propertyName + "=ref.value"); -}; diff --git a/tests/FunctionalTests/selenium/php/TestRunner.php b/tests/FunctionalTests/selenium/php/TestRunner.php index fb3cefb3..9bb5ada3 100644 --- a/tests/FunctionalTests/selenium/php/TestRunner.php +++ b/tests/FunctionalTests/selenium/php/TestRunner.php @@ -1,7 +1,8 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> + <html> + <head> -<HTA:APPLICATION ID="SeleniumTestRunner" APPLICATIONNAME="Selenium" > +<HTA:APPLICATION ID="SeleniumHTARunner" APPLICATIONNAME="Selenium" > <!-- the previous line is only relevant if you rename this file to "TestRunner.hta" --> @@ -24,40 +25,47 @@ Copyright 2004 ThoughtWorks, Inc --> <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type" /> -<title>Prado Functional Test Runner</title> -<link rel="stylesheet" type="text/css" href="<?php echo $base_dir; ?>selenium.css" /> -<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>html-xpath/html-xpath-patched.js"></script> -<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>selenium-browserbot.js"></script> -<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>selenium-api.js"></script> -<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>selenium-commandhandlers.js"></script> -<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>selenium-executionloop.js"></script> -<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>selenium-testrunner.js"></script> -<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>selenium-logging.js"></script> -<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>htmlutils.js"></script> -<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>xpath.js"></script> +<title>Selenium Functional Test Runner</title> +<link rel="stylesheet" type="text/css" href="<?php echo $base_dir; ?>core/selenium.css" /> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-browserdetect.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-browserbot.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/prototype-1.4.0.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/find_matching_child.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-api.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-commandhandlers.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-executionloop.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-testrunner.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-logging.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/selenium-version.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/scripts/htmlutils.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/xpath/misc.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/xpath/dom.js"></script> +<script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>core/xpath/xpath.js"></script> <script language="JavaScript" type="text/javascript" src="<?php echo $base_dir; ?>prado-functional-test.js"></script> <script language="JavaScript" type="text/javascript"> function openDomViewer() { var autFrame = document.getElementById('myiframe'); var autFrameDocument = getIframeDocument(autFrame); this.rootDocument = autFrameDocument; - var domViewer = window.open('<?php echo $base_dir; ?>domviewer.html'); + var domViewer = window.open('<?php echo $base_dir; ?>core/domviewer/domviewer.html'); return false; } - - Logger.prototype.openLogWindow = function() { + + Logger.prototype.openLogWindow = function() + { this.logWindow = window.open( - "<?php echo $base_dir; ?>SeleniumLog.html", "SeleniumLog", + "<?php echo $base_dir; ?>core/SeleniumLog.html", "SeleniumLog", "width=600,height=250,bottom=0,right=0,status,scrollbars,resizable" ); return this.logWindow; - }; + } - var post_results_to = "<?php echo $driver; ?>"; + var post_results_to = "<?php echo $driver; ?>"; + </script> </head> -<body onload="start();"> +<body onLoad="start();"> <table class="layout"> <form action="" name="controlPanel"> @@ -65,92 +73,98 @@ Copyright 2004 ThoughtWorks, Inc <!-- Suite, Test, Control Panel --> <tr class="selenium"> - <td width="25%" height="30%" rowspan="2"><iframe name="testSuiteFrame" id="testSuiteFrame" src="<?php echo $driver; ?>?testSuites"></iframe></td> - <td width="50%" height="30%" rowspan="2"><iframe name="testFrame" id="testFrame"></iframe></td> - <th width="25%" height="1" class="header"> - <h1><a href="http://selenium.thoughtworks.com" title="The Selenium Project">Selenium</a> TestRunner</h1> - </th> - </tr> - - <tr class="selenium"> - <td width="25%" height="30%" id="controlPanel"> - - <fieldset> - <legend>Execute Tests</legend> - - <div> - <input id="modeRun" type="radio" name="runMode" value="0" checked="checked"/><label for="modeRun">Run</label> - <input id="modeWalk" type="radio" name="runMode" value="500" /><label for="modeWalk">Walk</label> - <input id="modeStep" type="radio" name="runMode" value="-1" /><label for="modeStep">Step</label> - </div> - - <div> - <button type="button" id="runSuite" onclick="startTestSuite();" - title="Run the entire Test-Suite"> - <strong>All</strong> - </button> - <button type="button" id="runTest" onclick="runSingleTest();" - title="Run the current Test"> - <em>Selected</em> - </button> - <button type="button" id="continueTest" disabled="disabled" - title="Continue the Test"> - Continue - </button> - </div> - - </fieldset> - - <table id="stats" align="center"> - <tr> - <td colspan="2" align="right">Elapsed:</td> - <td id="elapsedTime" colspan="2">00.00</td> - </tr> - <tr> - <th colspan="2">Tests</th> - <th colspan="2">Commands</th> - </tr> - <tr> - <td class="count" id="testRuns">0</td> - <td>run</td> - <td class="count" id="commandPasses">0</td> - <td>passed</td> - </tr> - <tr> - <td class="count" id="testFailures">0</td> - <td>failed</td> - <td class="count" id="commandFailures">0</td> - <td>failed</td> - </tr> - <tr> - <td colspan="2"></td> - <td class="count" id="commandErrors">0</td> - <td>incomplete</td> - </tr> - </table> - - <fieldset> - <legend>Tools</legend> - - <button type="button" id="domViewer1" onclick="openDomViewer();"> - View DOM - </button> - <button type="button" onclick="LOG.show();"> - Show Log - </button> - - </fieldset> - - </td> - </tr> + <td width="25%" height="30%" ><iframe name="testSuiteFrame" id="testSuiteFrame" src="<?php echo $driver; ?>?testSuites" application="yes"></iframe></td> + <td width="50%" height="30%" ><iframe name="testFrame" id="testFrame" application="yes"></iframe></td> + + <td width="25%"> + <table class="layout"> + <tr class="selenium"> + <th width="25%" height="1" class="header"> + <h1><a href="http://selenium.thoughtworks.com" title="The Selenium Project">Selenium</a> TestRunner</h1> + </th> + </tr> + <tr> + <td width="25%" height="30%" id="controlPanel"> + + <fieldset> + <legend>Execute Tests</legend> + + <div> + <input id="modeRun" type="radio" name="runMode" value="0" checked="checked"/><label for="modeRun">Run</label> + <input id="modeWalk" type="radio" name="runMode" value="500" /><label for="modeWalk">Walk</label> + <input id="modeStep" type="radio" name="runMode" value="-1" /><label for="modeStep">Step</label> + </div> + + <div> + <button type="button" id="runSuite" onClick="startTestSuite();" + title="Run the entire Test-Suite"> + <strong>All</strong> + </button> + <button type="button" id="runTest" onClick="runSingleTest();" + title="Run the current Test"> + <em>Selected</em> + </button> + <button type="button" id="continueTest" disabled="disabled" + title="Continue the Test"> + Continue + </button> + </div> + + </fieldset> + + <table id="stats" align="center"> + <tr> + <td colspan="2" align="right">Elapsed:</td> + <td id="elapsedTime" colspan="2">00.00</td> + </tr> + <tr> + <th colspan="2">Tests</th> + <th colspan="2">Commands</th> + </tr> + <tr> + <td class="count" id="testRuns">0</td> + <td>run</td> + <td class="count" id="commandPasses">0</td> + <td>passed</td> + </tr> + <tr> + <td class="count" id="testFailures">0</td> + <td>failed</td> + <td class="count" id="commandFailures">0</td> + <td>failed</td> + </tr> + <tr> + <td colspan="2"></td> + <td class="count" id="commandErrors">0</td> + <td>incomplete</td> + </tr> + </table> + + <fieldset> + <legend>Tools</legend> + + <button type="button" id="domViewer1" onClick="openDomViewer();"> + View DOM + </button> + <button type="button" onClick="LOG.show();"> + Show Log + </button> + + </fieldset> + + </td> + </tr> + </table> + </td> + </tr> <!-- AUT --> <tr> - <td colspan="3" height="70%"><iframe name="myiframe" id="myiframe" src="<?php echo $base_dir; ?>TestRunner-splash.html"></iframe></td> + <td colspan="3" height="70%"><iframe name="myiframe" id="myiframe" src="<?php echo $base_dir; ?>core/TestRunner-splash.html"></iframe></td> </tr> </form> </table> </body> -</html> +</html>
\ No newline at end of file diff --git a/tests/FunctionalTests/selenium/php/selenium.php b/tests/FunctionalTests/selenium/php/selenium.php index 4a4c6e19..960791b8 100644 --- a/tests/FunctionalTests/selenium/php/selenium.php +++ b/tests/FunctionalTests/selenium/php/selenium.php @@ -112,15 +112,17 @@ class SeleneseInterpreter public function __call($func, $args) { if($func{0} == '_') return; + $ID = isset($args[0]) ? $args[0] : ""; - //if($ID instanceof TControl) - // $ID = $ID->ClientID; $value = isset($args[1]) ? $args[1] : ""; if(strpos(strtolower($func),'htmlpresent') || strpos(strtolower($func),'htmlnotpresent')) $ID = htmlspecialchars($ID); - //$command = "|{$func}|{$ID}|{$value}|"; $command = array($func, $ID, $value); $trace = debug_backtrace(); + + if(is_int(strpos(strtolower($func), 'visible'))) + $this->addCommand(array('pause','500',''),$trace); + return $this->addCommand($command, $trace); } diff --git a/tests/FunctionalTests/selenium/prado-functional-test.js b/tests/FunctionalTests/selenium/prado-functional-test.js index be2ea795..f9889a72 100644 --- a/tests/FunctionalTests/selenium/prado-functional-test.js +++ b/tests/FunctionalTests/selenium/prado-functional-test.js @@ -1,4 +1,45 @@ +/** + * Override selenium implementation. + */ +Selenium.prototype.getAttribute = function(target) { + return this.page().findAttribute(target); +}; + + +/** + * Override selenium implementation. + */ +Selenium.prototype.isVisible = function(locator) { + var element; + element = this.page().findElement(locator); + + if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + var visibility = element.style["visibility"]; + else + var visibility = this.findEffectiveStyleProperty(element, "visibility"); + + var _isDisplayed = this._isDisplayed(element); + return (visibility != "hidden" && _isDisplayed); +}; + + +/** + * Override selenium implementation. + */ +Selenium.prototype._isDisplayed = function(element) { + if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + var display = element.style["display"]; + else + var display = this.findEffectiveStyleProperty(element, "display"); + if (display == "none") return false; + if (element.parentNode.style) { + return this._isDisplayed(element.parentNode); + } + return true; +}; + + function runNextTest() { if (!runAllTests) return; diff --git a/tests/FunctionalTests/selenium/readme-selenium-fitrunner.txt b/tests/FunctionalTests/selenium/readme-selenium-fitrunner.txt deleted file mode 100644 index 7e13711e..00000000 --- a/tests/FunctionalTests/selenium/readme-selenium-fitrunner.txt +++ /dev/null @@ -1,46 +0,0 @@ -Selenium Functional Testing Tool -(c) ThoughtWorks, Inc., 2004 -- jrhuggins@thoughtworks.com - -To run a test suite with just a web browser (no server): - - Open <selenium_home>/javascript/TestRunner.html in your browser. - Example: file:///C:/selenium/javascript/TestRunner.html - - -To run a test suite with simple web server: - - 1) Launch <selenium_home>/javascript/startWebServer.bat - (An installation of Python is required. This works with 2.3.4, - but hasn't been tested with older versions, yet.) - - 2) Open up your browser, then goto the URL: - http://localhost:8000/TestRunner.html - (If you don't want the web server to run on port 8000, modify - <selenium_home>\javascript\tinyWebServer.py) - -To run a different test suite from the default, use the URL syntax: - ./TestRunner.html?test={my-test-suite} - -Supported Browsers: - Microsoft Internet Explorer 6.0+ - Mozilla 1.6+ - Mozilla Firefox 0.9.3+ - - Other browsers, I'd love to add support for, but havn't yet: - Opera, Konqueror, and Safari - -Gotchas: -1) The tinyWebServer included is not meant for production use. It is only -provided to show a simple, working example of Selenium running from a web server. -(TODO- provide instructions for installing in Apache or IIS) - -2) The tinyWebServer is only available from localhost (unless you hack the source). -This was done as a security measure. - -3) The tests may not complete automatically the first time, because the browser may pop-up alert boxes: - a) Asking if you want to remember form values (click "No" or "Never for this site") - b) Warning about security implications about posting data unencrypted. - -4) You'll need to whitelist "localhost" in your pop-up blocker, if it is enabled. - diff --git a/tests/FunctionalTests/selenium/readme-selenium-rpcrunner.txt b/tests/FunctionalTests/selenium/readme-selenium-rpcrunner.txt deleted file mode 100644 index cc4e9205..00000000 --- a/tests/FunctionalTests/selenium/readme-selenium-rpcrunner.txt +++ /dev/null @@ -1,17 +0,0 @@ -Selenium RPC runner currently consists of and XML-RPC binding between the browser -and external processes. There is currently only support for Java. - -To run the tests - just cd to the java folder and execute - -ant -Dbrowser=firefox - -This will Run the Java Selenium RPC runner unit-and integration tests. -The integration test will launch a browser that loads a JsUnit -test suite, which will run the Selenium browser part of the unit tests -and integration test. - -Prereqs: -o JDK 1.4 installed -o Ant 1.6.1 or later installed -o Xalan and JUnit in $ANT_HOME/lib - diff --git a/tests/FunctionalTests/selenium/selenium-api.js b/tests/FunctionalTests/selenium/selenium-api.js deleted file mode 100644 index 5d73e0fd..00000000 --- a/tests/FunctionalTests/selenium/selenium-api.js +++ /dev/null @@ -1,731 +0,0 @@ -/* - * Copyright 2004 ThoughtWorks, Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -storedVars = new Object(); - -function Selenium(browserbot) { - this.browserbot = browserbot; - this.optionLocatorFactory = new OptionLocatorFactory(); - this.page = function() { - return browserbot.getCurrentPage(); - }; -} - -Selenium.createForFrame = function(frame) { - return new Selenium(BrowserBot.createForFrame(frame)); -}; - -/* - * Reset the browserbot when an error occurs.. - */ -Selenium.prototype.reset = function() { - storedVars = new Object(); - this.browserbot.selectWindow("null"); -}; - -/* - * Click on the located element, and attach a callback to notify - * when the page is reloaded. - */ -Selenium.prototype.doModalDialogTest = function(returnValue) { - this.browserbot.doModalDialogTest(returnValue); -}; - -/* - * Click on the located element, and attach a callback to notify - * when the page is reloaded. - */ -Selenium.prototype.doClick = function(locator) { - var element = this.page().findElement(locator); - this.page().clickElement(element); -}; - -/** - * Overwrite the text in the located text element. - * TODO fail if it can't be typed into. - */ -Selenium.prototype.doType = function(locator, newText) { - var element = this.page().findElement(locator); - this.page().replaceText(element, newText); -}; - -Selenium.prototype.findToggleButton = function(locator) { - var element = this.page().findElement(locator); - if (element.checked == null) { - Assert.fail("Element " + locator + " is not a toggle-button."); - } - return element; -} - -/** - * Check a toggle-button. - */ -Selenium.prototype.doCheck = function(locator) { - this.findToggleButton(locator).checked = true; -}; - -/** - * Uncheck a toggle-button. - */ -Selenium.prototype.doUncheck = function(locator) { - this.findToggleButton(locator).checked = false; -}; - -/** - * Select the option from the located select element. - */ -Selenium.prototype.doSelect = function(locator, optionLocator) { - var element = this.page().findElement(locator); - if (!("options" in element)) { - throw new SeleniumError("Specified element is not a Select (has no options)"); - } - var locator = this.optionLocatorFactory.fromLocatorString(optionLocator); - var option = locator.findOption(element); - this.page().selectOption(element, option); -}; - -/* - * Open the browser to a new location. - */ -Selenium.prototype.doOpen = function(newLocation) { - this.browserbot.openLocation(newLocation); - return SELENIUM_PROCESS_WAIT; -}; - -/* - * Select the named window to be the active window. - */ -Selenium.prototype.doSelectWindow = function(windowName) { - this.browserbot.selectWindow(windowName); -}; - -/* - * Instruct Selenium to click Cancel on the next confirm dialog it encounters - */ -Selenium.prototype.doChooseCancelOnNextConfirmation = function() { - this.browserbot.cancelNextConfirmation(); -}; - -/* - * Instruct Selenium what to answear on the next prompt dialog it encounters - */ -Selenium.prototype.doAnswerOnNextPrompt = function(answer) { - this.browserbot.setNextPromptResult(answer); -}; - -/* - * Simulate the browser back button - */ -Selenium.prototype.doGoBack = function() { - this.page().goBack(); -}; - -/* - * Close the browser window or tab - */ -Selenium.prototype.doClose = function() { - this.page().close(); -}; - -/* - * Explicitly fire an event - */ -Selenium.prototype.doFireEvent = function(locator, event) { - var element = this.page().findElement(locator); - triggerEvent(element, event, false); -}; - -/* - * Get an alert message, or fail if there were no alerts. - */ -Selenium.prototype.getAlert = function() { - if (!this.browserbot.hasAlerts()) { - Assert.fail("There were no alerts"); - } - return this.browserbot.getNextAlert(); -}; - -/* - * Get a confirmation message, or fail if there were no confirmations. - */ -Selenium.prototype.getConfirmation = function() { - if (!this.browserbot.hasConfirmations()) { - Assert.fail("There were no confirmations"); - } - return this.browserbot.getNextConfirmation(); -}; - -/* - * Get a prompt message, or fail if there were no prompts. - */ -Selenium.prototype.getPrompt = function() { - if (! this.browserbot.hasPrompts()) { - Assert.fail("There were no prompts"); - } - return this.browserbot.getNextPrompt(); -}; - -/* - * Get the location of the current page. - */ -Selenium.prototype.getAbsoluteLocation = function() { - return this.page().location; -}; - -/* - * Verify the location of the current page ends with the expected location. - * If a querystring is provided, this is checked as well. - */ -Selenium.prototype.assertLocation = function(expectedLocation) { - var docLocation = this.page().location; - var searchPos = expectedLocation.lastIndexOf('?'); - - if (searchPos == -1) { - Assert.matches('*' + expectedLocation, docLocation.pathname); - } - else { - var expectedPath = expectedLocation.substring(0, searchPos); - Assert.matches('*' + expectedPath, docLocation.pathname); - - var expectedQueryString = expectedLocation.substring(searchPos); - Assert.equals(expectedQueryString, docLocation.search); - } -}; - -/* - * Get the title of the current page. - */ -Selenium.prototype.getTitle = function() { - return this.page().title(); -}; - - -/* - * Get the (trimmed) value of a form element. - * This is used to generate assertValue, verifyValue, ... - */ -Selenium.prototype.getValue = function(locator) { - var element = this.page().findElement(locator) - return getInputValue(element).trim(); -} - -/** - * Get the (trimmed) text of a form element. - * This is used to generate assertText, verifyText, ... - */ -Selenium.prototype.getText = function(locator) { - var element = this.page().findElement(locator); - return getText(element).trim(); -}; - -/** - * Assert that a toggle-button is checked. - */ -Selenium.prototype.assertChecked = function(locator) { - var element = this.page().findElement(locator); - if (element.checked == null) { - Assert.fail("Element " + locator + " is not a toggle-button."); - } - if (! element.checked) { - Assert.fail("Element " + locator + " is not checked."); - } -}; - -/** - * Assert that a toggle-button is NOT checked. - */ -Selenium.prototype.assertNotChecked = function(locator) { - var element = this.page().findElement(locator); - if (element.checked == null) { - Assert.fail("Element " + locator + " is not a toggle-button."); - } - if (element.checked) { - Assert.fail("Element " + locator + " is checked."); - } -}; - -/* - * Return the text for a single cell within an HTML table. - * The table locator syntax is table.row.column. - */ -Selenium.prototype.getTable = function(tableLocator) { - // This regular expression matches "tableName.row.column" - // For example, "mytable.3.4" - pattern = /(.*)\.(\d+)\.(\d+)/; - - if(!pattern.test(tableLocator)) { - throw new SeleniumError("Invalid target format. Correct format is tableName.rowNum.columnNum"); - } - - pieces = tableLocator.match(pattern); - - tableName = pieces[1]; - row = pieces[2]; - col = pieces[3]; - - var table = this.page().findElement(tableName); - if (row > table.rows.length) { - Assert.fail("Cannot access row " + row + " - table has " + table.rows.length + " rows"); - } - else if (col > table.rows[row].cells.length) { - Assert.fail("Cannot access column " + col + " - table row has " + table.rows[row].cells.length + " columns"); - } - else { - actualContent = getText(table.rows[row].cells[col]); - return actualContent.trim(); - } -}; - -/** - * Verify that the selected option satisfies the option locator. - */ -Selenium.prototype.assertSelected = function(target, optionLocator) { - var element = this.page().findElement(target); - var locator = this.optionLocatorFactory.fromLocatorString(optionLocator); - locator.assertSelected(element); -}; - -String.prototype.parseCSV = function() { - var values = this.replace(/\\,/g, "\n").split(","); - // Restore escaped commas - for (var i = 0; i < values.length; i++) { - values[i] = values[i].replace(/\n/g, ",").trim(); - } - return values; -}; - -/** - * Verify the label of all of the options in the drop=down. - */ -Selenium.prototype.assertSelectOptions = function(target, options) { - var element = this.page().findElement(target); - - var expectedOptionLabels = options.parseCSV(); - Assert.equals("Wrong number of options", expectedOptionLabels.length, element.options.length); - - for (var i = 0; i < element.options.length; i++) { - Assert.matches(expectedOptionLabels[i], element.options[i].text); - } -}; - -/** - * Get the value of an element attribute. The syntax for returning an element attribute - * is <element-locator>@attribute-name. Used to generate assert, verify, assertNot... - */ -Selenium.prototype.getAttribute = function(target) { - return this.page().findAttribute(target); -}; - -/* - * Asserts that the specified text is present in the page content. - */ -Selenium.prototype.assertTextPresent = function(expectedText) { - var allText = this.page().bodyText(); - - if(allText == "") { - Assert.fail("Page text not found"); - } else if(allText.indexOf(expectedText) == -1) { - Assert.fail("'" + expectedText + "' not found in page text."); - } -}; - -/* - * Asserts that the specified text is NOT present in the page content. - */ -Selenium.prototype.assertTextNotPresent = function(unexpectedText) { - var allText = this.page().bodyText(); - - if(allText == "") { - Assert.fail("Page text not found"); - } else if(allText.indexOf(unexpectedText) != -1) { - Assert.fail("'" + unexpectedText + "' was found in page text."); - } -}; - -/* - * Asserts that the specified element can be found. - */ -Selenium.prototype.assertElementPresent = function(locator) { - try { - this.page().findElement(locator); - } catch (e) { - Assert.fail("Element " + locator + " not found."); - } -}; - -/* - * Asserts that the specified element cannot be found. - */ -Selenium.prototype.assertElementNotPresent = function(locator) { - try { - this.page().findElement(locator); - } - catch (e) { - return; - } - Assert.fail("Element " + locator + " found."); -}; - -/* - * Asserts that the specified element is visible - */ -Selenium.prototype.assertVisible = function(locator) { - var element; - try { - element = this.page().findElement(locator); - } catch (e) { - Assert.fail("Element " + locator + " not present."); - } - if (! this.isVisible(element)) { - Assert.fail("Element " + locator + " not visible."); - } -}; - -/* - * Asserts that the specified element is visible - */ -Selenium.prototype.assertNotVisible = function(locator) { - var element; - try { - element = this.page().findElement(locator); - } catch (e) { - return; - } - if (this.isVisible(element)) { - Assert.fail("Element " + locator + " is visible."); - } -}; - -Selenium.prototype.isVisible = function(element) { - var visibility = this.getEffectiveStyleProperty(element, "visibility"); - var isDisplayed = this.isDisplayed(element); - return (visibility != "hidden" && isDisplayed); -}; - -Selenium.prototype.getEffectiveStyleProperty = function(element, property) { - var effectiveStyle = this.getEffectiveStyle(element); - var propertyValue = effectiveStyle[property]; - if (propertyValue == 'inherit' && element.parentNode.style) { - return this.getEffectiveStyleProperty(element.parentNode, property); - } - return propertyValue; -}; - -Selenium.prototype.isDisplayed = function(element) { - var display = this.getEffectiveStyleProperty(element, "display"); - if (display == "none") return false; - if (element.parentNode.style) { - return this.isDisplayed(element.parentNode); - } - return true; -}; - -Selenium.prototype.getEffectiveStyle = function(element) { - if (element.style == undefined) { - return undefined; // not a styled element - } - var window = this.browserbot.getContentWindow(); - if (window.getComputedStyle) { - // DOM-Level-2-CSS - return window.getComputedStyle(element, null); - } - if (element.currentStyle) { - // non-standard IE alternative - return element.currentStyle; - // TODO: this won't really work in a general sense, as - // currentStyle is not identical to getComputedStyle() - // ... but it's good enough for "visibility" - } - throw new SeleniumError("cannot determine effective stylesheet in this browser"); -}; - -/** - * Asserts that the specified element accepts user input visible - */ -Selenium.prototype.assertEditable = function(locator) { - var element = this.page().findElement(locator); - if (element.value == undefined) { - Assert.fail("Element " + locator + " is not an input."); - } - if (element.disabled) { - Assert.fail("Element " + locator + " is disabled."); - } -}; - -/** - * Asserts that the specified element does not accept user input - */ -Selenium.prototype.assertNotEditable = function(locator) { - var element = this.page().findElement(locator); - if (element.value == undefined) { - return; // not an input - } - if (element.disabled == false) { - Assert.fail("Element " + locator + " is editable."); - } -}; - - /* - * Return all buttons on the screen. - */ -Selenium.prototype.getAllButtons = function() { - return this.page().getAllButtons(); -}; - - /* - * Return all links on the screen. - */ -Selenium.prototype.getAllLinks = function() { - return this.page().getAllLinks(); -}; - - /* - * Return all fields on the screen. - */ -Selenium.prototype.getAllFields = function() { - return this.page().getAllFields(); -}; - -/* - * Set the context for the current Test - */ -Selenium.prototype.doContext = function(context) { - return this.page().setContext(context); -}; - -/* - * Store the value of a form input in a variable - */ -Selenium.prototype.doStoreValue = function(target, varName) { - if (!varName) { - // Backward compatibility mode: read the ENTIRE text of the page - // and stores it in a variable with the name of the target - value = this.page().bodyText(); - storedVars[target] = value; - return; - } - var element = this.page().findElement(target); - storedVars[varName] = getInputValue(element); -}; - -/* - * Store the text of an element in a variable - */ -Selenium.prototype.doStoreText = function(target, varName) { - var element = this.page().findElement(target); - storedVars[varName] = getText(element); -}; - -/* - * Store the value of an element attribute in a variable - */ -Selenium.prototype.doStoreAttribute = function(target, varName) { - storedVars[varName] = this.page().findAttribute(target); -}; - -/* - * Store the result of a literal value - */ -Selenium.prototype.doStore = function(value, varName) { - storedVars[varName] = value; -}; - - -/* - * Wait for the target to have the specified value by polling. - * The polling is done in TestLoop.kickoffNextCommandExecution() - */ -Selenium.prototype.doWaitForValue = function (target, value) { - var e = this.page().findElement(target); - testLoop.waitForCondition = function () { - return (e.value == value); - }; -}; - -/** - * Evaluate a parameter, performing javascript evaluation and variable substitution. - * If the string matches the pattern "javascript{ ... }", evaluate the string between the braces. - */ -Selenium.prototype.preprocessParameter = function(value) { - var match = value.match(/^javascript\{(.+)\}$/); - if (match && match[1]) { - return eval(match[1]).toString(); - } - return this.replaceVariables(value); -}; - -/* - * Search through str and replace all variable references ${varName} with their - * value in storedVars. - */ -Selenium.prototype.replaceVariables = function(str) { - var stringResult = str; - - // Find all of the matching variable references - var match = stringResult.match(/\$\{\w+\}/g); - if (!match) { - return stringResult; - } - - // For each match, lookup the variable value, and replace if found - for (var i = 0; match && i < match.length; i++) { - var variable = match[i]; // The replacement variable, with ${} - var name = variable.substring(2, variable.length - 1); // The replacement variable without ${} - var replacement = storedVars[name]; - if (replacement != undefined) { - stringResult = stringResult.replace(variable, replacement); - } - } - return stringResult; -}; - - -/** - * Factory for creating "Option Locators". - * An OptionLocator is an object for dealing with Select options (e.g. for - * finding a specified option, or asserting that the selected option of - * Select element matches some condition. - * The type of locator returned by the factory depends on the locator string: - * label=<exp> (OptionLocatorByLabel) - * value=<exp> (OptionLocatorByValue) - * index=<exp> (OptionLocatorByIndex) - * id=<exp> (OptionLocatorById) - * <exp> (default is OptionLocatorByLabel). - */ -function OptionLocatorFactory() { -} - -OptionLocatorFactory.prototype.fromLocatorString = function(locatorString) { - var locatorType = 'label'; - var locatorValue = locatorString; - // If there is a locator prefix, use the specified strategy - var result = locatorString.match(/^([a-zA-Z]+)=(.*)/); - if (result) { - locatorType = result[1]; - locatorValue = result[2]; - } - if (this.optionLocators == undefined) { - this.registerOptionLocators(); - } - if (this.optionLocators[locatorType]) { - return new this.optionLocators[locatorType](locatorValue); - } - throw new SeleniumError("Unkown option locator type: " + locatorType); -}; - -/** - * To allow for easy extension, all of the option locators are found by - * searching for all methods of OptionLocatorFactory.prototype that start - * with "OptionLocatorBy". - * TODO: Consider using the term "Option Specifier" instead of "Option Locator". - */ -OptionLocatorFactory.prototype.registerOptionLocators = function() { - this.optionLocators={}; - for (var functionName in this) { - var result = /OptionLocatorBy([A-Z].+)$/.exec(functionName); - if (result != null) { - var locatorName = result[1].lcfirst(); - this.optionLocators[locatorName] = this[functionName]; - } - } -}; - -/** - * OptionLocator for options identified by their labels. - */ -OptionLocatorFactory.prototype.OptionLocatorByLabel = function(label) { - this.label = label; - this.labelMatcher = new PatternMatcher(this.label); - this.findOption = function(element) { - for (var i = 0; i < element.options.length; i++) { - if (this.labelMatcher.matches(element.options[i].text)) { - return element.options[i]; - } - } - throw new SeleniumError("Option with label '" + this.label + "' not found"); - }; - - this.assertSelected = function(element) { - var selectedLabel = element.options[element.selectedIndex].text; - Assert.matches(this.label, selectedLabel); - }; -}; - -/** - * OptionLocator for options identified by their values. - */ -OptionLocatorFactory.prototype.OptionLocatorByValue = function(value) { - this.value = value; - this.valueMatcher = new PatternMatcher(this.value); - this.findOption = function(element) { - for (var i = 0; i < element.options.length; i++) { - if (this.valueMatcher.matches(element.options[i].value)) { - return element.options[i]; - } - } - throw new SeleniumError("Option with value '" + this.value + "' not found"); - }; - - this.assertSelected = function(element) { - var selectedValue = element.options[element.selectedIndex].value; - Assert.matches(this.value, selectedValue); - }; -}; - -/** - * OptionLocator for options identified by their index. - */ -OptionLocatorFactory.prototype.OptionLocatorByIndex = function(index) { - this.index = Number(index); - if (isNaN(this.index) || this.index < 0) { - throw new SeleniumError("Illegal Index: " + index); - } - - this.findOption = function(element) { - if (element.options.length <= this.index) { - throw new SeleniumError("Index out of range. Only " + element.options.length + " options available"); - } - return element.options[this.index]; - }; - - this.assertSelected = function(element) { - Assert.equals(this.index, element.selectedIndex); - }; -}; - -/** - * OptionLocator for options identified by their id. - */ -OptionLocatorFactory.prototype.OptionLocatorById = function(id) { - this.id = id; - this.idMatcher = new PatternMatcher(this.id); - this.findOption = function(element) { - for (var i = 0; i < element.options.length; i++) { - if (this.idMatcher.matches(element.options[i].id)) { - return element.options[i]; - } - } - throw new SeleniumError("Option with id '" + this.id + "' not found"); - }; - - this.assertSelected = function(element) { - var selectedId = element.options[element.selectedIndex].id; - Assert.matches(this.id, selectedId); - }; -}; - - diff --git a/tests/FunctionalTests/selenium/selenium-commandhandlers.js b/tests/FunctionalTests/selenium/selenium-commandhandlers.js deleted file mode 100644 index 10e2a518..00000000 --- a/tests/FunctionalTests/selenium/selenium-commandhandlers.js +++ /dev/null @@ -1,291 +0,0 @@ -/* -* Copyright 2004 ThoughtWorks, Inc -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*/ -function CommandHandlerFactory() { - this.actions = {}; - this.asserts = {}; - this.accessors = {}; - - var self = this; - - this.registerAction = function(name, action, wait) { - var handler = new ActionHandler(action, wait); - this.actions[name] = handler; - }; - - this.registerAccessor = function(name, accessor) { - var handler = new AccessorHandler(accessor); - this.accessors[name] = handler; - }; - - this.registerAssert = function(name, assertion, haltOnFailure) { - var handler = new AssertHandler(assertion, haltOnFailure); - this.asserts[name] = handler; - }; - - this.registerAssertUsingMatcherHandler = function(name, matcherHandler, haltOnFailure) { - var handler = new AssertUsingMatcherHandler(matcherHandler, haltOnFailure); - this.asserts[name] = handler; - } - - this.getCommandHandler = function(name) { - return this.actions[name] || this.accessors[name] || this.asserts[name] || null; - }; - - this.registerAll = function(commandObject) { - registerAllActions(commandObject); - registerAllAsserts(commandObject); - registerAllAccessors(commandObject); - }; - - var registerAllActions = function(commandObject) { - for (var functionName in commandObject) { - var result = /^do([A-Z].+)$/.exec(functionName); - if (result != null) { - var actionName = result[1].lcfirst(); - - // Register the action without the wait flag. - var action = commandObject[functionName]; - self.registerAction(actionName, action, false); - - // Register actionName + "AndWait" with the wait flag; - var waitActionName = actionName + "AndWait"; - self.registerAction(waitActionName, action, true); - } - } - }; - - var registerAllAsserts = function(commandObject) { - for (var functionName in commandObject) { - var result = /^assert([A-Z].+)$/.exec(functionName); - if (result != null) { - var assert = commandObject[functionName]; - - // Register the assert with the "assert" prefix, and halt on failure. - var assertName = functionName; - self.registerAssert(assertName, assert, true); - - // Register the assert with the "verify" prefix, and do not halt on failure. - var verifyName = "verify" + result[1]; - self.registerAssert(verifyName, assert, false); - } - } - }; - - // Given an accessor function, return a function that matches against the command.value - // the value returned by the accessor when applied to a command.target. - // Used by commands that take a target and a value (e.g. assertValue | target | value) - this.createMatcherHandlerFromSingleArgAccessor = function(accessor) { - return function(seleniumApi, command) { - var accessorResult = accessor.call(seleniumApi, command.target); - if (PatternMatcher.matches(command.value, accessorResult)) { - return new MatcherHandlerResult(true, "Actual value '" + accessorResult + "' did match '" + command.value + "'"); - } else { - return new MatcherHandlerResult(false, "Actual value '" + accessorResult + "' did not match '" + command.value + "'"); - } - }; - }; - - // Given an accessor function, return a function that matches against the command.target - // the value returned by the (no-arg) accessor returns a value that matches against the command.target - // Used by commands that only take a target (e.g. assertTitle | target | ) - this.createMatcherHandlerFromNoArgAccessor = function(accessor) { - return function(seleniumApi, command) { - var accessorResult = accessor.call(seleniumApi); - if (PatternMatcher.matches(command.target, accessorResult)) { - return new MatcherHandlerResult(true, "Actual value '" + accessorResult + "' did match '" + command.target + "'"); - } else { - return new MatcherHandlerResult(false, "Actual value '" + accessorResult + "' did not match '" + command.target + "'"); - } - }; - }; - - // Given a matcherHandler function, return a function that returns the same result - // as the matcherHandler, but with the result negated. - // Used to create assertNot and verifyNot commands (and soon hopefully waitForNot commands). - this.createMatcherHandlerNegator = function(matcherHandler) { - return function(seleniumApi, command) { - var result = matcherHandler(seleniumApi, command); - result.didMatch = ! result.didMatch; - return result; - }; - }; - - // Register an assertion, a verification, a negative assertion, - // and a negative verification based on the specified accessor. - this.registerAssertionsBasedOnAccessor = function(accessor, baseName) { - if (accessor.length > 1) { - return; - } - var matcherHandler; - if (accessor.length == 1) { - matcherHandler = self.createMatcherHandlerFromSingleArgAccessor(accessor); - } else { - matcherHandler = self.createMatcherHandlerFromNoArgAccessor(accessor); - } - // Register an assert with the "assert" prefix, and halt on failure. - self.registerAssertUsingMatcherHandler("assert" + baseName, matcherHandler, true); - // Register a verify with the "verify" prefix, and do not halt on failure. - self.registerAssertUsingMatcherHandler("verify" + baseName, matcherHandler, false); - - var negativeMatcherHandler = self.createMatcherHandlerNegator(matcherHandler); - // Register an assertNot with the "assertNot" prefix, and halt on failure. - self.registerAssertUsingMatcherHandler("assertNot"+baseName, negativeMatcherHandler, true); - // Register a verifyNot with the "verifyNot" prefix, and do not halt on failure. - self.registerAssertUsingMatcherHandler("verifyNot"+baseName, negativeMatcherHandler, false); - }; - - // Methods of the form getFoo(target) result in commands: - // getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo - var registerAllAccessors = function(commandObject) { - for (var functionName in commandObject) { - var match = /^get([A-Z].+)$/.exec(functionName); - if (match != null) { - var accessor = commandObject[functionName]; - var baseName = match[1]; - self.registerAccessor(functionName, accessor); - self.registerAssertionsBasedOnAccessor(accessor, baseName); - } - } - }; - - -} - -function MatcherHandlerResult(didMatch, message) { - this.didMatch = didMatch; - this.message = message; -} - -// NOTE: The CommandHandler is effectively an abstract base for ActionHandler, -// AccessorHandler and AssertHandler. -function CommandHandler(type, haltOnFailure, executor) { - this.type = type; - this.haltOnFailure = haltOnFailure; - this.executor = executor; -} -CommandHandler.prototype.execute = function(seleniumApi, command) { - return new CommandResult(this.executor.call(seleniumApi, command.target, command.value)); -}; - -function ActionHandler(action, wait) { - CommandHandler.call(this, "action", true, action); - if (wait) { - this.wait = true; - } -} -ActionHandler.prototype = new CommandHandler; -ActionHandler.prototype.execute = function(seleniumApi, command) { - if ( seleniumApi.browserbot.hasAlerts() ) { - throw new SeleniumCommandError("There was an unexpected Alert! [" + seleniumApi.browserbot.getNextAlert() + "]"); - } - if ( seleniumApi.browserbot.hasConfirmations() ) { - throw new SeleniumCommandError("There was an unexpected Confirmation! [" + seleniumApi.browserbot.getNextConfirmation() + "]"); - } - var processState = this.executor.call(seleniumApi, 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 (processState == undefined && this.wait) { - processState = SELENIUM_PROCESS_WAIT; - } - return new CommandResult(processState); -}; - -function AccessorHandler(accessor) { - CommandHandler.call(this, "accessor", true, accessor); -} -AccessorHandler.prototype = new CommandHandler; -AccessorHandler.prototype.execute = function(seleniumApi, command) { - var returnValue = this.executor.call(seleniumApi, command.target, command.value); - var result = new CommandResult(); - result.result = returnValue; - return result; -}; - -/** - * Abstract handler for assertions and verifications. - * Subclasses need to override executeAssertion() which in turn - * should throw an AssertFailedError if the assertion is to fail. - */ -function AbstractAssertHandler(assertion, haltOnFailure) { - CommandHandler.call(this, "assert", haltOnFailure || false, assertion); -} -AbstractAssertHandler.prototype = new CommandHandler; -AbstractAssertHandler.prototype.execute = function(seleniumApi, command) { - var result = new CommandResult(); - try { - this.executeAssertion(seleniumApi, command); - result.passed = true; - } catch (e) { - // If this is not a AssertionFailedError, or we should haltOnFailure, rethrow. - if (!e.isAssertionFailedError) { - throw e; - } - if (this.haltOnFailure) { - var error = new SeleniumCommandError(e.failureMessage); - throw error; - } - result.failed = true; - result.failureMessage = e.failureMessage; - } - return result; -}; - -/** - * Simple assertion handler whose command is expected to do the actual assertion. - */ -function AssertHandler(assertion, haltOnFailure) { - CommandHandler.call(this, "assert", haltOnFailure || false, assertion); -}; -AssertHandler.prototype = new AbstractAssertHandler; -AssertHandler.prototype.executeAssertion = function(seleniumApi, command) { - this.executor.call(seleniumApi, command.target, command.value); -}; - -/** - * Assertion handler whose command is expected to be a matcher-handler - */ -function AssertUsingMatcherHandler(matcherHandler, haltOnFailure) { - CommandHandler.call(this, "assert", haltOnFailure || false, matcherHandler); -}; -AssertUsingMatcherHandler.prototype = new AbstractAssertHandler; -AssertUsingMatcherHandler.prototype.executeAssertion = function(seleniumApi, command) { - var matcherResult = this.executor(seleniumApi, command); - if (!matcherResult.didMatch) { - Assert.fail(matcherResult.message); - } -}; - - -function CommandResult(processState) { - this.processState = processState; - this.result = "OK"; -} - -function SeleniumCommand(command, target, value) { - this.command = command; - this.target = target; - this.value = value; -} - -// TODO: dkemp - This is the same as SeleniumError as defined in selenium-browserbot.js -// I defined a new error simply to avoid creating a new dependency. -// Need to revisit to avoid this duplication. -function SeleniumCommandError(message) { - var error = new Error(message); - error.isSeleniumError = true; - return error; -}; diff --git a/tests/FunctionalTests/selenium/selenium-fitrunner.js b/tests/FunctionalTests/selenium/selenium-fitrunner.js deleted file mode 100644 index dddc7fc8..00000000 --- a/tests/FunctionalTests/selenium/selenium-fitrunner.js +++ /dev/null @@ -1,597 +0,0 @@ -/* -* Copyright 2004 ThoughtWorks, Inc -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*/ - -passColor = "#cfffcf"; -failColor = "#ffcfcf"; -workingColor = "#DEE7EC"; - -// The current row in the list of commands (test script) -currentCommandRow = 0; -inputTableRows = null; - -// The current row in the list of tests (test suite) -currentTestRow = 0; - -// Whether or not the jsFT should run all tests in the suite -runAllTests = false; - -// Whether or not the current test has any errors; -testFailed = false; -suiteFailed = false; - -// Holds the handlers for each command. -commandHandlers = null; - -// The number of tests run -numTestPasses = 0; - -// The number of tests that have failed -numTestFailures = 0; - -// The number of commands which have passed -numCommandPasses = 0; - -// The number of commands which have failed -numCommandFailures = 0; - -// The number of commands which have caused errors (element not found) -numCommandErrors = 0; - -// The time that the test was started. -startTime = null; - -// The current time. -currentTime = null; - -// An simple enum for failureType -ERROR = 0; -FAILURE = 1; - -runInterval = 0; - -function setRunInterval() { - // Get the value of the checked runMode option. - // There should be a way of getting the value of the "group", but I don't know how. - var runModeOptions = document.forms['controlPanel'].runMode; - for (var i = 0; i < runModeOptions.length; i++) { - if (runModeOptions[i].checked) { - runInterval = runModeOptions[i].value; - break; - } - } -} - -function continueCurrentTest() { - document.getElementById('continueTest').disabled = true; - testLoop.finishCommandExecution(); -} - -function getApplicationFrame() { - return document.getElementById('myiframe'); -} - -function getSuiteFrame() { - return document.getElementById('testSuiteFrame'); -} - -function getTestFrame(){ - return document.getElementById('testFrame'); -} - -function loadAndRunIfAuto() { - loadSuiteFrame(); -} - -function start() { - setRunInterval(); - loadSuiteFrame(); -} - -function loadSuiteFrame() { - var testAppFrame = document.getElementById('myiframe'); - selenium = Selenium.createForFrame(testAppFrame); - registerCommandHandlers(); - - //set the runInterval if there is a queryParameter for it - var tempRunInterval = getQueryParameter("runInterval"); - if (tempRunInterval) { - runInterval = tempRunInterval; - } - - document.getElementById("modeRun").onclick = setRunInterval; - document.getElementById('modeWalk').onclick = setRunInterval; - document.getElementById('modeStep').onclick = setRunInterval; - document.getElementById('continueTest').onclick = continueCurrentTest; - - var testSuiteName = getQueryParameter("test"); - - if (testSuiteName) { - addLoadListener(getSuiteFrame(), onloadTestSuite); - getSuiteFrame().src = testSuiteName; - } else { - onloadTestSuite(); - } -} - -function startSingleTest() { - removeLoadListener(getApplicationFrame(), startSingleTest); - var singleTestName = getQueryParameter("singletest"); - addLoadListener(getTestFrame(), startTest); - getTestFrame().src = singleTestName; -} - -function getIframeDocument(iframe) -{ - if (iframe.contentDocument) { - return iframe.contentDocument; - } - else { - return iframe.contentWindow.document; - } -} - -function onloadTestSuite() { - removeLoadListener(getSuiteFrame(), onloadTestSuite); - suiteTable = getIframeDocument(getSuiteFrame()).getElementsByTagName("table")[0]; - - // Add an onclick function to each link in the suite table - for(rowNum = 1;rowNum < suiteTable.rows.length; rowNum++) { - addOnclick(suiteTable, rowNum); - } - - - if (isAutomatedRun()) { - startTestSuite(); - } else if (getQueryParameter("autoURL")) { - - addLoadListener(getApplicationFrame(), startSingleTest); - - getApplicationFrame().src = getQueryParameter("autoURL"); - - } else { - testLink = suiteTable.rows[currentTestRow+1].cells[0].getElementsByTagName("a")[0]; - getTestFrame().src = testLink.href; - } -} - -// Adds an onclick function to the link in the given row in suite table. -// This function checks whether the test has already been run and the data is -// stored. If the data is stored, it sets the test frame to be the stored data. -// Otherwise, it loads the fresh page. -function addOnclick(suiteTable, rowNum) { - aLink = suiteTable.rows[rowNum].cells[0].getElementsByTagName("a")[0]; - aLink.onclick = function(eventObj) { - srcObj = null; - - // For mozilla-like browsers - if(eventObj) - srcObj = eventObj.target; - - // For IE-like browsers - else if (getSuiteFrame().contentWindow.event) - srcObj = getSuiteFrame().contentWindow.event.srcElement; - - // The target row (the event source is not consistently reported by browsers) - row = srcObj.parentNode.parentNode.rowIndex || srcObj.parentNode.parentNode.parentNode.rowIndex; - - // If the row has a stored results table, use that - if(suiteTable.rows[row].cells[1]) { - var bodyElement = getIframeDocument(getTestFrame()).body; - - // Create a div element to hold the results table. - var tableNode = getIframeDocument(getTestFrame()).createElement("div"); - var resultsCell = suiteTable.rows[row].cells[1]; - tableNode.innerHTML = resultsCell.innerHTML; - - // Append this text node, and remove all the preceding nodes. - bodyElement.appendChild(tableNode); - while (bodyElement.firstChild != bodyElement.lastChild) { - bodyElement.removeChild(bodyElement.firstChild); - } - } - // Otherwise, just open up the fresh page. - else { - getTestFrame().src = suiteTable.rows[row].cells[0].getElementsByTagName("a")[0].href; - } - - return false; - }; -} - -function isQueryParameterTrue(name) { - parameterValue = getQueryParameter(name); - return (parameterValue != null && parameterValue.toLowerCase() == "true"); -} - -function getQueryParameter(searchKey) { - var clauses = location.search.substr(1).split('&'); - for (var i = 0; i < clauses.length; i++) { - var keyValuePair = clauses[i].split('=',2); - var key = unescape(keyValuePair[0]); - if (key == searchKey) { - return unescape(keyValuePair[1]); - } - } - return null; -} - -function isNewWindow() { - return isQueryParameterTrue("newWindow"); -} - -function isAutomatedRun() { - return isQueryParameterTrue("auto"); -} - -function resetMetrics() { - numTestPasses = 0; - numTestFailures = 0; - numCommandPasses = 0; - numCommandFailures = 0; - numCommandErrors = 0; - startTime = new Date().getTime(); -} - -function runSingleTest() { - runAllTests = false; - resetMetrics(); - startTest(); -} - -function startTest() { - removeLoadListener(getTestFrame(), startTest); - - // Scroll to the top of the test frame - if (getTestFrame().contentWindow) { - getTestFrame().contentWindow.scrollTo(0,0); - } - else { - frames['testFrame'].scrollTo(0,0); - } - - inputTable = getIframeDocument(getTestFrame()).getElementsByTagName("table")[0]; - inputTableRows = inputTable.rows; - currentCommandRow = 0; - testFailed = false; - storedVars = new Object(); - - clearRowColours(); - - testLoop = initialiseTestLoop(); - testLoop.start(); -} - -function clearRowColours() { - for (var i = 0; i <= inputTableRows.length - 1; i++) { - inputTableRows[i].bgColor = "white"; - } -} - -function startTestSuite() { - resetMetrics(); - currentTestRow = 0; - runAllTests = true; - suiteFailed = false; - - runNextTest(); -} - -function runNextTest() { - if (!runAllTests) - return; - - suiteTable = getIframeDocument(getSuiteFrame()).getElementsByTagName("table")[0]; - - // Do not change the row color of the first row - if(currentTestRow > 0) { - // Make the previous row green or red depending if the test passed or failed - if(testFailed) - setCellColor(suiteTable.rows, currentTestRow, 0, failColor); - else - setCellColor(suiteTable.rows, currentTestRow, 0, passColor); - - // Set the results from the previous test run - setResultsData(suiteTable, currentTestRow); - } - - currentTestRow++; - - // If we are done with all of the tests, set the title bar as pass or fail - if(currentTestRow >= suiteTable.rows.length) { - if(suiteFailed) - setCellColor(suiteTable.rows, 0, 0, failColor); - else - setCellColor(suiteTable.rows, 0, 0, passColor); - - // If this is an automated run (i.e., build script), then submit - // the test results by posting to a form - if (isAutomatedRun()) - postTestResults(suiteFailed, suiteTable); - } - - else { - // Make the current row blue - setCellColor(suiteTable.rows, currentTestRow, 0, workingColor); - - testLink = suiteTable.rows[currentTestRow].cells[0].getElementsByTagName("a")[0]; - testLink.focus(); - - var testFrame = getTestFrame(); - addLoadListener(testFrame, startTest); - - selenium.browserbot.setIFrameLocation(testFrame, testLink.href); - } -} - -function setCellColor(tableRows, row, col, colorStr) { - tableRows[row].cells[col].bgColor = colorStr; -} - -// Sets the results from a test into a hidden column on the suite table. So, -// for each tests, the second column is set to the HTML from the test table. -function setResultsData(suiteTable, row) { - // Create a text node of the test table - var resultTable = getIframeDocument(getTestFrame()).body.innerHTML; - if (!resultTable) return; - - var tableNode = suiteTable.ownerDocument.createElement("div"); - tableNode.innerHTML = resultTable; - - var new_column = suiteTable.ownerDocument.createElement("td"); - new_column.appendChild(tableNode); - - // Set the column to be invisible - new_column.style.cssText = "display: none;"; - - // Add the invisible column - suiteTable.rows[row].appendChild(new_column); -} - -// Post the results to a servlet, CGI-script, etc. The URL of the -// results-handler defaults to "/postResults", but an alternative location -// can be specified by providing a "resultsUrl" query parameter. -// -// Parameters passed to the results-handler are: -// result: passed/failed depending on whether the suite passed or failed -// totalTime: the total running time in seconds for the suite. -// -// numTestPasses: the total number of tests which passed. -// numTestFailures: the total number of tests which failed. -// -// numCommandPasses: the total number of commands which passed. -// numCommandFailures: the total number of commands which failed. -// numCommandErrors: the total number of commands which errored. -// -// suite: the suite table, including the hidden column of test results -// testTable.1 to testTable.N: the individual test tables -// -function postTestResults(suiteFailed, suiteTable) { - - form = document.createElement("form"); - document.body.appendChild(form); - - form.id = "resultsForm"; - form.method="post"; - form.target="myiframe"; - - var resultsUrl = getQueryParameter("resultsUrl"); - if (!resultsUrl) { - resultsUrl = "./postResults"; - } - - var actionAndParameters = resultsUrl.split('?',2); - form.action = actionAndParameters[0]; - var resultsUrlQueryString = actionAndParameters[1]; - - form.createHiddenField = function(name, value) { - input = document.createElement("input"); - input.type = "hidden"; - input.name = name; - input.value = value; - this.appendChild(input); - }; - - if (resultsUrlQueryString) { - var clauses = resultsUrlQueryString.split('&'); - for (var i = 0; i < clauses.length; i++) { - var keyValuePair = clauses[i].split('=',2); - var key = unescape(keyValuePair[0]); - var value = unescape(keyValuePair[1]); - form.createHiddenField(key, value); - } - } - - form.createHiddenField("result", suiteFailed == true ? "failed" : "passed"); - - form.createHiddenField("totalTime", Math.floor((currentTime - startTime) / 1000)); - form.createHiddenField("numTestPasses", numTestPasses); - form.createHiddenField("numTestFailures", numTestFailures); - form.createHiddenField("numCommandPasses", numCommandPasses); - form.createHiddenField("numCommandFailures", numCommandFailures); - form.createHiddenField("numCommandErrors", numCommandErrors); - - // Create an input for each test table. The inputs are named - // testTable.1, testTable.2, etc. - for (rowNum = 1; rowNum < suiteTable.rows.length;rowNum++) { - // If there is a second column, then add a new input - if (suiteTable.rows[rowNum].cells.length > 1) { - var resultCell = suiteTable.rows[rowNum].cells[1]; - form.createHiddenField("testTable." + rowNum, resultCell.innerHTML); - // remove the resultCell, so it's not included in the suite HTML - resultCell.parentNode.removeChild(resultCell); - } - } - - // Add HTML for the suite itself - form.createHiddenField("suite", suiteTable.parentNode.innerHTML); - - form.submit(); - document.body.removeChild(form); - -} - -function printMetrics() { - setText(document.getElementById("commandPasses"), numCommandPasses); - setText(document.getElementById("commandFailures"), numCommandFailures); - setText(document.getElementById("commandErrors"), numCommandErrors); - setText(document.getElementById("testRuns"), numTestPasses + numTestFailures); - setText(document.getElementById("testFailures"), numTestFailures); - - currentTime = new Date().getTime(); - - timeDiff = currentTime - startTime; - totalSecs = Math.floor(timeDiff / 1000); - - minutes = Math.floor(totalSecs / 60); - seconds = totalSecs % 60; - - setText(document.getElementById("elapsedTime"), pad(minutes)+":"+pad(seconds)); -} - -// Puts a leading 0 on num if it is less than 10 -function pad (num) { - return (num > 9) ? num : "0" + num; -} - -/* - * Register all of the built-in command handlers with the CommandHandlerFactory. - * TODO work out an easy way for people to register handlers without modifying the Selenium sources. - */ -function registerCommandHandlers() { - commandFactory = new CommandHandlerFactory(); - commandFactory.registerAll(selenium); - -} - -function initialiseTestLoop() { - testLoop = new TestLoop(commandFactory); - - testLoop.getCommandInterval = function() { return runInterval; }; - testLoop.firstCommand = nextCommand; - testLoop.nextCommand = nextCommand; - testLoop.commandStarted = commandStarted; - testLoop.commandComplete = commandComplete; - testLoop.commandError = commandError; - testLoop.testComplete = testComplete; - testLoop.pause = function() { - document.getElementById('continueTest').disabled = false; - }; - return testLoop; -} - -function nextCommand() { - if (currentCommandRow >= inputTableRows.length - 1) { - return null; - } - - currentCommandRow++; - - var commandName = getCellText(currentCommandRow, 0); - var target = getCellText(currentCommandRow, 1); - var value = getCellText(currentCommandRow, 2); - - var command = new SeleniumCommand(commandName, target, removeNbsp(value)); - return command; -} - -function removeNbsp(value) -{ - return value.replace(/\240/g, ""); -} - -function scrollIntoView(element) { - if (element.scrollIntoView) { - element.scrollIntoView(false); - return; - } - // TODO: work out how to scroll browsers that don't support - // scrollIntoView (like Konqueror) -} - -function commandStarted() { - inputTableRows[currentCommandRow].bgColor = workingColor; - scrollIntoView(inputTableRows[currentCommandRow].cells[0]); - printMetrics(); -} - -function commandComplete(result) { - if (result.failed) { - setRowFailed(result.failureMessage, FAILURE); - } else if (result.passed) { - setRowPassed(); - } else { - setRowWhite(); - } -} - -function commandError(errorMessage) { - setRowFailed(errorMessage, ERROR); -} - -function setRowWhite() { - inputTableRows[currentCommandRow].bgColor = "white"; -} - -function setRowPassed() { - numCommandPasses += 1; - - // Set cell background to green - inputTableRows[currentCommandRow].bgColor = passColor; -} - -function setRowFailed(errorMsg, failureType) { - if (failureType == ERROR) - numCommandErrors += 1; - else if (failureType == FAILURE) - numCommandFailures += 1; - - // Set cell background to red - inputTableRows[currentCommandRow].bgColor = failColor; - - // Set error message - inputTableRows[currentCommandRow].cells[2].innerHTML = errorMsg; - inputTableRows[currentCommandRow].title = errorMsg; - testFailed = true; - suiteFailed = true; -} - -function testComplete() { - if(testFailed) { - inputTableRows[0].bgColor = failColor; - numTestFailures += 1; - } - else { - inputTableRows[0].bgColor = passColor; - numTestPasses += 1; - } - - printMetrics(); - - window.setTimeout("runNextTest()", 1); -} - -function getCellText(rowNumber, columnNumber) { - var cell = inputTableRows[rowNumber].cells[columnNumber]; - if (! cell.cachedText) { - cell.cachedText = getText(cell); - } - return cell.cachedText; -} - -Selenium.prototype.doPause = function(waitTime) { - testLoop.pauseInterval = waitTime; -}; diff --git a/tests/FunctionalTests/selenium/selenium-logo.graffle b/tests/FunctionalTests/selenium/selenium-logo.graffle deleted file mode 100644 index e605f4da..00000000 --- a/tests/FunctionalTests/selenium/selenium-logo.graffle +++ /dev/null @@ -1,509 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CanvasColor</key> - <dict> - <key>w</key> - <string>1</string> - </dict> - <key>ColumnAlign</key> - <integer>1</integer> - <key>ColumnSpacing</key> - <real>36</real> - <key>CreationDate</key> - <string>2005-04-17 21:44:18 -0500</string> - <key>Creator</key> - <string>Paul Hammant</string> - <key>GraphDocumentVersion</key> - <integer>4</integer> - <key>GraphicsList</key> - <array> - <dict> - <key>Bounds</key> - <string>{{54, 96}, {84, 57}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>6</integer> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - <key>stroke</key> - <dict> - <key>Color</key> - <dict> - <key>b</key> - <string>1</string> - <key>g</key> - <string>1</string> - <key>r</key> - <string>1</string> - </dict> - </dict> - </dict> - </dict> - <dict> - <key>Bounds</key> - <string>{{55, 135.875}, {42, 14}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>FitText</key> - <string>YES</string> - <key>Flow</key> - <string>Resize</string> - <key>ID</key> - <integer>5</integer> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - <key>shadow</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - <key>stroke</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\mac\ansicpg10000\cocoartf102 -{\fonttbl\f0\fswiss\fcharset77 Helvetica;} -{\colortbl;\red255\green255\blue255;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\f0\fs24 \cf2 78.96}</string> - </dict> - <key>Wrap</key> - <string>NO</string> - </dict> - <dict> - <key>Bounds</key> - <string>{{55, 109}, {84, 22}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>FitText</key> - <string>YES</string> - <key>Flow</key> - <string>Resize</string> - <key>FontInfo</key> - <dict> - <key>Font</key> - <string>Helvetica</string> - <key>Size</key> - <real>12</real> - </dict> - <key>HFlip</key> - <string>YES</string> - <key>ID</key> - <integer>4</integer> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - <key>shadow</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - <key>stroke</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\mac\ansicpg10000\cocoartf102 -{\fonttbl\f0\fswiss\fcharset77 Helvetica;} -{\colortbl;\red255\green255\blue255;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\f0\fs36 \cf2 selenium}</string> - </dict> - <key>Wrap</key> - <string>NO</string> - </dict> - <dict> - <key>Bounds</key> - <string>{{115, 98.125}, {25, 14}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>FitText</key> - <string>YES</string> - <key>Flow</key> - <string>Resize</string> - <key>ID</key> - <integer>3</integer> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - <key>shadow</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - <key>stroke</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\mac\ansicpg10000\cocoartf102 -{\fonttbl\f0\fswiss\fcharset77 Helvetica;} -{\colortbl;\red255\green255\blue255;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\f0\fs24 \cf2 34}</string> - </dict> - <key>Wrap</key> - <string>NO</string> - </dict> - <dict> - <key>Bounds</key> - <string>{{56, 93.625}, {30, 23}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>FitText</key> - <string>YES</string> - <key>Flow</key> - <string>Resize</string> - <key>FontInfo</key> - <dict> - <key>Font</key> - <string>LucidaBlackletter</string> - <key>Size</key> - <real>18</real> - </dict> - <key>ID</key> - <integer>2</integer> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - <key>shadow</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - <key>stroke</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\mac\ansicpg10000\cocoartf102 -{\fonttbl\f0\fnil\fcharset77 LucidaBlackletter;} -{\colortbl;\red255\green255\blue255;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\f0\b\fs36 \cf2 Se}</string> - </dict> - <key>Wrap</key> - <string>NO</string> - </dict> - <dict> - <key>Bounds</key> - <string>{{50, 91}, {92, 67}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>1</integer> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Color</key> - <dict> - <key>b</key> - <string>0.0556939</string> - <key>g</key> - <string>0.0239689</string> - <key>r</key> - <string>0.614286</string> - </dict> - </dict> - </dict> - </dict> - </array> - <key>GridInfo</key> - <dict/> - <key>GuidesLocked</key> - <string>NO</string> - <key>GuidesVisible</key> - <string>YES</string> - <key>HPages</key> - <integer>1</integer> - <key>ImageCounter</key> - <integer>1</integer> - <key>IsPalette</key> - <string>NO</string> - <key>Layers</key> - <array> - <dict> - <key>Lock</key> - <string>NO</string> - <key>Name</key> - <string>Layer 1</string> - <key>Print</key> - <string>YES</string> - <key>View</key> - <string>YES</string> - </dict> - </array> - <key>LayoutInfo</key> - <dict/> - <key>LinksVisible</key> - <string>NO</string> - <key>MagnetsVisible</key> - <string>NO</string> - <key>ModificationDate</key> - <string>2005-04-17 21:58:24 -0500</string> - <key>Modifier</key> - <string>Paul Hammant</string> - <key>Orientation</key> - <integer>2</integer> - <key>PageBreaks</key> - <string>YES</string> - <key>PageSetup</key> - <data> - BAt0eXBlZHN0cmVhbYED6IQBQISEhAtOU1ByaW50SW5mbwGEhAhOU09iamVjdACFkoSE - hBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCUhAFpFJKEhIQITlNT - dHJpbmcBlIQBKxBOU0pvYkRpc3Bvc2l0aW9uhpKEmZkPTlNQcmludFNwb29sSm9ihpKE - mZkLTlNQYXBlclNpemWGkoSEhAdOU1ZhbHVlAJSEASqEhAx7X05TU2l6ZT1mZn2cgQJk - gQMYhpKEmZkZTlNQcmludFJldmVyc2VPcmllbnRhdGlvboaShISECE5TTnVtYmVyAJ2b - hJeXAIaShJmZFE5TVmVydGljYWxQYWdpbmF0aW9uhpKEoZuilwCGkoSZmRROU1ZlcnRp - Y2FsbHlDZW50ZXJlZIaShKGbopcBhpKEmZkOTlNQTVBhZ2VGb3JtYXSGkoSEhAZOU0Rh - dGEAlJeBHa2EB1s3NTk3Y108P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJVVEYt - OCI/Pgo8IURPQ1RZUEUgcGxpc3QgUFVCTElDICItLy9BcHBsZSBDb21wdXRlci8vRFRE - IFBMSVNUIDEuMC8vRU4iICJodHRwOi8vd3d3LmFwcGxlLmNvbS9EVERzL1Byb3BlcnR5 - TGlzdC0xLjAuZHRkIj4KPHBsaXN0IHZlcnNpb249IjEuMCI+CjxkaWN0PgoJPGtleT5j - b20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTUhvcml6b250YWxSZXM8L2tleT4KCTxk - aWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk+CgkJ - PHN0cmluZz5jb20uYXBwbGUucHJpbnRpbmdtYW5hZ2VyPC9zdHJpbmc+CgkJPGtleT5j - b20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCTxhcnJheT4KCQkJ - PGRpY3Q+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNSG9yaXpv - bnRhbFJlczwva2V5PgoJCQkJPHJlYWw+NzI8L3JlYWw+CgkJCQk8a2V5PmNvbS5hcHBs - ZS5wcmludC50aWNrZXQuY2xpZW50PC9rZXk+CgkJCQk8c3RyaW5nPmNvbS5hcHBsZS5w - cmludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRp - Y2tldC5tb2REYXRlPC9rZXk+CgkJCQk8ZGF0ZT4yMDAzLTAxLTI0VDE2OjI4OjU0Wjwv - ZGF0ZT4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tl - eT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJ - PC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0 - aW9uPC9rZXk+Cgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3Jl - YXRvcjwva2V5PgoJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3Ry - aW5nPgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4K - CQk8YXJyYXk+CgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZv - cm1hdC5QTU9yaWVudGF0aW9uPC9rZXk+CgkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJ - CQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDwva2V5PgoJCQkJPHN0 - cmluZz5jb20uYXBwbGUucHJpbnRpbmdtYW5hZ2VyPC9zdHJpbmc+CgkJCQk8a2V5PmNv - bS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5PgoJCQkJPGRhdGU+MjAwMy0w - MS0yNFQxNjoyODo1NFo8L2RhdGU+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNr - ZXQuc3RhdGVGbGFnPC9rZXk+CgkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQk8L2Rp - Y3Q+CgkJPC9hcnJheT4KCTwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VG - b3JtYXQuUE1TY2FsaW5nPC9rZXk+Cgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmlu - dC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5n - bWFuYWdlcjwvc3RyaW5nPgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVt - QXJyYXk8L2tleT4KCQk8YXJyYXk+CgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUu - cHJpbnQuUGFnZUZvcm1hdC5QTVNjYWxpbmc8L2tleT4KCQkJCTxyZWFsPjE8L3JlYWw+ - CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY2xpZW50PC9rZXk+CgkJCQk8 - c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJCTxrZXk+ - Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQk8ZGF0ZT4yMDAz - LTAxLTI0VDE2OjI4OjU0WjwvZGF0ZT4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRp - Y2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCTwv - ZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFn - ZUZvcm1hdC5QTVZlcnRpY2FsUmVzPC9rZXk+Cgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBs - ZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc+Y29tLmFwcGxlLnBy - aW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tl - dC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk+CgkJCTxkaWN0PgoJCQkJPGtleT5jb20u - YXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVZlcnRpY2FsUmVzPC9rZXk+CgkJCQk8cmVh - bD43MjwvcmVhbD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5jbGllbnQ8 - L2tleT4KCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5n - PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lm1vZERhdGU8L2tleT4KCQkJ - CTxkYXRlPjIwMDMtMDEtMjRUMTY6Mjg6NTRaPC9kYXRlPgoJCQkJPGtleT5jb20uYXBw - bGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI+MDwvaW50 - ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk+Cgk8L2RpY3Q+Cgk8a2V5PmNvbS5hcHBs - ZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxTY2FsaW5nPC9rZXk+Cgk8ZGljdD4K - CQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJp - bmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCTxrZXk+Y29tLmFw - cGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk+CgkJCTxkaWN0 - PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVZlcnRpY2FsU2Nh - bGluZzwva2V5PgoJCQkJPHJlYWw+MTwvcmVhbD4KCQkJCTxrZXk+Y29tLmFwcGxlLnBy - aW50LnRpY2tldC5jbGllbnQ8L2tleT4KCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50 - aW5nbWFuYWdlcjwvc3RyaW5nPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0 - Lm1vZERhdGU8L2tleT4KCQkJCTxkYXRlPjIwMDMtMDEtMjRUMTY6Mjg6NTRaPC9kYXRl - PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJ - CQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk+Cgk8L2Rp - Y3Q+Cgk8a2V5PmNvbS5hcHBsZS5wcmludC5zdWJUaWNrZXQucGFwZXJfaW5mb190aWNr - ZXQ8L2tleT4KCTxkaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQu - UE1BZGp1c3RlZFBhZ2VSZWN0PC9rZXk+CgkJPGRpY3Q+CgkJCTxrZXk+Y29tLmFwcGxl - LnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk+CgkJCTxzdHJpbmc+Y29tLmFwcGxlLnBy - aW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNr - ZXQuaXRlbUFycmF5PC9rZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+ - Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1BZGp1c3RlZFBhZ2VSZWN0PC9rZXk+ - CgkJCQkJPGFycmF5PgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw+CgkJCQkJCTxyZWFsPjAu - MDwvcmVhbD4KCQkJCQkJPHJlYWw+NzM0PC9yZWFsPgoJCQkJCQk8cmVhbD41NzY8L3Jl - YWw+CgkJCQkJPC9hcnJheT4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQu - Y2xpZW50PC9rZXk+CgkJCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnRpbmdtYW5hZ2Vy - PC9zdHJpbmc+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lm1vZERhdGU8 - L2tleT4KCQkJCQk8ZGF0ZT4yMDA1LTA0LTE4VDAyOjQ0OjE4WjwvZGF0ZT4KCQkJCQk8 - a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9rZXk+CgkJCQkJPGlu - dGVnZXI+MDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJheT4KCQk8L2RpY3Q+ - CgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTUFkanVzdGVkUGFwZXJS - ZWN0PC9rZXk+CgkJPGRpY3Q+CgkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5j - cmVhdG9yPC9rZXk+CgkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwv - c3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9r - ZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50 - LlBhZ2VGb3JtYXQuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCQkJCTxhcnJheT4K - CQkJCQkJPHJlYWw+LTE4PC9yZWFsPgoJCQkJCQk8cmVhbD4tMTg8L3JlYWw+CgkJCQkJ - CTxyZWFsPjc3NDwvcmVhbD4KCQkJCQkJPHJlYWw+NTk0PC9yZWFsPgoJCQkJCTwvYXJy - YXk+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDwva2V5PgoJ - CQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCQkJ - CTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPGRh - dGU+MjAwNS0wNC0xOFQwMjo0NDoxOFo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUu - cHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVn - ZXI+CgkJCQk8L2RpY3Q+CgkJCTwvYXJyYXk+CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFw - cGxlLnByaW50LlBhcGVySW5mby5QTVBhcGVyTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8 - a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5n - PmNvbS5hcHBsZS5wcmludC5wbS5Qb3N0U2NyaXB0PC9zdHJpbmc+CgkJCTxrZXk+Y29t - LmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJ - PGRpY3Q+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNUGFwZXJO - YW1lPC9rZXk+CgkJCQkJPHN0cmluZz5uYS1sZXR0ZXI8L3N0cmluZz4KCQkJCQk8a2V5 - PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY2xpZW50PC9rZXk+CgkJCQkJPHN0cmluZz5j - b20uYXBwbGUucHJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5nPgoJCQkJCTxrZXk+Y29t - LmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPGRhdGU+MjAwMC0w - Ny0yOFQyMjo1NzowNFo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlj - a2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjE8L2ludGVnZXI+CgkJCQk8 - L2RpY3Q+CgkJCTwvYXJyYXk+CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50 - LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8 - a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5n - PmNvbS5hcHBsZS5wcmludC5wbS5Qb3N0U2NyaXB0PC9zdHJpbmc+CgkJCTxrZXk+Y29t - LmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJ - PGRpY3Q+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1 - c3RlZFBhZ2VSZWN0PC9rZXk+CgkJCQkJPGFycmF5PgoJCQkJCQk8cmVhbD4wLjA8L3Jl - YWw+CgkJCQkJCTxyZWFsPjAuMDwvcmVhbD4KCQkJCQkJPHJlYWw+NzM0PC9yZWFsPgoJ - CQkJCQk8cmVhbD41NzY8L3JlYWw+CgkJCQkJPC9hcnJheT4KCQkJCQk8a2V5PmNvbS5h - cHBsZS5wcmludC50aWNrZXQuY2xpZW50PC9rZXk+CgkJCQkJPHN0cmluZz5jb20uYXBw - bGUucHJpbnRpbmdtYW5hZ2VyPC9zdHJpbmc+CgkJCQkJPGtleT5jb20uYXBwbGUucHJp - bnQudGlja2V0Lm1vZERhdGU8L2tleT4KCQkJCQk8ZGF0ZT4yMDAzLTAxLTI0VDE2OjI4 - OjU0WjwvZGF0ZT4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVG - bGFnPC9rZXk+CgkJCQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJ - PC9hcnJheT4KCQk8L2RpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZv - LlBNVW5hZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5h - cHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBs - ZS5wcmludC5wbS5Qb3N0U2NyaXB0PC9zdHJpbmc+CgkJCTxrZXk+Y29tLmFwcGxlLnBy - aW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q+CgkJ - CQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhcGVy - UmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw+LTE4PC9yZWFsPgoJCQkJ - CQk8cmVhbD4tMTg8L3JlYWw+CgkJCQkJCTxyZWFsPjc3NDwvcmVhbD4KCQkJCQkJPHJl - YWw+NTk0PC9yZWFsPgoJCQkJCTwvYXJyYXk+CgkJCQkJPGtleT5jb20uYXBwbGUucHJp - bnQudGlja2V0LmNsaWVudDwva2V5PgoJCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50 - aW5nbWFuYWdlcjwvc3RyaW5nPgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tl - dC5tb2REYXRlPC9rZXk+CgkJCQkJPGRhdGU+MjAwMy0wMS0yNFQxNjoyODo1NFo8L2Rh - dGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5 - PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCQk8L2RpY3Q+CgkJCTwvYXJyYXk+ - CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5wcGQuUE1Q - YXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlj - a2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQucG0uUG9z - dFNjcmlwdDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRl - bUFycmF5PC9rZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmFw - cGxlLnByaW50LlBhcGVySW5mby5wcGQuUE1QYXBlck5hbWU8L2tleT4KCQkJCQk8c3Ry - aW5nPkxldHRlcjwvc3RyaW5nPgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tl - dC5jbGllbnQ8L2tleT4KCQkJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludC5wbS5Qb3N0 - U2NyaXB0PC9zdHJpbmc+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lm1v - ZERhdGU8L2tleT4KCQkJCQk8ZGF0ZT4yMDAwLTA3LTI4VDIyOjU3OjA0WjwvZGF0ZT4K - CQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9rZXk+CgkJ - CQkJPGludGVnZXI+MTwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJheT4KCQk8 - L2RpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LkFQSVZlcnNpb248L2tl - eT4KCQk8c3RyaW5nPjAwLjIwPC9zdHJpbmc+CgkJPGtleT5jb20uYXBwbGUucHJpbnQu - dGlja2V0LnByaXZhdGVMb2NrPC9rZXk+CgkJPGZhbHNlLz4KCQk8a2V5PmNvbS5hcHBs - ZS5wcmludC50aWNrZXQudHlwZTwva2V5PgoJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50 - LlBhcGVySW5mb1RpY2tldDwvc3RyaW5nPgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUu - cHJpbnQudGlja2V0LkFQSVZlcnNpb248L2tleT4KCTxzdHJpbmc+MDAuMjA8L3N0cmlu - Zz4KCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5wcml2YXRlTG9jazwva2V5PgoJ - PGZhbHNlLz4KCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC50eXBlPC9rZXk+Cgk8 - c3RyaW5nPmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0VGlja2V0PC9zdHJpbmc+Cjwv - ZGljdD4KPC9wbGlzdD4KhpKEmZkPTlNQcmludEFsbFBhZ2VzhpKgkoSZmQtOU1BhcGVy - TmFtZYaShJmZBkxldHRlcoaShJmZFU5TSG9yaXpvbmFsUGFnaW5hdGlvboaShKGbopcA - hpKEmZkWTlNIb3Jpem9udGFsbHlDZW50ZXJlZIaSppKEmZkJTlNQcmludGVyhpKEhIQJ - TlNQcmludGVyAJSShJmZFFRXIENoaWNhZ28gNnRoIGZsb29yhoaShJmZCE5TQ29waWVz - hpKEoZuilwGGkoSZmQ9OU1NjYWxpbmdGYWN0b3KGkoShm4SEAWahAYaShJmZDU5TUmln - aHRNYXJnaW6GkoShm7ihAIaShJmZDk5TQm90dG9tTWFyZ2luhpKEoZu4oQCGkoSZmQxO - U0xlZnRNYXJnaW6GkoShm7ihAIaShJmZC05TVG9wTWFyZ2luhpKEoZu4oQCGkoSZmQpO - U0xhc3RQYWdlhpKEoZuil4J/////hpKEmZkLTlNGaXJzdFBhZ2WGkoShm6KXAYaShJmZ - DU5TT3JpZW50YXRpb26GkoShm6KXAIaGhg== - </data> - <key>ReadOnly</key> - <string>NO</string> - <key>RowAlign</key> - <integer>1</integer> - <key>RowSpacing</key> - <real>36</real> - <key>SheetTitle</key> - <string>Canvas 1</string> - <key>SmartAlignmentGuidesActive</key> - <string>YES</string> - <key>SmartDistanceGuidesActive</key> - <string>YES</string> - <key>UseEntirePage</key> - <true/> - <key>VPages</key> - <integer>1</integer> - <key>WindowInfo</key> - <dict> - <key>CurrentSheet</key> - <string>0</string> - <key>Frame</key> - <string>{{342, -31}, {591, 828}}</string> - <key>ShowRuler</key> - <false/> - <key>ShowStatusBar</key> - <true/> - <key>VisibleRegion</key> - <string>{{0, 0}, {576, 730}}</string> - <key>Zoom</key> - <string>1</string> - </dict> -</dict> -</plist> diff --git a/tests/FunctionalTests/selenium/selenium-seleneserunner.js b/tests/FunctionalTests/selenium/selenium-seleneserunner.js deleted file mode 100644 index e1ae6f88..00000000 --- a/tests/FunctionalTests/selenium/selenium-seleneserunner.js +++ /dev/null @@ -1,172 +0,0 @@ -/* -* Copyright 2005 ThoughtWorks, Inc -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*/ - -passColor = "#cfffcf"; -failColor = "#ffcfcf"; -errorColor = "#ffffff"; -workingColor = "#DEE7EC"; -doneColor = "#FFFFCC"; - -slowMode = false; - -var cmd1 = document.createElement("div"); -var cmd2 = document.createElement("div"); -var cmd3 = document.createElement("div"); -var cmd4 = document.createElement("div"); - -var postResult = "START"; - -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.testComplete = function() {window.status = "Selenium Tests Complete, for this Test"}; - - document.getElementById("commandList").appendChild(cmd4); - document.getElementById("commandList").appendChild(cmd3); - document.getElementById("commandList").appendChild(cmd2); - document.getElementById("commandList").appendChild(cmd1); - - testLoop.start(); -} - -function nextCommand() { - - var xmlHttp = XmlHttp.create(); - - try { - alert("postResult == " + postResult); - if (postResult == "START") { - xmlHttp.open("GET", "driver?seleniumStart=true", false); - } else { - xmlHttp.open("GET", "driver?commandResult=" + postResult, false); - } - xmlHttp.send(null); - } catch(e) { - return null; - } - return extractCommand(xmlHttp); -} - -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 createCommandFromWikiRow(command); -} - -function commandStarted(command) { - commandNode = document.createElement("div"); - innerHTML = command.command + '('; - if (command.target != null && command.target != "") { - innerHTML += command.target; - if (command.value != null && command.value != "") { - innerHTML += ', ' + command.value; - } - } - innerHTML += ")"; - commandNode.innerHTML = innerHTML; - commandNode.style.backgroundColor = workingColor; - document.getElementById("commandList").removeChild(cmd1); - document.getElementById("commandList").removeChild(cmd2); - document.getElementById("commandList").removeChild(cmd3); - document.getElementById("commandList").removeChild(cmd4); - cmd4 = cmd3; - cmd3 = cmd2; - cmd2 = cmd1; - cmd1 = commandNode; - document.getElementById("commandList").appendChild(cmd4); - document.getElementById("commandList").appendChild(cmd3); - document.getElementById("commandList").appendChild(cmd2); - document.getElementById("commandList").appendChild(cmd1); -} - -function commandComplete(result) { - if (result.failed) { - postResult = result.failureMessage; - commandNode.title = result.failureMessage; - commandNode.style.backgroundColor = failColor; - } else if (result.passed) { - postResult = "PASSED"; - commandNode.style.backgroundColor = passColor; - } else { - postResult = result.result; - commandNode.style.backgroundColor = doneColor; - } -} - -function commandError(message) { - postResult = "ERROR"; - 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 > millis) { - break; - } - } -} - -function getIframeDocument(iframe) { - if (iframe.contentDocument) { - return iframe.contentDocument; - } - else { - return iframe.contentWindow.document; - } -} - -// Parses a Wiki table row into a Javascript expression -function createCommandFromWikiRow(wikiRow) { - var tokens; - if(tokens = wikiRow.trim().match(/^\|([^\|]*)\|([^\|]*)\|([^\|]*)\|$/m)) { - var functionName = tokens[1].trim(); - var arg1 = tokens[2].trim(); - var arg2 = tokens[3].trim(); - return new SeleniumCommand(functionName, arg1, arg2); - } else { - throw new Error("Bad wiki row format:" + wikiRow); - } -} diff --git a/tests/FunctionalTests/selenium/selenium-tableparser.js b/tests/FunctionalTests/selenium/selenium-tableparser.js deleted file mode 100644 index 2cc0ff9b..00000000 --- a/tests/FunctionalTests/selenium/selenium-tableparser.js +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2004 ThoughtWorks, Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -TableParser = function(wikiTableRows) { - this.wikiTableRows = wikiTableRows; -}; - -// Parses a Wiki table row into a SeleniumB Javascript expression -TableParser.prototype.createCommandFromWikiRow = function(wikiRow) { - var tokens; - if(tokens = wikiRow.trim().match(/^\|([^\|]*)\|([^\|]*)\|([^\|]*)\|$/m)) { - var functionName = tokens[1].trim(); - var arg1 = tokens[2].trim(); - var arg2 = tokens[3].trim(); - return new SeleniumCommand(functionName, arg1, arg2); - } else { - throw new Error("Bad wiki row format:" + wikiRow); - } -}; - -// Parses a HTML table row into a SeleniumB Javascript expression -TableParser.prototype.createCommandFromHtmlRow = function(row) { - if(row.cells.length != 3) { - throw new Error("Bad HTML row format. Rows must have 3 coumns, but had " + row.cells.length); - } - var functionName = getText(row.cells[0]); - var arg1 = getText(row.cells[1]); - var arg2 = getText(row.cells[2]); - return new SeleniumCommand(functionName, arg1, arg2); -}; - -TableParser.prototype.loop = function() { - row = this.wikiTableRows.getRow(); - if (row == null) return null; - return this.createCommandForRow(row); -};
\ No newline at end of file diff --git a/tests/FunctionalTests/selenium/xpath.js b/tests/FunctionalTests/selenium/xpath.js deleted file mode 100644 index ad635d65..00000000 --- a/tests/FunctionalTests/selenium/xpath.js +++ /dev/null @@ -1,4169 +0,0 @@ -/* - * xpath.js - * - * An XPath 1.0 library for JavaScript. - * - * Cameron McCormack <cam (at) mcc.id.au> - * - * 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ébastien Cramatte <contact (at) zeninteractif.com>. - * - * 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="...". - * - * 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 <sandy (at) mcarthur.org>. - * - * 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 - * <grant (at) gongaware.com>. - * - * 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 < and >. - * - * 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 <bedney (at) technicalpursuit.com>. - * 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] == "ancestor") { - return Step.ANCESTOR; - } else if (rhs[0] == "ancestor-or-self") { - return Step.ANCESTORORSELF; - } else if (rhs[0] == "attribute") { - return Step.ATTRIBUTE; - } else if (rhs[0] == "child") { - return Step.CHILD; - } else if (rhs[0] == "descendant") { - return Step.DESCENDANT; - } else if (rhs[0] == "descendant-or-self") { - return Step.DESCENDANTORSELF; - } else if (rhs[0] == "following") { - return Step.FOLLOWING; - } else if (rhs[0] == "following-sibling") { - return Step.FOLLOWINGSIBLING; - } else if (rhs[0] == "namespace") { - return Step.NAMESPACE; - } else if (rhs[0] == "parent") { - return Step.PARENT; - } else if (rhs[0] == "preceding") { - return Step.PRECEDING; - } else if (rhs[0] == "preceding-sibling") { - return Step.PRECEDINGSIBLING; - } else if (rhs[0] == "self") { - return Step.SELF; - } - return -1; - }; - this.reduceActions[57] = function(rhs) { - return Step.ATTRIBUTE; - }; - this.reduceActions[59] = function(rhs) { - if (rhs[0] == "comment") { - return new NodeTest(NodeTest.COMMENT, undefined); - } else if (rhs[0] == "text") { - return new NodeTest(NodeTest.TEXT, undefined); - } else if (rhs[0] == "processing-instruction") { - return new NodeTest(NodeTest.PI, undefined); - } else if (rhs[0] == "node") { - 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(":")); - return new NodeTest(NodeTest.NAMETESTPREFIXANY, prefix); - }; - this.reduceActions[70] = function(rhs) { - return new NodeTest(NodeTest.NAMETESTQNAME, rhs[0]); - }; -}; - -XPathParser.actionTable = [ - " s s sssssssss s ss s ss", - " s ", - "r rrrrrrrrr rrrrrrr rr r ", - " rrrrr ", - " s s sssssssss s ss s ss", - "rs rrrrrrrr s sssssrrrrrr rrs rs ", - " s s sssssssss s ss s ss", - " s ", - " s ", - "r rrrrrrrrr rrrrrrr rr rr ", - "r rrrrrrrrr rrrrrrr rr rr ", - "r rrrrrrrrr rrrrrrr rr rr ", - "r rrrrrrrrr rrrrrrr rr rr ", - "r rrrrrrrrr rrrrrrr rr rr ", - " s ", - " s ", - " s s sssss s s ", - "r rrrrrrrrr rrrrrrr rr r ", - "a ", - "r s rr r ", - "r sr rr r ", - "r s rr s rr r ", - "r rssrr rss rr r ", - "r rrrrr rrrss rr r ", - "r rrrrrsss rrrrr rr r ", - "r rrrrrrrr rrrrr rr r ", - "r rrrrrrrr rrrrrs rr r ", - "r rrrrrrrr rrrrrr rr r ", - "r rrrrrrrr rrrrrr rr r ", - "r srrrrrrrr rrrrrrs rr sr ", - "r srrrrrrrr rrrrrrs rr r ", - "r rrrrrrrrr rrrrrrr rr rr ", - "r rrrrrrrrr rrrrrrr rr rr ", - "r rrrrrrrrr rrrrrrr rr rr ", - "r rrrrrrrr rrrrrr rr r ", - "r rrrrrrrr rrrrrr rr r ", - "r rrrrrrrrr rrrrrrr rr r ", - "r rrrrrrrrr rrrrrrr rr r ", - " sssss ", - "r rrrrrrrrr rrrrrrr rr sr ", - "r rrrrrrrrr rrrrrrr rr r ", - "r rrrrrrrrr rrrrrrr rr rr ", - "r rrrrrrrrr rrrrrrr rr rr ", - " s ", - "r srrrrrrrr rrrrrrs rr r ", - "r rrrrrrrr rrrrr rr r ", - " s ", - " s ", - " rrrrr ", - " s s sssssssss s sss s ss", - "r srrrrrrrr rrrrrrs rr r ", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss s ss s ss", - " s s sssssssss ss s ss", - " s s sssssssss s ss s ss", - " s s sssss s s ", - " s s sssss s s ", - "r rrrrrrrrr rrrrrrr rr rr ", - " s s sssss s s ", - " s s sssss s s ", - "r rrrrrrrrr rrrrrrr rr sr ", - "r rrrrrrrrr rrrrrrr rr sr ", - "r rrrrrrrrr rrrrrrr rr r ", - "r rrrrrrrrr rrrrrrr rr rr ", - " s ", - "r rrrrrrrrr rrrrrrr rr rr ", - "r rrrrrrrrr rrrrrrr rr rr ", - " rr ", - " s ", - " rs ", - "r sr rr r ", - "r s rr s rr r ", - "r rssrr rss rr r ", - "r rssrr rss rr r ", - "r rrrrr rrrss rr r ", - "r rrrrr rrrss rr r ", - "r rrrrr rrrss rr r ", - "r rrrrr rrrss rr r ", - "r rrrrrsss rrrrr rr r ", - "r rrrrrsss rrrrr rr r ", - "r rrrrrrrr rrrrr rr r ", - "r rrrrrrrr rrrrr rr r ", - "r rrrrrrrr rrrrr rr r ", - "r rrrrrrrr rrrrrr rr r ", - " r ", - " s ", - "r srrrrrrrr rrrrrrs rr r ", - "r srrrrrrrr rrrrrrs rr r ", - "r rrrrrrrrr rrrrrrr rr r ", - "r rrrrrrrrr rrrrrrr rr r ", - "r rrrrrrrrr rrrrrrr rr r ", - "r rrrrrrrrr rrrrrrr rr r ", - "r rrrrrrrrr rrrrrrr rr rr ", - "r rrrrrrrrr rrrrrrr rr rr ", - " s s sssssssss s ss s ss", - "r rrrrrrrrr rrrrrrr rr rr ", - " r " -]; - -XPathParser.actionTableNumber = [ - " 1 0 /.-,+*)(' & %$ # \"!", - " J ", - "a aaaaaaaaa aaaaaaa aa a ", - " YYYYY ", - " 1 0 /.-,+*)(' & %$ # \"!", - "K1 KKKKKKKK . +*)('KKKKKK KK# K\" ", - " 1 0 /.-,+*)(' & %$ # \"!", - " N ", - " O ", - "e eeeeeeeee eeeeeee ee ee ", - "f fffffffff fffffff ff ff ", - "d ddddddddd ddddddd dd dd ", - "B BBBBBBBBB BBBBBBB BB BB ", - "A AAAAAAAAA AAAAAAA AA AA ", - " P ", - " Q ", - " 1 . +*)(' # \" ", - "b bbbbbbbbb bbbbbbb bb b ", - " ", - "! S !! ! ", - "\" T\" \"\" \" ", - "$ V $$ U $$ $ ", - "& &ZY&& &XW && & ", - ") ))))) )))\\[ )) ) ", - ". ....._^] ..... .. . ", - "1 11111111 11111 11 1 ", - "5 55555555 55555` 55 5 ", - "7 77777777 777777 77 7 ", - "9 99999999 999999 99 9 ", - ": c:::::::: ::::::b :: a: ", - "I fIIIIIIII IIIIIIe II I ", - "= ========= ======= == == ", - "? ????????? ??????? ?? ?? ", - "C CCCCCCCCC CCCCCCC CC CC ", - "J JJJJJJJJ JJJJJJ JJ J ", - "M MMMMMMMM MMMMMM MM M ", - "N NNNNNNNNN NNNNNNN NN N ", - "P PPPPPPPPP PPPPPPP PP P ", - " +*)(' ", - "R RRRRRRRRR RRRRRRR RR aR ", - "U UUUUUUUUU UUUUUUU UU U ", - "Z ZZZZZZZZZ ZZZZZZZ ZZ ZZ ", - "c ccccccccc ccccccc cc cc ", - " j ", - "L fLLLLLLLL LLLLLLe LL L ", - "6 66666666 66666 66 6 ", - " k ", - " l ", - " XXXXX ", - " 1 0 /.-,+*)(' & %$m # \"!", - "_ f________ ______e __ _ ", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 0 /.-,+*)(' %$ # \"!", - " 1 0 /.-,+*)(' & %$ # \"!", - " 1 . +*)(' # \" ", - " 1 . +*)(' # \" ", - "> >>>>>>>>> >>>>>>> >> >> ", - " 1 . +*)(' # \" ", - " 1 . +*)(' # \" ", - "Q QQQQQQQQQ QQQQQQQ QQ aQ ", - "V VVVVVVVVV VVVVVVV VV aV ", - "T TTTTTTTTT TTTTTTT TT T ", - "@ @@@@@@@@@ @@@@@@@ @@ @@ ", - " \x87 ", - "[ [[[[[[[[[ [[[[[[[ [[ [[ ", - "D DDDDDDDDD DDDDDDD DD DD ", - " HH ", - " \x88 ", - " F\x89 ", - "# T# ## # ", - "% V %% U %% % ", - "' 'ZY'' 'XW '' ' ", - "( (ZY(( (XW (( ( ", - "+ +++++ +++\\[ ++ + ", - "* ***** ***\\[ ** * ", - "- ----- ---\\[ -- - ", - ", ,,,,, ,,,\\[ ,, , ", - "0 00000_^] 00000 00 0 ", - "/ /////_^] ///// // / ", - "2 22222222 22222 22 2 ", - "3 33333333 33333 33 3 ", - "4 44444444 44444 44 4 ", - "8 88888888 888888 88 8 ", - " ^ ", - " \x8a ", - "; f;;;;;;;; ;;;;;;e ;; ; ", - "< f<<<<<<<< <<<<<<e << < ", - "O OOOOOOOOO OOOOOOO OO O ", - "` ````````` ``````` `` ` ", - "S SSSSSSSSS SSSSSSS SS S ", - "W WWWWWWWWW WWWWWWW WW W ", - "\\ \\\\\\\\\\\\\\\\\\ \\\\\\\\\\\\\\ \\\\ \\\\ ", - "E EEEEEEEEE EEEEEEE EE EE ", - " 1 0 /.-,+*)(' & %$ # \"!", - "] ]]]]]]]]] ]]]]]]] ]] ]] ", - " G " -]; - -XPathParser.gotoTable = [ - "3456789:;<=>?@ AB CDEFGH IJ ", - " ", - " ", - " ", - "L456789:;<=>?@ AB CDEFGH IJ ", - " M EFGH IJ ", - " N;<=>?@ AB CDEFGH IJ ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " S EFGH IJ ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " e ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " h J ", - " i j ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - "o456789:;<=>?@ ABpqCDEFGH IJ ", - " ", - " r6789:;<=>?@ AB CDEFGH IJ ", - " s789:;<=>?@ AB CDEFGH IJ ", - " t89:;<=>?@ AB CDEFGH IJ ", - " u89:;<=>?@ AB CDEFGH IJ ", - " v9:;<=>?@ AB CDEFGH IJ ", - " w9:;<=>?@ AB CDEFGH IJ ", - " x9:;<=>?@ AB CDEFGH IJ ", - " y9:;<=>?@ AB CDEFGH IJ ", - " z:;<=>?@ AB CDEFGH IJ ", - " {:;<=>?@ AB CDEFGH IJ ", - " |;<=>?@ AB CDEFGH IJ ", - " };<=>?@ AB CDEFGH IJ ", - " ~;<=>?@ AB CDEFGH IJ ", - " \x7f=>?@ AB CDEFGH IJ ", - "\x80456789:;<=>?@ AB CDEFGH IJ\x81", - " \x82 EFGH IJ ", - " \x83 EFGH IJ ", - " ", - " \x84 GH IJ ", - " \x85 GH IJ ", - " i \x86 ", - " i \x87 ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - "o456789:;<=>?@ AB\x8cqCDEFGH IJ ", - " ", - " " -]; - -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 >= 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(".."); - c = s.charAt(pos++); - continue; - } - if (c >= '0' && c <= '9') { - var number = "." + c; - c = s.charAt(pos++); - while (c >= '0' && c <= '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 == '"') { - var delimiter = c; - var literal = ""; - while ((c = s.charAt(pos++)) != delimiter) { - literal += c; - } - types.push(XPathParser.LITERAL); - values.push(literal); - c = s.charAt(pos++); - continue; - } - - if (c >= '0' && c <= '9') { - var number = c; - c = s.charAt(pos++); - while (c >= '0' && c <= '9') { - number += c; - c = s.charAt(pos++); - } - if (c == '.') { - if (s.charAt(pos) >= '0' && s.charAt(pos) <= '9') { - number += c; - number += s.charAt(pos++); - c = s.charAt(pos++); - while (c >= '0' && c <= '9') { - number += c; - c = s.charAt(pos++); - } - } - } - types.push(XPathParser.NUMBER); - values.push(number); - continue; - } - - if (c == '*') { - if (types.length > 0) { - var last = types[types.length - 1]; - if (last != XPathParser.AT - && last != XPathParser.DOUBLECOLON - && last != XPathParser.LEFTPARENTHESIS - && last != XPathParser.LEFTBRACKET - && last != XPathParser.AND - && last != XPathParser.OR - && last != XPathParser.MOD - && last != XPathParser.DIV - && last != XPathParser.MULTIPLYOPERATOR - && last != XPathParser.SLASH - && last != XPathParser.DOUBLESLASH - && last != XPathParser.BAR - && last != XPathParser.PLUS - && last != XPathParser.MINUS - && last != XPathParser.EQUALS - && last != XPathParser.NOTEQUAL - && last != XPathParser.LESSTHAN - && last != XPathParser.LESSTHANOREQUAL - && last != XPathParser.GREATERTHAN - && 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("::"); - pos++; - c = s.charAt(pos++); - continue; - } - } - - if (c == '/') { - c = s.charAt(pos++); - if (c == '/') { - types.push(XPathParser.DOUBLESLASH); - values.push("//"); - c = s.charAt(pos++); - continue; - } - types.push(XPathParser.SLASH); - values.push('/'); - continue; - } - - if (c == '!') { - if (s.charAt(pos) == '=') { - types.push(XPathParser.NOTEQUAL); - values.push("!="); - pos++; - c = s.charAt(pos++); - continue; - } - } - - if (c == '<') { - if (s.charAt(pos) == '=') { - types.push(XPathParser.LESSTHANOREQUAL); - values.push("<="); - pos++; - c = s.charAt(pos++); - continue; - } - types.push(XPathParser.LESSTHAN); - values.push('<'); - c = s.charAt(pos++); - continue; - } - - if (c == '>') { - if (s.charAt(pos) == '=') { - types.push(XPathParser.GREATERTHANOREQUAL); - values.push(">="); - pos++; - c = s.charAt(pos++); - continue; - } - types.push(XPathParser.GREATERTHAN); - values.push('>'); - 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 > 0) { - var last = types[types.length - 1]; - if (last != XPathParser.AT - && last != XPathParser.DOUBLECOLON - && last != XPathParser.LEFTPARENTHESIS - && last != XPathParser.LEFTBRACKET - && last != XPathParser.AND - && last != XPathParser.OR - && last != XPathParser.MOD - && last != XPathParser.DIV - && last != XPathParser.MULTIPLYOPERATOR - && last != XPathParser.SLASH - && last != XPathParser.DOUBLESLASH - && last != XPathParser.BAR - && last != XPathParser.PLUS - && last != XPathParser.MINUS - && last != XPathParser.EQUALS - && last != XPathParser.NOTEQUAL - && last != XPathParser.LESSTHAN - && last != XPathParser.LESSTHANOREQUAL - && last != XPathParser.GREATERTHAN - && last != XPathParser.GREATERTHANOREQUAL) { - if (name == "and") { - types.push(XPathParser.AND); - values.push(name); - continue; - } - if (name == "or") { - types.push(XPathParser.OR); - values.push(name); - continue; - } - if (name == "mod") { - types.push(XPathParser.MOD); - values.push(name); - continue; - } - if (name == "div") { - types.push(XPathParser.DIV); - values.push(name); - continue; - } - } - } - if (c == ':') { - if (s.charAt(pos) == '*') { - types.push(XPathParser.NCNAMECOLONASTERISK); - values.push(name + ":*"); - 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 == "comment" || name == "text" || name == "node") { - types.push(XPathParser.NODETYPE); - values.push(name); - continue; - } - if (name == "processing-instruction") { - 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("Unexpected character " + c); - } - types.push(1); - values.push("[EOF]"); - 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("_S"); - - 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 < 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("XPath parse error"); - } - } -}; - -// 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("HTML", "2.0"); - } catch (e) { - c.caseInsensitive = true; - } - } - return this.expression.evaluate(c); -}; - -XPath.XML_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace"; -XPath.XMLNS_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/"; - -// Expression //////////////////////////////////////////////////////////////// - -Expression.prototype = new Object(); -Expression.prototype.constructor = Expression; -Expression.superclass = Object.prototype; - -function Expression() { -} - -Expression.prototype.init = function() { -}; - -Expression.prototype.toString = function() { - return "<Expression>"; -}; - -Expression.prototype.evaluate = function(c) { - throw new Error("Could not evaluate expression."); -}; - -// UnaryOperation //////////////////////////////////////////////////////////// - -UnaryOperation.prototype = new Expression(); -UnaryOperation.prototype.constructor = UnaryOperation; -UnaryOperation.superclass = Expression.prototype; - -function UnaryOperation(rhs) { - if (arguments.length > 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 > 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 "-" + this.rhs.toString(); -}; - -// BinaryOperation /////////////////////////////////////////////////////////// - -BinaryOperation.prototype = new Expression(); -BinaryOperation.prototype.constructor = BinaryOperation; -BinaryOperation.superclass = Expression.prototype; - -function BinaryOperation(lhs, rhs) { - if (arguments.length > 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 > 0) { - this.init(lhs, rhs); - } -} - -OrOperation.prototype.init = function(lhs, rhs) { - OrOperation.superclass.init.call(this, lhs, rhs); -}; - -OrOperation.prototype.toString = function() { - return "(" + this.lhs.toString() + " or " + this.rhs.toString() + ")"; -}; - -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 > 0) { - this.init(lhs, rhs); - } -} - -AndOperation.prototype.init = function(lhs, rhs) { - AndOperation.superclass.init.call(this, lhs, rhs); -}; - -AndOperation.prototype.toString = function() { - return "(" + this.lhs.toString() + " and " + this.rhs.toString() + ")"; -}; - -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 > 0) { - this.init(lhs, rhs); - } -} - -EqualsOperation.prototype.init = function(lhs, rhs) { - EqualsOperation.superclass.init.call(this, lhs, rhs); -}; - -EqualsOperation.prototype.toString = function() { - return "(" + this.lhs.toString() + " = " + this.rhs.toString() + ")"; -}; - -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 > 0) { - this.init(lhs, rhs); - } -} - -NotEqualOperation.prototype.init = function(lhs, rhs) { - NotEqualOperation.superclass.init.call(this, lhs, rhs); -}; - -NotEqualOperation.prototype.toString = function() { - return "(" + this.lhs.toString() + " != " + this.rhs.toString() + ")"; -}; - -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 > 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 "(" + this.lhs.toString() + " < " + this.rhs.toString() + ")"; -}; - -// GreaterThanOperation ////////////////////////////////////////////////////// - -GreaterThanOperation.prototype = new BinaryOperation(); -GreaterThanOperation.prototype.constructor = GreaterThanOperation; -GreaterThanOperation.superclass = BinaryOperation.prototype; - -function GreaterThanOperation(lhs, rhs) { - if (arguments.length > 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 "(" + this.lhs.toString() + " > " + this.rhs.toString() + ")"; -}; - -// LessThanOrEqualOperation ////////////////////////////////////////////////// - -LessThanOrEqualOperation.prototype = new BinaryOperation(); -LessThanOrEqualOperation.prototype.constructor = LessThanOrEqualOperation; -LessThanOrEqualOperation.superclass = BinaryOperation.prototype; - -function LessThanOrEqualOperation(lhs, rhs) { - if (arguments.length > 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 "(" + this.lhs.toString() + " <= " + this.rhs.toString() + ")"; -}; - -// GreaterThanOrEqualOperation /////////////////////////////////////////////// - -GreaterThanOrEqualOperation.prototype = new BinaryOperation(); -GreaterThanOrEqualOperation.prototype.constructor = GreaterThanOrEqualOperation; -GreaterThanOrEqualOperation.superclass = BinaryOperation.prototype; - -function GreaterThanOrEqualOperation(lhs, rhs) { - if (arguments.length > 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 "(" + this.lhs.toString() + " >= " + this.rhs.toString() + ")"; -}; - -// PlusOperation ///////////////////////////////////////////////////////////// - -PlusOperation.prototype = new BinaryOperation(); -PlusOperation.prototype.constructor = PlusOperation; -PlusOperation.superclass = BinaryOperation.prototype; - -function PlusOperation(lhs, rhs) { - if (arguments.length > 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 "(" + this.lhs.toString() + " + " + this.rhs.toString() + ")"; -}; - -// MinusOperation //////////////////////////////////////////////////////////// - -MinusOperation.prototype = new BinaryOperation(); -MinusOperation.prototype.constructor = MinusOperation; -MinusOperation.superclass = BinaryOperation.prototype; - -function MinusOperation(lhs, rhs) { - if (arguments.length > 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 "(" + this.lhs.toString() + " - " + this.rhs.toString() + ")"; -}; - -// MultiplyOperation ///////////////////////////////////////////////////////// - -MultiplyOperation.prototype = new BinaryOperation(); -MultiplyOperation.prototype.constructor = MultiplyOperation; -MultiplyOperation.superclass = BinaryOperation.prototype; - -function MultiplyOperation(lhs, rhs) { - if (arguments.length > 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 "(" + this.lhs.toString() + " * " + this.rhs.toString() + ")"; -}; - -// DivOperation ////////////////////////////////////////////////////////////// - -DivOperation.prototype = new BinaryOperation(); -DivOperation.prototype.constructor = DivOperation; -DivOperation.superclass = BinaryOperation.prototype; - -function DivOperation(lhs, rhs) { - if (arguments.length > 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 "(" + this.lhs.toString() + " div " + this.rhs.toString() + ")"; -}; - -// ModOperation ////////////////////////////////////////////////////////////// - -ModOperation.prototype = new BinaryOperation(); -ModOperation.prototype.constructor = ModOperation; -ModOperation.superclass = BinaryOperation.prototype; - -function ModOperation(lhs, rhs) { - if (arguments.length > 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 "(" + this.lhs.toString() + " mod " + this.rhs.toString() + ")"; -}; - -// BarOperation ////////////////////////////////////////////////////////////// - -BarOperation.prototype = new BinaryOperation(); -BarOperation.prototype.constructor = BarOperation; -BarOperation.superclass = BinaryOperation.prototype; - -function BarOperation(lhs, rhs) { - if (arguments.length > 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() + " | " + this.rhs.toString(); -}; - -// PathExpr ////////////////////////////////////////////////////////////////// - -PathExpr.prototype = new Expression(); -PathExpr.prototype.constructor = PathExpr; -PathExpr.superclass = Expression.prototype; - -function PathExpr(filter, filterPreds, locpath) { - if (arguments.length > 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 && this.filterPredicates.length > 0 || this.locationPath != null) { - throw new Error("Path expression filter must evaluate to a nodset if predicates or location path are used"); - } - return ns; - } - nodes = ns.toArray(); - if (this.filterPredicates != null) { - // apply each of the predicates in turn - for (var j = 0; j < this.filterPredicates.length; j++) { - var pred = this.filterPredicates[j]; - var newNodes = []; - xpc.contextSize = nodes.length; - for (xpc.contextPosition = 1; xpc.contextPosition <= 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 < this.locationPath.steps.length; i++) { - var step = this.locationPath.steps[i]; - var newNodes = []; - for (var j = 0; j < 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 < 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 > 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 > 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 && m.nodeType != 9 /*Node.DOCUMENT_NODE*/ && 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 > 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["xml"] = XPath.XML_NAMESPACE_URI; - n["xmlns"] = XPath.XMLNS_NAMESPACE_URI; - for (var m = xpc.contextNode; m != null && m.nodeType == 1 /*Node.ELEMENT_NODE*/; m = m.parentNode) { - for (var k = 0; k < m.attributes.length; k++) { - var attr = m.attributes.item(k); - var nm = String(attr.name); - if (nm == "xmlns") { - if (n[""] == undefined) { - n[""] = attr.value; - } - } else if (nm.length > 6 && nm.substring(0, 6) == "xmlns:") { - 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 && 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 > 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 < step.predicates.length; j++) { - var pred = step.predicates[j]; - var newNodes = []; - xpc.contextSize = nodes.length; - for (xpc.contextPosition = 1; xpc.contextPosition <= 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 = "'" + s + "'"; - } - if (this.filterPredicates != undefined) { - for (var i = 0; i < this.filterPredicates.length; i++) { - s = s + "[" + this.filterPredicates[i].toString() + "]"; - } - } - if (this.locationPath != undefined) { - if (!this.locationPath.absolute) { - s += "/"; - } - 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(".."); - } - } 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("*"); - for (var i = 0; i < elts.length; i++) { - var elt = elts.item(i); - var nnm = elt.attributes; - for (var j = 0; j < 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 > 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 = "/"; - } else { - s = ""; - } - for (var i = 0; i < this.steps.length; i++) { - if (i != 0) { - s += "/"; - } - 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 > 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 = "ancestor"; - break; - case Step.ANCESTORORSELF: - s = "ancestor-or-self"; - break; - case Step.ATTRIBUTE: - s = "attribute"; - break; - case Step.CHILD: - s = "child"; - break; - case Step.DESCENDANT: - s = "descendant"; - break; - case Step.DESCENDANTORSELF: - s = "descendant-or-self"; - break; - case Step.FOLLOWING: - s = "following"; - break; - case Step.FOLLOWINGSIBLING: - s = "following-sibling"; - break; - case Step.NAMESPACE: - s = "namespace"; - break; - case Step.PARENT: - s = "parent"; - break; - case Step.PRECEDING: - s = "preceding"; - break; - case Step.PRECEDINGSIBLING: - s = "preceding-sibling"; - break; - case Step.SELF: - s = "self"; - break; - } - s += "::"; - s += this.nodeTest.toString(); - for (var i = 0; i < this.predicates.length; i++) { - s += "[" + this.predicates[i].toString() + "]"; - } - 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 > 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 "*"; - case NodeTest.NAMETESTPREFIXANY: - return this.value + ":*"; - case NodeTest.NAMETESTRESOLVEDANY: - return "{" + this.value + "}*"; - case NodeTest.NAMETESTQNAME: - return this.value; - case NodeTest.NAMETESTRESOLVEDNAME: - return "{" + this.namespaceURI + "}" + this.value; - case NodeTest.COMMENT: - return "comment()"; - case NodeTest.TEXT: - return "text()"; - case NodeTest.PI: - if (this.value != undefined) { - return "processing-instruction(\"" + this.value + "\")"; - } - return "processing-instruction()"; - case NodeTest.NODE: - return "node()"; - } - return "<unknown nodetest type>"; -}; - -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("Cannot resolve QName " + 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("Cannot resolve QName " + this.value); - } - test[0] = String(test[0]); - test[1] = String(test[1]); - if (test[0] == "") { - 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] == "") { - node[0] = null; - } - if (xpc.caseInsensitive) { - return test[0] == node[0] && String(test[1]).toLowerCase() == String(node[1]).toLowerCase(); - } - return test[0] == node[0] && 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*/ - && (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 > 0) { - this.init(v); - } -} - -VariableReference.prototype.init = function(v) { - this.variable = v; -}; - -VariableReference.prototype.toString = function() { - return "$" + 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 > 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 + "("; - for (var i = 0; i < this.arguments.length; i++) { - if (i > 0) { - s += ", "; - } - s += this.arguments[i].toString(); - } - return s + ")"; -}; - -FunctionCall.prototype.evaluate = function(c) { - var f = c.functionResolver.getFunction(this.functionName, c); - if (f == undefined) { - throw new Error("Unknown function " + 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 > 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("Cannot convert string to nodeset"); -}; - -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 > 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("Cannot convert number to nodeset"); -}; - -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 < 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 > 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 <= 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 >= 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 > 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("Cannot convert boolean to nodeset"); -}; - -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 ""; - } - 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 = ""; - 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 > 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 < 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 > d2) { - while (d1 > d2) { - n1 = n1.parentNode; - d1--; - } - if (n1 == n2) { - return 1; - } - } else if (d2 > d1) { - while (d2 > 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 && 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 < 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 < 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 < a.length; i++) { - var n = a[i]; - var l = new XString(this.stringForNode(n)); - var b = r.toArray(); - for (var j = 0; j < 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 = "#namespace"; - 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 "{ \"" + this.prefix + "\", \"" + this.namespaceURI + "\" }"; -}; - -// 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("Cannot resolve QName " + 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["{}last"] = Functions.last; - this.functions["{}position"] = Functions.position; - this.functions["{}count"] = Functions.count; - this.functions["{}id"] = Functions.id; - this.functions["{}local-name"] = Functions.localName; - this.functions["{}namespace-uri"] = Functions.namespaceURI; - this.functions["{}name"] = Functions.name; - this.functions["{}string"] = Functions.string; - this.functions["{}concat"] = Functions.concat; - this.functions["{}starts-with"] = Functions.startsWith; - this.functions["{}contains"] = Functions.contains; - this.functions["{}substring-before"] = Functions.substringBefore; - this.functions["{}substring-after"] = Functions.substringAfter; - this.functions["{}substring"] = Functions.substring; - this.functions["{}string-length"] = Functions.stringLength; - this.functions["{}normalize-space"] = Functions.normalizeSpace; - this.functions["{}translate"] = Functions.translate; - this.functions["{}boolean"] = Functions.boolean_; - this.functions["{}not"] = Functions.not; - this.functions["{}true"] = Functions.true_; - this.functions["{}false"] = Functions.false_; - this.functions["{}lang"] = Functions.lang; - this.functions["{}number"] = Functions.number; - this.functions["{}sum"] = Functions.sum; - this.functions["{}floor"] = Functions.floor; - this.functions["{}ceiling"] = Functions.ceiling; - this.functions["{}round"] = Functions.round; -}; - -FunctionResolver.prototype.addFunction = function(ns, ln, f) { - this.functions["{" + ns + "}" + 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("Cannot resolve QName " + fn); - } - return this.getFunctionWithName(parts[0], parts[1], c.contextNode); -}; - -FunctionResolver.prototype.getFunctionWithName = function(ns, ln, c) { - return this.functions["{" + ns + "}" + ln]; -}; - -// NamespaceResolver ///////////////////////////////////////////////////////// - -NamespaceResolver.prototype = new Object(); -NamespaceResolver.prototype.constructor = NamespaceResolver; -NamespaceResolver.superclass = Object.prototype; - -function NamespaceResolver() { -} - -NamespaceResolver.prototype.getNamespace = function(prefix, n) { - if (prefix == "xml") { - return XPath.XML_NAMESPACE_URI; - } else if (prefix == "xmlns") { - 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 && n.nodeType == 1 /*Node.ELEMENT_NODE*/) { - var nnm = n.attributes; - for (var i = 0; i < nnm.length; i++) { - var a = nnm.item(i); - var aname = a.nodeName; - if (aname == "xmlns" && prefix == "" - || aname == "xmlns:" + 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("Function last expects ()"); - } - return new XNumber(c.contextSize); -}; - -Functions.position = function() { - var c = arguments[0]; - if (arguments.length != 1) { - throw new Error("Function position expects ()"); - } - 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("Function count expects (node-set)"); - } - return new XNumber(ns.size); -}; - -Functions.id = function() { - var c = arguments[0]; - var id; - if (arguments.length != 2) { - throw new Error("Function id expects (object)"); - } - id = arguments[1].evaluate(c); - if (Utilities.instance_of(id, XNodeSet)) { - id = id.toArray().join(" "); - } 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 < 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("Function local-name expects (node-set?)"); - } - if (n == null) { - return new XString(""); - } - 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("Function namespace-uri expects (node-set?)"); - } - if (n == null) { - return new XString(""); - } - 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("Function name expects (node-set?)"); - } - if (n == null) { - return new XString(""); - } - 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(""); - } 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("Function string expects (object?)"); -}; - -Functions.concat = function() { - var c = arguments[0]; - if (arguments.length < 3) { - throw new Error("Function concat expects (string, string, string*)"); - } - var s = ""; - for (var i = 1; i < 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("Function startsWith expects (string, string)"); - } - 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("Function contains expects (string, string)"); - } - 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("Function substring-before expects (string, string)"); - } - 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("Function substring-after expects (string, string)"); - } - 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(""); - } - 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("Function substring expects (string, number, number?)"); - } - 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("Function string-length expects (string?)"); - } - 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("Function normalize-space expects (string?)"); - } - var i = 0; - var j = s.length - 1; - while (Utilities.isSpace(s.charCodeAt(j))) { - j--; - } - var t = ""; - while (i <= j && Utilities.isSpace(s.charCodeAt(i))) { - i++; - } - while (i <= j) { - if (Utilities.isSpace(s.charCodeAt(i))) { - t += " "; - while (i <= j && 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("Function translate expects (string, string, string)"); - } - 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 < s2.length; i++) { - var j = s2.charCodeAt(i); - if (map[j] == undefined) { - var k = i > s3.length ? "" : s3.charAt(i); - map[j] = k; - } - } - var t = ""; - for (var i = 0; i < 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("Function boolean expects (object)"); - } - return arguments[1].evaluate(c).bool(); -}; - -Functions.not = function() { - var c = arguments[0]; - if (arguments.length != 2) { - throw new Error("Function not expects (object)"); - } - return arguments[1].evaluate(c).bool().not(); -}; - -Functions.true_ = function() { - if (arguments.length != 1) { - throw new Error("Function true expects ()"); - } - return new XBoolean(true); -}; - -Functions.false_ = function() { - if (arguments.length != 1) { - throw new Error("Function false expects ()"); - } - return new XBoolean(false); -}; - -Functions.lang = function() { - var c = arguments[0]; - if (arguments.length != 2) { - throw new Error("Function lang expects (string)"); - } - var lang; - for (var n = c.contextNode; n != null && n.nodeType != 9 /*Node.DOCUMENT_NODE*/; n = n.parentNode) { - var a = n.getAttributeNS(XPath.XML_NAMESPACE_URI, "lang"); - 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 - && (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("Function number expects (object?)"); - } - 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("Function sum expects (node-set)"); - } - ns = ns.toArray(); - var n = 0; - for (var i = 0; i < 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("Function floor expects (number)"); - } - return new XNumber(Math.floor(arguments[1].evaluate(c).numberValue())); -}; - -Functions.ceiling = function() { - var c = arguments[0]; - if (arguments.length != 2) { - throw new Error("Function ceiling expects (number)"); - } - return new XNumber(Math.ceil(arguments[1].evaluate(c).numberValue())); -}; - -Functions.round = function() { - var c = arguments[0]; - if (arguments.length != 2) { - throw new Error("Function round expects (number)"); - } - return new XNumber(Math.round(arguments[1].evaluate(c).numberValue())); -}; - -// Utilities ///////////////////////////////////////////////////////////////// - -Utilities = new Object(); - -Utilities.splitQName = function(qn) { - var i = qn.indexOf(":"); - 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("", n); - if (parts[0] == null) { - parts[0] = ""; - } - } else { - parts[0] = ""; - } - } - return parts; -}; - -Utilities.isSpace = function(c) { - return c == 0x9 || c == 0xd || c == 0xa || c == 0x20; -}; - -Utilities.isLetter = function(c) { - return c >= 0x0041 && c <= 0x005A || - c >= 0x0061 && c <= 0x007A || - c >= 0x00C0 && c <= 0x00D6 || - c >= 0x00D8 && c <= 0x00F6 || - c >= 0x00F8 && c <= 0x00FF || - c >= 0x0100 && c <= 0x0131 || - c >= 0x0134 && c <= 0x013E || - c >= 0x0141 && c <= 0x0148 || - c >= 0x014A && c <= 0x017E || - c >= 0x0180 && c <= 0x01C3 || - c >= 0x01CD && c <= 0x01F0 || - c >= 0x01F4 && c <= 0x01F5 || - c >= 0x01FA && c <= 0x0217 || - c >= 0x0250 && c <= 0x02A8 || - c >= 0x02BB && c <= 0x02C1 || - c == 0x0386 || - c >= 0x0388 && c <= 0x038A || - c == 0x038C || - c >= 0x038E && c <= 0x03A1 || - c >= 0x03A3 && c <= 0x03CE || - c >= 0x03D0 && c <= 0x03D6 || - c == 0x03DA || - c == 0x03DC || - c == 0x03DE || - c == 0x03E0 || - c >= 0x03E2 && c <= 0x03F3 || - c >= 0x0401 && c <= 0x040C || - c >= 0x040E && c <= 0x044F || - c >= 0x0451 && c <= 0x045C || - c >= 0x045E && c <= 0x0481 || - c >= 0x0490 && c <= 0x04C4 || - c >= 0x04C7 && c <= 0x04C8 || - c >= 0x04CB && c <= 0x04CC || - c >= 0x04D0 && c <= 0x04EB || - c >= 0x04EE && c <= 0x04F5 || - c >= 0x04F8 && c <= 0x04F9 || - c >= 0x0531 && c <= 0x0556 || - c == 0x0559 || - c >= 0x0561 && c <= 0x0586 || - c >= 0x05D0 && c <= 0x05EA || - c >= 0x05F0 && c <= 0x05F2 || - c >= 0x0621 && c <= 0x063A || - c >= 0x0641 && c <= 0x064A || - c >= 0x0671 && c <= 0x06B7 || - c >= 0x06BA && c <= 0x06BE || - c >= 0x06C0 && c <= 0x06CE || - c >= 0x06D0 && c <= 0x06D3 || - c == 0x06D5 || - c >= 0x06E5 && c <= 0x06E6 || - c >= 0x0905 && c <= 0x0939 || - c == 0x093D || - c >= 0x0958 && c <= 0x0961 || - c >= 0x0985 && c <= 0x098C || - c >= 0x098F && c <= 0x0990 || - c >= 0x0993 && c <= 0x09A8 || - c >= 0x09AA && c <= 0x09B0 || - c == 0x09B2 || - c >= 0x09B6 && c <= 0x09B9 || - c >= 0x09DC && c <= 0x09DD || - c >= 0x09DF && c <= 0x09E1 || - c >= 0x09F0 && c <= 0x09F1 || - c >= 0x0A05 && c <= 0x0A0A || - c >= 0x0A0F && c <= 0x0A10 || - c >= 0x0A13 && c <= 0x0A28 || - c >= 0x0A2A && c <= 0x0A30 || - c >= 0x0A32 && c <= 0x0A33 || - c >= 0x0A35 && c <= 0x0A36 || - c >= 0x0A38 && c <= 0x0A39 || - c >= 0x0A59 && c <= 0x0A5C || - c == 0x0A5E || - c >= 0x0A72 && c <= 0x0A74 || - c >= 0x0A85 && c <= 0x0A8B || - c == 0x0A8D || - c >= 0x0A8F && c <= 0x0A91 || - c >= 0x0A93 && c <= 0x0AA8 || - c >= 0x0AAA && c <= 0x0AB0 || - c >= 0x0AB2 && c <= 0x0AB3 || - c >= 0x0AB5 && c <= 0x0AB9 || - c == 0x0ABD || - c == 0x0AE0 || - c >= 0x0B05 && c <= 0x0B0C || - c >= 0x0B0F && c <= 0x0B10 || - c >= 0x0B13 && c <= 0x0B28 || - c >= 0x0B2A && c <= 0x0B30 || - c >= 0x0B32 && c <= 0x0B33 || - c >= 0x0B36 && c <= 0x0B39 || - c == 0x0B3D || - c >= 0x0B5C && c <= 0x0B5D || - c >= 0x0B5F && c <= 0x0B61 || - c >= 0x0B85 && c <= 0x0B8A || - c >= 0x0B8E && c <= 0x0B90 || - c >= 0x0B92 && c <= 0x0B95 || - c >= 0x0B99 && c <= 0x0B9A || - c == 0x0B9C || - c >= 0x0B9E && c <= 0x0B9F || - c >= 0x0BA3 && c <= 0x0BA4 || - c >= 0x0BA8 && c <= 0x0BAA || - c >= 0x0BAE && c <= 0x0BB5 || - c >= 0x0BB7 && c <= 0x0BB9 || - c >= 0x0C05 && c <= 0x0C0C || - c >= 0x0C0E && c <= 0x0C10 || - c >= 0x0C12 && c <= 0x0C28 || - c >= 0x0C2A && c <= 0x0C33 || - c >= 0x0C35 && c <= 0x0C39 || - c >= 0x0C60 && c <= 0x0C61 || - c >= 0x0C85 && c <= 0x0C8C || - c >= 0x0C8E && c <= 0x0C90 || - c >= 0x0C92 && c <= 0x0CA8 || - c >= 0x0CAA && c <= 0x0CB3 || - c >= 0x0CB5 && c <= 0x0CB9 || - c == 0x0CDE || - c >= 0x0CE0 && c <= 0x0CE1 || - c >= 0x0D05 && c <= 0x0D0C || - c >= 0x0D0E && c <= 0x0D10 || - c >= 0x0D12 && c <= 0x0D28 || - c >= 0x0D2A && c <= 0x0D39 || - c >= 0x0D60 && c <= 0x0D61 || - c >= 0x0E01 && c <= 0x0E2E || - c == 0x0E30 || - c >= 0x0E32 && c <= 0x0E33 || - c >= 0x0E40 && c <= 0x0E45 || - c >= 0x0E81 && c <= 0x0E82 || - c == 0x0E84 || - c >= 0x0E87 && c <= 0x0E88 || - c == 0x0E8A || - c == 0x0E8D || - c >= 0x0E94 && c <= 0x0E97 || - c >= 0x0E99 && c <= 0x0E9F || - c >= 0x0EA1 && c <= 0x0EA3 || - c == 0x0EA5 || - c == 0x0EA7 || - c >= 0x0EAA && c <= 0x0EAB || - c >= 0x0EAD && c <= 0x0EAE || - c == 0x0EB0 || - c >= 0x0EB2 && c <= 0x0EB3 || - c == 0x0EBD || - c >= 0x0EC0 && c <= 0x0EC4 || - c >= 0x0F40 && c <= 0x0F47 || - c >= 0x0F49 && c <= 0x0F69 || - c >= 0x10A0 && c <= 0x10C5 || - c >= 0x10D0 && c <= 0x10F6 || - c == 0x1100 || - c >= 0x1102 && c <= 0x1103 || - c >= 0x1105 && c <= 0x1107 || - c == 0x1109 || - c >= 0x110B && c <= 0x110C || - c >= 0x110E && c <= 0x1112 || - c == 0x113C || - c == 0x113E || - c == 0x1140 || - c == 0x114C || - c == 0x114E || - c == 0x1150 || - c >= 0x1154 && c <= 0x1155 || - c == 0x1159 || - c >= 0x115F && c <= 0x1161 || - c == 0x1163 || - c == 0x1165 || - c == 0x1167 || - c == 0x1169 || - c >= 0x116D && c <= 0x116E || - c >= 0x1172 && c <= 0x1173 || - c == 0x1175 || - c == 0x119E || - c == 0x11A8 || - c == 0x11AB || - c >= 0x11AE && c <= 0x11AF || - c >= 0x11B7 && c <= 0x11B8 || - c == 0x11BA || - c >= 0x11BC && c <= 0x11C2 || - c == 0x11EB || - c == 0x11F0 || - c == 0x11F9 || - c >= 0x1E00 && c <= 0x1E9B || - c >= 0x1EA0 && c <= 0x1EF9 || - c >= 0x1F00 && c <= 0x1F15 || - c >= 0x1F18 && c <= 0x1F1D || - c >= 0x1F20 && c <= 0x1F45 || - c >= 0x1F48 && c <= 0x1F4D || - c >= 0x1F50 && c <= 0x1F57 || - c == 0x1F59 || - c == 0x1F5B || - c == 0x1F5D || - c >= 0x1F5F && c <= 0x1F7D || - c >= 0x1F80 && c <= 0x1FB4 || - c >= 0x1FB6 && c <= 0x1FBC || - c == 0x1FBE || - c >= 0x1FC2 && c <= 0x1FC4 || - c >= 0x1FC6 && c <= 0x1FCC || - c >= 0x1FD0 && c <= 0x1FD3 || - c >= 0x1FD6 && c <= 0x1FDB || - c >= 0x1FE0 && c <= 0x1FEC || - c >= 0x1FF2 && c <= 0x1FF4 || - c >= 0x1FF6 && c <= 0x1FFC || - c == 0x2126 || - c >= 0x212A && c <= 0x212B || - c == 0x212E || - c >= 0x2180 && c <= 0x2182 || - c >= 0x3041 && c <= 0x3094 || - c >= 0x30A1 && c <= 0x30FA || - c >= 0x3105 && c <= 0x312C || - c >= 0xAC00 && c <= 0xD7A3 || - c >= 0x4E00 && c <= 0x9FA5 || - c == 0x3007 || - c >= 0x3021 && c <= 0x3029; -}; - -Utilities.isNCNameChar = function(c) { - return c >= 0x0030 && c <= 0x0039 - || c >= 0x0660 && c <= 0x0669 - || c >= 0x06F0 && c <= 0x06F9 - || c >= 0x0966 && c <= 0x096F - || c >= 0x09E6 && c <= 0x09EF - || c >= 0x0A66 && c <= 0x0A6F - || c >= 0x0AE6 && c <= 0x0AEF - || c >= 0x0B66 && c <= 0x0B6F - || c >= 0x0BE7 && c <= 0x0BEF - || c >= 0x0C66 && c <= 0x0C6F - || c >= 0x0CE6 && c <= 0x0CEF - || c >= 0x0D66 && c <= 0x0D6F - || c >= 0x0E50 && c <= 0x0E59 - || c >= 0x0ED0 && c <= 0x0ED9 - || c >= 0x0F20 && c <= 0x0F29 - || c == 0x002E - || c == 0x002D - || c == 0x005F - || Utilities.isLetter(c) - || c >= 0x0300 && c <= 0x0345 - || c >= 0x0360 && c <= 0x0361 - || c >= 0x0483 && c <= 0x0486 - || c >= 0x0591 && c <= 0x05A1 - || c >= 0x05A3 && c <= 0x05B9 - || c >= 0x05BB && c <= 0x05BD - || c == 0x05BF - || c >= 0x05C1 && c <= 0x05C2 - || c == 0x05C4 - || c >= 0x064B && c <= 0x0652 - || c == 0x0670 - || c >= 0x06D6 && c <= 0x06DC - || c >= 0x06DD && c <= 0x06DF - || c >= 0x06E0 && c <= 0x06E4 - || c >= 0x06E7 && c <= 0x06E8 - || c >= 0x06EA && c <= 0x06ED - || c >= 0x0901 && c <= 0x0903 - || c == 0x093C - || c >= 0x093E && c <= 0x094C - || c == 0x094D - || c >= 0x0951 && c <= 0x0954 - || c >= 0x0962 && c <= 0x0963 - || c >= 0x0981 && c <= 0x0983 - || c == 0x09BC - || c == 0x09BE - || c == 0x09BF - || c >= 0x09C0 && c <= 0x09C4 - || c >= 0x09C7 && c <= 0x09C8 - || c >= 0x09CB && c <= 0x09CD - || c == 0x09D7 - || c >= 0x09E2 && c <= 0x09E3 - || c == 0x0A02 - || c == 0x0A3C - || c == 0x0A3E - || c == 0x0A3F - || c >= 0x0A40 && c <= 0x0A42 - || c >= 0x0A47 && c <= 0x0A48 - || c >= 0x0A4B && c <= 0x0A4D - || c >= 0x0A70 && c <= 0x0A71 - || c >= 0x0A81 && c <= 0x0A83 - || c == 0x0ABC - || c >= 0x0ABE && c <= 0x0AC5 - || c >= 0x0AC7 && c <= 0x0AC9 - || c >= 0x0ACB && c <= 0x0ACD - || c >= 0x0B01 && c <= 0x0B03 - || c == 0x0B3C - || c >= 0x0B3E && c <= 0x0B43 - || c >= 0x0B47 && c <= 0x0B48 - || c >= 0x0B4B && c <= 0x0B4D - || c >= 0x0B56 && c <= 0x0B57 - || c >= 0x0B82 && c <= 0x0B83 - || c >= 0x0BBE && c <= 0x0BC2 - || c >= 0x0BC6 && c <= 0x0BC8 - || c >= 0x0BCA && c <= 0x0BCD - || c == 0x0BD7 - || c >= 0x0C01 && c <= 0x0C03 - || c >= 0x0C3E && c <= 0x0C44 - || c >= 0x0C46 && c <= 0x0C48 - || c >= 0x0C4A && c <= 0x0C4D - || c >= 0x0C55 && c <= 0x0C56 - || c >= 0x0C82 && c <= 0x0C83 - || c >= 0x0CBE && c <= 0x0CC4 - || c >= 0x0CC6 && c <= 0x0CC8 - || c >= 0x0CCA && c <= 0x0CCD - || c >= 0x0CD5 && c <= 0x0CD6 - || c >= 0x0D02 && c <= 0x0D03 - || c >= 0x0D3E && c <= 0x0D43 - || c >= 0x0D46 && c <= 0x0D48 - || c >= 0x0D4A && c <= 0x0D4D - || c == 0x0D57 - || c == 0x0E31 - || c >= 0x0E34 && c <= 0x0E3A - || c >= 0x0E47 && c <= 0x0E4E - || c == 0x0EB1 - || c >= 0x0EB4 && c <= 0x0EB9 - || c >= 0x0EBB && c <= 0x0EBC - || c >= 0x0EC8 && c <= 0x0ECD - || c >= 0x0F18 && c <= 0x0F19 - || c == 0x0F35 - || c == 0x0F37 - || c == 0x0F39 - || c == 0x0F3E - || c == 0x0F3F - || c >= 0x0F71 && c <= 0x0F84 - || c >= 0x0F86 && c <= 0x0F8B - || c >= 0x0F90 && c <= 0x0F95 - || c == 0x0F97 - || c >= 0x0F99 && c <= 0x0FAD - || c >= 0x0FB1 && c <= 0x0FB7 - || c == 0x0FB9 - || c >= 0x20D0 && c <= 0x20DC - || c == 0x20E1 - || c >= 0x302A && c <= 0x302F - || c == 0x3099 - || c == 0x309A - || c == 0x00B7 - || c == 0x02D0 - || c == 0x02D1 - || c == 0x0387 - || c == 0x0640 - || c == 0x0E46 - || c == 0x0EC6 - || c == 0x3005 - || c >= 0x3031 && c <= 0x3035 - || c >= 0x309D && c <= 0x309E - || c >= 0x30FC && c <= 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 && (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("id") == id - || n.getAttributeNS(null, "id") == 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 ? ": " + this.exception.toString() : ""; - switch (this.code) { - case XPathException.INVALID_EXPRESSION_ERR: - return "Invalid expression" + msg; - case XPathException.TYPE_ERR: - return "Type error" + 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 - && 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 - && 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 < 0 || t > 9) { - throw { code: 0, toString: function() { return "Request type not supported"; } }; - } - 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 - && document.implementation.hasFeature - && document.implementation.hasFeature("XPath", null)) { - shouldInstall = false; - } - } catch (e) { - } - if (shouldInstall) { - installDOM3XPathSupport(document, new XPathParser()); - } -} catch (e) { -} diff --git a/tests/FunctionalTests/validators/tests/RequiredListTestCase.php b/tests/FunctionalTests/validators/tests/RequiredListTestCase.php index cb10f201..23e0f045 100644 --- a/tests/FunctionalTests/validators/tests/RequiredListTestCase.php +++ b/tests/FunctionalTests/validators/tests/RequiredListTestCase.php @@ -7,14 +7,13 @@ class RequiredListTestCase extends SeleniumTestCase {
$base = "ctl0_Content_";
$this->open("validators/index.php?page=RequiredListValidator");
- $this->assertLocation("index.php?page=RequiredListValidator");
$this->click("{$base}submit1");
$this->assertVisible("{$base}validator1");
$this->assertVisible("{$base}validator2");
$this->assertVisible("{$base}validator3");
$this->click("{$base}list1_c0");
- $this->select("{$base}list2", "label=One");
- $this->select("{$base}list2", "label=Two");
+ $this->addSelection("{$base}list2", "label=One");
+ $this->addSelection("{$base}list2", "label=Two");
$this->click("{$base}list3_c3");
$this->clickAndWait("{$base}submit1");
$this->assertNotVisible("{$base}validator1");
@@ -23,14 +22,14 @@ class RequiredListTestCase extends SeleniumTestCase $this->click("{$base}list1_c1");
$this->click("{$base}list1_c2");
$this->click("{$base}list1_c3");
- $this->select("{$base}list2", "label=Two");
+ $this->addSelection("{$base}list2", "label=Two");
$this->click("{$base}list1_c3");
- $this->click("{$base}submit1");
+ $this->clickAndWait("{$base}submit1");
$this->assertNotVisible("{$base}validator1");
$this->assertNotVisible("{$base}validator2");
$this->assertNotVisible("{$base}validator3");
$this->click("{$base}list3_c3");
- $this->click("{$base}submit1");
+ $this->clickAndWait("{$base}submit1");
$this->pause(200);
$this->assertNotVisible("{$base}validator1");
$this->assertNotVisible("{$base}validator2");
|