From bde8a0bde0b9b94ef5fd70d0d5de670518cff770 Mon Sep 17 00:00:00 2001 From: lukemelia Date: Tue, 2 Jan 2007 00:19:57 +0000 Subject: [PATCH] Introduce selenium tests to help get the ajax interactions correct and maintainable. Start to improve the ajax interactions around the new deferred actions section on the project detail page Add a README for developers describing how to run the selenium tests. git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@387 a4c988fc-2ded-0310-b66e-134b36920a42 --- tracks/README_DEVELOPERS | 11 + tracks/app/controllers/todo_controller.rb | 33 +- tracks/app/helpers/todo_helper.rb | 17 +- tracks/app/views/todo/create.rjs | 6 +- tracks/app/views/todo/toggle_check.rjs | 5 +- tracks/app/views/todo/update.rjs | 8 +- tracks/test/selenium/_login.rsel | 5 + .../create_deferred_todo_on_project_page.rsel | 8 + .../selenium/defer_todo_on_project_page.rsel | 9 + tracks/test/selenium/login_failure.rsel | 8 + tracks/test/selenium/login_success.rsel | 8 + ...eferred_todo_complete_on_project_page.rsel | 10 + .../test/selenium/mark_todo_complete_1.rsel | 4 + .../test/selenium/mark_todo_complete_2.rsel | 5 + .../test/selenium/mark_todo_incomplete.rsel | 5 + .../plugins/selenium-on-rails/LICENSE-2.0.txt | 202 ++ .../vendor/plugins/selenium-on-rails/README | 192 ++ .../vendor/plugins/selenium-on-rails/Rakefile | 27 + .../plugins/selenium-on-rails/config.yml | 27 + .../selenium-on-rails/config.yml.example | 27 + .../doc/classes/SeleniumController.html | 265 ++ .../doc/classes/SeleniumHelper.html | 148 ++ .../doc/classes/SeleniumOnRails.html | 124 + .../SeleniumOnRails/FixtureLoader.html | 231 ++ .../SeleniumOnRails/PartialsSupport.html | 195 ++ .../doc/classes/SeleniumOnRails/Paths.html | 295 +++ .../classes/SeleniumOnRails/RSelenese.html | 219 ++ .../doc/classes/SeleniumOnRails/Renderer.html | 156 ++ .../doc/classes/SeleniumOnRails/Selenese.html | 179 ++ .../SeleniumOnRails/SuiteRenderer.html | 223 ++ .../classes/SeleniumOnRails/TestBuilder.html | 372 +++ .../SeleniumOnRails/TestBuilderAccessors.html | 1672 ++++++++++++ .../SeleniumOnRails/TestBuilderActions.html | 1050 ++++++++ .../doc/classes/SeleniumOnRailsConfig.html | 150 ++ .../selenium-on-rails/doc/files/README.html | 388 +++ .../controllers/selenium_controller_rb.html | 108 + .../doc/files/lib/selenium_helper_rb.html | 101 + .../acceptance_test_runner_rb.html | 207 ++ .../selenium_on_rails/fixture_loader_rb.html | 108 + .../partials_support_rb.html | 111 + .../files/lib/selenium_on_rails/paths_rb.html | 101 + .../lib/selenium_on_rails/renderer_rb.html | 101 + .../lib/selenium_on_rails/rselenese_rb.html | 118 + .../lib/selenium_on_rails/selenese_rb.html | 101 + .../selenium_on_rails/suite_renderer_rb.html | 101 + .../test_builder_accessors_rb.html | 114 + .../test_builder_actions_rb.html | 113 + .../selenium_on_rails/test_builder_rb.html | 121 + .../lib/selenium_on_rails_config_rb.html | 108 + .../doc/files/lib/selenium_on_rails_rb.html | 115 + .../selenium-on-rails/doc/fr_class_index.html | 40 + .../selenium-on-rails/doc/fr_file_index.html | 42 + .../doc/fr_method_index.html | 119 + .../plugins/selenium-on-rails/doc/index.html | 24 + .../selenium-on-rails/doc/rdoc-style.css | 208 ++ .../generators/selenium/USAGE | 19 + .../generators/selenium/selenium_generator.rb | 50 + .../generators/selenium/templates/rhtml.rhtml | 16 + .../selenium/templates/rselenese.rhtml | 14 + .../selenium/templates/selenese.rhtml | 11 + .../vendor/plugins/selenium-on-rails/init.rb | 19 + .../lib/controllers/selenium_controller.rb | 119 + .../selenium-on-rails/lib/selenium_helper.rb | 9 + .../lib/selenium_on_rails.rb | 11 + .../acceptance_test_runner.rb | 210 ++ .../lib/selenium_on_rails/fixture_loader.rb | 54 + .../lib/selenium_on_rails/partials_support.rb | 38 + .../lib/selenium_on_rails/paths.rb | 61 + .../lib/selenium_on_rails/renderer.rb | 17 + .../lib/selenium_on_rails/rselenese.rb | 35 + .../lib/selenium_on_rails/selenese.rb | 81 + .../lib/selenium_on_rails/suite_renderer.rb | 51 + .../lib/selenium_on_rails/test_builder.rb | 92 + .../test_builder_accessors.rb | 575 ++++ .../selenium_on_rails/test_builder_actions.rb | 286 ++ .../lib/selenium_on_rails_config.rb | 22 + .../selenium-on-rails/lib/views/layout.rhtml | 18 + .../selenium-on-rails/lib/views/record.rhtml | 5 + .../selenium-on-rails/lib/views/setup.rhtml | 67 + .../lib/views/test_suite.rhtml | 26 + .../plugins/selenium-on-rails/routes.rb | 23 + .../switch_environment/init.rb | 20 + .../switch_environment_controller.rb | 16 + .../tasks/test_acceptance.rake | 8 + .../selenium-on-rails/test/renderer_test.rb | 147 ++ .../selenium-on-rails/test/rselenese_test.rb | 403 +++ .../selenium-on-rails/test/selenese_test.rb | 229 ++ .../test/selenium_controller_test.rb | 48 + .../test/selenium_support_test.rb | 33 + .../selenium-on-rails/test/setup_test.rb | 29 + .../test/suite_renderer_test.rb | 173 ++ .../selenium-on-rails/test/test_helper.rb | 70 + .../selenium-on-rails/test_data/_partial.rsel | 1 + .../selenium-on-rails/test_data/html.html | 6 + .../test_data/own_layout.html | 12 + .../test_data/partials/_html.html | 6 + .../test_data/partials/_nesting.rsel | 2 + .../test_data/partials/_rhtml.rhtml | 6 + .../test_data/partials/_rsel.rsel | 1 + .../test_data/partials/_sel.sel | 5 + .../test_data/partials/all_partials.rsel | 5 + .../selenium-on-rails/test_data/rhtml.rhtml | 7 + .../test_data/rselenese.rsel | 8 + .../selenium-on-rails/test_data/selenese.sel | 7 + .../subsuite/suite_one_subsuite_testcase.sel | 1 + .../suite_one/suite_one_testcase1.sel | 1 + .../suite_one/suite_one_testcase2.sel | 1 + .../suite_two/suite_two_testcase.sel | 1 + tracks/vendor/selenium/Blank.html | 8 + .../vendor/selenium/InjectedRemoteRunner.html | 8 + tracks/vendor/selenium/RemoteRunner.html | 109 + tracks/vendor/selenium/SeleniumLog.html | 74 + tracks/vendor/selenium/TestPrompt.html | 123 + tracks/vendor/selenium/TestRunner-splash.html | 55 + tracks/vendor/selenium/TestRunner.hta | 175 ++ tracks/vendor/selenium/TestRunner.html | 175 ++ tracks/vendor/selenium/domviewer/butmin.gif | Bin 0 -> 843 bytes tracks/vendor/selenium/domviewer/butplus.gif | Bin 0 -> 848 bytes .../vendor/selenium/domviewer/domviewer.css | 298 +++ .../vendor/selenium/domviewer/domviewer.html | 16 + .../selenium/domviewer/selenium-domviewer.js | 205 ++ tracks/vendor/selenium/icons/all.png | Bin 0 -> 662 bytes tracks/vendor/selenium/icons/continue.png | Bin 0 -> 405 bytes .../selenium/icons/continue_disabled.png | Bin 0 -> 374 bytes tracks/vendor/selenium/icons/pause.png | Bin 0 -> 275 bytes .../vendor/selenium/icons/pause_disabled.png | Bin 0 -> 263 bytes tracks/vendor/selenium/icons/selected.png | Bin 0 -> 668 bytes tracks/vendor/selenium/icons/step.png | Bin 0 -> 452 bytes .../vendor/selenium/icons/step_disabled.png | Bin 0 -> 402 bytes tracks/vendor/selenium/iedoc-core.xml | 1431 ++++++++++ tracks/vendor/selenium/iedoc.xml | 1368 ++++++++++ .../selenium/lib/cssQuery/cssQuery-p.js | 6 + .../lib/cssQuery/src/cssQuery-level2.js | 142 + .../lib/cssQuery/src/cssQuery-level3.js | 150 ++ .../lib/cssQuery/src/cssQuery-standard.js | 53 + .../selenium/lib/cssQuery/src/cssQuery.js | 356 +++ tracks/vendor/selenium/lib/prototype.js | 2006 ++++++++++++++ .../selenium/lib/scriptaculous/builder.js | 101 + .../selenium/lib/scriptaculous/controls.js | 815 ++++++ .../selenium/lib/scriptaculous/dragdrop.js | 915 +++++++ .../selenium/lib/scriptaculous/effects.js | 958 +++++++ .../lib/scriptaculous/scriptaculous.js | 47 + .../selenium/lib/scriptaculous/slider.js | 283 ++ .../selenium/lib/scriptaculous/unittest.js | 383 +++ .../selenium/scripts/find_matching_child.js | 69 + tracks/vendor/selenium/scripts/htmlutils.js | 842 ++++++ tracks/vendor/selenium/scripts/injection.html | 79 + .../selenium/scripts/injection_iframe.html | 7 + tracks/vendor/selenium/scripts/js2html.js | 70 + .../vendor/selenium/scripts/narcissus-defs.js | 175 ++ .../vendor/selenium/scripts/narcissus-exec.js | 1054 ++++++++ .../selenium/scripts/narcissus-parse.js | 1003 +++++++ tracks/vendor/selenium/scripts/se2html.js | 63 + .../vendor/selenium/scripts/selenium-api.js | 2329 +++++++++++++++++ .../selenium/scripts/selenium-browserbot.js | 1946 ++++++++++++++ .../scripts/selenium-browserdetect.js | 142 + .../scripts/selenium-commandhandlers.js | 375 +++ .../scripts/selenium-executionloop.js | 177 ++ .../selenium/scripts/selenium-logging.js | 139 + .../selenium/scripts/selenium-remoterunner.js | 466 ++++ .../selenium/scripts/selenium-testrunner.js | 1281 +++++++++ .../selenium/scripts/selenium-version.js | 5 + .../scripts/user-extensions.js.sample | 75 + tracks/vendor/selenium/scripts/xmlextras.js | 153 ++ tracks/vendor/selenium/selenium-logo.png | Bin 0 -> 6714 bytes tracks/vendor/selenium/selenium-test.css | 43 + tracks/vendor/selenium/selenium.css | 299 +++ tracks/vendor/selenium/xpath/dom.js | 428 +++ tracks/vendor/selenium/xpath/misc.js | 255 ++ tracks/vendor/selenium/xpath/xpath.js | 2182 +++++++++++++++ 170 files changed, 35512 insertions(+), 25 deletions(-) create mode 100644 tracks/README_DEVELOPERS create mode 100644 tracks/test/selenium/_login.rsel create mode 100644 tracks/test/selenium/create_deferred_todo_on_project_page.rsel create mode 100644 tracks/test/selenium/defer_todo_on_project_page.rsel create mode 100644 tracks/test/selenium/login_failure.rsel create mode 100644 tracks/test/selenium/login_success.rsel create mode 100644 tracks/test/selenium/mark_last_deferred_todo_complete_on_project_page.rsel create mode 100644 tracks/test/selenium/mark_todo_complete_1.rsel create mode 100644 tracks/test/selenium/mark_todo_complete_2.rsel create mode 100644 tracks/test/selenium/mark_todo_incomplete.rsel create mode 100644 tracks/vendor/plugins/selenium-on-rails/LICENSE-2.0.txt create mode 100644 tracks/vendor/plugins/selenium-on-rails/README create mode 100644 tracks/vendor/plugins/selenium-on-rails/Rakefile create mode 100644 tracks/vendor/plugins/selenium-on-rails/config.yml create mode 100644 tracks/vendor/plugins/selenium-on-rails/config.yml.example create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumController.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumHelper.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/FixtureLoader.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/PartialsSupport.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Paths.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/RSelenese.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Renderer.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Selenese.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/SuiteRenderer.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilder.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderAccessors.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderActions.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRailsConfig.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/README.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/controllers/selenium_controller_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_helper_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/acceptance_test_runner_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/fixture_loader_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/partials_support_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/paths_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/renderer_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/rselenese_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/selenese_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/suite_renderer_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_accessors_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_actions_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_config_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_rb.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/fr_class_index.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/fr_file_index.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/fr_method_index.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/index.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/doc/rdoc-style.css create mode 100644 tracks/vendor/plugins/selenium-on-rails/generators/selenium/USAGE create mode 100644 tracks/vendor/plugins/selenium-on-rails/generators/selenium/selenium_generator.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/rhtml.rhtml create mode 100644 tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/rselenese.rhtml create mode 100644 tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/selenese.rhtml create mode 100644 tracks/vendor/plugins/selenium-on-rails/init.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/controllers/selenium_controller.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_helper.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/acceptance_test_runner.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/fixture_loader.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/partials_support.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/paths.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/renderer.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/rselenese.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/selenese.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/suite_renderer.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_accessors.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_actions.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails_config.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/views/layout.rhtml create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/views/record.rhtml create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/views/setup.rhtml create mode 100644 tracks/vendor/plugins/selenium-on-rails/lib/views/test_suite.rhtml create mode 100644 tracks/vendor/plugins/selenium-on-rails/routes.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/switch_environment/init.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/switch_environment/switch_environment_controller.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/tasks/test_acceptance.rake create mode 100644 tracks/vendor/plugins/selenium-on-rails/test/renderer_test.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/test/rselenese_test.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/test/selenese_test.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/test/selenium_controller_test.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/test/selenium_support_test.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/test/setup_test.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/test/suite_renderer_test.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/test/test_helper.rb create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/_partial.rsel create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/html.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/own_layout.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/partials/_html.html create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/partials/_nesting.rsel create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/partials/_rhtml.rhtml create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/partials/_rsel.rsel create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/partials/_sel.sel create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/partials/all_partials.rsel create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/rhtml.rhtml create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/rselenese.rsel create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/selenese.sel create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/subsuite/suite_one_subsuite_testcase.sel create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase1.sel create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase2.sel create mode 100644 tracks/vendor/plugins/selenium-on-rails/test_data/suite_two/suite_two_testcase.sel create mode 100644 tracks/vendor/selenium/Blank.html create mode 100644 tracks/vendor/selenium/InjectedRemoteRunner.html create mode 100644 tracks/vendor/selenium/RemoteRunner.html create mode 100644 tracks/vendor/selenium/SeleniumLog.html create mode 100644 tracks/vendor/selenium/TestPrompt.html create mode 100644 tracks/vendor/selenium/TestRunner-splash.html create mode 100644 tracks/vendor/selenium/TestRunner.hta create mode 100644 tracks/vendor/selenium/TestRunner.html create mode 100644 tracks/vendor/selenium/domviewer/butmin.gif create mode 100644 tracks/vendor/selenium/domviewer/butplus.gif create mode 100644 tracks/vendor/selenium/domviewer/domviewer.css create mode 100644 tracks/vendor/selenium/domviewer/domviewer.html create mode 100644 tracks/vendor/selenium/domviewer/selenium-domviewer.js create mode 100644 tracks/vendor/selenium/icons/all.png create mode 100644 tracks/vendor/selenium/icons/continue.png create mode 100644 tracks/vendor/selenium/icons/continue_disabled.png create mode 100644 tracks/vendor/selenium/icons/pause.png create mode 100644 tracks/vendor/selenium/icons/pause_disabled.png create mode 100644 tracks/vendor/selenium/icons/selected.png create mode 100644 tracks/vendor/selenium/icons/step.png create mode 100644 tracks/vendor/selenium/icons/step_disabled.png create mode 100644 tracks/vendor/selenium/iedoc-core.xml create mode 100644 tracks/vendor/selenium/iedoc.xml create mode 100644 tracks/vendor/selenium/lib/cssQuery/cssQuery-p.js create mode 100644 tracks/vendor/selenium/lib/cssQuery/src/cssQuery-level2.js create mode 100644 tracks/vendor/selenium/lib/cssQuery/src/cssQuery-level3.js create mode 100644 tracks/vendor/selenium/lib/cssQuery/src/cssQuery-standard.js create mode 100644 tracks/vendor/selenium/lib/cssQuery/src/cssQuery.js create mode 100644 tracks/vendor/selenium/lib/prototype.js create mode 100644 tracks/vendor/selenium/lib/scriptaculous/builder.js create mode 100644 tracks/vendor/selenium/lib/scriptaculous/controls.js create mode 100644 tracks/vendor/selenium/lib/scriptaculous/dragdrop.js create mode 100644 tracks/vendor/selenium/lib/scriptaculous/effects.js create mode 100644 tracks/vendor/selenium/lib/scriptaculous/scriptaculous.js create mode 100644 tracks/vendor/selenium/lib/scriptaculous/slider.js create mode 100644 tracks/vendor/selenium/lib/scriptaculous/unittest.js create mode 100644 tracks/vendor/selenium/scripts/find_matching_child.js create mode 100644 tracks/vendor/selenium/scripts/htmlutils.js create mode 100644 tracks/vendor/selenium/scripts/injection.html create mode 100644 tracks/vendor/selenium/scripts/injection_iframe.html create mode 100644 tracks/vendor/selenium/scripts/js2html.js create mode 100644 tracks/vendor/selenium/scripts/narcissus-defs.js create mode 100644 tracks/vendor/selenium/scripts/narcissus-exec.js create mode 100644 tracks/vendor/selenium/scripts/narcissus-parse.js create mode 100644 tracks/vendor/selenium/scripts/se2html.js create mode 100644 tracks/vendor/selenium/scripts/selenium-api.js create mode 100644 tracks/vendor/selenium/scripts/selenium-browserbot.js create mode 100644 tracks/vendor/selenium/scripts/selenium-browserdetect.js create mode 100644 tracks/vendor/selenium/scripts/selenium-commandhandlers.js create mode 100644 tracks/vendor/selenium/scripts/selenium-executionloop.js create mode 100644 tracks/vendor/selenium/scripts/selenium-logging.js create mode 100644 tracks/vendor/selenium/scripts/selenium-remoterunner.js create mode 100644 tracks/vendor/selenium/scripts/selenium-testrunner.js create mode 100644 tracks/vendor/selenium/scripts/selenium-version.js create mode 100644 tracks/vendor/selenium/scripts/user-extensions.js.sample create mode 100644 tracks/vendor/selenium/scripts/xmlextras.js create mode 100644 tracks/vendor/selenium/selenium-logo.png create mode 100644 tracks/vendor/selenium/selenium-test.css create mode 100644 tracks/vendor/selenium/selenium.css create mode 100644 tracks/vendor/selenium/xpath/dom.js create mode 100644 tracks/vendor/selenium/xpath/misc.js create mode 100644 tracks/vendor/selenium/xpath/xpath.js diff --git a/tracks/README_DEVELOPERS b/tracks/README_DEVELOPERS new file mode 100644 index 00000000..5849fdc0 --- /dev/null +++ b/tracks/README_DEVELOPERS @@ -0,0 +1,11 @@ +To run selenium tests, start Tracks in test mode using + + script/server -e test + +Then open a browser to + + http://localhost:3000/selenium/ + +and interact with the test runner. + +For more information about Selenium on Rails, see vendor/plugins/selenium-on-rails/README \ No newline at end of file diff --git a/tracks/app/controllers/todo_controller.rb b/tracks/app/controllers/todo_controller.rb index 1df7dfe5..0a3c1f19 100644 --- a/tracks/app/controllers/todo_controller.rb +++ b/tracks/app/controllers/todo_controller.rb @@ -100,20 +100,25 @@ class TodoController < ApplicationController @item = check_user_return_item @item.toggle_completion() @saved = @item.save - if @saved - @remaining_undone_in_context = @user.contexts.find(@item.context_id).not_done_todo_count - determine_down_count - determine_completed_count - end - return if request.xhr? - - if @saved - # TODO: I think this will work, but can't figure out how to test it - notify :notice, "The action '#{@item.description}' was marked as #{@item.completed? ? 'complete' : 'incomplete' }" - redirect_to :action => "index" - else - notify :notice, "The action '#{@item.description}' was NOT marked as #{@item.completed? ? 'complete' : 'incomplete' } due to an error on the server.", "index" - redirect_to :action => "index" + respond_to do |format| + format.js do + if @saved + @remaining_undone_in_context = @user.contexts.find(@item.context_id).not_done_todo_count + determine_down_count + determine_completed_count + end + render + end + format.html do + if @saved + # TODO: I think this will work, but can't figure out how to test it + notify :notice, "The action '#{@item.description}' was marked as #{@item.completed? ? 'complete' : 'incomplete' }" + redirect_to :action => "index" + else + notify :notice, "The action '#{@item.description}' was NOT marked as #{@item.completed? ? 'complete' : 'incomplete' } due to an error on the server.", "index" + redirect_to :action => "index" + end + end end end diff --git a/tracks/app/helpers/todo_helper.rb b/tracks/app/helpers/todo_helper.rb index 2a43a0aa..fb9eb102 100644 --- a/tracks/app/helpers/todo_helper.rb +++ b/tracks/app/helpers/todo_helper.rb @@ -96,9 +96,20 @@ module TodoHelper def item_container_id return "tickler-items" if source_view_is :deferred - return "p#{@item.project_id}" if source_view_is :project + if source_view_is :project + return "p#{@item.project_id}" if @item.active? + return "tickler" if @item.deferred? + end return "c#{@item.context_id}" end + + def should_show_new_item + return true if source_view_is(:deferred) && @item.deferred? + return true if source_view_is(:project) && @item.project.hidden? && @item.project_hidden? + return true if source_view_is(:project) && @item.deferred? + return true if !source_view_is(:deferred) && @item.active? + return false + end def parent_container_type return 'tickler' if source_view_is :deferred @@ -107,10 +118,10 @@ module TodoHelper end def empty_container_msg_div_id + return "tickler-empty-nd" if source_view_is(:project) && @item.deferred? return "p#{@item.project_id}empty-nd" if source_view_is :project - return "c#{@item.context_id}empty-nd" if source_view_is :context return "tickler-empty-nd" if source_view_is :deferred - nil + return "c#{@item.context_id}empty-nd" end def project_names_for_autocomplete diff --git a/tracks/app/views/todo/create.rjs b/tracks/app/views/todo/create.rjs index 3ed6732b..811fcb5c 100644 --- a/tracks/app/views/todo/create.rjs +++ b/tracks/app/views/todo/create.rjs @@ -9,11 +9,7 @@ if @saved page.send :record, "Form.reset('todo-form-new-action');Form.focusFirstElement('todo-form-new-action')" page << "contextAutoCompleter.options.array = #{context_names_for_autocomplete}" if @new_context_created page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}" if @new_project_created - show_new_item = false - show_new_item = true if source_view_is(:deferred) && @item.deferred? - show_new_item = true if source_view_is(:project) && @item.project.hidden? && @item.project_hidden? - show_new_item = true if !source_view_is(:deferred) && @item.active? - if show_new_item + if should_show_new_item() if @new_context_created page.insert_html :top, 'display_box', :partial => 'context/context', :locals => { :context => @item.context, :collapsible => true } else diff --git a/tracks/app/views/todo/toggle_check.rjs b/tracks/app/views/todo/toggle_check.rjs index 8b64cec3..1faff081 100644 --- a/tracks/app/views/todo/toggle_check.rjs +++ b/tracks/app/views/todo/toggle_check.rjs @@ -6,7 +6,8 @@ if @saved page.insert_html :top, "completed", :partial => 'todo/item', :locals => { :parent_container_type => "completed" } page.visual_effect :highlight, dom_id(@item, 'line'), {'startcolor' => "'#99ff99'"} page[empty_container_msg_div_id].show if @down_count == 0 && !empty_container_msg_div_id.nil? - page.hide "empty-d" # If we've checked something as done, completed items can't be empty + page.show 'tickler-empty-nd' if source_view_is(:project) + page.hide 'empty-d' # If we've checked something as done, completed items can't be empty end if @remaining_undone_in_context == 0 && source_view_is(:todo) page.visual_effect :fade, item_container_id, :duration => 0.4 @@ -19,7 +20,7 @@ if @saved page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? # If we've checked something as undone, uncompleted items can't be empty end page.hide "status" - page.replace_html "badge_count", @down_count + page.replace_html "badge_count", @down_count else page.replace_html "status", content_tag("div", content_tag("h2", "#{pluralize(@item.errors.count, "error")} prohibited this record from being saved") + content_tag("p", "There were problems with the following fields:") + content_tag("ul", @item.errors.each_full { |msg| content_tag("li", msg) }), "id" => "errorExplanation", "class" => "errorExplanation") end \ No newline at end of file diff --git a/tracks/app/views/todo/update.rjs b/tracks/app/views/todo/update.rjs index 926a1a0a..5ed32330 100644 --- a/tracks/app/views/todo/update.rjs +++ b/tracks/app/views/todo/update.rjs @@ -33,10 +33,16 @@ if @saved page.visual_effect :highlight, dom_id(@item), :duration => 3 end elsif source_view_is :project - if @project_changed || @item.deferred? + if @project_changed page[@item].remove page.show("p#{@original_item_project_id}empty-nd") if (@remaining_undone_in_project == 0) page.replace_html "badge_count", @remaining_undone_in_project + elsif @item.deferred? + page[@item].remove + page.show("p#{@original_item_project_id}empty-nd") if (@remaining_undone_in_project == 0) + page.insert_html :bottom, "tickler", :partial => 'todo/item', :locals => { :parent_container_type => parent_container_type } + page['tickler-empty-nd'].hide + page.replace_html "badge_count", @remaining_undone_in_project else page.replace dom_id(@item), :partial => 'todo/item', :locals => { :parent_container_type => parent_container_type } page.visual_effect :highlight, dom_id(@item), :duration => 3 diff --git a/tracks/test/selenium/_login.rsel b/tracks/test/selenium/_login.rsel new file mode 100644 index 00000000..2ba7b163 --- /dev/null +++ b/tracks/test/selenium/_login.rsel @@ -0,0 +1,5 @@ +open :controller => 'login', :action => 'logout' +open :controller => 'login' +type "user_login", username +type "user_password", password +click_and_wait "login" \ No newline at end of file diff --git a/tracks/test/selenium/create_deferred_todo_on_project_page.rsel b/tracks/test/selenium/create_deferred_todo_on_project_page.rsel new file mode 100644 index 00000000..527b43cf --- /dev/null +++ b/tracks/test/selenium/create_deferred_todo_on_project_page.rsel @@ -0,0 +1,8 @@ +setup :fixtures => :all +include_partial 'login', :username => 'admin', :password => 'abracadabra' +open "/project/Build_a_working_time_machine" +type "todo_description", "choose era" +type "todo_show_from", "1/1/2030" +click "//input[@value='Add item']" +wait_for_element_present "xpath=//div[@id='tickler'] //div[@class='item-container']" +assert_not_visible "tickler-empty-nd" \ No newline at end of file diff --git a/tracks/test/selenium/defer_todo_on_project_page.rsel b/tracks/test/selenium/defer_todo_on_project_page.rsel new file mode 100644 index 00000000..040a63ac --- /dev/null +++ b/tracks/test/selenium/defer_todo_on_project_page.rsel @@ -0,0 +1,9 @@ +setup :fixtures => :all +include_partial 'login', :username => 'admin', :password => 'abracadabra' +open "/project/Build_a_working_time_machine" +click "edit_icon_todo_5" +wait_for_element_present "show_from_todo_5" +type "show_from_todo_5", "1/1/2030" +click "//input[@value='Update']" +wait_for_element_present "xpath=//div[@id='tickler'] //div[@id='todo_5']" +assert_not_visible "tickler-empty-nd" diff --git a/tracks/test/selenium/login_failure.rsel b/tracks/test/selenium/login_failure.rsel new file mode 100644 index 00000000..4b1240b4 --- /dev/null +++ b/tracks/test/selenium/login_failure.rsel @@ -0,0 +1,8 @@ + open :controller => 'login', :action => 'logout' + open :controller => 'login' + assert_title 'exact:TRACKS::Login' + type "user_login", "admin" + type "user_password", "incorrect_password" + click_and_wait "login" + assert_title 'exact:TRACKS::Login' + verify_text_present 'Login unsuccessful' diff --git a/tracks/test/selenium/login_success.rsel b/tracks/test/selenium/login_success.rsel new file mode 100644 index 00000000..a218872f --- /dev/null +++ b/tracks/test/selenium/login_success.rsel @@ -0,0 +1,8 @@ + + open :controller => 'login', :action => 'logout' + open :controller => 'login' + assert_title 'exact:TRACKS::Login' + type "user_login", "admin" + type "user_password", "abracadabra" + click_and_wait "login" + assert_title 'exact:TRACKS::List tasks' \ No newline at end of file diff --git a/tracks/test/selenium/mark_last_deferred_todo_complete_on_project_page.rsel b/tracks/test/selenium/mark_last_deferred_todo_complete_on_project_page.rsel new file mode 100644 index 00000000..4b836a1e --- /dev/null +++ b/tracks/test/selenium/mark_last_deferred_todo_complete_on_project_page.rsel @@ -0,0 +1,10 @@ +setup :fixtures => :all +include_partial 'login', :username => 'admin', :password => 'abracadabra' +open "/project/Build_a_working_time_machine" +type "todo_description", "choose era" +type "todo_show_from", "1/1/2030" +click "//input[@value='Add item']" +wait_for_element_present "xpath=//div[@id='tickler'] //div[@class='item-container']" +open "/project/Build_a_working_time_machine" +click "xpath=//div[@id='tickler'] //input[@class='item-checkbox']" +wait_for_visible "tickler-empty-nd" diff --git a/tracks/test/selenium/mark_todo_complete_1.rsel b/tracks/test/selenium/mark_todo_complete_1.rsel new file mode 100644 index 00000000..052e2c43 --- /dev/null +++ b/tracks/test/selenium/mark_todo_complete_1.rsel @@ -0,0 +1,4 @@ +setup :fixtures => :all +include_partial 'login', :username => 'admin', :password => 'abracadabra' +click "xpath=//div[@id='c1'] //div[@id='todo_9'] //input[@class='item-checkbox']" +wait_for_element_present "xpath=//div[@id='completed'] //div[@id='todo_9']" diff --git a/tracks/test/selenium/mark_todo_complete_2.rsel b/tracks/test/selenium/mark_todo_complete_2.rsel new file mode 100644 index 00000000..2388009d --- /dev/null +++ b/tracks/test/selenium/mark_todo_complete_2.rsel @@ -0,0 +1,5 @@ +setup :fixtures => :all +include_partial 'login', :username => 'admin', :password => 'abracadabra' +click "xpath=//div[@id='c5'] //div[@id='todo_5'] //input[@class='item-checkbox']" +wait_for_element_present "xpath=//div[@id='completed'] //div[@id='todo_5']" +wait_for_not_visible 'c5' diff --git a/tracks/test/selenium/mark_todo_incomplete.rsel b/tracks/test/selenium/mark_todo_incomplete.rsel new file mode 100644 index 00000000..2b76901d --- /dev/null +++ b/tracks/test/selenium/mark_todo_incomplete.rsel @@ -0,0 +1,5 @@ +setup :fixtures => :all +include_partial 'login', :username => 'admin', :password => 'abracadabra' +click "xpath=//div[@id='completed'] //div[@id='todo_3'] //input[@class='item-checkbox']" +wait_for_element_present "xpath=//div[@id='c4'] //div[@id='todo_3']" +assert_not_visible "c4empty-nd" diff --git a/tracks/vendor/plugins/selenium-on-rails/LICENSE-2.0.txt b/tracks/vendor/plugins/selenium-on-rails/LICENSE-2.0.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/LICENSE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/tracks/vendor/plugins/selenium-on-rails/README b/tracks/vendor/plugins/selenium-on-rails/README new file mode 100644 index 00000000..75a43918 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/README @@ -0,0 +1,192 @@ += Selenium on Rails + +== Overview + +Selenium on Rails provides an easy way to test Rails application with +SeleniumCore[http://www.openqa.org/selenium-core/]. + +This plugin does four things: +1. The Selenium Core files don't have to pollute /public, they can stay in the Selenium gem or in /vendor/selenium. +2. No need to create suite files, they are generated on the fly -- one suite per directory in /test/selenium (suites can be nested). +3. Instead of writing the test cases in HTML you can use a number of better formats (see Formats). +4. Loading of fixtures and wiping of session (/selenium/setup). + +== Installation + +1. Selenium Core needs to be available. It could either be installed as a gem (gem install selenium) or in /vendor/selenium/. +2. Install Selenium on Rails: script/plugin install http://svn.openqa.org/svn/selenium-on-rails/selenium-on-rails +3. If RedCloth is available the Selenese test cases can use it for better markup. +4. Run the Rakefile in the plugin's directory to run the tests in order to see that everything works. (If RedCloth isn't installed a few tests will fail since they assume RedCloth is installed.) +5. Create a test case: script/generate selenium login +6. Start the server: script/server -e test +7. Point your browser to http://localhost:3000/selenium +8. If everything works as expected you should see the Selenium test runner. The north east frame contains all your test cases (just one for now), and the north frame contains your test case. + +=== win32-open3 + +win32-open3[http://raa.ruby-lang.org/project/win32-open3/] is needed if you're +on Windows and want to run your tests as a Rake task +(see test:acceptance), i.e. you don't have to install it but it's +recommended. + +You can build it from source or install the binary: + +1. Download the latest version of win32-open3, open3-0.2.2.so[http://rubyforge.org/frs/download.php/8515/open3-0.2.2.so] at the time of this writing. +2. Open up irb and run this snippet: require 'rbconfig'; include Config; puts CONFIG['sitearchdir'] +3. Create a win32 directory under the directory you got, e.g. c:\ruby\lib\ruby\site_ruby\1.8\i386-msvcrt +4. Rename the .so file to open3.so and put it in the win32 directory. +5. Profit! (unless you get an error when doing require 'win32/open3') + +== Formats + +The test cases can be written in a number of formats. Which one you choose is a +matter of taste. You can generate your test files by running +script/generate selenium or by creating them manually in your +/test/selenium directory. + +=== Selenese, .sel + +Selenese is the dumbest format (in a good way). You just write your commands +delimited by | characters. + + |open|/selenium/setup| + |open|/| + |goBack| + +If you don't want to write Selenese tests by hand you can use +SeleniumIDE[http://www.openqa.org/selenium-ide/] which has +support[http://wiki.openqa.org/display/SIDE/SeleniumOnRails] for Selenese. + +SeleniumIDE makes it super easy to record test and edit them. + +=== RSelenese, .rsel + +RSelenese enable you to write your tests in Ruby. + + setup :fixtures => :all + open '/' + assert_title 'Home' + ('a'..'z').each {|c| open :controller => 'user', :action => 'create', :name => c } + +See SeleniumOnRails::TestBuilder for available commands. + +=== HTML/RHTML + +You can write your tests in HTML/RHTML but that's mostly useful if you have +existing tests you want to reuse. + +=== Partial test cases + +If you have some common actions you want to do in several test cases you can put +them in a separate partial test case and include them in your other test cases. + +A partial test case is just like a normal test case besides that its filename +has to start with _: + + #_login.rsel + open '/login' + type 'name', name + type 'password', password + click 'submit', :wait=>true + +To include a partial test case you write like this in a Selenese test case: + + |includePartial|login|name=John Doe|password=eoD nhoJ| + +in a RSelenese test case: + + include_partial 'login', :name => 'Jane Doe', :password => 'Jane Doe'.reverse + +and in a RHTML test case: + + <%= render :partial => 'login', :locals => {:name = 'Joe Schmo', :password => 'Joe Schmo'.reverse} %> + +== Configuration + +There are a number of settings available. You make them by renaming +config.yml.example to config.yml and make your changes in that +file. + +=== Environments + +Per default this plugin is only available in test environment. You can change +this by setting environments, such as: + + #config.yml + environments: + - test + - development + +== test:acceptance + +You can run all your Selenium tests as a Rake task. + +First, if you're on Windows, you have to make sure win32-open3 is installed. +Then you have to configure which browsers you want to run, like this: + + #config.yml + browsers: + firefox: 'c:\Program Files\Mozilla Firefox\firefox.exe' + ie: 'c:\Program Files\Internet Explorer\iexplore.exe' + +Now you're all set. First start a server: + + script/server -e test + +Then run the tests: + + rake test:acceptance + +Now it should work, otherwise let me know! + +=== Store results + +If you want to store the results from a test:acceptance you just need +to set in which directory they should be stored: + + #config.yml + result_dir: 'c:\result' + +So when you run rake test:acceptance the tables with the results will +be stored as .html files in that directory. + +This can be useful especially for continous integration. + +== Todo + +=== Standalone mode + +More work is needed on test:acceptance on Windows to be able to start +the server when needed. + +=== user_extension.js + +Selenium has support for user_extension.js which is a way to extend the +functionality of Selenium Core. However there is currently no easy way to add +such a file in Selenium on Rails. + +=== More setup/teardown support? + +Currently there is only support to load fixtures and to wipe the session in +/selenium/setup. Is there a need for more kinds of setups or teardowns? + +=== More documentation + + +== Not todo + +=== Editor + +Creating an editor for the test cases is currently considered out of scope for +this plugin. SeleniumIDE[http://www.openqa.org/selenium-ide/] does such a good +job and has support[http://wiki.openqa.org/display/SIDE/SeleniumOnRails] for +the Selenese format. + +== Credits + +* Jon Tirsen, http://jutopia.tirsen.com -- initial inspiration[http://wiki.rubyonrails.com/rails/pages/SeleniumIntegration] +* Eric Kidd, http://www.randomhacks.net -- contribution of RSelenese + +== Information + +For more information, check out the website[http://www.openqa.org/selenium-on-rails/]. diff --git a/tracks/vendor/plugins/selenium-on-rails/Rakefile b/tracks/vendor/plugins/selenium-on-rails/Rakefile new file mode 100644 index 00000000..fbd733d9 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/Rakefile @@ -0,0 +1,27 @@ +require 'rake' +require 'rake/testtask' +require 'rdoc/rdoc' + +desc 'Default: run unit tests.' +task :default => :test + +desc 'Test the Selenium on Rails plugin.' +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.pattern = 'test/**/*_test.rb' + t.verbose = true +end + +desc 'Generate documentation for the Selenium on Rails plugin.' +task :rdoc do + rm_rf 'doc' + RDoc::RDoc.new.document(%w(--line-numbers --inline-source --title SeleniumOnRails README lib)) +end + +begin + require 'rcov/rcovtask' + Rcov::RcovTask.new do |t| + t.test_files = FileList['test/*_test.rb'] + end +rescue LoadError #if rcov isn't available, ignore +end diff --git a/tracks/vendor/plugins/selenium-on-rails/config.yml b/tracks/vendor/plugins/selenium-on-rails/config.yml new file mode 100644 index 00000000..17746296 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/config.yml @@ -0,0 +1,27 @@ +# Rename this file to config.yml in order to configure the plugin + +# +# General settings +# + +environments: + - test +# - development # Uncomment this line to enable in development environment. N.B. your development database will likely be altered/destroyed/abducted + +#selenium_path: 'c:\selenium' #path to selenium installation. only needed when selenium isn't installed in /vendor/selenium or as a gem + +# +# rake test:acceptance settings +# + +browsers: + firefox: 'c:\Program Files\Mozilla Firefox\firefox.exe' + ie: 'c:\Program Files\Internet Explorer\iexplore.exe' + +#host: 'localhost' +#port_start: 3000 +#port_end: 3005 +#max_browser_duration: 120 +#multi_window: false + +#result_dir: 'c:\result' # the directory where the results will be stored after a test:acceptance run diff --git a/tracks/vendor/plugins/selenium-on-rails/config.yml.example b/tracks/vendor/plugins/selenium-on-rails/config.yml.example new file mode 100644 index 00000000..17746296 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/config.yml.example @@ -0,0 +1,27 @@ +# Rename this file to config.yml in order to configure the plugin + +# +# General settings +# + +environments: + - test +# - development # Uncomment this line to enable in development environment. N.B. your development database will likely be altered/destroyed/abducted + +#selenium_path: 'c:\selenium' #path to selenium installation. only needed when selenium isn't installed in /vendor/selenium or as a gem + +# +# rake test:acceptance settings +# + +browsers: + firefox: 'c:\Program Files\Mozilla Firefox\firefox.exe' + ie: 'c:\Program Files\Internet Explorer\iexplore.exe' + +#host: 'localhost' +#port_start: 3000 +#port_end: 3005 +#max_browser_duration: 120 +#multi_window: false + +#result_dir: 'c:\result' # the directory where the results will be stored after a test:acceptance run diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumController.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumController.html new file mode 100644 index 00000000..c8bcc34d --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumController.html @@ -0,0 +1,265 @@ + + + + + + Class: SeleniumController + + + + + + + + + + +
+ + + + + + + + + + + + + + +
ClassSeleniumController
In: + + lib/controllers/selenium_controller.rb + +
+
Parent: + ActionController::Base +
+
+ + +
+ + + +
+ + + +
+ +
+

Methods

+ +
+ record   + setup   + support_file   + test_file   +
+
+ +
+ + + +
+

Included Modules

+ + +
+ +
+ + + + + + + + + +
+

Public Instance methods

+ +
+ + + + +
+

[Source]

+
+
+    # File lib/controllers/selenium_controller.rb, line 50
+50:   def record
+51:     dir = record_table
+52: 
+53:     @result = {'resultDir' => dir}
+54:     for p in ['result', 'numTestFailures', 'numTestPasses', 'numCommandFailures', 'numCommandPasses', 'numCommandErrors', 'totalTime']
+55:       @result[p] = params[p]
+56:     end
+57:     File.open(log_path(params[:logFile] || 'default.yml'), 'w') {|f| YAML.dump(@result, f)}
+58:     
+59:     render :file => view_path('record.rhtml'), :layout => layout_path
+60:   end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/controllers/selenium_controller.rb, line 7
+ 7:   def setup
+ 8:     unless params.has_key? :keep_session
+ 9:       reset_session
+10:       @session_wiped = true
+11:     end
+12:     @cleared_tables = clear_tables params[:clear_tables].to_s
+13:     @loaded_fixtures = load_fixtures params[:fixtures].to_s
+14:     render :file => view_path('setup.rhtml'), :layout => layout_path
+15:   end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/controllers/selenium_controller.rb, line 34
+34:   def support_file
+35:     if params[:filename].empty?
+36:       redirect_to :filename => 'TestRunner.html', :test => 'tests'
+37:       return
+38:     end
+39: 
+40:     filename = File.join selenium_path, params[:filename]
+41:     if File.file? filename
+42:       type = WEBrick::HTTPUtils::DefaultMimeTypes[$1.downcase] if filename =~ /\.(\w+)$/
+43:       type ||= 'text/html'
+44:       send_file filename, :type => type, :disposition => 'inline', :stream => false
+45:     else
+46:       render :text => 'Not found', :status => 404
+47:     end
+48:   end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/controllers/selenium_controller.rb, line 17
+17:   def test_file
+18:     params[:testname] = '' if params[:testname].to_s == 'TestSuite.html'
+19:     filename = File.join selenium_tests_path, params[:testname]
+20:     if File.directory? filename
+21:       @suite_path = filename
+22:       render :file => view_path('test_suite.rhtml'), :layout => layout_path
+23:     elsif File.readable? filename
+24:       render_test_case filename
+25:     else
+26:       if File.directory? selenium_tests_path
+27:         render :text => 'Not found', :status => 404
+28:       else
+29:         render :text => "Did not find the Selenium tests path (#{selenium_tests_path}). Run script/generate selenium", :status => 404
+30:       end
+31:     end
+32:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumHelper.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumHelper.html new file mode 100644 index 00000000..5463a38e --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumHelper.html @@ -0,0 +1,148 @@ + + + + + + Module: SeleniumHelper + + + + + + + + + + +
+ + + + + + + + + + +
ModuleSeleniumHelper
In: + + lib/selenium_helper.rb + +
+
+
+ + +
+ + + +
+ + + +
+ +
+

Methods

+ +
+ test_case_name   +
+
+ +
+ + + +
+

Included Modules

+ + +
+ +
+ + + + + + + + + +
+

Public Instance methods

+ +
+ + + + +
+

[Source]

+
+
+   # File lib/selenium_helper.rb, line 5
+5:   def test_case_name filename
+6:     File.basename(filename).sub(/\..*/,'').humanize
+7:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails.html new file mode 100644 index 00000000..34015fde --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails.html @@ -0,0 +1,124 @@ + + + + + + Module: SeleniumOnRails + + + + + + + + + + +
+ + + + + + + + + + +
ModuleSeleniumOnRails
In: + + lib/selenium_on_rails/paths.rb + +
+ + lib/selenium_on_rails.rb + +
+
+
+ + +
+ + + +
+ + + +
+ + +
+ + + + +
+ + + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/FixtureLoader.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/FixtureLoader.html new file mode 100644 index 00000000..866e8904 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/FixtureLoader.html @@ -0,0 +1,231 @@ + + + + + + Module: SeleniumOnRails::FixtureLoader + + + + + + + + + + +
+ + + + + + + + + + +
ModuleSeleniumOnRails::FixtureLoader
In: + + lib/selenium_on_rails/fixture_loader.rb + +
+
+
+ + +
+ + + +
+ + + +
+ +
+

Methods

+ + +
+ +
+ + + +
+

Included Modules

+ + +
+ +
+ + + + + + + + + +
+

Public Instance methods

+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/fixture_loader.rb, line 6
+ 6:   def available_fixtures
+ 7:     fixtures = {}
+ 8:     path = fixtures_path + '/'
+ 9:     files = Dir["#{path}**/*.{yml,csv}"]
+10:     files.each do |file|
+11:       rel_path = file.sub(path, '')
+12:       next if skip_file? rel_path
+13:       fixture_set = File.dirname(rel_path)
+14:       fixture_set = '' if fixture_set == '.'
+15:       fixture = rel_path.sub /\.[^.]*$/, ''
+16:       fixtures[fixture_set] ||= []
+17:       fixtures[fixture_set] << fixture
+18:     end
+19:     
+20:     fixtures
+21:   end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/fixture_loader.rb, line 45
+45:   def clear_tables tables
+46:     table_names = tables.split /\s*,\s*/
+47:     connection = ActiveRecord::Base.connection 
+48:     table_names.each do |table|
+49:       connection.execute "DELETE FROM #{table}" 
+50:     end
+51:     table_names
+52:   end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/fixture_loader.rb, line 23
+23:   def load_fixtures fixtures_param
+24:     available = nil
+25:     fixtures = fixtures_param.split(/\s*,\s*/).collect do |f|
+26:       fixture_set = File.dirname f
+27:       fixture_set = '' if fixture_set == '.'
+28:       fixture = File.basename f
+29:       if fixture == 'all'
+30:         available ||= available_fixtures
+31:         available[fixture_set]
+32:       else
+33:         f
+34:       end
+35:     end
+36:     fixtures.flatten!
+37:     fixtures.reject! {|f| f.blank? }
+38: 
+39:     if fixtures.any?
+40:       Fixtures.create_fixtures fixtures_path, fixtures
+41:     end
+42:     fixtures
+43:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/PartialsSupport.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/PartialsSupport.html new file mode 100644 index 00000000..77cff331 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/PartialsSupport.html @@ -0,0 +1,195 @@ + + + + + + Module: SeleniumOnRails::PartialsSupport + + + + + + + + + + +
+ + + + + + + + + + +
ModuleSeleniumOnRails::PartialsSupport
In: + + lib/selenium_on_rails/partials_support.rb + +
+
+
+ + +
+ + + +
+ +
+

+Provides partials support to test cases so they can include other partial +test cases. +

+

+The partial’s commands are returned as html table rows. +

+ +
+ + +
+ +
+

Methods

+ + +
+ +
+ + + +
+

Included Modules

+ + +
+ +
+ + + + + + + + + +
+

Public Instance methods

+ +
+ + + + +
+

+Extracts the commands from a partial. The partial must contain a html table +and the first row is ignored since it cannot contain a command. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/partials_support.rb, line 19
+19:   def extract_commands_from_partial partial
+20:     partial = partial.match(/.*<table>.*?<tr>.*?<\/tr>(.*?)<\/table>/im)[1]
+21:     raise "Partial '#{name}' doesn't contain any table" unless partial
+22:     partial
+23:   end
+
+
+
+
+ +
+ + + + +
+

+Overrides where the partial is searched for, and returns only the command +table rows. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/partials_support.rb, line 9
+ 9:   def render_partial partial_path = default_template_name, object = nil, local_assigns = nil, status = nil
+10:     pattern = partial_pattern partial_path
+11:     filename = Dir[pattern].first
+12:     raise "Partial '#{partial_path}' cannot be found! (Looking for file: '#{pattern}')" unless filename
+13:     partial = render :file => filename, :use_full_path => false, :locals => local_assigns
+14:     extract_commands_from_partial partial
+15:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Paths.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Paths.html new file mode 100644 index 00000000..1d1db9bb --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Paths.html @@ -0,0 +1,295 @@ + + + + + + Module: SeleniumOnRails::Paths + + + + + + + + + + +
+ + + + + + + + + + +
ModuleSeleniumOnRails::Paths
In: + + lib/selenium_on_rails/paths.rb + +
+
+
+ + +
+ + + +
+ + + +
+ +
+

Methods

+ + +
+ +
+ + + + +
+ + + + + + + + + +
+

Public Instance methods

+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/paths.rb, line 25
+25:     def fixtures_path
+26:       File.expand_path File.join(RAILS_ROOT, 'test/fixtures')
+27:     end
+
+
+
+
+ +
+ + + + +
+

+Returns the path to the layout template. The path is relative in relation +to the app/views/ directory since Rails doesn’t support absolute +paths to layout templates. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/paths.rb, line 19
+19:     def layout_path
+20:       rails_root = Pathname.new File.expand_path(File.join(RAILS_ROOT, 'app/views'))
+21:       view_path = Pathname.new view_path('layout')
+22:       view_path.relative_path_from(rails_root).to_s
+23:     end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/paths.rb, line 29
+29:     def log_path log_file
+30:       File.expand_path(File.dirname(__FILE__) + '/../../log/' + log_file)
+31:     end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+   # File lib/selenium_on_rails/paths.rb, line 3
+3:     def selenium_path
+4:       @@selenium_path ||= find_selenium_path
+5:       @@selenium_path
+6:     end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/paths.rb, line 8
+ 8:     def selenium_tests_path
+ 9:       File.expand_path(File.join(RAILS_ROOT, 'test/selenium'))
+10:     end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/paths.rb, line 33
+33:     def skip_file? file
+34:       file.split('/').each do |f|
+35:         return true if f.upcase == 'CVS' or f.starts_with?('.') or f.ends_with?('~') or f.starts_with?('_')
+36:       end
+37:       false
+38:     end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/paths.rb, line 12
+12:     def view_path view
+13:       File.expand_path(File.dirname(__FILE__) + '/../views/' + view)
+14:     end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/RSelenese.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/RSelenese.html new file mode 100644 index 00000000..e8346742 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/RSelenese.html @@ -0,0 +1,219 @@ + + + + + + Class: SeleniumOnRails::RSelenese + + + + + + + + + + +
+ + + + + + + + + + + + + + +
ClassSeleniumOnRails::RSelenese
In: + + lib/selenium_on_rails/rselenese.rb + +
+
Parent: + + SeleniumOnRails::TestBuilder + +
+
+ + +
+ + + +
+ +
+

+Renders Selenium test templates in a fashion analogous to rxml and +rjs templates. +

+
+  setup
+  open :controller => 'customer', :action => 'list'
+  assert_title 'Customers'
+
+

+See SeleniumOnRails::TestBuilder for a list +of available commands. +

+ +
+ + +
+ +
+

Methods

+ +
+ new   + render   +
+
+ +
+ + + + +
+ + + + + +
+

Attributes

+ +
+ + + + + + +
view [RW] 
+
+
+ + + + +
+

Public Class methods

+ +
+ + + + +
+

+Create a new RSelenese renderer bound to +view. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/rselenese.rb, line 17
+17:   def initialize view
+18:     super view
+19:     @view = view
+20:   end
+
+
+
+
+ +

Public Instance methods

+ +
+ + + + +
+

+Render template using local_assigns. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/rselenese.rb, line 23
+23:   def render template, local_assigns
+24:     title = (@view.assigns['page_title'] or local_assigns['page_title'])
+25:     table(title) do
+26:       test = self #to enable test.command
+27: 
+28:       assign_locals_code = ''
+29:       local_assigns.each_key {|key| assign_locals_code << "#{key} = local_assigns[#{key.inspect}];"}
+30: 
+31:       eval assign_locals_code + "\n" + template
+32:     end
+33:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Renderer.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Renderer.html new file mode 100644 index 00000000..593ef6bd --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Renderer.html @@ -0,0 +1,156 @@ + + + + + + Module: SeleniumOnRails::Renderer + + + + + + + + + + +
+ + + + + + + + + + +
ModuleSeleniumOnRails::Renderer
In: + + lib/selenium_on_rails/renderer.rb + +
+
+
+ + +
+ + + +
+ + + +
+ +
+

Methods

+ + +
+ +
+ + + +
+

Included Modules

+ + +
+ +
+ + + + + + + + + +
+

Public Instance methods

+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/renderer.rb, line 5
+ 5:   def render_test_case filename
+ 6:     @template.extend SeleniumOnRails::PartialsSupport
+ 7:     @page_title = test_case_name filename
+ 8:     output = render_to_string :file => filename
+ 9:     layout = (output =~ /<html>/i ? false : layout_path)
+10:     render :text => output, :layout => layout
+11: 
+12:     headers['Cache-control'] = 'no-cache'
+13:     headers['Pragma'] = 'no-cache'
+14:     headers['Expires'] = '-1'
+15:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Selenese.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Selenese.html new file mode 100644 index 00000000..1c51f5cf --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Selenese.html @@ -0,0 +1,179 @@ + + + + + + Class: SeleniumOnRails::Selenese + + + + + + + + + + +
+ + + + + + + + + + + + + + +
ClassSeleniumOnRails::Selenese
In: + + lib/selenium_on_rails/selenese.rb + +
+
Parent: + Object +
+
+ + +
+ + + +
+ + + +
+ +
+

Methods

+ +
+ new   + render   +
+
+ +
+ + + + +
+ + + + + + + + + +
+

Public Class methods

+ +
+ + + + +
+

[Source]

+
+
+   # File lib/selenium_on_rails/selenese.rb, line 7
+7:   def initialize view
+8:     @view = view
+9:   end
+
+
+
+
+ +

Public Instance methods

+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/selenese.rb, line 11
+11:   def render template, local_assigns
+12:     name = (@view.assigns['page_title'] or local_assigns['page_title'])
+13:     lines = template.strip.split "\n"
+14:     html = ''
+15:     html << extract_comments(lines)
+16:     html << extract_commands(lines, name)
+17:     html << extract_comments(lines)
+18:     raise 'You cannot have comments in the middle of commands!' if next_line lines, :any
+19:     html
+20:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/SuiteRenderer.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/SuiteRenderer.html new file mode 100644 index 00000000..4c0dc0d6 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/SuiteRenderer.html @@ -0,0 +1,223 @@ + + + + + + Module: SeleniumOnRails::SuiteRenderer + + + + + + + + + + +
+ + + + + + + + + + +
ModuleSeleniumOnRails::SuiteRenderer
In: + + lib/selenium_on_rails/suite_renderer.rb + +
+
+
+ + +
+ + + +
+ + + +
+ +
+

Methods

+ + +
+ +
+ + + + +
+ + + + + + + + + +
+

Public Instance methods

+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/suite_renderer.rb, line 24
+24:   def link_to_test_case suite_name, filename
+25:     name = suite_name + test_case_name(filename)
+26:     link_to name, :action => :test_file, :testname => path_to_relative_url(filename).sub(/^\//,'')
+27:   end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/suite_renderer.rb, line 18
+18:   def test_cases path
+19:     tests = []
+20:     visit_all_tests path, '', nil, Proc.new {|n, p| tests << [n,p]}
+21:     tests
+22:   end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+   # File lib/selenium_on_rails/suite_renderer.rb, line 2
+2:   def test_suite_name path
+3:     return 'All test cases' if [nil, '/'].include? path_to_relative_url(path)
+4:     File.split(path)[-1].humanize
+5:   end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails/suite_renderer.rb, line 7
+ 7:   def test_suites path
+ 8:     suites = []
+ 9: 
+10:     parent_path = File.join(File.split(path).slice(0..-2)) #all but last
+11:     parent_path = path_to_relative_url parent_path
+12:     suites << ['..', parent_path] unless parent_path.nil?
+13: 
+14:     visit_all_tests path, '', Proc.new {|n, p| suites << [n,path_to_relative_url(p)]}, nil
+15:     suites
+16:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilder.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilder.html new file mode 100644 index 00000000..91529e68 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilder.html @@ -0,0 +1,372 @@ + + + + + + Class: SeleniumOnRails::TestBuilder + + + + + + + + + + +
+ + + + + + + + + + + + + + +
ClassSeleniumOnRails::TestBuilder
In: + + lib/selenium_on_rails/test_builder.rb + +
+
Parent: + Object +
+
+ + +
+ + + +
+ +
+

+Builds Selenium test table using a high-level Ruby interface. Normally +invoked through SeleniumOnRails::RSelenese. +

+

+See SeleniumOnRails::TestBuilderActions for +the available actions and SeleniumOnRails::TestBuilderAccessors +for the available checks. +

+

+For more information on the commands supported by TestBuilder, see the Selenium Commands +Documentation at release.openqa.org/selenium-core/nightly/reference.html. +

+ +
+ + +
+ +
+

Methods

+ +
+ command   + command_and_wait   + command_verbatim   + exactize   + make_command_waiting   + new   + selenize   + table   +
+
+ +
+ + + +
+

Included Modules

+ + +
+ +
+ + + + + + + + + +
+

Public Class methods

+ +
+ + + + +
+

+Create a new TestBuilder for view. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder.rb, line 26
+26:   def initialize view
+27:     @view = view
+28:     @output = ''
+29:     @xml = Builder::XmlMarkup.new :indent => 2, :target => @output
+30:   end
+
+
+
+
+ +
+ + + + +
+

+Convert str to a Selenium command name. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder.rb, line 15
+15:   def self.selenize str
+16:     str.camelize.gsub(/^[A-Z]/) {|s| s.downcase }
+17:   end
+
+
+
+
+ +

Public Instance methods

+ +
+ + + + +
+

+Add a new test command using cmd, target and +value. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder.rb, line 41
+41:   def command cmd, target=nil, value=nil
+42:     @xml.tr do
+43:       _tdata cmd
+44:       _tdata target
+45:       _tdata value
+46:     end
+47:   end
+
+
+
+
+ +
+ + + + +
+

+Same as command but add AndWait to the name of +cmd. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder.rb, line 52
+52:   def command_and_wait cmd, target=nil, value=nil
+53:     command_verbatim cmd.to_s + 'AndWait', target, value
+54:   end
+
+
+
+
+ +
+ + +
+ command_verbatim(cmd, target=nil, value=nil) +
+ +
+

+Alias for command +

+
+
+ +
+ + + + +
+

+Prepends pattern with ‘exact:’ if it would be +considered containing string-match pattern otherwise. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder.rb, line 21
+21:   def exactize pattern
+22:     pattern.include?(':') ? "exact:#{pattern}" : pattern
+23:   end
+
+
+
+
+ +
+ + + + +
+

+Re routes commands in the provided block to command_and_wait instead of command. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder.rb, line 58
+58:   def make_command_waiting
+59:     self.class.send :alias_method, :command, :command_and_wait
+60:     yield
+61:     self.class.send :alias_method, :command, :command_verbatim 
+62:   end
+
+
+
+
+ +
+ + + + +
+

+Add a new table of tests, and return the HTML. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder.rb, line 33
+33:   def table title
+34:     @xml.table do
+35:       @xml.tr do @xml.th(title, :colspan => 3) end
+36:       yield self
+37:     end
+38:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderAccessors.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderAccessors.html new file mode 100644 index 00000000..a81378eb --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderAccessors.html @@ -0,0 +1,1672 @@ + + + + + + Module: SeleniumOnRails::TestBuilderAccessors + + + + + + + + + + +
+ + + + + + + + + + +
ModuleSeleniumOnRails::TestBuilderAccessors
In: + + lib/selenium_on_rails/test_builder_accessors.rb + +
+
+
+ + +
+ + + +
+ +
+

+The accessors available for SeleniumOnRails::TestBuilder tests. +

+

+For each store_foo there’s assert_foo, +assert_not_foo, verify_foo, verify_not_foo, +wait_for_foo, wait_for_not_foo. +

+ +
+ + +
+ + + +
+ + + + +
+ + + + + + + + + +
+

Public Instance methods

+ +
+ + + + +
+

+Gets the absolute URL of the current page. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_absolute_location(pattern) + +
  • +
  • assert_not_absolute_location(pattern) + +
  • +
  • verify_absolute_location_present(pattern) + +
  • +
  • verify_not_absolute_location(pattern) + +
  • +
  • wait_for_absolute_location(pattern) + +
  • +
  • wait_for_not_absolute_location(pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 129
+129:   def store_absolute_location variable_name
+130:     command 'storeAbsoluteLocation', variable_name
+131:   end
+
+
+
+
+ +
+ + + + +
+

+Retrieves the message of a JavaScript alert generated during the previous +action, or fail if there were no alerts. +

+

+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. +

+

+NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert +dialog. +

+

+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. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_alert(pattern) + +
  • +
  • assert_not_alert(pattern) + +
  • +
  • verify_alert_present(pattern) + +
  • +
  • verify_not_alert(pattern) + +
  • +
  • wait_for_alert(pattern) + +
  • +
  • wait_for_not_alert(pattern) + +
  • +
+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_accessors.rb, line 66
+66:   def store_alert variable_name
+67:     command 'storeAlert', variable_name
+68:   end
+
+
+
+
+ +
+ + + + +
+

+Has an alert occurred? +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_alert_present + +
  • +
  • assert_alert_not_present + +
  • +
  • verify_alert_present + +
  • +
  • verify_alert_not_present + +
  • +
  • wait_for_alert_present + +
  • +
  • wait_for_alert_not_present + +
  • +
+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_accessors.rb, line 15
+15:   def store_alert_present variable_name
+16:     command 'storeAlertPresent', variable_name
+17:   end
+
+
+
+
+ +
+ + + + +
+

+Returns the IDs of all buttons on the page. +

+

+If a given button has no ID, it will appear as "" in this array. +

+

+The pattern for the automatically generated assertions can either +take an array or a pattern. +

+
+ assert_all_buttons ['but1', 'but2']
+ assert_all_buttons 'but?,but?*'
+
+

+Related Assertions, automatically generated: +

+
    +
  • assert_all_buttons(pattern) + +
  • +
  • assert_not_all_buttons(pattern) + +
  • +
  • verify_all_buttons(pattern) + +
  • +
  • verify_not_all_buttons(pattern) + +
  • +
  • wait_for_all_buttons(pattern) + +
  • +
  • wait_for_not_all_buttons(pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 400
+400:   def store_all_buttons variable_name
+401:     command 'storeAllButtons', variable_name
+402:   end
+
+
+
+
+ +
+ + + + +
+

+Returns the IDs of all input fields on the page. +

+

+If a given field has no ID, it will appear as "" in this array. +

+

+The pattern for the automatically generated assertions can either +take an array or a pattern. +

+
+ assert_all_fields ['field1', 'field2']
+ assert_all_fields 'field?,field?*'
+
+

+Related Assertions, automatically generated: +

+
    +
  • assert_all_fields(pattern) + +
  • +
  • assert_not_all_fields(pattern) + +
  • +
  • verify_all_fields(pattern) + +
  • +
  • verify_not_all_fields(pattern) + +
  • +
  • wait_for_all_fields(pattern) + +
  • +
  • wait_for_not_all_fields(pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 440
+440:   def store_all_fields variable_name
+441:     command 'storeAllFields', variable_name
+442:   end
+
+
+
+
+ +
+ + + + +
+

+Returns the IDs of all links on the page. +

+

+If a given link has no ID, it will appear as "" in this array. +

+

+The pattern for the automatically generated assertions can either +take an array or a pattern. +

+
+ assert_all_links ['link1', 'link2']
+ assert_all_links 'link?,link?*'
+
+

+Related Assertions, automatically generated: +

+
    +
  • assert_all_links(pattern) + +
  • +
  • assert_not_all_links(pattern) + +
  • +
  • verify_all_links(pattern) + +
  • +
  • verify_not_all_links(pattern) + +
  • +
  • wait_for_all_links(pattern) + +
  • +
  • wait_for_not_all_links(pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 420
+420:   def store_all_links variable_name
+421:     command 'storeAllLinks', variable_name
+422:   end
+
+
+
+
+ +
+ + + + +
+

+Gets the value of an element attribute. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_attribute(locator, attribute_name, pattern) + +
  • +
  • assert_not_attribute(locator, attribute_name, pattern) + +
  • +
  • verify_attribute_present(locator, attribute_name, pattern) + +
  • +
  • verify_not_attribute(locator, attribute_name, pattern) + +
  • +
  • wait_for_attribute(locator, attribute_name, pattern) + +
  • +
  • wait_for_not_attribute(locator, attribute_name, pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 322
+322:   def store_attribute locator, attribute_name, variable_name
+323:     command 'storeAttribute', "#{locator}@#{attribute_name}", variable_name
+324:   end
+
+
+
+
+ +
+ + + + +
+

+Gets the entire text of the page. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_body_text(pattern) + +
  • +
  • assert_not_body_text(pattern) + +
  • +
  • verify_body_text_present(pattern) + +
  • +
  • verify_not_body_text(pattern) + +
  • +
  • wait_for_body_text(pattern) + +
  • +
  • wait_for_not_body_text(pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 169
+169:   def store_body_text variable_name
+170:     command 'storeBodyText', variable_name
+171:   end
+
+
+
+
+ +
+ + + + +
+

+Gets whether a toggle-button (checkbox/radio) is checked. Fails if the +specified element doesn’t exist or isn’t a toggle-button. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_checked(locator, pattern) + +
  • +
  • assert_not_checked(locator, pattern) + +
  • +
  • verify_checked_present(locator, pattern) + +
  • +
  • verify_not_checked(locator, pattern) + +
  • +
  • wait_for_checked(locator, pattern) + +
  • +
  • wait_for_not_checked(locator, pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 239
+239:   def store_checked locator, variable_name
+240:     command 'storeChecked', locator, variable_name
+241:   end
+
+
+
+
+ +
+ + + + +
+

+Retrieves the message of a JavaScript confirmation dialog generated during +the previous action. +

+

+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 choose_cancel_on_next_confirmation command. If a confirmation +is generated but you do not get/verify it, the next Selenium action will +fail. +

+

+NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible +dialog. +

+

+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. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_confirmation(pattern) + +
  • +
  • assert_not_confirmation(pattern) + +
  • +
  • verify_confirmation_present(pattern) + +
  • +
  • verify_not_confirmation(pattern) + +
  • +
  • wait_for_confirmation(pattern) + +
  • +
  • wait_for_not_confirmation(pattern) + +
  • +
+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_accessors.rb, line 92
+92:   def store_confirmation variable_name
+93:     command 'storeConfirmation', variable_name
+94:   end
+
+
+
+
+ +
+ + + + +
+

+Has confirm() been called? +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_confirmation_present + +
  • +
  • assert_confirmation_not_present + +
  • +
  • verify_confirmation_present + +
  • +
  • verify_confirmation_not_present + +
  • +
  • wait_for_confirmation_present + +
  • +
  • wait_for_confirmation_not_present + +
  • +
+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_accessors.rb, line 41
+41:   def store_confirmation_present variable_name
+42:     command 'storeConfirmationPresent', variable_name
+43:   end
+
+
+
+
+ +
+ + + + +
+

+Determines whether the specified input element is editable, i.e. +hasn’t been disabled. This method will fail if the specified element +isn’t an input element. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_editable(locator) + +
  • +
  • assert_not_editable(locator) + +
  • +
  • verify_editable(locator) + +
  • +
  • verify_not_editable(locator) + +
  • +
  • wait_for_editable(locator) + +
  • +
  • wait_for_not_editable(locator) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 380
+380:   def store_editable locator, variable_name
+381:     command 'storeEditable', locator, variable_name
+382:   end
+
+
+
+
+ +
+ + + + +
+

+Verifies that the specified element is somewhere on the page. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_element_present(locator) + +
  • +
  • assert_element_not_present(locator) + +
  • +
  • verify_element_present(locator) + +
  • +
  • verify_element_not_present(locator) + +
  • +
  • wait_for_element_present(locator) + +
  • +
  • wait_for_element_not_present(locator) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 349
+349:   def store_element_present locator, variable_name
+350:     command 'storeElementPresent', locator, variable_name
+351:   end
+
+
+
+
+ +
+ + + + +
+

+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. +

+

+Note that, by default, the snippet will run in the context of the +"selenium" object itself, so this will refer to the +Selenium object, and window will refer to the top-level runner +test window, not the window of your application. +

+

+If you need a reference to the window of your application, you can refer to +this.browserbot.getCurrentWindow() and if you need to use a +locator to refer to a single element in your application page, you can use +this.page().findElement("foo") where +"foo" is your locator. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_eval(script, pattern) + +
  • +
  • assert_not_eval(script, pattern) + +
  • +
  • verify_eval_present(script, pattern) + +
  • +
  • verify_not_eval(script, pattern) + +
  • +
  • wait_for_eval(script, pattern) + +
  • +
  • wait_for_not_eval(script, pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 225
+225:   def store_eval script, variable_name
+226:     command 'storeEval', script, variable_name
+227:   end
+
+
+
+
+ +
+ + + + +
+

+Returns the specified expression. +

+

+This is useful because of JavaScript preprocessing. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_expression(expression, pattern) + +
  • +
  • assert_not_expression(expression, pattern) + +
  • +
  • verify_expression(expression, pattern) + +
  • +
  • verify_not_expression(expression, pattern) + +
  • +
  • wait_for_expression(expression, pattern) + +
  • +
  • wait_for_not_expression(expression, pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 468
+468:   def store_expression expression, variable_name
+469:     command 'storeExpression', expression, variable_name
+470:   end
+
+
+
+
+ +
+ + + + +
+

+Returns the entire HTML source between the opening and closing +"html" tags. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_html_source(pattern) + +
  • +
  • assert_not_html_source(pattern) + +
  • +
  • verify_html_source(pattern) + +
  • +
  • verify_not_html_source(pattern) + +
  • +
  • wait_for_html_source(pattern) + +
  • +
  • wait_for_not_html_source(pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 453
+453:   def store_html_source variable_name
+454:     command 'storeHtmlSource', variable_name
+455:   end
+
+
+
+
+ +
+ + + + +
+

+Verify the location of the current page ends with the expected location. If +an URL querystring is provided, this is checked as well. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_location(pattern) + +
  • +
  • assert_not_location(pattern) + +
  • +
  • verify_location_present(pattern) + +
  • +
  • verify_not_location(pattern) + +
  • +
  • wait_for_location(pattern) + +
  • +
  • wait_for_not_location(pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 143
+143:   def store_location expected_location, variable_name
+144:     command 'storeLocation', expected_location, variable_name
+145:   end
+
+
+
+
+ +
+ + + + +
+

+Retrieves the message of a JavaScript question prompt dialog generated +during the previous action. +

+

+Successful handling of the prompt requires prior execution of the +answer_on_next_prompt command. If a prompt is generated but you do +not get/verify it, the next Selenium action will fail. +

+

+NOTE: under Selenium, JavaScript prompts will NOT pop up a visible dialog. +

+

+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. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_prompt(pattern) + +
  • +
  • assert_not_prompt(pattern) + +
  • +
  • verify_prompt_present(pattern) + +
  • +
  • verify_not_prompt(pattern) + +
  • +
  • wait_for_prompt(pattern) + +
  • +
  • wait_for_not_prompt(pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 116
+116:   def store_prompt variable_name
+117:     command 'storePrompt', variable_name
+118:   end
+
+
+
+
+ +
+ + + + +
+

+Has a prompt occurred? +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_prompt_present + +
  • +
  • assert_prompt_not_present + +
  • +
  • verify_prompt_present + +
  • +
  • verify_prompt_not_present + +
  • +
  • wait_for_prompt_present + +
  • +
  • wait_for_prompt_not_present + +
  • +
+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_accessors.rb, line 28
+28:   def store_prompt_present variable_name
+29:     command 'storePromptPresent', variable_name
+30:   end
+
+
+
+
+ +
+ + + + +
+

+Gets all option labels in the specified select drop-down. +

+

+The pattern for the automatically generated assertions can either +take an array or a pattern. +

+
+ assert_select_options 'fruits', ['apple', 'pear']
+ assert_select_options 'fruits', 'a*,p*'
+
+

+Related Assertions, automatically generated: +

+
    +
  • assert_select_options(locator, pattern) + +
  • +
  • assert_not_select_options(locator, pattern) + +
  • +
  • verify_select_options_present(locator, pattern) + +
  • +
  • verify_not_select_options(locator, pattern) + +
  • +
  • wait_for_select_options(locator, pattern) + +
  • +
  • wait_for_not_select_options(locator, pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 309
+309:   def store_select_options locator, variable_name
+310:     command 'storeSelectOptions', locator, variable_name
+311:   end
+
+
+
+
+ +
+ + + + +
+

+Verifies that the selected option of a drop-down satisfies the +option_locator. +

+

+option_locator is typically just an option label (e.g. "John +Smith"). +

+

+See the select command for more information about option locators. +

+

+NOTE: store_selected is +currently not supported by Selenium Core. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_selected(locator, option_locator) + +
  • +
  • assert_not_selected(locator, option_locator) + +
  • +
  • verify_selected_present(locator, option_locator) + +
  • +
  • verify_not_selected(locator, option_locator) + +
  • +
  • wait_for_selected(locator, option_locator) + +
  • +
  • wait_for_not_selected(locator, option_locator) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 272
+272:   def store_selected locator, option_locator, variable_name
+273:     raise 'Not supported in Selenium Core at the moment'
+274:   end
+
+
+
+
+ +
+ + + + +
+

+Gets all option labels for selected options in the specified select or +multi-select element. +

+

+The pattern for the automatically generated assertions can either +take an array or a pattern. +

+
+ assert_selected_options 'fruits', ['apple', 'pear']
+ assert_selected_options 'fruits', 'a*,p*'
+
+

+Related Assertions, automatically generated: +

+
    +
  • assert_selected_options(locator, pattern) + +
  • +
  • assert_not_selected_options(locator, pattern) + +
  • +
  • verify_selected_options_present(locator, pattern) + +
  • +
  • verify_not_selected_options(locator, pattern) + +
  • +
  • wait_for_selected_options(locator, pattern) + +
  • +
  • wait_for_not_selected_options(locator, pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 291
+291:   def store_selected_options locator, variable_name
+292:     command 'storeSelectedOptions', locator, variable_name
+293:   end
+
+
+
+
+ +
+ + + + +
+

+Gets the text from a cell of a table. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_table(locator, row, column, pattern) + +
  • +
  • assert_not_table(locator, row, column, pattern) + +
  • +
  • verify_table_present(locator, row, column, pattern) + +
  • +
  • verify_not_table(locator, row, column, pattern) + +
  • +
  • wait_for_table(locator, row, column, pattern) + +
  • +
  • wait_for_not_table(locator, row, column, pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 252
+252:   def store_table locator, row, column, variable_name
+253:     command 'storeTable', "#{locator}.#{row}.#{column}", variable_name
+254:   end
+
+
+
+
+ +
+ + + + +
+

+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. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_text(locator, pattern) + +
  • +
  • assert_not_text(locator, pattern) + +
  • +
  • verify_text_present(locator, pattern) + +
  • +
  • verify_not_text(locator, pattern) + +
  • +
  • wait_for_text(locator, pattern) + +
  • +
  • wait_for_not_text(locator, pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 200
+200:   def store_text locator, variable_name
+201:     command 'storeText', locator, variable_name
+202:   end
+
+
+
+
+ +
+ + + + +
+

+Verifies that the specified text pattern appears somewhere on the rendered +page shown to the user. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_text_present(pattern) + +
  • +
  • assert_text_not_present(pattern) + +
  • +
  • verify_text_present(pattern) + +
  • +
  • verify_text_not_present(pattern) + +
  • +
  • wait_for_text_present(pattern) + +
  • +
  • wait_for_text_not_present(pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 336
+336:   def store_text_present pattern, variable_name
+337:     command 'storeTextPresent', pattern, variable_name
+338:   end
+
+
+
+
+ +
+ + + + +
+

+Gets the title of the current page. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_title(pattern) + +
  • +
  • assert_not_title(pattern) + +
  • +
  • verify_title_present(pattern) + +
  • +
  • verify_not_title(pattern) + +
  • +
  • wait_for_title(pattern) + +
  • +
  • wait_for_not_title(pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 156
+156:   def store_title variable_name
+157:     command 'storeTitle', variable_name
+158:   end
+
+
+
+
+ +
+ + + + +
+

+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. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_value(locator, pattern) + +
  • +
  • assert_not_value(locator, pattern) + +
  • +
  • verify_value_present(locator, pattern) + +
  • +
  • verify_not_value(locator, pattern) + +
  • +
  • wait_for_value(locator, pattern) + +
  • +
  • wait_for_not_value(locator, pattern) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 184
+184:   def store_value locator, variable_name
+185:     command 'storeValue', locator, variable_name
+186:   end
+
+
+
+
+ +
+ + + + +
+

+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. +

+

+Related Assertions, automatically generated: +

+
    +
  • assert_visible(locator) + +
  • +
  • assert_not_visible(locator) + +
  • +
  • verify_visible(locator) + +
  • +
  • verify_not_visible(locator) + +
  • +
  • wait_for_visible(locator) + +
  • +
  • wait_for_not_visible(locator) + +
  • +
+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_accessors.rb, line 365
+365:   def store_visible locator, variable_name
+366:     command 'storeVisible', locator, variable_name
+367:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderActions.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderActions.html new file mode 100644 index 00000000..f9d605f0 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderActions.html @@ -0,0 +1,1050 @@ + + + + + + Module: SeleniumOnRails::TestBuilderActions + + + + + + + + + + +
+ + + + + + + + + + +
ModuleSeleniumOnRails::TestBuilderActions
In: + + lib/selenium_on_rails/test_builder_actions.rb + +
+
+
+ + +
+ + + +
+ +
+

+The actions available for SeleniumOnRails::TestBuilder tests. +

+

+For each action foo there’s also an action +foo_and_wait. +

+ +
+ + +
+ +
+

Methods

+ + +
+ +
+ + + + +
+ + + + + + + + + +
+

Public Instance methods

+ +
+ + + + +
+

+Add a selection to the set of selected options in a multi-select element +using an option locator. +

+

+See the select +command for more information about option locators. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 142
+142:   def add_selection locator, option_locator
+143:     command 'addSelection', locator, option_locator
+144:   end
+
+
+
+
+ +
+ + + + +
+

+Instructs Selenium to return the specified answer string in response to the +next JavaScript prompt (window.prompt()). +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 193
+193:   def answer_on_next_prompt answer
+194:     command 'answerOnNextPrompt', answer
+195:   end
+
+
+
+
+ +
+ + + + +
+

+Check a toggle-button (checkbox/radio). +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 102
+102:   def check locator
+103:     command 'check', locator
+104:   end
+
+
+
+
+ +
+ + + + +
+

+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. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 187
+187:   def choose_cancel_on_next_confirmation
+188:     command 'chooseCancelOnNextConfirmation'
+189:   end
+
+
+
+
+ +
+ + + + +
+

+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 wait_for_page_to_load. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_actions.rb, line 47
+47:   def click locator
+48:     command 'click', locator
+49:   end
+
+
+
+
+ +
+ + + + +
+

+Simulates the user clicking the "close" button in the titlebar of +a popup window or tab. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 209
+209:   def close
+210:     command 'close'
+211:   end
+
+
+
+
+ +
+ + + + +
+

+Explicitly simulate an event (e.g. "focus", +"blur"), to trigger the corresponding +"on_event_" handler. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_actions.rb, line 53
+53:   def fire_event locator, event_name
+54:     command 'fireEvent', locator, event_name
+55:   end
+
+
+
+
+ +
+ + + + +
+

+Simulates the user clicking the "back" button on their browser. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 198
+198:   def go_back
+199:     command 'goBack'
+200:   end
+
+
+
+
+ +
+ + + + +
+

+Includes a partial. The path is relative to the Selenium tests root. The +starting _ and the file extension don’t have to be specified. +

+
+  #include test/selenium/_partial.*
+  include_partial 'partial'
+  #include test/selenium/suite/_partial.*
+  include_partial 'suite/partial'
+  #include test/selenium/suite/_partial.* and provide local assigns
+  include_partial 'suite/partial', :foo => bar
+
+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_actions.rb, line 39
+39:   def include_partial path, local_assigns = {}
+40:     partial = @view.render :partial => path, :locals => local_assigns
+41:     @output << partial
+42:   end
+
+
+
+
+ +
+ + + + +
+

+Simulates a user pressing a key (without releasing it yet). +

+

+keycode is the numeric keycode of the key to be pressed, normally +the ASCII value of that key. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_actions.rb, line 69
+69:   def key_down locator, keycode
+70:     command 'keyDown', locator, keycode
+71:   end
+
+
+
+
+ +
+ + + + +
+

+Simulates a user pressing and releasing a key. +

+

+keycode is the numeric keycode of the key to be pressed, normally +the ASCII value of that key. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_actions.rb, line 61
+61:   def key_press locator, keycode
+62:     command 'keyPress', locator, keycode
+63:   end
+
+
+
+
+ +
+ + + + +
+

+Simulates a user releasing a key. +

+

+keycode is the numeric keycode of the key to be released, normally +the ASCII value of that key. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_actions.rb, line 77
+77:   def key_up locator, keycode
+78:     command 'keyUp', locator, keycode
+79:   end
+
+
+
+
+ +
+ + + + +
+

+Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_actions.rb, line 88
+88:   def mouse_down locator
+89:     command 'mouseDown', locator
+90:   end
+
+
+
+
+ +
+ + + + +
+

+Simulates a user hovering a mouse over the specified element. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_actions.rb, line 82
+82:   def mouse_over locator
+83:     command 'mouseOver', locator
+84:   end
+
+
+
+
+ +
+ + + + +
+

+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, i.e. you don’t have to call wait_for_page_to_load. +

+

+Note: The URL must be on the same domain as the runner HTML due to security +restrictions in the browser (Same Origin Policy). +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 166
+166:   def open url
+167:     command 'open', url_arg(url)
+168:   end
+
+
+
+
+ +
+ + + + +
+

+Simulates the user clicking the "Refresh" button on their +browser. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 203
+203:   def refresh
+204:     command 'refresh'
+205:   end
+
+
+
+
+ +
+ + + + +
+

+Remove a selection from the set of selected options in a multi-select +element using an option locator. +

+

+See the select command for more information about option locators. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 150
+150:   def remove_selection locator, option_locator
+151:     command 'removeSelection', locator, option_locator
+152:   end
+
+
+
+
+ +
+ + + + +
+

+Select an option from a drop-down using an option locator. +

+

+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. +

+
    +
  • label=labelPattern matches options based on their labels, i.e. the visible +text. (This is the default.) + +
    +  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 option locator prefix is provided, the default behaviour is to match +on label. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 134
+134:   def select locator, option_locator
+135:     command 'select', locator, option_locator
+136:   end
+
+
+
+
+ +
+ + + + +
+

+Selects a popup window; once a popup window has been selected, all commands +go to that window. To select the main window again, use nil as the +target. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 172
+172:   def select_window window_id
+173:     command 'selectWindow', window_id||'null'
+174:   end
+
+
+
+
+ +
+ + + + +
+

+Writes a message to the status bar and adds a note to the browser-side log. +

+

+context is the message sent to the browser. +

+

+log_level_threshold can be nil, :debug, +:info, :warn or :error. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 219
+219:   def set_context context, log_level_threshold = nil
+220:     if log_level_threshold
+221:       command 'setContext', context, log_level_threshold.to_s
+222:     else
+223:       command 'setContext', context
+224:     end
+225:   end
+
+
+
+
+ +
+ + + + +
+

+Specifies the amount of time that Selenium will wait for actions to +complete. +

+

+Actions that require waiting include open and the +wait_for* actions. +

+

+The default timeout is 30 seconds. +

+

+timeout is specified in milliseconds. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 251
+251:   def set_timeout timeout
+252:     command 'setTimeout', timeout
+253:   end
+
+
+
+
+ +
+ + + + +
+

+Tell Selenium on Rails to clear the session and load any fixtures. DO NOT +CALL THIS AGAINST NON-TEST DATABASES. The supported options are +:keep_session, :fixtures and :clear_tables +

+
+  setup
+  setup :keep_session
+  setup :fixtures => :all
+  setup :keep_session, :fixtures => [:foo, :bar]
+  setup :clear_tables => [:foo, :bar]
+
+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_actions.rb, line 14
+14:   def setup options = {}
+15:     options = {options => nil} unless options.is_a? Hash
+16: 
+17:     opts = {:controller => 'selenium', :action => 'setup'}
+18:     opts[:keep_session] = true if options.has_key? :keep_session
+19: 
+20:     [:fixtures, :clear_tables].each do |key|
+21:       if (f = options[key])
+22:         f = [f] unless f.is_a? Array
+23:         opts[key] = f.join ','
+24:       end
+25:     end
+26: 
+27:     open opts
+28:   end
+
+
+
+
+ +
+ + + + +
+

+Submit the specified form. This is particularly useful for forms without +submit buttons, e.g. single-input "Search" forms. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 156
+156:   def submit locator
+157:     command 'submit', locator
+158:   end
+
+
+
+
+ +
+ + + + +
+

+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. +

+

[Source]

+
+
+    # File lib/selenium_on_rails/test_builder_actions.rb, line 97
+97:   def type locator, value
+98:     command 'type', locator, value
+99:   end
+
+
+
+
+ +
+ + + + +
+

+Uncheck a toggle-button (checkbox/radio). +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 107
+107:   def uncheck locator
+108:     command 'uncheck', locator
+109:   end
+
+
+
+
+ +
+ + + + +
+

+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. +

+

+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 +selenium.browserbot.getCurrentWindow(), and then run your +JavaScript in there. +

+

+timeout is specified in milliseconds. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 238
+238:   def wait_for_condition script, timeout
+239:     command 'waitForCondition', script, timeout
+240:   end
+
+
+
+
+ +
+ + + + +
+

+Waits for a new page to load. +

+

+You can use this command instead of the and_wait suffixes, +click_and_wait, select_and_wait, type_and_wait +etc. (which are only available in the JS API). +

+

+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. +

+

+timeout is specified in milliseconds. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 268
+268:   def wait_for_page_to_load timeout
+269:     command 'waitForPageToLoad', timeout
+270:   end
+
+
+
+
+ +
+ + + + +
+

+Waits for a popup window to appear and load up. +

+

+The timeout is specified in milliseconds. +

+

[Source]

+
+
+     # File lib/selenium_on_rails/test_builder_actions.rb, line 179
+179:   def wait_for_popup window_id, timeout
+180:     command 'waitForPopUp', window_id||'null', timeout
+181:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRailsConfig.html b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRailsConfig.html new file mode 100644 index 00000000..960d3b15 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRailsConfig.html @@ -0,0 +1,150 @@ + + + + + + Class: SeleniumOnRailsConfig + + + + + + + + + + +
+ + + + + + + + + + + + + + +
ClassSeleniumOnRailsConfig
In: + + lib/selenium_on_rails_config.rb + +
+
Parent: + Object +
+
+ + +
+ + + +
+ + + +
+ +
+

Methods

+ +
+ get   +
+
+ +
+ + + + +
+ + + + + + + + + +
+

Public Class methods

+ +
+ + + + +
+

[Source]

+
+
+    # File lib/selenium_on_rails_config.rb, line 5
+ 5:   def self.get var, default = nil
+ 6:     value = configs[var.to_s]
+ 7:     value ||= @@defaults[var]
+ 8:     value ||= default
+ 9:     value ||= yield if block_given?
+10:     value
+11:   end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/README.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/README.html new file mode 100644 index 00000000..77e99926 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/README.html @@ -0,0 +1,388 @@ + + + + + + File: README + + + + + + + + + + +
+

README

+ + + + + + + + + +
Path:README +
Last Update:Fri Dec 08 00:50:30 GMT Standard Time 2006
+
+ + +
+ + + +
+ +
+

Selenium on Rails

+

Overview

+

+Selenium on Rails provides an easy way to test Rails application with Selenium Core. +

+

+This plugin does four things: +

+
    +
  1. The Selenium Core files don’t have to pollute /public, they +can stay in the Selenium gem or in /vendor/selenium. + +
  2. +
  3. No need to create suite files, they are generated on the fly — one +suite per directory in /test/selenium (suites can be nested). + +
  4. +
  5. Instead of writing the test cases in HTML you can use a number of better +formats (see Formats). + +
  6. +
  7. Loading of fixtures and wiping of session (/selenium/setup). + +
  8. +
+

Installation

+
    +
  1. Selenium Core needs to be available. It could either be installed as a gem +(gem install selenium) or in /vendor/selenium/. + +
  2. +
  3. Install Selenium on Rails: script/plugin install http://svn.openqa.org/svn/selenium-on-rails/selenium-on-rails/ + +
  4. +
  5. If RedCloth is available the Selenese test cases can use it for better +markup. + +
  6. +
  7. Run the Rakefile in the plugin’s directory to run the tests in order +to see that everything works. (If RedCloth isn’t installed a few +tests will fail since they assume RedCloth is installed.) + +
  8. +
  9. Create a test case: script/generate selenium login + +
  10. +
  11. Start the server: script/server -e test + +
  12. +
  13. Point your browser to http://localhost:3000/selenium + +
  14. +
  15. If everything works as expected you should see the Selenium test runner. +The north east frame contains all your test cases (just one for now), and +the north frame contains your test case. + +
  16. +
+

win32-open3

+

+win32-open3 is +needed if you’re on Windows and want to run your tests as a Rake task +(see test:acceptance), i.e. you don’t have to install it but +it’s recommended. +

+

+You can build it from source or install the binary: +

+
    +
  1. Download the latest version of win32-open3, open3-0.2.2.so +at the time of this writing. + +
  2. +
  3. Open up irb and run this snippet: require ‘rbconfig’; +include Config; puts CONFIG[‘sitearchdir’] + +
  4. +
  5. Create a win32 directory under the directory you got, e.g. +c:\ruby\lib\ruby\site_ruby\1.8\i386-msvcrt + +
  6. +
  7. Rename the .so file to open3.so and put it in the win32 +directory. + +
  8. +
  9. Profit! (unless you get an error when doing require +‘win32/open3‘) + +
  10. +
+

Formats

+

+The test cases can be written in a number of formats. Which one you choose +is a matter of taste. You can generate your test files by running +script/generate selenium or by creating them manually in your +/test/selenium directory. +

+

Selenese, .sel

+

+Selenese is the dumbest format (in a good way). You just write your +commands delimited by | characters. +

+
+ |open|/selenium/setup|
+ |open|/|
+ |goBack|
+
+

+If you don’t want to write Selenese tests by hand you can use SeleniumIDE which has support for +Selenese. +

+

+SeleniumIDE makes it super easy to record test and edit them. +

+

RSelenese, .rsel

+

+RSelenese enable you to write your tests in Ruby. +

+
+ setup :fixtures => :all
+ open '/'
+ assert_title 'Home'
+ ('a'..'z').each {|c| open :controller => 'user', :action => 'create', :name => c }
+
+

+See SeleniumOnRails::TestBuilder +for available commands. +

+

HTML/RHTML

+

+You can write your tests in HTML/RHTML but that’s mostly useful if +you have existing tests you want to reuse. +

+

Partial test cases

+

+If you have some common actions you want to do in several test cases you +can put them in a separate partial test case and include them in your other +test cases. +

+

+A partial test case is just like a normal test case besides that its +filename has to start with _: +

+
+ #_login.rsel
+ open '/login'
+ type 'name', name
+ type 'password', password
+ click 'submit', :wait=>true
+
+

+To include a partial test case you write like this in a Selenese test case: +

+
+ |includePartial|login|name=John Doe|password=eoD nhoJ|
+
+

+in a RSelenese test case: +

+
+ include_partial 'login', :name => 'Jane Doe', :password => 'Jane Doe'.reverse
+
+

+and in a RHTML test case: +

+
+ <%= render :partial => 'login', :locals => {:name = 'Joe Schmo', :password => 'Joe Schmo'.reverse} %>
+
+

Configuration

+

+There are a number of settings available. You make them by renaming +config.yml.example to config.yml and make your changes in +that file. +

+

Environments

+

+Per default this plugin is only available in test environment. You can +change this by setting environments, such as: +

+
+ #config.yml
+ environments:
+   - test
+   - development
+
+

test:acceptance

+

+You can run all your Selenium tests as a Rake task. +

+

+First, if you’re on Windows, you have to make sure win32-open3 is +installed. Then you have to configure which browsers you want to run, like +this: +

+
+ #config.yml
+ browsers:
+   firefox: 'c:\Program Files\Mozilla Firefox\firefox.exe'
+   ie: 'c:\Program Files\Internet Explorer\iexplore.exe'
+
+

+Now you’re all set. First start a server: +

+
+ script/server -e test
+
+

+Then run the tests: +

+
+ rake test:acceptance
+
+

+Now it should work, otherwise let me know! +

+

Store results

+

+If you want to store the results from a test:acceptance you just +need to set in which directory they should be stored: +

+
+ #config.yml
+ result_dir: 'c:\result'
+
+

+So when you run rake test:acceptance the tables with the results +will be stored as .html files in that directory. +

+

+This can be useful especially for continous integration. +

+

Todo

+

Standalone mode

+

+More work is needed on test:acceptance on Windows to be able to +start the server when needed. +

+

user_extension.js

+

+Selenium has support for user_extension.js which is a way to +extend the functionality of Selenium Core. However there is currently no +easy way to add such a file in Selenium on Rails. +

+

More setup/teardown support?

+

+Currently there is only support to load fixtures and to wipe the session in +/selenium/setup. Is there a need for more kinds of setups or +teardowns? +

+

More documentation

+

Not todo

+

Editor

+

+Creating an editor for the test cases is currently considered out of scope +for this plugin. SeleniumIDE does such a good +job and has support for +the Selenese format. +

+

Credits

+ +

Information

+

+For more information, check out the website. +

+ +
+ + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/controllers/selenium_controller_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/controllers/selenium_controller_rb.html new file mode 100644 index 00000000..6e2ec72f --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/controllers/selenium_controller_rb.html @@ -0,0 +1,108 @@ + + + + + + File: selenium_controller.rb + + + + + + + + + + +
+

selenium_controller.rb

+ + + + + + + + + +
Path:lib/controllers/selenium_controller.rb +
Last Update:Fri Dec 08 00:52:51 GMT Standard Time 2006
+
+ + +
+ + + +
+ + +
+

Required files

+ +
+ webrick/httputils   +
+
+ +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_helper_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_helper_rb.html new file mode 100644 index 00000000..ff03a2c4 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_helper_rb.html @@ -0,0 +1,101 @@ + + + + + + File: selenium_helper.rb + + + + + + + + + + +
+

selenium_helper.rb

+ + + + + + + + + +
Path:lib/selenium_helper.rb +
Last Update:Sun Feb 05 01:02:10 W. Europe Standard Time 2006
+
+ + +
+ + + +
+ + + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/acceptance_test_runner_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/acceptance_test_runner_rb.html new file mode 100644 index 00000000..50258cfe --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/acceptance_test_runner_rb.html @@ -0,0 +1,207 @@ + + + + + + File: acceptance_test_runner.rb + + + + + + + + + + +
+

acceptance_test_runner.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/acceptance_test_runner.rb +
Last Update:Fri Dec 08 00:16:44 GMT Standard Time 2006
+
+ + +
+ + + +
+ + +
+

Required files

+ +
+ net/http   + tempfile   +
+
+ +
+ +
+

Methods

+ +
+ c   + c_b   +
+
+ +
+ + + + +
+ + +
+

Constants

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BROWSERS=c :browsers, {}
REUSE_EXISTING_SERVER=c :reuse_existing_server, true
START_SERVER=c :start_server, false
PORTS=c(:port_start, 3000)..c(:port_end, 3005)
TEST_RUNNER_URL=c :test_runner_url, '/selenium/TestRunner.html'
MAX_BROWSER_DURATION=c :max_browser_duration, 2*60
SERVER_COMMAND=c_b :server_command do server_path = File.expand_path(File.dirname(__FILE__) + '/../../../../../script/server')
+
+
+ + + + + + + +
+

Public Instance methods

+ +
+ + + + +
+

[Source]

+
+
+   # File lib/selenium_on_rails/acceptance_test_runner.rb, line 7
+7: def c(var, default = nil) SeleniumOnRailsConfig.get var, default end
+
+
+
+
+ +
+ + + + +
+

[Source]

+
+
+   # File lib/selenium_on_rails/acceptance_test_runner.rb, line 8
+8: def c_b(var, default = nil) SeleniumOnRailsConfig.get(var, default) { yield } end
+
+
+
+
+ + +
+ + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/fixture_loader_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/fixture_loader_rb.html new file mode 100644 index 00000000..5ef627b1 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/fixture_loader_rb.html @@ -0,0 +1,108 @@ + + + + + + File: fixture_loader.rb + + + + + + + + + + +
+

fixture_loader.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/fixture_loader.rb +
Last Update:Sun Feb 05 00:59:28 W. Europe Standard Time 2006
+
+ + +
+ + + +
+ + +
+

Required files

+ +
+ active_record/fixtures   +
+
+ +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/partials_support_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/partials_support_rb.html new file mode 100644 index 00000000..93c43ec6 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/partials_support_rb.html @@ -0,0 +1,111 @@ + + + + + + File: partials_support.rb + + + + + + + + + + +
+

partials_support.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/partials_support.rb +
Last Update:Tue May 02 00:43:37 W. Europe Daylight Time 2006
+
+ + +
+ + + +
+ +
+

+Provides partials support to test cases so they can include other partial +test cases. +

+

+The partial’s commands are returned as html table rows. +

+ +
+ + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/paths_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/paths_rb.html new file mode 100644 index 00000000..d1a48f63 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/paths_rb.html @@ -0,0 +1,101 @@ + + + + + + File: paths.rb + + + + + + + + + + +
+

paths.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/paths.rb +
Last Update:Sun Feb 05 00:59:28 W. Europe Standard Time 2006
+
+ + +
+ + + +
+ + + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/renderer_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/renderer_rb.html new file mode 100644 index 00000000..cc9595fc --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/renderer_rb.html @@ -0,0 +1,101 @@ + + + + + + File: renderer.rb + + + + + + + + + + +
+

renderer.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/renderer.rb +
Last Update:Sun Feb 05 00:59:29 W. Europe Standard Time 2006
+
+ + +
+ + + +
+ + + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/rselenese_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/rselenese_rb.html new file mode 100644 index 00000000..3e73734e --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/rselenese_rb.html @@ -0,0 +1,118 @@ + + + + + + File: rselenese.rb + + + + + + + + + + +
+

rselenese.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/rselenese.rb +
Last Update:Sun Feb 19 12:59:40 W. Europe Standard Time 2006
+
+ + +
+ + + +
+ +
+

+Renders Selenium test templates in a fashion analogous to rxml and +rjs templates. +

+
+  setup
+  open :controller => 'customer', :action => 'list'
+  assert_title 'Customers'
+
+

+See SeleniumOnRails::TestBuilder +for a list of available commands. +

+ +
+ + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/selenese_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/selenese_rb.html new file mode 100644 index 00000000..2e40de16 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/selenese_rb.html @@ -0,0 +1,101 @@ + + + + + + File: selenese.rb + + + + + + + + + + +
+

selenese.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/selenese.rb +
Last Update:Sun Feb 05 00:56:55 W. Europe Standard Time 2006
+
+ + +
+ + + +
+ + + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/suite_renderer_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/suite_renderer_rb.html new file mode 100644 index 00000000..25a9037d --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/suite_renderer_rb.html @@ -0,0 +1,101 @@ + + + + + + File: suite_renderer.rb + + + + + + + + + + +
+

suite_renderer.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/suite_renderer.rb +
Last Update:Sun Feb 05 04:12:56 W. Europe Standard Time 2006
+
+ + +
+ + + +
+ + + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_accessors_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_accessors_rb.html new file mode 100644 index 00000000..8aca17f2 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_accessors_rb.html @@ -0,0 +1,114 @@ + + + + + + File: test_builder_accessors.rb + + + + + + + + + + +
+

test_builder_accessors.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/test_builder_accessors.rb +
Last Update:Tue Jun 06 03:01:29 W. Europe Daylight Time 2006
+
+ + +
+ + + +
+ +
+

+The accessors available for SeleniumOnRails::TestBuilder +tests. +

+

+For each store_foo there’s assert_foo, +assert_not_foo, verify_foo, verify_not_foo, +wait_for_foo, wait_for_not_foo. +

+ +
+ + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_actions_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_actions_rb.html new file mode 100644 index 00000000..343a10e5 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_actions_rb.html @@ -0,0 +1,113 @@ + + + + + + File: test_builder_actions.rb + + + + + + + + + + +
+

test_builder_actions.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/test_builder_actions.rb +
Last Update:Tue Jun 06 03:12:04 W. Europe Daylight Time 2006
+
+ + +
+ + + +
+ +
+

+The actions available for SeleniumOnRails::TestBuilder +tests. +

+

+For each action foo there’s also an action +foo_and_wait. +

+ +
+ + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_rb.html new file mode 100644 index 00000000..9bdc7e5a --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_rb.html @@ -0,0 +1,121 @@ + + + + + + File: test_builder.rb + + + + + + + + + + +
+

test_builder.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails/test_builder.rb +
Last Update:Tue Jun 06 02:47:24 W. Europe Daylight Time 2006
+
+ + +
+ + + +
+ +
+

+Builds Selenium test table using a high-level Ruby interface. Normally +invoked through SeleniumOnRails::RSelenese. +

+

+See SeleniumOnRails::TestBuilderActions +for the available actions and SeleniumOnRails::TestBuilderAccessors +for the available checks. +

+

+For more information on the commands supported by TestBuilder, see the +Selenium Commands Documentation at release.openqa.org/selenium-core/nightly/reference.html. +

+ +
+ + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_config_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_config_rb.html new file mode 100644 index 00000000..26a16055 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_config_rb.html @@ -0,0 +1,108 @@ + + + + + + File: selenium_on_rails_config.rb + + + + + + + + + + +
+

selenium_on_rails_config.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails_config.rb +
Last Update:Mon Feb 20 21:58:17 W. Europe Standard Time 2006
+
+ + +
+ + + +
+ + +
+

Required files

+ +
+ yaml   +
+
+ +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_rb.html b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_rb.html new file mode 100644 index 00000000..aad82ae8 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_rb.html @@ -0,0 +1,115 @@ + + + + + + File: selenium_on_rails.rb + + + + + + + + + + +
+

selenium_on_rails.rb

+ + + + + + + + + +
Path:lib/selenium_on_rails.rb +
Last Update:Thu May 04 01:18:20 W. Europe Daylight Time 2006
+
+ + +
+ + + +
+ + +
+

Required files

+ +
+ selenium_on_rails/selenese   + selenium_on_rails/test_builder   + selenium_on_rails/rselenese   + selenium_on_rails/suite_renderer   + selenium_on_rails/paths   + selenium_on_rails/fixture_loader   + selenium_on_rails/partials_support   + selenium_on_rails/renderer   +
+
+ +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + +
+

[Validate]

+
+ + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/fr_class_index.html b/tracks/vendor/plugins/selenium-on-rails/doc/fr_class_index.html new file mode 100644 index 00000000..9e823f53 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/fr_class_index.html @@ -0,0 +1,40 @@ + + + + + + + + Classes + + + + + +
+

Classes

+ +
+ + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/fr_file_index.html b/tracks/vendor/plugins/selenium-on-rails/doc/fr_file_index.html new file mode 100644 index 00000000..765c4f3c --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/fr_file_index.html @@ -0,0 +1,42 @@ + + + + + + + + Files + + + + + +
+

Files

+ +
+ + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/fr_method_index.html b/tracks/vendor/plugins/selenium-on-rails/doc/fr_method_index.html new file mode 100644 index 00000000..1a25e630 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/fr_method_index.html @@ -0,0 +1,119 @@ + + + + + + + + Methods + + + + + +
+

Methods

+
+ add_selection (SeleniumOnRails::TestBuilderActions)
+ answer_on_next_prompt (SeleniumOnRails::TestBuilderActions)
+ available_fixtures (SeleniumOnRails::FixtureLoader)
+ c (lib/selenium_on_rails/acceptance_test_runner.rb)
+ c_b (lib/selenium_on_rails/acceptance_test_runner.rb)
+ check (SeleniumOnRails::TestBuilderActions)
+ choose_cancel_on_next_confirmation (SeleniumOnRails::TestBuilderActions)
+ clear_tables (SeleniumOnRails::FixtureLoader)
+ click (SeleniumOnRails::TestBuilderActions)
+ close (SeleniumOnRails::TestBuilderActions)
+ command (SeleniumOnRails::TestBuilder)
+ command_and_wait (SeleniumOnRails::TestBuilder)
+ command_verbatim (SeleniumOnRails::TestBuilder)
+ exactize (SeleniumOnRails::TestBuilder)
+ extract_commands_from_partial (SeleniumOnRails::PartialsSupport)
+ fire_event (SeleniumOnRails::TestBuilderActions)
+ fixtures_path (SeleniumOnRails::Paths)
+ get (SeleniumOnRailsConfig)
+ go_back (SeleniumOnRails::TestBuilderActions)
+ include_partial (SeleniumOnRails::TestBuilderActions)
+ key_down (SeleniumOnRails::TestBuilderActions)
+ key_press (SeleniumOnRails::TestBuilderActions)
+ key_up (SeleniumOnRails::TestBuilderActions)
+ layout_path (SeleniumOnRails::Paths)
+ link_to_test_case (SeleniumOnRails::SuiteRenderer)
+ load_fixtures (SeleniumOnRails::FixtureLoader)
+ log_path (SeleniumOnRails::Paths)
+ make_command_waiting (SeleniumOnRails::TestBuilder)
+ mouse_down (SeleniumOnRails::TestBuilderActions)
+ mouse_over (SeleniumOnRails::TestBuilderActions)
+ new (SeleniumOnRails::Selenese)
+ new (SeleniumOnRails::TestBuilder)
+ new (SeleniumOnRails::RSelenese)
+ open (SeleniumOnRails::TestBuilderActions)
+ record (SeleniumController)
+ refresh (SeleniumOnRails::TestBuilderActions)
+ remove_selection (SeleniumOnRails::TestBuilderActions)
+ render (SeleniumOnRails::Selenese)
+ render (SeleniumOnRails::RSelenese)
+ render_partial (SeleniumOnRails::PartialsSupport)
+ render_test_case (SeleniumOnRails::Renderer)
+ select (SeleniumOnRails::TestBuilderActions)
+ select_window (SeleniumOnRails::TestBuilderActions)
+ selenium_path (SeleniumOnRails::Paths)
+ selenium_tests_path (SeleniumOnRails::Paths)
+ selenize (SeleniumOnRails::TestBuilder)
+ set_context (SeleniumOnRails::TestBuilderActions)
+ set_timeout (SeleniumOnRails::TestBuilderActions)
+ setup (SeleniumController)
+ setup (SeleniumOnRails::TestBuilderActions)
+ skip_file? (SeleniumOnRails::Paths)
+ store_absolute_location (SeleniumOnRails::TestBuilderAccessors)
+ store_alert (SeleniumOnRails::TestBuilderAccessors)
+ store_alert_present (SeleniumOnRails::TestBuilderAccessors)
+ store_all_buttons (SeleniumOnRails::TestBuilderAccessors)
+ store_all_fields (SeleniumOnRails::TestBuilderAccessors)
+ store_all_links (SeleniumOnRails::TestBuilderAccessors)
+ store_attribute (SeleniumOnRails::TestBuilderAccessors)
+ store_body_text (SeleniumOnRails::TestBuilderAccessors)
+ store_checked (SeleniumOnRails::TestBuilderAccessors)
+ store_confirmation (SeleniumOnRails::TestBuilderAccessors)
+ store_confirmation_present (SeleniumOnRails::TestBuilderAccessors)
+ store_editable (SeleniumOnRails::TestBuilderAccessors)
+ store_element_present (SeleniumOnRails::TestBuilderAccessors)
+ store_eval (SeleniumOnRails::TestBuilderAccessors)
+ store_expression (SeleniumOnRails::TestBuilderAccessors)
+ store_html_source (SeleniumOnRails::TestBuilderAccessors)
+ store_location (SeleniumOnRails::TestBuilderAccessors)
+ store_prompt (SeleniumOnRails::TestBuilderAccessors)
+ store_prompt_present (SeleniumOnRails::TestBuilderAccessors)
+ store_select_options (SeleniumOnRails::TestBuilderAccessors)
+ store_selected (SeleniumOnRails::TestBuilderAccessors)
+ store_selected_options (SeleniumOnRails::TestBuilderAccessors)
+ store_table (SeleniumOnRails::TestBuilderAccessors)
+ store_text (SeleniumOnRails::TestBuilderAccessors)
+ store_text_present (SeleniumOnRails::TestBuilderAccessors)
+ store_title (SeleniumOnRails::TestBuilderAccessors)
+ store_value (SeleniumOnRails::TestBuilderAccessors)
+ store_visible (SeleniumOnRails::TestBuilderAccessors)
+ submit (SeleniumOnRails::TestBuilderActions)
+ support_file (SeleniumController)
+ table (SeleniumOnRails::TestBuilder)
+ test_case_name (SeleniumHelper)
+ test_cases (SeleniumOnRails::SuiteRenderer)
+ test_file (SeleniumController)
+ test_suite_name (SeleniumOnRails::SuiteRenderer)
+ test_suites (SeleniumOnRails::SuiteRenderer)
+ type (SeleniumOnRails::TestBuilderActions)
+ uncheck (SeleniumOnRails::TestBuilderActions)
+ view_path (SeleniumOnRails::Paths)
+ wait_for_condition (SeleniumOnRails::TestBuilderActions)
+ wait_for_page_to_load (SeleniumOnRails::TestBuilderActions)
+ wait_for_popup (SeleniumOnRails::TestBuilderActions)
+
+
+ + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/index.html b/tracks/vendor/plugins/selenium-on-rails/doc/index.html new file mode 100644 index 00000000..d7f62a7d --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/index.html @@ -0,0 +1,24 @@ + + + + + + + SeleniumOnRails + + + + + + + + + + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/doc/rdoc-style.css b/tracks/vendor/plugins/selenium-on-rails/doc/rdoc-style.css new file mode 100644 index 00000000..fbf7326a --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/doc/rdoc-style.css @@ -0,0 +1,208 @@ + +body { + font-family: Verdana,Arial,Helvetica,sans-serif; + font-size: 90%; + margin: 0; + margin-left: 40px; + padding: 0; + background: white; +} + +h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; } +h1 { font-size: 150%; } +h2,h3,h4 { margin-top: 1em; } + +a { background: #eef; color: #039; text-decoration: none; } +a:hover { background: #039; color: #eef; } + +/* Override the base stylesheet's Anchor inside a table cell */ +td > a { + background: transparent; + color: #039; + text-decoration: none; +} + +/* and inside a section title */ +.section-title > a { + background: transparent; + color: #eee; + text-decoration: none; +} + +/* === Structural elements =================================== */ + +div#index { + margin: 0; + margin-left: -40px; + padding: 0; + font-size: 90%; +} + + +div#index a { + margin-left: 0.7em; +} + +div#index .section-bar { + margin-left: 0px; + padding-left: 0.7em; + background: #ccc; + font-size: small; +} + + +div#classHeader, div#fileHeader { + width: auto; + color: white; + padding: 0.5em 1.5em 0.5em 1.5em; + margin: 0; + margin-left: -40px; + border-bottom: 3px solid #006; +} + +div#classHeader a, div#fileHeader a { + background: inherit; + color: white; +} + +div#classHeader td, div#fileHeader td { + background: inherit; + color: white; +} + + +div#fileHeader { + background: #057; +} + +div#classHeader { + background: #048; +} + + +.class-name-in-header { + font-size: 180%; + font-weight: bold; +} + + +div#bodyContent { + padding: 0 1.5em 0 1.5em; +} + +div#description { + padding: 0.5em 1.5em; + background: #efefef; + border: 1px dotted #999; +} + +div#description h1,h2,h3,h4,h5,h6 { + color: #125;; + background: transparent; +} + +div#validator-badges { + text-align: center; +} +div#validator-badges img { border: 0; } + +div#copyright { + color: #333; + background: #efefef; + font: 0.75em sans-serif; + margin-top: 5em; + margin-bottom: 0; + padding: 0.5em 2em; +} + + +/* === Classes =================================== */ + +table.header-table { + color: white; + font-size: small; +} + +.type-note { + font-size: small; + color: #DEDEDE; +} + +.xxsection-bar { + background: #eee; + color: #333; + padding: 3px; +} + +.section-bar { + color: #333; + border-bottom: 1px solid #999; + margin-left: -20px; +} + + +.section-title { + background: #79a; + color: #eee; + padding: 3px; + margin-top: 2em; + margin-left: -30px; + border: 1px solid #999; +} + +.top-aligned-row { vertical-align: top } +.bottom-aligned-row { vertical-align: bottom } + +/* --- Context section classes ----------------------- */ + +.context-row { } +.context-item-name { font-family: monospace; font-weight: bold; color: black; } +.context-item-value { font-size: small; color: #448; } +.context-item-desc { color: #333; padding-left: 2em; } + +/* --- Method classes -------------------------- */ +.method-detail { + background: #efefef; + padding: 0; + margin-top: 0.5em; + margin-bottom: 1em; + border: 1px dotted #ccc; +} +.method-heading { + color: black; + background: #ccc; + border-bottom: 1px solid #666; + padding: 0.2em 0.5em 0 0.5em; +} +.method-signature { color: black; background: inherit; } +.method-name { font-weight: bold; } +.method-args { font-style: italic; } +.method-description { padding: 0 0.5em 0 0.5em; } + +/* --- Source code sections -------------------- */ + +a.source-toggle { font-size: 90%; } +div.method-source-code { + background: #262626; + color: #ffdead; + margin: 1em; + padding: 0.5em; + border: 1px dashed #999; + overflow: hidden; +} + +div.method-source-code pre { color: #ffdead; overflow: hidden; } + +/* --- Ruby keyword styles --------------------- */ + +.standalone-code { background: #221111; color: #ffdead; overflow: hidden; } + +.ruby-constant { color: #7fffd4; background: transparent; } +.ruby-keyword { color: #00ffff; background: transparent; } +.ruby-ivar { color: #eedd82; background: transparent; } +.ruby-operator { color: #00ffee; background: transparent; } +.ruby-identifier { color: #ffdead; background: transparent; } +.ruby-node { color: #ffa07a; background: transparent; } +.ruby-comment { color: #b22222; font-weight: bold; background: transparent; } +.ruby-regexp { color: #ffa07a; background: transparent; } +.ruby-value { color: #7fffd4; background: transparent; } \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/generators/selenium/USAGE b/tracks/vendor/plugins/selenium-on-rails/generators/selenium/USAGE new file mode 100644 index 00000000..1a6ae9f0 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/generators/selenium/USAGE @@ -0,0 +1,19 @@ +Description: + Generates a stub Selenium test case. + +Examples: + ./script/generate selenium login + will create: + /test/selenium/login.sel + + ./script/generate selenium user/create + will create: + /test/selenium/user/create.sel + + ./script/generate selenium login.rsel + will create: + /test/selenium/login.rsel + + ./script/generate selenium logout.rhtml + will create: + /test/selenium/logout.rhtml diff --git a/tracks/vendor/plugins/selenium-on-rails/generators/selenium/selenium_generator.rb b/tracks/vendor/plugins/selenium-on-rails/generators/selenium/selenium_generator.rb new file mode 100644 index 00000000..bc51ef5a --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/generators/selenium/selenium_generator.rb @@ -0,0 +1,50 @@ +class SeleniumGenerator < Rails::Generator::Base + def initialize runtime_args, runtime_options = {} + super + usage if @args.empty? + end + + def banner + "Usage: #{$0} #{spec.name} testname [options]" + end + + def manifest + record do |m| + path = 'test/selenium' + path = File.join(path, suite_path) unless suite_path.empty? + m.directory path + + template = case File.extname(filename) + when '.rhtml' then 'rhtml.rhtml' + when '.rsel' then 'rselenese.rhtml' + else 'selenese.rhtml' + end + m.template template, File.join(path, filename) + end + end + + def filename + name = File.basename args[0] + extensions = ['.sel', '.rhtml', '.rsel'] + name = "#{name}.sel" unless extensions.include? File.extname(name) + name + end + + def suite_path + sp = File.dirname args[0] + sp = '' if sp == '.' + sp + end + + def testcase_link + l = "http://localhost:3000/selenium/tests/" + l = "#{l}#{suite_path}/" unless suite_path.empty? + l + filename + end + + def suite_link + l = "http://localhost:3000/selenium" + l = "#{l}/TestRunner.html?test=tests/#{suite_path}" unless suite_path.empty? + l + end +end diff --git a/tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/rhtml.rhtml b/tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/rhtml.rhtml new file mode 100644 index 00000000..c6c4c441 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/rhtml.rhtml @@ -0,0 +1,16 @@ +

It's often a good idea to start the test with opening /selenium/setup (see <%%= link_to 'here', :controller => 'selenium', :action => 'setup' %> for more info).

+ + + + +<%% for page in ['/', '/home'] -%> + + +<%% end -%> +
<%%= @page_title %>
open/selenium/setup 
open<%%= page %> 
assertTitleHome 
+ +

More information about the commands is available here.

+ +

You can write comments above and below the commands, but you can only have one set of commands, i.e. one table, per test.

+ +

Point the browser to <%= testcase_link %> to see how this test is rendered, or to <%= suite_link %> to run the suite.

diff --git a/tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/rselenese.rhtml b/tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/rselenese.rhtml new file mode 100644 index 00000000..419eb368 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/rselenese.rhtml @@ -0,0 +1,14 @@ +# It's often a good idea to start the test with 'setup'. +# See /selenium/setup for more info. + +setup +open '/' +assert_title 'Home' + +# More information about the commands is available at: +# http://release.openqa.org/selenium-core/nightly/reference.html +# See also the RDoc for SeleniumOnRails::TestBuilder. +# +# Point the browser to <%= testcase_link %> to see +# how this test is rendered, or to <%= suite_link %> to +# run the suite. diff --git a/tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/selenese.rhtml b/tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/selenese.rhtml new file mode 100644 index 00000000..f4ccb8a9 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/generators/selenium/templates/selenese.rhtml @@ -0,0 +1,11 @@ +It's often a good idea to start the test with opening /selenium/setup (see "here":/selenium/setup for more info). + +|open|/selenium/setup| +|open|/| +|assertTitle|Home| + +More information about the commands is available "here":http://release.openqa.org/selenium-core/nightly/reference.html. + +You can write comments above and below the commands, but you can only have one set of commands, i.e. one table, per test. "RedCloth":http://www.whytheluckystiff.net/ruby/redcloth/ is used for formatting if installed. + +Point the browser to "<%= testcase_link %>":<%= testcase_link %> to see how this test is rendered, or to "<%= suite_link %>":<%= suite_link %> to run the suite. diff --git a/tracks/vendor/plugins/selenium-on-rails/init.rb b/tracks/vendor/plugins/selenium-on-rails/init.rb new file mode 100644 index 00000000..05d43a8c --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/init.rb @@ -0,0 +1,19 @@ +require 'selenium_on_rails_config' +envs = SeleniumOnRailsConfig.get :environments + +if envs.include? RAILS_ENV + #initialize the plugin + $LOAD_PATH << File.dirname(__FILE__) + "/lib/controllers" + require 'selenium_controller' + require File.dirname(__FILE__) + '/routes' + +else + #erase all traces + $LOAD_PATH.delete lib_path + + #but help user figure out what to do + unless RAILS_ENV == 'production' # don't pollute production + require File.dirname(__FILE__) + '/switch_environment/init' + end +end + diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/controllers/selenium_controller.rb b/tracks/vendor/plugins/selenium-on-rails/lib/controllers/selenium_controller.rb new file mode 100644 index 00000000..f6e328c8 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/controllers/selenium_controller.rb @@ -0,0 +1,119 @@ +require 'webrick/httputils' + +class SeleniumController < ActionController::Base + include SeleniumOnRails::FixtureLoader + include SeleniumOnRails::Renderer + + def setup + unless params.has_key? :keep_session + reset_session + @session_wiped = true + end + @cleared_tables = clear_tables params[:clear_tables].to_s + @loaded_fixtures = load_fixtures params[:fixtures].to_s + render :file => view_path('setup.rhtml'), :layout => layout_path + end + + def test_file + params[:testname] = '' if params[:testname].to_s == 'TestSuite.html' + filename = File.join selenium_tests_path, params[:testname] + if File.directory? filename + @suite_path = filename + render :file => view_path('test_suite.rhtml'), :layout => layout_path + elsif File.readable? filename + render_test_case filename + else + if File.directory? selenium_tests_path + render :text => 'Not found', :status => 404 + else + render :text => "Did not find the Selenium tests path (#{selenium_tests_path}). Run script/generate selenium", :status => 404 + end + end + end + + def support_file + if params[:filename].empty? + redirect_to :filename => 'TestRunner.html', :test => 'tests' + return + end + + filename = File.join selenium_path, params[:filename] + if File.file? filename + type = WEBrick::HTTPUtils::DefaultMimeTypes[$1.downcase] if filename =~ /\.(\w+)$/ + type ||= 'text/html' + send_file filename, :type => type, :disposition => 'inline', :stream => false + else + render :text => 'Not found', :status => 404 + end + end + + def record + dir = record_table + + @result = {'resultDir' => dir} + for p in ['result', 'numTestFailures', 'numTestPasses', 'numCommandFailures', 'numCommandPasses', 'numCommandErrors', 'totalTime'] + @result[p] = params[p] + end + File.open(log_path(params[:logFile] || 'default.yml'), 'w') {|f| YAML.dump(@result, f)} + + render :file => view_path('record.rhtml'), :layout => layout_path + end + + def record_table + return nil unless result_dir = SeleniumOnRailsConfig.get(:result_dir) + + cur_result_dir = File.join(result_dir, (params[:logFile] || "default").sub(/\.yml$/, '')) + FileUtils.mkdir_p(cur_result_dir) + File.open("#{cur_result_dir}/index.html", "wb") do |f| + f.write < +Selenium Test Result + + + + + +EOS + end + html_header = < + + + + +EOS + html_footer = "\n" + if selenium_path + css_file = File.join selenium_path, "selenium-test.css" + if File.exist?(css_file) + FileUtils.cp css_file, cur_result_dir + end + end + File.open("#{cur_result_dir}/blank.html", "wb") do |f| + f.write "" + end + File.open("#{cur_result_dir}/suite.html", "wb") do |f| + suite = params[:suite] + suite.sub!(/^.*(])/im, '\1') + i = 1 + suite.gsub!(/(\shref=)"[^"]*"/i) do |m| + link = "#{$1}\"test#{i}.html\" target=\"testcase\"" + File.open("#{cur_result_dir}/test#{i}.html", "wb") do |testcase| + testcase.write html_header + testcase.write(params["testTable.#{i}"]) + testcase.write html_footer + end + i += 1 + link + end + f.write html_header + f.write suite + f.write html_footer + end + cur_result_dir + end + + private :record_table + + +end diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_helper.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_helper.rb new file mode 100644 index 00000000..4e178536 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_helper.rb @@ -0,0 +1,9 @@ +module SeleniumHelper + include SeleniumOnRails::SuiteRenderer + include SeleniumOnRails::FixtureLoader + + def test_case_name filename + File.basename(filename).sub(/\..*/,'').humanize + end + +end diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails.rb new file mode 100644 index 00000000..365d0034 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails.rb @@ -0,0 +1,11 @@ +module SeleniumOnRails # :nodoc +end + +require 'selenium_on_rails/selenese' +require 'selenium_on_rails/test_builder' +require 'selenium_on_rails/rselenese' +require 'selenium_on_rails/suite_renderer' +require 'selenium_on_rails/paths' +require 'selenium_on_rails/fixture_loader' +require 'selenium_on_rails/partials_support' +require 'selenium_on_rails/renderer' diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/acceptance_test_runner.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/acceptance_test_runner.rb new file mode 100644 index 00000000..9f1d106b --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/acceptance_test_runner.rb @@ -0,0 +1,210 @@ +require File.dirname(__FILE__) + '/paths' +require File.dirname(__FILE__) + '/../selenium_on_rails_config' +require 'net/http' +require 'tempfile' + + +def c(var, default = nil) SeleniumOnRailsConfig.get var, default end +def c_b(var, default = nil) SeleniumOnRailsConfig.get(var, default) { yield } end + +BROWSERS = c :browsers, {} +REUSE_EXISTING_SERVER = c :reuse_existing_server, true +START_SERVER = c :start_server, false #TODO can't get it to work reliably on Windows, perhaps it's just on my computer, but I leave it off by default for now +HOST = c :host, 'localhost' +PORTS = c(:port_start, 3000)..c(:port_end, 3005) +TEST_RUNNER_URL = c :test_runner_url, '/selenium/TestRunner.html' +MAX_BROWSER_DURATION = c :max_browser_duration, 2*60 +MULTI_WINDOW = c :multi_window, false +SERVER_COMMAND = c_b :server_command do + server_path = File.expand_path(File.dirname(__FILE__) + '/../../../../../script/server') + if RUBY_PLATFORM =~ /mswin/ + "ruby #{server_path} -p %d -e test > NUL 2>&1" + else + # don't use redirects to /dev/nul since it makes the fork return wrong pid + # see UnixSubProcess + "#{server_path} -p %d -e test" + end +end + +module SeleniumOnRails + class AcceptanceTestRunner + include SeleniumOnRails::Paths + + def run + raise 'no browser specified, edit/create config.yml' if BROWSERS.empty? + start_server + has_error = false + begin + BROWSERS.each_pair do |browser, path| + log_file = start_browser browser, path + wait_for_completion log_file + stop_browser + result = YAML::load_file log_file + print_result result + has_error ||= result['numTestFailures'].to_i > 0 + File.delete log_file unless has_error + end + rescue + stop_server + raise + end + stop_server + raise 'Test failures' if has_error + end + + private + def start_server + PORTS.each do |p| + @port = p + case server_check + when :success + return if REUSE_EXISTING_SERVER + next + when Fixnum + next + when :no_response + next unless START_SERVER + do_start_server + return + end + end + raise START_SERVER ? 'failed to start server': 'failed to find existing server, run script/server -e test' + end + + def do_start_server + puts 'Starting server' + @server = start_subprocess(format(SERVER_COMMAND, @port)) + while true + print '.' + r = server_check + if r == :success + puts + return + end + raise "server returned error: #{r}" if r.instance_of? Fixnum + sleep 3 + end + end + + def server_check + begin + res = Net::HTTP.get_response HOST, TEST_RUNNER_URL, @port + return :success if (200..399).include? res.code.to_i + return res.code.to_i + rescue Errno::ECONNREFUSED + return :no_response + end + end + + def stop_server + return unless defined? @server + puts + @server.stop 'server' + end + + def start_browser browser, path + puts + puts "Starting #{browser}" + log = log_file browser + command = "\"#{path}\" \"http://#{HOST}:#{@port}#{TEST_RUNNER_URL}?test=tests&auto=true&resultsUrl=postResults/#{log}&multiWindow=#{MULTI_WINDOW}\"" + @browser = start_subprocess command + log_path log + end + + def stop_browser + @browser.stop 'browser' + end + + def start_subprocess command + if RUBY_PLATFORM =~ /mswin/ + SeleniumOnRails::AcceptanceTestRunner::Win32SubProcess.new command + elsif RUBY_PLATFORM =~ /darwin/i && command =~ /safari/i + SeleniumOnRails::AcceptanceTestRunner::SafariSubProcess.new command + else + SeleniumOnRails::AcceptanceTestRunner::UnixSubProcess.new command + end + end + + def log_file browser + (0..100).each do |i| + name = browser + (i==0 ? '' : "(#{i})") + '.yml' + return name unless File.exist?(log_path(name)) + end + raise 'there are way too many files in the log directory...' + end + + def wait_for_completion log_file + duration = 0 + while true + raise 'browser takes too long' if duration > MAX_BROWSER_DURATION + print '.' + break if File.exist? log_file + sleep 5 + duration += 5 + end + puts + end + + def print_result result + puts "Finished in #{result['totalTime']} seconds." + puts + puts "#{result['numTestPasses']} tests passed, #{result['numTestFailures']} tests failed" + puts "(Results stored in '#{result['resultDir']}')" if result['resultDir'] + end + + end +end + +class SeleniumOnRails::AcceptanceTestRunner::SubProcess + def stop what + begin + puts "Stopping #{what} (pid=#{@pid}) ..." + Process.kill 9, @pid + rescue Errno::EPERM #such as the process is already closed (tabbed browser) + end + end +end + +class SeleniumOnRails::AcceptanceTestRunner::Win32SubProcess < SeleniumOnRails::AcceptanceTestRunner::SubProcess + def initialize command + require 'win32/open3' #win32-open3 http://raa.ruby-lang.org/project/win32-open3/ + + puts command + input, output, error, @pid = Open4.popen4 command, 't', true + end +end + +class SeleniumOnRails::AcceptanceTestRunner::UnixSubProcess < SeleniumOnRails::AcceptanceTestRunner::SubProcess + def initialize command + puts command + @pid = fork do + # Since we can't use shell redirects without screwing + # up the pid, we'll reopen stdin and stdout instead + # to get the same effect. + [STDOUT,STDERR].each {|f| f.reopen '/dev/null', 'w' } + exec command + end + end +end + +# The path to Safari should look like this: /Applications/Safari.app/Contents/MacOS/Safari +class SeleniumOnRails::AcceptanceTestRunner::SafariSubProcess < SeleniumOnRails::AcceptanceTestRunner::UnixSubProcess + def initialize command + f = File.open(Tempfile.new('selenium-on-rails').path, 'w') + f.puts <<-HTML + + + + + + + HTML + f.close + + super "#{command.split.first} #{f.path}" + end + +end + diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/fixture_loader.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/fixture_loader.rb new file mode 100644 index 00000000..c4d513cc --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/fixture_loader.rb @@ -0,0 +1,54 @@ +require 'active_record/fixtures' + +module SeleniumOnRails::FixtureLoader + include SeleniumOnRails::Paths + + def available_fixtures + fixtures = {} + path = fixtures_path + '/' + files = Dir["#{path}**/*.{yml,csv}"] + files.each do |file| + rel_path = file.sub(path, '') + next if skip_file? rel_path + fixture_set = File.dirname(rel_path) + fixture_set = '' if fixture_set == '.' + fixture = rel_path.sub /\.[^.]*$/, '' + fixtures[fixture_set] ||= [] + fixtures[fixture_set] << fixture + end + + fixtures + end + + def load_fixtures fixtures_param + available = nil + fixtures = fixtures_param.split(/\s*,\s*/).collect do |f| + fixture_set = File.dirname f + fixture_set = '' if fixture_set == '.' + fixture = File.basename f + if fixture == 'all' + available ||= available_fixtures + available[fixture_set] + else + f + end + end + fixtures.flatten! + fixtures.reject! {|f| f.blank? } + + if fixtures.any? + Fixtures.create_fixtures fixtures_path, fixtures + end + fixtures + end + + def clear_tables tables + table_names = tables.split /\s*,\s*/ + connection = ActiveRecord::Base.connection + table_names.each do |table| + connection.execute "DELETE FROM #{table}" + end + table_names + end + +end diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/partials_support.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/partials_support.rb new file mode 100644 index 00000000..4374c4e9 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/partials_support.rb @@ -0,0 +1,38 @@ +# Provides partials support to test cases so they can include other partial test +# cases. +# +# The partial's commands are returned as html table rows. +module SeleniumOnRails::PartialsSupport + include SeleniumOnRails::Paths + + # Overrides where the partial is searched for, and returns only the command table rows. + def render_partial partial_path = default_template_name, object = nil, local_assigns = nil, status = nil + pattern = partial_pattern partial_path + filename = Dir[pattern].first + raise "Partial '#{partial_path}' cannot be found! (Looking for file: '#{pattern}')" unless filename + partial = render :file => filename, :use_full_path => false, :locals => local_assigns + extract_commands_from_partial partial + end + + # Extracts the commands from a partial. The partial must contain a html table + # and the first row is ignored since it cannot contain a command. + def extract_commands_from_partial partial + partial = partial.match(/.*.*?.*?<\/tr>(.*?)<\/table>/im)[1] + raise "Partial '#{name}' doesn't contain any table" unless partial + partial + end + + private + # Generates the file pattern from the provided partial path. + # The starting _ and file extension don't have too be provided. + def partial_pattern partial_path + path = partial_path.split '/' + filename = path.delete_at(-1) + filename = '_' + filename unless filename.starts_with? '_' + filename << '.*' unless filename.include? '.' + pattern = selenium_tests_path + '/' + pattern << path.join('/') + '/' if path + pattern << filename + end + +end \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/paths.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/paths.rb new file mode 100644 index 00000000..03dfc7f2 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/paths.rb @@ -0,0 +1,61 @@ +module SeleniumOnRails + module Paths + def selenium_path + @@selenium_path ||= find_selenium_path + @@selenium_path + end + + def selenium_tests_path + File.expand_path(File.join(RAILS_ROOT, 'test/selenium')) + end + + def view_path view + File.expand_path(File.dirname(__FILE__) + '/../views/' + view) + end + + # Returns the path to the layout template. The path is relative in relation + # to the app/views/ directory since Rails doesn't support absolute paths + # to layout templates. + def layout_path + rails_root = Pathname.new File.expand_path(File.join(RAILS_ROOT, 'app/views')) + view_path = Pathname.new view_path('layout') + view_path.relative_path_from(rails_root).to_s + end + + def fixtures_path + File.expand_path File.join(RAILS_ROOT, 'test/fixtures') + end + + def log_path log_file + File.expand_path(File.dirname(__FILE__) + '/../../log/' + File.basename(log_file)) + end + + def skip_file? file + file.split('/').each do |f| + return true if f.upcase == 'CVS' or f.starts_with?('.') or f.ends_with?('~') or f.starts_with?('_') + end + false + end + + private + def find_selenium_path + sel_dirs = SeleniumOnRailsConfig.get :selenium_path do + ds = [File.expand_path(File.join(RAILS_ROOT, 'vendor/selenium')), + File.expand_path(File.join(RAILS_ROOT, 'vendor/selenium-core'))] + gems = Gem.source_index.find_name 'selenium', nil + ds << gems.last.full_gem_path unless gems.empty? + ds + end + + sel_dirs.to_a.each do |seleniumdir| + ['', 'core', 'selenium', 'javascript'].each do |subdir| + path = File.join seleniumdir, subdir + return path if File.exist?(File.join(path, 'TestRunner.html')) + end + end + + raise 'Could not find Selenium Core installation' + end + + end +end diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/renderer.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/renderer.rb new file mode 100644 index 00000000..f49f3162 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/renderer.rb @@ -0,0 +1,17 @@ +module SeleniumOnRails::Renderer + include SeleniumOnRails::Paths + include SeleniumHelper + + def render_test_case filename + @template.extend SeleniumOnRails::PartialsSupport + @page_title = test_case_name filename + output = render_to_string :file => filename + layout = (output =~ //i ? false : layout_path) + render :text => output, :layout => layout + + headers['Cache-control'] = 'no-cache' + headers['Pragma'] = 'no-cache' + headers['Expires'] = '-1' + end + +end \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/rselenese.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/rselenese.rb new file mode 100644 index 00000000..fea4a57c --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/rselenese.rb @@ -0,0 +1,35 @@ +# Renders Selenium test templates in a fashion analogous to +rxml+ and +# +rjs+ templates. +# +# setup +# open :controller => 'customer', :action => 'list' +# assert_title 'Customers' +# +# See SeleniumOnRails::TestBuilder for a list of available commands. +class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder +end +ActionView::Base.register_template_handler 'rsel', SeleniumOnRails::RSelenese + +class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder + attr_accessor :view + + # Create a new RSelenese renderer bound to _view_. + def initialize view + super view + @view = view + end + + # Render _template_ using _local_assigns_. + def render template, local_assigns + title = (@view.assigns['page_title'] or local_assigns['page_title']) + table(title) do + test = self #to enable test.command + + assign_locals_code = '' + local_assigns.each_key {|key| assign_locals_code << "#{key} = local_assigns[#{key.inspect}];"} + + eval assign_locals_code + "\n" + template + end + end + +end \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/selenese.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/selenese.rb new file mode 100644 index 00000000..4f9e4524 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/selenese.rb @@ -0,0 +1,81 @@ +class SeleniumOnRails::Selenese +end +ActionView::Base.register_template_handler 'sel', SeleniumOnRails::Selenese + + +class SeleniumOnRails::Selenese + def initialize view + @view = view + end + + def render template, local_assigns + name = (@view.assigns['page_title'] or local_assigns['page_title']) + lines = template.strip.split "\n" + html = '' + html << extract_comments(lines) + html << extract_commands(lines, name) + html << extract_comments(lines) + raise 'You cannot have comments in the middle of commands!' if next_line lines, :any + html + end + + private + def next_line lines, expects + while lines.any? + l = lines.shift.strip + next if (l.empty? and expects != :comment) + comment = (l =~ /^\|.*\|$/).nil? + if (comment and expects == :command) or (!comment and expects == :comment) + lines.unshift l + return nil + end + return l + end + end + + def extract_comments lines + comments = '' + while (line = next_line lines, :comment) + comments << line + "\n" + end + if defined? RedCloth + comments = RedCloth.new(comments).to_html + end + comments += "\n" unless comments.empty? + comments + end + + def extract_commands lines, name + html = "
\n\n" + while (line = next_line lines, :command) + line = line[1..-2] #remove starting and ending | + cells = line.split '|' + if cells.first == 'includePartial' + html << include_partial(cells[1..-1]) + next + end + raise 'There might only be a maximum of three cells!' if cells.length > 3 + html << '' + (1..3).each do + cell = cells.shift + cell = (cell ? CGI.escapeHTML(cell.strip) : ' ') + html << "" + end + html << "\n" + end + html << "
#{name}
#{cell}
\n" + end + + def include_partial params + partial = params.shift + locals = {} + params.each do |assignment| + next if assignment.empty? + _, var, value = assignment.split(/^([a-z_][a-zA-Z0-9_]*)\s*=\s*(.*)$/) + raise "Invalid format '#{assignment}'. Should be '|includePartial|partial|var1=value|var2=value|." unless var + locals[var.to_sym] = value or '' + end + @view.render :partial => partial, :locals => locals + end + +end \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/suite_renderer.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/suite_renderer.rb new file mode 100644 index 00000000..10f87771 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/suite_renderer.rb @@ -0,0 +1,51 @@ +module SeleniumOnRails::SuiteRenderer + def test_suite_name path + return 'All test cases' if [nil, '/'].include? path_to_relative_url(path) + File.split(path)[-1].humanize + end + + def test_suites path + suites = [] + + parent_path = File.join(File.split(path).slice(0..-2)) #all but last + parent_path = path_to_relative_url parent_path + suites << ['..', parent_path] unless parent_path.nil? + + visit_all_tests path, '', Proc.new {|n, p| suites << [n,path_to_relative_url(p)]}, nil + suites + end + + def test_cases path + tests = [] + visit_all_tests path, '', nil, Proc.new {|n, p| tests << [n,p]} + tests + end + + def link_to_test_case suite_name, filename + name = suite_name + test_case_name(filename) + link_to name, :action => :test_file, :testname => path_to_relative_url(filename).sub(/^\//,'') + end + + private + def path_to_relative_url path + slt = @controller.selenium_tests_path + return nil unless path.index slt + path.sub slt, '' + end + + def visit_all_tests path, suite_name, suite_consumer, test_consumer + dirs = [] #add dirs to an array in order for files to be processed before dirs + Dir.entries(path).sort.each do |e| + next if skip_file?(e) or ['.','..'].include?(e) + filename = File.join path, e + if File.directory? filename + dirs << [filename, "#{suite_name}#{e.humanize}."] + suite_consumer.call("#{suite_name}#{e.humanize}", filename) if suite_consumer + else + test_consumer.call(suite_name, filename) if test_consumer + end + end + #recurse through dirs + dirs.each {|p, n| visit_all_tests p, n, suite_consumer, test_consumer } + end +end diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder.rb new file mode 100644 index 00000000..c2a33078 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder.rb @@ -0,0 +1,92 @@ +# Builds Selenium test table using a high-level Ruby interface. Normally +# invoked through SeleniumOnRails::RSelenese. +# +# See SeleniumOnRails::TestBuilderActions for the available actions and +# SeleniumOnRails::TestBuilderAccessors for the available checks. +# +# For more information on the commands supported by TestBuilder, see the +# Selenium Commands Documentation at +# http://release.openqa.org/selenium-core/nightly/reference.html. +class SeleniumOnRails::TestBuilder + include SeleniumOnRails::TestBuilderActions + include SeleniumOnRails::TestBuilderAccessors + + # Convert _str_ to a Selenium command name. + def self.selenize str + str.camelize.gsub(/^[A-Z]/) {|s| s.downcase } + end + + # Prepends _pattern_ with 'exact:' if it would be considered containing + # string-match pattern otherwise. + def exactize pattern + pattern.include?(':') ? "exact:#{pattern}" : pattern + end + + # Create a new TestBuilder for _view_. + def initialize view + @view = view + @output = '' + @xml = Builder::XmlMarkup.new :indent => 2, :target => @output + end + + # Add a new table of tests, and return the HTML. + def table title + @xml.table do + @xml.tr do @xml.th(title, :colspan => 3) end + yield self + end + end + + # Add a new test command using _cmd_, _target_ and _value_. + def command cmd, target=nil, value=nil + @xml.tr do + _tdata cmd + _tdata target + _tdata value + end + end + # :nodoc + alias_method :command_verbatim, :command + + # Same as _command_ but add _AndWait_ to the name of _cmd_. + def command_and_wait cmd, target=nil, value=nil + command_verbatim cmd.to_s + 'AndWait', target, value + end + + # Re routes commands in the provided block to #command_and_wait instead of + # #command. + def make_command_waiting + self.class.send :alias_method, :command, :command_and_wait + yield + self.class.send :alias_method, :command, :command_verbatim + end + +protected + + # If _url_ is a string, return unchanged. Otherwise, pass it to + # ActionView#UrlHelper#url_for. + def url_arg url + if url.instance_of?(String) then url else exactize(@view.url_for(url)) end + end + + # If _arg_ is an array formats _arg_ to a textual representation. + # Otherwise return unchanged. + def collection_arg arg + if arg.is_a? Array + arg.collect {|e| e.gsub(/[\\,]/) {|s| "\\#{s}" } }.join(',') + else + arg + end + end + +private + + # Output a single TD element. + def _tdata value + if value + @xml.td(value.to_s) + else + @xml.td do @xml.target! << ' ' end + end + end +end diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_accessors.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_accessors.rb new file mode 100644 index 00000000..6c892770 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_accessors.rb @@ -0,0 +1,575 @@ +# The accessors available for SeleniumOnRails::TestBuilder tests. +# +# For each +store_foo+ there's +assert_foo+, +assert_not_foo+, +verify_foo+, +# +verify_not_foo+, +wait_for_foo+, +wait_for_not_foo+. +module SeleniumOnRails::TestBuilderAccessors + # Has an alert occurred? + # + # Related Assertions, automatically generated: + # * +assert_alert_present+ + # * +assert_alert_not_present+ + # * +verify_alert_present+ + # * +verify_alert_not_present+ + # * +wait_for_alert_present+ + # * +wait_for_alert_not_present+ + def store_alert_present variable_name + command 'storeAlertPresent', variable_name + end + + # Has a prompt occurred? + # + # Related Assertions, automatically generated: + # * +assert_prompt_present+ + # * +assert_prompt_not_present+ + # * +verify_prompt_present+ + # * +verify_prompt_not_present+ + # * +wait_for_prompt_present+ + # * +wait_for_prompt_not_present+ + def store_prompt_present variable_name + command 'storePromptPresent', variable_name + end + + # Has confirm() been called? + # + # Related Assertions, automatically generated: + # * +assert_confirmation_present+ + # * +assert_confirmation_not_present+ + # * +verify_confirmation_present+ + # * +verify_confirmation_not_present+ + # * +wait_for_confirmation_present+ + # * +wait_for_confirmation_not_present+ + def store_confirmation_present variable_name + command 'storeConfirmationPresent', variable_name + end + + # Retrieves the message of a JavaScript alert generated during the previous + # action, or fail if there were no alerts. + # + # 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. + # + # NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert + # dialog. + # + # 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. + # + # Related Assertions, automatically generated: + # * assert_alert(pattern) + # * assert_not_alert(pattern) + # * verify_alert_present(pattern) + # * verify_not_alert(pattern) + # * wait_for_alert(pattern) + # * wait_for_not_alert(pattern) + def store_alert variable_name + command 'storeAlert', variable_name + end + + # Retrieves the message of a JavaScript confirmation dialog generated during + # the previous action. + # + # 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 + # +choose_cancel_on_next_confirmation+ command. If a confirmation is + # generated but you do not get/verify it, the next Selenium action will fail. + # + # NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible + # dialog. + # + # 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. + # + # Related Assertions, automatically generated: + # * assert_confirmation(pattern) + # * assert_not_confirmation(pattern) + # * verify_confirmation_present(pattern) + # * verify_not_confirmation(pattern) + # * wait_for_confirmation(pattern) + # * wait_for_not_confirmation(pattern) + def store_confirmation variable_name + command 'storeConfirmation', variable_name + end + + # Retrieves the message of a JavaScript question prompt dialog generated + # during the previous action. + # + # Successful handling of the prompt requires prior execution of the + # +answer_on_next_prompt+ command. If a prompt is generated but you do not + # get/verify it, the next Selenium action will fail. + # + # NOTE: under Selenium, JavaScript prompts will NOT pop up a visible dialog. + # + # 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. + # + # Related Assertions, automatically generated: + # * assert_prompt(pattern) + # * assert_not_prompt(pattern) + # * verify_prompt_present(pattern) + # * verify_not_prompt(pattern) + # * wait_for_prompt(pattern) + # * wait_for_not_prompt(pattern) + def store_prompt variable_name + command 'storePrompt', variable_name + end + + # Gets the absolute URL of the current page. + # + # Related Assertions, automatically generated: + # * assert_absolute_location(pattern) + # * assert_not_absolute_location(pattern) + # * verify_absolute_location_present(pattern) + # * verify_not_absolute_location(pattern) + # * wait_for_absolute_location(pattern) + # * wait_for_not_absolute_location(pattern) + def store_absolute_location variable_name + command 'storeAbsoluteLocation', variable_name + end + + # Verify the location of the current page ends with the expected location. + # If an URL querystring is provided, this is checked as well. + # + # Related Assertions, automatically generated: + # * assert_location(pattern) + # * assert_not_location(pattern) + # * verify_location_present(pattern) + # * verify_not_location(pattern) + # * wait_for_location(pattern) + # * wait_for_not_location(pattern) + def store_location expected_location, variable_name + command 'storeLocation', expected_location, variable_name + end + + # Gets the title of the current page. + # + # Related Assertions, automatically generated: + # * assert_title(pattern) + # * assert_not_title(pattern) + # * verify_title_present(pattern) + # * verify_not_title(pattern) + # * wait_for_title(pattern) + # * wait_for_not_title(pattern) + def store_title variable_name + command 'storeTitle', variable_name + end + + # Gets the entire text of the page. + # + # Related Assertions, automatically generated: + # * assert_body_text(pattern) + # * assert_not_body_text(pattern) + # * verify_body_text_present(pattern) + # * verify_not_body_text(pattern) + # * wait_for_body_text(pattern) + # * wait_for_not_body_text(pattern) + def store_body_text variable_name + command 'storeBodyText', variable_name + end + + # 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. + # + # Related Assertions, automatically generated: + # * assert_value(locator, pattern) + # * assert_not_value(locator, pattern) + # * verify_value_present(locator, pattern) + # * verify_not_value(locator, pattern) + # * wait_for_value(locator, pattern) + # * wait_for_not_value(locator, pattern) + def store_value locator, variable_name + command 'storeValue', locator, variable_name + end + + # 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. + # + # Related Assertions, automatically generated: + # * assert_text(locator, pattern) + # * assert_not_text(locator, pattern) + # * verify_text_present(locator, pattern) + # * verify_not_text(locator, pattern) + # * wait_for_text(locator, pattern) + # * wait_for_not_text(locator, pattern) + def store_text locator, variable_name + command 'storeText', locator, variable_name + end + + # 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. + # + # Note that, by default, the snippet will run in the context of the + # "selenium" object itself, so +this+ will refer to the Selenium object, and + # +window+ will refer to the top-level runner test window, not the window of + # your application. + # + # If you need a reference to the window of your application, you can refer to + # this.browserbot.getCurrentWindow() and if you need to use a locator to + # refer to a single element in your application page, you can use + # this.page().findElement("foo") where "foo" is your locator. + # + # Related Assertions, automatically generated: + # * assert_eval(script, pattern) + # * assert_not_eval(script, pattern) + # * verify_eval_present(script, pattern) + # * verify_not_eval(script, pattern) + # * wait_for_eval(script, pattern) + # * wait_for_not_eval(script, pattern) + def store_eval script, variable_name + command 'storeEval', script, variable_name + end + + # Gets whether a toggle-button (checkbox/radio) is checked. Fails if the + # specified element doesn't exist or isn't a toggle-button. + # + # Related Assertions, automatically generated: + # * assert_checked(locator, pattern) + # * assert_not_checked(locator, pattern) + # * verify_checked_present(locator, pattern) + # * verify_not_checked(locator, pattern) + # * wait_for_checked(locator, pattern) + # * wait_for_not_checked(locator, pattern) + def store_checked locator, variable_name + command 'storeChecked', locator, variable_name + end + + # Gets the text from a cell of a table. + # + # Related Assertions, automatically generated: + # * assert_table(locator, row, column, pattern) + # * assert_not_table(locator, row, column, pattern) + # * verify_table_present(locator, row, column, pattern) + # * verify_not_table(locator, row, column, pattern) + # * wait_for_table(locator, row, column, pattern) + # * wait_for_not_table(locator, row, column, pattern) + def store_table locator, row, column, variable_name + command 'storeTable', "#{locator}.#{row}.#{column}", variable_name + end + + # Verifies that the selected option of a drop-down satisfies the + # +option_locator+. + # + # +option_locator+ is typically just an option label (e.g. "John Smith"). + # + # See the +select+ command for more information about option locators. + # + # NOTE: +store_selected+ is currently not supported by Selenium Core. + # + # Related Assertions, automatically generated: + # * assert_selected(locator, option_locator) + # * assert_not_selected(locator, option_locator) + # * verify_selected_present(locator, option_locator) + # * verify_not_selected(locator, option_locator) + # * wait_for_selected(locator, option_locator) + # * wait_for_not_selected(locator, option_locator) + def store_selected locator, option_locator, variable_name + raise 'Not supported in Selenium Core at the moment' + end + + # Gets all option labels for selected options in the specified select or + # multi-select element. + # + # The +pattern+ for the automatically generated assertions can either take an + # array or a pattern. + # assert_selected_options 'fruits', ['apple', 'pear'] + # assert_selected_options 'fruits', 'a*,p*' + # + # Related Assertions, automatically generated: + # * assert_selected_options(locator, pattern) + # * assert_not_selected_options(locator, pattern) + # * verify_selected_options_present(locator, pattern) + # * verify_not_selected_options(locator, pattern) + # * wait_for_selected_options(locator, pattern) + # * wait_for_not_selected_options(locator, pattern) + def store_selected_options locator, variable_name + command 'storeSelectedOptions', locator, variable_name + end + + # Gets all option labels in the specified select drop-down. + # + # The +pattern+ for the automatically generated assertions can either take an + # array or a pattern. + # assert_select_options 'fruits', ['apple', 'pear'] + # assert_select_options 'fruits', 'a*,p*' + # + # Related Assertions, automatically generated: + # * assert_select_options(locator, pattern) + # * assert_not_select_options(locator, pattern) + # * verify_select_options_present(locator, pattern) + # * verify_not_select_options(locator, pattern) + # * wait_for_select_options(locator, pattern) + # * wait_for_not_select_options(locator, pattern) + def store_select_options locator, variable_name + command 'storeSelectOptions', locator, variable_name + end + + # Gets the value of an element attribute. + # + # Related Assertions, automatically generated: + # * assert_attribute(locator, attribute_name, pattern) + # * assert_not_attribute(locator, attribute_name, pattern) + # * verify_attribute_present(locator, attribute_name, pattern) + # * verify_not_attribute(locator, attribute_name, pattern) + # * wait_for_attribute(locator, attribute_name, pattern) + # * wait_for_not_attribute(locator, attribute_name, pattern) + def store_attribute locator, attribute_name, variable_name + command 'storeAttribute', "#{locator}@#{attribute_name}", variable_name + end + + # Verifies that the specified text pattern appears somewhere on the rendered + # page shown to the user. + # + # Related Assertions, automatically generated: + # * assert_text_present(pattern) + # * assert_text_not_present(pattern) + # * verify_text_present(pattern) + # * verify_text_not_present(pattern) + # * wait_for_text_present(pattern) + # * wait_for_text_not_present(pattern) + def store_text_present pattern, variable_name + command 'storeTextPresent', pattern, variable_name + end + + # Verifies that the specified element is somewhere on the page. + # + # Related Assertions, automatically generated: + # * assert_element_present(locator) + # * assert_element_not_present(locator) + # * verify_element_present(locator) + # * verify_element_not_present(locator) + # * wait_for_element_present(locator) + # * wait_for_element_not_present(locator) + def store_element_present locator, variable_name + command 'storeElementPresent', locator, variable_name + end + + # 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. + # + # Related Assertions, automatically generated: + # * assert_visible(locator) + # * assert_not_visible(locator) + # * verify_visible(locator) + # * verify_not_visible(locator) + # * wait_for_visible(locator) + # * wait_for_not_visible(locator) + def store_visible locator, variable_name + command 'storeVisible', locator, variable_name + end + + # Determines whether the specified input element is editable, i.e. hasn't + # been disabled. This method will fail if the specified element isn't an + # input element. + # + # Related Assertions, automatically generated: + # * assert_editable(locator) + # * assert_not_editable(locator) + # * verify_editable(locator) + # * verify_not_editable(locator) + # * wait_for_editable(locator) + # * wait_for_not_editable(locator) + def store_editable locator, variable_name + command 'storeEditable', locator, variable_name + end + + # Returns the IDs of all buttons on the page. + # + # If a given button has no ID, it will appear as "" in this array. + # + # The +pattern+ for the automatically generated assertions can either take an + # array or a pattern. + # assert_all_buttons ['but1', 'but2'] + # assert_all_buttons 'but?,but?*' + # + # Related Assertions, automatically generated: + # * assert_all_buttons(pattern) + # * assert_not_all_buttons(pattern) + # * verify_all_buttons(pattern) + # * verify_not_all_buttons(pattern) + # * wait_for_all_buttons(pattern) + # * wait_for_not_all_buttons(pattern) + def store_all_buttons variable_name + command 'storeAllButtons', variable_name + end + + # Returns the IDs of all links on the page. + # + # If a given link has no ID, it will appear as "" in this array. + # + # The +pattern+ for the automatically generated assertions can either take an + # array or a pattern. + # assert_all_links ['link1', 'link2'] + # assert_all_links 'link?,link?*' + # + # Related Assertions, automatically generated: + # * assert_all_links(pattern) + # * assert_not_all_links(pattern) + # * verify_all_links(pattern) + # * verify_not_all_links(pattern) + # * wait_for_all_links(pattern) + # * wait_for_not_all_links(pattern) + def store_all_links variable_name + command 'storeAllLinks', variable_name + end + + # Returns the IDs of all input fields on the page. + # + # If a given field has no ID, it will appear as "" in this array. + # + # The +pattern+ for the automatically generated assertions can either take an + # array or a pattern. + # assert_all_fields ['field1', 'field2'] + # assert_all_fields 'field?,field?*' + # + # Related Assertions, automatically generated: + # * assert_all_fields(pattern) + # * assert_not_all_fields(pattern) + # * verify_all_fields(pattern) + # * verify_not_all_fields(pattern) + # * wait_for_all_fields(pattern) + # * wait_for_not_all_fields(pattern) + def store_all_fields variable_name + command 'storeAllFields', variable_name + end + + # Returns the entire HTML source between the opening and closing "html" tags. + # + # Related Assertions, automatically generated: + # * assert_html_source(pattern) + # * assert_not_html_source(pattern) + # * verify_html_source(pattern) + # * verify_not_html_source(pattern) + # * wait_for_html_source(pattern) + # * wait_for_not_html_source(pattern) + def store_html_source variable_name + command 'storeHtmlSource', variable_name + end + + # Returns the specified expression. + # + # This is useful because of JavaScript preprocessing. + # + # Related Assertions, automatically generated: + # * assert_expression(expression, pattern) + # * assert_not_expression(expression, pattern) + # * verify_expression(expression, pattern) + # * verify_not_expression(expression, pattern) + # * wait_for_expression(expression, pattern) + # * wait_for_not_expression(expression, pattern) + def store_expression expression, variable_name + command 'storeExpression', expression, variable_name + end + +private + # Generates all assertions for the accessors. + def self.generate_methods + public_instance_methods.each do |method| + case method + when 'store_alert_present', + 'store_prompt_present', + 'store_confirmation_present' + each_assertion method do |assertion_method, command_name| + define_method assertion_method do + command command_name + end + end + when 'store_alert', + 'store_confirmation', + 'store_prompt', + 'store_title', + 'store_body_text', + 'store_text_present', + 'store_element_present', + 'store_visible', + 'store_editable', + 'store_html_source' + each_assertion method do |assertion_method, command_name| + define_method assertion_method do |pattern| + command command_name, pattern + end + end + when 'store_value', + 'store_text', + 'store_eval', + 'store_checked', + 'store_selected', + 'store_expression' + each_assertion method do |assertion_method, command_name| + define_method assertion_method do |arg1, arg2| + command command_name, arg1, arg2 + end + end + when 'store_all_buttons', + 'store_all_links', + 'store_all_fields' + each_assertion method do |assertion_method, command_name| + define_method assertion_method do |pattern| + command command_name, collection_arg(pattern) + end + end + when 'store_select_options', + 'store_selected_options' + each_assertion method do |assertion_method, command_name| + define_method assertion_method do |locator, pattern| + command command_name, locator, collection_arg(pattern) + end + end + when 'store_attribute' + each_assertion method do |assertion_method, command_name| + define_method assertion_method do |locator, attribute_name, pattern| + command command_name, "#{locator}@#{attribute_name}", pattern + end + end + when 'store_table' + each_assertion method do |assertion_method, command_name| + define_method assertion_method do |locator, row, column, pattern| + command command_name, "#{locator}.#{row}.#{column}", pattern + end + end + when 'store_absolute_location', + 'store_location' + each_assertion method do |assertion_method, command_name| + define_method assertion_method do |pattern| + if method == 'store_absolute_location' and pattern.is_a? Hash + pattern[:only_path] = false + end + + command command_name, url_arg(pattern) + end + end + when /^store_/ + raise 'internal error' + end + end + end + + # Generates all the assertions needed given a +store_method+. + def self.each_assertion store_method + before_negation = nil + after_negation = store_method.split('_')[1..-1] #throw away 'store' + if after_negation.last == 'present' + before_negation, after_negation = after_negation, after_negation.pop + end + + ['assert', 'verify', ['wait','for']].each do |action| + [nil, 'not'].each do |negation| + name = [action, before_negation, negation, after_negation].flatten.reject{|a|a.nil?} + method_name = name.join '_' + command = name.inject(name.shift.clone) {|n, p| n << p.capitalize} + yield method_name, command + end + end + end + + generate_methods +end + diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_actions.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_actions.rb new file mode 100644 index 00000000..65c272d1 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_actions.rb @@ -0,0 +1,286 @@ +# The actions available for SeleniumOnRails::TestBuilder tests. +# +# For each action +foo+ there's also an action +foo_and_wait+. +module SeleniumOnRails::TestBuilderActions + # Tell Selenium on Rails to clear the session and load any fixtures. DO + # NOT CALL THIS AGAINST NON-TEST DATABASES. + # The supported +options+ are :keep_session, + # :fixtures and :clear_tables + # setup + # setup :keep_session + # setup :fixtures => :all + # setup :keep_session, :fixtures => [:foo, :bar] + # setup :clear_tables => [:foo, :bar] + def setup options = {} + options = {options => nil} unless options.is_a? Hash + + opts = {:controller => 'selenium', :action => 'setup'} + opts[:keep_session] = true if options.has_key? :keep_session + + [:fixtures, :clear_tables].each do |key| + if (f = options[key]) + f = [f] unless f.is_a? Array + opts[key] = f.join ',' + end + end + + open opts + end + + # Includes a partial. + # The path is relative to the Selenium tests root. The starting _ and the file + # extension don't have to be specified. + # #include test/selenium/_partial.* + # include_partial 'partial' + # #include test/selenium/suite/_partial.* + # include_partial 'suite/partial' + # #include test/selenium/suite/_partial.* and provide local assigns + # include_partial 'suite/partial', :foo => bar + def include_partial path, local_assigns = {} + partial = @view.render :partial => path, :locals => local_assigns + @output << partial + end + + # 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 + # +wait_for_page_to_load+. + def click locator + command 'click', locator + end + + # Explicitly simulate an event (e.g. "focus", "blur"), to + # trigger the corresponding "on_event_" handler. + def fire_event locator, event_name + command 'fireEvent', locator, event_name + end + + # Simulates a user pressing and releasing a key. + # + # +keycode+ is the numeric keycode of the key to be pressed, normally the + # ASCII value of that key. + def key_press locator, keycode + command 'keyPress', locator, keycode + end + + # Simulates a user pressing a key (without releasing it yet). + # + # +keycode+ is the numeric keycode of the key to be pressed, normally the + # ASCII value of that key. + def key_down locator, keycode + command 'keyDown', locator, keycode + end + + # Simulates a user releasing a key. + # + # +keycode+ is the numeric keycode of the key to be released, normally the + # ASCII value of that key. + def key_up locator, keycode + command 'keyUp', locator, keycode + end + + # Simulates a user hovering a mouse over the specified element. + def mouse_over locator + command 'mouseOver', locator + end + + # Simulates a user pressing the mouse button (without releasing it yet) on the + # specified element. + def mouse_down locator + command 'mouseDown', locator + end + + # 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. + def type locator, value + command 'type', locator, value + end + + # Check a toggle-button (checkbox/radio). + def check locator + command 'check', locator + end + + # Uncheck a toggle-button (checkbox/radio). + def uncheck locator + command 'uncheck', locator + end + + # Select an option from a drop-down using an option locator. + # + # 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. + # + # * label=labelPattern + # matches options based on their labels, i.e. the visible text. (This is the + # default.) + # 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 option locator prefix is provided, the default behaviour is to match + # on label. + def select locator, option_locator + command 'select', locator, option_locator + end + + # Add a selection to the set of selected options in a multi-select element + # using an option locator. + # + # See the #select command for more information about option locators. + def add_selection locator, option_locator + command 'addSelection', locator, option_locator + end + + # Remove a selection from the set of selected options in a multi-select + # element using an option locator. + # + # See the +select+ command for more information about option locators. + def remove_selection locator, option_locator + command 'removeSelection', locator, option_locator + end + + # Submit the specified form. This is particularly useful for forms without + # submit buttons, e.g. single-input "Search" forms. + def submit locator + command 'submit', locator + end + + # 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, i.e. you don't have to call +wait_for_page_to_load+. + # + # Note: The URL must be on the same domain as the runner HTML due to security + # restrictions in the browser (Same Origin Policy). + def open url + command 'open', url_arg(url) + end + + # Selects a popup window; once a popup window has been selected, all commands + # go to that window. To select the main window again, use +nil+ as the target. + def select_window window_id + command 'selectWindow', window_id||'null' + end + + # Waits for a popup window to appear and load up. + # + # The +timeout+ is specified in milliseconds. + def wait_for_popup window_id, timeout + command 'waitForPopUp', window_id||'null', timeout + end + + # 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. + def choose_cancel_on_next_confirmation + command 'chooseCancelOnNextConfirmation' + end + + # Instructs Selenium to return the specified answer string in response to the + # next JavaScript prompt (window.prompt()). + def answer_on_next_prompt answer + command 'answerOnNextPrompt', answer + end + + # Simulates the user clicking the "back" button on their browser. + def go_back + command 'goBack' + end + + # Simulates the user clicking the "Refresh" button on their browser. + def refresh + command 'refresh' + end + + # Simulates the user clicking the "close" button in the titlebar of a popup + # window or tab. + def close + command 'close' + end + + # Writes a message to the status bar and adds a note to the browser-side log. + # + # +context+ is the message sent to the browser. + # + # +log_level_threshold+ can be +nil+, :debug, :info, + # :warn or :error. + def set_context context, log_level_threshold = nil + if log_level_threshold + command 'setContext', context, log_level_threshold.to_s + else + command 'setContext', context + end + end + + # 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. + # + # 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 + # selenium.browserbot.getCurrentWindow(), and then run your + # JavaScript in there. + # + # +timeout+ is specified in milliseconds. + def wait_for_condition script, timeout + command 'waitForCondition', script, timeout + end + + # Specifies the amount of time that Selenium will wait for actions to + # complete. + # + # Actions that require waiting include +open+ and the wait_for* + # actions. + # + # The default timeout is 30 seconds. + # + # +timeout+ is specified in milliseconds. + def set_timeout timeout + command 'setTimeout', timeout + end + + # Waits for a new page to load. + # + # You can use this command instead of the +and_wait+ suffixes, + # +click_and_wait+, +select_and_wait+, +type_and_wait+ etc. (which are only + # available in the JS API). + # + # 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. + # + # +timeout+ is specified in milliseconds. + def wait_for_page_to_load timeout + command 'waitForPageToLoad', timeout + end + +private + # Generates the corresponding +_and_wait+ for each action. + def self.generate_and_wait_actions + public_instance_methods.each do |method| + define_method method + '_and_wait' do |*args| + make_command_waiting do + send method, *args + end + end + end + end + + generate_and_wait_actions +end + diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails_config.rb b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails_config.rb new file mode 100644 index 00000000..0cdcb50d --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/selenium_on_rails_config.rb @@ -0,0 +1,22 @@ +require 'yaml' + +class SeleniumOnRailsConfig + @@defaults = {:environments => ['test']} + def self.get var, default = nil + value = configs[var.to_s] + value ||= @@defaults[var] + value ||= default + value ||= yield if block_given? + value + end + + private + def self.configs + unless defined? @@configs + file = File.expand_path(File.dirname(__FILE__) + '/../config.yml') + @@configs = File.exist?(file) ? YAML.load_file(file) : {} + end + @@configs + end + +end diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/views/layout.rhtml b/tracks/vendor/plugins/selenium-on-rails/lib/views/layout.rhtml new file mode 100644 index 00000000..16f827fd --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/views/layout.rhtml @@ -0,0 +1,18 @@ + + + + Selenium on Rails<%= defined?(@page_title) ? ": #{@page_title}" : '' %> + + + +<%= @content_for_layout %> + + \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/views/record.rhtml b/tracks/vendor/plugins/selenium-on-rails/lib/views/record.rhtml new file mode 100644 index 00000000..65687553 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/views/record.rhtml @@ -0,0 +1,5 @@ + +<% @result.each_pair do |key, value| -%> + +<% end -%> +
<%= key %><%= value %>
diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/views/setup.rhtml b/tracks/vendor/plugins/selenium-on-rails/lib/views/setup.rhtml new file mode 100644 index 00000000..4091124b --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/views/setup.rhtml @@ -0,0 +1,67 @@ +<% @page_title = 'Setup' -%> +<% if defined?(@session_wiped) or @cleared_tables.any? or @loaded_fixtures.any? -%> +
+ <% if defined?(@session_wiped) -%> +

The session is wiped clean.

+ <% end-%> + <% if @cleared_tables.any? -%> +

The following database tables are cleared:

+
    + <% for table in @cleared_tables -%> +
  • <%= table %>
  • + <% end-%> +
+ <% end -%> + <% if @loaded_fixtures.any? -%> +

The following fixtures are loaded:

+
    + <% for fixture in @loaded_fixtures -%> +
  • <%= fixture %>
  • + <% end-%> +
+ <% end -%> +
+<% end -%> + +
+

This page can be used to setup your Selenium tests. The following options can be used:

+
+
keep_session
+
+ Per default the session is reset, so add keep_session in order to keep the current session. + + +
open<%= url_for %>?keep_session 
+
+
fixtures
+
+ Loads one or many fixtures into the database. This will destroy the current data you have in your database! Use all as name in order to load all fixtures, or specify which fixtures that should be loaded (delimited by commas).
+ If a test needs different data than you have in your fixtures, you can add another fixture set. A fixture set is just a sub directory in /test/fixtures/ where you can add alternate fixtures (e.g. /test/fixtures/blank/users.yml). + + + + +
open<%= url_for :fixtures => 'all' %> 
open<%= url_for :fixtures => 'fixture' %> 
open<%= url_for :fixtures => 'fixture_one' %>,fixture_two 
+ Available fixtures
+ <% fixtures = available_fixtures -%> + <% for fixture_set in fixtures.keys.sort -%> + In the <%= fixture_set.blank? ? 'default' : "#{fixture_set}" %> fixture set: +
    + <% fixtures[fixture_set].unshift fixture_set.blank? ? 'all' : "#{fixture_set}/all" -%> + <% for fixture in fixtures[fixture_set] -%> +
  • <%= fixture %>
  • + <% end -%> +
+ <% end -%> +
+
clear_tables
+
+ Clears one or many database tables. Another way to do the same thing is to create an empty fixture in a new fixture set (see fixtures above). + + + +
open<%= url_for :clear_tables => 'sessions' %> 
open<%= url_for :clear_tables => 'sessions' %>,outgoing_messages 
+
+
+ +
diff --git a/tracks/vendor/plugins/selenium-on-rails/lib/views/test_suite.rhtml b/tracks/vendor/plugins/selenium-on-rails/lib/views/test_suite.rhtml new file mode 100644 index 00000000..5973fc97 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/lib/views/test_suite.rhtml @@ -0,0 +1,26 @@ +<% @page_title = test_suite_name @suite_path -%> + + + + + +<% for name, path in test_cases @suite_path -%> + +<% end -%> +
<%= @page_title %>
<%= link_to_test_case name, path %>
\ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/routes.rb b/tracks/vendor/plugins/selenium-on-rails/routes.rb new file mode 100644 index 00000000..40985dcd --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/routes.rb @@ -0,0 +1,23 @@ +module ActionController + module Routing #:nodoc: + class RouteSet #:nodoc: + alias_method :draw_without_selenium_routes, :draw + def draw + draw_without_selenium_routes do |map| + map.connect 'selenium/setup', + :controller => 'selenium', :action => 'setup' + map.connect 'selenium/tests/*testname', + :controller => 'selenium', :action => 'test_file' + map.connect 'selenium/postResults', + :controller => 'selenium', :action => 'record' + map.connect 'selenium/postResults/:logFile', + :controller => 'selenium', :action => 'record', :requirements => { :logFile => /.*/ } + map.connect 'selenium/*filename', + :controller => 'selenium', :action => 'support_file' + + yield map + end + end + end + end +end diff --git a/tracks/vendor/plugins/selenium-on-rails/switch_environment/init.rb b/tracks/vendor/plugins/selenium-on-rails/switch_environment/init.rb new file mode 100644 index 00000000..687cb995 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/switch_environment/init.rb @@ -0,0 +1,20 @@ +#make sure the controller is accessible +$LOAD_PATH << File.dirname(__FILE__) +require 'switch_environment_controller' + +#hijack /selenium +module ActionController + module Routing #:nodoc: + class RouteSet #:nodoc: + alias_method :draw_without_selenium_routes, :draw + def draw + draw_without_selenium_routes do |map| + map.connect 'selenium/*filename', + :controller => 'switch_environment', :action => 'index' + + yield map + end + end + end + end +end diff --git a/tracks/vendor/plugins/selenium-on-rails/switch_environment/switch_environment_controller.rb b/tracks/vendor/plugins/selenium-on-rails/switch_environment/switch_environment_controller.rb new file mode 100644 index 00000000..51c1ca23 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/switch_environment/switch_environment_controller.rb @@ -0,0 +1,16 @@ +class SwitchEnvironmentController < ActionController::Base + def index + readme_path = File.expand_path File.join(File.dirname(__FILE__), '..', 'README') + render :status => 500, :locals => {:readme_path => readme_path }, :inline => < + Selenium on Rails is only activated for <%= SeleniumOnRailsConfig.get(:environments).join ', ' %> + environment<%= SeleniumOnRailsConfig.get(:environments).size > 1 ? 's' : '' %> (you're running + <%= RAILS_ENV %>). +

+

+ Start your server in a different environment or see <%= readme_path %> + for information regarding how to change this behavior. +

+END + end +end \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/tasks/test_acceptance.rake b/tracks/vendor/plugins/selenium-on-rails/tasks/test_acceptance.rake new file mode 100644 index 00000000..a9598ca6 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/tasks/test_acceptance.rake @@ -0,0 +1,8 @@ +task :test_acceptance => 'test:acceptance' +namespace :test do + desc 'Run Selenium tests in all browsers' + task :acceptance do + require File.dirname(__FILE__) + '/../lib/selenium_on_rails/acceptance_test_runner' + SeleniumOnRails::AcceptanceTestRunner.new.run + end +end diff --git a/tracks/vendor/plugins/selenium-on-rails/test/renderer_test.rb b/tracks/vendor/plugins/selenium-on-rails/test/renderer_test.rb new file mode 100644 index 00000000..189094a5 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test/renderer_test.rb @@ -0,0 +1,147 @@ +require File.dirname(__FILE__) + '/test_helper' + +class RendererTest < Test::Unit::TestCase + def setup + @controller = SeleniumController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + @controller.layout_override =<test layout +@content_for_layout + +END + end + + def test_route + get :test_file, :testname => 'html.html' #initialize the controller + assert_equal 'http://test.host/selenium/tests/suite/test_case.sel', + @controller.url_for(:controller => 'selenium', :action => 'test_file', :testname => 'suite/test_case.sel') + end + + def test_html + get :test_file, :testname => 'html.html' + assert_headers + expected =<test layout +

Testing plain HTML

+ + + +
Test HTML
open/selenium/setup 
+

and it works...

+ +END + assert_text_equal expected, @response.body + end + + def test_rhtml + get :test_file, :testname => 'rhtml.rhtml' + assert_headers + expected =<test layout + + + + + + +
Rhtml
open/fi 
open/fo 
open/fum 
assertTitlePartial from RHTML 
+ +END + assert_text_equal expected, @response.body + end + + def test_selenese + get :test_file, :testname => 'selenese.sel' + assert_headers + expected =<test layout +

Selenese support

+ + + + + +
Selenese
open/selenium/setup 
goBack  
assertTitlePartial from Selenese 
+

works.

+ + +END + assert_text_equal expected, @response.body + end + + def test_rselenese + get :test_file, :testname => 'rselenese.rsel' + assert_headers + expected = <test layout + + + + + + + + + + +
Rselenese
open/selenium/setup 
open/selenium/setup?keep_session=true 
open/selenium/setup?fixtures=all 
open/selenium/setup?fixtures=foo%2Cbar 
open/selenium/setup?fixtures=all&amp;clear_tables=foo%2Cbar 
assertAbsoluteLocationexact:http://test.host/selenium/setup 
assertTitleselenium 
assertTitlePartial from RSelenese 
+ +END + assert_text_equal expected, @response.body + end + + def test_partial_support + get :test_file, :testname => 'partials/all_partials.rsel' + assert_headers + expected = <test layout + + + + + + + + + +
All partials
assertTitlePartial from All partials 
typepartialHTML partial
typeworldRHTML partial
typepartialSelenese partial
typeworldRSelenese partial
typenestingNesting partial
typedlrowRSelenese partial
+ +END + assert_text_equal expected, @response.body + end + + def test_own_layout + get :test_file, :testname => 'own_layout.html' + assert_headers + expected =< + + Test case with own layout + + + + + + +
Test own layout
open/selenium/setup 
+ + +END + assert_text_equal expected, @response.body + end + + def test_not_found + get :test_file, :testname => 'missing' + assert_response 404 + assert_equal 'Not found', @response.body + end + + def assert_headers + assert_response :success + assert_equal 'no-cache', @response.headers['Cache-control'] + assert_equal 'no-cache', @response.headers['Pragma'] + assert_equal '-1', @response.headers['Expires'] + end + +end diff --git a/tracks/vendor/plugins/selenium-on-rails/test/rselenese_test.rb b/tracks/vendor/plugins/selenium-on-rails/test/rselenese_test.rb new file mode 100644 index 00000000..b85ed581 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test/rselenese_test.rb @@ -0,0 +1,403 @@ +require File.dirname(__FILE__) + '/test_helper' + +class RSeleneseTest < Test::Unit::TestCase + include ERB::Util + + def rselenese name, input, partial = nil, type = nil + view = TestView.new + view.override_partial partial, type do + view.assigns['page_title'] = name + view.render_template 'rsel', input + end + end + + def assert_rselenese expected, name, input, partial = nil, type = nil + assert_text_equal(expected, rselenese(name, input, partial, type)) + end + + def test_empty + expected = < +Empty + +END + input = '' + assert_rselenese expected, 'Empty', input + end + + def assert_generates_command expected, name, *args + expected = expected.map {|v| h(v) } + expected << ' ' while expected.length < 3 + expected = expected.map {|v| "#{v}" }.join + expected_html = < +Selenese Commands +#{expected} + +END + args_str = args.map {|a| a.inspect }.join(',') + input = "#{name}(#{args_str})" + assert_rselenese expected_html, 'Selenese Commands', input + end + + def test_element_locators + assert_generates_command %w{click aCheckbox}, :click, 'aCheckbox' + assert_generates_command %w{click document.foo}, :click, 'document.foo' + assert_generates_command %w{click //a}, :click, '//a' + end + + def test_collection_arguments + assert_generates_command ['assertAllLinks', 'link1,link2,link3'], :assert_all_links, ['link1', 'link2','link3'] + assert_generates_command ['assertAllLinks', 'link?,link?,link?'], :assert_all_links, 'link?,link?,link?' + end + + ARG_VALUE_MAP = { + # We can't test url_for style arguments here, because we don't have + # a valid controller to interpret them. See RendererTest. + :url => '/relative/url', + :string => '1234', + :pattern => 'glob:J* Smith', # Also: many other formats. + :variable => 'varname', + :locator => 'foo', + :script => 'script', + :locator_and_attribute_name => [['foo', 'attribute'], 'foo@attribute'], + :table_locator => [['table', 2, 4], 'table.2.4'], + :coll_pattern => [[['a', "b\\", 'c,']], "a,b\\\\,c\\,"], + :event_name => 'eventName', + :keycode => 123, + :option_locator => 'label=hello', + :window_id => [[nil], 'null'], + :timeout => 123, + :log_level => :debug + } + + # Call _command_ with _args_ and make sure it produces a good table. + # If the input command doesn't 'selenize' cleanly (e.g. if the input command + # is :do_foo and the expected result is +dofoo+ and not +doFoo+) +command+ + # can be specified as an array (e.g. +[:input_command, 'expectedResult']). + def assert_command_works command, *args + expected_values = args.inject([]) do |c, arg| + v = ARG_VALUE_MAP[arg] + if v.is_a? Array + c << v[1] + else + c << v + end + end + input_values = args.inject([]) do |c, arg| + v = ARG_VALUE_MAP[arg] + if v.is_a? Array + c.concat v[0] + else + c << v + end + end + input_name, expected_name = (command.is_a?(Array) ? command : [command, SeleniumOnRails::TestBuilder.selenize(command.to_s)]) + assert_generates_command [expected_name]+expected_values, input_name.to_s, *input_values + end + + def test_action_commands + assert_command_works :click, :locator + assert_command_works :click_and_wait, :locator + assert_command_works :fire_event, :locator, :event_name + assert_command_works :fire_event_and_wait, :locator, :event_name + assert_command_works :key_press, :locator, :keycode + assert_command_works :key_press_and_wait, :locator, :keycode + assert_command_works :key_down, :locator, :keycode + assert_command_works :key_down_and_wait, :locator, :keycode + assert_command_works :key_up, :locator, :keycode + assert_command_works :key_up_and_wait, :locator, :keycode + assert_command_works :mouse_over, :locator + assert_command_works :mouse_over_and_wait, :locator + assert_command_works :mouse_down, :locator + assert_command_works :mouse_down_and_wait, :locator + assert_command_works :type, :locator, :string + assert_command_works :type_and_wait, :locator, :string + assert_command_works :check, :locator + assert_command_works :check_and_wait, :locator + assert_command_works :uncheck, :locator + assert_command_works :uncheck_and_wait, :locator + assert_command_works :select, :locator, :option_locator + assert_command_works :select_and_wait, :locator, :option_locator + assert_command_works :add_selection, :locator, :option_locator + assert_command_works :add_selection_and_wait, :locator, :option_locator + assert_command_works :remove_selection, :locator, :option_locator + assert_command_works :remove_selection_and_wait, :locator, :option_locator + assert_command_works :submit, :locator + assert_command_works :open, :url + assert_command_works :select_window, :window_id + assert_command_works [:wait_for_popup, 'waitForPopUp'], :window_id, :timeout + assert_command_works :choose_cancel_on_next_confirmation + assert_command_works :choose_cancel_on_next_confirmation_and_wait + assert_command_works :answer_on_next_prompt, :string + assert_command_works :answer_on_next_prompt_and_wait, :string + assert_command_works :go_back + assert_command_works :refresh + assert_command_works :close + assert_command_works :set_context, :string + assert_command_works :set_context, :string, :log_level + assert_command_works :wait_for_condition, :script, :timeout + assert_command_works :set_timeout, :timeout + assert_command_works :wait_for_page_to_load, :timeout + end + + def test_accessor_commands + assert_command_works :store_alert_present, :variable + assert_command_works :assert_alert_present + assert_command_works :assert_alert_not_present + assert_command_works :verify_alert_present + assert_command_works :verify_alert_not_present + assert_command_works :wait_for_alert_present + assert_command_works :wait_for_alert_not_present + + assert_command_works :store_prompt_present, :variable + assert_command_works :assert_prompt_present + assert_command_works :assert_prompt_not_present + assert_command_works :verify_prompt_present + assert_command_works :verify_prompt_not_present + assert_command_works :wait_for_prompt_present + assert_command_works :wait_for_prompt_not_present + + assert_command_works :store_confirmation_present, :variable + assert_command_works :assert_confirmation_present + assert_command_works :assert_confirmation_not_present + assert_command_works :verify_confirmation_present + assert_command_works :verify_confirmation_not_present + assert_command_works :wait_for_confirmation_present + assert_command_works :wait_for_confirmation_not_present + + assert_command_works :store_alert, :variable + assert_command_works :assert_alert, :pattern + assert_command_works :assert_not_alert, :pattern + assert_command_works :verify_alert, :pattern + assert_command_works :verify_not_alert, :pattern + assert_command_works :wait_for_alert, :pattern + assert_command_works :wait_for_not_alert, :pattern + + assert_command_works :store_confirmation, :variable + assert_command_works :assert_confirmation, :pattern + assert_command_works :assert_not_confirmation, :pattern + assert_command_works :verify_confirmation, :pattern + assert_command_works :verify_not_confirmation, :pattern + assert_command_works :wait_for_confirmation, :pattern + assert_command_works :wait_for_not_confirmation, :pattern + + assert_command_works :store_prompt, :variable + assert_command_works :assert_prompt, :pattern + assert_command_works :assert_not_prompt, :pattern + assert_command_works :verify_prompt, :pattern + assert_command_works :verify_not_prompt, :pattern + assert_command_works :wait_for_prompt, :pattern + assert_command_works :wait_for_not_prompt, :pattern + + assert_command_works :store_absolute_location, :variable + assert_command_works :assert_absolute_location, :url + assert_command_works :assert_not_absolute_location, :url + assert_command_works :verify_absolute_location, :url + assert_command_works :verify_not_absolute_location, :url + assert_command_works :wait_for_absolute_location, :url + assert_command_works :wait_for_not_absolute_location, :url + + assert_command_works :store_location, :pattern, :variable + assert_command_works :assert_location, :url + assert_command_works :assert_not_location, :url + assert_command_works :verify_location, :url + assert_command_works :verify_not_location, :url + assert_command_works :wait_for_location, :url + assert_command_works :wait_for_not_location, :url + + assert_command_works :store_title, :variable + assert_command_works :assert_title, :pattern + assert_command_works :assert_not_title, :pattern + assert_command_works :verify_title, :pattern + assert_command_works :verify_not_title, :pattern + assert_command_works :wait_for_title, :pattern + assert_command_works :wait_for_not_title, :pattern + + assert_command_works :store_body_text, :variable + assert_command_works :assert_body_text, :pattern + assert_command_works :assert_not_body_text, :pattern + assert_command_works :verify_body_text, :pattern + assert_command_works :verify_not_body_text, :pattern + assert_command_works :wait_for_body_text, :pattern + assert_command_works :wait_for_not_body_text, :pattern + + assert_command_works :store_value, :locator, :variable + assert_command_works :assert_value, :locator, :pattern + assert_command_works :assert_not_value, :locator, :pattern + assert_command_works :verify_value, :locator, :pattern + assert_command_works :verify_not_value, :locator, :pattern + assert_command_works :wait_for_value, :locator, :pattern + assert_command_works :wait_for_not_value, :locator, :pattern + + assert_command_works :store_text, :locator, :variable + assert_command_works :assert_text, :locator, :pattern + assert_command_works :assert_not_text, :locator, :pattern + assert_command_works :verify_text, :locator, :pattern + assert_command_works :verify_not_text, :locator, :pattern + assert_command_works :wait_for_text, :locator, :pattern + assert_command_works :wait_for_not_text, :locator, :pattern + + assert_command_works :store_eval, :script, :variable + assert_command_works :assert_eval, :script, :pattern + assert_command_works :assert_not_eval, :script, :pattern + assert_command_works :verify_eval, :script, :pattern + assert_command_works :verify_not_eval, :script, :pattern + assert_command_works :wait_for_eval, :script, :pattern + assert_command_works :wait_for_not_eval, :script, :pattern + + assert_command_works :store_checked, :locator, :variable + assert_command_works :assert_checked, :locator, :pattern + assert_command_works :assert_not_checked, :locator, :pattern + assert_command_works :verify_checked, :locator, :pattern + assert_command_works :verify_not_checked, :locator, :pattern + assert_command_works :wait_for_checked, :locator, :pattern + assert_command_works :wait_for_not_checked, :locator, :pattern + + assert_command_works :store_table, :table_locator, :variable + assert_command_works :assert_table, :table_locator, :pattern + assert_command_works :assert_not_table, :table_locator, :pattern + assert_command_works :verify_table, :table_locator, :pattern + assert_command_works :verify_not_table, :table_locator, :pattern + assert_command_works :wait_for_table, :table_locator, :pattern + assert_command_works :wait_for_not_table, :table_locator, :pattern + + assert_raise RuntimeError do + assert_command_works :store_selected, :locator, :option_locator, :variable + end + assert_command_works :assert_selected, :locator, :option_locator + assert_command_works :assert_not_selected, :locator, :option_locator + assert_command_works :verify_selected, :locator, :option_locator + assert_command_works :verify_not_selected, :locator, :option_locator + assert_command_works :wait_for_selected, :locator, :option_locator + assert_command_works :wait_for_not_selected, :locator, :option_locator + + assert_command_works :store_selected_options, :locator, :variable + assert_command_works :assert_selected_options, :locator, :coll_pattern + assert_command_works :assert_not_selected_options, :locator, :coll_pattern + assert_command_works :verify_selected_options, :locator, :coll_pattern + assert_command_works :verify_not_selected_options, :locator, :coll_pattern + assert_command_works :wait_for_selected_options, :locator, :coll_pattern + assert_command_works :wait_for_not_selected_options, :locator, :coll_pattern + + assert_command_works :store_select_options, :locator, :variable + assert_command_works :assert_select_options, :locator, :coll_pattern + assert_command_works :assert_not_select_options, :locator, :coll_pattern + assert_command_works :verify_select_options, :locator, :coll_pattern + assert_command_works :verify_not_select_options, :locator, :coll_pattern + assert_command_works :wait_for_select_options, :locator, :coll_pattern + assert_command_works :wait_for_not_select_options, :locator, :coll_pattern + + assert_command_works :store_attribute, :locator_and_attribute_name, :variable + assert_command_works :assert_attribute, :locator_and_attribute_name, :pattern + assert_command_works :assert_not_attribute, :locator_and_attribute_name, :pattern + assert_command_works :verify_attribute, :locator_and_attribute_name, :pattern + assert_command_works :verify_not_attribute, :locator_and_attribute_name, :pattern + assert_command_works :wait_for_attribute, :locator_and_attribute_name, :pattern + assert_command_works :wait_for_not_attribute, :locator_and_attribute_name, :pattern + + assert_command_works :store_text_present, :pattern, :variable + assert_command_works :assert_text_present, :pattern + assert_command_works :assert_text_not_present, :pattern + assert_command_works :verify_text_present, :pattern + assert_command_works :verify_text_not_present, :pattern + assert_command_works :wait_for_text_present, :pattern + assert_command_works :wait_for_text_not_present, :pattern + + assert_command_works :store_element_present, :locator, :variable + assert_command_works :assert_element_present, :locator + assert_command_works :assert_element_not_present, :locator + assert_command_works :verify_element_present, :locator + assert_command_works :verify_element_not_present, :locator + assert_command_works :wait_for_element_present, :locator + assert_command_works :wait_for_element_not_present, :locator + + assert_command_works :store_visible, :locator, :variable + assert_command_works :assert_visible, :locator + assert_command_works :assert_not_visible, :locator + assert_command_works :verify_visible, :locator + assert_command_works :verify_not_visible, :locator + assert_command_works :wait_for_visible, :locator + assert_command_works :wait_for_not_visible, :locator + + assert_command_works :store_editable, :locator, :variable + assert_command_works :assert_editable, :locator + assert_command_works :assert_not_editable, :locator + assert_command_works :verify_editable, :locator + assert_command_works :verify_not_editable, :locator + assert_command_works :wait_for_editable, :locator + assert_command_works :wait_for_not_editable, :locator + + assert_command_works :store_all_buttons, :variable + assert_command_works :assert_all_buttons, :coll_pattern + assert_command_works :assert_not_all_buttons, :coll_pattern + assert_command_works :verify_all_buttons, :coll_pattern + assert_command_works :verify_not_all_buttons, :coll_pattern + assert_command_works :wait_for_all_buttons, :coll_pattern + assert_command_works :wait_for_not_all_buttons, :coll_pattern + + assert_command_works :store_all_links, :variable + assert_command_works :assert_all_links, :coll_pattern + assert_command_works :assert_not_all_links, :coll_pattern + assert_command_works :verify_all_links, :coll_pattern + assert_command_works :verify_not_all_links, :coll_pattern + assert_command_works :wait_for_all_links, :coll_pattern + assert_command_works :wait_for_not_all_links, :coll_pattern + + assert_command_works :store_all_fields, :variable + assert_command_works :assert_all_fields, :coll_pattern + assert_command_works :assert_not_all_fields, :coll_pattern + assert_command_works :verify_all_fields, :coll_pattern + assert_command_works :verify_not_all_fields, :coll_pattern + assert_command_works :wait_for_all_fields, :coll_pattern + assert_command_works :wait_for_not_all_fields, :coll_pattern + + assert_command_works :store_html_source, :variable + assert_command_works :assert_html_source, :pattern + assert_command_works :assert_not_html_source, :pattern + assert_command_works :verify_html_source, :pattern + assert_command_works :verify_not_html_source, :pattern + assert_command_works :wait_for_html_source, :pattern + assert_command_works :wait_for_not_html_source, :pattern + + assert_command_works :store_expression, :script, :variable + assert_command_works :assert_expression, :script, :pattern + assert_command_works :assert_not_expression, :script, :pattern + assert_command_works :verify_expression, :script, :pattern + assert_command_works :verify_not_expression, :script, :pattern + assert_command_works :wait_for_expression, :script, :pattern + assert_command_works :wait_for_not_expression, :script, :pattern + end + + def test_partial_support + expected = < +Partial support +typepartialRSelenese partial + +END + input = "include_partial 'override'" + partial = "type 'partial', 'RSelenese partial'" + assert_rselenese expected, 'Partial support', input, partial, 'rsel' + end + + def test_partial_support_with_local_assigns + expected = < +Partial support with local variables +typepartialRSelenese partial +typelocalpar +typelocaltial + +END_EXPECTED + input = "include_partial 'override', :locator => 'local', :input => ['par', 'tial']" + partial = < +Empty + +END + input = '' + assert_selenese expected, 'Empty', '' + end + + def test_one_line + expected = < +One line +open/  + +END + input = '|open|/|' + assert_selenese expected, 'One line', input + end + + def test_comments_only + expected = <Comment 1

+ + +

Comment 2

+ + +
Only comments
+END + input = < +Only commands +goBack   +open/foo  +fireEventtextFieldfocus + +END + input = < +Commands and comments +goBack   +fireEventtextFieldfocus + +

Comment 1

+ + +

Comment 2

+END + input = <Comment 1

+ + +

Comment 2

+ + + + +
Comments and commands
goBack  
fireEventtextFieldfocus
+END + input = <Comment 1

+ + +

Comment 2

+ + + + +
Comments, commands and comments
goBack  
fireEventtextFieldfocus
+

Comment 3

+END + input = < +HTML escaping +typenameField<>& + +END + input = '|type|nameField|<>&|' + assert_selenese expected, 'HTML escaping', input + end + + def test_partial_support + expected = < +Partial support +typepartialSelenese partial + +END + input = '|includePartial|override|' + partial = '|type|partial|Selenese partial|' + assert_selenese expected, 'Partial support', input, partial, 'sel' + end + + def test_partial_support_with_local_assigns + expected = < +Partial support with local assigns +typeassignsa=hello,b=world!,c_123ABC= +typeassignsa=a b c d,b=,c_123ABC=hello + +END_EXPECTED + input = <whatever +typeassigns +a=<%= a if defined? a%>, +b=<%= b if defined? b%>, +c_123ABC=<%= c_123ABC if defined? c_123ABC%> + + +END_PARTIAL + assert_selenese expected, 'Partial support with local assigns', input, partial, 'rhtml' + end + + def test_raised_when_more_than_three_columns + assert_raise RuntimeError, 'There might only be a maximum of three cells!' do + selenese 'name', '|col1|col2|col3|col4|' + end + end + + def test_raised_when_more_than_one_set_of_commands + assert_raise RuntimeError, 'You cannot have comments in the middle of commands!' do + input = < + + + + +
Foo
Bar
+EOS + post :record, :suite => suite, + "testTable.1" => "
", + "testTable.2" => "
" + cur_result_dir = File.join(@result_dir, "default") + assert File.directory?(cur_result_dir) + assert_equal ["blank.html", "index.html", "suite.html", "test1.html", "test2.html"], + Dir.glob("#{cur_result_dir}/*.html").map{|path| File.basename(path)}.sort + expected = < + + + + + + + +
Foo
Bar
+ +EOS + assert_equal expected, File.read("#{cur_result_dir}/suite.html") + end +end diff --git a/tracks/vendor/plugins/selenium-on-rails/test/selenium_support_test.rb b/tracks/vendor/plugins/selenium-on-rails/test/selenium_support_test.rb new file mode 100644 index 00000000..6502461a --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test/selenium_support_test.rb @@ -0,0 +1,33 @@ +require File.dirname(__FILE__) + '/test_helper' + +class SeleniumSupportTest < Test::Unit::TestCase + def setup + @controller = SeleniumController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + end + + def test_route + get :support_file, :filename => 'TestRunner.html' #initialize the controller + assert_equal 'http://test.host/selenium/TestRunner.html', + @controller.url_for(:controller => 'selenium', :action => 'support_file', :filename => 'TestRunner.html') + end + + def test_test_runner_existance + get :support_file, :filename => 'TestRunner.html' + assert_response :success + assert @response.body.include?('Selenium') + end + + def test_default_file + get :support_file, :filename => '' + assert_redirected_to :filename => 'TestRunner.html', :test => 'tests' + end + + def test_missing_file + get :support_file, :filename => 'missing.html' + assert_response 404 + assert_equal 'Not found', @response.body + end + +end diff --git a/tracks/vendor/plugins/selenium-on-rails/test/setup_test.rb b/tracks/vendor/plugins/selenium-on-rails/test/setup_test.rb new file mode 100644 index 00000000..ad2e4c3c --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test/setup_test.rb @@ -0,0 +1,29 @@ +require File.dirname(__FILE__) + '/test_helper' + +class SetupTest < Test::Unit::TestCase + def setup + @controller = SeleniumController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + end + + def test_session_reset + @request.session['key'] = 'value' + get :setup + assert_nil session['key'] + assert_response :success + assert_tag :content => 'The session is wiped clean.' + end + + def test_session_no_reset + @request.session['key'] = 'value' + get :setup, :keep_session => true + assert_equal 'value', session['key'] + assert_response :success + assert_no_tag :content => 'The session is wiped clean.' + end + + # + # Don't have the nerve to test fixtures since this is a plugin + # +end diff --git a/tracks/vendor/plugins/selenium-on-rails/test/suite_renderer_test.rb b/tracks/vendor/plugins/selenium-on-rails/test/suite_renderer_test.rb new file mode 100644 index 00000000..07fe568f --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test/suite_renderer_test.rb @@ -0,0 +1,173 @@ +require File.dirname(__FILE__) + '/test_helper' + +class SuiteRendererTest < Test::Unit::TestCase + def setup + @controller = SeleniumController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + @controller.layout_override =<test layout +@content_for_layout + +END + end + + def test_empty_suite + get :test_file, :testname => 'empty_suite' + assert_response :success + expected =<test layout + + + + + +
Empty suite
+ +END + assert_text_equal expected, @response.body + end + + + def test_root_suite + _test_root_suite '' + end + + def test_test_suite_html + #TestSuite.html is the default name the Selenium Runner tries to run + _test_root_suite 'TestSuite.html' + end + + def _test_root_suite testname + get :test_file, :testname => testname + assert_response :success + expected =<test layout + + + + + + + + + + + + + + + +
All test cases
Html
Own layout
Rhtml
Rselenese
Selenese
Partials.All partials
Suite one.Suite one testcase1
Suite one.Suite one testcase2
Suite one.Subsuite.Suite one subsuite testcase
Suite two.Suite two testcase
+ +END + assert_text_equal expected, @response.body + end + + def test_suite_one + get :test_file, :testname => 'suite_one' + assert_response :success + expected =<test layout + + + + + + + + +
Suite one
Suite one testcase1
Suite one testcase2
Subsuite.Suite one subsuite testcase
+ +END + assert_text_equal expected, @response.body + end + + def test_sub_suite + get :test_file, :testname => 'suite_one/subsuite' + assert_response :success + expected =<test layout + + + + + + +
Subsuite
Suite one subsuite testcase
+ +END + assert_text_equal expected, @response.body + end + + def test_missing_tests_directory + def @controller.selenium_tests_path + File.join(File.dirname(__FILE__), 'invalid') + end + get :test_file, :testname => '' + assert_response 404 + assert_equal "Did not find the Selenium tests path (#{File.join(File.dirname(__FILE__), 'invalid')}). Run script/generate selenium", @response.body + end + +end diff --git a/tracks/vendor/plugins/selenium-on-rails/test/test_helper.rb b/tracks/vendor/plugins/selenium-on-rails/test/test_helper.rb new file mode 100644 index 00000000..7c8e4705 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test/test_helper.rb @@ -0,0 +1,70 @@ +ENV["RAILS_ENV"] = "test" +require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment") +require 'test_help' +require 'controllers/selenium_controller' + +module SeleniumOnRails::Paths + def selenium_tests_path + File.expand_path(File.dirname(__FILE__) + '/../test_data') + end +end + +class SeleniumController + attr_accessor :layout_override + # Re-raise errors caught by the controller. + def rescue_action e + raise e + end + + def render options = nil, deprecated_status = nil + if override_layout? options + options[:layout] = false + super options, deprecated_status + return response.body = @layout_override.gsub('@content_for_layout', response.body) + end + super options, deprecated_status + end + + private + def override_layout? options + return false unless @layout_override + if options[:action] or options[:template] + options[:layout] != false #for action and template the default layout is used if not explicitly disabled + else + not [nil, false].include? options[:layout] #otherwise a layout has to be specified + end + end + +end + +class Test::Unit::TestCase + def assert_text_equal expected, actual + assert_equal clean_text(expected), clean_text(actual) + end + + def clean_text text + text.gsub("\t", ' ').gsub("\r", '').gsub("\n", '').gsub(/ * @override, :type => @override_type, :locals => local_assigns + extract_commands_from_partial partial + else + render_partial_without_override partial_path, object, local_assigns, status + end + end + + def override_partial partial, type + @override, @override_type = partial, type + result = yield + @override, @override_type = nil, nil + result + end +end \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/_partial.rsel b/tracks/vendor/plugins/selenium-on-rails/test_data/_partial.rsel new file mode 100644 index 00000000..d6c575a7 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/_partial.rsel @@ -0,0 +1 @@ +assert_title "Partial from #{source}" \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/html.html b/tracks/vendor/plugins/selenium-on-rails/test_data/html.html new file mode 100644 index 00000000..1a0addb8 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/html.html @@ -0,0 +1,6 @@ +

Testing plain HTML

+ + + +
Test HTML
open/selenium/setup 
+

and it works...

\ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/own_layout.html b/tracks/vendor/plugins/selenium-on-rails/test_data/own_layout.html new file mode 100644 index 00000000..a45c5498 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/own_layout.html @@ -0,0 +1,12 @@ + + + Test case with own layout + + + + + + +
Test own layout
open/selenium/setup 
+ + diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_html.html b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_html.html new file mode 100644 index 00000000..916ca4f7 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_html.html @@ -0,0 +1,6 @@ +

This should never be visible!

+ + + +
HTML partial
typepartialHTML partial
+

Neither should this!

\ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_nesting.rsel b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_nesting.rsel new file mode 100644 index 00000000..28e7bf91 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_nesting.rsel @@ -0,0 +1,2 @@ +type 'nesting', 'Nesting partial' +include_partial 'partials/rsel', :hello => hello.reverse \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_rhtml.rhtml b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_rhtml.rhtml new file mode 100644 index 00000000..af6599de --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_rhtml.rhtml @@ -0,0 +1,6 @@ +

This should never be visible!

+ + + +
RHTML partial
type<%= hello %>RHTML partial
+

Neither should this!

\ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_rsel.rsel b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_rsel.rsel new file mode 100644 index 00000000..0ce0e9fe --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_rsel.rsel @@ -0,0 +1 @@ +type hello, 'RSelenese partial' diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_sel.sel b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_sel.sel new file mode 100644 index 00000000..353f0f26 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/_sel.sel @@ -0,0 +1,5 @@ +h1. This should not be visible! + +|type|partial|Selenese partial| + +p. Neither should this! \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/partials/all_partials.rsel b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/all_partials.rsel new file mode 100644 index 00000000..08c0faf5 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/partials/all_partials.rsel @@ -0,0 +1,5 @@ +include_partial 'partial', :source => title +['html', 'rhtml', 'sel', 'rsel'].each do |format| + include_partial "partials/#{format}", :hello => 'world' +end +include_partial 'partials/nesting', :hello => 'world' \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/rhtml.rhtml b/tracks/vendor/plugins/selenium-on-rails/test_data/rhtml.rhtml new file mode 100644 index 00000000..2ae4e413 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/rhtml.rhtml @@ -0,0 +1,7 @@ + + +<% for page in ['/fi', '/fo', '/fum'] -%> + +<% end -%> + <%= render :partial => 'partial', :locals => {:source => 'RHTML'} %> +
<%= @page_title %>
open<%= page %> 
\ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/rselenese.rsel b/tracks/vendor/plugins/selenium-on-rails/test_data/rselenese.rsel new file mode 100644 index 00000000..0c8e9d59 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/rselenese.rsel @@ -0,0 +1,8 @@ +setup +setup :keep_session +test.setup :fixtures => :all +setup :fixtures => [:foo, 'bar'] +setup :clear_tables => [:foo, :bar], :fixtures => :all +assert_absolute_location :controller => 'selenium', :action => 'setup' #urls must be tested with a controller +assert_title view.controller.controller_name #make sure we can access the view easily +include_partial 'partial', :source => 'RSelenese' diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/selenese.sel b/tracks/vendor/plugins/selenium-on-rails/test_data/selenese.sel new file mode 100644 index 00000000..46b1dce9 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/selenese.sel @@ -0,0 +1,7 @@ +Selenese *support* + +|open|/selenium/setup| +|goBack| +|includePartial|partial|source=Selenese| + +works. \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/subsuite/suite_one_subsuite_testcase.sel b/tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/subsuite/suite_one_subsuite_testcase.sel new file mode 100644 index 00000000..9c3209b6 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/subsuite/suite_one_subsuite_testcase.sel @@ -0,0 +1 @@ +|open|/| \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase1.sel b/tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase1.sel new file mode 100644 index 00000000..9c3209b6 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase1.sel @@ -0,0 +1 @@ +|open|/| \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase2.sel b/tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase2.sel new file mode 100644 index 00000000..9c3209b6 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase2.sel @@ -0,0 +1 @@ +|open|/| \ No newline at end of file diff --git a/tracks/vendor/plugins/selenium-on-rails/test_data/suite_two/suite_two_testcase.sel b/tracks/vendor/plugins/selenium-on-rails/test_data/suite_two/suite_two_testcase.sel new file mode 100644 index 00000000..9c3209b6 --- /dev/null +++ b/tracks/vendor/plugins/selenium-on-rails/test_data/suite_two/suite_two_testcase.sel @@ -0,0 +1 @@ +|open|/| \ No newline at end of file diff --git a/tracks/vendor/selenium/Blank.html b/tracks/vendor/selenium/Blank.html new file mode 100644 index 00000000..cb4432c0 --- /dev/null +++ b/tracks/vendor/selenium/Blank.html @@ -0,0 +1,8 @@ + + + + + +

selenium-rc initial page

+ + diff --git a/tracks/vendor/selenium/InjectedRemoteRunner.html b/tracks/vendor/selenium/InjectedRemoteRunner.html new file mode 100644 index 00000000..cb4432c0 --- /dev/null +++ b/tracks/vendor/selenium/InjectedRemoteRunner.html @@ -0,0 +1,8 @@ + + + + + +

selenium-rc initial page

+ + diff --git a/tracks/vendor/selenium/RemoteRunner.html b/tracks/vendor/selenium/RemoteRunner.html new file mode 100644 index 00000000..74d55579 --- /dev/null +++ b/tracks/vendor/selenium/RemoteRunner.html @@ -0,0 +1,109 @@ + + + + + + +Selenium Functional Test Runner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + +

Selenium Functional Testing for Web Apps

+ Open Source From ThoughtWorks, Inc and Friends +
+
Slow Mode: + +
+ Tools + + + +
+ +
+ +
+
+ +
+
+ Last Four Test Commands:
+
+
+ +
+ + + + diff --git a/tracks/vendor/selenium/SeleniumLog.html b/tracks/vendor/selenium/SeleniumLog.html new file mode 100644 index 00000000..dffa184f --- /dev/null +++ b/tracks/vendor/selenium/SeleniumLog.html @@ -0,0 +1,74 @@ + + + +Selenium Log Console + + + + + + + + + +
    + + + diff --git a/tracks/vendor/selenium/TestPrompt.html b/tracks/vendor/selenium/TestPrompt.html new file mode 100644 index 00000000..58ba1cb0 --- /dev/null +++ b/tracks/vendor/selenium/TestPrompt.html @@ -0,0 +1,123 @@ + + + + + + Select a Test Suite + + + + + +
    + +

    + Test Suite: + +

    + +

    + +
    + Options + +

    + + +

    + +

    + +

    + + +
    + + +
    + + \ No newline at end of file diff --git a/tracks/vendor/selenium/TestRunner-splash.html b/tracks/vendor/selenium/TestRunner-splash.html new file mode 100644 index 00000000..da2acdce --- /dev/null +++ b/tracks/vendor/selenium/TestRunner-splash.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + +
    Test SuiteCurrent TestControl Panel
     
    + + + +

    Selenium

    +

    by ThoughtWorks and friends

    + +

    +For more information on Selenium, visit + +

    +    http://selenium.openqa.org
    +
    + +
    + + diff --git a/tracks/vendor/selenium/TestRunner.hta b/tracks/vendor/selenium/TestRunner.hta new file mode 100644 index 00000000..848ca7dc --- /dev/null +++ b/tracks/vendor/selenium/TestRunner.hta @@ -0,0 +1,175 @@ + + + + + + + + + + + Selenium Functional Test Runner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    +

    Selenium TestRunner +

    +
    +
    + Execute Tests + +
    + + + + +
    + +
    Fast
    +
    Slow
    +
    +
    +
     
    +
     
    +
    + +
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Elapsed:00.00
    TestsCommands
    0run0passed
    0failed0failed
    0incomplete
    + +
    + Tools + + + + +
    + +
    +
    + +
    + + + diff --git a/tracks/vendor/selenium/TestRunner.html b/tracks/vendor/selenium/TestRunner.html new file mode 100644 index 00000000..848ca7dc --- /dev/null +++ b/tracks/vendor/selenium/TestRunner.html @@ -0,0 +1,175 @@ + + + + + + + + + + + Selenium Functional Test Runner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    +

    Selenium TestRunner +

    +
    +
    + Execute Tests + +
    + + + + +
    + +
    Fast
    +
    Slow
    +
    +
    +
     
    +
     
    +
    + +
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Elapsed:00.00
    TestsCommands
    0run0passed
    0failed0failed
    0incomplete
    + +
    + Tools + + + + +
    + +
    +
    + +
    + + + diff --git a/tracks/vendor/selenium/domviewer/butmin.gif b/tracks/vendor/selenium/domviewer/butmin.gif new file mode 100644 index 0000000000000000000000000000000000000000..7b7cefd5d6cdbd093c4351cc8edb1d8386a63247 GIT binary patch literal 843 zcmb`GKWfx*5XFbsB*jWVZN#;(N|7QkjUw3juhS!lFlVp|O9B?bLdyXgZ(wP;2!Ryu z5IKZkIey;v3=S;6ot>FC@4fl$>$k6-KKrnVP5ceVZhdy^u_N0#_WS+t`^2mVPrecS z_WvRyDxxFgO=Ovo71@!B92YC2DypLtHSX<TW$?WU8w~On9ws~E}y~}93zP$73vcpGM z3XQZ#`zDKrffqEd16RU4G_mT`Z?pdkad;M+qepMP-d*wwM^Je?-;^f

    #O?@9)JJz{>jTP+wJ+En|B|7U){ZP5YPVs26b!g literal 0 HcmV?d00001 diff --git a/tracks/vendor/selenium/domviewer/butplus.gif b/tracks/vendor/selenium/domviewer/butplus.gif new file mode 100644 index 0000000000000000000000000000000000000000..6d68cfa9f1aa5c7f2cafc6a742aa1352985c1f3c GIT binary patch literal 848 zcmb`Gv1*iY5X85zh{Zv`fGBo?l?G)A7zD90rnB$`Y__(rusjS%NM&KOrTPMvmRE_z zHu?m43$>d3=Kl;X9N+Ej?(EF$cPFRMA0NNo#3ufPW4k`v_1Kba9lPC5{I1Y?@Z=l8 zZ~recq9QtCyooF`vLZWjBIRObR7G{vM9ICK(G}g%6D^NU##BtlOpKX^GE|`s4YUnx zXs{TjTi)ZS%+8!lc`aF0R%cC?%z|Q9c4tqvOp4yAoX(jX!I9Bar8+e!w!#`PNW&6Z z)L0BQ&|onv3vy0$)l|v)SWDJjJ=L;YSWuj@k(Wu2r{D3CY*a0U5 zqY&1BL7GWu!GpzM11+D(-IU1*aWRZUA8W~Gy=3-v-P^@=LfgEq;@)MnU0>dLblKq} zEQLl|q~}|NFPg-G|Ms+n@fv m`SSVv=7Ymm?;d_Vxj1-qaqZu)yFcFVAK!cX^Y_bV@#G3d32$5g literal 0 HcmV?d00001 diff --git a/tracks/vendor/selenium/domviewer/domviewer.css b/tracks/vendor/selenium/domviewer/domviewer.css new file mode 100644 index 00000000..b64b2435 --- /dev/null +++ b/tracks/vendor/selenium/domviewer/domviewer.css @@ -0,0 +1,298 @@ +/****************************************************************************** +* Defines default styles for site pages. * +******************************************************************************/ +.hidden { + display: none; +} + +img{ + display: inline; + border: none; +} + +.box{ + background: #fcfcfc; + border: 1px solid #000; + border-color: blue; + color: #000000; + margin: 10px auto; + padding: 3px; + vertical-align: bottom; +} +a { + text-decoration: none; +} + +body { + background-color: #ffffff; + color: #000000; + font-family: Arial, Helvetica, sans-serif; + font-size: 10pt; +} + +h2 { + font-size: 140%; +} + +h3 { + font-size: 120%; +} + +h4 { + font-size: 100%; +} + +pre { + font-family: Courier New, Courier, monospace; + font-size: 80%; +} + +td, th { + font-family: Arial, Helvetica, sans-serif; + font-size: 10pt; + text-align: left; + vertical-align: top; +} + +th { + font-weight: bold; + vertical-align: bottom; +} + +ul { + list-style-type: square; +} + +#demoBox { + border-color: #000000; + border-style: solid; + border-width: 1px; + padding: 8px; + width: 24em; +} + +.footer { + margin-bottom: 0px; + text-align: center; +} + +/* Boxed table styles */ + +table.boxed { + border-spacing: 2px; + empty-cells: hide; +} + +td.boxed, th.boxed, th.boxedHeader { + background-color: #ffffff; + border-color: #000000; + border-style: solid; + border-width: 1px; + color: #000000; + padding: 2px; + padding-left: 8px; + padding-right: 8px; +} + +th.boxed { + background-color: #c0c0c0; +} + +th.boxedHeader { + background-color: #808080; + color: #ffffff; +} + +a.object { + color: #0000ff; +} + +li { + white-space: nowrap; +} + +ul { + list-style-type: square; + margin-left: 0px; + padding-left: 1em; +} + +.boxlevel1{ + background: #FFD700; +} + +.boxlevel2{ + background: #D2691E; +} + +.boxlevel3{ + background: #DCDCDC; +} + +.boxlevel4{ + background: #F5F5F5; +} + +.boxlevel5{ + background: #BEBEBE; +} + +.boxlevel6{ + background: #D3D3D3; +} + +.boxlevel7{ + background: #A9A9A9; +} + +.boxlevel8{ + background: #191970; +} + +.boxlevel9{ + background: #000080; +} + +.boxlevel10{ + background: #6495ED; +} + +.boxlevel11{ + background: #483D8B; +} + +.boxlevel12{ + background: #6A5ACD; +} + +.boxlevel13{ + background: #7B68EE; +} + +.boxlevel14{ + background: #8470FF; +} + +.boxlevel15{ + background: #0000CD; +} + +.boxlevel16{ + background: #4169E1; +} + +.boxlevel17{ + background: #0000FF; +} + +.boxlevel18{ + background: #1E90FF; +} + +.boxlevel19{ + background: #00BFFF; +} + +.boxlevel20{ + background: #87CEEB; +} + +.boxlevel21{ + background: #B0C4DE; +} + +.boxlevel22{ + background: #ADD8E6; +} + +.boxlevel23{ + background: #00CED1; +} + +.boxlevel24{ + background: #48D1CC; +} + +.boxlevel25{ + background: #40E0D0; +} + +.boxlevel26{ + background: #008B8B; +} + +.boxlevel27{ + background: #00FFFF; +} + +.boxlevel28{ + background: #E0FFFF; +} + +.boxlevel29{ + background: #5F9EA0; +} + +.boxlevel30{ + background: #66CDAA; +} + +.boxlevel31{ + background: #7FFFD4; +} + +.boxlevel32{ + background: #006400; +} + +.boxlevel33{ + background: #556B2F; +} + +.boxlevel34{ + background: #8FBC8F; +} + +.boxlevel35{ + background: #2E8B57; +} + +.boxlevel36{ + background: #3CB371; +} + +.boxlevel37{ + background: #20B2AA; +} + +.boxlevel38{ + background: #00FF7F; +} + +.boxlevel39{ + background: #7CFC00; +} + +.boxlevel40{ + background: #90EE90; +} + +.boxlevel41{ + background: #00FF00; +} + +.boxlevel41{ + background: #7FFF00; +} + +.boxlevel42{ + background: #00FA9A; +} + +.boxlevel43{ + background: #ADFF2F; +} + +.boxlevel44{ + background: #32CD32; +} \ No newline at end of file diff --git a/tracks/vendor/selenium/domviewer/domviewer.html b/tracks/vendor/selenium/domviewer/domviewer.html new file mode 100644 index 00000000..9158a50f --- /dev/null +++ b/tracks/vendor/selenium/domviewer/domviewer.html @@ -0,0 +1,16 @@ + + + + + DOM Viewer + + + + + +

    DOM Viewer

    +

    This page is generated using JavaScript. If you see this text, your + browser doesn't support JavaScript.

    + + + diff --git a/tracks/vendor/selenium/domviewer/selenium-domviewer.js b/tracks/vendor/selenium/domviewer/selenium-domviewer.js new file mode 100644 index 00000000..941aab16 --- /dev/null +++ b/tracks/vendor/selenium/domviewer/selenium-domviewer.js @@ -0,0 +1,205 @@ +var HIDDEN="hidden"; +var LEVEL = "level"; +var PLUS_SRC="butplus.gif"; +var MIN_SRC="butmin.gif"; +var newRoot; +var maxColumns=1; + +function loadDomViewer() { + // See if the rootDocument variable has been set on this window. + var rootDocument = window.rootDocument; + + // If not look to the opener for an explicity rootDocument variable, otherwise, use the opener document + if (!rootDocument && window.opener) { + rootDocument = window.opener.rootDocument || window.opener.document; + } + + if (rootDocument) { + document.body.innerHTML = displayDOM(rootDocument); + } + else { + document.body.innerHTML = "Must specify rootDocument for window. This can be done by setting the rootDocument variable on this window, or on the opener window for a popup window."; + } +} + + +function displayDOM(root){ + var str = ""; + str+=""; + str += treeTraversal(root,0); + // to make table columns work well. + str += ""; + for (var i=0; i < maxColumns; i++) { + str+= ""; + } + str += ""; + str += "
        
    "; + return str; +} + +function checkForChildren(element){ + if(!element.hasChildNodes()) + return false; + + var nodes = element.childNodes; + var size = nodes.length; + var count=0; + + for(var i=0; i< size; i++){ + var node = nodes.item(i); + //if(node.toString()=="[object Text]"){ + //this is equalent to the above + //but will work with more browsers + if(node.nodeType!=1){ + count++; + } + } + + if(count == size) + return false; + else + return true; +} + +function treeTraversal(root, level){ + var str = ""; + var nodes= null; + var size = null; + //it is supposed to show the last node, + //but the last node is always nodeText type + //and we don't show it + if(!root.hasChildNodes()) + return "";//displayNode(root,level,false); + + nodes = root.childNodes; + size = nodes.length; + + for(var i=0; i< size; i++){ + var element = nodes.item(i); + //if the node is textNode, don't display + if(element.nodeType==1){ + str+= displayNode(element,level,checkForChildren(element)); + str+=treeTraversal(element, level+1); + } + } + return str; +} + +function displayNode(element, level, isLink){ + nodeContent = getNodeContent(element); + columns = Math.round((nodeContent.length / 12) + 0.5); + if (columns + level > maxColumns) { + maxColumns = columns + level; + } + var str =""; + for (var i=0; i < level; i++) + str+= " "; + str+=""; + if(isLink){ + str+=''; + str+=''; + } + str += nodeContent; + if(isLink) + str+=""; + return str; +} + +function getNodeContent(element) { + + str = ""; + id =""; + if (element.id != null && element.id != "") { + id = " ID(" + element.id +")"; + } + name =""; + if (element.name != null && element.name != "") { + name = " NAME(" + element.name + ")"; + } + value =""; + if (element.value != null && element.value != "") { + value = " VALUE(" + element.value + ")"; + } + href =""; + if (element.href != null && element.href != "") { + href = " HREF(" + element.href + ")"; + } + clazz = ""; + if (element.className != null && element.className != "") { + clazz = " CLASS(" + element.className + ")"; + } + src = ""; + if (element.src != null && element.src != "") { + src = " SRC(" + element.src + ")"; + } + alt = ""; + if (element.alt != null && element.alt != "") { + alt = " ALT(" + element.alt + ")"; + } + type = ""; + if (element.type != null && element.type != "") { + type = " TYPE(" + element.type + ")"; + } + text =""; + if (element.text != null && element.text != "" && element.text != "undefined") { + text = " #TEXT(" + trim(element.text) +")"; + } + str+=" "+ element.nodeName + id + alt + type + clazz + name + value + href + src + text + ""; + return str; + +} + +function trim(val) { + val2 = val.substring(0,40) + " "; + var spaceChr = String.fromCharCode(32); + var length = val2.length; + var retVal = ""; + var ix = length -1; + + while(ix > -1){ + if(val2.charAt(ix) == spaceChr) { + } else { + retVal = val2.substring(0, ix +1); + break; + } + ix = ix-1; + } + if (val.length > 40) { + retVal += "..."; + } + return retVal; +} + +function hide(hlink){ + var isHidden = false; + var image = hlink.firstChild; + if(image.src.toString().indexOf(MIN_SRC)!=-1){ + image.src=PLUS_SRC; + isHidden=true; + }else{ + image.src=MIN_SRC; + } + var rowObj= hlink.parentNode.parentNode; + var rowLevel = parseInt(rowObj.className.substring(LEVEL.length)); + + var sibling = rowObj.nextSibling; + var siblingLevel = sibling.className.substring(LEVEL.length); + if(siblingLevel.indexOf(HIDDEN)!=-1){ + siblingLevel = siblingLevel.substring(0,siblingLevel.length - HIDDEN.length-1); + } + siblingLevel=parseInt(siblingLevel); + while(sibling!=null && rowLevel{V~`fQ7z+$t zgdj#1Wg&e?Pzym6f-GqfZA65ERv|&qKj5+mw*}&+$O6-Jz+u#BKDr<0o))*!v1FQ| z7Y^q=54_KFo^#H7l!3{+^4#%GTEk~3CYtXx;qiKqQi7!5te~Jo2I^%+pn}@+gN$}R zBDg11P~gN+gutWqIYM0D%3|X7TMjnYlD0T@Q(dPZvd0C_MQKZHGop`yoQz zH9VNQPv5hvEXEi9OF;V*aD;=mOefS)%hGI&^J87~G+(0stb1%P-%YZTTmht8AUCq# z$tLdKwRfw4umon>#7wzkXH^+WL}7CJ1#`=vnYjLx@u@KaE!EhDjW7hl6mFiI^Dx!Q zb6^{`OPnY0V>wCYYl`|nn8D5=01h9hBR;dT`6K~VDyULJmnt%6K$rNd{H}nNwYHFF z6B*V&t)ZqB9``&UTHm_i^3|Q!rQLy)U?dI9l!=itNPLWwnNQIhy~d5UTj-iz65(G0 zATg2#Yu{E`pH0vZY+z(~lzqGRZ(C4mA858Mh)sXTo6#7*GC^bA$$~{UpZAs2$=Z?N wQQ9LNT#ojldendJO4od>&LxWai*&D#Ul$}mCj_*0X#fBK07*qoM6N<$g4UKH`2YX_ literal 0 HcmV?d00001 diff --git a/tracks/vendor/selenium/icons/continue.png b/tracks/vendor/selenium/icons/continue.png new file mode 100644 index 0000000000000000000000000000000000000000..1efa13aa87ee2ae4f498528ce9851f8e060893c9 GIT binary patch literal 405 zcmV;G0c!ql z=0dbtjnSm7{U{o~yyrcCiVayq=;2WJ74Cjp{i4oB*>qSYPL4&<@bU@wkpryA8obte z1?~xqy%1y5VHw>Y{`Kcq6b<)oa_-#9jI1Gee{kHDSnF1cwCRaIe*HlA!{u}A>sB!$ zYlyiJZPjdEyrAIE-#^$w>0cfM!8MrG7}c+=`SSEq_y7O@k)44>1MvU=deTu(^``2!00000NkvXXu0mjf%y7hZ literal 0 HcmV?d00001 diff --git a/tracks/vendor/selenium/icons/continue_disabled.png b/tracks/vendor/selenium/icons/continue_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..5370b965f78b79e71cf23c60435c15c36fd4653f GIT binary patch literal 374 zcmV-+0g3*JP)gRVw?BAp8S%%;{1aF<=sDUU*I1>LTs@#5432`HqX||JtRRHx zd6$I5Mf1GWEsp1-p8%%`U@Q#9Dm~Bqqacp*GH6F_v}vlA@;Du&gZ(ZigU)-WwQHd* zQnAvX^i#lgos|LFAU)6<^9Bqdu$8+PF9ArLU(OSt{#vj2mD;#wEDX&pH}7ErMEm?~ zdM1PFu&M_-3}N{e7+#%Fk^pa;++=(z1AVGDS5R|6tB)w7-)C4YvqS$x1~7zW2XqG} z<1B4U!aU>)s93pq!kqJzJ75f4_u>!>tCSMW2Hwa$`Wl=w#^`655JGMv1^m7~0D!r; UQOMLJy#N3J07*qoM6N<$f-du$DF6Tf literal 0 HcmV?d00001 diff --git a/tracks/vendor/selenium/icons/pause.png b/tracks/vendor/selenium/icons/pause.png new file mode 100644 index 0000000000000000000000000000000000000000..aa78d755ce1816bd7b7b4d87347050b6c9750fc7 GIT binary patch literal 275 zcmV+u0qp*XP)HuHoKI&YfGC(KI~1$F*u10}em@%Yz^kKm5stAQTNGCk@)B ZRsiU9TrpMNhPnU%002ovPDHLkV1l!veh2^n literal 0 HcmV?d00001 diff --git a/tracks/vendor/selenium/icons/pause_disabled.png b/tracks/vendor/selenium/icons/pause_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..6c0bd31bdfaba13662f414ef68efd7cc91b52a9b GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|oCO|{#S9FrH$a$?Q%Y+uP;jTG zi(^Q|oVk}yb2T}LuqE98&l{9p)2vg?+B(bNNU5Oe-K9Rp($9*bBDnRLecS859^U@r zp~Qm9WA+WjW~zq^4A&Ve=WSOxGBv1%A=#u)T|q{G&%uD-{r)B9hX!4azXG3d9;{~y zUCB1-dxMg@mxJ~l)|*$unl?GaPKdkmplvTPcpX3XqY(!EZ^w(t5nM2x9OJo{@i;QjV$&oZJ5Ed1Lz3`Pgg&e IbxsLQ0H42O0RR91 literal 0 HcmV?d00001 diff --git a/tracks/vendor/selenium/icons/selected.png b/tracks/vendor/selenium/icons/selected.png new file mode 100644 index 0000000000000000000000000000000000000000..d6a5ac4ba33c9de1595564ac206ec7c13bd370c9 GIT binary patch literal 668 zcmV;N0%QG&P)v(F$2s43fm7Y5@pwF&BKudk z(27SP)LBd8STm14Jml!D!@Q5Z`!9htt~!>3V@tM0Lj3%kV&cI#-E}=o4PPTr7U1}u zLDIRj+fdvF=kv<)O1FR|EX1-X9L?v&-?WwIZ=a!Ob?%+JPi!$pbyp3RVPP9Kwy?1c z3tQN(nk6jsg#K5+TrpkbVp)*+n#PFbI9T7y)bKO_(SfTZml8-XP`--IPf&Vb6}10`^4YWN98q35ISUmIa2sLTc^@BL0g@N3Jju8AVZ) zk_gubfW*)ZQlFA6&1L9q>?1lhNuVsSE?`l7D`^ij%e08UoF}LTnVGmteQiTg(5uXg z(l$A~yOTq${S1bOHr17?sMLBxNKJyT_+O-XG=2k5nnO2G zC7oH$ODKx7->y^?C$&3-ZX^zq-r~qXRI=f$eVFX_WVa`MZPG+x!z2A=!9gon9nSsJ z>Gcz!S-}9Eg|^V~g|06R + + + +Defines an object that runs Selenium commands. + +

    Element Locators

    +

    +Element Locators tell Selenium which HTML element a command refers to. +The format of a locator is:

    +
    +locatorType=argument +
    + +

    +We support the following strategies for locating elements: +

    + +
      +
    • identifier=id: +Select the element with the specified @id attribute. If no match is +found, select the first element whose @name attribute is id. +(This is normally the default; see below.)
    • +
    • 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 followed 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
      • +
      +
    • +
    • dom=javascriptExpression: + +Find an element by evaluating the specified string. This allows you to traverse the HTML Document Object +Model using JavaScript. Note that you must not return a value in this string; simply make it the last expression in the block. +
        +
      • dom=document.forms['myForm'].myDropdown
      • +
      • dom=document.images[56]
      • +
      • dom=function foo() { return document.links[1]; }; foo();
      • +
      + +
    • + +
    • xpath=xpathExpression: +Locate an element using an XPath expression. +
        +
      • xpath=//img[@alt='The image alt text']
      • +
      • xpath=//table[@id='table1']//tr[4]/td[2]
      • +
      • xpath=//a[contains(@href,'#id1')]
      • +
      • xpath=//a[contains(@href,'#id1')]/@class
      • +
      • xpath=(//table[@class='stylee'])//th[text()='theHeaderText']/../td
      • +
      • xpath=//input[@name='name2' and @value='yes']
      • +
      • xpath=//*[text()="right"]
      • + +
      +
    • +
    • link=textPattern: +Select the link (anchor) element which contains text matching the +specified pattern. +
        +
      • link=The link text
      • +
      + +
    • + +
    • css=cssSelectorSyntax: +Select the element using css selectors. Please refer to CSS2 selectors, CSS3 selectors for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package. +
        +
      • css=a[href="#id3"]
      • +
      • css=span#firstChild + span
      • +
      +

      Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after).

      +
    • +
    + +

    +Without an explicit locator prefix, Selenium uses the following default +strategies: +

    + +
      +
    • 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).
    +
    + +

    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. +

    + + + +an element 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. + + + + + +an element locator + +Double clicks on a link, button, checkbox or radio button. If the double click action +causes a new page to load (like a link usually does), call +waitForPageToLoad. + + + + + +an element locator + +specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the 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. + + + + + +an element locator + +specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator. + +Doubleclicks on a link, button, checkbox or radio button. If the action +causes a new page to load (like a link usually does), call +waitForPageToLoad. + + + + + +an element locator + +the event name, e.g. "focus" or "blur" + +Explicitly simulate an event, to trigger the corresponding "onevent" +handler. + + + + + +an element locator + +Either be a string("\" followed by the numeric keycode of the key to be pressed, normally the ASCII value of that key), or a single character. For example: "w", "\119". + +Simulates a user pressing and releasing a key. + + + + + +Press the shift key and hold it down until doShiftUp() is called or a new page is loaded. + + + + + +Release the shift key. + + + + + +Press the meta key and hold it down until doMetaUp() is called or a new page is loaded. + + + + + +Release the meta key. + + + + + +Press the alt key and hold it down until doAltUp() is called or a new page is loaded. + + + + + +Release the alt key. + + + + + +Press the control key and hold it down until doControlUp() is called or a new page is loaded. + + + + + +Release the control key. + + + + + +an element locator + +Either be a string("\" followed by the numeric keycode of the key to be pressed, normally the ASCII value of that key), or a single character. For example: "w", "\119". + +Simulates a user pressing a key (without releasing it yet). + + + + + +an element locator + +Either be a string("\" followed by the numeric keycode of the key to be pressed, normally the ASCII value of that key), or a single character. For example: "w", "\119". + +Simulates a user releasing a key. + + + + + +an element locator + +Simulates a user hovering a mouse over the specified element. + + + + + +an element locator + +Simulates a user moving the mouse pointer away from the specified element. + + + + + +an element locator + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator. + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator. + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator. + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +the value to type + +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.

    + +
    + + + +an element locator + +the value to type + +Simulates keystroke events on the specified element, as though you typed the value key-by-key. + +

    This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string; +this is useful for dynamic UI widgets (like auto-completing combo boxes) that require explicit key events.

    + +

    Unlike the simple "type" command, which forces the specified value into the page directly, this command +may or may not have any visible effect, even in cases where typing keys would normally have a visible effect. +For example, if you use "typeKeys" on a form element, you may or may not see the results of what you typed in +the field.

    +

    In some cases, you may need to use the simple "type" command to set the value of the field and then the "typeKeys" command to +send the keystroke events corresponding to what you just typed.

    + +
    + + + +the number of milliseconds to pause after operation + +Set execution speed (i.e., set the millisecond length of a delay which will follow each selenium operation). By default, there is no such delay, i.e., +the delay is 0 milliseconds. + + + + + +Get execution speed (i.e., get the millisecond length of the delay following each selenium operation). By default, there is no such delay, i.e., +the delay is 0 milliseconds. + +See also setSpeed. + + + + + +an element locator + +Check a toggle-button (checkbox/radio) + + + + + +an element locator + +Uncheck a toggle-button (checkbox/radio) + + + + + +an element locator identifying a drop-down menu + +an option locator (a label by default) + +Select an option from a drop-down using an option locator. + +

    +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. +

    +
      +
    • label=labelPattern: +matches options based on their labels, i.e. the visible text. (This +is the default.) +
        +
      • 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 option locator prefix is provided, the default behaviour is to match on label. +

    + +
    + + + +an element locator identifying a multi-select box + +an option locator (a label by default) + +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 + + + + + +an element locator identifying a multi-select box + +an option locator (a label by default) + +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 + + + + + +an element locator identifying a multi-select box + +Unselects all of the selected options in a multi-select element. + + + + + +an element locator for the form you want to submit + +Submit the specified form. This is particularly useful for forms without +submit buttons, e.g. single-input "Search" forms. + + + + + +the URL to open; may be relative or absolute + +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. + +Note: 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. + + + + + +the URL to open, which can be blank + +the JavaScript window ID of the window to select + +Opens a popup window (if a window with that ID isn't already open). +After opening the window, you'll need to select it using the selectWindow +command. + +

    This command can also be a useful workaround for bug SEL-339. In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example). +In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using +an empty (blank) url, like this: openWindow("", "myFunnyWindow").

    + +
    + + + +the JavaScript window ID of the window to select + +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. + +

    Selenium has several strategies for finding the window object referred to by the "windowID" parameter.

    + +

    1.) if windowID is null, then it is assumed the user is referring to the original window instantiated by the browser).

    +

    2.) if the value of the "windowID" parameter is a JavaScript variable name in the current application window, then it is assumed +that this variable contains the return value from a call to the JavaScript window.open() method.

    +

    3.) Otherwise, selenium looks in a hash it maintains that maps string names to window objects. Each of these string +names matches the second parameter "windowName" past to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag) +(which selenium intercepts).

    + +

    If you're having trouble figuring out what is the name of a window that you want to manipulate, look at the selenium log messages +which identify the names of windows created via window.open (and therefore intercepted by selenium). You will see messages +like the following for each window as it is opened:

    + +

    debug: window.open call intercepted; window ID (which you can use with selectWindow()) is "myNewWindow"

    + +

    In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example). +(This is bug SEL-339.) In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using +an empty (blank) url, like this: openWindow("", "myFunnyWindow").

    + +
    + + + +an element locator identifying a frame or iframe + +Selects a frame within the current window. (You may invoke this command +multiple times to select nested frames.) To select the parent frame, use +"relative=parent" as a locator; to select the top frame, use "relative=top". + +

    You may also use a DOM expression to identify the frame you want directly, +like this: dom=frames["main"].frames["subframe"]

    + +
    + + + +all log messages seen since the last call to this API + +Return the contents of the log. + +

    This is a placeholder intended to make the code generator make this API +available to clients. The selenium server will intercept this call, however, +and return its recordkeeping of log messages since the last call to this API. +Thus this code in JavaScript will never be called.

    + +

    The reason I opted for a servercentric solution is to be able to support +multiple frames served from different domains, which would break a +centralized JavaScript logging mechanism under some conditions.

    + +
    + + + +true if the new frame is this code's window + +starting frame + +new frame (which might be relative to the current one) + +Determine whether current/locator identify the frame containing this running code. + +

    This is useful in proxy injection mode, where this code runs in every +browser frame and window, and sometimes the selenium server needs to identify +the "current" frame. In this case, when the test calls selectFrame, this +routine is called for each frame to figure out which one has been selected. +The selected frame will return true, while all others will return false.

    + +
    + + + +true if the new window is this code's window + +starting window + +new window (which might be relative to the current one, e.g., "_parent") + +Determine whether currentWindowString plus target identify the window containing this running code. + +

    This is useful in proxy injection mode, where this code runs in every +browser frame and window, and sometimes the selenium server needs to identify +the "current" window. In this case, when the test calls selectWindow, this +routine is called for each window to figure out which one has been selected. +The selected window will return true, while all others will return false.

    + +
    + + + +the JavaScript window ID of the window that will appear + +a timeout in milliseconds, after which the action will return with an error + +Waits for a popup window to appear and load up. + + + + + +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. + + + + + +the answer to give in response to the prompt pop-up + +Instructs Selenium to return the specified answer string in response to +the next JavaScript prompt [window.prompt()]. + + + + + +Simulates the user clicking the "back" button on their browser. + + + + + +Simulates the user clicking the "Refresh" button on their browser. + + + + + +Simulates the user clicking the "close" button in the titlebar of a popup +window or tab. + + + + + +true if there is an alert + +Has an alert occurred? + +

    +This function never throws an exception +

    + +
    + + + +true if there is a pending prompt + +Has a prompt occurred? + +

    +This function never throws an exception +

    + +
    + + + +true if there is a pending confirmation + +Has confirm() been called? + +

    +This function never throws an exception +

    + +
    + + + +The message of the most recent JavaScript alert + +Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts. + +

    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.

    + +

    NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert +dialog.

    + +

    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.

    + +
    + + + +the message of the most recent JavaScript confirmation dialog + +Retrieves the message of a JavaScript confirmation dialog generated during +the previous action. + +

    +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. +

    + +

    +NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible +dialog. +

    + +

    +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. +

    + +
    + + + +the message of the most recent JavaScript question prompt + +Retrieves the message of a JavaScript question prompt dialog generated during +the previous action. + +

    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.

    + +

    NOTE: under Selenium, JavaScript prompts will NOT pop up a visible +dialog.

    + +

    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.

    + +
    + + + +the absolute URL of the current page + +Gets the absolute URL of the current page. + + + + + +the title of the current page + +Gets the title of the current page. + + + + + +the entire text of the page + +Gets the entire text of the page. + + + + + +the element value, or "on/off" for checkbox/radio elements + +an element 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. + + + + + +the text of the element + +an element 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. + + + + + +an element locator + +Briefly changes the backgroundColor of the specified element yellow. Useful for debugging. + + + + + +the results of evaluating the snippet + +the JavaScript snippet to run + +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. + +

    Note that, by default, the snippet will run in the context of the "selenium" +object itself, so this will refer to the Selenium object, and window will +refer to the top-level runner test window, not the window of your application.

    + +

    If you need a reference to the window of your application, you can refer +to this.browserbot.getCurrentWindow() and if you need to use +a locator to refer to a single element in your application page, you can +use this.browserbot.findElement("foo") where "foo" is your locator.

    + +
    + + + +true if the checkbox is checked, false otherwise + +an element locator pointing to a checkbox or radio button + +Gets whether a toggle-button (checkbox/radio) is checked. Fails if the specified element doesn't exist or isn't a toggle-button. + + + + + +the text from the specified cell + +a cell address, e.g. "foo.1.4" + +Gets the text from a cell of a table. The cellAddress syntax +tableLocator.row.column, where row and column start at 0. + + + + + +an array of all selected option labels in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets all option labels (visible text) for selected options in the specified select or multi-select element. + + + + + +the selected option label in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets option label (visible text) for selected option in the specified select element. + + + + + +an array of all selected option values in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets all option values (value attributes) for selected options in the specified select or multi-select element. + + + + + +the selected option value in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets option value (value attribute) for selected option in the specified select element. + + + + + +an array of all selected option indexes in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets all option indexes (option number, starting at 0) for selected options in the specified select or multi-select element. + + + + + +the selected option index in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets option index (option number, starting at 0) for selected option in the specified select element. + + + + + +an array of all selected option IDs in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets all option element IDs for selected options in the specified select or multi-select element. + + + + + +the selected option ID in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets option element ID for selected option in the specified select element. + + + + + +true if some option has been selected, false otherwise + +an element locator identifying a drop-down menu + +Determines whether some option in a drop-down menu is selected. + + + + + +an array of all option labels in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets all option labels in the specified select drop-down. + + + + + +the value of the specified attribute + +an element locator followed by an + +Gets the value of an element attribute. + + + + + +true if the pattern matches the text, false otherwise + +a pattern to match with the text of the page + +Verifies that the specified text pattern appears somewhere on the rendered page shown to the user. + + + + + +true if the element is present, false otherwise + +an element locator + +Verifies that the specified element is somewhere on the page. + + + + + +true if the specified element is visible, false otherwise + +an element 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. + + + + + +true if the input element is editable, false otherwise + +an element 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. + + + + + +the IDs of all buttons on the page + +Returns the IDs of all buttons on the page. + +

    If a given button has no ID, it will appear as "" in this array.

    + +
    + + + +the IDs of all links on the page + +Returns the IDs of all links on the page. + +

    If a given link has no ID, it will appear as "" in this array.

    + +
    + + + +the IDs of all field on the page + +Returns the IDs of all input fields on the page. + +

    If a given field has no ID, it will appear as "" in this array.

    + +
    + + + +the set of values of this attribute from all known windows. + +name of an attribute on the windows + +Returns every instance of some attribute from all known windows. + + + + + +an element locator + +offset in pixels from the current location to which the element should be moved, e.g., "+70,-300" + +deprecated - use dragAndDrop instead + + + + + +the number of pixels between "mousemove" events + +Configure the number of pixels between "mousemove" events during dragAndDrop commands (default=10). +

    Setting this value to 0 means that we'll send a "mousemove" event to every single pixel +in between the start location and the end location; that can be very slow, and may +cause some browsers to force the JavaScript to timeout.

    + +

    If the mouse speed is greater than the distance between the two dragged objects, we'll +just send one "mousemove" at the start location and then one final one at the end location.

    + +
    + + + +the number of pixels between "mousemove" events during dragAndDrop commands (default=10) + +Returns the number of pixels between "mousemove" events during dragAndDrop commands (default=10). + + + + + +an element locator + +offset in pixels from the current location to which the element should be moved, e.g., "+70,-300" + +Drags an element a certain distance and then drops it + + + + + +an element to be dragged + +an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged is dropped + +Drags an element and drops it on another element + + + + + +name of the window to be given focus + +Gives focus to a window + + + + + +name of the window to be enlarged + +Resize window to take up the entire screen + + + + + +the IDs of all windows that the browser knows about. + +Returns the IDs of all windows that the browser knows about. + + + + + +the names of all windows that the browser knows about. + +Returns the names of all windows that the browser knows about. + + + + + +the titles of all windows that the browser knows about. + +Returns the titles of all windows that the browser knows about. + + + + + +the entire HTML source + +Returns the entire HTML source between the opening and +closing "html" tags. + + + + + +an element locator pointing to an input element or textarea + +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. + +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. + + + + + +of relative index of the element to its parent (starting from 0) + +an element locator pointing to an element + +Get the relative index of an element to its parent (starting from 0). The comment node and empty text node +will be ignored. + + + + + +true if two elements are ordered and have same parent, false otherwise + +an element locator pointing to the first element + +an element locator pointing to the second element + +Check if these two elements have same parent and are ordered. Two same elements will +not be considered ordered. + + + + + +of pixels from the edge of the frame. + +an element locator pointing to an element OR an element itself + +Retrieves the horizontal position of an element + + + + + +of pixels from the edge of the frame. + +an element locator pointing to an element OR an element itself + +Retrieves the vertical position of an element + + + + + +width of an element in pixels + +an element locator pointing to an element + +Retrieves the width of an element + + + + + +height of an element in pixels + +an element locator pointing to an element + +Retrieves the height of an element + + + + + +the numerical position of the cursor in the field + +an element locator pointing to an input element or textarea + +Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers. + +

    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 SEL-243.

    +This method will fail if the specified element isn't an input element or textarea, or there is no cursor in the element.
    + +
    + + + +the message to be sent to the browser + +one of "debug", "info", "warn", "error", sets the threshold for browser-side logging + +Writes a message to the status bar and adds a note to the browser-side +log. + +

    If logLevelThreshold is specified, set the threshold for logging +to that level (debug, info, warn, error).

    + +

    (Note that the browser-side logs will not be sent back to the +server, and are invisible to the Client Driver.)

    + +
    + + + +the value passed in + +the value to return + +Returns the specified expression. + +

    This is useful because of JavaScript preprocessing. +It is used to generate commands like assertExpression and waitForExpression.

    + +
    + + + +the JavaScript snippet to run + +a timeout in milliseconds, after which this command will return with an error + +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. + +

    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 selenium.browserbot.getCurrentWindow(), and then +run your JavaScript in there

    + +
    + + + +a timeout in milliseconds, after which the action will return with an error + +Specifies the amount of time that Selenium will wait for actions to complete. + +

    Actions that require waiting include "open" and the "waitFor*" actions.

    +The default timeout is 30 seconds.
    + +
    + + + +a timeout in milliseconds, after which this command will return with an error + +Waits for a new page to load. + +

    You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc. +(which are only available in the JS API).

    + +

    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.

    + +
    + + + +all cookies of the current page under test + +Return all cookies of the current page under test. + + + + + +name and value of the cookie in a format "name=value" + +options for the cookie. Currently supported options include 'path' and 'max_age'. the optionsString's format is "path=/path/, max_age=60". The order of options are irrelevant, the unit of the value of 'max_age' is second. + +Create a new cookie whose path and domain are same with those of current page +under test, unless you specified a path for this cookie explicitly. + + + + + +the name of the cookie to be deleted + +the path property of the cookie to be deleted + +Delete a named cookie with specified path. + + + + + +the amount of time to sleep (in milliseconds) + +Wait for the specified amount of time (in milliseconds) + + + + + +Halt the currently running test, and wait for the user to press the Continue button. +This command is useful for debugging, but be careful when using it, because it will +force automated tests to hang until a user intervenes manually. + + + + + +the value to store + +the name of a variable in which the result is to be stored. + +This command is a synonym for storeExpression. + + + + + +the message to print + +Prints the specified message into the third table cell in your Selenese tables. +Useful for debugging. + + + + + +an element locator identifying a drop-down menu + +an option locator, typically just an option label (e.g. "John Smith") + +Verifies that the selected option of a drop-down satisfies the optionSpecifier. Note that this command is deprecated; you should use assertSelectedLabel, assertSelectedValue, assertSelectedIndex, or assertSelectedId instead. + +

    See the select command for more information about option locators.

    + +
    + + + +The failure message we should expect. This command will fail if the wrong failure message appears. + +Tell Selenium to expect a failure on the next command execution. + + + + + +The error message we should expect. This command will fail if the wrong error message appears. + +Tell Selenium to expect an error on the next command execution. + + + +
    diff --git a/tracks/vendor/selenium/iedoc.xml b/tracks/vendor/selenium/iedoc.xml new file mode 100644 index 00000000..be90412c --- /dev/null +++ b/tracks/vendor/selenium/iedoc.xml @@ -0,0 +1,1368 @@ + + + + +Defines an object that runs Selenium commands. + +

    Element Locators

    +

    +Element Locators tell Selenium which HTML element a command refers to. +The format of a locator is:

    +
    +locatorType=argument +
    + +

    +We support the following strategies for locating elements: +

    + +
      +
    • identifier=id: +Select the element with the specified @id attribute. If no match is +found, select the first element whose @name attribute is id. +(This is normally the default; see below.)
    • +
    • 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 followed 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
      • +
      +
    • +
    • dom=javascriptExpression: + +Find an element by evaluating the specified string. This allows you to traverse the HTML Document Object +Model using JavaScript. Note that you must not return a value in this string; simply make it the last expression in the block. +
        +
      • dom=document.forms['myForm'].myDropdown
      • +
      • dom=document.images[56]
      • +
      • dom=function foo() { return document.links[1]; }; foo();
      • +
      + +
    • + +
    • xpath=xpathExpression: +Locate an element using an XPath expression. +
        +
      • xpath=//img[@alt='The image alt text']
      • +
      • xpath=//table[@id='table1']//tr[4]/td[2]
      • +
      • xpath=//a[contains(@href,'#id1')]
      • +
      • xpath=//a[contains(@href,'#id1')]/@class
      • +
      • xpath=(//table[@class='stylee'])//th[text()='theHeaderText']/../td
      • +
      • xpath=//input[@name='name2' and @value='yes']
      • +
      • xpath=//*[text()="right"]
      • + +
      +
    • +
    • link=textPattern: +Select the link (anchor) element which contains text matching the +specified pattern. +
        +
      • link=The link text
      • +
      + +
    • + +
    • css=cssSelectorSyntax: +Select the element using css selectors. Please refer to CSS2 selectors, CSS3 selectors for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package. +
        +
      • css=a[href="#id3"]
      • +
      • css=span#firstChild + span
      • +
      +

      Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after).

      +
    • +
    + +

    +Without an explicit locator prefix, Selenium uses the following default +strategies: +

    + +
      +
    • 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).
    +
    + +

    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. +

    + + + +an element 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. + + + + + +an element locator + +Double clicks on a link, button, checkbox or radio button. If the double click action +causes a new page to load (like a link usually does), call +waitForPageToLoad. + + + + + +an element locator + +specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the 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. + + + + + +an element locator + +specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator. + +Doubleclicks on a link, button, checkbox or radio button. If the action +causes a new page to load (like a link usually does), call +waitForPageToLoad. + + + + + +an element locator + +the event name, e.g. "focus" or "blur" + +Explicitly simulate an event, to trigger the corresponding "onevent" +handler. + + + + + +an element locator + +Either be a string("\" followed by the numeric keycode of the key to be pressed, normally the ASCII value of that key), or a single character. For example: "w", "\119". + +Simulates a user pressing and releasing a key. + + + + + +Press the shift key and hold it down until doShiftUp() is called or a new page is loaded. + + + + + +Release the shift key. + + + + + +Press the meta key and hold it down until doMetaUp() is called or a new page is loaded. + + + + + +Release the meta key. + + + + + +Press the alt key and hold it down until doAltUp() is called or a new page is loaded. + + + + + +Release the alt key. + + + + + +Press the control key and hold it down until doControlUp() is called or a new page is loaded. + + + + + +Release the control key. + + + + + +an element locator + +Either be a string("\" followed by the numeric keycode of the key to be pressed, normally the ASCII value of that key), or a single character. For example: "w", "\119". + +Simulates a user pressing a key (without releasing it yet). + + + + + +an element locator + +Either be a string("\" followed by the numeric keycode of the key to be pressed, normally the ASCII value of that key), or a single character. For example: "w", "\119". + +Simulates a user releasing a key. + + + + + +an element locator + +Simulates a user hovering a mouse over the specified element. + + + + + +an element locator + +Simulates a user moving the mouse pointer away from the specified element. + + + + + +an element locator + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator. + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator. + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +specifies the x,y position (i.e. - 10,20) of the mouse event relative to the element returned by the locator. + +Simulates a user pressing the mouse button (without releasing it yet) on +the specified element. + + + + + +an element locator + +the value to type + +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.

    + +
    + + + +an element locator + +the value to type + +Simulates keystroke events on the specified element, as though you typed the value key-by-key. + +

    This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string; +this is useful for dynamic UI widgets (like auto-completing combo boxes) that require explicit key events.

    + +

    Unlike the simple "type" command, which forces the specified value into the page directly, this command +may or may not have any visible effect, even in cases where typing keys would normally have a visible effect. +For example, if you use "typeKeys" on a form element, you may or may not see the results of what you typed in +the field.

    +

    In some cases, you may need to use the simple "type" command to set the value of the field and then the "typeKeys" command to +send the keystroke events corresponding to what you just typed.

    + +
    + + + +the number of milliseconds to pause after operation + +Set execution speed (i.e., set the millisecond length of a delay which will follow each selenium operation). By default, there is no such delay, i.e., +the delay is 0 milliseconds. + + + + + +Get execution speed (i.e., get the millisecond length of the delay following each selenium operation). By default, there is no such delay, i.e., +the delay is 0 milliseconds. + +See also setSpeed. + + + + + +an element locator + +Check a toggle-button (checkbox/radio) + + + + + +an element locator + +Uncheck a toggle-button (checkbox/radio) + + + + + +an element locator identifying a drop-down menu + +an option locator (a label by default) + +Select an option from a drop-down using an option locator. + +

    +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. +

    +
      +
    • label=labelPattern: +matches options based on their labels, i.e. the visible text. (This +is the default.) +
        +
      • 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 option locator prefix is provided, the default behaviour is to match on label. +

    + +
    + + + +an element locator identifying a multi-select box + +an option locator (a label by default) + +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 + + + + + +an element locator identifying a multi-select box + +an option locator (a label by default) + +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 + + + + + +an element locator identifying a multi-select box + +Unselects all of the selected options in a multi-select element. + + + + + +an element locator for the form you want to submit + +Submit the specified form. This is particularly useful for forms without +submit buttons, e.g. single-input "Search" forms. + + + + + +the URL to open; may be relative or absolute + +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. + +Note: 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. + + + + + +the URL to open, which can be blank + +the JavaScript window ID of the window to select + +Opens a popup window (if a window with that ID isn't already open). +After opening the window, you'll need to select it using the selectWindow +command. + +

    This command can also be a useful workaround for bug SEL-339. In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example). +In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using +an empty (blank) url, like this: openWindow("", "myFunnyWindow").

    + +
    + + + +the JavaScript window ID of the window to select + +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. + +

    Selenium has several strategies for finding the window object referred to by the "windowID" parameter.

    + +

    1.) if windowID is null, then it is assumed the user is referring to the original window instantiated by the browser).

    +

    2.) if the value of the "windowID" parameter is a JavaScript variable name in the current application window, then it is assumed +that this variable contains the return value from a call to the JavaScript window.open() method.

    +

    3.) Otherwise, selenium looks in a hash it maintains that maps string names to window objects. Each of these string +names matches the second parameter "windowName" past to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag) +(which selenium intercepts).

    + +

    If you're having trouble figuring out what is the name of a window that you want to manipulate, look at the selenium log messages +which identify the names of windows created via window.open (and therefore intercepted by selenium). You will see messages +like the following for each window as it is opened:

    + +

    debug: window.open call intercepted; window ID (which you can use with selectWindow()) is "myNewWindow"

    + +

    In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example). +(This is bug SEL-339.) In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using +an empty (blank) url, like this: openWindow("", "myFunnyWindow").

    + +
    + + + +an element locator identifying a frame or iframe + +Selects a frame within the current window. (You may invoke this command +multiple times to select nested frames.) To select the parent frame, use +"relative=parent" as a locator; to select the top frame, use "relative=top". + +

    You may also use a DOM expression to identify the frame you want directly, +like this: dom=frames["main"].frames["subframe"]

    + +
    + + + +all log messages seen since the last call to this API + +Return the contents of the log. + +

    This is a placeholder intended to make the code generator make this API +available to clients. The selenium server will intercept this call, however, +and return its recordkeeping of log messages since the last call to this API. +Thus this code in JavaScript will never be called.

    + +

    The reason I opted for a servercentric solution is to be able to support +multiple frames served from different domains, which would break a +centralized JavaScript logging mechanism under some conditions.

    + +
    + + + +true if the new frame is this code's window + +starting frame + +new frame (which might be relative to the current one) + +Determine whether current/locator identify the frame containing this running code. + +

    This is useful in proxy injection mode, where this code runs in every +browser frame and window, and sometimes the selenium server needs to identify +the "current" frame. In this case, when the test calls selectFrame, this +routine is called for each frame to figure out which one has been selected. +The selected frame will return true, while all others will return false.

    + +
    + + + +true if the new window is this code's window + +starting window + +new window (which might be relative to the current one, e.g., "_parent") + +Determine whether currentWindowString plus target identify the window containing this running code. + +

    This is useful in proxy injection mode, where this code runs in every +browser frame and window, and sometimes the selenium server needs to identify +the "current" window. In this case, when the test calls selectWindow, this +routine is called for each window to figure out which one has been selected. +The selected window will return true, while all others will return false.

    + +
    + + + +the JavaScript window ID of the window that will appear + +a timeout in milliseconds, after which the action will return with an error + +Waits for a popup window to appear and load up. + + + + + +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. + + + + + +the answer to give in response to the prompt pop-up + +Instructs Selenium to return the specified answer string in response to +the next JavaScript prompt [window.prompt()]. + + + + + +Simulates the user clicking the "back" button on their browser. + + + + + +Simulates the user clicking the "Refresh" button on their browser. + + + + + +Simulates the user clicking the "close" button in the titlebar of a popup +window or tab. + + + + + +true if there is an alert + +Has an alert occurred? + +

    +This function never throws an exception +

    + +
    + + + +true if there is a pending prompt + +Has a prompt occurred? + +

    +This function never throws an exception +

    + +
    + + + +true if there is a pending confirmation + +Has confirm() been called? + +

    +This function never throws an exception +

    + +
    + + + +The message of the most recent JavaScript alert + +Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts. + +

    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.

    + +

    NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert +dialog.

    + +

    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.

    + +
    + + + +the message of the most recent JavaScript confirmation dialog + +Retrieves the message of a JavaScript confirmation dialog generated during +the previous action. + +

    +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. +

    + +

    +NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible +dialog. +

    + +

    +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. +

    + +
    + + + +the message of the most recent JavaScript question prompt + +Retrieves the message of a JavaScript question prompt dialog generated during +the previous action. + +

    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.

    + +

    NOTE: under Selenium, JavaScript prompts will NOT pop up a visible +dialog.

    + +

    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.

    + +
    + + + +the absolute URL of the current page + +Gets the absolute URL of the current page. + + + + + +the title of the current page + +Gets the title of the current page. + + + + + +the entire text of the page + +Gets the entire text of the page. + + + + + +the element value, or "on/off" for checkbox/radio elements + +an element 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. + + + + + +the text of the element + +an element 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. + + + + + +an element locator + +Briefly changes the backgroundColor of the specified element yellow. Useful for debugging. + + + + + +the results of evaluating the snippet + +the JavaScript snippet to run + +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. + +

    Note that, by default, the snippet will run in the context of the "selenium" +object itself, so this will refer to the Selenium object, and window will +refer to the top-level runner test window, not the window of your application.

    + +

    If you need a reference to the window of your application, you can refer +to this.browserbot.getCurrentWindow() and if you need to use +a locator to refer to a single element in your application page, you can +use this.browserbot.findElement("foo") where "foo" is your locator.

    + +
    + + + +true if the checkbox is checked, false otherwise + +an element locator pointing to a checkbox or radio button + +Gets whether a toggle-button (checkbox/radio) is checked. Fails if the specified element doesn't exist or isn't a toggle-button. + + + + + +the text from the specified cell + +a cell address, e.g. "foo.1.4" + +Gets the text from a cell of a table. The cellAddress syntax +tableLocator.row.column, where row and column start at 0. + + + + + +an array of all selected option labels in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets all option labels (visible text) for selected options in the specified select or multi-select element. + + + + + +the selected option label in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets option label (visible text) for selected option in the specified select element. + + + + + +an array of all selected option values in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets all option values (value attributes) for selected options in the specified select or multi-select element. + + + + + +the selected option value in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets option value (value attribute) for selected option in the specified select element. + + + + + +an array of all selected option indexes in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets all option indexes (option number, starting at 0) for selected options in the specified select or multi-select element. + + + + + +the selected option index in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets option index (option number, starting at 0) for selected option in the specified select element. + + + + + +an array of all selected option IDs in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets all option element IDs for selected options in the specified select or multi-select element. + + + + + +the selected option ID in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets option element ID for selected option in the specified select element. + + + + + +true if some option has been selected, false otherwise + +an element locator identifying a drop-down menu + +Determines whether some option in a drop-down menu is selected. + + + + + +an array of all option labels in the specified select drop-down + +an element locator identifying a drop-down menu + +Gets all option labels in the specified select drop-down. + + + + + +the value of the specified attribute + +an element locator followed by an + +Gets the value of an element attribute. + + + + + +true if the pattern matches the text, false otherwise + +a pattern to match with the text of the page + +Verifies that the specified text pattern appears somewhere on the rendered page shown to the user. + + + + + +true if the element is present, false otherwise + +an element locator + +Verifies that the specified element is somewhere on the page. + + + + + +true if the specified element is visible, false otherwise + +an element 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. + + + + + +true if the input element is editable, false otherwise + +an element 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. + + + + + +the IDs of all buttons on the page + +Returns the IDs of all buttons on the page. + +

    If a given button has no ID, it will appear as "" in this array.

    + +
    + + + +the IDs of all links on the page + +Returns the IDs of all links on the page. + +

    If a given link has no ID, it will appear as "" in this array.

    + +
    + + + +the IDs of all field on the page + +Returns the IDs of all input fields on the page. + +

    If a given field has no ID, it will appear as "" in this array.

    + +
    + + + +the set of values of this attribute from all known windows. + +name of an attribute on the windows + +Returns every instance of some attribute from all known windows. + + + + + +an element locator + +offset in pixels from the current location to which the element should be moved, e.g., "+70,-300" + +deprecated - use dragAndDrop instead + + + + + +the number of pixels between "mousemove" events + +Configure the number of pixels between "mousemove" events during dragAndDrop commands (default=10). +

    Setting this value to 0 means that we'll send a "mousemove" event to every single pixel +in between the start location and the end location; that can be very slow, and may +cause some browsers to force the JavaScript to timeout.

    + +

    If the mouse speed is greater than the distance between the two dragged objects, we'll +just send one "mousemove" at the start location and then one final one at the end location.

    + +
    + + + +the number of pixels between "mousemove" events during dragAndDrop commands (default=10) + +Returns the number of pixels between "mousemove" events during dragAndDrop commands (default=10). + + + + + +an element locator + +offset in pixels from the current location to which the element should be moved, e.g., "+70,-300" + +Drags an element a certain distance and then drops it + + + + + +an element to be dragged + +an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged is dropped + +Drags an element and drops it on another element + + + + + +name of the window to be given focus + +Gives focus to a window + + + + + +name of the window to be enlarged + +Resize window to take up the entire screen + + + + + +the IDs of all windows that the browser knows about. + +Returns the IDs of all windows that the browser knows about. + + + + + +the names of all windows that the browser knows about. + +Returns the names of all windows that the browser knows about. + + + + + +the titles of all windows that the browser knows about. + +Returns the titles of all windows that the browser knows about. + + + + + +the entire HTML source + +Returns the entire HTML source between the opening and +closing "html" tags. + + + + + +an element locator pointing to an input element or textarea + +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. + +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. + + + + + +of relative index of the element to its parent (starting from 0) + +an element locator pointing to an element + +Get the relative index of an element to its parent (starting from 0). The comment node and empty text node +will be ignored. + + + + + +true if two elements are ordered and have same parent, false otherwise + +an element locator pointing to the first element + +an element locator pointing to the second element + +Check if these two elements have same parent and are ordered. Two same elements will +not be considered ordered. + + + + + +of pixels from the edge of the frame. + +an element locator pointing to an element OR an element itself + +Retrieves the horizontal position of an element + + + + + +of pixels from the edge of the frame. + +an element locator pointing to an element OR an element itself + +Retrieves the vertical position of an element + + + + + +width of an element in pixels + +an element locator pointing to an element + +Retrieves the width of an element + + + + + +height of an element in pixels + +an element locator pointing to an element + +Retrieves the height of an element + + + + + +the numerical position of the cursor in the field + +an element locator pointing to an input element or textarea + +Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers. + +

    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 SEL-243.

    +This method will fail if the specified element isn't an input element or textarea, or there is no cursor in the element.
    + +
    + + + +the message to be sent to the browser + +one of "debug", "info", "warn", "error", sets the threshold for browser-side logging + +Writes a message to the status bar and adds a note to the browser-side +log. + +

    If logLevelThreshold is specified, set the threshold for logging +to that level (debug, info, warn, error).

    + +

    (Note that the browser-side logs will not be sent back to the +server, and are invisible to the Client Driver.)

    + +
    + + + +the value passed in + +the value to return + +Returns the specified expression. + +

    This is useful because of JavaScript preprocessing. +It is used to generate commands like assertExpression and waitForExpression.

    + +
    + + + +the JavaScript snippet to run + +a timeout in milliseconds, after which this command will return with an error + +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. + +

    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 selenium.browserbot.getCurrentWindow(), and then +run your JavaScript in there

    + +
    + + + +a timeout in milliseconds, after which the action will return with an error + +Specifies the amount of time that Selenium will wait for actions to complete. + +

    Actions that require waiting include "open" and the "waitFor*" actions.

    +The default timeout is 30 seconds.
    + +
    + + + +a timeout in milliseconds, after which this command will return with an error + +Waits for a new page to load. + +

    You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc. +(which are only available in the JS API).

    + +

    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.

    + +
    + + + +all cookies of the current page under test + +Return all cookies of the current page under test. + + + + + +name and value of the cookie in a format "name=value" + +options for the cookie. Currently supported options include 'path' and 'max_age'. the optionsString's format is "path=/path/, max_age=60". The order of options are irrelevant, the unit of the value of 'max_age' is second. + +Create a new cookie whose path and domain are same with those of current page +under test, unless you specified a path for this cookie explicitly. + + + + + +the name of the cookie to be deleted + +the path property of the cookie to be deleted + +Delete a named cookie with specified path. + + + +
    diff --git a/tracks/vendor/selenium/lib/cssQuery/cssQuery-p.js b/tracks/vendor/selenium/lib/cssQuery/cssQuery-p.js new file mode 100644 index 00000000..4a7eb88a --- /dev/null +++ b/tracks/vendor/selenium/lib/cssQuery/cssQuery-p.js @@ -0,0 +1,6 @@ +/* + cssQuery, version 2.0.2 (2005-08-19) + Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/) + License: http://creativecommons.org/licenses/LGPL/2.1/ +*/ +eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7 x=6(){7 1D="2.0.2";7 C=/\\s*,\\s*/;7 x=6(s,A){33{7 m=[];7 u=1z.32.2c&&!A;7 b=(A)?(A.31==22)?A:[A]:[1g];7 1E=18(s).1l(C),i;9(i=0;i<1E.y;i++){s=1y(1E[i]);8(U&&s.Z(0,3).2b("")==" *#"){s=s.Z(2);A=24([],b,s[1])}1A A=b;7 j=0,t,f,a,c="";H(j+~]/;7 20=/[\\s#.:>+~()@]|[^\\s#.:>+~()@]+/g;6 1y(s){8(S.l(s))s=" "+s;5 s.P(20)||[]};7 W=/\\s*([\\s>+~(),]|^|$)\\s*/g;7 I=/([\\s>+~,]|[^(]\\+|^)([#.:@])/g;7 18=6(s){5 s.O(W,"$1").O(I,"$1*$2")};7 1u={1Z:6(){5"\'"},P:/^(\'[^\']*\')|("[^"]*")$/,l:6(s){5 o.P.l(s)},1S:6(s){5 o.l(s)?s:o+s+o},1Y:6(s){5 o.l(s)?s.Z(1,-1):s}};7 1s=6(t){5 1u.1Y(t)};7 E=/([\\/()[\\]?{}|*+-])/g;6 R(s){5 s.O(E,"\\\\$1")};x.15("1j-2H",6(){D[">"]=6(r,f,t,n){7 e,i,j;9(i=0;i=c);5(c%m)==s}});x.15("1j-2m",6(){U=1i("L;/*@2l@8(@\\2k)U=K@2j@*/");8(!U){X=6(e,t,n){5 n?e.2i("*",t):e.X(t)};14=6(e,n){5!n||(n=="*")||(e.2h==n)};1h=1g.1I?6(e){5/1J/i.l(Q(e).1I)}:6(e){5 Q(e).1H.1f!="2g"};1e=6(e){5 e.2f||e.1G||1b(e)};6 1b(e){7 t="",n,i;9(i=0;(n=e.1F[i]);i++){1d(n.1c){F 11:F 1:t+=1b(n);1a;F 3:t+=n.2e;1a}}5 t}}});19=K;5 x}();',62,190,'|||||return|function|var|if|for||||||||pseudoClasses||||test|||this||AttributeSelector|||||||cssQuery|length|push|fr|id||selectors||case|nextElementSibling|while||tests|true|false|thisElement||replace|match|getDocument|regEscape||attributeSelectors|isMSIE|cache||getElementsByTagName|isNaN|slice|child||new|getAttribute|compareNamespace|addModule|previousElementSibling|compareTagName|parseSelector|loaded|break|_0|nodeType|switch|getTextContent|tagName|document|isXML|eval|css|_1|split|ch|parentNode|childElements|nthChild|disabled|firstElementChild|getText|RegExp|Quote|x22|PREFIX|lang|_2|arguments|else|all|links|version|se|childNodes|innerText|documentElement|contentType|xml|parseInt|indeterminate|checked|last|nth|lastElementChild|parse|_3|add|href|String|className|create|NS_IE|remove|toString|ST|select|Array|null|_4|mimeType|lastChild|firstChild|continue|modules|delete|join|caching|error|nodeValue|textContent|HTML|prefix|getElementsByTagNameNS|end|x5fwin32|cc_on|standard||odd|even|enabled|hash|location|target|not|only|empty|root|contains|level3|outerHTML|htmlFor|class|toLowerCase|Function|name|first|level2|prototype|item|scopeName|toUpperCase|ownerDocument|Document|XML|Boolean|URL|unknown|typeof|nextSibling|previousSibling|visited|link|valueOf|clearCache|catch|concat|constructor|callee|try'.split('|'),0,{})) diff --git a/tracks/vendor/selenium/lib/cssQuery/src/cssQuery-level2.js b/tracks/vendor/selenium/lib/cssQuery/src/cssQuery-level2.js new file mode 100644 index 00000000..02dd0e5f --- /dev/null +++ b/tracks/vendor/selenium/lib/cssQuery/src/cssQuery-level2.js @@ -0,0 +1,142 @@ +/* + cssQuery, version 2.0.2 (2005-08-19) + Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/) + License: http://creativecommons.org/licenses/LGPL/2.1/ +*/ + +cssQuery.addModule("css-level2", function() { + +// ----------------------------------------------------------------------- +// selectors +// ----------------------------------------------------------------------- + +// child selector +selectors[">"] = function($results, $from, $tagName, $namespace) { + var $element, i, j; + for (i = 0; i < $from.length; i++) { + var $subset = childElements($from[i]); + for (j = 0; ($element = $subset[j]); j++) + if (compareTagName($element, $tagName, $namespace)) + $results.push($element); + } +}; + +// sibling selector +selectors["+"] = function($results, $from, $tagName, $namespace) { + for (var i = 0; i < $from.length; i++) { + var $element = nextElementSibling($from[i]); + if ($element && compareTagName($element, $tagName, $namespace)) + $results.push($element); + } +}; + +// attribute selector +selectors["@"] = function($results, $from, $attributeSelectorID) { + var $test = attributeSelectors[$attributeSelectorID].test; + var $element, i; + for (i = 0; ($element = $from[i]); i++) + if ($test($element)) $results.push($element); +}; + +// ----------------------------------------------------------------------- +// pseudo-classes +// ----------------------------------------------------------------------- + +pseudoClasses["first-child"] = function($element) { + return !previousElementSibling($element); +}; + +pseudoClasses["lang"] = function($element, $code) { + $code = new RegExp("^" + $code, "i"); + while ($element && !$element.getAttribute("lang")) $element = $element.parentNode; + return $element && $code.test($element.getAttribute("lang")); +}; + +// ----------------------------------------------------------------------- +// attribute selectors +// ----------------------------------------------------------------------- + +// constants +AttributeSelector.NS_IE = /\\:/g; +AttributeSelector.PREFIX = "@"; +// properties +AttributeSelector.tests = {}; +// methods +AttributeSelector.replace = function($match, $attribute, $namespace, $compare, $value) { + var $key = this.PREFIX + $match; + if (!attributeSelectors[$key]) { + $attribute = this.create($attribute, $compare || "", $value || ""); + // store the selector + attributeSelectors[$key] = $attribute; + attributeSelectors.push($attribute); + } + return attributeSelectors[$key].id; +}; +AttributeSelector.parse = function($selector) { + $selector = $selector.replace(this.NS_IE, "|"); + var $match; + while ($match = $selector.match(this.match)) { + var $replace = this.replace($match[0], $match[1], $match[2], $match[3], $match[4]); + $selector = $selector.replace(this.match, $replace); + } + return $selector; +}; +AttributeSelector.create = function($propertyName, $test, $value) { + var $attributeSelector = {}; + $attributeSelector.id = this.PREFIX + attributeSelectors.length; + $attributeSelector.name = $propertyName; + $test = this.tests[$test]; + $test = $test ? $test(this.getAttribute($propertyName), getText($value)) : false; + $attributeSelector.test = new Function("e", "return " + $test); + return $attributeSelector; +}; +AttributeSelector.getAttribute = function($name) { + switch ($name.toLowerCase()) { + case "id": + return "e.id"; + case "class": + return "e.className"; + case "for": + return "e.htmlFor"; + case "href": + if (isMSIE) { + // IE always returns the full path not the fragment in the href attribute + // so we RegExp it out of outerHTML. Opera does the same thing but there + // is no way to get the original attribute. + return "String((e.outerHTML.match(/href=\\x22?([^\\s\\x22]*)\\x22?/)||[])[1]||'')"; + } + } + return "e.getAttribute('" + $name.replace($NAMESPACE, ":") + "')"; +}; + +// ----------------------------------------------------------------------- +// attribute selector tests +// ----------------------------------------------------------------------- + +AttributeSelector.tests[""] = function($attribute) { + return $attribute; +}; + +AttributeSelector.tests["="] = function($attribute, $value) { + return $attribute + "==" + Quote.add($value); +}; + +AttributeSelector.tests["~="] = function($attribute, $value) { + return "/(^| )" + regEscape($value) + "( |$)/.test(" + $attribute + ")"; +}; + +AttributeSelector.tests["|="] = function($attribute, $value) { + return "/^" + regEscape($value) + "(-|$)/.test(" + $attribute + ")"; +}; + +// ----------------------------------------------------------------------- +// parsing +// ----------------------------------------------------------------------- + +// override parseSelector to parse out attribute selectors +var _parseSelector = parseSelector; +parseSelector = function($selector) { + return _parseSelector(AttributeSelector.parse($selector)); +}; + +}); // addModule diff --git a/tracks/vendor/selenium/lib/cssQuery/src/cssQuery-level3.js b/tracks/vendor/selenium/lib/cssQuery/src/cssQuery-level3.js new file mode 100644 index 00000000..11d19664 --- /dev/null +++ b/tracks/vendor/selenium/lib/cssQuery/src/cssQuery-level3.js @@ -0,0 +1,150 @@ +/* + cssQuery, version 2.0.2 (2005-08-19) + Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/) + License: http://creativecommons.org/licenses/LGPL/2.1/ +*/ + +/* Thanks to Bill Edney */ + +cssQuery.addModule("css-level3", function() { + +// ----------------------------------------------------------------------- +// selectors +// ----------------------------------------------------------------------- + +// indirect sibling selector +selectors["~"] = function($results, $from, $tagName, $namespace) { + var $element, i; + for (i = 0; ($element = $from[i]); i++) { + while ($element = nextElementSibling($element)) { + if (compareTagName($element, $tagName, $namespace)) + $results.push($element); + } + } +}; + +// ----------------------------------------------------------------------- +// pseudo-classes +// ----------------------------------------------------------------------- + +// I'm hoping these pseudo-classes are pretty readable. Let me know if +// any need explanation. + +pseudoClasses["contains"] = function($element, $text) { + $text = new RegExp(regEscape(getText($text))); + return $text.test(getTextContent($element)); +}; + +pseudoClasses["root"] = function($element) { + return $element == getDocument($element).documentElement; +}; + +pseudoClasses["empty"] = function($element) { + var $node, i; + for (i = 0; ($node = $element.childNodes[i]); i++) { + if (thisElement($node) || $node.nodeType == 3) return false; + } + return true; +}; + +pseudoClasses["last-child"] = function($element) { + return !nextElementSibling($element); +}; + +pseudoClasses["only-child"] = function($element) { + $element = $element.parentNode; + return firstElementChild($element) == lastElementChild($element); +}; + +pseudoClasses["not"] = function($element, $selector) { + var $negated = cssQuery($selector, getDocument($element)); + for (var i = 0; i < $negated.length; i++) { + if ($negated[i] == $element) return false; + } + return true; +}; + +pseudoClasses["nth-child"] = function($element, $arguments) { + return nthChild($element, $arguments, previousElementSibling); +}; + +pseudoClasses["nth-last-child"] = function($element, $arguments) { + return nthChild($element, $arguments, nextElementSibling); +}; + +pseudoClasses["target"] = function($element) { + return $element.id == location.hash.slice(1); +}; + +// UI element states + +pseudoClasses["checked"] = function($element) { + return $element.checked; +}; + +pseudoClasses["enabled"] = function($element) { + return $element.disabled === false; +}; + +pseudoClasses["disabled"] = function($element) { + return $element.disabled; +}; + +pseudoClasses["indeterminate"] = function($element) { + return $element.indeterminate; +}; + +// ----------------------------------------------------------------------- +// attribute selector tests +// ----------------------------------------------------------------------- + +AttributeSelector.tests["^="] = function($attribute, $value) { + return "/^" + regEscape($value) + "/.test(" + $attribute + ")"; +}; + +AttributeSelector.tests["$="] = function($attribute, $value) { + return "/" + regEscape($value) + "$/.test(" + $attribute + ")"; +}; + +AttributeSelector.tests["*="] = function($attribute, $value) { + return "/" + regEscape($value) + "/.test(" + $attribute + ")"; +}; + +// ----------------------------------------------------------------------- +// nth child support (Bill Edney) +// ----------------------------------------------------------------------- + +function nthChild($element, $arguments, $traverse) { + switch ($arguments) { + case "n": return true; + case "even": $arguments = "2n"; break; + case "odd": $arguments = "2n+1"; + } + + var $$children = childElements($element.parentNode); + function _checkIndex($index) { + var $index = ($traverse == nextElementSibling) ? $$children.length - $index : $index - 1; + return $$children[$index] == $element; + }; + + // it was just a number (no "n") + if (!isNaN($arguments)) return _checkIndex($arguments); + + $arguments = $arguments.split("n"); + var $multiplier = parseInt($arguments[0]); + var $step = parseInt($arguments[1]); + + if ((isNaN($multiplier) || $multiplier == 1) && $step == 0) return true; + if ($multiplier == 0 && !isNaN($step)) return _checkIndex($step); + if (isNaN($step)) $step = 0; + + var $count = 1; + while ($element = $traverse($element)) $count++; + + if (isNaN($multiplier) || $multiplier == 1) + return ($traverse == nextElementSibling) ? ($count <= $step) : ($step >= $count); + + return ($count % $multiplier) == $step; +}; + +}); // addModule diff --git a/tracks/vendor/selenium/lib/cssQuery/src/cssQuery-standard.js b/tracks/vendor/selenium/lib/cssQuery/src/cssQuery-standard.js new file mode 100644 index 00000000..77314b86 --- /dev/null +++ b/tracks/vendor/selenium/lib/cssQuery/src/cssQuery-standard.js @@ -0,0 +1,53 @@ +/* + cssQuery, version 2.0.2 (2005-08-19) + Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/) + License: http://creativecommons.org/licenses/LGPL/2.1/ +*/ + +cssQuery.addModule("css-standard", function() { // override IE optimisation + +// cssQuery was originally written as the CSS engine for IE7. It is +// optimised (in terms of size not speed) for IE so this module is +// provided separately to provide cross-browser support. + +// ----------------------------------------------------------------------- +// browser compatibility +// ----------------------------------------------------------------------- + +// sniff for Win32 Explorer +isMSIE = eval("false;/*@cc_on@if(@\x5fwin32)isMSIE=true@end@*/"); + +if (!isMSIE) { + getElementsByTagName = function($element, $tagName, $namespace) { + return $namespace ? $element.getElementsByTagNameNS("*", $tagName) : + $element.getElementsByTagName($tagName); + }; + + compareNamespace = function($element, $namespace) { + return !$namespace || ($namespace == "*") || ($element.prefix == $namespace); + }; + + isXML = document.contentType ? function($element) { + return /xml/i.test(getDocument($element).contentType); + } : function($element) { + return getDocument($element).documentElement.tagName != "HTML"; + }; + + getTextContent = function($element) { + // mozilla || opera || other + return $element.textContent || $element.innerText || _getTextContent($element); + }; + + function _getTextContent($element) { + var $textContent = "", $node, i; + for (i = 0; ($node = $element.childNodes[i]); i++) { + switch ($node.nodeType) { + case 11: // document fragment + case 1: $textContent += _getTextContent($node); break; + case 3: $textContent += $node.nodeValue; break; + } + } + return $textContent; + }; +} +}); // addModule diff --git a/tracks/vendor/selenium/lib/cssQuery/src/cssQuery.js b/tracks/vendor/selenium/lib/cssQuery/src/cssQuery.js new file mode 100644 index 00000000..1fcab4a1 --- /dev/null +++ b/tracks/vendor/selenium/lib/cssQuery/src/cssQuery.js @@ -0,0 +1,356 @@ +/* + cssQuery, version 2.0.2 (2005-08-19) + Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/) + License: http://creativecommons.org/licenses/LGPL/2.1/ +*/ + +// the following functions allow querying of the DOM using CSS selectors +var cssQuery = function() { +var version = "2.0.2"; + +// ----------------------------------------------------------------------- +// main query function +// ----------------------------------------------------------------------- + +var $COMMA = /\s*,\s*/; +var cssQuery = function($selector, $$from) { +try { + var $match = []; + var $useCache = arguments.callee.caching && !$$from; + var $base = ($$from) ? ($$from.constructor == Array) ? $$from : [$$from] : [document]; + // process comma separated selectors + var $$selectors = parseSelector($selector).split($COMMA), i; + for (i = 0; i < $$selectors.length; i++) { + // convert the selector to a stream + $selector = _toStream($$selectors[i]); + // faster chop if it starts with id (MSIE only) + if (isMSIE && $selector.slice(0, 3).join("") == " *#") { + $selector = $selector.slice(2); + $$from = _msie_selectById([], $base, $selector[1]); + } else $$from = $base; + // process the stream + var j = 0, $token, $filter, $arguments, $cacheSelector = ""; + while (j < $selector.length) { + $token = $selector[j++]; + $filter = $selector[j++]; + $cacheSelector += $token + $filter; + // some pseudo-classes allow arguments to be passed + // e.g. nth-child(even) + $arguments = ""; + if ($selector[j] == "(") { + while ($selector[j++] != ")" && j < $selector.length) { + $arguments += $selector[j]; + } + $arguments = $arguments.slice(0, -1); + $cacheSelector += "(" + $arguments + ")"; + } + // process a token/filter pair use cached results if possible + $$from = ($useCache && cache[$cacheSelector]) ? + cache[$cacheSelector] : select($$from, $token, $filter, $arguments); + if ($useCache) cache[$cacheSelector] = $$from; + } + $match = $match.concat($$from); + } + delete cssQuery.error; + return $match; +} catch ($error) { + cssQuery.error = $error; + return []; +}}; + +// ----------------------------------------------------------------------- +// public interface +// ----------------------------------------------------------------------- + +cssQuery.toString = function() { + return "function cssQuery() {\n [version " + version + "]\n}"; +}; + +// caching +var cache = {}; +cssQuery.caching = false; +cssQuery.clearCache = function($selector) { + if ($selector) { + $selector = _toStream($selector).join(""); + delete cache[$selector]; + } else cache = {}; +}; + +// allow extensions +var modules = {}; +var loaded = false; +cssQuery.addModule = function($name, $script) { + if (loaded) eval("$script=" + String($script)); + modules[$name] = new $script();; +}; + +// hackery +cssQuery.valueOf = function($code) { + return $code ? eval($code) : this; +}; + +// ----------------------------------------------------------------------- +// declarations +// ----------------------------------------------------------------------- + +var selectors = {}; +var pseudoClasses = {}; +// a safari bug means that these have to be declared here +var AttributeSelector = {match: /\[([\w-]+(\|[\w-]+)?)\s*(\W?=)?\s*([^\]]*)\]/}; +var attributeSelectors = []; + +// ----------------------------------------------------------------------- +// selectors +// ----------------------------------------------------------------------- + +// descendant selector +selectors[" "] = function($results, $from, $tagName, $namespace) { + // loop through current selection + var $element, i, j; + for (i = 0; i < $from.length; i++) { + // get descendants + var $subset = getElementsByTagName($from[i], $tagName, $namespace); + // loop through descendants and add to results selection + for (j = 0; ($element = $subset[j]); j++) { + if (thisElement($element) && compareNamespace($element, $namespace)) + $results.push($element); + } + } +}; + +// ID selector +selectors["#"] = function($results, $from, $id) { + // loop through current selection and check ID + var $element, j; + for (j = 0; ($element = $from[j]); j++) if ($element.id == $id) $results.push($element); +}; + +// class selector +selectors["."] = function($results, $from, $className) { + // create a RegExp version of the class + $className = new RegExp("(^|\\s)" + $className + "(\\s|$)"); + // loop through current selection and check class + var $element, i; + for (i = 0; ($element = $from[i]); i++) + if ($className.test($element.className)) $results.push($element); +}; + +// pseudo-class selector +selectors[":"] = function($results, $from, $pseudoClass, $arguments) { + // retrieve the cssQuery pseudo-class function + var $test = pseudoClasses[$pseudoClass], $element, i; + // loop through current selection and apply pseudo-class filter + if ($test) for (i = 0; ($element = $from[i]); i++) + // if the cssQuery pseudo-class function returns "true" add the element + if ($test($element, $arguments)) $results.push($element); +}; + +// ----------------------------------------------------------------------- +// pseudo-classes +// ----------------------------------------------------------------------- + +pseudoClasses["link"] = function($element) { + var $document = getDocument($element); + if ($document.links) for (var i = 0; i < $document.links.length; i++) { + if ($document.links[i] == $element) return true; + } +}; + +pseudoClasses["visited"] = function($element) { + // can't do this without jiggery-pokery +}; + +// ----------------------------------------------------------------------- +// DOM traversal +// ----------------------------------------------------------------------- + +// IE5/6 includes comments (LOL) in it's elements collections. +// so we have to check for this. the test is tagName != "!". LOL (again). +var thisElement = function($element) { + return ($element && $element.nodeType == 1 && $element.tagName != "!") ? $element : null; +}; + +// return the previous element to the supplied element +// previousSibling is not good enough as it might return a text or comment node +var previousElementSibling = function($element) { + while ($element && ($element = $element.previousSibling) && !thisElement($element)) continue; + return $element; +}; + +// return the next element to the supplied element +var nextElementSibling = function($element) { + while ($element && ($element = $element.nextSibling) && !thisElement($element)) continue; + return $element; +}; + +// return the first child ELEMENT of an element +// NOT the first child node (though they may be the same thing) +var firstElementChild = function($element) { + return thisElement($element.firstChild) || nextElementSibling($element.firstChild); +}; + +var lastElementChild = function($element) { + return thisElement($element.lastChild) || previousElementSibling($element.lastChild); +}; + +// return child elements of an element (not child nodes) +var childElements = function($element) { + var $childElements = []; + $element = firstElementChild($element); + while ($element) { + $childElements.push($element); + $element = nextElementSibling($element); + } + return $childElements; +}; + +// ----------------------------------------------------------------------- +// browser compatibility +// ----------------------------------------------------------------------- + +// all of the functions in this section can be overwritten. the default +// configuration is for IE. The functions below reflect this. standard +// methods are included in a separate module. It would probably be better +// the other way round of course but this makes it easier to keep IE7 trim. + +var isMSIE = true; + +var isXML = function($element) { + var $document = getDocument($element); + return (typeof $document.mimeType == "unknown") ? + /\.xml$/i.test($document.URL) : + Boolean($document.mimeType == "XML Document"); +}; + +// return the element's containing document +var getDocument = function($element) { + return $element.ownerDocument || $element.document; +}; + +var getElementsByTagName = function($element, $tagName) { + return ($tagName == "*" && $element.all) ? $element.all : $element.getElementsByTagName($tagName); +}; + +var compareTagName = function($element, $tagName, $namespace) { + if ($tagName == "*") return thisElement($element); + if (!compareNamespace($element, $namespace)) return false; + if (!isXML($element)) $tagName = $tagName.toUpperCase(); + return $element.tagName == $tagName; +}; + +var compareNamespace = function($element, $namespace) { + return !$namespace || ($namespace == "*") || ($element.scopeName == $namespace); +}; + +var getTextContent = function($element) { + return $element.innerText; +}; + +function _msie_selectById($results, $from, id) { + var $match, i, j; + for (i = 0; i < $from.length; i++) { + if ($match = $from[i].all.item(id)) { + if ($match.id == id) $results.push($match); + else if ($match.length != null) { + for (j = 0; j < $match.length; j++) { + if ($match[j].id == id) $results.push($match[j]); + } + } + } + } + return $results; +}; + +// for IE5.0 +if (![].push) Array.prototype.push = function() { + for (var i = 0; i < arguments.length; i++) { + this[this.length] = arguments[i]; + } + return this.length; +}; + +// ----------------------------------------------------------------------- +// query support +// ----------------------------------------------------------------------- + +// select a set of matching elements. +// "from" is an array of elements. +// "token" is a character representing the type of filter +// e.g. ">" means child selector +// "filter" represents the tag name, id or class name that is being selected +// the function returns an array of matching elements +var $NAMESPACE = /\|/; +function select($$from, $token, $filter, $arguments) { + if ($NAMESPACE.test($filter)) { + $filter = $filter.split($NAMESPACE); + $arguments = $filter[0]; + $filter = $filter[1]; + } + var $results = []; + if (selectors[$token]) { + selectors[$token]($results, $$from, $filter, $arguments); + } + return $results; +}; + +// ----------------------------------------------------------------------- +// parsing +// ----------------------------------------------------------------------- + +// convert css selectors to a stream of tokens and filters +// it's not a real stream. it's just an array of strings. +var $STANDARD_SELECT = /^[^\s>+~]/; +var $$STREAM = /[\s#.:>+~()@]|[^\s#.:>+~()@]+/g; +function _toStream($selector) { + if ($STANDARD_SELECT.test($selector)) $selector = " " + $selector; + return $selector.match($$STREAM) || []; +}; + +var $WHITESPACE = /\s*([\s>+~(),]|^|$)\s*/g; +var $IMPLIED_ALL = /([\s>+~,]|[^(]\+|^)([#.:@])/g; +var parseSelector = function($selector) { + return $selector + // trim whitespace + .replace($WHITESPACE, "$1") + // e.g. ".class1" --> "*.class1" + .replace($IMPLIED_ALL, "$1*$2"); +}; + +var Quote = { + toString: function() {return "'"}, + match: /^('[^']*')|("[^"]*")$/, + test: function($string) { + return this.match.test($string); + }, + add: function($string) { + return this.test($string) ? $string : this + $string + this; + }, + remove: function($string) { + return this.test($string) ? $string.slice(1, -1) : $string; + } +}; + +var getText = function($text) { + return Quote.remove($text); +}; + +var $ESCAPE = /([\/()[\]?{}|*+-])/g; +function regEscape($string) { + return $string.replace($ESCAPE, "\\$1"); +}; + +// ----------------------------------------------------------------------- +// modules +// ----------------------------------------------------------------------- + +// -------- >> insert modules here for packaging << -------- \\ + +loaded = true; + +// ----------------------------------------------------------------------- +// return the query function +// ----------------------------------------------------------------------- + +return cssQuery; + +}(); // cssQuery diff --git a/tracks/vendor/selenium/lib/prototype.js b/tracks/vendor/selenium/lib/prototype.js new file mode 100644 index 00000000..0caf9cd7 --- /dev/null +++ b/tracks/vendor/selenium/lib/prototype.js @@ -0,0 +1,2006 @@ +/* Prototype JavaScript framework, version 1.5.0_rc0 + * (c) 2005 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://prototype.conio.net/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.5.0_rc0', + ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)', + + emptyFunction: function() {}, + K: function(x) {return x} +} + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +} + +var Abstract = new Object(); + +Object.extend = function(destination, source) { + for (var property in source) { + destination[property] = source[property]; + } + return destination; +} + +Object.inspect = function(object) { + try { + if (object == undefined) return 'undefined'; + if (object == null) return 'null'; + return object.inspect ? object.inspect() : object.toString(); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } +} + +Function.prototype.bind = function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } +} + +Function.prototype.bindAsEventListener = function(object) { + var __method = this; + return function(event) { + return __method.call(object, event || window.event); + } +} + +Object.extend(Number.prototype, { + toColorPart: function() { + var digits = this.toString(16); + if (this < 16) return '0' + digits; + return digits; + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + } +}); + +var Try = { + these: function() { + var returnValue; + + for (var i = 0; i < arguments.length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) {} + } + + return returnValue; + } +} + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create(); +PeriodicalExecuter.prototype = { + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.callback(); + } finally { + this.currentlyExecuting = false; + } + } + } +} +Object.extend(String.prototype, { + gsub: function(pattern, replacement) { + var result = '', source = this, match; + replacement = arguments.callee.prepareReplacement(replacement); + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += (replacement(match) || '').toString(); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + }, + + sub: function(pattern, replacement, count) { + replacement = this.gsub.prepareReplacement(replacement); + count = count === undefined ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + }, + + scan: function(pattern, iterator) { + this.gsub(pattern, iterator); + return this; + }, + + truncate: function(length, truncation) { + length = length || 30; + truncation = truncation === undefined ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : this; + }, + + strip: function() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + }, + + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(function(script) { return eval(script) }); + }, + + escapeHTML: function() { + var div = document.createElement('div'); + var text = document.createTextNode(this); + div.appendChild(text); + return div.innerHTML; + }, + + unescapeHTML: function() { + var div = document.createElement('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; + }, + + toQueryParams: function() { + var pairs = this.match(/^\??(.*)$/)[1].split('&'); + return pairs.inject({}, function(params, pairString) { + var pair = pairString.split('='); + params[pair[0]] = pair[1]; + return params; + }); + }, + + toArray: function() { + return this.split(''); + }, + + camelize: function() { + var oStringList = this.split('-'); + if (oStringList.length == 1) return oStringList[0]; + + var camelizedString = this.indexOf('-') == 0 + ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) + : oStringList[0]; + + for (var i = 1, len = oStringList.length; i < len; i++) { + var s = oStringList[i]; + camelizedString += s.charAt(0).toUpperCase() + s.substring(1); + } + + return camelizedString; + }, + + inspect: function() { + return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'"; + } +}); + +String.prototype.gsub.prepareReplacement = function(replacement) { + if (typeof replacement == 'function') return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; +} + +String.prototype.parseQuery = String.prototype.toQueryParams; + +var Template = Class.create(); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; +Template.prototype = { + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + return this.template.gsub(this.pattern, function(match) { + var before = match[1]; + if (before == '\\') return match[2]; + return before + (object[match[3]] || '').toString(); + }); + } +} + +var $break = new Object(); +var $continue = new Object(); + +var Enumerable = { + each: function(iterator) { + var index = 0; + try { + this._each(function(value) { + try { + iterator(value, index++); + } catch (e) { + if (e != $continue) throw e; + } + }); + } catch (e) { + if (e != $break) throw e; + } + }, + + all: function(iterator) { + var result = true; + this.each(function(value, index) { + result = result && !!(iterator || Prototype.K)(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator) { + var result = true; + this.each(function(value, index) { + if (result = !!(iterator || Prototype.K)(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator) { + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function (iterator) { + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator) { + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(pattern, iterator) { + var results = []; + this.each(function(value, index) { + var stringValue = value.toString(); + if (stringValue.match(pattern)) + results.push((iterator || Prototype.K)(value, index)); + }) + return results; + }, + + include: function(object) { + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inject: function(memo, iterator) { + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.collect(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (result == undefined || value >= result) + result = value; + }); + return result; + }, + + min: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (result == undefined || value < result) + result = value; + }); + return result; + }, + + partition: function(iterator) { + var trues = [], falses = []; + this.each(function(value, index) { + ((iterator || Prototype.K)(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value, index) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator) { + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator) { + return this.collect(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.collect(Prototype.K); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (typeof args.last() == 'function') + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + }, + + inspect: function() { + return '#'; + } +} + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray +}); +var $A = Array.from = function(iterable) { + if (!iterable) return []; + if (iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0; i < iterable.length; i++) + results.push(iterable[i]); + return results; + } +} + +Object.extend(Array.prototype, Enumerable); + +if (!Array.prototype._reverse) + Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0; i < this.length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != undefined || value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(value && value.constructor == Array ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + indexOf: function(object) { + for (var i = 0; i < this.length; i++) + if (this[i] == object) return i; + return -1; + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } +}); +var Hash = { + _each: function(iterator) { + for (var key in this) { + var value = this[key]; + if (typeof value == 'function') continue; + + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + merge: function(hash) { + return $H(hash).inject($H(this), function(mergedHash, pair) { + mergedHash[pair.key] = pair.value; + return mergedHash; + }); + }, + + toQueryString: function() { + return this.map(function(pair) { + return pair.map(encodeURIComponent).join('='); + }).join('&'); + }, + + inspect: function() { + return '#'; + } +} + +function $H(object) { + var hash = Object.extend({}, object || {}); + Object.extend(hash, Enumerable); + Object.extend(hash, Hash); + return hash; +} +ObjectRange = Class.create(); +Object.extend(ObjectRange.prototype, Enumerable); +Object.extend(ObjectRange.prototype, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + do { + iterator(value); + value = value.succ(); + } while (this.include(value)); + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +} + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responderToAdd) { + if (!this.include(responderToAdd)) + this.responders.push(responderToAdd); + }, + + unregister: function(responderToRemove) { + this.responders = this.responders.without(responderToRemove); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (responder[callback] && typeof responder[callback] == 'function') { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) {} + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { + Ajax.activeRequestCount++; + }, + + onComplete: function() { + Ajax.activeRequestCount--; + } +}); + +Ajax.Base = function() {}; +Ajax.Base.prototype = { + setOptions: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + parameters: '' + } + Object.extend(this.options, options || {}); + }, + + responseIsSuccess: function() { + return this.transport.status == undefined + || this.transport.status == 0 + || (this.transport.status >= 200 && this.transport.status < 300); + }, + + responseIsFailure: function() { + return !this.responseIsSuccess(); + } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { + initialize: function(url, options) { + this.transport = Ajax.getTransport(); + this.setOptions(options); + this.request(url); + }, + + request: function(url) { + var parameters = this.options.parameters || ''; + if (parameters.length > 0) parameters += '&_='; + + try { + this.url = url; + if (this.options.method == 'get' && parameters.length > 0) + this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; + + Ajax.Responders.dispatch('onCreate', this, this.transport); + + this.transport.open(this.options.method, this.url, + this.options.asynchronous); + + if (this.options.asynchronous) { + this.transport.onreadystatechange = this.onStateChange.bind(this); + setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); + } + + this.setRequestHeaders(); + + var body = this.options.postBody ? this.options.postBody : parameters; + this.transport.send(this.options.method == 'post' ? body : null); + + } catch (e) { + this.dispatchException(e); + } + }, + + setRequestHeaders: function() { + var requestHeaders = + ['X-Requested-With', 'XMLHttpRequest', + 'X-Prototype-Version', Prototype.Version, + 'Accept', 'text/javascript, text/html, application/xml, text/xml, */*']; + + if (this.options.method == 'post') { + requestHeaders.push('Content-type', this.options.contentType); + + /* Force "Connection: close" for Mozilla browsers to work around + * a bug where XMLHttpReqeuest sends an incorrect Content-length + * header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType) + requestHeaders.push('Connection', 'close'); + } + + if (this.options.requestHeaders) + requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); + + for (var i = 0; i < requestHeaders.length; i += 2) + this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState != 1) + this.respondToReadyState(this.transport.readyState); + }, + + header: function(name) { + try { + return this.transport.getResponseHeader(name); + } catch (e) {} + }, + + evalJSON: function() { + try { + return eval('(' + this.header('X-JSON') + ')'); + } catch (e) {} + }, + + evalResponse: function() { + try { + return eval(this.transport.responseText); + } catch (e) { + this.dispatchException(e); + } + }, + + respondToReadyState: function(readyState) { + var event = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.evalJSON(); + + if (event == 'Complete') { + try { + (this.options['on' + this.transport.status] + || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(transport, json); + } catch (e) { + this.dispatchException(e); + } + + if ((this.header('Content-type') || '').match(/^text\/javascript/i)) + this.evalResponse(); + } + + try { + (this.options['on' + event] || Prototype.emptyFunction)(transport, json); + Ajax.Responders.dispatch('on' + event, this, transport, json); + } catch (e) { + this.dispatchException(e); + } + + /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ + if (event == 'Complete') + this.transport.onreadystatechange = Prototype.emptyFunction; + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Updater = Class.create(); + +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { + initialize: function(container, url, options) { + this.containers = { + success: container.success ? $(container.success) : $(container), + failure: container.failure ? $(container.failure) : + (container.success ? null : $(container)) + } + + this.transport = Ajax.getTransport(); + this.setOptions(options); + + var onComplete = this.options.onComplete || Prototype.emptyFunction; + this.options.onComplete = (function(transport, object) { + this.updateContent(); + onComplete(transport, object); + }).bind(this); + + this.request(url); + }, + + updateContent: function() { + var receiver = this.responseIsSuccess() ? + this.containers.success : this.containers.failure; + var response = this.transport.responseText; + + if (!this.options.evalScripts) + response = response.stripScripts(); + + if (receiver) { + if (this.options.insertion) { + new this.options.insertion(receiver, response); + } else { + Element.update(receiver, response); + } + } + + if (this.responseIsSuccess()) { + if (this.onComplete) + setTimeout(this.onComplete.bind(this), 10); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { + initialize: function(container, url, options) { + this.setOptions(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = {}; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(request) { + if (this.options.decay) { + this.decay = (request.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = request.responseText; + } + this.timer = setTimeout(this.onTimerEvent.bind(this), + this.decay * this.frequency * 1000); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +function $() { + var results = [], element; + for (var i = 0; i < arguments.length; i++) { + element = arguments[i]; + if (typeof element == 'string') + element = document.getElementById(element); + results.push(Element.extend(element)); + } + return results.length < 2 ? results[0] : results; +} + +document.getElementsByClassName = function(className, parentElement) { + var children = ($(parentElement) || document.body).getElementsByTagName('*'); + return $A(children).inject([], function(elements, child) { + if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) + elements.push(Element.extend(child)); + return elements; + }); +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Element) + var Element = new Object(); + +Element.extend = function(element) { + if (!element) return; + if (_nativeExtensions) return element; + + if (!element._extended && element.tagName && element != window) { + var methods = Element.Methods, cache = Element.extend.cache; + for (property in methods) { + var value = methods[property]; + if (typeof value == 'function') + element[property] = cache.findOrStore(value); + } + } + + element._extended = true; + return element; +} + +Element.extend.cache = { + findOrStore: function(value) { + return this[value] = this[value] || function() { + return value.apply(null, [this].concat($A(arguments))); + } + } +} + +Element.Methods = { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + Element[Element.visible(element) ? 'hide' : 'show'](element); + } + }, + + hide: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = 'none'; + } + }, + + show: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = ''; + } + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + }, + + update: function(element, html) { + $(element).innerHTML = html.stripScripts(); + setTimeout(function() {html.evalScripts()}, 10); + }, + + replace: function(element, html) { + element = $(element); + if (element.outerHTML) { + element.outerHTML = html.stripScripts(); + } else { + var range = element.ownerDocument.createRange(); + range.selectNodeContents(element); + element.parentNode.replaceChild( + range.createContextualFragment(html.stripScripts()), element); + } + setTimeout(function() {html.evalScripts()}, 10); + }, + + getHeight: function(element) { + element = $(element); + return element.offsetHeight; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).include(className); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).add(className); + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).remove(className); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + for (var i = 0; i < element.childNodes.length; i++) { + var node = element.childNodes[i]; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + Element.remove(node); + } + }, + + empty: function(element) { + return $(element).innerHTML.match(/^\s*$/); + }, + + childOf: function(element, ancestor) { + element = $(element), ancestor = $(ancestor); + while (element = element.parentNode) + if (element == ancestor) return true; + return false; + }, + + scrollTo: function(element) { + element = $(element); + var x = element.x ? element.x : element.offsetLeft, + y = element.y ? element.y : element.offsetTop; + window.scrollTo(x, y); + }, + + getStyle: function(element, style) { + element = $(element); + var value = element.style[style.camelize()]; + if (!value) { + if (document.defaultView && document.defaultView.getComputedStyle) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css.getPropertyValue(style) : null; + } else if (element.currentStyle) { + value = element.currentStyle[style.camelize()]; + } + } + + if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) + if (Element.getStyle(element, 'position') == 'static') value = 'auto'; + + return value == 'auto' ? null : value; + }, + + setStyle: function(element, style) { + element = $(element); + for (var name in style) + element.style[name.camelize()] = style[name]; + }, + + getDimensions: function(element) { + element = $(element); + if (Element.getStyle(element, 'display') != 'none') + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = ''; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = 'none'; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return; + element._overflow = element.style.overflow; + if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') + element.style.overflow = 'hidden'; + }, + + undoClipping: function(element) { + element = $(element); + if (element._overflow) return; + element.style.overflow = element._overflow; + element._overflow = undefined; + } +} + +Object.extend(Element, Element.Methods); + +var _nativeExtensions = false; + +if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + var HTMLElement = {} + HTMLElement.prototype = document.createElement('div').__proto__; +} + +Element.addMethods = function(methods) { + Object.extend(Element.Methods, methods || {}); + + if(typeof HTMLElement != 'undefined') { + var methods = Element.Methods, cache = Element.extend.cache; + for (property in methods) { + var value = methods[property]; + if (typeof value == 'function') + HTMLElement.prototype[property] = cache.findOrStore(value); + } + _nativeExtensions = true; + } +} + +Element.addMethods(); + +var Toggle = new Object(); +Toggle.display = Element.toggle; + +/*--------------------------------------------------------------------------*/ + +Abstract.Insertion = function(adjacency) { + this.adjacency = adjacency; +} + +Abstract.Insertion.prototype = { + initialize: function(element, content) { + this.element = $(element); + this.content = content.stripScripts(); + + if (this.adjacency && this.element.insertAdjacentHTML) { + try { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } catch (e) { + var tagName = this.element.tagName.toLowerCase(); + if (tagName == 'tbody' || tagName == 'tr') { + this.insertContent(this.contentFromAnonymousTable()); + } else { + throw e; + } + } + } else { + this.range = this.element.ownerDocument.createRange(); + if (this.initializeRange) this.initializeRange(); + this.insertContent([this.range.createContextualFragment(this.content)]); + } + + setTimeout(function() {content.evalScripts()}, 10); + }, + + contentFromAnonymousTable: function() { + var div = document.createElement('div'); + div.innerHTML = '' + this.content + '
    '; + return $A(div.childNodes[0].childNodes[0].childNodes); + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, this.element); + }).bind(this)); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function(fragments) { + fragments.reverse(false).each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.appendChild(fragment); + }).bind(this)); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, + this.element.nextSibling); + }).bind(this)); + } +}); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set(this.toArray().concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set(this.select(function(className) { + return className != classNameToRemove; + }).join(' ')); + }, + + toString: function() { + return this.toArray().join(' '); + } +} + +Object.extend(Element.ClassNames.prototype, Enumerable); +var Selector = Class.create(); +Selector.prototype = { + initialize: function(expression) { + this.params = {classNames: []}; + this.expression = expression.toString().strip(); + this.parseExpression(); + this.compileMatcher(); + }, + + parseExpression: function() { + function abort(message) { throw 'Parse error in selector: ' + message; } + + if (this.expression == '') abort('empty expression'); + + var params = this.params, expr = this.expression, match, modifier, clause, rest; + while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) { + params.attributes = params.attributes || []; + params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''}); + expr = match[1]; + } + + if (expr == '*') return this.params.wildcard = true; + + while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) { + modifier = match[1], clause = match[2], rest = match[3]; + switch (modifier) { + case '#': params.id = clause; break; + case '.': params.classNames.push(clause); break; + case '': + case undefined: params.tagName = clause.toUpperCase(); break; + default: abort(expr.inspect()); + } + expr = rest; + } + + if (expr.length > 0) abort(expr.inspect()); + }, + + buildMatchExpression: function() { + var params = this.params, conditions = [], clause; + + if (params.wildcard) + conditions.push('true'); + if (clause = params.id) + conditions.push('element.id == ' + clause.inspect()); + if (clause = params.tagName) + conditions.push('element.tagName.toUpperCase() == ' + clause.inspect()); + if ((clause = params.classNames).length > 0) + for (var i = 0; i < clause.length; i++) + conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')'); + if (clause = params.attributes) { + clause.each(function(attribute) { + var value = 'element.getAttribute(' + attribute.name.inspect() + ')'; + var splitValueBy = function(delimiter) { + return value + ' && ' + value + '.split(' + delimiter.inspect() + ')'; + } + + switch (attribute.operator) { + case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break; + case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break; + case '|=': conditions.push( + splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect() + ); break; + case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break; + case '': + case undefined: conditions.push(value + ' != null'); break; + default: throw 'Unknown operator ' + attribute.operator + ' in selector'; + } + }); + } + + return conditions.join(' && '); + }, + + compileMatcher: function() { + this.match = new Function('element', 'if (!element.tagName) return false; \ + return ' + this.buildMatchExpression()); + }, + + findElements: function(scope) { + var element; + + if (element = $(this.params.id)) + if (this.match(element)) + if (!scope || Element.childOf(element, scope)) + return [element]; + + scope = (scope || document).getElementsByTagName(this.params.tagName || '*'); + + var results = []; + for (var i = 0; i < scope.length; i++) + if (this.match(element = scope[i])) + results.push(Element.extend(element)); + + return results; + }, + + toString: function() { + return this.expression; + } +} + +function $$() { + return $A(arguments).map(function(expression) { + return expression.strip().split(/\s+/).inject([null], function(results, expr) { + var selector = new Selector(expr); + return results.map(selector.findElements.bind(selector)).flatten(); + }); + }).flatten(); +} +var Field = { + clear: function() { + for (var i = 0; i < arguments.length; i++) + $(arguments[i]).value = ''; + }, + + focus: function(element) { + $(element).focus(); + }, + + present: function() { + for (var i = 0; i < arguments.length; i++) + if ($(arguments[i]).value == '') return false; + return true; + }, + + select: function(element) { + $(element).select(); + }, + + activate: function(element) { + element = $(element); + element.focus(); + if (element.select) + element.select(); + } +} + +/*--------------------------------------------------------------------------*/ + +var Form = { + serialize: function(form) { + var elements = Form.getElements($(form)); + var queryComponents = new Array(); + + for (var i = 0; i < elements.length; i++) { + var queryComponent = Form.Element.serialize(elements[i]); + if (queryComponent) + queryComponents.push(queryComponent); + } + + return queryComponents.join('&'); + }, + + getElements: function(form) { + form = $(form); + var elements = new Array(); + + for (var tagName in Form.Element.Serializers) { + var tagElements = form.getElementsByTagName(tagName); + for (var j = 0; j < tagElements.length; j++) + elements.push(tagElements[j]); + } + return elements; + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) + return inputs; + + var matchingInputs = new Array(); + for (var i = 0; i < inputs.length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || + (name && input.name != name)) + continue; + matchingInputs.push(input); + } + + return matchingInputs; + }, + + disable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.blur(); + element.disabled = 'true'; + } + }, + + enable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.disabled = ''; + } + }, + + findFirstElement: function(form) { + return Form.getElements(form).find(function(element) { + return element.type != 'hidden' && !element.disabled && + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + Field.activate(Form.findFirstElement(form)); + }, + + reset: function(form) { + $(form).reset(); + } +} + +Form.Element = { + serialize: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) { + var key = encodeURIComponent(parameter[0]); + if (key.length == 0) return; + + if (parameter[1].constructor != Array) + parameter[1] = [parameter[1]]; + + return parameter[1].map(function(value) { + return key + '=' + encodeURIComponent(value); + }).join('&'); + } + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return parameter[1]; + } +} + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'submit': + case 'hidden': + case 'password': + case 'text': + return Form.Element.Serializers.textarea(element); + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + } + return false; + }, + + inputSelector: function(element) { + if (element.checked) + return [element.name, element.value]; + }, + + textarea: function(element) { + return [element.name, element.value]; + }, + + select: function(element) { + return Form.Element.Serializers[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + }, + + selectOne: function(element) { + var value = '', opt, index = element.selectedIndex; + if (index >= 0) { + opt = element.options[index]; + value = opt.value || opt.text; + } + return [element.name, value]; + }, + + selectMany: function(element) { + var value = []; + for (var i = 0; i < element.length; i++) { + var opt = element.options[i]; + if (opt.selected) + value.push(opt.value || opt.text); + } + return [element.name, value]; + } +} + +/*--------------------------------------------------------------------------*/ + +var $F = Form.Element.getValue; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + var elements = Form.getElements(this.element); + for (var i = 0; i < elements.length; i++) + this.registerCallback(elements[i]); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + case 'password': + case 'text': + case 'textarea': + case 'select-one': + case 'select-multiple': + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + + element: function(event) { + return event.target || event.srcElement; + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + event.cancelBubble = true; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0; i < Event.observers.length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.attachEvent)) + name = 'keydown'; + + this._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.detachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + element.detachEvent('on' + name, observer); + } + } +}); + +/* prevent memory leaks in IE */ +if (navigator.appVersion.match(/\bMSIE\b/)) + Event.observe(window, 'unload', Event.unloadCache, false); +var Position = { + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + p = Element.getStyle(element, 'position'); + if (p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; + }, + + offsetParent: function(element) { + if (element.offsetParent) return element.offsetParent; + if (element == document.body) return element; + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return element; + + return document.body; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + clone: function(source, target) { + source = $(source); + target = $(target); + target.style.position = 'absolute'; + var offsets = this.cumulativeOffset(source); + target.style.top = offsets[1] + 'px'; + target.style.left = offsets[0] + 'px'; + target.style.width = source.offsetWidth + 'px'; + target.style.height = source.offsetHeight + 'px'; + }, + + page: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent==document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } while (element = element.parentNode); + + return [valueL, valueT]; + }, + + clone: function(source, target) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || {}) + + // find page position of source + source = $(source); + var p = Position.page(source); + + // find coordinate system to use + target = $(target); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(target,'position') == 'absolute') { + parent = Position.offsetParent(target); + delta = Position.page(parent); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if(options.setWidth) target.style.width = source.offsetWidth + 'px'; + if(options.setHeight) target.style.height = source.offsetHeight + 'px'; + }, + + absolutize: function(element) { + element = $(element); + if (element.style.position == 'absolute') return; + Position.prepare(); + + var offsets = Position.positionedOffset(element); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px';; + element.style.left = left + 'px';; + element.style.width = width + 'px';; + element.style.height = height + 'px';; + }, + + relativize: function(element) { + element = $(element); + if (element.style.position == 'relative') return; + Position.prepare(); + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + } +} + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned. For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + Position.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return [valueL, valueT]; + } +} \ No newline at end of file diff --git a/tracks/vendor/selenium/lib/scriptaculous/builder.js b/tracks/vendor/selenium/lib/scriptaculous/builder.js new file mode 100644 index 00000000..5b15ba93 --- /dev/null +++ b/tracks/vendor/selenium/lib/scriptaculous/builder.js @@ -0,0 +1,101 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// See scriptaculous.js for full license. + +var Builder = { + NODEMAP: { + AREA: 'map', + CAPTION: 'table', + COL: 'table', + COLGROUP: 'table', + LEGEND: 'fieldset', + OPTGROUP: 'select', + OPTION: 'select', + PARAM: 'object', + TBODY: 'table', + TD: 'table', + TFOOT: 'table', + TH: 'table', + THEAD: 'table', + TR: 'table' + }, + // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, + // due to a Firefox bug + node: function(elementName) { + elementName = elementName.toUpperCase(); + + // try innerHTML approach + var parentTag = this.NODEMAP[elementName] || 'div'; + var parentElement = document.createElement(parentTag); + try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 + parentElement.innerHTML = "<" + elementName + ">"; + } catch(e) {} + var element = parentElement.firstChild || null; + + // see if browser added wrapping tags + if(element && (element.tagName != elementName)) + element = element.getElementsByTagName(elementName)[0]; + + // fallback to createElement approach + if(!element) element = document.createElement(elementName); + + // abort if nothing could be created + if(!element) return; + + // attributes (or text) + if(arguments[1]) + if(this._isStringOrNumber(arguments[1]) || + (arguments[1] instanceof Array)) { + this._children(element, arguments[1]); + } else { + var attrs = this._attributes(arguments[1]); + if(attrs.length) { + try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 + parentElement.innerHTML = "<" +elementName + " " + + attrs + ">"; + } catch(e) {} + element = parentElement.firstChild || null; + // workaround firefox 1.0.X bug + if(!element) { + element = document.createElement(elementName); + for(attr in arguments[1]) + element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; + } + if(element.tagName != elementName) + element = parentElement.getElementsByTagName(elementName)[0]; + } + } + + // text, or array of children + if(arguments[2]) + this._children(element, arguments[2]); + + return element; + }, + _text: function(text) { + return document.createTextNode(text); + }, + _attributes: function(attributes) { + var attrs = []; + for(attribute in attributes) + attrs.push((attribute=='className' ? 'class' : attribute) + + '="' + attributes[attribute].toString().escapeHTML() + '"'); + return attrs.join(" "); + }, + _children: function(element, children) { + if(typeof children=='object') { // array can hold nodes and text + children.flatten().each( function(e) { + if(typeof e=='object') + element.appendChild(e) + else + if(Builder._isStringOrNumber(e)) + element.appendChild(Builder._text(e)); + }); + } else + if(Builder._isStringOrNumber(children)) + element.appendChild(Builder._text(children)); + }, + _isStringOrNumber: function(param) { + return(typeof param=='string' || typeof param=='number'); + } +} \ No newline at end of file diff --git a/tracks/vendor/selenium/lib/scriptaculous/controls.js b/tracks/vendor/selenium/lib/scriptaculous/controls.js new file mode 100644 index 00000000..de0261ed --- /dev/null +++ b/tracks/vendor/selenium/lib/scriptaculous/controls.js @@ -0,0 +1,815 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +// (c) 2005 Jon Tirsen (http://www.tirsen.com) +// Contributors: +// Richard Livsey +// Rahul Bhargava +// Rob Wills +// +// See scriptaculous.js for full license. + +// Autocompleter.Base handles all the autocompletion functionality +// that's independent of the data source for autocompletion. This +// includes drawing the autocompletion menu, observing keyboard +// and mouse events, and similar. +// +// Specific autocompleters need to provide, at the very least, +// a getUpdatedChoices function that will be invoked every time +// the text inside the monitored textbox changes. This method +// should get the text for which to provide autocompletion by +// invoking this.getToken(), NOT by directly accessing +// this.element.value. This is to allow incremental tokenized +// autocompletion. Specific auto-completion logic (AJAX, etc) +// belongs in getUpdatedChoices. +// +// Tokenized incremental autocompletion is enabled automatically +// when an autocompleter is instantiated with the 'tokens' option +// in the options parameter, e.g.: +// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); +// will incrementally autocomplete with a comma as the token. +// Additionally, ',' in the above example can be replaced with +// a token array, e.g. { tokens: [',', '\n'] } which +// enables autocompletion on multiple tokens. This is most +// useful when one of the tokens is \n (a newline), as it +// allows smart autocompletion after linebreaks. + +var Autocompleter = {} +Autocompleter.Base = function() {}; +Autocompleter.Base.prototype = { + baseInitialize: function(element, update, options) { + this.element = $(element); + this.update = $(update); + this.hasFocus = false; + this.changed = false; + this.active = false; + this.index = 0; + this.entryCount = 0; + + if (this.setOptions) + this.setOptions(options); + else + this.options = options || {}; + + this.options.paramName = this.options.paramName || this.element.name; + this.options.tokens = this.options.tokens || []; + this.options.frequency = this.options.frequency || 0.4; + this.options.minChars = this.options.minChars || 1; + this.options.onShow = this.options.onShow || + function(element, update){ + if(!update.style.position || update.style.position=='absolute') { + update.style.position = 'absolute'; + Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight}); + } + Effect.Appear(update,{duration:0.15}); + }; + this.options.onHide = this.options.onHide || + function(element, update){ new Effect.Fade(update,{duration:0.15}) }; + + if (typeof(this.options.tokens) == 'string') + this.options.tokens = new Array(this.options.tokens); + + this.observer = null; + + this.element.setAttribute('autocomplete','off'); + + Element.hide(this.update); + + Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this)); + Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this)); + }, + + show: function() { + if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); + if(!this.iefix && + (navigator.appVersion.indexOf('MSIE')>0) && + (navigator.userAgent.indexOf('Opera')<0) && + (Element.getStyle(this.update, 'position')=='absolute')) { + new Insertion.After(this.update, + ''); + this.iefix = $(this.update.id+'_iefix'); + } + if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); + }, + + fixIEOverlapping: function() { + Position.clone(this.update, this.iefix); + this.iefix.style.zIndex = 1; + this.update.style.zIndex = 2; + Element.show(this.iefix); + }, + + hide: function() { + this.stopIndicator(); + if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); + if(this.iefix) Element.hide(this.iefix); + }, + + startIndicator: function() { + if(this.options.indicator) Element.show(this.options.indicator); + }, + + stopIndicator: function() { + if(this.options.indicator) Element.hide(this.options.indicator); + }, + + onKeyPress: function(event) { + if(this.active) + switch(event.keyCode) { + case Event.KEY_TAB: + case Event.KEY_RETURN: + this.selectEntry(); + Event.stop(event); + case Event.KEY_ESC: + this.hide(); + this.active = false; + Event.stop(event); + return; + case Event.KEY_LEFT: + case Event.KEY_RIGHT: + return; + case Event.KEY_UP: + this.markPrevious(); + this.render(); + if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); + return; + case Event.KEY_DOWN: + this.markNext(); + this.render(); + if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); + return; + } + else + if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || + (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return; + + this.changed = true; + this.hasFocus = true; + + if(this.observer) clearTimeout(this.observer); + this.observer = + setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); + }, + + activate: function() { + this.changed = false; + this.hasFocus = true; + this.getUpdatedChoices(); + }, + + onHover: function(event) { + var element = Event.findElement(event, 'LI'); + if(this.index != element.autocompleteIndex) + { + this.index = element.autocompleteIndex; + this.render(); + } + Event.stop(event); + }, + + onClick: function(event) { + var element = Event.findElement(event, 'LI'); + this.index = element.autocompleteIndex; + this.selectEntry(); + this.hide(); + }, + + onBlur: function(event) { + // needed to make click events working + setTimeout(this.hide.bind(this), 250); + this.hasFocus = false; + this.active = false; + }, + + render: function() { + if(this.entryCount > 0) { + for (var i = 0; i < this.entryCount; i++) + this.index==i ? + Element.addClassName(this.getEntry(i),"selected") : + Element.removeClassName(this.getEntry(i),"selected"); + + if(this.hasFocus) { + this.show(); + this.active = true; + } + } else { + this.active = false; + this.hide(); + } + }, + + markPrevious: function() { + if(this.index > 0) this.index-- + else this.index = this.entryCount-1; + }, + + markNext: function() { + if(this.index < this.entryCount-1) this.index++ + else this.index = 0; + }, + + getEntry: function(index) { + return this.update.firstChild.childNodes[index]; + }, + + getCurrentEntry: function() { + return this.getEntry(this.index); + }, + + selectEntry: function() { + this.active = false; + this.updateElement(this.getCurrentEntry()); + }, + + updateElement: function(selectedElement) { + if (this.options.updateElement) { + this.options.updateElement(selectedElement); + return; + } + var value = ''; + if (this.options.select) { + var nodes = document.getElementsByClassName(this.options.select, selectedElement) || []; + if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); + } else + value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); + + var lastTokenPos = this.findLastToken(); + if (lastTokenPos != -1) { + var newValue = this.element.value.substr(0, lastTokenPos + 1); + var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/); + if (whitespace) + newValue += whitespace[0]; + this.element.value = newValue + value; + } else { + this.element.value = value; + } + this.element.focus(); + + if (this.options.afterUpdateElement) + this.options.afterUpdateElement(this.element, selectedElement); + }, + + updateChoices: function(choices) { + if(!this.changed && this.hasFocus) { + this.update.innerHTML = choices; + Element.cleanWhitespace(this.update); + Element.cleanWhitespace(this.update.firstChild); + + if(this.update.firstChild && this.update.firstChild.childNodes) { + this.entryCount = + this.update.firstChild.childNodes.length; + for (var i = 0; i < this.entryCount; i++) { + var entry = this.getEntry(i); + entry.autocompleteIndex = i; + this.addObservers(entry); + } + } else { + this.entryCount = 0; + } + + this.stopIndicator(); + + this.index = 0; + this.render(); + } + }, + + addObservers: function(element) { + Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); + Event.observe(element, "click", this.onClick.bindAsEventListener(this)); + }, + + onObserverEvent: function() { + this.changed = false; + if(this.getToken().length>=this.options.minChars) { + this.startIndicator(); + this.getUpdatedChoices(); + } else { + this.active = false; + this.hide(); + } + }, + + getToken: function() { + var tokenPos = this.findLastToken(); + if (tokenPos != -1) + var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,''); + else + var ret = this.element.value; + + return /\n/.test(ret) ? '' : ret; + }, + + findLastToken: function() { + var lastTokenPos = -1; + + for (var i=0; i lastTokenPos) + lastTokenPos = thisTokenPos; + } + return lastTokenPos; + } +} + +Ajax.Autocompleter = Class.create(); +Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { + initialize: function(element, update, url, options) { + this.baseInitialize(element, update, options); + this.options.asynchronous = true; + this.options.onComplete = this.onComplete.bind(this); + this.options.defaultParams = this.options.parameters || null; + this.url = url; + }, + + getUpdatedChoices: function() { + entry = encodeURIComponent(this.options.paramName) + '=' + + encodeURIComponent(this.getToken()); + + this.options.parameters = this.options.callback ? + this.options.callback(this.element, entry) : entry; + + if(this.options.defaultParams) + this.options.parameters += '&' + this.options.defaultParams; + + new Ajax.Request(this.url, this.options); + }, + + onComplete: function(request) { + this.updateChoices(request.responseText); + } + +}); + +// The local array autocompleter. Used when you'd prefer to +// inject an array of autocompletion options into the page, rather +// than sending out Ajax queries, which can be quite slow sometimes. +// +// The constructor takes four parameters. The first two are, as usual, +// the id of the monitored textbox, and id of the autocompletion menu. +// The third is the array you want to autocomplete from, and the fourth +// is the options block. +// +// Extra local autocompletion options: +// - choices - How many autocompletion choices to offer +// +// - partialSearch - If false, the autocompleter will match entered +// text only at the beginning of strings in the +// autocomplete array. Defaults to true, which will +// match text at the beginning of any *word* in the +// strings in the autocomplete array. If you want to +// search anywhere in the string, additionally set +// the option fullSearch to true (default: off). +// +// - fullSsearch - Search anywhere in autocomplete array strings. +// +// - partialChars - How many characters to enter before triggering +// a partial match (unlike minChars, which defines +// how many characters are required to do any match +// at all). Defaults to 2. +// +// - ignoreCase - Whether to ignore case when autocompleting. +// Defaults to true. +// +// It's possible to pass in a custom function as the 'selector' +// option, if you prefer to write your own autocompletion logic. +// In that case, the other options above will not apply unless +// you support them. + +Autocompleter.Local = Class.create(); +Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { + initialize: function(element, update, array, options) { + this.baseInitialize(element, update, options); + this.options.array = array; + }, + + getUpdatedChoices: function() { + this.updateChoices(this.options.selector(this)); + }, + + setOptions: function(options) { + this.options = Object.extend({ + choices: 10, + partialSearch: true, + partialChars: 2, + ignoreCase: true, + fullSearch: false, + selector: function(instance) { + var ret = []; // Beginning matches + var partial = []; // Inside matches + var entry = instance.getToken(); + var count = 0; + + for (var i = 0; i < instance.options.array.length && + ret.length < instance.options.choices ; i++) { + + var elem = instance.options.array[i]; + var foundPos = instance.options.ignoreCase ? + elem.toLowerCase().indexOf(entry.toLowerCase()) : + elem.indexOf(entry); + + while (foundPos != -1) { + if (foundPos == 0 && elem.length != entry.length) { + ret.push("
  • " + elem.substr(0, entry.length) + "" + + elem.substr(entry.length) + "
  • "); + break; + } else if (entry.length >= instance.options.partialChars && + instance.options.partialSearch && foundPos != -1) { + if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { + partial.push("
  • " + elem.substr(0, foundPos) + "" + + elem.substr(foundPos, entry.length) + "" + elem.substr( + foundPos + entry.length) + "
  • "); + break; + } + } + + foundPos = instance.options.ignoreCase ? + elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : + elem.indexOf(entry, foundPos + 1); + + } + } + if (partial.length) + ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) + return "
      " + ret.join('') + "
    "; + } + }, options || {}); + } +}); + +// AJAX in-place editor +// +// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor + +// Use this if you notice weird scrolling problems on some browsers, +// the DOM might be a bit confused when this gets called so do this +// waits 1 ms (with setTimeout) until it does the activation +Field.scrollFreeActivate = function(field) { + setTimeout(function() { + Field.activate(field); + }, 1); +} + +Ajax.InPlaceEditor = Class.create(); +Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; +Ajax.InPlaceEditor.prototype = { + initialize: function(element, url, options) { + this.url = url; + this.element = $(element); + + this.options = Object.extend({ + okButton: true, + okText: "ok", + cancelLink: true, + cancelText: "cancel", + savingText: "Saving...", + clickToEditText: "Click to edit", + okText: "ok", + rows: 1, + onComplete: function(transport, element) { + new Effect.Highlight(element, {startcolor: this.options.highlightcolor}); + }, + onFailure: function(transport) { + alert("Error communicating with the server: " + transport.responseText.stripTags()); + }, + callback: function(form) { + return Form.serialize(form); + }, + handleLineBreaks: true, + loadingText: 'Loading...', + savingClassName: 'inplaceeditor-saving', + loadingClassName: 'inplaceeditor-loading', + formClassName: 'inplaceeditor-form', + highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, + highlightendcolor: "#FFFFFF", + externalControl: null, + submitOnBlur: false, + ajaxOptions: {}, + evalScripts: false + }, options || {}); + + if(!this.options.formId && this.element.id) { + this.options.formId = this.element.id + "-inplaceeditor"; + if ($(this.options.formId)) { + // there's already a form with that name, don't specify an id + this.options.formId = null; + } + } + + if (this.options.externalControl) { + this.options.externalControl = $(this.options.externalControl); + } + + this.originalBackground = Element.getStyle(this.element, 'background-color'); + if (!this.originalBackground) { + this.originalBackground = "transparent"; + } + + this.element.title = this.options.clickToEditText; + + this.onclickListener = this.enterEditMode.bindAsEventListener(this); + this.mouseoverListener = this.enterHover.bindAsEventListener(this); + this.mouseoutListener = this.leaveHover.bindAsEventListener(this); + Event.observe(this.element, 'click', this.onclickListener); + Event.observe(this.element, 'mouseover', this.mouseoverListener); + Event.observe(this.element, 'mouseout', this.mouseoutListener); + if (this.options.externalControl) { + Event.observe(this.options.externalControl, 'click', this.onclickListener); + Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener); + Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); + } + }, + enterEditMode: function(evt) { + if (this.saving) return; + if (this.editing) return; + this.editing = true; + this.onEnterEditMode(); + if (this.options.externalControl) { + Element.hide(this.options.externalControl); + } + Element.hide(this.element); + this.createForm(); + this.element.parentNode.insertBefore(this.form, this.element); + Field.scrollFreeActivate(this.editField); + // stop the event to avoid a page refresh in Safari + if (evt) { + Event.stop(evt); + } + return false; + }, + createForm: function() { + this.form = document.createElement("form"); + this.form.id = this.options.formId; + Element.addClassName(this.form, this.options.formClassName) + this.form.onsubmit = this.onSubmit.bind(this); + + this.createEditField(); + + if (this.options.textarea) { + var br = document.createElement("br"); + this.form.appendChild(br); + } + + if (this.options.okButton) { + okButton = document.createElement("input"); + okButton.type = "submit"; + okButton.value = this.options.okText; + okButton.className = 'editor_ok_button'; + this.form.appendChild(okButton); + } + + if (this.options.cancelLink) { + cancelLink = document.createElement("a"); + cancelLink.href = "#"; + cancelLink.appendChild(document.createTextNode(this.options.cancelText)); + cancelLink.onclick = this.onclickCancel.bind(this); + cancelLink.className = 'editor_cancel'; + this.form.appendChild(cancelLink); + } + }, + hasHTMLLineBreaks: function(string) { + if (!this.options.handleLineBreaks) return false; + return string.match(/
    /i); + }, + convertHTMLLineBreaks: function(string) { + return string.replace(/
    /gi, "\n").replace(//gi, "\n").replace(/<\/p>/gi, "\n").replace(/

    /gi, ""); + }, + createEditField: function() { + var text; + if(this.options.loadTextURL) { + text = this.options.loadingText; + } else { + text = this.getText(); + } + + var obj = this; + + if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { + this.options.textarea = false; + var textField = document.createElement("input"); + textField.obj = this; + textField.type = "text"; + textField.name = "value"; + textField.value = text; + textField.style.backgroundColor = this.options.highlightcolor; + textField.className = 'editor_field'; + var size = this.options.size || this.options.cols || 0; + if (size != 0) textField.size = size; + if (this.options.submitOnBlur) + textField.onblur = this.onSubmit.bind(this); + this.editField = textField; + } else { + this.options.textarea = true; + var textArea = document.createElement("textarea"); + textArea.obj = this; + textArea.name = "value"; + textArea.value = this.convertHTMLLineBreaks(text); + textArea.rows = this.options.rows; + textArea.cols = this.options.cols || 40; + textArea.className = 'editor_field'; + if (this.options.submitOnBlur) + textArea.onblur = this.onSubmit.bind(this); + this.editField = textArea; + } + + if(this.options.loadTextURL) { + this.loadExternalText(); + } + this.form.appendChild(this.editField); + }, + getText: function() { + return this.element.innerHTML; + }, + loadExternalText: function() { + Element.addClassName(this.form, this.options.loadingClassName); + this.editField.disabled = true; + new Ajax.Request( + this.options.loadTextURL, + Object.extend({ + asynchronous: true, + onComplete: this.onLoadedExternalText.bind(this) + }, this.options.ajaxOptions) + ); + }, + onLoadedExternalText: function(transport) { + Element.removeClassName(this.form, this.options.loadingClassName); + this.editField.disabled = false; + this.editField.value = transport.responseText.stripTags(); + }, + onclickCancel: function() { + this.onComplete(); + this.leaveEditMode(); + return false; + }, + onFailure: function(transport) { + this.options.onFailure(transport); + if (this.oldInnerHTML) { + this.element.innerHTML = this.oldInnerHTML; + this.oldInnerHTML = null; + } + return false; + }, + onSubmit: function() { + // onLoading resets these so we need to save them away for the Ajax call + var form = this.form; + var value = this.editField.value; + + // do this first, sometimes the ajax call returns before we get a chance to switch on Saving... + // which means this will actually switch on Saving... *after* we've left edit mode causing Saving... + // to be displayed indefinitely + this.onLoading(); + + if (this.options.evalScripts) { + new Ajax.Request( + this.url, Object.extend({ + parameters: this.options.callback(form, value), + onComplete: this.onComplete.bind(this), + onFailure: this.onFailure.bind(this), + asynchronous:true, + evalScripts:true + }, this.options.ajaxOptions)); + } else { + new Ajax.Updater( + { success: this.element, + // don't update on failure (this could be an option) + failure: null }, + this.url, Object.extend({ + parameters: this.options.callback(form, value), + onComplete: this.onComplete.bind(this), + onFailure: this.onFailure.bind(this) + }, this.options.ajaxOptions)); + } + // stop the event to avoid a page refresh in Safari + if (arguments.length > 1) { + Event.stop(arguments[0]); + } + return false; + }, + onLoading: function() { + this.saving = true; + this.removeForm(); + this.leaveHover(); + this.showSaving(); + }, + showSaving: function() { + this.oldInnerHTML = this.element.innerHTML; + this.element.innerHTML = this.options.savingText; + Element.addClassName(this.element, this.options.savingClassName); + this.element.style.backgroundColor = this.originalBackground; + Element.show(this.element); + }, + removeForm: function() { + if(this.form) { + if (this.form.parentNode) Element.remove(this.form); + this.form = null; + } + }, + enterHover: function() { + if (this.saving) return; + this.element.style.backgroundColor = this.options.highlightcolor; + if (this.effect) { + this.effect.cancel(); + } + Element.addClassName(this.element, this.options.hoverClassName) + }, + leaveHover: function() { + if (this.options.backgroundColor) { + this.element.style.backgroundColor = this.oldBackground; + } + Element.removeClassName(this.element, this.options.hoverClassName) + if (this.saving) return; + this.effect = new Effect.Highlight(this.element, { + startcolor: this.options.highlightcolor, + endcolor: this.options.highlightendcolor, + restorecolor: this.originalBackground + }); + }, + leaveEditMode: function() { + Element.removeClassName(this.element, this.options.savingClassName); + this.removeForm(); + this.leaveHover(); + this.element.style.backgroundColor = this.originalBackground; + Element.show(this.element); + if (this.options.externalControl) { + Element.show(this.options.externalControl); + } + this.editing = false; + this.saving = false; + this.oldInnerHTML = null; + this.onLeaveEditMode(); + }, + onComplete: function(transport) { + this.leaveEditMode(); + this.options.onComplete.bind(this)(transport, this.element); + }, + onEnterEditMode: function() {}, + onLeaveEditMode: function() {}, + dispose: function() { + if (this.oldInnerHTML) { + this.element.innerHTML = this.oldInnerHTML; + } + this.leaveEditMode(); + Event.stopObserving(this.element, 'click', this.onclickListener); + Event.stopObserving(this.element, 'mouseover', this.mouseoverListener); + Event.stopObserving(this.element, 'mouseout', this.mouseoutListener); + if (this.options.externalControl) { + Event.stopObserving(this.options.externalControl, 'click', this.onclickListener); + Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener); + Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener); + } + } +}; + +Ajax.InPlaceCollectionEditor = Class.create(); +Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype); +Object.extend(Ajax.InPlaceCollectionEditor.prototype, { + createEditField: function() { + if (!this.cached_selectTag) { + var selectTag = document.createElement("select"); + var collection = this.options.collection || []; + var optionTag; + collection.each(function(e,i) { + optionTag = document.createElement("option"); + optionTag.value = (e instanceof Array) ? e[0] : e; + if(this.options.value==optionTag.value) optionTag.selected = true; + optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e)); + selectTag.appendChild(optionTag); + }.bind(this)); + this.cached_selectTag = selectTag; + } + + this.editField = this.cached_selectTag; + if(this.options.loadTextURL) this.loadExternalText(); + this.form.appendChild(this.editField); + this.options.callback = function(form, value) { + return "value=" + encodeURIComponent(value); + } + } +}); + +// Delayed observer, like Form.Element.Observer, +// but waits for delay after last key input +// Ideal for live-search fields + +Form.Element.DelayedObserver = Class.create(); +Form.Element.DelayedObserver.prototype = { + initialize: function(element, delay, callback) { + this.delay = delay || 0.5; + this.element = $(element); + this.callback = callback; + this.timer = null; + this.lastValue = $F(this.element); + Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); + }, + delayedListener: function(event) { + if(this.lastValue == $F(this.element)) return; + if(this.timer) clearTimeout(this.timer); + this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); + this.lastValue = $F(this.element); + }, + onTimerEvent: function() { + this.timer = null; + this.callback(this.element, $F(this.element)); + } +}; diff --git a/tracks/vendor/selenium/lib/scriptaculous/dragdrop.js b/tracks/vendor/selenium/lib/scriptaculous/dragdrop.js new file mode 100644 index 00000000..be2a30f5 --- /dev/null +++ b/tracks/vendor/selenium/lib/scriptaculous/dragdrop.js @@ -0,0 +1,915 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) +// +// See scriptaculous.js for full license. + +/*--------------------------------------------------------------------------*/ + +var Droppables = { + drops: [], + + remove: function(element) { + this.drops = this.drops.reject(function(d) { return d.element==$(element) }); + }, + + add: function(element) { + element = $(element); + var options = Object.extend({ + greedy: true, + hoverclass: null, + tree: false + }, arguments[1] || {}); + + // cache containers + if(options.containment) { + options._containers = []; + var containment = options.containment; + if((typeof containment == 'object') && + (containment.constructor == Array)) { + containment.each( function(c) { options._containers.push($(c)) }); + } else { + options._containers.push($(containment)); + } + } + + if(options.accept) options.accept = [options.accept].flatten(); + + Element.makePositioned(element); // fix IE + options.element = element; + + this.drops.push(options); + }, + + findDeepestChild: function(drops) { + deepest = drops[0]; + + for (i = 1; i < drops.length; ++i) + if (Element.isParent(drops[i].element, deepest.element)) + deepest = drops[i]; + + return deepest; + }, + + isContained: function(element, drop) { + var containmentNode; + if(drop.tree) { + containmentNode = element.treeNode; + } else { + containmentNode = element.parentNode; + } + return drop._containers.detect(function(c) { return containmentNode == c }); + }, + + isAffected: function(point, element, drop) { + return ( + (drop.element!=element) && + ((!drop._containers) || + this.isContained(element, drop)) && + ((!drop.accept) || + (Element.classNames(element).detect( + function(v) { return drop.accept.include(v) } ) )) && + Position.within(drop.element, point[0], point[1]) ); + }, + + deactivate: function(drop) { + if(drop.hoverclass) + Element.removeClassName(drop.element, drop.hoverclass); + this.last_active = null; + }, + + activate: function(drop) { + if(drop.hoverclass) + Element.addClassName(drop.element, drop.hoverclass); + this.last_active = drop; + }, + + show: function(point, element) { + if(!this.drops.length) return; + var affected = []; + + if(this.last_active) this.deactivate(this.last_active); + this.drops.each( function(drop) { + if(Droppables.isAffected(point, element, drop)) + affected.push(drop); + }); + + if(affected.length>0) { + drop = Droppables.findDeepestChild(affected); + Position.within(drop.element, point[0], point[1]); + if(drop.onHover) + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); + + Droppables.activate(drop); + } + }, + + fire: function(event, element) { + if(!this.last_active) return; + Position.prepare(); + + if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) + if (this.last_active.onDrop) + this.last_active.onDrop(element, this.last_active.element, event); + }, + + reset: function() { + if(this.last_active) + this.deactivate(this.last_active); + } +} + +var Draggables = { + drags: [], + observers: [], + + register: function(draggable) { + if(this.drags.length == 0) { + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.updateDrag.bindAsEventListener(this); + this.eventKeypress = this.keyPress.bindAsEventListener(this); + + Event.observe(document, "mouseup", this.eventMouseUp); + Event.observe(document, "mousemove", this.eventMouseMove); + Event.observe(document, "keypress", this.eventKeypress); + } + this.drags.push(draggable); + }, + + unregister: function(draggable) { + this.drags = this.drags.reject(function(d) { return d==draggable }); + if(this.drags.length == 0) { + Event.stopObserving(document, "mouseup", this.eventMouseUp); + Event.stopObserving(document, "mousemove", this.eventMouseMove); + Event.stopObserving(document, "keypress", this.eventKeypress); + } + }, + + activate: function(draggable) { + window.focus(); // allows keypress events if window isn't currently focused, fails for Safari + this.activeDraggable = draggable; + }, + + deactivate: function() { + this.activeDraggable = null; + }, + + updateDrag: function(event) { + if(!this.activeDraggable) return; + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + // Mozilla-based browsers fire successive mousemove events with + // the same coordinates, prevent needless redrawing (moz bug?) + if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; + this._lastPointer = pointer; + this.activeDraggable.updateDrag(event, pointer); + }, + + endDrag: function(event) { + if(!this.activeDraggable) return; + this._lastPointer = null; + this.activeDraggable.endDrag(event); + this.activeDraggable = null; + }, + + keyPress: function(event) { + if(this.activeDraggable) + this.activeDraggable.keyPress(event); + }, + + addObserver: function(observer) { + this.observers.push(observer); + this._cacheObserverCallbacks(); + }, + + removeObserver: function(element) { // element instead of observer fixes mem leaks + this.observers = this.observers.reject( function(o) { return o.element==element }); + this._cacheObserverCallbacks(); + }, + + notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' + if(this[eventName+'Count'] > 0) + this.observers.each( function(o) { + if(o[eventName]) o[eventName](eventName, draggable, event); + }); + }, + + _cacheObserverCallbacks: function() { + ['onStart','onEnd','onDrag'].each( function(eventName) { + Draggables[eventName+'Count'] = Draggables.observers.select( + function(o) { return o[eventName]; } + ).length; + }); + } +} + +/*--------------------------------------------------------------------------*/ + +var Draggable = Class.create(); +Draggable.prototype = { + initialize: function(element) { + var options = Object.extend({ + handle: false, + starteffect: function(element) { + element._opacity = Element.getOpacity(element); + new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); + }, + reverteffect: function(element, top_offset, left_offset) { + var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; + element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur}); + }, + endeffect: function(element) { + var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0 + new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity}); + }, + zindex: 1000, + revert: false, + scroll: false, + scrollSensitivity: 20, + scrollSpeed: 15, + snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] } + }, arguments[1] || {}); + + this.element = $(element); + + if(options.handle && (typeof options.handle == 'string')) { + var h = Element.childrenWithClassName(this.element, options.handle, true); + if(h.length>0) this.handle = h[0]; + } + if(!this.handle) this.handle = $(options.handle); + if(!this.handle) this.handle = this.element; + + if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) + options.scroll = $(options.scroll); + + Element.makePositioned(this.element); // fix IE + + this.delta = this.currentDelta(); + this.options = options; + this.dragging = false; + + this.eventMouseDown = this.initDrag.bindAsEventListener(this); + Event.observe(this.handle, "mousedown", this.eventMouseDown); + + Draggables.register(this); + }, + + destroy: function() { + Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); + Draggables.unregister(this); + }, + + currentDelta: function() { + return([ + parseInt(Element.getStyle(this.element,'left') || '0'), + parseInt(Element.getStyle(this.element,'top') || '0')]); + }, + + initDrag: function(event) { + if(Event.isLeftClick(event)) { + // abort on form elements, fixes a Firefox issue + var src = Event.element(event); + if(src.tagName && ( + src.tagName=='INPUT' || + src.tagName=='SELECT' || + src.tagName=='OPTION' || + src.tagName=='BUTTON' || + src.tagName=='TEXTAREA')) return; + + if(this.element._revert) { + this.element._revert.cancel(); + this.element._revert = null; + } + + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var pos = Position.cumulativeOffset(this.element); + this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); + + Draggables.activate(this); + Event.stop(event); + } + }, + + startDrag: function(event) { + this.dragging = true; + + if(this.options.zindex) { + this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); + this.element.style.zIndex = this.options.zindex; + } + + if(this.options.ghosting) { + this._clone = this.element.cloneNode(true); + Position.absolutize(this.element); + this.element.parentNode.insertBefore(this._clone, this.element); + } + + if(this.options.scroll) { + if (this.options.scroll == window) { + var where = this._getWindowScroll(this.options.scroll); + this.originalScrollLeft = where.left; + this.originalScrollTop = where.top; + } else { + this.originalScrollLeft = this.options.scroll.scrollLeft; + this.originalScrollTop = this.options.scroll.scrollTop; + } + } + + Draggables.notify('onStart', this, event); + if(this.options.starteffect) this.options.starteffect(this.element); + }, + + updateDrag: function(event, pointer) { + if(!this.dragging) this.startDrag(event); + Position.prepare(); + Droppables.show(pointer, this.element); + Draggables.notify('onDrag', this, event); + this.draw(pointer); + if(this.options.change) this.options.change(this); + + if(this.options.scroll) { + this.stopScrolling(); + + var p; + if (this.options.scroll == window) { + with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } + } else { + p = Position.page(this.options.scroll); + p[0] += this.options.scroll.scrollLeft; + p[1] += this.options.scroll.scrollTop; + p.push(p[0]+this.options.scroll.offsetWidth); + p.push(p[1]+this.options.scroll.offsetHeight); + } + var speed = [0,0]; + if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); + if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); + if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); + if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); + this.startScrolling(speed); + } + + // fix AppleWebKit rendering + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); + + Event.stop(event); + }, + + finishDrag: function(event, success) { + this.dragging = false; + + if(this.options.ghosting) { + Position.relativize(this.element); + Element.remove(this._clone); + this._clone = null; + } + + if(success) Droppables.fire(event, this.element); + Draggables.notify('onEnd', this, event); + + var revert = this.options.revert; + if(revert && typeof revert == 'function') revert = revert(this.element); + + var d = this.currentDelta(); + if(revert && this.options.reverteffect) { + this.options.reverteffect(this.element, + d[1]-this.delta[1], d[0]-this.delta[0]); + } else { + this.delta = d; + } + + if(this.options.zindex) + this.element.style.zIndex = this.originalZ; + + if(this.options.endeffect) + this.options.endeffect(this.element); + + Draggables.deactivate(this); + Droppables.reset(); + }, + + keyPress: function(event) { + if(event.keyCode!=Event.KEY_ESC) return; + this.finishDrag(event, false); + Event.stop(event); + }, + + endDrag: function(event) { + if(!this.dragging) return; + this.stopScrolling(); + this.finishDrag(event, true); + Event.stop(event); + }, + + draw: function(point) { + var pos = Position.cumulativeOffset(this.element); + var d = this.currentDelta(); + pos[0] -= d[0]; pos[1] -= d[1]; + + if(this.options.scroll && (this.options.scroll != window)) { + pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; + pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; + } + + var p = [0,1].map(function(i){ + return (point[i]-pos[i]-this.offset[i]) + }.bind(this)); + + if(this.options.snap) { + if(typeof this.options.snap == 'function') { + p = this.options.snap(p[0],p[1],this); + } else { + if(this.options.snap instanceof Array) { + p = p.map( function(v, i) { + return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this)) + } else { + p = p.map( function(v) { + return Math.round(v/this.options.snap)*this.options.snap }.bind(this)) + } + }} + + var style = this.element.style; + if((!this.options.constraint) || (this.options.constraint=='horizontal')) + style.left = p[0] + "px"; + if((!this.options.constraint) || (this.options.constraint=='vertical')) + style.top = p[1] + "px"; + if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering + }, + + stopScrolling: function() { + if(this.scrollInterval) { + clearInterval(this.scrollInterval); + this.scrollInterval = null; + Draggables._lastScrollPointer = null; + } + }, + + startScrolling: function(speed) { + this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; + this.lastScrolled = new Date(); + this.scrollInterval = setInterval(this.scroll.bind(this), 10); + }, + + scroll: function() { + var current = new Date(); + var delta = current - this.lastScrolled; + this.lastScrolled = current; + if(this.options.scroll == window) { + with (this._getWindowScroll(this.options.scroll)) { + if (this.scrollSpeed[0] || this.scrollSpeed[1]) { + var d = delta / 1000; + this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); + } + } + } else { + this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; + this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000; + } + + Position.prepare(); + Droppables.show(Draggables._lastPointer, this.element); + Draggables.notify('onDrag', this); + Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); + Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; + Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; + if (Draggables._lastScrollPointer[0] < 0) + Draggables._lastScrollPointer[0] = 0; + if (Draggables._lastScrollPointer[1] < 0) + Draggables._lastScrollPointer[1] = 0; + this.draw(Draggables._lastScrollPointer); + + if(this.options.change) this.options.change(this); + }, + + _getWindowScroll: function(w) { + var T, L, W, H; + with (w.document) { + if (w.document.documentElement && documentElement.scrollTop) { + T = documentElement.scrollTop; + L = documentElement.scrollLeft; + } else if (w.document.body) { + T = body.scrollTop; + L = body.scrollLeft; + } + if (w.innerWidth) { + W = w.innerWidth; + H = w.innerHeight; + } else if (w.document.documentElement && documentElement.clientWidth) { + W = documentElement.clientWidth; + H = documentElement.clientHeight; + } else { + W = body.offsetWidth; + H = body.offsetHeight + } + } + return { top: T, left: L, width: W, height: H }; + } +} + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create(); +SortableObserver.prototype = { + initialize: function(element, observer) { + this.element = $(element); + this.observer = observer; + this.lastValue = Sortable.serialize(this.element); + }, + + onStart: function() { + this.lastValue = Sortable.serialize(this.element); + }, + + onEnd: function() { + Sortable.unmark(); + if(this.lastValue != Sortable.serialize(this.element)) + this.observer(this.element) + } +} + +var Sortable = { + sortables: {}, + + _findRootElement: function(element) { + while (element.tagName != "BODY") { + if(element.id && Sortable.sortables[element.id]) return element; + element = element.parentNode; + } + }, + + options: function(element) { + element = Sortable._findRootElement($(element)); + if(!element) return; + return Sortable.sortables[element.id]; + }, + + destroy: function(element){ + var s = Sortable.options(element); + + if(s) { + Draggables.removeObserver(s.element); + s.droppables.each(function(d){ Droppables.remove(d) }); + s.draggables.invoke('destroy'); + + delete Sortable.sortables[s.element.id]; + } + }, + + create: function(element) { + element = $(element); + var options = Object.extend({ + element: element, + tag: 'li', // assumes li children, override with tag: 'tagname' + dropOnEmpty: false, + tree: false, + treeTag: 'ul', + overlap: 'vertical', // one of 'vertical', 'horizontal' + constraint: 'vertical', // one of 'vertical', 'horizontal', false + containment: element, // also takes array of elements (or id's); or false + handle: false, // or a CSS class + only: false, + hoverclass: null, + ghosting: false, + scroll: false, + scrollSensitivity: 20, + scrollSpeed: 15, + format: /^[^_]*_(.*)$/, + onChange: Prototype.emptyFunction, + onUpdate: Prototype.emptyFunction + }, arguments[1] || {}); + + // clear any old sortable with same element + this.destroy(element); + + // build options for the draggables + var options_for_draggable = { + revert: true, + scroll: options.scroll, + scrollSpeed: options.scrollSpeed, + scrollSensitivity: options.scrollSensitivity, + ghosting: options.ghosting, + constraint: options.constraint, + handle: options.handle }; + + if(options.starteffect) + options_for_draggable.starteffect = options.starteffect; + + if(options.reverteffect) + options_for_draggable.reverteffect = options.reverteffect; + else + if(options.ghosting) options_for_draggable.reverteffect = function(element) { + element.style.top = 0; + element.style.left = 0; + }; + + if(options.endeffect) + options_for_draggable.endeffect = options.endeffect; + + if(options.zindex) + options_for_draggable.zindex = options.zindex; + + // build options for the droppables + var options_for_droppable = { + overlap: options.overlap, + containment: options.containment, + tree: options.tree, + hoverclass: options.hoverclass, + onHover: Sortable.onHover + //greedy: !options.dropOnEmpty + } + + var options_for_tree = { + onHover: Sortable.onEmptyHover, + overlap: options.overlap, + containment: options.containment, + hoverclass: options.hoverclass + } + + // fix for gecko engine + Element.cleanWhitespace(element); + + options.draggables = []; + options.droppables = []; + + // drop on empty handling + if(options.dropOnEmpty || options.tree) { + Droppables.add(element, options_for_tree); + options.droppables.push(element); + } + + (this.findElements(element, options) || []).each( function(e) { + // handles are per-draggable + var handle = options.handle ? + Element.childrenWithClassName(e, options.handle)[0] : e; + options.draggables.push( + new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); + Droppables.add(e, options_for_droppable); + if(options.tree) e.treeNode = element; + options.droppables.push(e); + }); + + if(options.tree) { + (Sortable.findTreeElements(element, options) || []).each( function(e) { + Droppables.add(e, options_for_tree); + e.treeNode = element; + options.droppables.push(e); + }); + } + + // keep reference + this.sortables[element.id] = options; + + // for onupdate + Draggables.addObserver(new SortableObserver(element, options.onUpdate)); + + }, + + // return all suitable-for-sortable elements in a guaranteed order + findElements: function(element, options) { + return Element.findChildren( + element, options.only, options.tree ? true : false, options.tag); + }, + + findTreeElements: function(element, options) { + return Element.findChildren( + element, options.only, options.tree ? true : false, options.treeTag); + }, + + onHover: function(element, dropon, overlap) { + if(Element.isParent(dropon, element)) return; + + if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { + return; + } else if(overlap>0.5) { + Sortable.mark(dropon, 'before'); + if(dropon.previousSibling != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, dropon); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } else { + Sortable.mark(dropon, 'after'); + var nextElement = dropon.nextSibling || null; + if(nextElement != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, nextElement); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } + }, + + onEmptyHover: function(element, dropon, overlap) { + var oldParentNode = element.parentNode; + var droponOptions = Sortable.options(dropon); + + if(!Element.isParent(dropon, element)) { + var index; + + var children = Sortable.findElements(dropon, {tag: droponOptions.tag}); + var child = null; + + if(children) { + var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); + + for (index = 0; index < children.length; index += 1) { + if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { + offset -= Element.offsetSize (children[index], droponOptions.overlap); + } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { + child = index + 1 < children.length ? children[index + 1] : null; + break; + } else { + child = children[index]; + break; + } + } + } + + dropon.insertBefore(element, child); + + Sortable.options(oldParentNode).onChange(element); + droponOptions.onChange(element); + } + }, + + unmark: function() { + if(Sortable._marker) Element.hide(Sortable._marker); + }, + + mark: function(dropon, position) { + // mark on ghosting only + var sortable = Sortable.options(dropon.parentNode); + if(sortable && !sortable.ghosting) return; + + if(!Sortable._marker) { + Sortable._marker = $('dropmarker') || document.createElement('DIV'); + Element.hide(Sortable._marker); + Element.addClassName(Sortable._marker, 'dropmarker'); + Sortable._marker.style.position = 'absolute'; + document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); + } + var offsets = Position.cumulativeOffset(dropon); + Sortable._marker.style.left = offsets[0] + 'px'; + Sortable._marker.style.top = offsets[1] + 'px'; + + if(position=='after') + if(sortable.overlap == 'horizontal') + Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px'; + else + Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px'; + + Element.show(Sortable._marker); + }, + + _tree: function(element, options, parent) { + var children = Sortable.findElements(element, options) || []; + + for (var i = 0; i < children.length; ++i) { + var match = children[i].id.match(options.format); + + if (!match) continue; + + var child = { + id: encodeURIComponent(match ? match[1] : null), + element: element, + parent: parent, + children: new Array, + position: parent.children.length, + container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase()) + } + + /* Get the element containing the children and recurse over it */ + if (child.container) + this._tree(child.container, options, child) + + parent.children.push (child); + } + + return parent; + }, + + /* Finds the first element of the given tag type within a parent element. + Used for finding the first LI[ST] within a L[IST]I[TEM].*/ + _findChildrenElement: function (element, containerTag) { + if (element && element.hasChildNodes) + for (var i = 0; i < element.childNodes.length; ++i) + if (element.childNodes[i].tagName == containerTag) + return element.childNodes[i]; + + return null; + }, + + tree: function(element) { + element = $(element); + var sortableOptions = this.options(element); + var options = Object.extend({ + tag: sortableOptions.tag, + treeTag: sortableOptions.treeTag, + only: sortableOptions.only, + name: element.id, + format: sortableOptions.format + }, arguments[1] || {}); + + var root = { + id: null, + parent: null, + children: new Array, + container: element, + position: 0 + } + + return Sortable._tree (element, options, root); + }, + + /* Construct a [i] index for a particular node */ + _constructIndex: function(node) { + var index = ''; + do { + if (node.id) index = '[' + node.position + ']' + index; + } while ((node = node.parent) != null); + return index; + }, + + sequence: function(element) { + element = $(element); + var options = Object.extend(this.options(element), arguments[1] || {}); + + return $(this.findElements(element, options) || []).map( function(item) { + return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; + }); + }, + + setSequence: function(element, new_sequence) { + element = $(element); + var options = Object.extend(this.options(element), arguments[2] || {}); + + var nodeMap = {}; + this.findElements(element, options).each( function(n) { + if (n.id.match(options.format)) + nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; + n.parentNode.removeChild(n); + }); + + new_sequence.each(function(ident) { + var n = nodeMap[ident]; + if (n) { + n[1].appendChild(n[0]); + delete nodeMap[ident]; + } + }); + }, + + serialize: function(element) { + element = $(element); + var options = Object.extend(Sortable.options(element), arguments[1] || {}); + var name = encodeURIComponent( + (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); + + if (options.tree) { + return Sortable.tree(element, arguments[1]).children.map( function (item) { + return [name + Sortable._constructIndex(item) + "=" + + encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); + }).flatten().join('&'); + } else { + return Sortable.sequence(element, arguments[1]).map( function(item) { + return name + "[]=" + encodeURIComponent(item); + }).join('&'); + } + } +} + +/* Returns true if child is contained within element */ +Element.isParent = function(child, element) { + if (!child.parentNode || child == element) return false; + + if (child.parentNode == element) return true; + + return Element.isParent(child.parentNode, element); +} + +Element.findChildren = function(element, only, recursive, tagName) { + if(!element.hasChildNodes()) return null; + tagName = tagName.toUpperCase(); + if(only) only = [only].flatten(); + var elements = []; + $A(element.childNodes).each( function(e) { + if(e.tagName && e.tagName.toUpperCase()==tagName && + (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) + elements.push(e); + if(recursive) { + var grandchildren = Element.findChildren(e, only, recursive, tagName); + if(grandchildren) elements.push(grandchildren); + } + }); + + return (elements.length>0 ? elements.flatten() : []); +} + +Element.offsetSize = function (element, type) { + if (type == 'vertical' || type == 'height') + return element.offsetHeight; + else + return element.offsetWidth; +} \ No newline at end of file diff --git a/tracks/vendor/selenium/lib/scriptaculous/effects.js b/tracks/vendor/selenium/lib/scriptaculous/effects.js new file mode 100644 index 00000000..0864323e --- /dev/null +++ b/tracks/vendor/selenium/lib/scriptaculous/effects.js @@ -0,0 +1,958 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Contributors: +// Justin Palmer (http://encytemedia.com/) +// Mark Pilgrim (http://diveintomark.org/) +// Martin Bialasinki +// +// See scriptaculous.js for full license. + +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { + var color = '#'; + if(this.slice(0,4) == 'rgb(') { + var cols = this.slice(4,this.length-1).split(','); + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); + } else { + if(this.slice(0,1) == '#') { + if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if(this.length==7) color = this.toLowerCase(); + } + } + return(color.length==7 ? color : (arguments[0] || this)); +} + +/*--------------------------------------------------------------------------*/ + +Element.collectTextNodes = function(element) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); + }).flatten().join(''); +} + +Element.collectTextNodesIgnoreClass = function(element, className) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? + Element.collectTextNodesIgnoreClass(node, className) : '')); + }).flatten().join(''); +} + +Element.setContentZoom = function(element, percent) { + element = $(element); + Element.setStyle(element, {fontSize: (percent/100) + 'em'}); + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); +} + +Element.getOpacity = function(element){ + var opacity; + if (opacity = Element.getStyle(element, 'opacity')) + return parseFloat(opacity); + if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/)) + if(opacity[1]) return parseFloat(opacity[1]) / 100; + return 1.0; +} + +Element.setOpacity = function(element, value){ + element= $(element); + if (value == 1){ + Element.setStyle(element, { opacity: + (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? + 0.999999 : null }); + if(/MSIE/.test(navigator.userAgent)) + Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')}); + } else { + if(value < 0.00001) value = 0; + Element.setStyle(element, {opacity: value}); + if(/MSIE/.test(navigator.userAgent)) + Element.setStyle(element, + { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') + + 'alpha(opacity='+value*100+')' }); + } +} + +Element.getInlineOpacity = function(element){ + return $(element).style.opacity || ''; +} + +Element.childrenWithClassName = function(element, className, findFirst) { + var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)"); + var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { + return (c.className && c.className.match(classNameRegExp)); + }); + if(!results) results = []; + return results; +} + +Element.forceRerendering = function(element) { + try { + element = $(element); + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch(e) { } +}; + +/*--------------------------------------------------------------------------*/ + +Array.prototype.call = function() { + var args = arguments; + this.each(function(f){ f.apply(this, args) }); +} + +/*--------------------------------------------------------------------------*/ + +var Effect = { + tagifyText: function(element) { + var tagifyStyle = 'position:relative'; + if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1'; + element = $(element); + $A(element.childNodes).each( function(child) { + if(child.nodeType==3) { + child.nodeValue.toArray().each( function(character) { + element.insertBefore( + Builder.node('span',{style: tagifyStyle}, + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if(((typeof element == 'object') || + (typeof element == 'function')) && + (element.length)) + elements = element; + else + elements = $(element).childNodes; + + var options = Object.extend({ + speed: 0.1, + delay: 0.0 + }, arguments[2] || {}); + var masterDelay = options.delay; + + $A(elements).each( function(element, index) { + new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); + }); + }, + PAIRS: { + 'slide': ['SlideDown','SlideUp'], + 'blind': ['BlindDown','BlindUp'], + 'appear': ['Appear','Fade'] + }, + toggle: function(element, effect) { + element = $(element); + effect = (effect || 'appear').toLowerCase(); + var options = Object.extend({ + queue: { position:'end', scope:(element.id || 'global'), limit: 1 } + }, arguments[2] || {}); + Effect[element.visible() ? + Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); + } +}; + +var Effect2 = Effect; // deprecated + +/* ------------- transitions ------------- */ + +Effect.Transitions = {} + +Effect.Transitions.linear = function(pos) { + return pos; +} +Effect.Transitions.sinoidal = function(pos) { + return (-Math.cos(pos*Math.PI)/2) + 0.5; +} +Effect.Transitions.reverse = function(pos) { + return 1-pos; +} +Effect.Transitions.flicker = function(pos) { + return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; +} +Effect.Transitions.wobble = function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; +} +Effect.Transitions.pulse = function(pos) { + return (Math.floor(pos*10) % 2 == 0 ? + (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10))); +} +Effect.Transitions.none = function(pos) { + return 0; +} +Effect.Transitions.full = function(pos) { + return 1; +} + +/* ------------- core effects ------------- */ + +Effect.ScopedQueue = Class.create(); +Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { + initialize: function() { + this.effects = []; + this.interval = null; + }, + _each: function(iterator) { + this.effects._each(iterator); + }, + add: function(effect) { + var timestamp = new Date().getTime(); + + var position = (typeof effect.options.queue == 'string') ? + effect.options.queue : effect.options.queue.position; + + switch(position) { + case 'front': + // move unstarted effects after this effect + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { + e.startOn += effect.finishOn; + e.finishOn += effect.finishOn; + }); + break; + case 'end': + // start effect after last queued effect has finished + timestamp = this.effects.pluck('finishOn').max() || timestamp; + break; + } + + effect.startOn += timestamp; + effect.finishOn += timestamp; + + if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) + this.effects.push(effect); + + if(!this.interval) + this.interval = setInterval(this.loop.bind(this), 40); + }, + remove: function(effect) { + this.effects = this.effects.reject(function(e) { return e==effect }); + if(this.effects.length == 0) { + clearInterval(this.interval); + this.interval = null; + } + }, + loop: function() { + var timePos = new Date().getTime(); + this.effects.invoke('loop', timePos); + } +}); + +Effect.Queues = { + instances: $H(), + get: function(queueName) { + if(typeof queueName != 'string') return queueName; + + if(!this.instances[queueName]) + this.instances[queueName] = new Effect.ScopedQueue(); + + return this.instances[queueName]; + } +} +Effect.Queue = Effect.Queues.get('global'); + +Effect.DefaultOptions = { + transition: Effect.Transitions.sinoidal, + duration: 1.0, // seconds + fps: 25.0, // max. 25fps due to Effect.Queue implementation + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' +} + +Effect.Base = function() {}; +Effect.Base.prototype = { + position: null, + start: function(options) { + this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {}); + this.currentFrame = 0; + this.state = 'idle'; + this.startOn = this.options.delay*1000; + this.finishOn = this.startOn + (this.options.duration*1000); + this.event('beforeStart'); + if(!this.options.sync) + Effect.Queues.get(typeof this.options.queue == 'string' ? + 'global' : this.options.queue.scope).add(this); + }, + loop: function(timePos) { + if(timePos >= this.startOn) { + if(timePos >= this.finishOn) { + this.render(1.0); + this.cancel(); + this.event('beforeFinish'); + if(this.finish) this.finish(); + this.event('afterFinish'); + return; + } + var pos = (timePos - this.startOn) / (this.finishOn - this.startOn); + var frame = Math.round(pos * this.options.fps * this.options.duration); + if(frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + render: function(pos) { + if(this.state == 'idle') { + this.state = 'running'; + this.event('beforeSetup'); + if(this.setup) this.setup(); + this.event('afterSetup'); + } + if(this.state == 'running') { + if(this.options.transition) pos = this.options.transition(pos); + pos *= (this.options.to-this.options.from); + pos += this.options.from; + this.position = pos; + this.event('beforeUpdate'); + if(this.update) this.update(pos); + this.event('afterUpdate'); + } + }, + cancel: function() { + if(!this.options.sync) + Effect.Queues.get(typeof this.options.queue == 'string' ? + 'global' : this.options.queue.scope).remove(this); + this.state = 'finished'; + }, + event: function(eventName) { + if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if(this.options[eventName]) this.options[eventName](this); + }, + inspect: function() { + return '#'; + } +} + +Effect.Parallel = Class.create(); +Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + this.effects.invoke('render', position); + }, + finish: function(position) { + this.effects.each( function(effect) { + effect.render(1.0); + effect.cancel(); + effect.event('beforeFinish'); + if(effect.finish) effect.finish(position); + effect.event('afterFinish'); + }); + } +}); + +Effect.Opacity = Class.create(); +Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + // make this work on IE on elements without 'layout' + if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout)) + this.element.setStyle({zoom: 1}); + var options = Object.extend({ + from: this.element.getOpacity() || 0.0, + to: 1.0 + }, arguments[1] || {}); + this.start(options); + }, + update: function(position) { + this.element.setOpacity(position); + } +}); + +Effect.Move = Class.create(); +Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + var options = Object.extend({ + x: 0, + y: 0, + mode: 'relative' + }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Bug in Opera: Opera returns the "real" position of a static element or + // relative element that does not have top/left explicitly set. + // ==> Always set top and left for position relative elements in your stylesheets + // (to 0 if you do not need them) + this.element.makePositioned(); + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); + if(this.options.mode == 'absolute') { + // absolute movement, so we need to calc deltaX and deltaY + this.options.x = this.options.x - this.originalLeft; + this.options.y = this.options.y - this.originalTop; + } + }, + update: function(position) { + this.element.setStyle({ + left: this.options.x * position + this.originalLeft + 'px', + top: this.options.y * position + this.originalTop + 'px' + }); + } +}); + +// for backwards compatibility +Effect.MoveBy = function(element, toTop, toLeft) { + return new Effect.Move(element, + Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); +}; + +Effect.Scale = Class.create(); +Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { + initialize: function(element, percent) { + this.element = $(element) + var options = Object.extend({ + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or {} with provided values + scaleFrom: 100.0, + scaleTo: percent + }, arguments[2] || {}); + this.start(options); + }, + setup: function() { + this.restoreAfterFinish = this.options.restoreAfterFinish || false; + this.elementPositioning = this.element.getStyle('position'); + + this.originalStyle = {}; + ['top','left','width','height','fontSize'].each( function(k) { + this.originalStyle[k] = this.element.style[k]; + }.bind(this)); + + this.originalTop = this.element.offsetTop; + this.originalLeft = this.element.offsetLeft; + + var fontSize = this.element.getStyle('font-size') || '100%'; + ['em','px','%'].each( function(fontSizeType) { + if(fontSize.indexOf(fontSizeType)>0) { + this.fontSize = parseFloat(fontSize); + this.fontSizeType = fontSizeType; + } + }.bind(this)); + + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; + + this.dims = null; + if(this.options.scaleMode=='box') + this.dims = [this.element.offsetHeight, this.element.offsetWidth]; + if(/^content/.test(this.options.scaleMode)) + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; + if(!this.dims) + this.dims = [this.options.scaleMode.originalHeight, + this.options.scaleMode.originalWidth]; + }, + update: function(position) { + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if(this.options.scaleContent && this.fontSize) + this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); + }, + finish: function(position) { + if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle); + }, + setDimensions: function(height, width) { + var d = {}; + if(this.options.scaleX) d.width = width + 'px'; + if(this.options.scaleY) d.height = height + 'px'; + if(this.options.scaleFromCenter) { + var topd = (height - this.dims[0])/2; + var leftd = (width - this.dims[1])/2; + if(this.elementPositioning == 'absolute') { + if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; + if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; + } else { + if(this.options.scaleY) d.top = -topd + 'px'; + if(this.options.scaleX) d.left = -leftd + 'px'; + } + } + this.element.setStyle(d); + } +}); + +Effect.Highlight = Class.create(); +Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Prevent executing on elements not in the layout flow + if(this.element.getStyle('display')=='none') { this.cancel(); return; } + // Disable background image during the effect + this.oldStyle = { + backgroundImage: this.element.getStyle('background-image') }; + this.element.setStyle({backgroundImage: 'none'}); + if(!this.options.endcolor) + this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); + if(!this.options.restorecolor) + this.options.restorecolor = this.element.getStyle('background-color'); + // init color calculations + this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); + this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); + }, + update: function(position) { + this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ + return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); + }, + finish: function() { + this.element.setStyle(Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = Class.create(); +Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + this.start(arguments[1] || {}); + }, + setup: function() { + Position.prepare(); + var offsets = Position.cumulativeOffset(this.element); + if(this.options.offset) offsets[1] += this.options.offset; + var max = window.innerHeight ? + window.height - window.innerHeight : + document.body.scrollHeight - + (document.documentElement.clientHeight ? + document.documentElement.clientHeight : document.body.clientHeight); + this.scrollStart = Position.deltaY; + this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; + }, + update: function(position) { + Position.prepare(); + window.scrollTo(Position.deltaX, + this.scrollStart + (position*this.delta)); + } +}); + +/* ------------- combination effects ------------- */ + +Effect.Fade = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + var options = Object.extend({ + from: element.getOpacity() || 1.0, + to: 0.0, + afterFinishInternal: function(effect) { + if(effect.options.to!=0) return; + effect.element.hide(); + effect.element.setStyle({opacity: oldOpacity}); + }}, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Appear = function(element) { + element = $(element); + var options = Object.extend({ + from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), + to: 1.0, + // force Safari to render floated elements properly + afterFinishInternal: function(effect) { + effect.element.forceRerendering(); + }, + beforeSetup: function(effect) { + effect.element.setOpacity(effect.options.from); + effect.element.show(); + }}, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Puff = function(element) { + element = $(element); + var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') }; + return new Effect.Parallel( + [ new Effect.Scale(element, 200, + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], + Object.extend({ duration: 1.0, + beforeSetupInternal: function(effect) { + effect.effects[0].element.setStyle({position: 'absolute'}); }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide(); + effect.effects[0].element.setStyle(oldStyle); } + }, arguments[1] || {}) + ); +} + +Effect.BlindUp = function(element) { + element = $(element); + element.makeClipping(); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + restoreAfterFinish: true, + afterFinishInternal: function(effect) { + effect.element.hide(); + effect.element.undoClipping(); + } + }, arguments[1] || {}) + ); +} + +Effect.BlindDown = function(element) { + element = $(element); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, + Object.extend({ scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makeClipping(); + effect.element.setStyle({height: '0px'}); + effect.element.show(); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + } + }, arguments[1] || {}) + ); +} + +Effect.SwitchOff = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + return new Effect.Appear(element, { + duration: 0.4, + from: 0, + transition: Effect.Transitions.flicker, + afterFinishInternal: function(effect) { + new Effect.Scale(effect.element, 1, { + duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makePositioned(); + effect.element.makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide(); + effect.element.undoClipping(); + effect.element.undoPositioned(); + effect.element.setStyle({opacity: oldOpacity}); + } + }) + } + }); +} + +Effect.DropOut = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left'), + opacity: element.getInlineOpacity() }; + return new Effect.Parallel( + [ new Effect.Move(element, {x: 0, y: 100, sync: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 }) ], + Object.extend( + { duration: 0.5, + beforeSetup: function(effect) { + effect.effects[0].element.makePositioned(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide(); + effect.effects[0].element.undoPositioned(); + effect.effects[0].element.setStyle(oldStyle); + } + }, arguments[1] || {})); +} + +Effect.Shake = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left') }; + return new Effect.Move(element, + { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { + effect.element.undoPositioned(); + effect.element.setStyle(oldStyle); + }}) }}) }}) }}) }}) }}); +} + +Effect.SlideDown = function(element) { + element = $(element); + element.cleanWhitespace(); + // SlideDown need to have the content of the element wrapped in a container element with fixed height! + var oldInnerBottom = $(element.firstChild).getStyle('bottom'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: window.opera ? 0 : 1, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makePositioned(); + effect.element.firstChild.makePositioned(); + if(window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping(); + effect.element.setStyle({height: '0px'}); + effect.element.show(); }, + afterUpdateInternal: function(effect) { + effect.element.firstChild.setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + // IE will crash if child is undoPositioned first + if(/MSIE/.test(navigator.userAgent)){ + effect.element.undoPositioned(); + effect.element.firstChild.undoPositioned(); + }else{ + effect.element.firstChild.undoPositioned(); + effect.element.undoPositioned(); + } + effect.element.firstChild.setStyle({bottom: oldInnerBottom}); } + }, arguments[1] || {}) + ); +} + +Effect.SlideUp = function(element) { + element = $(element); + element.cleanWhitespace(); + var oldInnerBottom = $(element.firstChild).getStyle('bottom'); + return new Effect.Scale(element, window.opera ? 0 : 1, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + restoreAfterFinish: true, + beforeStartInternal: function(effect) { + effect.element.makePositioned(); + effect.element.firstChild.makePositioned(); + if(window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping(); + effect.element.show(); }, + afterUpdateInternal: function(effect) { + effect.element.firstChild.setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); }, + afterFinishInternal: function(effect) { + effect.element.hide(); + effect.element.undoClipping(); + effect.element.firstChild.undoPositioned(); + effect.element.undoPositioned(); + effect.element.setStyle({bottom: oldInnerBottom}); } + }, arguments[1] || {}) + ); +} + +// Bug in opera makes the TD containing this element expand for a instance after finish +Effect.Squish = function(element) { + return new Effect.Scale(element, window.opera ? 1 : 0, + { restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makeClipping(effect.element); }, + afterFinishInternal: function(effect) { + effect.element.hide(effect.element); + effect.element.undoClipping(effect.element); } + }); +} + +Effect.Grow = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.full + }, arguments[1] || {}); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = dims.width; + initialMoveY = moveY = 0; + moveX = -dims.width; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = dims.height; + moveY = -dims.height; + break; + case 'bottom-right': + initialMoveX = dims.width; + initialMoveY = dims.height; + moveX = -dims.width; + moveY = -dims.height; + break; + case 'center': + initialMoveX = dims.width / 2; + initialMoveY = dims.height / 2; + moveX = -dims.width / 2; + moveY = -dims.height / 2; + break; + } + + return new Effect.Move(element, { + x: initialMoveX, + y: initialMoveY, + duration: 0.01, + beforeSetup: function(effect) { + effect.element.hide(); + effect.element.makeClipping(); + effect.element.makePositioned(); + }, + afterFinishInternal: function(effect) { + new Effect.Parallel( + [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), + new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), + new Effect.Scale(effect.element, 100, { + scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, + sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) + ], Object.extend({ + beforeSetup: function(effect) { + effect.effects[0].element.setStyle({height: '0px'}); + effect.effects[0].element.show(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.undoClipping(); + effect.effects[0].element.undoPositioned(); + effect.effects[0].element.setStyle(oldStyle); + } + }, options) + ) + } + }); +} + +Effect.Shrink = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.none + }, arguments[1] || {}); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = dims.width; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = dims.height; + break; + case 'bottom-right': + moveX = dims.width; + moveY = dims.height; + break; + case 'center': + moveX = dims.width / 2; + moveY = dims.height / 2; + break; + } + + return new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), + new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), + new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) + ], Object.extend({ + beforeStartInternal: function(effect) { + effect.effects[0].element.makePositioned(); + effect.effects[0].element.makeClipping(); }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide(); + effect.effects[0].element.undoClipping(); + effect.effects[0].element.undoPositioned(); + effect.effects[0].element.setStyle(oldStyle); } + }, options) + ); +} + +Effect.Pulsate = function(element) { + element = $(element); + var options = arguments[1] || {}; + var oldOpacity = element.getInlineOpacity(); + var transition = options.transition || Effect.Transitions.sinoidal; + var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) }; + reverser.bind(transition); + return new Effect.Opacity(element, + Object.extend(Object.extend({ duration: 3.0, from: 0, + afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } + }, options), {transition: reverser})); +} + +Effect.Fold = function(element) { + element = $(element); + var oldStyle = { + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height }; + Element.makeClipping(element); + return new Effect.Scale(element, 5, Object.extend({ + scaleContent: false, + scaleX: false, + afterFinishInternal: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleY: false, + afterFinishInternal: function(effect) { + effect.element.hide(); + effect.element.undoClipping(); + effect.element.setStyle(oldStyle); + } }); + }}, arguments[1] || {})); +}; + +['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom', + 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( + function(f) { Element.Methods[f] = Element[f]; } +); + +Element.Methods.visualEffect = function(element, effect, options) { + s = effect.gsub(/_/, '-').camelize(); + effect_class = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[effect_class](element, options); + return $(element); +}; + +Element.addMethods(); \ No newline at end of file diff --git a/tracks/vendor/selenium/lib/scriptaculous/scriptaculous.js b/tracks/vendor/selenium/lib/scriptaculous/scriptaculous.js new file mode 100644 index 00000000..f61fc57f --- /dev/null +++ b/tracks/vendor/selenium/lib/scriptaculous/scriptaculous.js @@ -0,0 +1,47 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +var Scriptaculous = { + Version: '1.6.1', + require: function(libraryName) { + // inserting via DOM fails in Safari 2.0, so brute force approach + document.write(''); + }, + load: function() { + if((typeof Prototype=='undefined') || + (typeof Element == 'undefined') || + (typeof Element.Methods=='undefined') || + parseFloat(Prototype.Version.split(".")[0] + "." + + Prototype.Version.split(".")[1]) < 1.5) + throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0"); + + $A(document.getElementsByTagName("script")).findAll( function(s) { + return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/)) + }).each( function(s) { + var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,''); + var includes = s.src.match(/\?.*load=([a-z,]*)/); + (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each( + function(include) { Scriptaculous.require(path+include+'.js') }); + }); + } +} + +Scriptaculous.load(); \ No newline at end of file diff --git a/tracks/vendor/selenium/lib/scriptaculous/slider.js b/tracks/vendor/selenium/lib/scriptaculous/slider.js new file mode 100644 index 00000000..c0f1fc01 --- /dev/null +++ b/tracks/vendor/selenium/lib/scriptaculous/slider.js @@ -0,0 +1,283 @@ +// Copyright (c) 2005 Marty Haught, Thomas Fuchs +// +// See http://script.aculo.us for more info +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if(!Control) var Control = {}; +Control.Slider = Class.create(); + +// options: +// axis: 'vertical', or 'horizontal' (default) +// +// callbacks: +// onChange(value) +// onSlide(value) +Control.Slider.prototype = { + initialize: function(handle, track, options) { + var slider = this; + + if(handle instanceof Array) { + this.handles = handle.collect( function(e) { return $(e) }); + } else { + this.handles = [$(handle)]; + } + + this.track = $(track); + this.options = options || {}; + + this.axis = this.options.axis || 'horizontal'; + this.increment = this.options.increment || 1; + this.step = parseInt(this.options.step || '1'); + this.range = this.options.range || $R(0,1); + + this.value = 0; // assure backwards compat + this.values = this.handles.map( function() { return 0 }); + this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false; + this.options.startSpan = $(this.options.startSpan || null); + this.options.endSpan = $(this.options.endSpan || null); + + this.restricted = this.options.restricted || false; + + this.maximum = this.options.maximum || this.range.end; + this.minimum = this.options.minimum || this.range.start; + + // Will be used to align the handle onto the track, if necessary + this.alignX = parseInt(this.options.alignX || '0'); + this.alignY = parseInt(this.options.alignY || '0'); + + this.trackLength = this.maximumOffset() - this.minimumOffset(); + this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth; + + this.active = false; + this.dragging = false; + this.disabled = false; + + if(this.options.disabled) this.setDisabled(); + + // Allowed values array + this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false; + if(this.allowedValues) { + this.minimum = this.allowedValues.min(); + this.maximum = this.allowedValues.max(); + } + + this.eventMouseDown = this.startDrag.bindAsEventListener(this); + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.update.bindAsEventListener(this); + + // Initialize handles in reverse (make sure first handle is active) + this.handles.each( function(h,i) { + i = slider.handles.length-1-i; + slider.setValue(parseFloat( + (slider.options.sliderValue instanceof Array ? + slider.options.sliderValue[i] : slider.options.sliderValue) || + slider.range.start), i); + Element.makePositioned(h); // fix IE + Event.observe(h, "mousedown", slider.eventMouseDown); + }); + + Event.observe(this.track, "mousedown", this.eventMouseDown); + Event.observe(document, "mouseup", this.eventMouseUp); + Event.observe(document, "mousemove", this.eventMouseMove); + + this.initialized = true; + }, + dispose: function() { + var slider = this; + Event.stopObserving(this.track, "mousedown", this.eventMouseDown); + Event.stopObserving(document, "mouseup", this.eventMouseUp); + Event.stopObserving(document, "mousemove", this.eventMouseMove); + this.handles.each( function(h) { + Event.stopObserving(h, "mousedown", slider.eventMouseDown); + }); + }, + setDisabled: function(){ + this.disabled = true; + }, + setEnabled: function(){ + this.disabled = false; + }, + getNearestValue: function(value){ + if(this.allowedValues){ + if(value >= this.allowedValues.max()) return(this.allowedValues.max()); + if(value <= this.allowedValues.min()) return(this.allowedValues.min()); + + var offset = Math.abs(this.allowedValues[0] - value); + var newValue = this.allowedValues[0]; + this.allowedValues.each( function(v) { + var currentOffset = Math.abs(v - value); + if(currentOffset <= offset){ + newValue = v; + offset = currentOffset; + } + }); + return newValue; + } + if(value > this.range.end) return this.range.end; + if(value < this.range.start) return this.range.start; + return value; + }, + setValue: function(sliderValue, handleIdx){ + if(!this.active) { + this.activeHandle = this.handles[handleIdx]; + this.activeHandleIdx = handleIdx; + this.updateStyles(); + } + handleIdx = handleIdx || this.activeHandleIdx || 0; + if(this.initialized && this.restricted) { + if((handleIdx>0) && (sliderValuethis.values[handleIdx+1])) + sliderValue = this.values[handleIdx+1]; + } + sliderValue = this.getNearestValue(sliderValue); + this.values[handleIdx] = sliderValue; + this.value = this.values[0]; // assure backwards compat + + this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = + this.translateToPx(sliderValue); + + this.drawSpans(); + if(!this.dragging || !this.event) this.updateFinished(); + }, + setValueBy: function(delta, handleIdx) { + this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, + handleIdx || this.activeHandleIdx || 0); + }, + translateToPx: function(value) { + return Math.round( + ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * + (value - this.range.start)) + "px"; + }, + translateToValue: function(offset) { + return ((offset/(this.trackLength-this.handleLength) * + (this.range.end-this.range.start)) + this.range.start); + }, + getRange: function(range) { + var v = this.values.sortBy(Prototype.K); + range = range || 0; + return $R(v[range],v[range+1]); + }, + minimumOffset: function(){ + return(this.isVertical() ? this.alignY : this.alignX); + }, + maximumOffset: function(){ + return(this.isVertical() ? + this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX); + }, + isVertical: function(){ + return (this.axis == 'vertical'); + }, + drawSpans: function() { + var slider = this; + if(this.spans) + $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) }); + if(this.options.startSpan) + this.setSpan(this.options.startSpan, + $R(0, this.values.length>1 ? this.getRange(0).min() : this.value )); + if(this.options.endSpan) + this.setSpan(this.options.endSpan, + $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum)); + }, + setSpan: function(span, range) { + if(this.isVertical()) { + span.style.top = this.translateToPx(range.start); + span.style.height = this.translateToPx(range.end - range.start + this.range.start); + } else { + span.style.left = this.translateToPx(range.start); + span.style.width = this.translateToPx(range.end - range.start + this.range.start); + } + }, + updateStyles: function() { + this.handles.each( function(h){ Element.removeClassName(h, 'selected') }); + Element.addClassName(this.activeHandle, 'selected'); + }, + startDrag: function(event) { + if(Event.isLeftClick(event)) { + if(!this.disabled){ + this.active = true; + + var handle = Event.element(event); + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + if(handle==this.track) { + var offsets = Position.cumulativeOffset(this.track); + this.event = event; + this.setValue(this.translateToValue( + (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2) + )); + var offsets = Position.cumulativeOffset(this.activeHandle); + this.offsetX = (pointer[0] - offsets[0]); + this.offsetY = (pointer[1] - offsets[1]); + } else { + // find the handle (prevents issues with Safari) + while((this.handles.indexOf(handle) == -1) && handle.parentNode) + handle = handle.parentNode; + + this.activeHandle = handle; + this.activeHandleIdx = this.handles.indexOf(this.activeHandle); + this.updateStyles(); + + var offsets = Position.cumulativeOffset(this.activeHandle); + this.offsetX = (pointer[0] - offsets[0]); + this.offsetY = (pointer[1] - offsets[1]); + } + } + Event.stop(event); + } + }, + update: function(event) { + if(this.active) { + if(!this.dragging) this.dragging = true; + this.draw(event); + // fix AppleWebKit rendering + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); + Event.stop(event); + } + }, + draw: function(event) { + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var offsets = Position.cumulativeOffset(this.track); + pointer[0] -= this.offsetX + offsets[0]; + pointer[1] -= this.offsetY + offsets[1]; + this.event = event; + this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] )); + if(this.initialized && this.options.onSlide) + this.options.onSlide(this.values.length>1 ? this.values : this.value, this); + }, + endDrag: function(event) { + if(this.active && this.dragging) { + this.finishDrag(event, true); + Event.stop(event); + } + this.active = false; + this.dragging = false; + }, + finishDrag: function(event, success) { + this.active = false; + this.dragging = false; + this.updateFinished(); + }, + updateFinished: function() { + if(this.initialized && this.options.onChange) + this.options.onChange(this.values.length>1 ? this.values : this.value, this); + this.event = null; + } +} \ No newline at end of file diff --git a/tracks/vendor/selenium/lib/scriptaculous/unittest.js b/tracks/vendor/selenium/lib/scriptaculous/unittest.js new file mode 100644 index 00000000..d2c2d817 --- /dev/null +++ b/tracks/vendor/selenium/lib/scriptaculous/unittest.js @@ -0,0 +1,383 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005 Jon Tirsen (http://www.tirsen.com) +// (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +// experimental, Firefox-only +Event.simulateMouse = function(element, eventName) { + var options = Object.extend({ + pointerX: 0, + pointerY: 0, + buttons: 0 + }, arguments[2] || {}); + var oEvent = document.createEvent("MouseEvents"); + oEvent.initMouseEvent(eventName, true, true, document.defaultView, + options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, + false, false, false, false, 0, $(element)); + + if(this.mark) Element.remove(this.mark); + this.mark = document.createElement('div'); + this.mark.appendChild(document.createTextNode(" ")); + document.body.appendChild(this.mark); + this.mark.style.position = 'absolute'; + this.mark.style.top = options.pointerY + "px"; + this.mark.style.left = options.pointerX + "px"; + this.mark.style.width = "5px"; + this.mark.style.height = "5px;"; + this.mark.style.borderTop = "1px solid red;" + this.mark.style.borderLeft = "1px solid red;" + + if(this.step) + alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options)); + + $(element).dispatchEvent(oEvent); +}; + +// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2. +// You need to downgrade to 1.0.4 for now to get this working +// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much +Event.simulateKey = function(element, eventName) { + var options = Object.extend({ + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false, + keyCode: 0, + charCode: 0 + }, arguments[2] || {}); + + var oEvent = document.createEvent("KeyEvents"); + oEvent.initKeyEvent(eventName, true, true, window, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, + options.keyCode, options.charCode ); + $(element).dispatchEvent(oEvent); +}; + +Event.simulateKeys = function(element, command) { + for(var i=0; i' + + '' + + '' + + '' + + '
    StatusTestMessage
    '; + this.logsummary = $('logsummary') + this.loglines = $('loglines'); + }, + _toHTML: function(txt) { + return txt.escapeHTML().replace(/\n/g,"
    "); + } +} + +Test.Unit.Runner = Class.create(); +Test.Unit.Runner.prototype = { + initialize: function(testcases) { + this.options = Object.extend({ + testLog: 'testlog' + }, arguments[1] || {}); + this.options.resultsURL = this.parseResultsURLQueryParameter(); + if (this.options.testLog) { + this.options.testLog = $(this.options.testLog) || null; + } + if(this.options.tests) { + this.tests = []; + for(var i = 0; i < this.options.tests.length; i++) { + if(/^test/.test(this.options.tests[i])) { + this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"])); + } + } + } else { + if (this.options.test) { + this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])]; + } else { + this.tests = []; + for(var testcase in testcases) { + if(/^test/.test(testcase)) { + this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"])); + } + } + } + } + this.currentTest = 0; + this.logger = new Test.Unit.Logger(this.options.testLog); + setTimeout(this.runTests.bind(this), 1000); + }, + parseResultsURLQueryParameter: function() { + return window.location.search.parseQuery()["resultsURL"]; + }, + // Returns: + // "ERROR" if there was an error, + // "FAILURE" if there was a failure, or + // "SUCCESS" if there was neither + getResult: function() { + var hasFailure = false; + for(var i=0;i 0) { + return "ERROR"; + } + if (this.tests[i].failures > 0) { + hasFailure = true; + } + } + if (hasFailure) { + return "FAILURE"; + } else { + return "SUCCESS"; + } + }, + postResults: function() { + if (this.options.resultsURL) { + new Ajax.Request(this.options.resultsURL, + { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false }); + } + }, + runTests: function() { + var test = this.tests[this.currentTest]; + if (!test) { + // finished! + this.postResults(); + this.logger.summary(this.summary()); + return; + } + if(!test.isWaiting) { + this.logger.start(test.name); + } + test.run(); + if(test.isWaiting) { + this.logger.message("Waiting for " + test.timeToWait + "ms"); + setTimeout(this.runTests.bind(this), test.timeToWait || 1000); + } else { + this.logger.finish(test.status(), test.summary()); + this.currentTest++; + // tail recursive, hopefully the browser will skip the stackframe + this.runTests(); + } + }, + summary: function() { + var assertions = 0; + var failures = 0; + var errors = 0; + var messages = []; + for(var i=0;i 0) return 'failed'; + if (this.errors > 0) return 'error'; + return 'passed'; + }, + assert: function(expression) { + var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"'; + try { expression ? this.pass() : + this.fail(message); } + catch(e) { this.error(e); } + }, + assertEqual: function(expected, actual) { + var message = arguments[2] || "assertEqual"; + try { (expected == actual) ? this.pass() : + this.fail(message + ': expected "' + Test.Unit.inspect(expected) + + '", actual "' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, + assertEnumEqual: function(expected, actual) { + var message = arguments[2] || "assertEnumEqual"; + try { $A(expected).length == $A(actual).length && + expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ? + this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + + ', actual ' + Test.Unit.inspect(actual)); } + catch(e) { this.error(e); } + }, + assertNotEqual: function(expected, actual) { + var message = arguments[2] || "assertNotEqual"; + try { (expected != actual) ? this.pass() : + this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, + assertNull: function(obj) { + var message = arguments[1] || 'assertNull' + try { (obj==null) ? this.pass() : + this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); } + catch(e) { this.error(e); } + }, + assertHidden: function(element) { + var message = arguments[1] || 'assertHidden'; + this.assertEqual("none", element.style.display, message); + }, + assertNotNull: function(object) { + var message = arguments[1] || 'assertNotNull'; + this.assert(object != null, message); + }, + assertInstanceOf: function(expected, actual) { + var message = arguments[2] || 'assertInstanceOf'; + try { + (actual instanceof expected) ? this.pass() : + this.fail(message + ": object was not an instance of the expected type"); } + catch(e) { this.error(e); } + }, + assertNotInstanceOf: function(expected, actual) { + var message = arguments[2] || 'assertNotInstanceOf'; + try { + !(actual instanceof expected) ? this.pass() : + this.fail(message + ": object was an instance of the not expected type"); } + catch(e) { this.error(e); } + }, + _isVisible: function(element) { + element = $(element); + if(!element.parentNode) return true; + this.assertNotNull(element); + if(element.style && Element.getStyle(element, 'display') == 'none') + return false; + + return this._isVisible(element.parentNode); + }, + assertNotVisible: function(element) { + this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1])); + }, + assertVisible: function(element) { + this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1])); + }, + benchmark: function(operation, iterations) { + var startAt = new Date(); + (iterations || 1).times(operation); + var timeTaken = ((new Date())-startAt); + this.info((arguments[2] || 'Operation') + ' finished ' + + iterations + ' iterations in ' + (timeTaken/1000)+'s' ); + return timeTaken; + } +} + +Test.Unit.Testcase = Class.create(); +Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), { + initialize: function(name, test, setup, teardown) { + Test.Unit.Assertions.prototype.initialize.bind(this)(); + this.name = name; + this.test = test || function() {}; + this.setup = setup || function() {}; + this.teardown = teardown || function() {}; + this.isWaiting = false; + this.timeToWait = 1000; + }, + wait: function(time, nextPart) { + this.isWaiting = true; + this.test = nextPart; + this.timeToWait = time; + }, + run: function() { + try { + try { + if (!this.isWaiting) this.setup.bind(this)(); + this.isWaiting = false; + this.test.bind(this)(); + } finally { + if(!this.isWaiting) { + this.teardown.bind(this)(); + } + } + } + catch(e) { this.error(e); } + } +}); diff --git a/tracks/vendor/selenium/scripts/find_matching_child.js b/tracks/vendor/selenium/scripts/find_matching_child.js new file mode 100644 index 00000000..e18a089b --- /dev/null +++ b/tracks/vendor/selenium/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. + * + */ + +elementFindMatchingChildren = function(element, selector) { + var matches = []; + + var childCount = element.childNodes.length; + for (var i=0; i= "1.5"); + if (isRecentFirefox || browserVersion.isKonqueror || browserVersion.isSafari || browserVersion.isOpera) { + text = getTextContent(element); + } else if (element.textContent) { + text = element.textContent; + } else if (element.innerText) { + text = element.innerText; + } + + text = normalizeNewlines(text); + text = normalizeSpaces(text); + + return text.trim(); +} + +function getTextContent(element, preformatted) { + if (element.nodeType == 3 /*Node.TEXT_NODE*/) { + var text = element.data; + if (!preformatted) { + text = text.replace(/\n|\r|\t/g, " "); + } + return text; + } + if (element.nodeType == 1 /*Node.ELEMENT_NODE*/) { + var childrenPreformatted = preformatted || (element.tagName == "PRE"); + var text = ""; + for (var i = 0; i < element.childNodes.length; i++) { + var child = element.childNodes.item(i); + text += getTextContent(child, childrenPreformatted); + } + // Handle block elements that introduce newlines + // -- From HTML spec: + // + // + // TODO: should potentially introduce multiple newlines to separate blocks + if (element.tagName == "P" || element.tagName == "BR" || element.tagName == "HR" || element.tagName == "DIV") { + text += "\n"; + } + return text; + } + return ''; +} + +/** + * Convert all newlines to \m + */ +function normalizeNewlines(text) +{ + return text.replace(/\r\n|\r/g, "\n"); +} + +/** + * Replace multiple sequential spaces with a single space, and then convert   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 nbspPattern = new RegExp(String.fromCharCode(160), "g"); + if (browserVersion.isSafari) { + return replaceAll(text, String.fromCharCode(160), " "); + } else { + return text.replace(nbspPattern, " "); + } +} + +function replaceAll(text, oldText, newText) { + while (text.indexOf(oldText) != -1) { + text = text.replace(oldText, newText); + } + return text; +} + + +function xmlDecode(text) { + text = text.replace(/"/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 != null) { + element.textContent = text; + } else if (element.innerText != null) { + element.innerText = text; + } +} + +// Get the value of an element +function getInputValue(inputElement) { + if (inputElement.type) { + if (inputElement.type.toUpperCase() == 'CHECKBOX' || + inputElement.type.toUpperCase() == 'RADIO') + { + return (inputElement.checked ? 'on' : 'off'); + } + } + if (inputElement.value == null) { + throw new SeleniumError("This element has no value; is it really a form field?"); + } + return inputElement.value; +} + +/* Fire an event in a browser-compatible manner */ +function triggerEvent(element, eventType, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) { + canBubble = (typeof(canBubble) == undefined) ? true : canBubble; + if (element.fireEvent) { + var evt = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown); + element.fireEvent('on' + eventType, evt); + } + else { + var evt = document.createEvent('HTMLEvents'); + + try { + evt.shiftKey = shiftKeyDown; + evt.metaKey = metaKeyDown; + evt.altKey = altKeyDown; + evt.ctrlKey = controlKeyDown; + } catch (e) { + // On Firefox 1.0, you can only set these during initMouseEvent or initKeyEvent + // we'll have to ignore them here + LOG.exception(e); + } + + evt.initEvent(eventType, canBubble, true); + element.dispatchEvent(evt); + } +} + +function getKeyCodeFromKeySequence(keySequence) { + var match = /^\\(\d{1,3})$/.exec(keySequence); + if (match != null) { + return match[1]; + } + match = /^.$/.exec(keySequence); + if (match != null) { + return match[0].charCodeAt(0); + } + // this is for backward compatibility with existing tests + // 1 digit ascii codes will break however because they are used for the digit chars + match = /^\d{2,3}$/.exec(keySequence); + if (match != null) { + return match[0]; + } + throw new SeleniumError("invalid keySequence"); +} + +function createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) { + var evt = element.ownerDocument.createEventObject(); + evt.shiftKey = shiftKeyDown; + evt.metaKey = metaKeyDown; + evt.altKey = altKeyDown; + evt.ctrlKey = controlKeyDown; + return evt; +} + +function triggerKeyEvent(element, eventType, keySequence, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) { + var keycode = getKeyCodeFromKeySequence(keySequence); + canBubble = (typeof(canBubble) == undefined) ? true : canBubble; + if (element.fireEvent) { + var keyEvent = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown); + keyEvent.keyCode = keycode; + element.fireEvent('on' + eventType, keyEvent); + } + else { + var evt; + if (window.KeyEvent) { + evt = document.createEvent('KeyEvents'); + evt.initKeyEvent(eventType, true, true, window, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown, keycode, keycode); + } else { + evt = document.createEvent('UIEvents'); + + evt.shiftKey = shiftKeyDown; + evt.metaKey = metaKeyDown; + evt.altKey = altKeyDown; + evt.ctrlKey = controlKeyDown; + + evt.initUIEvent(eventType, true, true, window, 1); + evt.keyCode = keycode; + evt.which = keycode; + } + + element.dispatchEvent(evt); + } +} + +function removeLoadListener(element, command) { + LOG.info('Removing loadListenter for ' + element + ', ' + command); + if (window.removeEventListener) + element.removeEventListener("load", command, true); + else if (window.detachEvent) + element.detachEvent("onload", command); +} + +function addLoadListener(element, command) { + LOG.info('Adding loadListenter for ' + element + ', ' + command); + var augmentedCommand = function() { + command.call(this, element); + } + if (window.addEventListener && !browserVersion.isOpera) + element.addEventListener("load", augmentedCommand, true); + else if (window.attachEvent) + element.attachEvent("onload", augmentedCommand); +} + +/** + * Override the broken getFunctionName() method from JsUnit + * This file must be loaded _after_ the jsunitCore.js + */ +function getFunctionName(aFunction) { + var regexpResult = aFunction.toString().match(/function (\w*)/); + if (regexpResult && regexpResult[1]) { + return regexpResult[1]; + } + return 'anonymous'; +} + +function getDocumentBase(doc) { + var bases = document.getElementsByTagName("base"); + if (bases && bases.length && bases[0].href) { + return bases[0].href; + } + return ""; +} + +function getTagName(element) { + var tagName; + if (element && element.tagName && element.tagName.toLowerCase) { + tagName = element.tagName.toLowerCase(); + } + return tagName; +} + +function absolutify(url, baseUrl) { + /** returns a relative url in its absolute form, given by baseUrl. + * + * This function is a little odd, because it can take baseUrls that + * aren't necessarily directories. It uses the same rules as the HTML + * <base> tag; if the baseUrl doesn't end with "/", we'll assume + * that it points to a file, and strip the filename off to find its + * base directory. + * + * So absolutify("foo", "http://x/bar") will return "http://x/foo" (stripping off bar), + * whereas absolutify("foo", "http://x/bar/") will return "http://x/bar/foo" (preserving bar). + * Naturally absolutify("foo", "http://x") will return "http://x/foo", appropriately. + * + * @param url the url to make absolute; if this url is already absolute, we'll just return that, unchanged + * @param baseUrl the baseUrl from which we'll absolutify, following the rules above. + * @return 'url' if it was already absolute, or the absolutized version of url if it was not absolute. + */ + + // DGF isn't there some library we could use for this? + + if (/^\w+:/.test(url)) { + // it's already absolute + return url; + } + + var loc; + try { + loc = parseUrl(baseUrl); + } catch (e) { + // is it an absolute windows file path? let's play the hero in that case + if (/^\w:\\/.test(baseUrl)) { + baseUrl = "file:///" + baseUrl.replace(/\\/g, "/"); + loc = parseUrl(baseUrl); + } else { + throw new SeleniumError("baseUrl wasn't absolute: " + baseUrl); + } + } + loc.search = null; + loc.hash = null; + + // if url begins with /, then that's the whole pathname + if (/^\//.test(url)) { + loc.pathname = url; + var result = reassembleLocation(loc); + return result; + } + + // if pathname is null, then we'll just append "/" + the url + if (!loc.pathname) { + loc.pathname = "/" + url; + var result = reassembleLocation(loc); + return result; + } + + // if pathname ends with /, just append url + if (/\/$/.test(loc.pathname)) { + loc.pathname += url; + var result = reassembleLocation(loc); + return result; + } + + // if we're here, then the baseUrl has a pathname, but it doesn't end with / + // in that case, we replace everything after the final / with the relative url + loc.pathname = loc.pathname.replace(/[^\/\\]+$/, url); + var result = reassembleLocation(loc); + return result; + +} + +var URL_REGEX = /^((\w+):\/\/)(([^:]+):?([^@]+)?@)?([^\/\?:]*):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(.+)?/; + +function parseUrl(url) { + var fields = ['url', null, 'protocol', null, 'username', 'password', 'host', 'port', 'pathname', 'search', 'hash']; + var result = URL_REGEX.exec(url); + if (!result) { + throw new SeleniumError("Invalid URL: " + url); + } + var loc = new Object(); + for (var i = 0; i < fields.length; i++) { + var field = fields[i]; + if (field == null) { + continue; + } + loc[field] = result[i]; + } + return loc; +} + +function reassembleLocation(loc) { + if (!loc.protocol) { + throw new Error("Not a valid location object: " + o2s(loc)); + } + var protocol = loc.protocol; + protocol = protocol.replace(/:$/, ""); + var url = protocol + "://"; + if (loc.username) { + url += loc.username; + if (loc.password) { + url += ":" + loc.password; + } + url += "@"; + } + if (loc.host) { + url += loc.host; + } + + if (loc.port) { + url += ":" + loc.port; + } + + if (loc.pathname) { + url += loc.pathname; + } + + if (loc.search) { + url += "?" + loc.search; + } + if (loc.hash) { + var hash = loc.hash; + hash = loc.hash.replace(/^#/, ""); + url += "#" + hash; + } + return url; +} + +function canonicalize(url) { + var tempLink = window.document.createElement("link"); + tempLink.href = url; // this will canonicalize the href + return tempLink.href; +} + +function extractExceptionMessage(ex) { + if (ex == null) return "null exception"; + if (ex.message != null) return ex.message; + if (ex.toString && ex.toString() != null) return ex.toString(); +} + + +function describe(object, delimiter) { + var props = new Array(); + for (var prop in object) { + try { + props.push(prop + " -> " + object[prop]); + } catch (e) { + props.push(prop + " -> [htmlutils: ack! couldn't read this property! (Permission Denied?)]"); + } + } + return props.join(delimiter || '\n'); +} + +var PatternMatcher = function(pattern) { + this.selectStrategy(pattern); +}; +PatternMatcher.prototype = { + + selectStrategy: function(pattern) { + this.pattern = pattern; + var strategyName = 'glob'; + // by default + if (/^([a-z-]+):(.*)/.test(pattern)) { + var possibleNewStrategyName = RegExp.$1; + var possibleNewPattern = RegExp.$2; + if (PatternMatcher.strategies[possibleNewStrategyName]) { + strategyName = possibleNewStrategyName; + pattern = possibleNewPattern; + } + } + var matchStrategy = PatternMatcher.strategies[strategyName]; + if (!matchStrategy) { + throw new SeleniumError("cannot find PatternMatcher.strategies." + strategyName); + } + this.strategy = matchStrategy; + this.matcher = new matchStrategy(pattern); + }, + + matches: function(actual) { + return this.matcher.matches(actual + ''); + // Note: appending an empty string avoids a Konqueror bug + } + +}; + +/** + * A "static" convenience method for easy matching + */ +PatternMatcher.matches = function(pattern, actual) { + return new PatternMatcher(pattern).matches(actual); +}; + +PatternMatcher.strategies = { + +/** + * Exact matching, e.g. "exact:***" + */ + exact: function(expected) { + this.expected = expected; + this.matches = function(actual) { + return actual == this.expected; + }; + }, + +/** + * Match by regular expression, e.g. "regexp:^[0-9]+$" + */ + regexp: function(regexpString) { + this.regexp = new RegExp(regexpString); + this.matches = function(actual) { + return this.regexp.test(actual); + }; + }, + + regex: function(regexpString) { + this.regexp = new RegExp(regexpString); + this.matches = function(actual) { + return this.regexp.test(actual); + }; + }, + +/** + * "globContains" (aka "wildmat") patterns, e.g. "glob:one,two,*", + * but don't require a perfect match; instead succeed if actual + * contains something that matches globString. + * Making this distinction is motivated by a bug in IE6 which + * leads to the browser hanging if we implement *TextPresent tests + * by just matching against a regular expression beginning and + * ending with ".*". The globcontains strategy allows us to satisfy + * the functional needs of the *TextPresent ops more efficiently + * and so avoid running into this IE6 freeze. + */ + globContains: function(globString) { + this.regexp = new RegExp(PatternMatcher.regexpFromGlobContains(globString)); + this.matches = function(actual) { + return this.regexp.test(actual); + }; + }, + + +/** + * "glob" (aka "wildmat") patterns, e.g. "glob:one,two,*" + */ + glob: function(globString) { + this.regexp = new RegExp(PatternMatcher.regexpFromGlob(globString)); + this.matches = function(actual) { + return this.regexp.test(actual); + }; + } + +}; + +PatternMatcher.convertGlobMetaCharsToRegexpMetaChars = function(glob) { + var re = glob; + re = re.replace(/([.^$+(){}\[\]\\|])/g, "\\$1"); + re = re.replace(/\?/g, "(.|[\r\n])"); + re = re.replace(/\*/g, "(.|[\r\n])*"); + return re; +}; + +PatternMatcher.regexpFromGlobContains = function(globContains) { + return PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(globContains); +}; + +PatternMatcher.regexpFromGlob = function(glob) { + return "^" + PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(glob) + "$"; +}; + +var Assert = { + + fail: function(message) { + throw new AssertionFailedError(message); + }, + +/* +* Assert.equals(comment?, expected, actual) +*/ + equals: function() { + var args = new AssertionArguments(arguments); + if (args.expected === args.actual) { + return; + } + Assert.fail(args.comment + + "Expected '" + args.expected + + "' but was '" + args.actual + "'"); + }, + +/* +* Assert.matches(comment?, pattern, actual) +*/ + matches: function() { + var args = new AssertionArguments(arguments); + if (PatternMatcher.matches(args.expected, args.actual)) { + return; + } + Assert.fail(args.comment + + "Actual value '" + args.actual + + "' did not match '" + args.expected + "'"); + }, + +/* +* Assert.notMtches(comment?, pattern, actual) +*/ + notMatches: function() { + var args = new AssertionArguments(arguments); + if (!PatternMatcher.matches(args.expected, args.actual)) { + return; + } + Assert.fail(args.comment + + "Actual value '" + args.actual + + "' did match '" + args.expected + "'"); + } + +}; + +// Preprocess the arguments to allow for an optional comment. +function AssertionArguments(args) { + if (args.length == 2) { + this.comment = ""; + this.expected = args[0]; + this.actual = args[1]; + } else { + this.comment = args[0] + "; "; + this.expected = args[1]; + this.actual = args[2]; + } +} + +function AssertionFailedError(message) { + this.isAssertionFailedError = true; + this.isSeleniumError = true; + this.message = message; + this.failureMessage = message; +} + +function SeleniumError(message) { + var error = new Error(message); + error.isSeleniumError = true; + return error; +} + +function highlight(element) { + var highLightColor = "yellow"; + if (element.originalColor == undefined) { // avoid picking up highlight + element.originalColor = elementGetStyle(element, "background-color"); + } + elementSetStyle(element, {"backgroundColor" : highLightColor}); + window.setTimeout(function() { + try { + //if element is orphan, probably page of it has already gone, so ignore + if (!element.parentNode) { + return; + } + elementSetStyle(element, {"backgroundColor" : element.originalColor}); + } catch (e) {} // DGF unhighlighting is very dangerous and low priority + }, 200); +} + + + +// for use from vs.2003 debugger +function o2s(obj) { + var s = ""; + for (key in obj) { + var line = key + "->" + obj[key]; + line.replace("\n", " "); + s += line + "\n"; + } + return s; +} + +var seenReadyStateWarning = false; + +function openSeparateApplicationWindow(url, suppressMozillaWarning) { + // resize the Selenium window itself + window.resizeTo(1200, 500); + window.moveTo(window.screenX, 0); + + var appWindow = window.open(url + '?start=true', 'main'); + try { + var windowHeight = 500; + if (window.outerHeight) { + windowHeight = window.outerHeight; + } else if (document.documentElement && document.documentElement.offsetHeight) { + windowHeight = document.documentElement.offsetHeight; + } + + if (window.screenLeft && !window.screenX) window.screenX = window.screenLeft; + if (window.screenTop && !window.screenY) window.screenY = window.screenTop; + + appWindow.resizeTo(1200, screen.availHeight - windowHeight - 60); + appWindow.moveTo(window.screenX, window.screenY + windowHeight + 25); + } catch (e) { + LOG.error("Couldn't resize app window"); + LOG.exception(e); + } + + + if (!suppressMozillaWarning && window.document.readyState == null && !seenReadyStateWarning) { + alert("Beware! Mozilla bug 300992 means that we can't always reliably detect when a new page has loaded. Install the Selenium IDE extension or the readyState extension available from selenium.openqa.org to make page load detection more reliable."); + seenReadyStateWarning = true; + } + + return appWindow; +} + +var URLConfiguration = classCreate(); +objectExtend(URLConfiguration.prototype, { + initialize: function() { + }, + _isQueryParameterTrue: function (name) { + var parameterValue = this._getQueryParameter(name); + if (parameterValue == null) return false; + if (parameterValue.toLowerCase() == "true") return true; + if (parameterValue.toLowerCase() == "on") return true; + return false; + }, + + _getQueryParameter: function(searchKey) { + var str = this.queryString + if (str == null) return null; + var clauses = str.split('&'); + for (var i = 0; i < clauses.length; i++) { + var keyValuePair = clauses[i].split('=', 2); + var key = unescape(keyValuePair[0]); + if (key == searchKey) { + return unescape(keyValuePair[1]); + } + } + return null; + }, + + _extractArgs: function() { + var str = SeleniumHTARunner.commandLine; + if (str == null || str == "") return new Array(); + var matches = str.match(/(?:\"([^\"]+)\"|(?!\"([^\"]+)\")(\S+))/g); + // We either want non quote stuff ([^"]+) surrounded by quotes + // or we want to look-ahead, see that the next character isn't + // a quoted argument, and then grab all the non-space stuff + // this will return for the line: "foo" bar + // the results "\"foo\"" and "bar" + + // So, let's unquote the quoted arguments: + var args = new Array; + for (var i = 0; i < matches.length; i++) { + args[i] = matches[i]; + args[i] = args[i].replace(/^"(.*)"$/, "$1"); + } + return args; + }, + + isMultiWindowMode:function() { + return this._isQueryParameterTrue('multiWindow'); + }, + + getBaseUrl:function() { + return this._getQueryParameter('baseUrl'); + + } +}); + + +function safeScrollIntoView(element) { + if (element.scrollIntoView) { + element.scrollIntoView(false); + return; + } + // TODO: work out how to scroll browsers that don't support + // scrollIntoView (like Konqueror) +} diff --git a/tracks/vendor/selenium/scripts/injection.html b/tracks/vendor/selenium/scripts/injection.html new file mode 100644 index 00000000..a75c7211 --- /dev/null +++ b/tracks/vendor/selenium/scripts/injection.html @@ -0,0 +1,79 @@ + diff --git a/tracks/vendor/selenium/scripts/injection_iframe.html b/tracks/vendor/selenium/scripts/injection_iframe.html new file mode 100644 index 00000000..bc26e859 --- /dev/null +++ b/tracks/vendor/selenium/scripts/injection_iframe.html @@ -0,0 +1,7 @@ + diff --git a/tracks/vendor/selenium/scripts/js2html.js b/tracks/vendor/selenium/scripts/js2html.js new file mode 100644 index 00000000..a384dce3 --- /dev/null +++ b/tracks/vendor/selenium/scripts/js2html.js @@ -0,0 +1,70 @@ +/* + +This is an experiment in using the Narcissus JavaScript engine +to allow Selenium scripts to be written in plain JavaScript. + +The 'jsparse' function will compile each high level block into a Selenium table script. + + +TODO: +1) Test! (More browsers, more sample scripts) +2) Stepping and walking lower levels of the parse tree +3) Calling Selenium commands directly from JavaScript +4) Do we want comments to appear in the TestRunner? +5) Fix context so variables don't have to be global + For now, variables defined with "var" won't be found + if used later on in a script. +6) Fix formatting +*/ + + +function jsparse() { + var script = document.getElementById('sejs') + var fname = 'javascript script'; + parse_result = parse(script.text, fname, 0); + + var x2 = new ExecutionContext(GLOBAL_CODE); + ExecutionContext.current = x2; + + + var new_test_source = ''; + var new_line = ''; + + for (i=0;icurrentTest.doNextCommand()' + + '' + script_fragment + '' + + '\n'; + new_test_source += new_line; + //eval(script_fragment); + + + }; + + + + execute(parse_result,x2) + + // Create HTML Table + body = document.body + body.innerHTML += ""+ + "" + + "" + + new_test_source + + ""; + + //body.innerHTML = "
    " + parse_result + "
    " +} + + diff --git a/tracks/vendor/selenium/scripts/narcissus-defs.js b/tracks/vendor/selenium/scripts/narcissus-defs.js new file mode 100644 index 00000000..5869397d --- /dev/null +++ b/tracks/vendor/selenium/scripts/narcissus-defs.js @@ -0,0 +1,175 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Well-known constants and lookup tables. Many consts are generated from the + * tokens table via eval to minimize redundancy, so consumers must be compiled + * separately to take advantage of the simple switch-case constant propagation + * done by SpiderMonkey. + */ + +// jrh +//module('JS.Defs'); + +GLOBAL = this; + +var tokens = [ + // End of source. + "END", + + // Operators and punctuators. Some pair-wise order matters, e.g. (+, -) + // and (UNARY_PLUS, UNARY_MINUS). + "\n", ";", + ",", + "=", + "?", ":", "CONDITIONAL", + "||", + "&&", + "|", + "^", + "&", + "==", "!=", "===", "!==", + "<", "<=", ">=", ">", + "<<", ">>", ">>>", + "+", "-", + "*", "/", "%", + "!", "~", "UNARY_PLUS", "UNARY_MINUS", + "++", "--", + ".", + "[", "]", + "{", "}", + "(", ")", + + // Nonterminal tree node type codes. + "SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX", + "ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER", + "GROUP", "LIST", + + // Terminals. + "IDENTIFIER", "NUMBER", "STRING", "REGEXP", + + // Keywords. + "break", + "case", "catch", "const", "continue", + "debugger", "default", "delete", "do", + "else", "enum", + "false", "finally", "for", "function", + "if", "in", "instanceof", + "new", "null", + "return", + "switch", + "this", "throw", "true", "try", "typeof", + "var", "void", + "while", "with", + // Extensions + "require", "bless", "mixin", "import" +]; + +// Operator and punctuator mapping from token to tree node type name. +// NB: superstring tokens (e.g., ++) must come before their substring token +// counterparts (+ in the example), so that the opRegExp regular expression +// synthesized from this list makes the longest possible match. +var opTypeNames = { + '\n': "NEWLINE", + ';': "SEMICOLON", + ',': "COMMA", + '?': "HOOK", + ':': "COLON", + '||': "OR", + '&&': "AND", + '|': "BITWISE_OR", + '^': "BITWISE_XOR", + '&': "BITWISE_AND", + '===': "STRICT_EQ", + '==': "EQ", + '=': "ASSIGN", + '!==': "STRICT_NE", + '!=': "NE", + '<<': "LSH", + '<=': "LE", + '<': "LT", + '>>>': "URSH", + '>>': "RSH", + '>=': "GE", + '>': "GT", + '++': "INCREMENT", + '--': "DECREMENT", + '+': "PLUS", + '-': "MINUS", + '*': "MUL", + '/': "DIV", + '%': "MOD", + '!': "NOT", + '~': "BITWISE_NOT", + '.': "DOT", + '[': "LEFT_BRACKET", + ']': "RIGHT_BRACKET", + '{': "LEFT_CURLY", + '}': "RIGHT_CURLY", + '(': "LEFT_PAREN", + ')': "RIGHT_PAREN" +}; + +// Hash of keyword identifier to tokens index. NB: we must null __proto__ to +// avoid toString, etc. namespace pollution. +var keywords = {__proto__: null}; + +// Define const END, etc., based on the token names. Also map name to index. +var consts = " "; +for (var i = 0, j = tokens.length; i < j; i++) { + if (i > 0) + consts += "; "; + var t = tokens[i]; + if (/^[a-z]/.test(t)) { + consts += t.toUpperCase(); + keywords[t] = i; + } else { + consts += (/^\W/.test(t) ? opTypeNames[t] : t); + } + consts += " = " + i; + tokens[t] = i; +} +eval(consts + ";"); + +// Map assignment operators to their indexes in the tokens array. +var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%']; + +for (i = 0, j = assignOps.length; i < j; i++) { + t = assignOps[i]; + assignOps[t] = tokens[t]; +} diff --git a/tracks/vendor/selenium/scripts/narcissus-exec.js b/tracks/vendor/selenium/scripts/narcissus-exec.js new file mode 100644 index 00000000..e2c88f81 --- /dev/null +++ b/tracks/vendor/selenium/scripts/narcissus-exec.js @@ -0,0 +1,1054 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * vim: set ts=4 sw=4 et tw=80: + * + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Execution of parse trees. + * + * Standard classes except for eval, Function, Array, and String are borrowed + * from the host JS environment. Function is metacircular. Array and String + * are reflected via wrapping the corresponding native constructor and adding + * an extra level of prototype-based delegation. + */ + +// jrh +//module('JS.Exec'); +// end jrh + +GLOBAL_CODE = 0; EVAL_CODE = 1; FUNCTION_CODE = 2; + +function ExecutionContext(type) { + this.type = type; +} + +// jrh +var agenda = new Array(); +var skip_setup = 0; +// end jrh + +var global = { + // Value properties. + NaN: NaN, Infinity: Infinity, undefined: undefined, + alert : function(msg) { alert(msg) }, + confirm : function(msg) { return confirm(msg) }, + document : document, + window : window, + // jrh + //debug: window.open('','debugwindow','width=600,height=400,scrollbars=yes,resizable=yes'), + // end jrh + navigator : navigator, + XMLHttpRequest : function() { return new XMLHttpRequest() }, + // Function properties. + eval: function(s) { + if (typeof s != "string") { + return s; + } + + var x = ExecutionContext.current; + var x2 = new ExecutionContext(EVAL_CODE); + x2.thisObject = x.thisObject; + x2.caller = x.caller; + x2.callee = x.callee; + x2.scope = x.scope; + ExecutionContext.current = x2; + try { + execute(parse(s), x2); + } catch (e) { + x.result = x2.result; + throw e; + } finally { + ExecutionContext.current = x; + } + return x2.result; + }, + parseInt: parseInt, parseFloat: parseFloat, + isNaN: isNaN, isFinite: isFinite, + decodeURI: decodeURI, encodeURI: encodeURI, + decodeURIComponent: decodeURIComponent, + encodeURIComponent: encodeURIComponent, + + // Class constructors. Where ECMA-262 requires C.length == 1, we declare + // a dummy formal parameter. + Object: Object, + Function: function(dummy) { + var p = "", b = "", n = arguments.length; + if (n) { + var m = n - 1; + if (m) { + p += arguments[0]; + for (var k = 1; k < m; k++) + p += "," + arguments[k]; + } + b += arguments[m]; + } + + // XXX We want to pass a good file and line to the tokenizer. + // Note the anonymous name to maintain parity with Spidermonkey. + var t = new Tokenizer("anonymous(" + p + ") {" + b + "}"); + + // NB: Use the STATEMENT_FORM constant since we don't want to push this + // function onto the null compilation context. + var f = FunctionDefinition(t, null, false, STATEMENT_FORM); + var s = {object: global, parent: null}; + return new FunctionObject(f, s); + }, + Array: function(dummy) { + // Array when called as a function acts as a constructor. + return GLOBAL.Array.apply(this, arguments); + }, + String: function(s) { + // Called as function or constructor: convert argument to string type. + s = arguments.length ? "" + s : ""; + if (this instanceof String) { + // Called as constructor: save the argument as the string value + // of this String object and return this object. + this.value = s; + return this; + } + return s; + }, + Boolean: Boolean, Number: Number, Date: Date, RegExp: RegExp, + Error: Error, EvalError: EvalError, RangeError: RangeError, + ReferenceError: ReferenceError, SyntaxError: SyntaxError, + TypeError: TypeError, URIError: URIError, + + // Other properties. + Math: Math, + + // Extensions to ECMA. + //snarf: snarf, + evaluate: evaluate, + load: function(s) { + if (typeof s != "string") + return s; + var req = new XMLHttpRequest(); + req.open('GET', s, false); + req.send(null); + + evaluate(req.responseText, s, 1) + }, + print: print, version: null +}; + +// jrh +//global.debug.document.body.innerHTML = '' +// end jrh + +// Helper to avoid Object.prototype.hasOwnProperty polluting scope objects. +function hasDirectProperty(o, p) { + return Object.prototype.hasOwnProperty.call(o, p); +} + +// Reflect a host class into the target global environment by delegation. +function reflectClass(name, proto) { + var gctor = global[name]; + gctor.prototype = proto; + proto.constructor = gctor; + return proto; +} + +// Reflect Array -- note that all Array methods are generic. +reflectClass('Array', new Array); + +// Reflect String, overriding non-generic methods. +var gSp = reflectClass('String', new String); +gSp.toSource = function () { return this.value.toSource(); }; +gSp.toString = function () { return this.value; }; +gSp.valueOf = function () { return this.value; }; +global.String.fromCharCode = String.fromCharCode; + +var XCp = ExecutionContext.prototype; +ExecutionContext.current = XCp.caller = XCp.callee = null; +XCp.scope = {object: global, parent: null}; +XCp.thisObject = global; +XCp.result = undefined; +XCp.target = null; +XCp.ecmaStrictMode = false; + +function Reference(base, propertyName, node) { + this.base = base; + this.propertyName = propertyName; + this.node = node; +} + +Reference.prototype.toString = function () { return this.node.getSource(); } + +function getValue(v) { + if (v instanceof Reference) { + if (!v.base) { + throw new ReferenceError(v.propertyName + " is not defined", + v.node.filename(), v.node.lineno); + } + return v.base[v.propertyName]; + } + return v; +} + +function putValue(v, w, vn) { + if (v instanceof Reference) + return (v.base || global)[v.propertyName] = w; + throw new ReferenceError("Invalid assignment left-hand side", + vn.filename(), vn.lineno); +} + +function isPrimitive(v) { + var t = typeof v; + return (t == "object") ? v === null : t != "function"; +} + +function isObject(v) { + var t = typeof v; + return (t == "object") ? v !== null : t == "function"; +} + +// If r instanceof Reference, v == getValue(r); else v === r. If passed, rn +// is the node whose execute result was r. +function toObject(v, r, rn) { + switch (typeof v) { + case "boolean": + return new global.Boolean(v); + case "number": + return new global.Number(v); + case "string": + return new global.String(v); + case "function": + return v; + case "object": + if (v !== null) + return v; + } + var message = r + " (type " + (typeof v) + ") has no properties"; + throw rn ? new TypeError(message, rn.filename(), rn.lineno) + : new TypeError(message); +} + +function execute(n, x) { + if (!this.new_block) + new_block = new Array(); + //alert (n) + var a, f, i, j, r, s, t, u, v; + switch (n.type) { + case FUNCTION: + if (n.functionForm != DECLARED_FORM) { + if (!n.name || n.functionForm == STATEMENT_FORM) { + v = new FunctionObject(n, x.scope); + if (n.functionForm == STATEMENT_FORM) + x.scope.object[n.name] = v; + } else { + t = new Object; + x.scope = {object: t, parent: x.scope}; + try { + v = new FunctionObject(n, x.scope); + t[n.name] = v; + } finally { + x.scope = x.scope.parent; + } + } + } + break; + + case SCRIPT: + t = x.scope.object; + a = n.funDecls; + for (i = 0, j = a.length; i < j; i++) { + s = a[i].name; + f = new FunctionObject(a[i], x.scope); + t[s] = f; + } + a = n.varDecls; + for (i = 0, j = a.length; i < j; i++) { + u = a[i]; + s = u.name; + if (u.readOnly && hasDirectProperty(t, s)) { + throw new TypeError("Redeclaration of const " + s, + u.filename(), u.lineno); + } + if (u.readOnly || !hasDirectProperty(t, s)) { + t[s] = null; + } + } + // FALL THROUGH + + case BLOCK: + for (i = 0, j = n.$length; i < j; i++) { + //jrh + //execute(n[i], x); + //new_block.unshift([n[i], x]); + new_block.push([n[i], x]); + } + new_block.reverse(); + agenda = agenda.concat(new_block); + //agenda = new_block.concat(agenda) + // end jrh + break; + + case IF: + if (getValue(execute(n.condition, x))) + execute(n.thenPart, x); + else if (n.elsePart) + execute(n.elsePart, x); + break; + + case SWITCH: + s = getValue(execute(n.discriminant, x)); + a = n.cases; + var matchDefault = false; + switch_loop: + for (i = 0, j = a.length; ; i++) { + if (i == j) { + if (n.defaultIndex >= 0) { + i = n.defaultIndex - 1; // no case matched, do default + matchDefault = true; + continue; + } + break; // no default, exit switch_loop + } + t = a[i]; // next case (might be default!) + if (t.type == CASE) { + u = getValue(execute(t.caseLabel, x)); + } else { + if (!matchDefault) // not defaulting, skip for now + continue; + u = s; // force match to do default + } + if (u === s) { + for (;;) { // this loop exits switch_loop + if (t.statements.length) { + try { + execute(t.statements, x); + } catch (e) { + if (!(e == BREAK && x.target == n)) { throw e } + break switch_loop; + } + } + if (++i == j) + break switch_loop; + t = a[i]; + } + // NOT REACHED + } + } + break; + + case FOR: + // jrh + // added "skip_setup" so initialization doesn't get called + // on every call.. + if (!skip_setup) + n.setup && getValue(execute(n.setup, x)); + // FALL THROUGH + case WHILE: + // jrh + //while (!n.condition || getValue(execute(n.condition, x))) { + if (!n.condition || getValue(execute(n.condition, x))) { + try { + // jrh + //execute(n.body, x); + new_block.push([n.body, x]); + agenda.push([n.body, x]) + //agenda.unshift([n.body, x]) + // end jrh + } catch (e) { + if (e == BREAK && x.target == n) { + break; + } else if (e == CONTINUE && x.target == n) { + // jrh + // 'continue' is invalid inside an 'if' clause + // I don't know what commenting this out will break! + //continue; + // end jrh + + } else { + throw e; + } + } + n.update && getValue(execute(n.update, x)); + // jrh + new_block.unshift([n, x]) + agenda.splice(agenda.length-1,0,[n, x]) + //agenda.splice(1,0,[n, x]) + skip_setup = 1 + // end jrh + } else { + skip_setup = 0 + } + + break; + + case FOR_IN: + u = n.varDecl; + if (u) + execute(u, x); + r = n.iterator; + s = execute(n.object, x); + v = getValue(s); + + // ECMA deviation to track extant browser JS implementation behavior. + t = (v == null && !x.ecmaStrictMode) ? v : toObject(v, s, n.object); + a = []; + for (i in t) + a.push(i); + for (i = 0, j = a.length; i < j; i++) { + putValue(execute(r, x), a[i], r); + try { + execute(n.body, x); + } catch (e) { + if (e == BREAK && x.target == n) { + break; + } else if (e == CONTINUE && x.target == n) { + continue; + } else { + throw e; + } + } + } + break; + + case DO: + do { + try { + execute(n.body, x); + } catch (e) { + if (e == BREAK && x.target == n) { + break; + } else if (e == CONTINUE && x.target == n) { + continue; + } else { + throw e; + } + } + } while (getValue(execute(n.condition, x))); + break; + + case BREAK: + case CONTINUE: + x.target = n.target; + throw n.type; + + case TRY: + try { + execute(n.tryBlock, x); + } catch (e) { + if (!(e == THROW && (j = n.catchClauses.length))) { + throw e; + } + e = x.result; + x.result = undefined; + for (i = 0; ; i++) { + if (i == j) { + x.result = e; + throw THROW; + } + t = n.catchClauses[i]; + x.scope = {object: {}, parent: x.scope}; + x.scope.object[t.varName] = e; + try { + if (t.guard && !getValue(execute(t.guard, x))) + continue; + execute(t.block, x); + break; + } finally { + x.scope = x.scope.parent; + } + } + } finally { + if (n.finallyBlock) + execute(n.finallyBlock, x); + } + break; + + case THROW: + x.result = getValue(execute(n.exception, x)); + throw THROW; + + case RETURN: + x.result = getValue(execute(n.value, x)); + throw RETURN; + + case WITH: + r = execute(n.object, x); + t = toObject(getValue(r), r, n.object); + x.scope = {object: t, parent: x.scope}; + try { + execute(n.body, x); + } finally { + x.scope = x.scope.parent; + } + break; + + case VAR: + case CONST: + for (i = 0, j = n.$length; i < j; i++) { + u = n[i].initializer; + if (!u) + continue; + t = n[i].name; + for (s = x.scope; s; s = s.parent) { + if (hasDirectProperty(s.object, t)) + break; + } + u = getValue(execute(u, x)); + if (n.type == CONST) + s.object[t] = u; + else + s.object[t] = u; + } + break; + + case DEBUGGER: + throw "NYI: " + tokens[n.type]; + + case REQUIRE: + var req = new XMLHttpRequest(); + req.open('GET', n.filename, 'false'); + + case SEMICOLON: + if (n.expression) + // print debugging statements + + var the_start = n.start + var the_end = n.end + var the_statement = parse_result.tokenizer.source.slice(the_start,the_end) + //global.debug.document.body.innerHTML += ('
    >>> ' + the_statement + '
    ') + LOG.info('>>>' + the_statement) + x.result = getValue(execute(n.expression, x)); + //if (x.result) + //global.debug.document.body.innerHTML += ( '
    >>> ' + x.result + '
    ') + + break; + + case LABEL: + try { + execute(n.statement, x); + } catch (e) { + if (!(e == BREAK && x.target == n)) { throw e } + } + break; + + case COMMA: + for (i = 0, j = n.$length; i < j; i++) + v = getValue(execute(n[i], x)); + break; + + case ASSIGN: + r = execute(n[0], x); + t = n[0].assignOp; + if (t) + u = getValue(r); + v = getValue(execute(n[1], x)); + if (t) { + switch (t) { + case BITWISE_OR: v = u | v; break; + case BITWISE_XOR: v = u ^ v; break; + case BITWISE_AND: v = u & v; break; + case LSH: v = u << v; break; + case RSH: v = u >> v; break; + case URSH: v = u >>> v; break; + case PLUS: v = u + v; break; + case MINUS: v = u - v; break; + case MUL: v = u * v; break; + case DIV: v = u / v; break; + case MOD: v = u % v; break; + } + } + putValue(r, v, n[0]); + break; + + case CONDITIONAL: + v = getValue(execute(n[0], x)) ? getValue(execute(n[1], x)) + : getValue(execute(n[2], x)); + break; + + case OR: + v = getValue(execute(n[0], x)) || getValue(execute(n[1], x)); + break; + + case AND: + v = getValue(execute(n[0], x)) && getValue(execute(n[1], x)); + break; + + case BITWISE_OR: + v = getValue(execute(n[0], x)) | getValue(execute(n[1], x)); + break; + + case BITWISE_XOR: + v = getValue(execute(n[0], x)) ^ getValue(execute(n[1], x)); + break; + + case BITWISE_AND: + v = getValue(execute(n[0], x)) & getValue(execute(n[1], x)); + break; + + case EQ: + v = getValue(execute(n[0], x)) == getValue(execute(n[1], x)); + break; + + case NE: + v = getValue(execute(n[0], x)) != getValue(execute(n[1], x)); + break; + + case STRICT_EQ: + v = getValue(execute(n[0], x)) === getValue(execute(n[1], x)); + break; + + case STRICT_NE: + v = getValue(execute(n[0], x)) !== getValue(execute(n[1], x)); + break; + + case LT: + v = getValue(execute(n[0], x)) < getValue(execute(n[1], x)); + break; + + case LE: + v = getValue(execute(n[0], x)) <= getValue(execute(n[1], x)); + break; + + case GE: + v = getValue(execute(n[0], x)) >= getValue(execute(n[1], x)); + break; + + case GT: + v = getValue(execute(n[0], x)) > getValue(execute(n[1], x)); + break; + + case IN: + v = getValue(execute(n[0], x)) in getValue(execute(n[1], x)); + break; + + case INSTANCEOF: + t = getValue(execute(n[0], x)); + u = getValue(execute(n[1], x)); + if (isObject(u) && typeof u.__hasInstance__ == "function") + v = u.__hasInstance__(t); + else + v = t instanceof u; + break; + + case LSH: + v = getValue(execute(n[0], x)) << getValue(execute(n[1], x)); + break; + + case RSH: + v = getValue(execute(n[0], x)) >> getValue(execute(n[1], x)); + break; + + case URSH: + v = getValue(execute(n[0], x)) >>> getValue(execute(n[1], x)); + break; + + case PLUS: + v = getValue(execute(n[0], x)) + getValue(execute(n[1], x)); + break; + + case MINUS: + v = getValue(execute(n[0], x)) - getValue(execute(n[1], x)); + break; + + case MUL: + v = getValue(execute(n[0], x)) * getValue(execute(n[1], x)); + break; + + case DIV: + v = getValue(execute(n[0], x)) / getValue(execute(n[1], x)); + break; + + case MOD: + v = getValue(execute(n[0], x)) % getValue(execute(n[1], x)); + break; + + case DELETE: + t = execute(n[0], x); + v = !(t instanceof Reference) || delete t.base[t.propertyName]; + break; + + case VOID: + getValue(execute(n[0], x)); + break; + + case TYPEOF: + t = execute(n[0], x); + if (t instanceof Reference) + t = t.base ? t.base[t.propertyName] : undefined; + v = typeof t; + break; + + case NOT: + v = !getValue(execute(n[0], x)); + break; + + case BITWISE_NOT: + v = ~getValue(execute(n[0], x)); + break; + + case UNARY_PLUS: + v = +getValue(execute(n[0], x)); + break; + + case UNARY_MINUS: + v = -getValue(execute(n[0], x)); + break; + + case INCREMENT: + case DECREMENT: + t = execute(n[0], x); + u = Number(getValue(t)); + if (n.postfix) + v = u; + putValue(t, (n.type == INCREMENT) ? ++u : --u, n[0]); + if (!n.postfix) + v = u; + break; + + case DOT: + r = execute(n[0], x); + t = getValue(r); + u = n[1].value; + v = new Reference(toObject(t, r, n[0]), u, n); + break; + + case INDEX: + r = execute(n[0], x); + t = getValue(r); + u = getValue(execute(n[1], x)); + v = new Reference(toObject(t, r, n[0]), String(u), n); + break; + + case LIST: + // Curse ECMA for specifying that arguments is not an Array object! + v = {}; + for (i = 0, j = n.$length; i < j; i++) { + u = getValue(execute(n[i], x)); + v[i] = u; + } + v.length = i; + break; + + case CALL: + r = execute(n[0], x); + a = execute(n[1], x); + f = getValue(r); + if (isPrimitive(f) || typeof f.__call__ != "function") { + throw new TypeError(r + " is not callable", + n[0].filename(), n[0].lineno); + } + t = (r instanceof Reference) ? r.base : null; + if (t instanceof Activation) + t = null; + v = f.__call__(t, a, x); + break; + + case NEW: + case NEW_WITH_ARGS: + r = execute(n[0], x); + f = getValue(r); + if (n.type == NEW) { + a = {}; + a.length = 0; + } else { + a = execute(n[1], x); + } + if (isPrimitive(f) || typeof f.__construct__ != "function") { + throw new TypeError(r + " is not a constructor", + n[0].filename(), n[0].lineno); + } + v = f.__construct__(a, x); + break; + + case ARRAY_INIT: + v = []; + for (i = 0, j = n.$length; i < j; i++) { + if (n[i]) + v[i] = getValue(execute(n[i], x)); + } + v.length = j; + break; + + case OBJECT_INIT: + v = {}; + for (i = 0, j = n.$length; i < j; i++) { + t = n[i]; + if (t.type == PROPERTY_INIT) { + v[t[0].value] = getValue(execute(t[1], x)); + } else { + f = new FunctionObject(t, x.scope); + /* + u = (t.type == GETTER) ? '__defineGetter__' + : '__defineSetter__'; + v[u](t.name, thunk(f, x)); + */ + } + } + break; + + case NULL: + v = null; + break; + + case THIS: + v = x.thisObject; + break; + + case TRUE: + v = true; + break; + + case FALSE: + v = false; + break; + + case IDENTIFIER: + for (s = x.scope; s; s = s.parent) { + if (n.value in s.object) + break; + } + v = new Reference(s && s.object, n.value, n); + break; + + case NUMBER: + case STRING: + case REGEXP: + v = n.value; + break; + + case GROUP: + v = execute(n[0], x); + break; + + default: + throw "PANIC: unknown operation " + n.type + ": " + uneval(n); + } + return v; +} + +function Activation(f, a) { + for (var i = 0, j = f.params.length; i < j; i++) + this[f.params[i]] = a[i]; + this.arguments = a; +} + +// Null Activation.prototype's proto slot so that Object.prototype.* does not +// pollute the scope of heavyweight functions. Also delete its 'constructor' +// property so that it doesn't pollute function scopes. + +Activation.prototype.__proto__ = null; +delete Activation.prototype.constructor; + +function FunctionObject(node, scope) { + this.node = node; + this.scope = scope; + this.length = node.params.length; + var proto = {}; + this.prototype = proto; + proto.constructor = this; +} + +var FOp = FunctionObject.prototype = { + // Internal methods. + __call__: function (t, a, x) { + var x2 = new ExecutionContext(FUNCTION_CODE); + x2.thisObject = t || global; + x2.caller = x; + x2.callee = this; + a.callee = this; + var f = this.node; + x2.scope = {object: new Activation(f, a), parent: this.scope}; + + ExecutionContext.current = x2; + try { + execute(f.body, x2); + } catch (e) { + if (!(e == RETURN)) { throw e } else if (e == RETURN) { + return x2.result; + } + if (e != THROW) { throw e } + x.result = x2.result; + throw THROW; + } finally { + ExecutionContext.current = x; + } + return undefined; + }, + + __construct__: function (a, x) { + var o = new Object; + var p = this.prototype; + if (isObject(p)) + o.__proto__ = p; + // else o.__proto__ defaulted to Object.prototype + + var v = this.__call__(o, a, x); + if (isObject(v)) + return v; + return o; + }, + + __hasInstance__: function (v) { + if (isPrimitive(v)) + return false; + var p = this.prototype; + if (isPrimitive(p)) { + throw new TypeError("'prototype' property is not an object", + this.node.filename(), this.node.lineno); + } + var o; + while ((o = v.__proto__)) { + if (o == p) + return true; + v = o; + } + return false; + }, + + // Standard methods. + toString: function () { + return this.node.getSource(); + }, + + apply: function (t, a) { + // Curse ECMA again! + if (typeof this.__call__ != "function") { + throw new TypeError("Function.prototype.apply called on" + + " uncallable object"); + } + + if (t === undefined || t === null) + t = global; + else if (typeof t != "object") + t = toObject(t, t); + + if (a === undefined || a === null) { + a = {}; + a.length = 0; + } else if (a instanceof Array) { + var v = {}; + for (var i = 0, j = a.length; i < j; i++) + v[i] = a[i]; + v.length = i; + a = v; + } else if (!(a instanceof Object)) { + // XXX check for a non-arguments object + throw new TypeError("Second argument to Function.prototype.apply" + + " must be an array or arguments object", + this.node.filename(), this.node.lineno); + } + + return this.__call__(t, a, ExecutionContext.current); + }, + + call: function (t) { + // Curse ECMA a third time! + var a = Array.prototype.splice.call(arguments, 1); + return this.apply(t, a); + } +}; + +// Connect Function.prototype and Function.prototype.constructor in global. +reflectClass('Function', FOp); + +// Help native and host-scripted functions be like FunctionObjects. +var Fp = Function.prototype; +var REp = RegExp.prototype; + +if (!('__call__' in Fp)) { + Fp.__call__ = function (t, a, x) { + // Curse ECMA yet again! + a = Array.prototype.splice.call(a, 0, a.length); + return this.apply(t, a); + }; + + REp.__call__ = function (t, a, x) { + a = Array.prototype.splice.call(a, 0, a.length); + return this.exec.apply(this, a); + }; + + Fp.__construct__ = function (a, x) { + switch (a.length) { + case 0: + return new this(); + case 1: + return new this(a[0]); + case 2: + return new this(a[0], a[1]); + case 3: + return new this(a[0], a[1], a[2]); + case 4: + return new this(a[0], a[1], a[2], a[3]); + case 5: + return new this(a[0], a[1], a[2], a[3], a[4]); + case 6: + return new this(a[0], a[1], a[2], a[3], a[4], a[5]); + case 7: + return new this(a[0], a[1], a[2], a[3], a[4], a[5], a[6]); + } + throw "PANIC: too many arguments to constructor"; + } + + // Since we use native functions such as Date along with host ones such + // as global.eval, we want both to be considered instances of the native + // Function constructor. + Fp.__hasInstance__ = function (v) { + return v instanceof Function || v instanceof global.Function; + }; +} + +function thunk(f, x) { + return function () { return f.__call__(this, arguments, x); }; +} + +function evaluate(s, f, l) { + if (typeof s != "string") + return s; + + var x = ExecutionContext.current; + var x2 = new ExecutionContext(GLOBAL_CODE); + ExecutionContext.current = x2; + try { + execute(parse(s, f, l), x2); + } catch (e) { + if (e != THROW) { throw e } + if (x) { + x.result = x2.result; + throw(THROW); + } + throw x2.result; + } finally { + ExecutionContext.current = x; + } + return x2.result; +} diff --git a/tracks/vendor/selenium/scripts/narcissus-parse.js b/tracks/vendor/selenium/scripts/narcissus-parse.js new file mode 100644 index 00000000..d6acb836 --- /dev/null +++ b/tracks/vendor/selenium/scripts/narcissus-parse.js @@ -0,0 +1,1003 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): Richard Hundt + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Lexical scanner and parser. + */ + +// jrh +//module('JS.Parse'); + +// Build a regexp that recognizes operators and punctuators (except newline). +var opRegExp = +/^;|^,|^\?|^:|^\|\||^\&\&|^\||^\^|^\&|^===|^==|^=|^!==|^!=|^<<|^<=|^<|^>>>|^>>|^>=|^>|^\+\+|^\-\-|^\+|^\-|^\*|^\/|^%|^!|^~|^\.|^\[|^\]|^\{|^\}|^\(|^\)/; + +// A regexp to match floating point literals (but not integer literals). +var fpRegExp = /^\d+\.\d*(?:[eE][-+]?\d+)?|^\d+(?:\.\d*)?[eE][-+]?\d+|^\.\d+(?:[eE][-+]?\d+)?/; + +function Tokenizer(s, f, l) { + this.cursor = 0; + this.source = String(s); + this.tokens = []; + this.tokenIndex = 0; + this.lookahead = 0; + this.scanNewlines = false; + this.scanOperand = true; + this.filename = f || ""; + this.lineno = l || 1; +} + +Tokenizer.prototype = { + input : function() { + return this.source.substring(this.cursor); + }, + + done : function() { + return this.peek() == END; + }, + + token : function() { + return this.tokens[this.tokenIndex]; + }, + + match: function (tt) { + return this.get() == tt || this.unget(); + }, + + mustMatch: function (tt) { + if (!this.match(tt)) + throw this.newSyntaxError("Missing " + this.tokens[tt].toLowerCase()); + return this.token(); + }, + + peek: function () { + var tt; + if (this.lookahead) { + tt = this.tokens[(this.tokenIndex + this.lookahead) & 3].type; + } else { + tt = this.get(); + this.unget(); + } + return tt; + }, + + peekOnSameLine: function () { + this.scanNewlines = true; + var tt = this.peek(); + this.scanNewlines = false; + return tt; + }, + + get: function () { + var token; + while (this.lookahead) { + --this.lookahead; + this.tokenIndex = (this.tokenIndex + 1) & 3; + token = this.tokens[this.tokenIndex]; + if (token.type != NEWLINE || this.scanNewlines) + return token.type; + } + + for (;;) { + var input = this.input(); + var rx = this.scanNewlines ? /^[ \t]+/ : /^\s+/; + var match = input.match(rx); + if (match) { + var spaces = match[0]; + this.cursor += spaces.length; + var newlines = spaces.match(/\n/g); + if (newlines) + this.lineno += newlines.length; + input = this.input(); + } + + if (!(match = input.match(/^\/(?:\*(?:.|\n)*?\*\/|\/.*)/))) + break; + var comment = match[0]; + this.cursor += comment.length; + newlines = comment.match(/\n/g); + if (newlines) + this.lineno += newlines.length + } + + this.tokenIndex = (this.tokenIndex + 1) & 3; + token = this.tokens[this.tokenIndex]; + if (!token) + this.tokens[this.tokenIndex] = token = {}; + if (!input) + return token.type = END; + if ((match = input.match(fpRegExp))) { + token.type = NUMBER; + token.value = parseFloat(match[0]); + } else if ((match = input.match(/^0[xX][\da-fA-F]+|^0[0-7]*|^\d+/))) { + token.type = NUMBER; + token.value = parseInt(match[0]); + } else if ((match = input.match(/^((\$\w*)|(\w+))/))) { + var id = match[0]; + token.type = keywords[id] || IDENTIFIER; + token.value = id; + } else if ((match = input.match(/^"(?:\\.|[^"])*"|^'(?:[^']|\\.)*'/))) { + token.type = STRING; + token.value = eval(match[0]); + } else if (this.scanOperand && + (match = input.match(/^\/((?:\\.|[^\/])+)\/([gi]*)/))) { + token.type = REGEXP; + token.value = new RegExp(match[1], match[2]); + } else if ((match = input.match(opRegExp))) { + var op = match[0]; + if (assignOps[op] && input[op.length] == '=') { + token.type = ASSIGN; + token.assignOp = GLOBAL[opTypeNames[op]]; + match[0] += '='; + } else { + token.type = GLOBAL[opTypeNames[op]]; + if (this.scanOperand && + (token.type == PLUS || token.type == MINUS)) { + token.type += UNARY_PLUS - PLUS; + } + token.assignOp = null; + } + //debug('token.value => '+op+', token.type => '+token.type); + token.value = op; + } else { + throw this.newSyntaxError("Illegal token"); + } + + token.start = this.cursor; + this.cursor += match[0].length; + token.end = this.cursor; + token.lineno = this.lineno; + return token.type; + }, + + unget: function () { + if (++this.lookahead == 4) throw "PANIC: too much lookahead!"; + this.tokenIndex = (this.tokenIndex - 1) & 3; + }, + + newSyntaxError: function (m) { + var e = new SyntaxError(m, this.filename, this.lineno); + e.source = this.source; + e.cursor = this.cursor; + return e; + } +}; + +function CompilerContext(inFunction) { + this.inFunction = inFunction; + this.stmtStack = []; + this.funDecls = []; + this.varDecls = []; +} + +var CCp = CompilerContext.prototype; +CCp.bracketLevel = CCp.curlyLevel = CCp.parenLevel = CCp.hookLevel = 0; +CCp.ecmaStrictMode = CCp.inForLoopInit = false; + +function Script(t, x) { + var n = Statements(t, x); + n.type = SCRIPT; + n.funDecls = x.funDecls; + n.varDecls = x.varDecls; + return n; +} + +// Node extends Array, which we extend slightly with a top-of-stack method. +Array.prototype.top = function() { + return this.length && this[this.length-1]; +} + +function NarcNode(t, type) { + var token = t.token(); + if (token) { + this.type = type || token.type; + this.value = token.value; + this.lineno = token.lineno; + this.start = token.start; + this.end = token.end; + } else { + this.type = type; + this.lineno = t.lineno; + } + this.tokenizer = t; + for (var i = 2; i < arguments.length; i++) + this.push(arguments[i]); +} + +var Np = NarcNode.prototype = new Array(); +Np.constructor = NarcNode; +Np.$length = 0; +Np.toSource = Object.prototype.toSource; + +// Always use push to add operands to an expression, to update start and end. +Np.push = function (kid) { + if (kid.start < this.start) + this.start = kid.start; + if (this.end < kid.end) + this.end = kid.end; + //debug('length before => '+this.$length); + this[this.$length] = kid; + this.$length++; + //debug('length after => '+this.$length); +} + +NarcNode.indentLevel = 0; + +function tokenstr(tt) { + var t = tokens[tt]; + return /^\W/.test(t) ? opTypeNames[t] : t.toUpperCase(); +} + +Np.toString = function () { + var a = []; + for (var i in this) { + if (this.hasOwnProperty(i) && i != 'type') + a.push({id: i, value: this[i]}); + } + a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; }); + INDENTATION = " "; + var n = ++NarcNode.indentLevel; + var s = "{\n" + INDENTATION.repeat(n) + "type: " + tokenstr(this.type); + for (i = 0; i < a.length; i++) + s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value; + n = --NarcNode.indentLevel; + s += "\n" + INDENTATION.repeat(n) + "}"; + return s; +} + +Np.getSource = function () { + return this.tokenizer.source.slice(this.start, this.end); +}; + +Np.filename = function () { return this.tokenizer.filename; }; + +String.prototype.repeat = function (n) { + var s = "", t = this + s; + while (--n >= 0) + s += t; + return s; +} + +// Statement stack and nested statement handler. +function nest(t, x, node, func, end) { + x.stmtStack.push(node); + var n = func(t, x); + x.stmtStack.pop(); + end && t.mustMatch(end); + return n; +} + +function Statements(t, x) { + var n = new NarcNode(t, BLOCK); + x.stmtStack.push(n); + while (!t.done() && t.peek() != RIGHT_CURLY) + n.push(Statement(t, x)); + x.stmtStack.pop(); + return n; +} + +function Block(t, x) { + t.mustMatch(LEFT_CURLY); + var n = Statements(t, x); + t.mustMatch(RIGHT_CURLY); + return n; +} + +DECLARED_FORM = 0; EXPRESSED_FORM = 1; STATEMENT_FORM = 2; + +function Statement(t, x) { + var i, label, n, n2, ss, tt = t.get(); + + // Cases for statements ending in a right curly return early, avoiding the + // common semicolon insertion magic after this switch. + switch (tt) { + case FUNCTION: + return FunctionDefinition(t, x, true, + (x.stmtStack.length > 1) + ? STATEMENT_FORM + : DECLARED_FORM); + + case LEFT_CURLY: + n = Statements(t, x); + t.mustMatch(RIGHT_CURLY); + return n; + + case IF: + n = new NarcNode(t); + n.condition = ParenExpression(t, x); + x.stmtStack.push(n); + n.thenPart = Statement(t, x); + n.elsePart = t.match(ELSE) ? Statement(t, x) : null; + x.stmtStack.pop(); + return n; + + case SWITCH: + n = new NarcNode(t); + t.mustMatch(LEFT_PAREN); + n.discriminant = Expression(t, x); + t.mustMatch(RIGHT_PAREN); + n.cases = []; + n.defaultIndex = -1; + x.stmtStack.push(n); + t.mustMatch(LEFT_CURLY); + while ((tt = t.get()) != RIGHT_CURLY) { + switch (tt) { + case DEFAULT: + if (n.defaultIndex >= 0) + throw t.newSyntaxError("More than one switch default"); + // FALL THROUGH + case CASE: + n2 = new NarcNode(t); + if (tt == DEFAULT) + n.defaultIndex = n.cases.length; + else + n2.caseLabel = Expression(t, x, COLON); + break; + default: + throw t.newSyntaxError("Invalid switch case"); + } + t.mustMatch(COLON); + n2.statements = new NarcNode(t, BLOCK); + while ((tt=t.peek()) != CASE && tt != DEFAULT && tt != RIGHT_CURLY) + n2.statements.push(Statement(t, x)); + n.cases.push(n2); + } + x.stmtStack.pop(); + return n; + + case FOR: + n = new NarcNode(t); + n.isLoop = true; + t.mustMatch(LEFT_PAREN); + if ((tt = t.peek()) != SEMICOLON) { + x.inForLoopInit = true; + if (tt == VAR || tt == CONST) { + t.get(); + n2 = Variables(t, x); + } else { + n2 = Expression(t, x); + } + x.inForLoopInit = false; + } + if (n2 && t.match(IN)) { + n.type = FOR_IN; + if (n2.type == VAR) { + if (n2.$length != 1) { + throw new SyntaxError("Invalid for..in left-hand side", + t.filename, n2.lineno); + } + + // NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name. + n.iterator = n2[0]; + n.varDecl = n2; + } else { + n.iterator = n2; + n.varDecl = null; + } + n.object = Expression(t, x); + } else { + n.setup = n2 || null; + t.mustMatch(SEMICOLON); + n.condition = (t.peek() == SEMICOLON) ? null : Expression(t, x); + t.mustMatch(SEMICOLON); + n.update = (t.peek() == RIGHT_PAREN) ? null : Expression(t, x); + } + t.mustMatch(RIGHT_PAREN); + n.body = nest(t, x, n, Statement); + return n; + + case WHILE: + n = new NarcNode(t); + n.isLoop = true; + n.condition = ParenExpression(t, x); + n.body = nest(t, x, n, Statement); + return n; + + case DO: + n = new NarcNode(t); + n.isLoop = true; + n.body = nest(t, x, n, Statement, WHILE); + n.condition = ParenExpression(t, x); + if (!x.ecmaStrictMode) { + //
    // " + document.title + "