zena 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (392) hide show
  1. data/History.txt +23 -1
  2. data/MIT-LICENSE +2 -2
  3. data/README.rdoc +2 -15
  4. data/app/controllers/acls_controller.rb +1 -1
  5. data/app/controllers/documents_controller.rb +12 -3
  6. data/app/controllers/nodes_controller.rb +1 -3
  7. data/app/controllers/sites_controller.rb +7 -0
  8. data/app/controllers/versions_controller.rb +1 -1
  9. data/app/controllers/virtual_classes_controller.rb +7 -8
  10. data/app/models/acl.rb +3 -2
  11. data/app/models/relation_proxy.rb +2 -1
  12. data/app/models/role.rb +16 -0
  13. data/app/models/site.rb +48 -0
  14. data/app/models/text_document.rb +2 -1
  15. data/app/models/user.rb +2 -1
  16. data/app/models/zip.rb +2 -1
  17. data/app/views/acls/index.rhtml +11 -1
  18. data/app/views/documents/show.rhtml +1 -1
  19. data/app/views/relations/_add.erb +2 -2
  20. data/app/views/relations/_form.erb +5 -2
  21. data/app/views/relations/_li.erb +4 -1
  22. data/app/views/relations/index.erb +3 -3
  23. data/app/views/sites/_form.erb +1 -1
  24. data/app/views/sites/_job.erb +4 -0
  25. data/app/views/sites/index.erb +1 -1
  26. data/app/views/sites/jobs.erb +10 -0
  27. data/app/views/templates/document_create_tabs/_file.rhtml +3 -0
  28. data/app/views/zafu/default/Node-+search.zafu +2 -2
  29. data/bricks/acls/zena/init.rb +2 -1
  30. data/bricks/acls/zena/test/integration/acl_integration_test.rb +5 -5
  31. data/bricks/acls/zena/test/sites/erebus/acls.yml +2 -2
  32. data/bricks/acls/zena/test/unit/acl_test.rb +2 -2
  33. data/bricks/grid/lib/bricks/grid.rb +110 -0
  34. data/bricks/grid/zena/init.rb +4 -0
  35. data/bricks/math/lib/bricks/math.rb +1 -0
  36. data/bricks/pdf/lib/bricks/pdf.rb +10 -1
  37. data/bricks/sphinx/zena/tasks.rb +8 -40
  38. data/bricks/spreadsheet/README +31 -0
  39. data/bricks/spreadsheet/lib/bricks/spreadsheet.rb +293 -0
  40. data/bricks/spreadsheet/zena/init.rb +3 -0
  41. data/bricks/spreadsheet/zena/test/sites/zena/links.yml +11 -0
  42. data/bricks/spreadsheet/zena/test/unit/xlsx_test.rb +5 -0
  43. data/bricks/spreadsheet/zena/test/zafu/README +27 -0
  44. data/bricks/spreadsheet/zena/test/zafu/spreadsheet.yml +19 -0
  45. data/bricks/worker/lib/bricks/worker.rb +11 -5
  46. data/bricks/worker/zena/init.rb +8 -0
  47. data/bricks/worker/zena/migrate/20120501091514_add_site_id_to_jobs.rb +9 -0
  48. data/bricks/zena/zena/migrate/01_base.rb +0 -7
  49. data/bricks/zena/zena/migrate/20111214112233_add_comment_to_relations.rb +9 -0
  50. data/config/bricks.yml +15 -1
  51. data/config/environments/production.rb +2 -2
  52. data/config/gems.yml +6 -1
  53. data/doc/zafu_changes.yml +1 -1
  54. data/lib/tasks/zena.rake +6 -2
  55. data/lib/zena/app.rb +1 -1
  56. data/lib/zena/code/default_syntax.rb +5 -1
  57. data/lib/zena/db_helper/mysql.rb +5 -1
  58. data/lib/zena/deploy/app_init.rhtml +11 -7
  59. data/lib/zena/deploy/awstats.conf.rhtml +1 -1
  60. data/lib/zena/deploy/start_stop.rhtml +5 -5
  61. data/lib/zena/deploy/vhost.rhtml +16 -2
  62. data/lib/zena/deploy.rb +5 -3
  63. data/lib/zena/info.rb +1 -1
  64. data/lib/zena/parser/zazen_rules.rb +7 -7
  65. data/lib/zena/remote/node.rb +7 -0
  66. data/lib/zena/routes.rb +4 -5
  67. data/lib/zena/test_controller.rb +18 -1
  68. data/lib/zena/use/action.rb +13 -4
  69. data/lib/zena/use/ajax.rb +10 -4
  70. data/lib/zena/use/authlogic.rb +1 -1
  71. data/lib/zena/use/calendar.rb +4 -0
  72. data/lib/zena/use/conditional.rb +3 -0
  73. data/lib/zena/use/dates.rb +44 -4
  74. data/lib/zena/use/display.rb +45 -15
  75. data/lib/zena/use/forms.rb +59 -30
  76. data/lib/zena/use/query_builder.rb +12 -12
  77. data/lib/zena/use/recursion.rb +1 -1
  78. data/lib/zena/use/rendering.rb +18 -17
  79. data/lib/zena/use/urls.rb +2 -1
  80. data/lib/zena/use/zafu_safe_definitions.rb +0 -9
  81. data/lib/zena/use/zafu_templates.rb +2 -2
  82. data/lib/zena/use/zazen.rb +1 -3
  83. data/lib/zena/use.rb +13 -2
  84. data/lib/zena.rb +8 -2
  85. data/public/images/bullet_back.png +0 -0
  86. data/public/javascripts/grid.js +535 -0
  87. data/public/javascripts/tablekit.js +0 -0
  88. data/public/javascripts/window.js +0 -0
  89. data/public/javascripts/zena.js +17 -7
  90. data/public/stylesheets/admin.css +5 -1
  91. data/public/stylesheets/grid.css +19 -0
  92. data/test/fixtures/files/TestNode.zafu +123 -311
  93. data/test/fixtures/files/translations_fr.yml +6 -1
  94. data/test/functional/acls_controller_test.rb +2 -2
  95. data/test/functional/nodes_controller_test.rb +17 -11
  96. data/test/functional/virtual_classes_controller_test.rb +1 -1
  97. data/test/integration/query_node/basic.yml +9 -0
  98. data/test/integration/query_node/dates.yml +1 -1
  99. data/test/integration/zafu_compiler/ajax.yml +9 -9
  100. data/test/integration/zafu_compiler/basic.yml +0 -24
  101. data/test/integration/zafu_compiler/dates.yml +24 -7
  102. data/test/integration/zafu_compiler/display.yml +45 -2
  103. data/test/integration/zafu_compiler/errors.yml +0 -6
  104. data/test/integration/zafu_compiler/forms.yml +41 -3
  105. data/test/integration/zafu_compiler/query.yml +29 -3
  106. data/test/integration/zafu_compiler/roles.yml +0 -4
  107. data/test/integration/zafu_compiler/safe_definitions.yml +1 -1
  108. data/test/integration/zafu_compiler_test.rb +11 -3
  109. data/test/selenium/Add/add3.rsel +8 -8
  110. data/test/selenium/Drop/drop1.rsel +12 -12
  111. data/test/selenium/Drop/drop2.rsel +14 -14
  112. data/test/selenium/Drop/drop3.rsel +21 -21
  113. data/test/selenium/Drop/drop4.rsel +1 -1
  114. data/test/selenium/Edit/edit2.rsel +9 -9
  115. data/test/selenium/Edit/edit4.rsel +55 -0
  116. data/test/selenium/Edit/edit5.rsel +41 -0
  117. data/test/selenium/Edit/edit6.rsel +53 -0
  118. data/test/selenium/Filter/0setup.rsel +12 -0
  119. data/test/selenium/Filter/filter1.rsel +9 -0
  120. data/test/selenium/Filter/filter2.rsel +9 -0
  121. data/test/selenium/Form/form1.rsel +1 -1
  122. data/test/selenium/Form/form2.rsel +1 -1
  123. data/test/selenium/Form/form3.rsel +15 -0
  124. data/test/selenium/Grid/0setup.rsel +13 -0
  125. data/test/selenium/Grid/grid1.rsel +11 -0
  126. data/test/selenium/Swap/0setup.rsel +12 -0
  127. data/test/selenium/Swap/swap1.rsel +13 -0
  128. data/test/selenium/Swap/swap2.rsel +11 -0
  129. data/test/selenium/Toggle/toggle1.rsel +6 -6
  130. data/test/sites/zena/versions.yml +1 -1
  131. data/test/unit/relation_proxy_test.rb +36 -0
  132. data/test/unit/site_test.rb +10 -2
  133. data/test/unit/zena/use/rendering_test.rb +7 -6
  134. data/test/unit/zena/use/zazen_test.rb +1 -2
  135. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run 2.tmCommand +24 -0
  136. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run Context.tmCommand +58 -0
  137. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run Focused Should.tmCommand +88 -0
  138. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/Run.tmCommand +27 -0
  139. data/vendor/TextMate/Ruby Shoulda.tmbundle/Commands/YAML to Shoulda.tmCommand +23 -0
  140. data/vendor/TextMate/Ruby Shoulda.tmbundle/Preferences/Symbol List: Context.tmPreferences +19 -0
  141. data/vendor/TextMate/Ruby Shoulda.tmbundle/Preferences/Symbol List: Should.tmPreferences +19 -0
  142. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory attributes for.tmSnippet +16 -0
  143. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory build.tmSnippet +16 -0
  144. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory.tmSnippet +16 -0
  145. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_define with class.tmSnippet +18 -0
  146. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_define.tmSnippet +18 -0
  147. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_next.tmSnippet +16 -0
  148. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/Factory_sequence.tmSnippet +18 -0
  149. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_bad_value.tmSnippet +16 -0
  150. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_contains.tmSnippet +16 -0
  151. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_does_not_contain.tmSnippet +16 -0
  152. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_good_value.tmSnippet +16 -0
  153. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_same_elements.tmSnippet +16 -0
  154. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_save.tmSnippet +16 -0
  155. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_sent_email.tmSnippet +18 -0
  156. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/assert_valid.tmSnippet +16 -0
  157. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/association.tmSnippet +17 -0
  158. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/attribute.tmSnippet +17 -0
  159. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/before_should block.tmSnippet +18 -0
  160. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/context block get.tmSnippet +22 -0
  161. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/context block post.tmSnippet +23 -0
  162. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/context block with setup.tmSnippet +25 -0
  163. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/setup.tmSnippet +18 -0
  164. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should block with before proc.tmSnippet +18 -0
  165. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should block.tmSnippet +18 -0
  166. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_allow_values_for.tmSnippet +16 -0
  167. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_assign_to.tmSnippet +16 -0
  168. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_be_restful denied.tmSnippet +20 -0
  169. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_be_restful.tmSnippet +20 -0
  170. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_belong_to.tmSnippet +16 -0
  171. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_change by.tmSnippet +16 -0
  172. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_change from to.tmSnippet +16 -0
  173. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_length_at_least.tmSnippet +16 -0
  174. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_length_in_range.tmSnippet +16 -0
  175. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_length_is.tmSnippet +16 -0
  176. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_ensure_value_in_range.tmSnippet +16 -0
  177. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_eventually.tmSnippet +18 -0
  178. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_filter_params.tmSnippet +16 -0
  179. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_and_belong_to_many.tmSnippet +16 -0
  180. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_class_methods.tmSnippet +16 -0
  181. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_db_column.tmSnippet +16 -0
  182. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_db_columns.tmSnippet +16 -0
  183. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_index.tmSnippet +16 -0
  184. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_indices.tmSnippet +16 -0
  185. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_instance_methods.tmSnippet +16 -0
  186. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_many.tmSnippet +16 -0
  187. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_named_scope.tmSnippet +16 -0
  188. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_one.tmSnippet +16 -0
  189. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_have_readonly_attributes.tmSnippet +16 -0
  190. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_allow_mass_assignment_of.tmSnippet +16 -0
  191. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_allow_values_for.tmSnippet +16 -0
  192. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_assign_to.tmSnippet +16 -0
  193. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_change.tmSnippet +16 -0
  194. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_not_set_the_flash.tmSnippet +16 -0
  195. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_redirect_to.tmSnippet +16 -0
  196. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_a_form.tmSnippet +16 -0
  197. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_template.tmSnippet +16 -0
  198. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_with_layout.tmSnippet +16 -0
  199. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_render_without_layout.tmSnippet +16 -0
  200. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_respond_with.tmSnippet +16 -0
  201. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_respond_with_content_type.tmSnippet +16 -0
  202. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_return_from_session.tmSnippet +16 -0
  203. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_route.tmSnippet +16 -0
  204. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_set_the_flash_to.tmSnippet +16 -0
  205. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_acceptance_of.tmSnippet +16 -0
  206. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_numericality_of.tmSnippet +16 -0
  207. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_presence_of.tmSnippet +16 -0
  208. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_uniqueness_of with scope.tmSnippet +16 -0
  209. data/vendor/TextMate/Ruby Shoulda.tmbundle/Snippets/should_validate_uniqueness_of.tmSnippet +16 -0
  210. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/catch_exception.rb +39 -0
  211. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/run_script.rb +104 -0
  212. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/stdin_dialog.rb +14 -0
  213. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/test.rb +17 -0
  214. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/RubyMate/todo.txt +13 -0
  215. data/vendor/TextMate/Ruby Shoulda.tmbundle/Support/bin/yaml_to_shoulda.rb +25 -0
  216. data/vendor/TextMate/Ruby Shoulda.tmbundle/Syntaxes/Ruby on Rails (Shoulda).tmLanguage +166 -0
  217. data/vendor/TextMate/Ruby Shoulda.tmbundle/info.plist +304 -0
  218. data/vendor/TextMate/Zena.tmbundle/Commands/Run all yaml tests.tmCommand +37 -0
  219. data/vendor/TextMate/Zena.tmbundle/Commands/Run focused yaml test.tmCommand +52 -0
  220. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/catch_exception.rb +39 -0
  221. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/run_script.rb +118 -0
  222. data/vendor/TextMate/Zena.tmbundle/Support/RubyMate/stdin_dialog.rb +14 -0
  223. data/vendor/TextMate/Zena.tmbundle/info.plist +17 -0
  224. data/vendor/plugins/selenium-on-rails/CHANGELOG +125 -0
  225. data/vendor/plugins/selenium-on-rails/LICENSE-2.0.txt +202 -0
  226. data/vendor/plugins/selenium-on-rails/README.md +202 -0
  227. data/vendor/plugins/selenium-on-rails/Rakefile +38 -0
  228. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumController.html +265 -0
  229. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumHelper.html +148 -0
  230. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/FixtureLoader.html +231 -0
  231. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/PartialsSupport.html +195 -0
  232. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Paths.html +295 -0
  233. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/RSelenese.html +219 -0
  234. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Renderer.html +156 -0
  235. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/Selenese.html +179 -0
  236. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/SuiteRenderer.html +223 -0
  237. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilder.html +441 -0
  238. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderAccessors.html +3098 -0
  239. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderActions.html +2080 -0
  240. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderUserAccessors.html +116 -0
  241. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderUserActions.html +116 -0
  242. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRails.html +126 -0
  243. data/vendor/plugins/selenium-on-rails/doc/classes/SeleniumOnRailsConfig.html +150 -0
  244. data/vendor/plugins/selenium-on-rails/doc/files/CHANGELOG.html +422 -0
  245. data/vendor/plugins/selenium-on-rails/doc/files/README.html +321 -0
  246. data/vendor/plugins/selenium-on-rails/doc/files/lib/controllers/selenium_controller_rb.html +108 -0
  247. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_helper_rb.html +101 -0
  248. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/acceptance_test_runner_rb.html +222 -0
  249. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/fixture_loader_rb.html +109 -0
  250. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/partials_support_rb.html +111 -0
  251. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/paths_rb.html +101 -0
  252. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/renderer_rb.html +101 -0
  253. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/rselenese_rb.html +118 -0
  254. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/selenese_rb.html +101 -0
  255. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/suite_renderer_rb.html +101 -0
  256. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_accessors_rb.html +114 -0
  257. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_actions_rb.html +113 -0
  258. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails/test_builder_rb.html +120 -0
  259. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_config_rb.html +108 -0
  260. data/vendor/plugins/selenium-on-rails/doc/files/lib/selenium_on_rails_rb.html +115 -0
  261. data/vendor/plugins/selenium-on-rails/doc/fr_class_index.html +42 -0
  262. data/vendor/plugins/selenium-on-rails/doc/fr_file_index.html +43 -0
  263. data/vendor/plugins/selenium-on-rails/doc/fr_method_index.html +182 -0
  264. data/vendor/plugins/selenium-on-rails/doc/index.html +24 -0
  265. data/vendor/plugins/selenium-on-rails/doc/rdoc-style.css +208 -0
  266. data/vendor/plugins/selenium-on-rails/generators/selenium/USAGE +19 -0
  267. data/vendor/plugins/selenium-on-rails/generators/selenium/selenium_generator.rb +50 -0
  268. data/vendor/plugins/selenium-on-rails/generators/selenium/templates/rhtml.rhtml +16 -0
  269. data/vendor/plugins/selenium-on-rails/generators/selenium/templates/rselenese.rhtml +14 -0
  270. data/vendor/plugins/selenium-on-rails/generators/selenium/templates/selenese.rhtml +11 -0
  271. data/vendor/plugins/selenium-on-rails/init.rb +15 -0
  272. data/vendor/plugins/selenium-on-rails/lib/controllers/selenium_controller.rb +122 -0
  273. data/vendor/plugins/selenium-on-rails/lib/controllers/switch_environment_controller.rb +16 -0
  274. data/vendor/plugins/selenium-on-rails/lib/selenium_helper.rb +8 -0
  275. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/acceptance_test_runner.rb +215 -0
  276. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/fixture_loader.rb +57 -0
  277. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/partials_support.rb +36 -0
  278. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/paths.rb +61 -0
  279. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/renderer.rb +20 -0
  280. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/rselenese.rb +44 -0
  281. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/selenese.rb +87 -0
  282. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/suite_renderer.rb +56 -0
  283. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder.rb +116 -0
  284. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_accessors.rb +1002 -0
  285. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_actions.rb +514 -0
  286. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_user_accessors.rb.example +91 -0
  287. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails/test_builder_user_actions.rb.example +24 -0
  288. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails.rb +11 -0
  289. data/vendor/plugins/selenium-on-rails/lib/selenium_on_rails_config.rb +30 -0
  290. data/vendor/plugins/selenium-on-rails/lib/views/layouts/layout.rhtml +18 -0
  291. data/vendor/plugins/selenium-on-rails/lib/views/record.rhtml +5 -0
  292. data/vendor/plugins/selenium-on-rails/lib/views/selenium_helper.rb +9 -0
  293. data/vendor/plugins/selenium-on-rails/lib/views/setup.rhtml +67 -0
  294. data/vendor/plugins/selenium-on-rails/lib/views/test_suite.rhtml +26 -0
  295. data/vendor/plugins/selenium-on-rails/routes.rb +24 -0
  296. data/vendor/plugins/selenium-on-rails/selenium-core/Blank.html +7 -0
  297. data/vendor/plugins/selenium-on-rails/selenium-core/InjectedRemoteRunner.html +8 -0
  298. data/vendor/plugins/selenium-on-rails/selenium-core/RemoteRunner.html +110 -0
  299. data/vendor/plugins/selenium-on-rails/selenium-core/SeleniumLog.html +109 -0
  300. data/vendor/plugins/selenium-on-rails/selenium-core/TestPrompt.html +145 -0
  301. data/vendor/plugins/selenium-on-rails/selenium-core/TestRunner-splash.html +55 -0
  302. data/vendor/plugins/selenium-on-rails/selenium-core/TestRunner.hta +177 -0
  303. data/vendor/plugins/selenium-on-rails/selenium-core/TestRunner.html +177 -0
  304. data/vendor/plugins/selenium-on-rails/selenium-core/domviewer/butmin.gif +0 -0
  305. data/vendor/plugins/selenium-on-rails/selenium-core/domviewer/butplus.gif +0 -0
  306. data/vendor/plugins/selenium-on-rails/selenium-core/domviewer/domviewer.css +298 -0
  307. data/vendor/plugins/selenium-on-rails/selenium-core/domviewer/domviewer.html +16 -0
  308. data/vendor/plugins/selenium-on-rails/selenium-core/domviewer/selenium-domviewer.js +205 -0
  309. data/vendor/plugins/selenium-on-rails/selenium-core/icons/all.png +0 -0
  310. data/vendor/plugins/selenium-on-rails/selenium-core/icons/continue.png +0 -0
  311. data/vendor/plugins/selenium-on-rails/selenium-core/icons/continue_disabled.png +0 -0
  312. data/vendor/plugins/selenium-on-rails/selenium-core/icons/pause.png +0 -0
  313. data/vendor/plugins/selenium-on-rails/selenium-core/icons/pause_disabled.png +0 -0
  314. data/vendor/plugins/selenium-on-rails/selenium-core/icons/selected.png +0 -0
  315. data/vendor/plugins/selenium-on-rails/selenium-core/icons/step.png +0 -0
  316. data/vendor/plugins/selenium-on-rails/selenium-core/icons/step_disabled.png +0 -0
  317. data/vendor/plugins/selenium-on-rails/selenium-core/iedoc-core.xml +1759 -0
  318. data/vendor/plugins/selenium-on-rails/selenium-core/iedoc.xml +1800 -0
  319. data/vendor/plugins/selenium-on-rails/selenium-core/lib/cssQuery/cssQuery-p.js +6 -0
  320. data/vendor/plugins/selenium-on-rails/selenium-core/lib/cssQuery/src/cssQuery-level2.js +142 -0
  321. data/vendor/plugins/selenium-on-rails/selenium-core/lib/cssQuery/src/cssQuery-level3.js +150 -0
  322. data/vendor/plugins/selenium-on-rails/selenium-core/lib/cssQuery/src/cssQuery-standard.js +53 -0
  323. data/vendor/plugins/selenium-on-rails/selenium-core/lib/cssQuery/src/cssQuery.js +356 -0
  324. data/vendor/plugins/selenium-on-rails/selenium-core/lib/prototype.js +2006 -0
  325. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/builder.js +101 -0
  326. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/controls.js +815 -0
  327. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/dragdrop.js +915 -0
  328. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/effects.js +958 -0
  329. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/scriptaculous.js +47 -0
  330. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/slider.js +283 -0
  331. data/vendor/plugins/selenium-on-rails/selenium-core/lib/scriptaculous/unittest.js +383 -0
  332. data/vendor/plugins/selenium-on-rails/selenium-core/lib/snapsie.js +91 -0
  333. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/find_matching_child.js +69 -0
  334. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/htmlutils.js +1616 -0
  335. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/injection.html +72 -0
  336. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-api.js +3184 -0
  337. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-browserbot.js +2300 -0
  338. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-browserdetect.js +153 -0
  339. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-commandhandlers.js +377 -0
  340. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-executionloop.js +175 -0
  341. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-logging.js +148 -0
  342. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-remoterunner.js +695 -0
  343. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-testrunner.js +1362 -0
  344. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/selenium-version.js +5 -0
  345. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/ui-doc.html +803 -0
  346. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/ui-element.js +1537 -0
  347. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/ui-map-sample.js +979 -0
  348. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/user-extensions.js +3 -0
  349. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/user-extensions.js.sample +75 -0
  350. data/vendor/plugins/selenium-on-rails/selenium-core/scripts/xmlextras.js +153 -0
  351. data/vendor/plugins/selenium-on-rails/selenium-core/selenium-logo.png +0 -0
  352. data/vendor/plugins/selenium-on-rails/selenium-core/selenium-test.css +43 -0
  353. data/vendor/plugins/selenium-on-rails/selenium-core/selenium.css +316 -0
  354. data/vendor/plugins/selenium-on-rails/selenium-core/xpath/dom.js +566 -0
  355. data/vendor/plugins/selenium-on-rails/selenium-core/xpath/javascript-xpath-0.1.11.js +2816 -0
  356. data/vendor/plugins/selenium-on-rails/selenium-core/xpath/util.js +549 -0
  357. data/vendor/plugins/selenium-on-rails/selenium-core/xpath/xmltoken.js +149 -0
  358. data/vendor/plugins/selenium-on-rails/selenium-core/xpath/xpath.js +2450 -0
  359. data/vendor/plugins/selenium-on-rails/tasks/test_acceptance.rake +8 -0
  360. data/vendor/plugins/selenium-on-rails/test/fixtures/config.yml +37 -0
  361. data/vendor/plugins/selenium-on-rails/test/fixtures/selenium.yml +27 -0
  362. data/vendor/plugins/selenium-on-rails/test/paths_test.rb +72 -0
  363. data/vendor/plugins/selenium-on-rails/test/renderer_test.rb +157 -0
  364. data/vendor/plugins/selenium-on-rails/test/rselenese_test.rb +708 -0
  365. data/vendor/plugins/selenium-on-rails/test/selenese_test.rb +242 -0
  366. data/vendor/plugins/selenium-on-rails/test/selenium_controller_test.rb +67 -0
  367. data/vendor/plugins/selenium-on-rails/test/selenium_on_rails_config_test.rb +43 -0
  368. data/vendor/plugins/selenium-on-rails/test/selenium_support_test.rb +35 -0
  369. data/vendor/plugins/selenium-on-rails/test/setup_test.rb +31 -0
  370. data/vendor/plugins/selenium-on-rails/test/suite_renderer_test.rb +109 -0
  371. data/vendor/plugins/selenium-on-rails/test/switch_environment_controller_test.rb +17 -0
  372. data/vendor/plugins/selenium-on-rails/test/test_builder_functions_authortest.rb +51 -0
  373. data/vendor/plugins/selenium-on-rails/test/test_helper.rb +101 -0
  374. data/vendor/plugins/selenium-on-rails/test_data/_partial.rsel +1 -0
  375. data/vendor/plugins/selenium-on-rails/test_data/own_layout.html +12 -0
  376. data/vendor/plugins/selenium-on-rails/test_data/partials/_html.html +6 -0
  377. data/vendor/plugins/selenium-on-rails/test_data/partials/_nesting.rsel +2 -0
  378. data/vendor/plugins/selenium-on-rails/test_data/partials/_rhtml.rhtml +6 -0
  379. data/vendor/plugins/selenium-on-rails/test_data/partials/_rsel.rsel +1 -0
  380. data/vendor/plugins/selenium-on-rails/test_data/partials/_sel.sel +5 -0
  381. data/vendor/plugins/selenium-on-rails/test_data/partials/all_partials.rsel +5 -0
  382. data/vendor/plugins/selenium-on-rails/test_data/rhtml.rhtml +7 -0
  383. data/vendor/plugins/selenium-on-rails/test_data/rselenese.rsel +8 -0
  384. data/vendor/plugins/selenium-on-rails/test_data/selenese.sel +7 -0
  385. data/vendor/plugins/selenium-on-rails/test_data/suite_one/subsuite/suite_one_subsuite_testcase.sel +1 -0
  386. data/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase1.sel +1 -0
  387. data/vendor/plugins/selenium-on-rails/test_data/suite_one/suite_one_testcase2.sel +1 -0
  388. data/vendor/plugins/selenium-on-rails/test_data/suite_two/suite_two_testcase.sel +1 -0
  389. data/zena.gemspec +290 -7
  390. metadata +346 -52
  391. data/app/views/nodes/_table.rhtml +0 -14
  392. data/lib/zena/use/grid.rb +0 -154
@@ -0,0 +1,1616 @@
1
+ /*
2
+ * Copyright 2004 ThoughtWorks, Inc
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ // This script contains a badly-organised collection of miscellaneous
19
+ // functions that really better homes.
20
+
21
+ function classCreate() {
22
+ return function() {
23
+ this.initialize.apply(this, arguments);
24
+ }
25
+ }
26
+
27
+ function objectExtend(destination, source) {
28
+ for (var property in source) {
29
+ destination[property] = source[property];
30
+ }
31
+ return destination;
32
+ }
33
+
34
+ function sel$() {
35
+ var results = [], element;
36
+ for (var i = 0; i < arguments.length; i++) {
37
+ element = arguments[i];
38
+ if (typeof element == 'string')
39
+ element = document.getElementById(element);
40
+ results[results.length] = element;
41
+ }
42
+ return results.length < 2 ? results[0] : results;
43
+ }
44
+
45
+ function sel$A(iterable) {
46
+ if (!iterable) return [];
47
+ if (iterable.toArray) {
48
+ return iterable.toArray();
49
+ } else {
50
+ var results = [];
51
+ for (var i = 0; i < iterable.length; i++)
52
+ results.push(iterable[i]);
53
+ return results;
54
+ }
55
+ }
56
+
57
+ function fnBind() {
58
+ var args = sel$A(arguments), __method = args.shift(), object = args.shift();
59
+ var retval = function() {
60
+ return __method.apply(object, args.concat(sel$A(arguments)));
61
+ }
62
+ retval.__method = __method;
63
+ return retval;
64
+ }
65
+
66
+ function fnBindAsEventListener(fn, object) {
67
+ var __method = fn;
68
+ return function(event) {
69
+ return __method.call(object, event || window.event);
70
+ }
71
+ }
72
+
73
+ function removeClassName(element, name) {
74
+ var re = new RegExp("\\b" + name + "\\b", "g");
75
+ element.className = element.className.replace(re, "");
76
+ }
77
+
78
+ function addClassName(element, name) {
79
+ element.className = element.className + ' ' + name;
80
+ }
81
+
82
+ function elementSetStyle(element, style) {
83
+ for (var name in style) {
84
+ var value = style[name];
85
+ if (value == null) value = "";
86
+ element.style[name] = value;
87
+ }
88
+ }
89
+
90
+ function elementGetStyle(element, style) {
91
+ var value = element.style[style];
92
+ if (!value) {
93
+ if (document.defaultView && document.defaultView.getComputedStyle) {
94
+ var css = document.defaultView.getComputedStyle(element, null);
95
+ value = css ? css.getPropertyValue(style) : null;
96
+ } else if (element.currentStyle) {
97
+ value = element.currentStyle[style];
98
+ }
99
+ }
100
+
101
+ /** DGF necessary?
102
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
103
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto'; */
104
+
105
+ return value == 'auto' ? null : value;
106
+ }
107
+
108
+ String.prototype.trim = function() {
109
+ var result = this.replace(/^\s+/g, "");
110
+ // strip leading
111
+ return result.replace(/\s+$/g, "");
112
+ // strip trailing
113
+ };
114
+ String.prototype.lcfirst = function() {
115
+ return this.charAt(0).toLowerCase() + this.substr(1);
116
+ };
117
+ String.prototype.ucfirst = function() {
118
+ return this.charAt(0).toUpperCase() + this.substr(1);
119
+ };
120
+ String.prototype.startsWith = function(str) {
121
+ return this.indexOf(str) == 0;
122
+ };
123
+
124
+ /**
125
+ * Given a string literal that would appear in an XPath, puts it in quotes and
126
+ * returns it. Special consideration is given to literals who themselves
127
+ * contain quotes. It's possible for a concat() expression to be returned.
128
+ */
129
+ String.prototype.quoteForXPath = function()
130
+ {
131
+ if (/\'/.test(this)) {
132
+ if (/\"/.test(this)) {
133
+ // concat scenario
134
+ var pieces = [];
135
+ var a = "'", b = '"', c;
136
+ for (var i = 0, j = 0; i < this.length;) {
137
+ if (this.charAt(i) == a) {
138
+ // encountered a quote that cannot be contained in current
139
+ // quote, so need to flip-flop quoting scheme
140
+ if (j < i) {
141
+ pieces.push(a + this.substring(j, i) + a);
142
+ j = i;
143
+ }
144
+ c = a;
145
+ a = b;
146
+ b = c;
147
+ }
148
+ else {
149
+ ++i;
150
+ }
151
+ }
152
+ pieces.push(a + this.substring(j) + a);
153
+ return 'concat(' + pieces.join(', ') + ')';
154
+ }
155
+ else {
156
+ // quote with doubles
157
+ return '"' + this + '"';
158
+ }
159
+ }
160
+ // quote with singles
161
+ return "'" + this + "'";
162
+ };
163
+
164
+ // Returns the text in this element
165
+ function getText(element) {
166
+ var text = "";
167
+
168
+ var isRecentFirefox = (browserVersion.isFirefox && browserVersion.firefoxVersion >= "1.5");
169
+ if (isRecentFirefox || browserVersion.isKonqueror || browserVersion.isSafari || browserVersion.isOpera) {
170
+ text = getTextContent(element);
171
+ } else if (element.textContent) {
172
+ text = element.textContent;
173
+ } else if (element.innerText) {
174
+ text = element.innerText;
175
+ }
176
+
177
+ text = normalizeNewlines(text);
178
+ text = normalizeSpaces(text);
179
+
180
+ return text.trim();
181
+ }
182
+
183
+ function getTextContent(element, preformatted) {
184
+ if (element.nodeType == 3 /*Node.TEXT_NODE*/) {
185
+ var text = element.data;
186
+ if (!preformatted) {
187
+ text = text.replace(/\n|\r|\t/g, " ");
188
+ }
189
+ return text;
190
+ }
191
+ if (element.nodeType == 1 /*Node.ELEMENT_NODE*/) {
192
+ var childrenPreformatted = preformatted || (element.tagName == "PRE");
193
+ var text = "";
194
+ for (var i = 0; i < element.childNodes.length; i++) {
195
+ var child = element.childNodes.item(i);
196
+ text += getTextContent(child, childrenPreformatted);
197
+ }
198
+ // Handle block elements that introduce newlines
199
+ // -- From HTML spec:
200
+ //<!ENTITY % block
201
+ // "P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT |
202
+ // BLOCKQUOTE | F:wORM | HR | TABLE | FIELDSET | ADDRESS">
203
+ //
204
+ // TODO: should potentially introduce multiple newlines to separate blocks
205
+ if (element.tagName == "P" || element.tagName == "BR" || element.tagName == "HR" || element.tagName == "DIV") {
206
+ text += "\n";
207
+ }
208
+ return text;
209
+ }
210
+ return '';
211
+ }
212
+
213
+ /**
214
+ * Convert all newlines to \n
215
+ */
216
+ function normalizeNewlines(text)
217
+ {
218
+ return text.replace(/\r\n|\r/g, "\n");
219
+ }
220
+
221
+ /**
222
+ * Replace multiple sequential spaces with a single space, and then convert &nbsp; to space.
223
+ */
224
+ function normalizeSpaces(text)
225
+ {
226
+ // IE has already done this conversion, so doing it again will remove multiple nbsp
227
+ if (browserVersion.isIE)
228
+ {
229
+ return text;
230
+ }
231
+
232
+ // Replace multiple spaces with a single space
233
+ // TODO - this shouldn't occur inside PRE elements
234
+ text = text.replace(/\ +/g, " ");
235
+
236
+ // Replace &nbsp; with a space
237
+ var nbspPattern = new RegExp(String.fromCharCode(160), "g");
238
+ if (browserVersion.isSafari) {
239
+ return replaceAll(text, String.fromCharCode(160), " ");
240
+ } else {
241
+ return text.replace(nbspPattern, " ");
242
+ }
243
+ }
244
+
245
+ function replaceAll(text, oldText, newText) {
246
+ while (text.indexOf(oldText) != -1) {
247
+ text = text.replace(oldText, newText);
248
+ }
249
+ return text;
250
+ }
251
+
252
+
253
+ function xmlDecode(text) {
254
+ text = text.replace(/&quot;/g, '"');
255
+ text = text.replace(/&apos;/g, "'");
256
+ text = text.replace(/&lt;/g, "<");
257
+ text = text.replace(/&gt;/g, ">");
258
+ text = text.replace(/&amp;/g, "&");
259
+ return text;
260
+ }
261
+
262
+ // Sets the text in this element
263
+ function setText(element, text) {
264
+ if (element.textContent != null) {
265
+ element.textContent = text;
266
+ } else if (element.innerText != null) {
267
+ element.innerText = text;
268
+ }
269
+ }
270
+
271
+ // Get the value of an <input> element
272
+ function getInputValue(inputElement) {
273
+ if (inputElement.type) {
274
+ if (inputElement.type.toUpperCase() == 'CHECKBOX' ||
275
+ inputElement.type.toUpperCase() == 'RADIO')
276
+ {
277
+ return (inputElement.checked ? 'on' : 'off');
278
+ }
279
+ }
280
+ if (inputElement.value == null) {
281
+ throw new SeleniumError("This element has no value; is it really a form field?");
282
+ }
283
+ return inputElement.value;
284
+ }
285
+
286
+ /* Fire an event in a browser-compatible manner */
287
+ function triggerEvent(element, eventType, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
288
+ canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
289
+ if (element.fireEvent && element.ownerDocument && element.ownerDocument.createEventObject) { // IE
290
+ var evt = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
291
+ element.fireEvent('on' + eventType, evt);
292
+ }
293
+ else {
294
+ var evt = document.createEvent('HTMLEvents');
295
+
296
+ try {
297
+ evt.shiftKey = shiftKeyDown;
298
+ evt.metaKey = metaKeyDown;
299
+ evt.altKey = altKeyDown;
300
+ evt.ctrlKey = controlKeyDown;
301
+ } catch (e) {
302
+ // On Firefox 1.0, you can only set these during initMouseEvent or initKeyEvent
303
+ // we'll have to ignore them here
304
+ LOG.exception(e);
305
+ }
306
+
307
+ evt.initEvent(eventType, canBubble, true);
308
+ element.dispatchEvent(evt);
309
+ }
310
+ }
311
+
312
+ function getKeyCodeFromKeySequence(keySequence) {
313
+ var match = /^\\(\d{1,3})$/.exec(keySequence);
314
+ if (match != null) {
315
+ return match[1];
316
+ }
317
+ match = /^.$/.exec(keySequence);
318
+ if (match != null) {
319
+ return match[0].charCodeAt(0);
320
+ }
321
+ // this is for backward compatibility with existing tests
322
+ // 1 digit ascii codes will break however because they are used for the digit chars
323
+ match = /^\d{2,3}$/.exec(keySequence);
324
+ if (match != null) {
325
+ return match[0];
326
+ }
327
+ throw new SeleniumError("invalid keySequence");
328
+ }
329
+
330
+ function createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
331
+ var evt = element.ownerDocument.createEventObject();
332
+ evt.shiftKey = shiftKeyDown;
333
+ evt.metaKey = metaKeyDown;
334
+ evt.altKey = altKeyDown;
335
+ evt.ctrlKey = controlKeyDown;
336
+ return evt;
337
+ }
338
+
339
+ function triggerKeyEvent(element, eventType, keySequence, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
340
+ var keycode = getKeyCodeFromKeySequence(keySequence);
341
+ canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
342
+ if (element.fireEvent && element.ownerDocument && element.ownerDocument.createEventObject) { // IE
343
+ var keyEvent = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
344
+ keyEvent.keyCode = keycode;
345
+ element.fireEvent('on' + eventType, keyEvent);
346
+ }
347
+ else {
348
+ var evt;
349
+ if (window.KeyEvent) {
350
+ evt = document.createEvent('KeyEvents');
351
+ evt.initKeyEvent(eventType, true, true, window, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown, keycode, keycode);
352
+ } else {
353
+ evt = document.createEvent('UIEvents');
354
+
355
+ evt.shiftKey = shiftKeyDown;
356
+ evt.metaKey = metaKeyDown;
357
+ evt.altKey = altKeyDown;
358
+ evt.ctrlKey = controlKeyDown;
359
+
360
+ evt.initUIEvent(eventType, true, true, window, 1);
361
+ evt.keyCode = keycode;
362
+ evt.which = keycode;
363
+ }
364
+
365
+ element.dispatchEvent(evt);
366
+ }
367
+ }
368
+
369
+ function removeLoadListener(element, command) {
370
+ LOG.debug('Removing loadListenter for ' + element + ', ' + command);
371
+ if (window.removeEventListener)
372
+ element.removeEventListener("load", command, true);
373
+ else if (window.detachEvent)
374
+ element.detachEvent("onload", command);
375
+ }
376
+
377
+ function addLoadListener(element, command) {
378
+ LOG.debug('Adding loadListenter for ' + element + ', ' + command);
379
+ var augmentedCommand = function() {
380
+ command.call(this, element);
381
+ }
382
+ if (window.addEventListener && !browserVersion.isOpera)
383
+ element.addEventListener("load", augmentedCommand, true);
384
+ else if (window.attachEvent)
385
+ element.attachEvent("onload", augmentedCommand);
386
+ }
387
+
388
+ /**
389
+ * Override the broken getFunctionName() method from JsUnit
390
+ * This file must be loaded _after_ the jsunitCore.js
391
+ */
392
+ function getFunctionName(aFunction) {
393
+ var regexpResult = aFunction.toString().match(/function (\w*)/);
394
+ if (regexpResult && regexpResult[1]) {
395
+ return regexpResult[1];
396
+ }
397
+ return 'anonymous';
398
+ }
399
+
400
+ function getDocumentBase(doc) {
401
+ var bases = document.getElementsByTagName("base");
402
+ if (bases && bases.length && bases[0].href) {
403
+ return bases[0].href;
404
+ }
405
+ return "";
406
+ }
407
+
408
+ function getTagName(element) {
409
+ var tagName;
410
+ if (element && element.tagName && element.tagName.toLowerCase) {
411
+ tagName = element.tagName.toLowerCase();
412
+ }
413
+ return tagName;
414
+ }
415
+
416
+ function selArrayToString(a) {
417
+ if (isArray(a)) {
418
+ // DGF copying the array, because the array-like object may be a non-modifiable nodelist
419
+ var retval = [];
420
+ for (var i = 0; i < a.length; i++) {
421
+ var item = a[i];
422
+ var replaced = new String(item).replace(/([,\\])/g, '\\$1');
423
+ retval[i] = replaced;
424
+ }
425
+ return retval;
426
+ }
427
+ return new String(a);
428
+ }
429
+
430
+
431
+ function isArray(x) {
432
+ return ((typeof x) == "object") && (x["length"] != null);
433
+ }
434
+
435
+ function absolutify(url, baseUrl) {
436
+ /** returns a relative url in its absolute form, given by baseUrl.
437
+ *
438
+ * This function is a little odd, because it can take baseUrls that
439
+ * aren't necessarily directories. It uses the same rules as the HTML
440
+ * &lt;base&gt; tag; if the baseUrl doesn't end with "/", we'll assume
441
+ * that it points to a file, and strip the filename off to find its
442
+ * base directory.
443
+ *
444
+ * So absolutify("foo", "http://x/bar") will return "http://x/foo" (stripping off bar),
445
+ * whereas absolutify("foo", "http://x/bar/") will return "http://x/bar/foo" (preserving bar).
446
+ * Naturally absolutify("foo", "http://x") will return "http://x/foo", appropriately.
447
+ *
448
+ * @param url the url to make absolute; if this url is already absolute, we'll just return that, unchanged
449
+ * @param baseUrl the baseUrl from which we'll absolutify, following the rules above.
450
+ * @return 'url' if it was already absolute, or the absolutized version of url if it was not absolute.
451
+ */
452
+
453
+ // DGF isn't there some library we could use for this?
454
+
455
+ if (/^\w+:/.test(url)) {
456
+ // it's already absolute
457
+ return url;
458
+ }
459
+
460
+ var loc;
461
+ try {
462
+ loc = parseUrl(baseUrl);
463
+ } catch (e) {
464
+ // is it an absolute windows file path? let's play the hero in that case
465
+ if (/^\w:\\/.test(baseUrl)) {
466
+ baseUrl = "file:///" + baseUrl.replace(/\\/g, "/");
467
+ loc = parseUrl(baseUrl);
468
+ } else {
469
+ throw new SeleniumError("baseUrl wasn't absolute: " + baseUrl);
470
+ }
471
+ }
472
+ loc.search = null;
473
+ loc.hash = null;
474
+
475
+ // if url begins with /, then that's the whole pathname
476
+ if (/^\//.test(url)) {
477
+ loc.pathname = url;
478
+ var result = reassembleLocation(loc);
479
+ return result;
480
+ }
481
+
482
+ // if pathname is null, then we'll just append "/" + the url
483
+ if (!loc.pathname) {
484
+ loc.pathname = "/" + url;
485
+ var result = reassembleLocation(loc);
486
+ return result;
487
+ }
488
+
489
+ // if pathname ends with /, just append url
490
+ if (/\/$/.test(loc.pathname)) {
491
+ loc.pathname += url;
492
+ var result = reassembleLocation(loc);
493
+ return result;
494
+ }
495
+
496
+ // if we're here, then the baseUrl has a pathname, but it doesn't end with /
497
+ // in that case, we replace everything after the final / with the relative url
498
+ loc.pathname = loc.pathname.replace(/[^\/\\]+$/, url);
499
+ var result = reassembleLocation(loc);
500
+ return result;
501
+
502
+ }
503
+
504
+ var URL_REGEX = /^((\w+):\/\/)(([^:]+):?([^@]+)?@)?([^\/\?:]*):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(.+)?/;
505
+
506
+ function parseUrl(url) {
507
+ var fields = ['url', null, 'protocol', null, 'username', 'password', 'host', 'port', 'pathname', 'search', 'hash'];
508
+ var result = URL_REGEX.exec(url);
509
+ if (!result) {
510
+ throw new SeleniumError("Invalid URL: " + url);
511
+ }
512
+ var loc = new Object();
513
+ for (var i = 0; i < fields.length; i++) {
514
+ var field = fields[i];
515
+ if (field == null) {
516
+ continue;
517
+ }
518
+ loc[field] = result[i];
519
+ }
520
+ return loc;
521
+ }
522
+
523
+ function reassembleLocation(loc) {
524
+ if (!loc.protocol) {
525
+ throw new Error("Not a valid location object: " + o2s(loc));
526
+ }
527
+ var protocol = loc.protocol;
528
+ protocol = protocol.replace(/:$/, "");
529
+ var url = protocol + "://";
530
+ if (loc.username) {
531
+ url += loc.username;
532
+ if (loc.password) {
533
+ url += ":" + loc.password;
534
+ }
535
+ url += "@";
536
+ }
537
+ if (loc.host) {
538
+ url += loc.host;
539
+ }
540
+
541
+ if (loc.port) {
542
+ url += ":" + loc.port;
543
+ }
544
+
545
+ if (loc.pathname) {
546
+ url += loc.pathname;
547
+ }
548
+
549
+ if (loc.search) {
550
+ url += "?" + loc.search;
551
+ }
552
+ if (loc.hash) {
553
+ var hash = loc.hash;
554
+ hash = loc.hash.replace(/^#/, "");
555
+ url += "#" + hash;
556
+ }
557
+ return url;
558
+ }
559
+
560
+ function canonicalize(url) {
561
+ if(url == "about:blank")
562
+ {
563
+ return url;
564
+ }
565
+ var tempLink = window.document.createElement("link");
566
+ tempLink.href = url; // this will canonicalize the href on most browsers
567
+ var loc = parseUrl(tempLink.href)
568
+ if (!/\/\.\.\//.test(loc.pathname)) {
569
+ return tempLink.href;
570
+ }
571
+ // didn't work... let's try it the hard way
572
+ var originalParts = loc.pathname.split("/");
573
+ var newParts = [];
574
+ newParts.push(originalParts.shift());
575
+ for (var i = 0; i < originalParts.length; i++) {
576
+ var part = originalParts[i];
577
+ if (".." == part) {
578
+ newParts.pop();
579
+ continue;
580
+ }
581
+ newParts.push(part);
582
+ }
583
+ loc.pathname = newParts.join("/");
584
+ return reassembleLocation(loc);
585
+ }
586
+
587
+ function extractExceptionMessage(ex) {
588
+ if (ex == null) return "null exception";
589
+ if (ex.message != null) return ex.message;
590
+ if (ex.toString && ex.toString() != null) return ex.toString();
591
+ }
592
+
593
+
594
+ function describe(object, delimiter) {
595
+ var props = new Array();
596
+ for (var prop in object) {
597
+ try {
598
+ props.push(prop + " -> " + object[prop]);
599
+ } catch (e) {
600
+ props.push(prop + " -> [htmlutils: ack! couldn't read this property! (Permission Denied?)]");
601
+ }
602
+ }
603
+ return props.join(delimiter || '\n');
604
+ }
605
+
606
+ var PatternMatcher = function(pattern) {
607
+ this.selectStrategy(pattern);
608
+ };
609
+ PatternMatcher.prototype = {
610
+
611
+ selectStrategy: function(pattern) {
612
+ this.pattern = pattern;
613
+ var strategyName = 'glob';
614
+ // by default
615
+ if (/^([a-z-]+):(.*)/.test(pattern)) {
616
+ var possibleNewStrategyName = RegExp.$1;
617
+ var possibleNewPattern = RegExp.$2;
618
+ if (PatternMatcher.strategies[possibleNewStrategyName]) {
619
+ strategyName = possibleNewStrategyName;
620
+ pattern = possibleNewPattern;
621
+ }
622
+ }
623
+ var matchStrategy = PatternMatcher.strategies[strategyName];
624
+ if (!matchStrategy) {
625
+ throw new SeleniumError("cannot find PatternMatcher.strategies." + strategyName);
626
+ }
627
+ this.strategy = matchStrategy;
628
+ this.matcher = new matchStrategy(pattern);
629
+ },
630
+
631
+ matches: function(actual) {
632
+ return this.matcher.matches(actual + '');
633
+ // Note: appending an empty string avoids a Konqueror bug
634
+ }
635
+
636
+ };
637
+
638
+ /**
639
+ * A "static" convenience method for easy matching
640
+ */
641
+ PatternMatcher.matches = function(pattern, actual) {
642
+ return new PatternMatcher(pattern).matches(actual);
643
+ };
644
+
645
+ PatternMatcher.strategies = {
646
+
647
+ /**
648
+ * Exact matching, e.g. "exact:***"
649
+ */
650
+ exact: function(expected) {
651
+ this.expected = expected;
652
+ this.matches = function(actual) {
653
+ return actual == this.expected;
654
+ };
655
+ },
656
+
657
+ /**
658
+ * Match by regular expression, e.g. "regexp:^[0-9]+$"
659
+ */
660
+ regexp: function(regexpString) {
661
+ this.regexp = new RegExp(regexpString);
662
+ this.matches = function(actual) {
663
+ return this.regexp.test(actual);
664
+ };
665
+ },
666
+
667
+ regex: function(regexpString) {
668
+ this.regexp = new RegExp(regexpString);
669
+ this.matches = function(actual) {
670
+ return this.regexp.test(actual);
671
+ };
672
+ },
673
+
674
+ regexpi: function(regexpString) {
675
+ this.regexp = new RegExp(regexpString, "i");
676
+ this.matches = function(actual) {
677
+ return this.regexp.test(actual);
678
+ };
679
+ },
680
+
681
+ regexi: function(regexpString) {
682
+ this.regexp = new RegExp(regexpString, "i");
683
+ this.matches = function(actual) {
684
+ return this.regexp.test(actual);
685
+ };
686
+ },
687
+
688
+ /**
689
+ * "globContains" (aka "wildmat") patterns, e.g. "glob:one,two,*",
690
+ * but don't require a perfect match; instead succeed if actual
691
+ * contains something that matches globString.
692
+ * Making this distinction is motivated by a bug in IE6 which
693
+ * leads to the browser hanging if we implement *TextPresent tests
694
+ * by just matching against a regular expression beginning and
695
+ * ending with ".*". The globcontains strategy allows us to satisfy
696
+ * the functional needs of the *TextPresent ops more efficiently
697
+ * and so avoid running into this IE6 freeze.
698
+ */
699
+ globContains: function(globString) {
700
+ this.regexp = new RegExp(PatternMatcher.regexpFromGlobContains(globString));
701
+ this.matches = function(actual) {
702
+ return this.regexp.test(actual);
703
+ };
704
+ },
705
+
706
+
707
+ /**
708
+ * "glob" (aka "wildmat") patterns, e.g. "glob:one,two,*"
709
+ */
710
+ glob: function(globString) {
711
+ this.regexp = new RegExp(PatternMatcher.regexpFromGlob(globString));
712
+ this.matches = function(actual) {
713
+ return this.regexp.test(actual);
714
+ };
715
+ }
716
+
717
+ };
718
+
719
+ PatternMatcher.convertGlobMetaCharsToRegexpMetaChars = function(glob) {
720
+ var re = glob;
721
+ re = re.replace(/([.^$+(){}\[\]\\|])/g, "\\$1");
722
+ re = re.replace(/\?/g, "(.|[\r\n])");
723
+ re = re.replace(/\*/g, "(.|[\r\n])*");
724
+ return re;
725
+ };
726
+
727
+ PatternMatcher.regexpFromGlobContains = function(globContains) {
728
+ return PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(globContains);
729
+ };
730
+
731
+ PatternMatcher.regexpFromGlob = function(glob) {
732
+ return "^" + PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(glob) + "$";
733
+ };
734
+
735
+ if (!this["Assert"]) Assert = {};
736
+
737
+
738
+ Assert.fail = function(message) {
739
+ throw new AssertionFailedError(message);
740
+ };
741
+
742
+ /*
743
+ * Assert.equals(comment?, expected, actual)
744
+ */
745
+ Assert.equals = function() {
746
+ var args = new AssertionArguments(arguments);
747
+ if (args.expected === args.actual) {
748
+ return;
749
+ }
750
+ Assert.fail(args.comment +
751
+ "Expected '" + args.expected +
752
+ "' but was '" + args.actual + "'");
753
+ };
754
+
755
+ Assert.assertEquals = Assert.equals;
756
+
757
+ /*
758
+ * Assert.matches(comment?, pattern, actual)
759
+ */
760
+ Assert.matches = function() {
761
+ var args = new AssertionArguments(arguments);
762
+ if (PatternMatcher.matches(args.expected, args.actual)) {
763
+ return;
764
+ }
765
+ Assert.fail(args.comment +
766
+ "Actual value '" + args.actual +
767
+ "' did not match '" + args.expected + "'");
768
+ }
769
+
770
+ /*
771
+ * Assert.notMtches(comment?, pattern, actual)
772
+ */
773
+ Assert.notMatches = function() {
774
+ var args = new AssertionArguments(arguments);
775
+ if (!PatternMatcher.matches(args.expected, args.actual)) {
776
+ return;
777
+ }
778
+ Assert.fail(args.comment +
779
+ "Actual value '" + args.actual +
780
+ "' did match '" + args.expected + "'");
781
+ }
782
+
783
+
784
+ // Preprocess the arguments to allow for an optional comment.
785
+ function AssertionArguments(args) {
786
+ if (args.length == 2) {
787
+ this.comment = "";
788
+ this.expected = args[0];
789
+ this.actual = args[1];
790
+ } else {
791
+ this.comment = args[0] + "; ";
792
+ this.expected = args[1];
793
+ this.actual = args[2];
794
+ }
795
+ }
796
+
797
+ function AssertionFailedError(message) {
798
+ this.isAssertionFailedError = true;
799
+ this.isSeleniumError = true;
800
+ this.message = message;
801
+ this.failureMessage = message;
802
+ }
803
+
804
+ function SeleniumError(message) {
805
+ var error = new Error(message);
806
+ if (typeof(arguments.caller) != 'undefined') { // IE, not ECMA
807
+ var result = '';
808
+ for (var a = arguments.caller; a != null; a = a.caller) {
809
+ result += '> ' + a.callee.toString() + '\n';
810
+ if (a.caller == a) {
811
+ result += '*';
812
+ break;
813
+ }
814
+ }
815
+ error.stack = result;
816
+ }
817
+ error.isSeleniumError = true;
818
+ return error;
819
+ }
820
+
821
+ function highlight(element) {
822
+ var highLightColor = "yellow";
823
+ if (element.originalColor == undefined) { // avoid picking up highlight
824
+ element.originalColor = elementGetStyle(element, "background-color");
825
+ }
826
+ elementSetStyle(element, {"backgroundColor" : highLightColor});
827
+ window.setTimeout(function() {
828
+ try {
829
+ //if element is orphan, probably page of it has already gone, so ignore
830
+ if (!element.parentNode) {
831
+ return;
832
+ }
833
+ elementSetStyle(element, {"backgroundColor" : element.originalColor});
834
+ } catch (e) {} // DGF unhighlighting is very dangerous and low priority
835
+ }, 200);
836
+ }
837
+
838
+
839
+
840
+ // for use from vs.2003 debugger
841
+ function o2s(obj) {
842
+ var s = "";
843
+ for (key in obj) {
844
+ var line = key + "->" + obj[key];
845
+ line.replace("\n", " ");
846
+ s += line + "\n";
847
+ }
848
+ return s;
849
+ }
850
+
851
+ var seenReadyStateWarning = false;
852
+
853
+ function openSeparateApplicationWindow(url, suppressMozillaWarning) {
854
+ // resize the Selenium window itself
855
+ window.resizeTo(1200, 500);
856
+ window.moveTo(window.screenX, 0);
857
+
858
+ var appWindow = window.open(url + '?start=true', 'main');
859
+ if (appWindow == null) {
860
+ var errorMessage = "Couldn't open app window; is the pop-up blocker enabled?"
861
+ LOG.error(errorMessage);
862
+ throw new Error("Couldn't open app window; is the pop-up blocker enabled?");
863
+ }
864
+ try {
865
+ var windowHeight = 500;
866
+ if (window.outerHeight) {
867
+ windowHeight = window.outerHeight;
868
+ } else if (document.documentElement && document.documentElement.offsetHeight) {
869
+ windowHeight = document.documentElement.offsetHeight;
870
+ }
871
+
872
+ if (window.screenLeft && !window.screenX) window.screenX = window.screenLeft;
873
+ if (window.screenTop && !window.screenY) window.screenY = window.screenTop;
874
+
875
+ appWindow.resizeTo(1200, screen.availHeight - windowHeight - 60);
876
+ appWindow.moveTo(window.screenX, window.screenY + windowHeight + 25);
877
+ } catch (e) {
878
+ LOG.error("Couldn't resize app window");
879
+ LOG.exception(e);
880
+ }
881
+
882
+
883
+ if (!suppressMozillaWarning && window.document.readyState == null && !seenReadyStateWarning) {
884
+ 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.");
885
+ seenReadyStateWarning = true;
886
+ }
887
+
888
+ return appWindow;
889
+ }
890
+
891
+ var URLConfiguration = classCreate();
892
+ objectExtend(URLConfiguration.prototype, {
893
+ initialize: function() {
894
+ },
895
+ _isQueryParameterTrue: function (name) {
896
+ var parameterValue = this._getQueryParameter(name);
897
+ if (parameterValue == null) return false;
898
+ if (parameterValue.toLowerCase() == "true") return true;
899
+ if (parameterValue.toLowerCase() == "on") return true;
900
+ return false;
901
+ },
902
+
903
+ _getQueryParameter: function(searchKey) {
904
+ var str = this.queryString
905
+ if (str == null) return null;
906
+ var clauses = str.split('&');
907
+ for (var i = 0; i < clauses.length; i++) {
908
+ var keyValuePair = clauses[i].split('=', 2);
909
+ var key = unescape(keyValuePair[0]);
910
+ if (key == searchKey) {
911
+ return unescape(keyValuePair[1]);
912
+ }
913
+ }
914
+ return null;
915
+ },
916
+
917
+ _extractArgs: function() {
918
+ var str = SeleniumHTARunner.commandLine;
919
+ if (str == null || str == "") return new Array();
920
+ var matches = str.match(/(?:\"([^\"]+)\"|(?!\"([^\"]+)\")(\S+))/g);
921
+ // We either want non quote stuff ([^"]+) surrounded by quotes
922
+ // or we want to look-ahead, see that the next character isn't
923
+ // a quoted argument, and then grab all the non-space stuff
924
+ // this will return for the line: "foo" bar
925
+ // the results "\"foo\"" and "bar"
926
+
927
+ // So, let's unquote the quoted arguments:
928
+ var args = new Array;
929
+ for (var i = 0; i < matches.length; i++) {
930
+ args[i] = matches[i];
931
+ args[i] = args[i].replace(/^"(.*)"$/, "$1");
932
+ }
933
+ return args;
934
+ },
935
+
936
+ isMultiWindowMode:function() {
937
+ return this._isQueryParameterTrue('multiWindow');
938
+ },
939
+
940
+ getBaseUrl:function() {
941
+ return this._getQueryParameter('baseUrl');
942
+
943
+ }
944
+ });
945
+
946
+
947
+ function safeScrollIntoView(element) {
948
+ if (element.scrollIntoView) {
949
+ element.scrollIntoView(false);
950
+ return;
951
+ }
952
+ // TODO: work out how to scroll browsers that don't support
953
+ // scrollIntoView (like Konqueror)
954
+ }
955
+
956
+ /**
957
+ * Returns the absolute time represented as an offset of the current time.
958
+ * Throws a SeleniumException if timeout is invalid.
959
+ *
960
+ * @param timeout the number of milliseconds from "now" whose absolute time
961
+ * to return
962
+ */
963
+ function getTimeoutTime(timeout) {
964
+ var now = new Date().getTime();
965
+ var timeoutLength = parseInt(timeout);
966
+
967
+ if (isNaN(timeoutLength)) {
968
+ throw new SeleniumError("Timeout is not a number: '" + timeout + "'");
969
+ }
970
+
971
+ return now + timeoutLength;
972
+ }
973
+
974
+ /**
975
+ * Returns true iff the current environment is the IDE.
976
+ */
977
+ function is_IDE()
978
+ {
979
+ return (typeof(SeleniumIDE) != 'undefined');
980
+ }
981
+
982
+ /**
983
+ * Logs a message if the Logger exists, and does nothing if it doesn't exist.
984
+ *
985
+ * @param level the level to log at
986
+ * @param msg the message to log
987
+ */
988
+ function safe_log(level, msg)
989
+ {
990
+ try {
991
+ LOG[level](msg);
992
+ }
993
+ catch (e) {
994
+ // couldn't log!
995
+ }
996
+ }
997
+
998
+ /**
999
+ * Displays a warning message to the user appropriate to the context under
1000
+ * which the issue is encountered. This is primarily used to avoid popping up
1001
+ * alert dialogs that might pause an automated test suite.
1002
+ *
1003
+ * @param msg the warning message to display
1004
+ */
1005
+ function safe_alert(msg)
1006
+ {
1007
+ if (is_IDE()) {
1008
+ alert(msg);
1009
+ }
1010
+ }
1011
+
1012
+ /**
1013
+ * Returns true iff the given element represents a link with a javascript
1014
+ * href attribute, and does not have an onclick attribute defined.
1015
+ *
1016
+ * @param element the element to test
1017
+ */
1018
+ function hasJavascriptHref(element) {
1019
+ if (getTagName(element) != 'a') {
1020
+ return false;
1021
+ }
1022
+ if (element.onclick) {
1023
+ return false;
1024
+ }
1025
+ if (! element.href) {
1026
+ return false;
1027
+ }
1028
+ if (! /\s*javascript:/i.test(element.href)) {
1029
+ return false;
1030
+ }
1031
+ return true;
1032
+ }
1033
+
1034
+ /**
1035
+ * Returns the given element, or its nearest ancestor, that satisfies
1036
+ * hasJavascriptHref(). Returns null if none is found.
1037
+ *
1038
+ * @param element the element whose ancestors to test
1039
+ */
1040
+ function getAncestorOrSelfWithJavascriptHref(element) {
1041
+ if (hasJavascriptHref(element)) {
1042
+ return element;
1043
+ }
1044
+ if (element.parentNode == null) {
1045
+ return null;
1046
+ }
1047
+ return getAncestorOrSelfWithJavascriptHref(element.parentNode);
1048
+ }
1049
+
1050
+ //******************************************************************************
1051
+ // Locator evaluation support
1052
+
1053
+ /**
1054
+ * Parses a Selenium locator, returning its type and the unprefixed locator
1055
+ * string as an object.
1056
+ *
1057
+ * @param locator the locator to parse
1058
+ */
1059
+ function parse_locator(locator)
1060
+ {
1061
+ var result = locator.match(/^([A-Za-z]+)=(.+)/);
1062
+ if (result) {
1063
+ return { type: result[1].toLowerCase(), string: result[2] };
1064
+ }
1065
+ return { type: 'implicit', string: locator };
1066
+ }
1067
+
1068
+ /**
1069
+ * Evaluates an xpath on a document, and returns a list containing nodes in the
1070
+ * resulting nodeset. The browserbot xpath methods are now backed by this
1071
+ * function. A context node may optionally be provided, and the xpath will be
1072
+ * evaluated from that context.
1073
+ *
1074
+ * @param xpath the xpath to evaluate
1075
+ * @param inDocument the document in which to evaluate the xpath.
1076
+ * @param opts (optional) An object containing various flags that can
1077
+ * modify how the xpath is evaluated. Here's a listing of
1078
+ * the meaningful keys:
1079
+ *
1080
+ * contextNode:
1081
+ * the context node from which to evaluate the xpath. If
1082
+ * unspecified, the context will be the root document
1083
+ * element.
1084
+ *
1085
+ * namespaceResolver:
1086
+ * the namespace resolver function. Defaults to null.
1087
+ *
1088
+ * xpathLibrary:
1089
+ * the javascript library to use for XPath. "ajaxslt" is
1090
+ * the default. "javascript-xpath" is newer and faster,
1091
+ * but needs more testing.
1092
+ *
1093
+ * allowNativeXpath:
1094
+ * whether to allow native evaluate(). Defaults to true.
1095
+ *
1096
+ * ignoreAttributesWithoutValue:
1097
+ * whether it's ok to ignore attributes without value
1098
+ * when evaluating the xpath. This can greatly improve
1099
+ * performance in IE; however, if your xpaths depend on
1100
+ * such attributes, you can't ignore them! Defaults to
1101
+ * true.
1102
+ *
1103
+ * returnOnFirstMatch:
1104
+ * whether to optimize the XPath evaluation to only
1105
+ * return the first match. The match, if any, will still
1106
+ * be returned in a list. Defaults to false.
1107
+ */
1108
+ function eval_xpath(xpath, inDocument, opts)
1109
+ {
1110
+ if (!opts) {
1111
+ var opts = {};
1112
+ }
1113
+ var contextNode = opts.contextNode
1114
+ ? opts.contextNode : inDocument;
1115
+ var namespaceResolver = opts.namespaceResolver
1116
+ ? opts.namespaceResolver : null;
1117
+ var xpathLibrary = opts.xpathLibrary
1118
+ ? opts.xpathLibrary : null;
1119
+ var allowNativeXpath = (opts.allowNativeXpath != undefined)
1120
+ ? opts.allowNativeXpath : true;
1121
+ var ignoreAttributesWithoutValue = (opts.ignoreAttributesWithoutValue != undefined)
1122
+ ? opts.ignoreAttributesWithoutValue : true;
1123
+ var returnOnFirstMatch = (opts.returnOnFirstMatch != undefined)
1124
+ ? opts.returnOnFirstMatch : false;
1125
+
1126
+ // Trim any trailing "/": not valid xpath, and remains from attribute
1127
+ // locator.
1128
+ if (xpath.charAt(xpath.length - 1) == '/') {
1129
+ xpath = xpath.slice(0, -1);
1130
+ }
1131
+ // HUGE hack - remove namespace from xpath for IE
1132
+ if (browserVersion && browserVersion.isIE) {
1133
+ xpath = xpath.replace(/x:/g, '')
1134
+ }
1135
+
1136
+ var nativeXpathAvailable = inDocument.evaluate;
1137
+ var useNativeXpath = allowNativeXpath && nativeXpathAvailable;
1138
+ var useDocumentEvaluate = useNativeXpath;
1139
+
1140
+ // When using the new and faster javascript-xpath library,
1141
+ // we'll use the TestRunner's document object, not the App-Under-Test's document.
1142
+ // The new library only modifies the TestRunner document with the new
1143
+ // functionality.
1144
+ if (xpathLibrary == 'javascript-xpath' && !useNativeXpath) {
1145
+ documentForXpath = document;
1146
+ useDocumentEvaluate = true;
1147
+ } else {
1148
+ documentForXpath = inDocument;
1149
+ }
1150
+ var results = [];
1151
+
1152
+ // this is either native xpath or javascript-xpath via TestRunner.evaluate
1153
+ if (useDocumentEvaluate) {
1154
+ try {
1155
+ // Regarding use of the second argument to document.evaluate():
1156
+ // http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/a59ce20639c74ba1/a9d9f53e88e5ebb5
1157
+ var xpathResult = documentForXpath
1158
+ .evaluate((contextNode == inDocument ? xpath : '.' + xpath),
1159
+ contextNode, namespaceResolver, 0, null);
1160
+ }
1161
+ catch (e) {
1162
+ throw new SeleniumError("Invalid xpath: " + extractExceptionMessage(e));
1163
+ }
1164
+ finally{
1165
+ if (xpathResult == null) {
1166
+ // If the result is null, we should still throw an Error.
1167
+ throw new SeleniumError("Invalid xpath: " + xpath);
1168
+ }
1169
+ }
1170
+ var result = xpathResult.iterateNext();
1171
+ while (result) {
1172
+ results.push(result);
1173
+ result = xpathResult.iterateNext();
1174
+ }
1175
+ return results;
1176
+ }
1177
+
1178
+ // If not, fall back to slower JavaScript implementation
1179
+ // DGF set xpathdebug = true (using getEval, if you like) to turn on JS XPath debugging
1180
+ //xpathdebug = true;
1181
+ var context;
1182
+ if (contextNode == inDocument) {
1183
+ context = new ExprContext(inDocument);
1184
+ }
1185
+ else {
1186
+ // provide false values to get the default constructor values
1187
+ context = new ExprContext(contextNode, false, false,
1188
+ contextNode.parentNode);
1189
+ }
1190
+ context.setCaseInsensitive(true);
1191
+ context.setIgnoreAttributesWithoutValue(ignoreAttributesWithoutValue);
1192
+ context.setReturnOnFirstMatch(returnOnFirstMatch);
1193
+ var xpathObj;
1194
+ try {
1195
+ xpathObj = xpathParse(xpath);
1196
+ }
1197
+ catch (e) {
1198
+ throw new SeleniumError("Invalid xpath: " + extractExceptionMessage(e));
1199
+ }
1200
+ var xpathResult = xpathObj.evaluate(context);
1201
+ if (xpathResult && xpathResult.value) {
1202
+ for (var i = 0; i < xpathResult.value.length; ++i) {
1203
+ results.push(xpathResult.value[i]);
1204
+ }
1205
+ }
1206
+ return results;
1207
+ }
1208
+
1209
+ /**
1210
+ * Returns the full resultset of a CSS selector evaluation.
1211
+ */
1212
+ function eval_css(locator, inDocument)
1213
+ {
1214
+ return cssQuery(locator, inDocument);
1215
+ }
1216
+
1217
+ /**
1218
+ * This function duplicates part of BrowserBot.findElement() to open up locator
1219
+ * evaluation on arbitrary documents. It returns a plain old array of located
1220
+ * elements found by using a Selenium locator.
1221
+ *
1222
+ * Multiple results may be generated for xpath and CSS locators. Even though a
1223
+ * list could potentially be generated for other locator types, such as link,
1224
+ * we don't try for them, because they aren't very expressive location
1225
+ * strategies; if you want a list, use xpath or CSS. Furthermore, strategies
1226
+ * for these locators have been optimized to only return the first result. For
1227
+ * these types of locators, performance is more important than ideal behavior.
1228
+ *
1229
+ * @param locator a locator string
1230
+ * @param inDocument the document in which to apply the locator
1231
+ * @param opt_contextNode the context within which to evaluate the locator
1232
+ *
1233
+ * @return a list of result elements
1234
+ */
1235
+ function eval_locator(locator, inDocument, opt_contextNode)
1236
+ {
1237
+ locator = parse_locator(locator);
1238
+
1239
+ var pageBot;
1240
+ if (typeof(selenium) != 'undefined' && selenium != undefined) {
1241
+ if (typeof(editor) == 'undefined' || editor.state == 'playing') {
1242
+ safe_log('info', 'Trying [' + locator.type + ']: '
1243
+ + locator.string);
1244
+ }
1245
+ pageBot = selenium.browserbot;
1246
+ }
1247
+ else {
1248
+ if (!UI_GLOBAL.mozillaBrowserBot) {
1249
+ // create a browser bot to evaluate the locator. Hand it the IDE
1250
+ // window as a dummy window, and cache it for future use.
1251
+ UI_GLOBAL.mozillaBrowserBot = new MozillaBrowserBot(window)
1252
+ }
1253
+ pageBot = UI_GLOBAL.mozillaBrowserBot;
1254
+ }
1255
+
1256
+ var results = [];
1257
+
1258
+ if (locator.type == 'xpath' || (locator.string.charAt(0) == '/' &&
1259
+ locator.type == 'implicit')) {
1260
+ results = eval_xpath(locator.string, inDocument,
1261
+ { contextNode: opt_contextNode });
1262
+ }
1263
+ else if (locator.type == 'css') {
1264
+ results = eval_css(locator.string, inDocument);
1265
+ }
1266
+ else {
1267
+ var element = pageBot
1268
+ .findElementBy(locator.type, locator.string, inDocument);
1269
+ if (element != null) {
1270
+ results.push(element);
1271
+ }
1272
+ }
1273
+
1274
+ return results;
1275
+ }
1276
+
1277
+ //******************************************************************************
1278
+ // UI-Element
1279
+
1280
+ /**
1281
+ * Escapes the special regular expression characters in a string intended to be
1282
+ * used as a regular expression.
1283
+ *
1284
+ * Based on: http://simonwillison.net/2006/Jan/20/escape/
1285
+ */
1286
+ RegExp.escape = (function() {
1287
+ var specials = [
1288
+ '/', '.', '*', '+', '?', '|', '^', '$',
1289
+ '(', ')', '[', ']', '{', '}', '\\'
1290
+ ];
1291
+
1292
+ var sRE = new RegExp(
1293
+ '(\\' + specials.join('|\\') + ')', 'g'
1294
+ );
1295
+
1296
+ return function(text) {
1297
+ return text.replace(sRE, '\\$1');
1298
+ }
1299
+ })();
1300
+
1301
+ /**
1302
+ * Returns true if two arrays are identical, and false otherwise.
1303
+ *
1304
+ * @param a1 the first array, may only contain simple values (strings or
1305
+ * numbers)
1306
+ * @param a2 the second array, same restricts on data as for a1
1307
+ * @return true if the arrays are equivalent, false otherwise.
1308
+ */
1309
+ function are_equal(a1, a2)
1310
+ {
1311
+ if (typeof(a1) != typeof(a2))
1312
+ return false;
1313
+
1314
+ switch(typeof(a1)) {
1315
+ case 'object':
1316
+ // arrays
1317
+ if (a1.length) {
1318
+ if (a1.length != a2.length)
1319
+ return false;
1320
+ for (var i = 0; i < a1.length; ++i) {
1321
+ if (!are_equal(a1[i], a2[i]))
1322
+ return false
1323
+ }
1324
+ }
1325
+ // associative arrays
1326
+ else {
1327
+ var keys = {};
1328
+ for (var key in a1) {
1329
+ keys[key] = true;
1330
+ }
1331
+ for (var key in a2) {
1332
+ keys[key] = true;
1333
+ }
1334
+ for (var key in keys) {
1335
+ if (!are_equal(a1[key], a2[key]))
1336
+ return false;
1337
+ }
1338
+ }
1339
+ return true;
1340
+
1341
+ default:
1342
+ return a1 == a2;
1343
+ }
1344
+ }
1345
+
1346
+
1347
+ /**
1348
+ * Create a clone of an object and return it. This is a deep copy of everything
1349
+ * but functions, whose references are copied. You shouldn't expect a deep copy
1350
+ * of functions anyway.
1351
+ *
1352
+ * @param orig the original object to copy
1353
+ * @return a deep copy of the original object. Any functions attached,
1354
+ * however, will have their references copied only.
1355
+ */
1356
+ function clone(orig) {
1357
+ var copy;
1358
+ switch(typeof(orig)) {
1359
+ case 'object':
1360
+ copy = (orig.length) ? [] : {};
1361
+ for (var attr in orig) {
1362
+ copy[attr] = clone(orig[attr]);
1363
+ }
1364
+ break;
1365
+ default:
1366
+ copy = orig;
1367
+ break;
1368
+ }
1369
+ return copy;
1370
+ }
1371
+
1372
+ /**
1373
+ * Emulates php's print_r() functionality. Returns a nicely formatted string
1374
+ * representation of an object. Very useful for debugging.
1375
+ *
1376
+ * @param object the object to dump
1377
+ * @param maxDepth the maximum depth to recurse into the object. Ellipses will
1378
+ * be shown for objects whose depth exceeds the maximum.
1379
+ * @param indent the string to use for indenting progressively deeper levels
1380
+ * of the dump.
1381
+ * @return a string representing a dump of the object
1382
+ */
1383
+ function print_r(object, maxDepth, indent)
1384
+ {
1385
+ var parentIndent, attr, str = "";
1386
+ if (arguments.length == 1) {
1387
+ var maxDepth = Number.MAX_VALUE;
1388
+ } else {
1389
+ maxDepth--;
1390
+ }
1391
+ if (arguments.length < 3) {
1392
+ parentIndent = ''
1393
+ var indent = ' ';
1394
+ } else {
1395
+ parentIndent = indent;
1396
+ indent += ' ';
1397
+ }
1398
+
1399
+ switch(typeof(object)) {
1400
+ case 'object':
1401
+ if (object.length != undefined) {
1402
+ if (object.length == 0) {
1403
+ str += "Array ()\r\n";
1404
+ }
1405
+ else {
1406
+ str += "Array (\r\n";
1407
+ for (var i = 0; i < object.length; ++i) {
1408
+ str += indent + '[' + i + '] => ';
1409
+ if (maxDepth == 0)
1410
+ str += "...\r\n";
1411
+ else
1412
+ str += print_r(object[i], maxDepth, indent);
1413
+ }
1414
+ str += parentIndent + ")\r\n";
1415
+ }
1416
+ }
1417
+ else {
1418
+ str += "Object (\r\n";
1419
+ for (attr in object) {
1420
+ str += indent + "[" + attr + "] => ";
1421
+ if (maxDepth == 0)
1422
+ str += "...\r\n";
1423
+ else
1424
+ str += print_r(object[attr], maxDepth, indent);
1425
+ }
1426
+ str += parentIndent + ")\r\n";
1427
+ }
1428
+ break;
1429
+ case 'boolean':
1430
+ str += (object ? 'true' : 'false') + "\r\n";
1431
+ break;
1432
+ case 'function':
1433
+ str += "Function\r\n";
1434
+ break;
1435
+ default:
1436
+ str += object + "\r\n";
1437
+ break;
1438
+
1439
+ }
1440
+ return str;
1441
+ }
1442
+
1443
+ /**
1444
+ * Return an array containing all properties of an object. Perl-style.
1445
+ *
1446
+ * @param object the object whose keys to return
1447
+ * @return array of object keys, as strings
1448
+ */
1449
+ function keys(object)
1450
+ {
1451
+ var keys = [];
1452
+ for (var k in object) {
1453
+ keys.push(k);
1454
+ }
1455
+ return keys;
1456
+ }
1457
+
1458
+ /**
1459
+ * Emulates python's range() built-in. Returns an array of integers, counting
1460
+ * up (or down) from start to end. Note that the range returned is up to, but
1461
+ * NOT INCLUDING, end.
1462
+ *.
1463
+ * @param start integer from which to start counting. If the end parameter is
1464
+ * not provided, this value is considered the end and start will
1465
+ * be zero.
1466
+ * @param end integer to which to count. If omitted, the function will count
1467
+ * up from zero to the value of the start parameter. Note that
1468
+ * the array returned will count up to but will not include this
1469
+ * value.
1470
+ * @return an array of consecutive integers.
1471
+ */
1472
+ function range(start, end)
1473
+ {
1474
+ if (arguments.length == 1) {
1475
+ var end = start;
1476
+ start = 0;
1477
+ }
1478
+
1479
+ var r = [];
1480
+ if (start < end) {
1481
+ while (start != end)
1482
+ r.push(start++);
1483
+ }
1484
+ else {
1485
+ while (start != end)
1486
+ r.push(start--);
1487
+ }
1488
+ return r;
1489
+ }
1490
+
1491
+ /**
1492
+ * Parses a python-style keyword arguments string and returns the pairs in a
1493
+ * new object.
1494
+ *
1495
+ * @param kwargs a string representing a set of keyword arguments. It should
1496
+ * look like <tt>keyword1=value1, keyword2=value2, ...</tt>
1497
+ * @return an object mapping strings to strings
1498
+ */
1499
+ function parse_kwargs(kwargs)
1500
+ {
1501
+ var args = new Object();
1502
+ var pairs = kwargs.split(/,/);
1503
+ for (var i = 0; i < pairs.length;) {
1504
+ if (i > 0 && pairs[i].indexOf('=') == -1) {
1505
+ // the value string contained a comma. Glue the parts back together.
1506
+ pairs[i-1] += ',' + pairs.splice(i, 1)[0];
1507
+ }
1508
+ else {
1509
+ ++i;
1510
+ }
1511
+ }
1512
+ for (var i = 0; i < pairs.length; ++i) {
1513
+ var splits = pairs[i].split(/=/);
1514
+ if (splits.length == 1) {
1515
+ continue;
1516
+ }
1517
+ var key = splits.shift();
1518
+ var value = splits.join('=');
1519
+ args[key.trim()] = value.trim();
1520
+ }
1521
+ return args;
1522
+ }
1523
+
1524
+ /**
1525
+ * Creates a python-style keyword arguments string from an object.
1526
+ *
1527
+ * @param args an associative array mapping strings to strings
1528
+ * @param sortedKeys (optional) a list of keys of the args parameter that
1529
+ * specifies the order in which the arguments will appear in
1530
+ * the returned kwargs string
1531
+ *
1532
+ * @return a kwarg string representation of args
1533
+ */
1534
+ function to_kwargs(args, sortedKeys)
1535
+ {
1536
+ var s = '';
1537
+ if (!sortedKeys) {
1538
+ var sortedKeys = keys(args).sort();
1539
+ }
1540
+ for (var i = 0; i < sortedKeys.length; ++i) {
1541
+ var k = sortedKeys[i];
1542
+ if (args[k] != undefined) {
1543
+ if (s) {
1544
+ s += ', ';
1545
+ }
1546
+ s += k + '=' + args[k];
1547
+ }
1548
+ }
1549
+ return s;
1550
+ }
1551
+
1552
+ /**
1553
+ * Returns true if a node is an ancestor node of a target node, and false
1554
+ * otherwise.
1555
+ *
1556
+ * @param node the node being compared to the target node
1557
+ * @param target the target node
1558
+ * @return true if node is an ancestor node of target, false otherwise.
1559
+ */
1560
+ function is_ancestor(node, target)
1561
+ {
1562
+ while (target.parentNode) {
1563
+ target = target.parentNode;
1564
+ if (node == target)
1565
+ return true;
1566
+ }
1567
+ return false;
1568
+ }
1569
+
1570
+ //******************************************************************************
1571
+ // parseUri 1.2.1
1572
+ // MIT License
1573
+
1574
+ /*
1575
+ Copyright (c) 2007 Steven Levithan <stevenlevithan.com>
1576
+
1577
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1578
+ of this software and associated documentation files (the "Software"), to deal
1579
+ in the Software without restriction, including without limitation the rights
1580
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1581
+ copies of the Software, and to permit persons to whom the Software is
1582
+ furnished to do so, subject to the following conditions:
1583
+
1584
+ The above copyright notice and this permission notice shall be included in
1585
+ all copies or substantial portions of the Software.
1586
+ */
1587
+
1588
+ function parseUri (str) {
1589
+ var o = parseUri.options,
1590
+ m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
1591
+ uri = {},
1592
+ i = 14;
1593
+
1594
+ while (i--) uri[o.key[i]] = m[i] || "";
1595
+
1596
+ uri[o.q.name] = {};
1597
+ uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
1598
+ if ($1) uri[o.q.name][$1] = $2;
1599
+ });
1600
+
1601
+ return uri;
1602
+ };
1603
+
1604
+ parseUri.options = {
1605
+ strictMode: false,
1606
+ key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
1607
+ q: {
1608
+ name: "queryKey",
1609
+ parser: /(?:^|&)([^&=]*)=?([^&]*)/g
1610
+ },
1611
+ parser: {
1612
+ strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
1613
+ loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
1614
+ }
1615
+ };
1616
+