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,2450 @@
1
+ // Copyright 2005 Google Inc.
2
+ // All Rights Reserved
3
+ //
4
+ // An XPath parser and evaluator written in JavaScript. The
5
+ // implementation is complete except for functions handling
6
+ // namespaces.
7
+ //
8
+ // Reference: [XPATH] XPath Specification
9
+ // <http://www.w3.org/TR/1999/REC-xpath-19991116>.
10
+ //
11
+ //
12
+ // The API of the parser has several parts:
13
+ //
14
+ // 1. The parser function xpathParse() that takes a string and returns
15
+ // an expession object.
16
+ //
17
+ // 2. The expression object that has an evaluate() method to evaluate the
18
+ // XPath expression it represents. (It is actually a hierarchy of
19
+ // objects that resembles the parse tree, but an application will call
20
+ // evaluate() only on the top node of this hierarchy.)
21
+ //
22
+ // 3. The context object that is passed as an argument to the evaluate()
23
+ // method, which represents the DOM context in which the expression is
24
+ // evaluated.
25
+ //
26
+ // 4. The value object that is returned from evaluate() and represents
27
+ // values of the different types that are defined by XPath (number,
28
+ // string, boolean, and node-set), and allows to convert between them.
29
+ //
30
+ // These parts are near the top of the file, the functions and data
31
+ // that are used internally follow after them.
32
+ //
33
+ //
34
+ // Author: Steffen Meschkat <mesch@google.com>
35
+
36
+
37
+ // The entry point for the parser.
38
+ //
39
+ // @param expr a string that contains an XPath expression.
40
+ // @return an expression object that can be evaluated with an
41
+ // expression context.
42
+
43
+ function xpathParse(expr) {
44
+ xpathLog('parse ' + expr);
45
+ xpathParseInit();
46
+
47
+ var cached = xpathCacheLookup(expr);
48
+ if (cached) {
49
+ xpathLog(' ... cached');
50
+ return cached;
51
+ }
52
+
53
+ // Optimize for a few common cases: simple attribute node tests
54
+ // (@id), simple element node tests (page), variable references
55
+ // ($address), numbers (4), multi-step path expressions where each
56
+ // step is a plain element node test
57
+ // (page/overlay/locations/location).
58
+
59
+ if (expr.match(/^(\$|@)?\w+$/i)) {
60
+ var ret = makeSimpleExpr(expr);
61
+ xpathParseCache[expr] = ret;
62
+ xpathLog(' ... simple');
63
+ return ret;
64
+ }
65
+
66
+ if (expr.match(/^\w+(\/\w+)*$/i)) {
67
+ var ret = makeSimpleExpr2(expr);
68
+ xpathParseCache[expr] = ret;
69
+ xpathLog(' ... simple 2');
70
+ return ret;
71
+ }
72
+
73
+ var cachekey = expr; // expr is modified during parse
74
+
75
+ var stack = [];
76
+ var ahead = null;
77
+ var previous = null;
78
+ var done = false;
79
+
80
+ var parse_count = 0;
81
+ var lexer_count = 0;
82
+ var reduce_count = 0;
83
+
84
+ while (!done) {
85
+ parse_count++;
86
+ expr = expr.replace(/^\s*/, '');
87
+ previous = ahead;
88
+ ahead = null;
89
+
90
+ var rule = null;
91
+ var match = '';
92
+ for (var i = 0; i < xpathTokenRules.length; ++i) {
93
+ var result = xpathTokenRules[i].re.exec(expr);
94
+ lexer_count++;
95
+ if (result && result.length > 0 && result[0].length > match.length) {
96
+ rule = xpathTokenRules[i];
97
+ match = result[0];
98
+ break;
99
+ }
100
+ }
101
+
102
+ // Special case: allow operator keywords to be element and
103
+ // variable names.
104
+
105
+ // NOTE(mesch): The parser resolves conflicts by looking ahead,
106
+ // and this is the only case where we look back to
107
+ // disambiguate. So this is indeed something different, and
108
+ // looking back is usually done in the lexer (via states in the
109
+ // general case, called "start conditions" in flex(1)). Also,the
110
+ // conflict resolution in the parser is not as robust as it could
111
+ // be, so I'd like to keep as much off the parser as possible (all
112
+ // these precedence values should be computed from the grammar
113
+ // rules and possibly associativity declarations, as in bison(1),
114
+ // and not explicitly set.
115
+
116
+ if (rule &&
117
+ (rule == TOK_DIV ||
118
+ rule == TOK_MOD ||
119
+ rule == TOK_AND ||
120
+ rule == TOK_OR) &&
121
+ (!previous ||
122
+ previous.tag == TOK_AT ||
123
+ previous.tag == TOK_DSLASH ||
124
+ previous.tag == TOK_SLASH ||
125
+ previous.tag == TOK_AXIS ||
126
+ previous.tag == TOK_DOLLAR)) {
127
+ rule = TOK_QNAME;
128
+ }
129
+
130
+ if (rule) {
131
+ expr = expr.substr(match.length);
132
+ xpathLog('token: ' + match + ' -- ' + rule.label);
133
+ ahead = {
134
+ tag: rule,
135
+ match: match,
136
+ prec: rule.prec ? rule.prec : 0, // || 0 is removed by the compiler
137
+ expr: makeTokenExpr(match)
138
+ };
139
+
140
+ } else {
141
+ xpathLog('DONE');
142
+ done = true;
143
+ }
144
+
145
+ while (xpathReduce(stack, ahead)) {
146
+ reduce_count++;
147
+ xpathLog('stack: ' + stackToString(stack));
148
+ }
149
+ }
150
+
151
+ xpathLog('stack: ' + stackToString(stack));
152
+
153
+ // DGF any valid XPath should "reduce" to a single Expr token
154
+ if (stack.length != 1) {
155
+ throw 'XPath parse error ' + cachekey + ':\n' + stackToString(stack);
156
+ }
157
+
158
+ var result = stack[0].expr;
159
+ xpathParseCache[cachekey] = result;
160
+
161
+ xpathLog('XPath parse: ' + parse_count + ' / ' +
162
+ lexer_count + ' / ' + reduce_count);
163
+
164
+ return result;
165
+ }
166
+
167
+ var xpathParseCache = {};
168
+
169
+ function xpathCacheLookup(expr) {
170
+ return xpathParseCache[expr];
171
+ }
172
+
173
+ /*DGF xpathReduce is where the magic happens in this parser.
174
+ Skim down to the bottom of this file to find the table of
175
+ grammatical rules and precedence numbers, "The productions of the grammar".
176
+
177
+ The idea here
178
+ is that we want to take a stack of tokens and apply
179
+ grammatical rules to them, "reducing" them to higher-level
180
+ tokens. Ultimately, any valid XPath should reduce to exactly one
181
+ "Expr" token.
182
+
183
+ Reduce too early or too late and you'll have two tokens that can't reduce
184
+ to single Expr. For example, you may hastily reduce a qname that
185
+ should name a function, incorrectly treating it as a tag name.
186
+ Or you may reduce too late, accidentally reducing the last part of the
187
+ XPath into a top-level "Expr" that won't reduce with earlier parts of
188
+ the XPath.
189
+
190
+ A "cand" is a grammatical rule candidate, with a given precedence
191
+ number. "ahead" is the upcoming token, which also has a precedence
192
+ number. If the token has a higher precedence number than
193
+ the rule candidate, we'll "shift" the token onto the token stack,
194
+ instead of immediately applying the rule candidate.
195
+
196
+ Some tokens have left associativity, in which case we shift when they
197
+ have LOWER precedence than the candidate.
198
+ */
199
+ function xpathReduce(stack, ahead) {
200
+ var cand = null;
201
+
202
+ if (stack.length > 0) {
203
+ var top = stack[stack.length-1];
204
+ var ruleset = xpathRules[top.tag.key];
205
+
206
+ if (ruleset) {
207
+ for (var i = 0; i < ruleset.length; ++i) {
208
+ var rule = ruleset[i];
209
+ var match = xpathMatchStack(stack, rule[1]);
210
+ if (match.length) {
211
+ cand = {
212
+ tag: rule[0],
213
+ rule: rule,
214
+ match: match
215
+ };
216
+ cand.prec = xpathGrammarPrecedence(cand);
217
+ break;
218
+ }
219
+ }
220
+ }
221
+ }
222
+
223
+ var ret;
224
+ if (cand && (!ahead || cand.prec > ahead.prec ||
225
+ (ahead.tag.left && cand.prec >= ahead.prec))) {
226
+ for (var i = 0; i < cand.match.matchlength; ++i) {
227
+ stack.pop();
228
+ }
229
+
230
+ xpathLog('reduce ' + cand.tag.label + ' ' + cand.prec +
231
+ ' ahead ' + (ahead ? ahead.tag.label + ' ' + ahead.prec +
232
+ (ahead.tag.left ? ' left' : '')
233
+ : ' none '));
234
+
235
+ var matchexpr = mapExpr(cand.match, function(m) { return m.expr; });
236
+ xpathLog('going to apply ' + cand.rule[3].toString());
237
+ cand.expr = cand.rule[3].apply(null, matchexpr);
238
+
239
+ stack.push(cand);
240
+ ret = true;
241
+
242
+ } else {
243
+ if (ahead) {
244
+ xpathLog('shift ' + ahead.tag.label + ' ' + ahead.prec +
245
+ (ahead.tag.left ? ' left' : '') +
246
+ ' over ' + (cand ? cand.tag.label + ' ' +
247
+ cand.prec : ' none'));
248
+ stack.push(ahead);
249
+ }
250
+ ret = false;
251
+ }
252
+ return ret;
253
+ }
254
+
255
+ function xpathMatchStack(stack, pattern) {
256
+
257
+ // NOTE(mesch): The stack matches for variable cardinality are
258
+ // greedy but don't do backtracking. This would be an issue only
259
+ // with rules of the form A* A, i.e. with an element with variable
260
+ // cardinality followed by the same element. Since that doesn't
261
+ // occur in the grammar at hand, all matches on the stack are
262
+ // unambiguous.
263
+
264
+ var S = stack.length;
265
+ var P = pattern.length;
266
+ var p, s;
267
+ var match = [];
268
+ match.matchlength = 0;
269
+ var ds = 0;
270
+ for (p = P - 1, s = S - 1; p >= 0 && s >= 0; --p, s -= ds) {
271
+ ds = 0;
272
+ var qmatch = [];
273
+ if (pattern[p] == Q_MM) {
274
+ p -= 1;
275
+ match.push(qmatch);
276
+ while (s - ds >= 0 && stack[s - ds].tag == pattern[p]) {
277
+ qmatch.push(stack[s - ds]);
278
+ ds += 1;
279
+ match.matchlength += 1;
280
+ }
281
+
282
+ } else if (pattern[p] == Q_01) {
283
+ p -= 1;
284
+ match.push(qmatch);
285
+ while (s - ds >= 0 && ds < 2 && stack[s - ds].tag == pattern[p]) {
286
+ qmatch.push(stack[s - ds]);
287
+ ds += 1;
288
+ match.matchlength += 1;
289
+ }
290
+
291
+ } else if (pattern[p] == Q_1M) {
292
+ p -= 1;
293
+ match.push(qmatch);
294
+ if (stack[s].tag == pattern[p]) {
295
+ while (s - ds >= 0 && stack[s - ds].tag == pattern[p]) {
296
+ qmatch.push(stack[s - ds]);
297
+ ds += 1;
298
+ match.matchlength += 1;
299
+ }
300
+ } else {
301
+ return [];
302
+ }
303
+
304
+ } else if (stack[s].tag == pattern[p]) {
305
+ match.push(stack[s]);
306
+ ds += 1;
307
+ match.matchlength += 1;
308
+
309
+ } else {
310
+ return [];
311
+ }
312
+
313
+ reverseInplace(qmatch);
314
+ qmatch.expr = mapExpr(qmatch, function(m) { return m.expr; });
315
+ }
316
+
317
+ reverseInplace(match);
318
+
319
+ if (p == -1) {
320
+ return match;
321
+
322
+ } else {
323
+ return [];
324
+ }
325
+ }
326
+
327
+ function xpathTokenPrecedence(tag) {
328
+ return tag.prec || 2;
329
+ }
330
+
331
+ function xpathGrammarPrecedence(frame) {
332
+ var ret = 0;
333
+
334
+ if (frame.rule) { /* normal reduce */
335
+ if (frame.rule.length >= 3 && frame.rule[2] >= 0) {
336
+ ret = frame.rule[2];
337
+
338
+ } else {
339
+ for (var i = 0; i < frame.rule[1].length; ++i) {
340
+ var p = xpathTokenPrecedence(frame.rule[1][i]);
341
+ ret = Math.max(ret, p);
342
+ }
343
+ }
344
+ } else if (frame.tag) { /* TOKEN match */
345
+ ret = xpathTokenPrecedence(frame.tag);
346
+
347
+ } else if (frame.length) { /* Q_ match */
348
+ for (var j = 0; j < frame.length; ++j) {
349
+ var p = xpathGrammarPrecedence(frame[j]);
350
+ ret = Math.max(ret, p);
351
+ }
352
+ }
353
+
354
+ return ret;
355
+ }
356
+
357
+ function stackToString(stack) {
358
+ var ret = '';
359
+ for (var i = 0; i < stack.length; ++i) {
360
+ if (ret) {
361
+ ret += '\n';
362
+ }
363
+ ret += stack[i].tag.label;
364
+ }
365
+ return ret;
366
+ }
367
+
368
+
369
+ // XPath expression evaluation context. An XPath context consists of a
370
+ // DOM node, a list of DOM nodes that contains this node, a number
371
+ // that represents the position of the single node in the list, and a
372
+ // current set of variable bindings. (See XPath spec.)
373
+ //
374
+ // The interface of the expression context:
375
+ //
376
+ // Constructor -- gets the node, its position, the node set it
377
+ // belongs to, and a parent context as arguments. The parent context
378
+ // is used to implement scoping rules for variables: if a variable
379
+ // is not found in the current context, it is looked for in the
380
+ // parent context, recursively. Except for node, all arguments have
381
+ // default values: default position is 0, default node set is the
382
+ // set that contains only the node, and the default parent is null.
383
+ //
384
+ // Notice that position starts at 0 at the outside interface;
385
+ // inside XPath expressions this shows up as position()=1.
386
+ //
387
+ // clone() -- creates a new context with the current context as
388
+ // parent. If passed as argument to clone(), the new context has a
389
+ // different node, position, or node set. What is not passed is
390
+ // inherited from the cloned context.
391
+ //
392
+ // setVariable(name, expr) -- binds given XPath expression to the
393
+ // name.
394
+ //
395
+ // getVariable(name) -- what the name says.
396
+ //
397
+ // setNode(position) -- sets the context to the node at the given
398
+ // position. Needed to implement scoping rules for variables in
399
+ // XPath. (A variable is visible to all subsequent siblings, not
400
+ // only to its children.)
401
+ //
402
+ // set/isCaseInsensitive -- specifies whether node name tests should
403
+ // be case sensitive. If you're executing xpaths against a regular
404
+ // HTML DOM, you probably don't want case-sensitivity, because
405
+ // browsers tend to disagree about whether elements & attributes
406
+ // should be upper/lower case. If you're running xpaths in an
407
+ // XSLT instance, you probably DO want case sensitivity, as per the
408
+ // XSL spec.
409
+
410
+ function ExprContext(node, opt_position, opt_nodelist, opt_parent,
411
+ opt_caseInsensitive, opt_ignoreAttributesWithoutValue,
412
+ opt_returnOnFirstMatch)
413
+ {
414
+ this.node = node;
415
+ this.position = opt_position || 0;
416
+ this.nodelist = opt_nodelist || [ node ];
417
+ this.variables = {};
418
+ this.parent = opt_parent || null;
419
+ this.caseInsensitive = opt_caseInsensitive || false;
420
+ this.ignoreAttributesWithoutValue = opt_ignoreAttributesWithoutValue || false;
421
+ this.returnOnFirstMatch = opt_returnOnFirstMatch || false;
422
+ if (opt_parent) {
423
+ this.root = opt_parent.root;
424
+ } else if (this.node.nodeType == DOM_DOCUMENT_NODE) {
425
+ // NOTE(mesch): DOM Spec stipulates that the ownerDocument of a
426
+ // document is null. Our root, however is the document that we are
427
+ // processing, so the initial context is created from its document
428
+ // node, which case we must handle here explcitly.
429
+ this.root = node;
430
+ } else {
431
+ this.root = node.ownerDocument;
432
+ }
433
+ }
434
+
435
+ ExprContext.prototype.clone = function(opt_node, opt_position, opt_nodelist) {
436
+ return new ExprContext(
437
+ opt_node || this.node,
438
+ typeof opt_position != 'undefined' ? opt_position : this.position,
439
+ opt_nodelist || this.nodelist, this, this.caseInsensitive,
440
+ this.ignoreAttributesWithoutValue, this.returnOnFirstMatch);
441
+ };
442
+
443
+ ExprContext.prototype.setVariable = function(name, value) {
444
+ if (value instanceof StringValue || value instanceof BooleanValue ||
445
+ value instanceof NumberValue || value instanceof NodeSetValue) {
446
+ this.variables[name] = value;
447
+ return;
448
+ }
449
+ if ('true' === value) {
450
+ this.variables[name] = new BooleanValue(true);
451
+ } else if ('false' === value) {
452
+ this.variables[name] = new BooleanValue(false);
453
+ } else if (TOK_NUMBER.re.test(value)) {
454
+ this.variables[name] = new NumberValue(value);
455
+ } else {
456
+ // DGF What if it's null?
457
+ this.variables[name] = new StringValue(value);
458
+ }
459
+ };
460
+
461
+ ExprContext.prototype.getVariable = function(name) {
462
+ if (typeof this.variables[name] != 'undefined') {
463
+ return this.variables[name];
464
+
465
+ } else if (this.parent) {
466
+ return this.parent.getVariable(name);
467
+
468
+ } else {
469
+ return null;
470
+ }
471
+ };
472
+
473
+ ExprContext.prototype.setNode = function(position) {
474
+ this.node = this.nodelist[position];
475
+ this.position = position;
476
+ };
477
+
478
+ ExprContext.prototype.contextSize = function() {
479
+ return this.nodelist.length;
480
+ };
481
+
482
+ ExprContext.prototype.isCaseInsensitive = function() {
483
+ return this.caseInsensitive;
484
+ };
485
+
486
+ ExprContext.prototype.setCaseInsensitive = function(caseInsensitive) {
487
+ return this.caseInsensitive = caseInsensitive;
488
+ };
489
+
490
+ ExprContext.prototype.isIgnoreAttributesWithoutValue = function() {
491
+ return this.ignoreAttributesWithoutValue;
492
+ };
493
+
494
+ ExprContext.prototype.setIgnoreAttributesWithoutValue = function(ignore) {
495
+ return this.ignoreAttributesWithoutValue = ignore;
496
+ };
497
+
498
+ ExprContext.prototype.isReturnOnFirstMatch = function() {
499
+ return this.returnOnFirstMatch;
500
+ };
501
+
502
+ ExprContext.prototype.setReturnOnFirstMatch = function(returnOnFirstMatch) {
503
+ return this.returnOnFirstMatch = returnOnFirstMatch;
504
+ };
505
+
506
+ // XPath expression values. They are what XPath expressions evaluate
507
+ // to. Strangely, the different value types are not specified in the
508
+ // XPath syntax, but only in the semantics, so they don't show up as
509
+ // nonterminals in the grammar. Yet, some expressions are required to
510
+ // evaluate to particular types, and not every type can be coerced
511
+ // into every other type. Although the types of XPath values are
512
+ // similar to the types present in JavaScript, the type coercion rules
513
+ // are a bit peculiar, so we explicitly model XPath types instead of
514
+ // mapping them onto JavaScript types. (See XPath spec.)
515
+ //
516
+ // The four types are:
517
+ //
518
+ // StringValue
519
+ //
520
+ // NumberValue
521
+ //
522
+ // BooleanValue
523
+ //
524
+ // NodeSetValue
525
+ //
526
+ // The common interface of the value classes consists of methods that
527
+ // implement the XPath type coercion rules:
528
+ //
529
+ // stringValue() -- returns the value as a JavaScript String,
530
+ //
531
+ // numberValue() -- returns the value as a JavaScript Number,
532
+ //
533
+ // booleanValue() -- returns the value as a JavaScript Boolean,
534
+ //
535
+ // nodeSetValue() -- returns the value as a JavaScript Array of DOM
536
+ // Node objects.
537
+ //
538
+
539
+ function StringValue(value) {
540
+ this.value = value;
541
+ this.type = 'string';
542
+ }
543
+
544
+ StringValue.prototype.stringValue = function() {
545
+ return this.value;
546
+ }
547
+
548
+ StringValue.prototype.booleanValue = function() {
549
+ return this.value.length > 0;
550
+ }
551
+
552
+ StringValue.prototype.numberValue = function() {
553
+ return this.value - 0;
554
+ }
555
+
556
+ StringValue.prototype.nodeSetValue = function() {
557
+ throw this;
558
+ }
559
+
560
+ function BooleanValue(value) {
561
+ this.value = value;
562
+ this.type = 'boolean';
563
+ }
564
+
565
+ BooleanValue.prototype.stringValue = function() {
566
+ return '' + this.value;
567
+ }
568
+
569
+ BooleanValue.prototype.booleanValue = function() {
570
+ return this.value;
571
+ }
572
+
573
+ BooleanValue.prototype.numberValue = function() {
574
+ return this.value ? 1 : 0;
575
+ }
576
+
577
+ BooleanValue.prototype.nodeSetValue = function() {
578
+ throw this;
579
+ }
580
+
581
+ function NumberValue(value) {
582
+ this.value = value;
583
+ this.type = 'number';
584
+ }
585
+
586
+ NumberValue.prototype.stringValue = function() {
587
+ return '' + this.value;
588
+ }
589
+
590
+ NumberValue.prototype.booleanValue = function() {
591
+ return !!this.value;
592
+ }
593
+
594
+ NumberValue.prototype.numberValue = function() {
595
+ return this.value - 0;
596
+ }
597
+
598
+ NumberValue.prototype.nodeSetValue = function() {
599
+ throw this;
600
+ }
601
+
602
+ function NodeSetValue(value) {
603
+ this.value = value;
604
+ this.type = 'node-set';
605
+ }
606
+
607
+ NodeSetValue.prototype.stringValue = function() {
608
+ if (this.value.length == 0) {
609
+ return '';
610
+ } else {
611
+ return xmlValue(this.value[0]);
612
+ }
613
+ }
614
+
615
+ NodeSetValue.prototype.booleanValue = function() {
616
+ return this.value.length > 0;
617
+ }
618
+
619
+ NodeSetValue.prototype.numberValue = function() {
620
+ return this.stringValue() - 0;
621
+ }
622
+
623
+ NodeSetValue.prototype.nodeSetValue = function() {
624
+ return this.value;
625
+ };
626
+
627
+ // XPath expressions. They are used as nodes in the parse tree and
628
+ // possess an evaluate() method to compute an XPath value given an XPath
629
+ // context. Expressions are returned from the parser. Teh set of
630
+ // expression classes closely mirrors the set of non terminal symbols
631
+ // in the grammar. Every non trivial nonterminal symbol has a
632
+ // corresponding expression class.
633
+ //
634
+ // The common expression interface consists of the following methods:
635
+ //
636
+ // evaluate(context) -- evaluates the expression, returns a value.
637
+ //
638
+ // toString() -- returns the XPath text representation of the
639
+ // expression (defined in xsltdebug.js).
640
+ //
641
+ // parseTree(indent) -- returns a parse tree representation of the
642
+ // expression (defined in xsltdebug.js).
643
+
644
+ function TokenExpr(m) {
645
+ this.value = m;
646
+ }
647
+
648
+ TokenExpr.prototype.evaluate = function() {
649
+ return new StringValue(this.value);
650
+ };
651
+
652
+ function LocationExpr() {
653
+ this.absolute = false;
654
+ this.steps = [];
655
+ }
656
+
657
+ LocationExpr.prototype.appendStep = function(s) {
658
+ var combinedStep = this._combineSteps(this.steps[this.steps.length-1], s);
659
+ if (combinedStep) {
660
+ this.steps[this.steps.length-1] = combinedStep;
661
+ } else {
662
+ this.steps.push(s);
663
+ }
664
+ }
665
+
666
+ LocationExpr.prototype.prependStep = function(s) {
667
+ var combinedStep = this._combineSteps(s, this.steps[0]);
668
+ if (combinedStep) {
669
+ this.steps[0] = combinedStep;
670
+ } else {
671
+ this.steps.unshift(s);
672
+ }
673
+ };
674
+
675
+ // DGF try to combine two steps into one step (perf enhancement)
676
+ LocationExpr.prototype._combineSteps = function(prevStep, nextStep) {
677
+ if (!prevStep) return null;
678
+ if (!nextStep) return null;
679
+ var hasPredicates = (prevStep.predicates && prevStep.predicates.length > 0);
680
+ if (prevStep.nodetest instanceof NodeTestAny && !hasPredicates) {
681
+ // maybe suitable to be combined
682
+ if (prevStep.axis == xpathAxis.DESCENDANT_OR_SELF) {
683
+ if (nextStep.axis == xpathAxis.CHILD) {
684
+ // HBC - commenting out, because this is not a valid reduction
685
+ //nextStep.axis = xpathAxis.DESCENDANT;
686
+ //return nextStep;
687
+ } else if (nextStep.axis == xpathAxis.SELF) {
688
+ nextStep.axis = xpathAxis.DESCENDANT_OR_SELF;
689
+ return nextStep;
690
+ }
691
+ } else if (prevStep.axis == xpathAxis.DESCENDANT) {
692
+ if (nextStep.axis == xpathAxis.SELF) {
693
+ nextStep.axis = xpathAxis.DESCENDANT;
694
+ return nextStep;
695
+ }
696
+ }
697
+ }
698
+ return null;
699
+ }
700
+
701
+ LocationExpr.prototype.evaluate = function(ctx) {
702
+ var start;
703
+ if (this.absolute) {
704
+ start = ctx.root;
705
+
706
+ } else {
707
+ start = ctx.node;
708
+ }
709
+
710
+ var nodes = [];
711
+ xPathStep(nodes, this.steps, 0, start, ctx);
712
+ return new NodeSetValue(nodes);
713
+ };
714
+
715
+ function xPathStep(nodes, steps, step, input, ctx) {
716
+ var s = steps[step];
717
+ var ctx2 = ctx.clone(input);
718
+
719
+ if (ctx.returnOnFirstMatch && !s.hasPositionalPredicate) {
720
+ var nodelist = s.evaluate(ctx2).nodeSetValue();
721
+ // the predicates were not processed in the last evaluate(), so that we can
722
+ // process them here with the returnOnFirstMatch optimization. We do a
723
+ // depth-first grab at any nodes that pass the predicate tests. There is no
724
+ // way to optimize when predicates contain positional selectors, including
725
+ // indexes or uses of the last() or position() functions, because they
726
+ // typically require the entire nodelist for context. Process without
727
+ // optimization if we encounter such selectors.
728
+ var nLength = nodelist.length;
729
+ var pLength = s.predicate.length;
730
+ nodelistLoop:
731
+ for (var i = 0; i < nLength; ++i) {
732
+ var n = nodelist[i];
733
+ for (var j = 0; j < pLength; ++j) {
734
+ if (!s.predicate[j].evaluate(ctx.clone(n, i, nodelist)).booleanValue()) {
735
+ continue nodelistLoop;
736
+ }
737
+ }
738
+ // n survived the predicate tests!
739
+ if (step == steps.length - 1) {
740
+ nodes.push(n);
741
+ }
742
+ else {
743
+ xPathStep(nodes, steps, step + 1, n, ctx);
744
+ }
745
+ if (nodes.length > 0) {
746
+ break;
747
+ }
748
+ }
749
+ }
750
+ else {
751
+ // set returnOnFirstMatch to false for the cloned ExprContext, because
752
+ // behavior in StepExpr.prototype.evaluate is driven off its value. Note
753
+ // that the original context may still have true for this value.
754
+ ctx2.returnOnFirstMatch = false;
755
+ var nodelist = s.evaluate(ctx2).nodeSetValue();
756
+ for (var i = 0; i < nodelist.length; ++i) {
757
+ if (step == steps.length - 1) {
758
+ nodes.push(nodelist[i]);
759
+ } else {
760
+ xPathStep(nodes, steps, step + 1, nodelist[i], ctx);
761
+ }
762
+ }
763
+ }
764
+ }
765
+
766
+ function StepExpr(axis, nodetest, opt_predicate) {
767
+ this.axis = axis;
768
+ this.nodetest = nodetest;
769
+ this.predicate = opt_predicate || [];
770
+ this.hasPositionalPredicate = false;
771
+ for (var i = 0; i < this.predicate.length; ++i) {
772
+ if (predicateExprHasPositionalSelector(this.predicate[i].expr)) {
773
+ this.hasPositionalPredicate = true;
774
+ break;
775
+ }
776
+ }
777
+ }
778
+
779
+ StepExpr.prototype.appendPredicate = function(p) {
780
+ this.predicate.push(p);
781
+ if (!this.hasPositionalPredicate) {
782
+ this.hasPositionalPredicate = predicateExprHasPositionalSelector(p.expr);
783
+ }
784
+ }
785
+
786
+ StepExpr.prototype.evaluate = function(ctx) {
787
+ var input = ctx.node;
788
+ var nodelist = [];
789
+ var skipNodeTest = false;
790
+
791
+ if (this.nodetest instanceof NodeTestAny) {
792
+ skipNodeTest = true;
793
+ }
794
+
795
+ // NOTE(mesch): When this was a switch() statement, it didn't work
796
+ // in Safari/2.0. Not sure why though; it resulted in the JavaScript
797
+ // console output "undefined" (without any line number or so).
798
+
799
+ if (this.axis == xpathAxis.ANCESTOR_OR_SELF) {
800
+ nodelist.push(input);
801
+ for (var n = input.parentNode; n; n = n.parentNode) {
802
+ nodelist.push(n);
803
+ }
804
+
805
+ } else if (this.axis == xpathAxis.ANCESTOR) {
806
+ for (var n = input.parentNode; n; n = n.parentNode) {
807
+ nodelist.push(n);
808
+ }
809
+
810
+ } else if (this.axis == xpathAxis.ATTRIBUTE) {
811
+ if (this.nodetest.name != undefined) {
812
+ // single-attribute step
813
+ if (input.attributes) {
814
+ if (input.attributes instanceof Array) {
815
+ // probably evaluating on document created by xmlParse()
816
+ copyArray(nodelist, input.attributes);
817
+ }
818
+ else {
819
+ if (this.nodetest.name == 'style') {
820
+ var value = input.getAttribute('style');
821
+ if (value && typeof(value) != 'string') {
822
+ // this is the case where indexing into the attributes array
823
+ // doesn't give us the attribute node in IE - we create our own
824
+ // node instead
825
+ nodelist.push(XNode.create(DOM_ATTRIBUTE_NODE, 'style',
826
+ value.cssText, document));
827
+ }
828
+ else {
829
+ nodelist.push(input.attributes[this.nodetest.name]);
830
+ }
831
+ }
832
+ else {
833
+ nodelist.push(input.attributes[this.nodetest.name]);
834
+ }
835
+ }
836
+ }
837
+ }
838
+ else {
839
+ // all-attributes step
840
+ if (ctx.ignoreAttributesWithoutValue) {
841
+ copyArrayIgnoringAttributesWithoutValue(nodelist, input.attributes);
842
+ }
843
+ else {
844
+ copyArray(nodelist, input.attributes);
845
+ }
846
+ }
847
+
848
+ } else if (this.axis == xpathAxis.CHILD) {
849
+ copyArray(nodelist, input.childNodes);
850
+
851
+ } else if (this.axis == xpathAxis.DESCENDANT_OR_SELF) {
852
+ if (this.nodetest.evaluate(ctx).booleanValue()) {
853
+ nodelist.push(input);
854
+ }
855
+ var tagName = xpathExtractTagNameFromNodeTest(this.nodetest);
856
+ xpathCollectDescendants(nodelist, input, tagName);
857
+ if (tagName) skipNodeTest = true;
858
+
859
+ } else if (this.axis == xpathAxis.DESCENDANT) {
860
+ var tagName = xpathExtractTagNameFromNodeTest(this.nodetest);
861
+ xpathCollectDescendants(nodelist, input, tagName);
862
+ if (tagName) skipNodeTest = true;
863
+
864
+ } else if (this.axis == xpathAxis.FOLLOWING) {
865
+ for (var n = input; n; n = n.parentNode) {
866
+ for (var nn = n.nextSibling; nn; nn = nn.nextSibling) {
867
+ nodelist.push(nn);
868
+ xpathCollectDescendants(nodelist, nn);
869
+ }
870
+ }
871
+
872
+ } else if (this.axis == xpathAxis.FOLLOWING_SIBLING) {
873
+ for (var n = input.nextSibling; n; n = n.nextSibling) {
874
+ nodelist.push(n);
875
+ }
876
+
877
+ } else if (this.axis == xpathAxis.NAMESPACE) {
878
+ alert('not implemented: axis namespace');
879
+
880
+ } else if (this.axis == xpathAxis.PARENT) {
881
+ if (input.parentNode) {
882
+ nodelist.push(input.parentNode);
883
+ }
884
+
885
+ } else if (this.axis == xpathAxis.PRECEDING) {
886
+ for (var n = input; n; n = n.parentNode) {
887
+ for (var nn = n.previousSibling; nn; nn = nn.previousSibling) {
888
+ nodelist.push(nn);
889
+ xpathCollectDescendantsReverse(nodelist, nn);
890
+ }
891
+ }
892
+
893
+ } else if (this.axis == xpathAxis.PRECEDING_SIBLING) {
894
+ for (var n = input.previousSibling; n; n = n.previousSibling) {
895
+ nodelist.push(n);
896
+ }
897
+
898
+ } else if (this.axis == xpathAxis.SELF) {
899
+ nodelist.push(input);
900
+
901
+ } else {
902
+ throw 'ERROR -- NO SUCH AXIS: ' + this.axis;
903
+ }
904
+
905
+ if (!skipNodeTest) {
906
+ // process node test
907
+ var nodelist0 = nodelist;
908
+ nodelist = [];
909
+ for (var i = 0; i < nodelist0.length; ++i) {
910
+ var n = nodelist0[i];
911
+ if (this.nodetest.evaluate(ctx.clone(n, i, nodelist0)).booleanValue()) {
912
+ nodelist.push(n);
913
+ }
914
+ }
915
+ }
916
+
917
+ // process predicates
918
+ if (!ctx.returnOnFirstMatch) {
919
+ for (var i = 0; i < this.predicate.length; ++i) {
920
+ var nodelist0 = nodelist;
921
+ nodelist = [];
922
+ for (var ii = 0; ii < nodelist0.length; ++ii) {
923
+ var n = nodelist0[ii];
924
+ if (this.predicate[i].evaluate(ctx.clone(n, ii, nodelist0)).booleanValue()) {
925
+ nodelist.push(n);
926
+ }
927
+ }
928
+ }
929
+ }
930
+
931
+ return new NodeSetValue(nodelist);
932
+ };
933
+
934
+ function NodeTestAny() {
935
+ this.value = new BooleanValue(true);
936
+ }
937
+
938
+ NodeTestAny.prototype.evaluate = function(ctx) {
939
+ return this.value;
940
+ };
941
+
942
+ function NodeTestElementOrAttribute() {}
943
+
944
+ NodeTestElementOrAttribute.prototype.evaluate = function(ctx) {
945
+ return new BooleanValue(
946
+ ctx.node.nodeType == DOM_ELEMENT_NODE ||
947
+ ctx.node.nodeType == DOM_ATTRIBUTE_NODE);
948
+ }
949
+
950
+ function NodeTestText() {}
951
+
952
+ NodeTestText.prototype.evaluate = function(ctx) {
953
+ return new BooleanValue(ctx.node.nodeType == DOM_TEXT_NODE);
954
+ }
955
+
956
+ function NodeTestComment() {}
957
+
958
+ NodeTestComment.prototype.evaluate = function(ctx) {
959
+ return new BooleanValue(ctx.node.nodeType == DOM_COMMENT_NODE);
960
+ }
961
+
962
+ function NodeTestPI(target) {
963
+ this.target = target;
964
+ }
965
+
966
+ NodeTestPI.prototype.evaluate = function(ctx) {
967
+ return new
968
+ BooleanValue(ctx.node.nodeType == DOM_PROCESSING_INSTRUCTION_NODE &&
969
+ (!this.target || ctx.node.nodeName == this.target));
970
+ }
971
+
972
+ function NodeTestNC(nsprefix) {
973
+ this.regex = new RegExp("^" + nsprefix + ":");
974
+ this.nsprefix = nsprefix;
975
+ }
976
+
977
+ NodeTestNC.prototype.evaluate = function(ctx) {
978
+ var n = ctx.node;
979
+ return new BooleanValue(this.regex.match(n.nodeName));
980
+ }
981
+
982
+ function NodeTestName(name) {
983
+ this.name = name;
984
+ this.re = new RegExp('^' + name + '$', "i");
985
+ }
986
+
987
+ NodeTestName.prototype.evaluate = function(ctx) {
988
+ var n = ctx.node;
989
+ if (ctx.caseInsensitive) {
990
+ if (n.nodeName.length != this.name.length) return new BooleanValue(false);
991
+ return new BooleanValue(this.re.test(n.nodeName));
992
+ } else {
993
+ return new BooleanValue(n.nodeName == this.name);
994
+ }
995
+ }
996
+
997
+ function PredicateExpr(expr) {
998
+ this.expr = expr;
999
+ }
1000
+
1001
+ PredicateExpr.prototype.evaluate = function(ctx) {
1002
+ var v = this.expr.evaluate(ctx);
1003
+ if (v.type == 'number') {
1004
+ // NOTE(mesch): Internally, position is represented starting with
1005
+ // 0, however in XPath position starts with 1. See functions
1006
+ // position() and last().
1007
+ return new BooleanValue(ctx.position == v.numberValue() - 1);
1008
+ } else {
1009
+ return new BooleanValue(v.booleanValue());
1010
+ }
1011
+ };
1012
+
1013
+ function FunctionCallExpr(name) {
1014
+ this.name = name;
1015
+ this.args = [];
1016
+ }
1017
+
1018
+ FunctionCallExpr.prototype.appendArg = function(arg) {
1019
+ this.args.push(arg);
1020
+ };
1021
+
1022
+ FunctionCallExpr.prototype.evaluate = function(ctx) {
1023
+ var fn = '' + this.name.value;
1024
+ var f = this.xpathfunctions[fn];
1025
+ if (f) {
1026
+ return f.call(this, ctx);
1027
+ } else {
1028
+ xpathLog('XPath NO SUCH FUNCTION ' + fn);
1029
+ return new BooleanValue(false);
1030
+ }
1031
+ };
1032
+
1033
+ FunctionCallExpr.prototype.xpathfunctions = {
1034
+ 'last': function(ctx) {
1035
+ assert(this.args.length == 0);
1036
+ // NOTE(mesch): XPath position starts at 1.
1037
+ return new NumberValue(ctx.contextSize());
1038
+ },
1039
+
1040
+ 'position': function(ctx) {
1041
+ assert(this.args.length == 0);
1042
+ // NOTE(mesch): XPath position starts at 1.
1043
+ return new NumberValue(ctx.position + 1);
1044
+ },
1045
+
1046
+ 'count': function(ctx) {
1047
+ assert(this.args.length == 1);
1048
+ var v = this.args[0].evaluate(ctx);
1049
+ return new NumberValue(v.nodeSetValue().length);
1050
+ },
1051
+
1052
+ 'id': function(ctx) {
1053
+ assert(this.args.length == 1);
1054
+ var e = this.args[0].evaluate(ctx);
1055
+ var ret = [];
1056
+ var ids;
1057
+ if (e.type == 'node-set') {
1058
+ ids = [];
1059
+ var en = e.nodeSetValue();
1060
+ for (var i = 0; i < en.length; ++i) {
1061
+ var v = xmlValue(en[i]).split(/\s+/);
1062
+ for (var ii = 0; ii < v.length; ++ii) {
1063
+ ids.push(v[ii]);
1064
+ }
1065
+ }
1066
+ } else {
1067
+ ids = e.stringValue().split(/\s+/);
1068
+ }
1069
+ var d = ctx.root;
1070
+ for (var i = 0; i < ids.length; ++i) {
1071
+ var n = d.getElementById(ids[i]);
1072
+ if (n) {
1073
+ ret.push(n);
1074
+ }
1075
+ }
1076
+ return new NodeSetValue(ret);
1077
+ },
1078
+
1079
+ 'local-name': function(ctx) {
1080
+ alert('not implmented yet: XPath function local-name()');
1081
+ },
1082
+
1083
+ 'namespace-uri': function(ctx) {
1084
+ alert('not implmented yet: XPath function namespace-uri()');
1085
+ },
1086
+
1087
+ 'name': function(ctx) {
1088
+ assert(this.args.length == 1 || this.args.length == 0);
1089
+ var n;
1090
+ if (this.args.length == 0) {
1091
+ n = [ ctx.node ];
1092
+ } else {
1093
+ n = this.args[0].evaluate(ctx).nodeSetValue();
1094
+ }
1095
+
1096
+ if (n.length == 0) {
1097
+ return new StringValue('');
1098
+ } else {
1099
+ return new StringValue(n[0].nodeName);
1100
+ }
1101
+ },
1102
+
1103
+ 'string': function(ctx) {
1104
+ assert(this.args.length == 1 || this.args.length == 0);
1105
+ if (this.args.length == 0) {
1106
+ return new StringValue(new NodeSetValue([ ctx.node ]).stringValue());
1107
+ } else {
1108
+ return new StringValue(this.args[0].evaluate(ctx).stringValue());
1109
+ }
1110
+ },
1111
+
1112
+ 'concat': function(ctx) {
1113
+ var ret = '';
1114
+ for (var i = 0; i < this.args.length; ++i) {
1115
+ ret += this.args[i].evaluate(ctx).stringValue();
1116
+ }
1117
+ return new StringValue(ret);
1118
+ },
1119
+
1120
+ 'starts-with': function(ctx) {
1121
+ assert(this.args.length == 2);
1122
+ var s0 = this.args[0].evaluate(ctx).stringValue();
1123
+ var s1 = this.args[1].evaluate(ctx).stringValue();
1124
+ return new BooleanValue(s0.indexOf(s1) == 0);
1125
+ },
1126
+
1127
+ 'ends-with': function(ctx) {
1128
+ assert(this.args.length == 2);
1129
+ var s0 = this.args[0].evaluate(ctx).stringValue();
1130
+ var s1 = this.args[1].evaluate(ctx).stringValue();
1131
+ var re = new RegExp(RegExp.escape(s1) + '$');
1132
+ return new BooleanValue(re.test(s0));
1133
+ },
1134
+
1135
+ 'contains': function(ctx) {
1136
+ assert(this.args.length == 2);
1137
+ var s0 = this.args[0].evaluate(ctx).stringValue();
1138
+ var s1 = this.args[1].evaluate(ctx).stringValue();
1139
+ return new BooleanValue(s0.indexOf(s1) != -1);
1140
+ },
1141
+
1142
+ 'substring-before': function(ctx) {
1143
+ assert(this.args.length == 2);
1144
+ var s0 = this.args[0].evaluate(ctx).stringValue();
1145
+ var s1 = this.args[1].evaluate(ctx).stringValue();
1146
+ var i = s0.indexOf(s1);
1147
+ var ret;
1148
+ if (i == -1) {
1149
+ ret = '';
1150
+ } else {
1151
+ ret = s0.substr(0,i);
1152
+ }
1153
+ return new StringValue(ret);
1154
+ },
1155
+
1156
+ 'substring-after': function(ctx) {
1157
+ assert(this.args.length == 2);
1158
+ var s0 = this.args[0].evaluate(ctx).stringValue();
1159
+ var s1 = this.args[1].evaluate(ctx).stringValue();
1160
+ var i = s0.indexOf(s1);
1161
+ var ret;
1162
+ if (i == -1) {
1163
+ ret = '';
1164
+ } else {
1165
+ ret = s0.substr(i + s1.length);
1166
+ }
1167
+ return new StringValue(ret);
1168
+ },
1169
+
1170
+ 'substring': function(ctx) {
1171
+ // NOTE: XPath defines the position of the first character in a
1172
+ // string to be 1, in JavaScript this is 0 ([XPATH] Section 4.2).
1173
+ assert(this.args.length == 2 || this.args.length == 3);
1174
+ var s0 = this.args[0].evaluate(ctx).stringValue();
1175
+ var s1 = this.args[1].evaluate(ctx).numberValue();
1176
+ var ret;
1177
+ if (this.args.length == 2) {
1178
+ var i1 = Math.max(0, Math.round(s1) - 1);
1179
+ ret = s0.substr(i1);
1180
+
1181
+ } else {
1182
+ var s2 = this.args[2].evaluate(ctx).numberValue();
1183
+ var i0 = Math.round(s1) - 1;
1184
+ var i1 = Math.max(0, i0);
1185
+ var i2 = Math.round(s2) - Math.max(0, -i0);
1186
+ ret = s0.substr(i1, i2);
1187
+ }
1188
+ return new StringValue(ret);
1189
+ },
1190
+
1191
+ 'string-length': function(ctx) {
1192
+ var s;
1193
+ if (this.args.length > 0) {
1194
+ s = this.args[0].evaluate(ctx).stringValue();
1195
+ } else {
1196
+ s = new NodeSetValue([ ctx.node ]).stringValue();
1197
+ }
1198
+ return new NumberValue(s.length);
1199
+ },
1200
+
1201
+ 'normalize-space': function(ctx) {
1202
+ var s;
1203
+ if (this.args.length > 0) {
1204
+ s = this.args[0].evaluate(ctx).stringValue();
1205
+ } else {
1206
+ s = new NodeSetValue([ ctx.node ]).stringValue();
1207
+ }
1208
+ s = s.replace(/^\s*/,'').replace(/\s*$/,'').replace(/\s+/g, ' ');
1209
+ return new StringValue(s);
1210
+ },
1211
+
1212
+ 'translate': function(ctx) {
1213
+ assert(this.args.length == 3);
1214
+ var s0 = this.args[0].evaluate(ctx).stringValue();
1215
+ var s1 = this.args[1].evaluate(ctx).stringValue();
1216
+ var s2 = this.args[2].evaluate(ctx).stringValue();
1217
+
1218
+ for (var i = 0; i < s1.length; ++i) {
1219
+ s0 = s0.replace(new RegExp(s1.charAt(i), 'g'), s2.charAt(i));
1220
+ }
1221
+ return new StringValue(s0);
1222
+ },
1223
+
1224
+ 'matches': function(ctx) {
1225
+ assert(this.args.length >= 2);
1226
+ var s0 = this.args[0].evaluate(ctx).stringValue();
1227
+ var s1 = this.args[1].evaluate(ctx).stringValue();
1228
+ if (this.args.length > 2) {
1229
+ var s2 = this.args[2].evaluate(ctx).stringValue();
1230
+ if (/[^mi]/.test(s2)) {
1231
+ throw 'Invalid regular expression syntax: ' + s2;
1232
+ }
1233
+ }
1234
+
1235
+ try {
1236
+ var re = new RegExp(s1, s2);
1237
+ }
1238
+ catch (e) {
1239
+ throw 'Invalid matches argument: ' + s1;
1240
+ }
1241
+ return new BooleanValue(re.test(s0));
1242
+ },
1243
+
1244
+ 'boolean': function(ctx) {
1245
+ assert(this.args.length == 1);
1246
+ return new BooleanValue(this.args[0].evaluate(ctx).booleanValue());
1247
+ },
1248
+
1249
+ 'not': function(ctx) {
1250
+ assert(this.args.length == 1);
1251
+ var ret = !this.args[0].evaluate(ctx).booleanValue();
1252
+ return new BooleanValue(ret);
1253
+ },
1254
+
1255
+ 'true': function(ctx) {
1256
+ assert(this.args.length == 0);
1257
+ return new BooleanValue(true);
1258
+ },
1259
+
1260
+ 'false': function(ctx) {
1261
+ assert(this.args.length == 0);
1262
+ return new BooleanValue(false);
1263
+ },
1264
+
1265
+ 'lang': function(ctx) {
1266
+ assert(this.args.length == 1);
1267
+ var lang = this.args[0].evaluate(ctx).stringValue();
1268
+ var xmllang;
1269
+ var n = ctx.node;
1270
+ while (n && n != n.parentNode /* just in case ... */) {
1271
+ xmllang = n.getAttribute('xml:lang');
1272
+ if (xmllang) {
1273
+ break;
1274
+ }
1275
+ n = n.parentNode;
1276
+ }
1277
+ if (!xmllang) {
1278
+ return new BooleanValue(false);
1279
+ } else {
1280
+ var re = new RegExp('^' + lang + '$', 'i');
1281
+ return new BooleanValue(xmllang.match(re) ||
1282
+ xmllang.replace(/_.*$/,'').match(re));
1283
+ }
1284
+ },
1285
+
1286
+ 'number': function(ctx) {
1287
+ assert(this.args.length == 1 || this.args.length == 0);
1288
+
1289
+ if (this.args.length == 1) {
1290
+ return new NumberValue(this.args[0].evaluate(ctx).numberValue());
1291
+ } else {
1292
+ return new NumberValue(new NodeSetValue([ ctx.node ]).numberValue());
1293
+ }
1294
+ },
1295
+
1296
+ 'sum': function(ctx) {
1297
+ assert(this.args.length == 1);
1298
+ var n = this.args[0].evaluate(ctx).nodeSetValue();
1299
+ var sum = 0;
1300
+ for (var i = 0; i < n.length; ++i) {
1301
+ sum += xmlValue(n[i]) - 0;
1302
+ }
1303
+ return new NumberValue(sum);
1304
+ },
1305
+
1306
+ 'floor': function(ctx) {
1307
+ assert(this.args.length == 1);
1308
+ var num = this.args[0].evaluate(ctx).numberValue();
1309
+ return new NumberValue(Math.floor(num));
1310
+ },
1311
+
1312
+ 'ceiling': function(ctx) {
1313
+ assert(this.args.length == 1);
1314
+ var num = this.args[0].evaluate(ctx).numberValue();
1315
+ return new NumberValue(Math.ceil(num));
1316
+ },
1317
+
1318
+ 'round': function(ctx) {
1319
+ assert(this.args.length == 1);
1320
+ var num = this.args[0].evaluate(ctx).numberValue();
1321
+ return new NumberValue(Math.round(num));
1322
+ },
1323
+
1324
+ // TODO(mesch): The following functions are custom. There is a
1325
+ // standard that defines how to add functions, which should be
1326
+ // applied here.
1327
+
1328
+ 'ext-join': function(ctx) {
1329
+ assert(this.args.length == 2);
1330
+ var nodes = this.args[0].evaluate(ctx).nodeSetValue();
1331
+ var delim = this.args[1].evaluate(ctx).stringValue();
1332
+ var ret = '';
1333
+ for (var i = 0; i < nodes.length; ++i) {
1334
+ if (ret) {
1335
+ ret += delim;
1336
+ }
1337
+ ret += xmlValue(nodes[i]);
1338
+ }
1339
+ return new StringValue(ret);
1340
+ },
1341
+
1342
+ // ext-if() evaluates and returns its second argument, if the
1343
+ // boolean value of its first argument is true, otherwise it
1344
+ // evaluates and returns its third argument.
1345
+
1346
+ 'ext-if': function(ctx) {
1347
+ assert(this.args.length == 3);
1348
+ if (this.args[0].evaluate(ctx).booleanValue()) {
1349
+ return this.args[1].evaluate(ctx);
1350
+ } else {
1351
+ return this.args[2].evaluate(ctx);
1352
+ }
1353
+ },
1354
+
1355
+ // ext-cardinal() evaluates its single argument as a number, and
1356
+ // returns the current node that many times. It can be used in the
1357
+ // select attribute to iterate over an integer range.
1358
+
1359
+ 'ext-cardinal': function(ctx) {
1360
+ assert(this.args.length >= 1);
1361
+ var c = this.args[0].evaluate(ctx).numberValue();
1362
+ var ret = [];
1363
+ for (var i = 0; i < c; ++i) {
1364
+ ret.push(ctx.node);
1365
+ }
1366
+ return new NodeSetValue(ret);
1367
+ }
1368
+ };
1369
+
1370
+ function UnionExpr(expr1, expr2) {
1371
+ this.expr1 = expr1;
1372
+ this.expr2 = expr2;
1373
+ }
1374
+
1375
+ UnionExpr.prototype.evaluate = function(ctx) {
1376
+ var nodes1 = this.expr1.evaluate(ctx).nodeSetValue();
1377
+ var nodes2 = this.expr2.evaluate(ctx).nodeSetValue();
1378
+ var I1 = nodes1.length;
1379
+ for (var i2 = 0; i2 < nodes2.length; ++i2) {
1380
+ var n = nodes2[i2];
1381
+ var inBoth = false;
1382
+ for (var i1 = 0; i1 < I1; ++i1) {
1383
+ if (nodes1[i1] == n) {
1384
+ inBoth = true;
1385
+ i1 = I1; // break inner loop
1386
+ }
1387
+ }
1388
+ if (!inBoth) {
1389
+ nodes1.push(n);
1390
+ }
1391
+ }
1392
+ return new NodeSetValue(nodes1);
1393
+ };
1394
+
1395
+ function PathExpr(filter, rel) {
1396
+ this.filter = filter;
1397
+ this.rel = rel;
1398
+ }
1399
+
1400
+ PathExpr.prototype.evaluate = function(ctx) {
1401
+ // the filter expression should be evaluated in its entirety with no
1402
+ // optimization, as we can't backtrack to it after having moved on to
1403
+ // evaluating the relative location path
1404
+ var flag = ctx.returnOnFirstMatch;
1405
+ ctx.setReturnOnFirstMatch(false);
1406
+ var nodes = this.filter.evaluate(ctx).nodeSetValue();
1407
+ ctx.setReturnOnFirstMatch(flag);
1408
+
1409
+ var nodes1 = [];
1410
+ if (ctx.returnOnFirstMatch) {
1411
+ for (var i = 0; i < nodes.length; ++i) {
1412
+ nodes1 = this.rel.evaluate(ctx.clone(nodes[i], i, nodes)).nodeSetValue();
1413
+ if (nodes1.length > 0) {
1414
+ break;
1415
+ }
1416
+ }
1417
+ return new NodeSetValue(nodes1);
1418
+ }
1419
+ else {
1420
+ for (var i = 0; i < nodes.length; ++i) {
1421
+ var nodes0 = this.rel.evaluate(ctx.clone(nodes[i], i, nodes)).nodeSetValue();
1422
+ for (var ii = 0; ii < nodes0.length; ++ii) {
1423
+ nodes1.push(nodes0[ii]);
1424
+ }
1425
+ }
1426
+ return new NodeSetValue(nodes1);
1427
+ }
1428
+ };
1429
+
1430
+ function FilterExpr(expr, predicate) {
1431
+ this.expr = expr;
1432
+ this.predicate = predicate;
1433
+ }
1434
+
1435
+ FilterExpr.prototype.evaluate = function(ctx) {
1436
+ var nodes = this.expr.evaluate(ctx).nodeSetValue();
1437
+ for (var i = 0; i < this.predicate.length; ++i) {
1438
+ var nodes0 = nodes;
1439
+ nodes = [];
1440
+ for (var j = 0; j < nodes0.length; ++j) {
1441
+ var n = nodes0[j];
1442
+ if (this.predicate[i].evaluate(ctx.clone(n, j, nodes0)).booleanValue()) {
1443
+ nodes.push(n);
1444
+ }
1445
+ }
1446
+ }
1447
+
1448
+ return new NodeSetValue(nodes);
1449
+ }
1450
+
1451
+ function UnaryMinusExpr(expr) {
1452
+ this.expr = expr;
1453
+ }
1454
+
1455
+ UnaryMinusExpr.prototype.evaluate = function(ctx) {
1456
+ return new NumberValue(-this.expr.evaluate(ctx).numberValue());
1457
+ };
1458
+
1459
+ function BinaryExpr(expr1, op, expr2) {
1460
+ this.expr1 = expr1;
1461
+ this.expr2 = expr2;
1462
+ this.op = op;
1463
+ }
1464
+
1465
+ BinaryExpr.prototype.evaluate = function(ctx) {
1466
+ var ret;
1467
+ switch (this.op.value) {
1468
+ case 'or':
1469
+ ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() ||
1470
+ this.expr2.evaluate(ctx).booleanValue());
1471
+ break;
1472
+
1473
+ case 'and':
1474
+ ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() &&
1475
+ this.expr2.evaluate(ctx).booleanValue());
1476
+ break;
1477
+
1478
+ case '+':
1479
+ ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() +
1480
+ this.expr2.evaluate(ctx).numberValue());
1481
+ break;
1482
+
1483
+ case '-':
1484
+ ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() -
1485
+ this.expr2.evaluate(ctx).numberValue());
1486
+ break;
1487
+
1488
+ case '*':
1489
+ ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() *
1490
+ this.expr2.evaluate(ctx).numberValue());
1491
+ break;
1492
+
1493
+ case 'mod':
1494
+ ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() %
1495
+ this.expr2.evaluate(ctx).numberValue());
1496
+ break;
1497
+
1498
+ case 'div':
1499
+ ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() /
1500
+ this.expr2.evaluate(ctx).numberValue());
1501
+ break;
1502
+
1503
+ case '=':
1504
+ ret = this.compare(ctx, function(x1, x2) { return x1 == x2; });
1505
+ break;
1506
+
1507
+ case '!=':
1508
+ ret = this.compare(ctx, function(x1, x2) { return x1 != x2; });
1509
+ break;
1510
+
1511
+ case '<':
1512
+ ret = this.compare(ctx, function(x1, x2) { return x1 < x2; });
1513
+ break;
1514
+
1515
+ case '<=':
1516
+ ret = this.compare(ctx, function(x1, x2) { return x1 <= x2; });
1517
+ break;
1518
+
1519
+ case '>':
1520
+ ret = this.compare(ctx, function(x1, x2) { return x1 > x2; });
1521
+ break;
1522
+
1523
+ case '>=':
1524
+ ret = this.compare(ctx, function(x1, x2) { return x1 >= x2; });
1525
+ break;
1526
+
1527
+ default:
1528
+ alert('BinaryExpr.evaluate: ' + this.op.value);
1529
+ }
1530
+ return ret;
1531
+ };
1532
+
1533
+ BinaryExpr.prototype.compare = function(ctx, cmp) {
1534
+ var v1 = this.expr1.evaluate(ctx);
1535
+ var v2 = this.expr2.evaluate(ctx);
1536
+
1537
+ var ret;
1538
+ if (v1.type == 'node-set' && v2.type == 'node-set') {
1539
+ var n1 = v1.nodeSetValue();
1540
+ var n2 = v2.nodeSetValue();
1541
+ ret = false;
1542
+ for (var i1 = 0; i1 < n1.length; ++i1) {
1543
+ for (var i2 = 0; i2 < n2.length; ++i2) {
1544
+ if (cmp(xmlValue(n1[i1]), xmlValue(n2[i2]))) {
1545
+ ret = true;
1546
+ // Break outer loop. Labels confuse the jscompiler and we
1547
+ // don't use them.
1548
+ i2 = n2.length;
1549
+ i1 = n1.length;
1550
+ }
1551
+ }
1552
+ }
1553
+
1554
+ } else if (v1.type == 'node-set' || v2.type == 'node-set') {
1555
+
1556
+ if (v1.type == 'number') {
1557
+ var s = v1.numberValue();
1558
+ var n = v2.nodeSetValue();
1559
+
1560
+ ret = false;
1561
+ for (var i = 0; i < n.length; ++i) {
1562
+ var nn = xmlValue(n[i]) - 0;
1563
+ if (cmp(s, nn)) {
1564
+ ret = true;
1565
+ break;
1566
+ }
1567
+ }
1568
+
1569
+ } else if (v2.type == 'number') {
1570
+ var n = v1.nodeSetValue();
1571
+ var s = v2.numberValue();
1572
+
1573
+ ret = false;
1574
+ for (var i = 0; i < n.length; ++i) {
1575
+ var nn = xmlValue(n[i]) - 0;
1576
+ if (cmp(nn, s)) {
1577
+ ret = true;
1578
+ break;
1579
+ }
1580
+ }
1581
+
1582
+ } else if (v1.type == 'string') {
1583
+ var s = v1.stringValue();
1584
+ var n = v2.nodeSetValue();
1585
+
1586
+ ret = false;
1587
+ for (var i = 0; i < n.length; ++i) {
1588
+ var nn = xmlValue(n[i]);
1589
+ if (cmp(s, nn)) {
1590
+ ret = true;
1591
+ break;
1592
+ }
1593
+ }
1594
+
1595
+ } else if (v2.type == 'string') {
1596
+ var n = v1.nodeSetValue();
1597
+ var s = v2.stringValue();
1598
+
1599
+ ret = false;
1600
+ for (var i = 0; i < n.length; ++i) {
1601
+ var nn = xmlValue(n[i]);
1602
+ if (cmp(nn, s)) {
1603
+ ret = true;
1604
+ break;
1605
+ }
1606
+ }
1607
+
1608
+ } else {
1609
+ ret = cmp(v1.booleanValue(), v2.booleanValue());
1610
+ }
1611
+
1612
+ } else if (v1.type == 'boolean' || v2.type == 'boolean') {
1613
+ ret = cmp(v1.booleanValue(), v2.booleanValue());
1614
+
1615
+ } else if (v1.type == 'number' || v2.type == 'number') {
1616
+ ret = cmp(v1.numberValue(), v2.numberValue());
1617
+
1618
+ } else {
1619
+ ret = cmp(v1.stringValue(), v2.stringValue());
1620
+ }
1621
+
1622
+ return new BooleanValue(ret);
1623
+ }
1624
+
1625
+ function LiteralExpr(value) {
1626
+ this.value = value;
1627
+ }
1628
+
1629
+ LiteralExpr.prototype.evaluate = function(ctx) {
1630
+ return new StringValue(this.value);
1631
+ };
1632
+
1633
+ function NumberExpr(value) {
1634
+ this.value = value;
1635
+ }
1636
+
1637
+ NumberExpr.prototype.evaluate = function(ctx) {
1638
+ return new NumberValue(this.value);
1639
+ };
1640
+
1641
+ function VariableExpr(name) {
1642
+ this.name = name;
1643
+ }
1644
+
1645
+ VariableExpr.prototype.evaluate = function(ctx) {
1646
+ return ctx.getVariable(this.name);
1647
+ }
1648
+
1649
+ // Factory functions for semantic values (i.e. Expressions) of the
1650
+ // productions in the grammar. When a production is matched to reduce
1651
+ // the current parse state stack, the function is called with the
1652
+ // semantic values of the matched elements as arguments, and returns
1653
+ // another semantic value. The semantic value is a node of the parse
1654
+ // tree, an expression object with an evaluate() method that evaluates the
1655
+ // expression in an actual context. These factory functions are used
1656
+ // in the specification of the grammar rules, below.
1657
+
1658
+ function makeTokenExpr(m) {
1659
+ return new TokenExpr(m);
1660
+ }
1661
+
1662
+ function passExpr(e) {
1663
+ return e;
1664
+ }
1665
+
1666
+ function makeLocationExpr1(slash, rel) {
1667
+ rel.absolute = true;
1668
+ return rel;
1669
+ }
1670
+
1671
+ function makeLocationExpr2(dslash, rel) {
1672
+ rel.absolute = true;
1673
+ rel.prependStep(makeAbbrevStep(dslash.value));
1674
+ return rel;
1675
+ }
1676
+
1677
+ function makeLocationExpr3(slash) {
1678
+ var ret = new LocationExpr();
1679
+ ret.appendStep(makeAbbrevStep('.'));
1680
+ ret.absolute = true;
1681
+ return ret;
1682
+ }
1683
+
1684
+ function makeLocationExpr4(dslash) {
1685
+ var ret = new LocationExpr();
1686
+ ret.absolute = true;
1687
+ ret.appendStep(makeAbbrevStep(dslash.value));
1688
+ return ret;
1689
+ }
1690
+
1691
+ function makeLocationExpr5(step) {
1692
+ var ret = new LocationExpr();
1693
+ ret.appendStep(step);
1694
+ return ret;
1695
+ }
1696
+
1697
+ function makeLocationExpr6(rel, slash, step) {
1698
+ rel.appendStep(step);
1699
+ return rel;
1700
+ }
1701
+
1702
+ function makeLocationExpr7(rel, dslash, step) {
1703
+ rel.appendStep(makeAbbrevStep(dslash.value));
1704
+ rel.appendStep(step);
1705
+ return rel;
1706
+ }
1707
+
1708
+ function makeStepExpr1(dot) {
1709
+ return makeAbbrevStep(dot.value);
1710
+ }
1711
+
1712
+ function makeStepExpr2(ddot) {
1713
+ return makeAbbrevStep(ddot.value);
1714
+ }
1715
+
1716
+ function makeStepExpr3(axisname, axis, nodetest) {
1717
+ return new StepExpr(axisname.value, nodetest);
1718
+ }
1719
+
1720
+ function makeStepExpr4(at, nodetest) {
1721
+ return new StepExpr('attribute', nodetest);
1722
+ }
1723
+
1724
+ function makeStepExpr5(nodetest) {
1725
+ return new StepExpr('child', nodetest);
1726
+ }
1727
+
1728
+ function makeStepExpr6(step, predicate) {
1729
+ step.appendPredicate(predicate);
1730
+ return step;
1731
+ }
1732
+
1733
+ function makeAbbrevStep(abbrev) {
1734
+ switch (abbrev) {
1735
+ case '//':
1736
+ return new StepExpr('descendant-or-self', new NodeTestAny);
1737
+
1738
+ case '.':
1739
+ return new StepExpr('self', new NodeTestAny);
1740
+
1741
+ case '..':
1742
+ return new StepExpr('parent', new NodeTestAny);
1743
+ }
1744
+ }
1745
+
1746
+ function makeNodeTestExpr1(asterisk) {
1747
+ return new NodeTestElementOrAttribute;
1748
+ }
1749
+
1750
+ function makeNodeTestExpr2(ncname, colon, asterisk) {
1751
+ return new NodeTestNC(ncname.value);
1752
+ }
1753
+
1754
+ function makeNodeTestExpr3(qname) {
1755
+ return new NodeTestName(qname.value);
1756
+ }
1757
+
1758
+ function makeNodeTestExpr4(typeo, parenc) {
1759
+ var type = typeo.value.replace(/\s*\($/, '');
1760
+ switch(type) {
1761
+ case 'node':
1762
+ return new NodeTestAny;
1763
+
1764
+ case 'text':
1765
+ return new NodeTestText;
1766
+
1767
+ case 'comment':
1768
+ return new NodeTestComment;
1769
+
1770
+ case 'processing-instruction':
1771
+ return new NodeTestPI('');
1772
+ }
1773
+ }
1774
+
1775
+ function makeNodeTestExpr5(typeo, target, parenc) {
1776
+ var type = typeo.replace(/\s*\($/, '');
1777
+ if (type != 'processing-instruction') {
1778
+ throw type;
1779
+ }
1780
+ return new NodeTestPI(target.value);
1781
+ }
1782
+
1783
+ function makePredicateExpr(pareno, expr, parenc) {
1784
+ return new PredicateExpr(expr);
1785
+ }
1786
+
1787
+ function makePrimaryExpr(pareno, expr, parenc) {
1788
+ return expr;
1789
+ }
1790
+
1791
+ function makeFunctionCallExpr1(name, pareno, parenc) {
1792
+ return new FunctionCallExpr(name);
1793
+ }
1794
+
1795
+ function makeFunctionCallExpr2(name, pareno, arg1, args, parenc) {
1796
+ var ret = new FunctionCallExpr(name);
1797
+ ret.appendArg(arg1);
1798
+ for (var i = 0; i < args.length; ++i) {
1799
+ ret.appendArg(args[i]);
1800
+ }
1801
+ return ret;
1802
+ }
1803
+
1804
+ function makeArgumentExpr(comma, expr) {
1805
+ return expr;
1806
+ }
1807
+
1808
+ function makeUnionExpr(expr1, pipe, expr2) {
1809
+ return new UnionExpr(expr1, expr2);
1810
+ }
1811
+
1812
+ function makePathExpr1(filter, slash, rel) {
1813
+ return new PathExpr(filter, rel);
1814
+ }
1815
+
1816
+ function makePathExpr2(filter, dslash, rel) {
1817
+ rel.prependStep(makeAbbrevStep(dslash.value));
1818
+ return new PathExpr(filter, rel);
1819
+ }
1820
+
1821
+ function makeFilterExpr(expr, predicates) {
1822
+ if (predicates.length > 0) {
1823
+ return new FilterExpr(expr, predicates);
1824
+ } else {
1825
+ return expr;
1826
+ }
1827
+ }
1828
+
1829
+ function makeUnaryMinusExpr(minus, expr) {
1830
+ return new UnaryMinusExpr(expr);
1831
+ }
1832
+
1833
+ function makeBinaryExpr(expr1, op, expr2) {
1834
+ return new BinaryExpr(expr1, op, expr2);
1835
+ }
1836
+
1837
+ function makeLiteralExpr(token) {
1838
+ // remove quotes from the parsed value:
1839
+ var value = token.value.substring(1, token.value.length - 1);
1840
+ return new LiteralExpr(value);
1841
+ }
1842
+
1843
+ function makeNumberExpr(token) {
1844
+ return new NumberExpr(token.value);
1845
+ }
1846
+
1847
+ function makeVariableReference(dollar, name) {
1848
+ return new VariableExpr(name.value);
1849
+ }
1850
+
1851
+ // Used before parsing for optimization of common simple cases. See
1852
+ // the begin of xpathParse() for which they are.
1853
+ function makeSimpleExpr(expr) {
1854
+ if (expr.charAt(0) == '$') {
1855
+ return new VariableExpr(expr.substr(1));
1856
+ } else if (expr.charAt(0) == '@') {
1857
+ var a = new NodeTestName(expr.substr(1));
1858
+ var b = new StepExpr('attribute', a);
1859
+ var c = new LocationExpr();
1860
+ c.appendStep(b);
1861
+ return c;
1862
+ } else if (expr.match(/^[0-9]+$/)) {
1863
+ return new NumberExpr(expr);
1864
+ } else {
1865
+ var a = new NodeTestName(expr);
1866
+ var b = new StepExpr('child', a);
1867
+ var c = new LocationExpr();
1868
+ c.appendStep(b);
1869
+ return c;
1870
+ }
1871
+ }
1872
+
1873
+ function makeSimpleExpr2(expr) {
1874
+ var steps = stringSplit(expr, '/');
1875
+ var c = new LocationExpr();
1876
+ for (var i = 0; i < steps.length; ++i) {
1877
+ var a = new NodeTestName(steps[i]);
1878
+ var b = new StepExpr('child', a);
1879
+ c.appendStep(b);
1880
+ }
1881
+ return c;
1882
+ }
1883
+
1884
+ // The axes of XPath expressions.
1885
+
1886
+ var xpathAxis = {
1887
+ ANCESTOR_OR_SELF: 'ancestor-or-self',
1888
+ ANCESTOR: 'ancestor',
1889
+ ATTRIBUTE: 'attribute',
1890
+ CHILD: 'child',
1891
+ DESCENDANT_OR_SELF: 'descendant-or-self',
1892
+ DESCENDANT: 'descendant',
1893
+ FOLLOWING_SIBLING: 'following-sibling',
1894
+ FOLLOWING: 'following',
1895
+ NAMESPACE: 'namespace',
1896
+ PARENT: 'parent',
1897
+ PRECEDING_SIBLING: 'preceding-sibling',
1898
+ PRECEDING: 'preceding',
1899
+ SELF: 'self'
1900
+ };
1901
+
1902
+ var xpathAxesRe = [
1903
+ xpathAxis.ANCESTOR_OR_SELF,
1904
+ xpathAxis.ANCESTOR,
1905
+ xpathAxis.ATTRIBUTE,
1906
+ xpathAxis.CHILD,
1907
+ xpathAxis.DESCENDANT_OR_SELF,
1908
+ xpathAxis.DESCENDANT,
1909
+ xpathAxis.FOLLOWING_SIBLING,
1910
+ xpathAxis.FOLLOWING,
1911
+ xpathAxis.NAMESPACE,
1912
+ xpathAxis.PARENT,
1913
+ xpathAxis.PRECEDING_SIBLING,
1914
+ xpathAxis.PRECEDING,
1915
+ xpathAxis.SELF
1916
+ ].join('|');
1917
+
1918
+
1919
+ // The tokens of the language. The label property is just used for
1920
+ // generating debug output. The prec property is the precedence used
1921
+ // for shift/reduce resolution. Default precedence is 0 as a lookahead
1922
+ // token and 2 on the stack. TODO(mesch): this is certainly not
1923
+ // necessary and too complicated. Simplify this!
1924
+
1925
+ // NOTE: tabular formatting is the big exception, but here it should
1926
+ // be OK.
1927
+
1928
+ var TOK_PIPE = { label: "|", prec: 17, re: new RegExp("^\\|") };
1929
+ var TOK_DSLASH = { label: "//", prec: 19, re: new RegExp("^//") };
1930
+ var TOK_SLASH = { label: "/", prec: 30, re: new RegExp("^/") };
1931
+ var TOK_AXIS = { label: "::", prec: 20, re: new RegExp("^::") };
1932
+ var TOK_COLON = { label: ":", prec: 1000, re: new RegExp("^:") };
1933
+ var TOK_AXISNAME = { label: "[axis]", re: new RegExp('^(' + xpathAxesRe + ')') };
1934
+ var TOK_PARENO = { label: "(", prec: 34, re: new RegExp("^\\(") };
1935
+ var TOK_PARENC = { label: ")", re: new RegExp("^\\)") };
1936
+ var TOK_DDOT = { label: "..", prec: 34, re: new RegExp("^\\.\\.") };
1937
+ var TOK_DOT = { label: ".", prec: 34, re: new RegExp("^\\.") };
1938
+ var TOK_AT = { label: "@", prec: 34, re: new RegExp("^@") };
1939
+
1940
+ var TOK_COMMA = { label: ",", re: new RegExp("^,") };
1941
+
1942
+ var TOK_OR = { label: "or", prec: 10, re: new RegExp("^or\\b") };
1943
+ var TOK_AND = { label: "and", prec: 11, re: new RegExp("^and\\b") };
1944
+ var TOK_EQ = { label: "=", prec: 12, re: new RegExp("^=") };
1945
+ var TOK_NEQ = { label: "!=", prec: 12, re: new RegExp("^!=") };
1946
+ var TOK_GE = { label: ">=", prec: 13, re: new RegExp("^>=") };
1947
+ var TOK_GT = { label: ">", prec: 13, re: new RegExp("^>") };
1948
+ var TOK_LE = { label: "<=", prec: 13, re: new RegExp("^<=") };
1949
+ var TOK_LT = { label: "<", prec: 13, re: new RegExp("^<") };
1950
+ var TOK_PLUS = { label: "+", prec: 14, re: new RegExp("^\\+"), left: true };
1951
+ var TOK_MINUS = { label: "-", prec: 14, re: new RegExp("^\\-"), left: true };
1952
+ var TOK_DIV = { label: "div", prec: 15, re: new RegExp("^div\\b"), left: true };
1953
+ var TOK_MOD = { label: "mod", prec: 15, re: new RegExp("^mod\\b"), left: true };
1954
+
1955
+ var TOK_BRACKO = { label: "[", prec: 32, re: new RegExp("^\\[") };
1956
+ var TOK_BRACKC = { label: "]", re: new RegExp("^\\]") };
1957
+ var TOK_DOLLAR = { label: "$", re: new RegExp("^\\$") };
1958
+
1959
+ var TOK_NCNAME = { label: "[ncname]", re: new RegExp('^' + XML_NC_NAME) };
1960
+
1961
+ var TOK_ASTERISK = { label: "*", prec: 15, re: new RegExp("^\\*"), left: true };
1962
+ var TOK_LITERALQ = { label: "[litq]", prec: 20, re: new RegExp("^'[^\\']*'") };
1963
+ var TOK_LITERALQQ = {
1964
+ label: "[litqq]",
1965
+ prec: 20,
1966
+ re: new RegExp('^"[^\\"]*"')
1967
+ };
1968
+
1969
+ var TOK_NUMBER = {
1970
+ label: "[number]",
1971
+ prec: 35,
1972
+ re: new RegExp('^\\d+(\\.\\d*)?') };
1973
+
1974
+ var TOK_QNAME = {
1975
+ label: "[qname]",
1976
+ re: new RegExp('^(' + XML_NC_NAME + ':)?' + XML_NC_NAME)
1977
+ };
1978
+
1979
+ var TOK_NODEO = {
1980
+ label: "[nodetest-start]",
1981
+ re: new RegExp('^(processing-instruction|comment|text|node)\\(')
1982
+ };
1983
+
1984
+ // The table of the tokens of our grammar, used by the lexer: first
1985
+ // column the tag, second column a regexp to recognize it in the
1986
+ // input, third column the precedence of the token, fourth column a
1987
+ // factory function for the semantic value of the token.
1988
+ //
1989
+ // NOTE: order of this list is important, because the first match
1990
+ // counts. Cf. DDOT and DOT, and AXIS and COLON.
1991
+
1992
+ var xpathTokenRules = [
1993
+ TOK_DSLASH,
1994
+ TOK_SLASH,
1995
+ TOK_DDOT,
1996
+ TOK_DOT,
1997
+ TOK_AXIS,
1998
+ TOK_COLON,
1999
+ TOK_AXISNAME,
2000
+ TOK_NODEO,
2001
+ TOK_PARENO,
2002
+ TOK_PARENC,
2003
+ TOK_BRACKO,
2004
+ TOK_BRACKC,
2005
+ TOK_AT,
2006
+ TOK_COMMA,
2007
+ TOK_OR,
2008
+ TOK_AND,
2009
+ TOK_NEQ,
2010
+ TOK_EQ,
2011
+ TOK_GE,
2012
+ TOK_GT,
2013
+ TOK_LE,
2014
+ TOK_LT,
2015
+ TOK_PLUS,
2016
+ TOK_MINUS,
2017
+ TOK_ASTERISK,
2018
+ TOK_PIPE,
2019
+ TOK_MOD,
2020
+ TOK_DIV,
2021
+ TOK_LITERALQ,
2022
+ TOK_LITERALQQ,
2023
+ TOK_NUMBER,
2024
+ TOK_QNAME,
2025
+ TOK_NCNAME,
2026
+ TOK_DOLLAR
2027
+ ];
2028
+
2029
+ // All the nonterminals of the grammar. The nonterminal objects are
2030
+ // identified by object identity; the labels are used in the debug
2031
+ // output only.
2032
+ var XPathLocationPath = { label: "LocationPath" };
2033
+ var XPathRelativeLocationPath = { label: "RelativeLocationPath" };
2034
+ var XPathAbsoluteLocationPath = { label: "AbsoluteLocationPath" };
2035
+ var XPathStep = { label: "Step" };
2036
+ var XPathNodeTest = { label: "NodeTest" };
2037
+ var XPathPredicate = { label: "Predicate" };
2038
+ var XPathLiteral = { label: "Literal" };
2039
+ var XPathExpr = { label: "Expr" };
2040
+ var XPathPrimaryExpr = { label: "PrimaryExpr" };
2041
+ var XPathVariableReference = { label: "Variablereference" };
2042
+ var XPathNumber = { label: "Number" };
2043
+ var XPathFunctionCall = { label: "FunctionCall" };
2044
+ var XPathArgumentRemainder = { label: "ArgumentRemainder" };
2045
+ var XPathPathExpr = { label: "PathExpr" };
2046
+ var XPathUnionExpr = { label: "UnionExpr" };
2047
+ var XPathFilterExpr = { label: "FilterExpr" };
2048
+ var XPathDigits = { label: "Digits" };
2049
+
2050
+ var xpathNonTerminals = [
2051
+ XPathLocationPath,
2052
+ XPathRelativeLocationPath,
2053
+ XPathAbsoluteLocationPath,
2054
+ XPathStep,
2055
+ XPathNodeTest,
2056
+ XPathPredicate,
2057
+ XPathLiteral,
2058
+ XPathExpr,
2059
+ XPathPrimaryExpr,
2060
+ XPathVariableReference,
2061
+ XPathNumber,
2062
+ XPathFunctionCall,
2063
+ XPathArgumentRemainder,
2064
+ XPathPathExpr,
2065
+ XPathUnionExpr,
2066
+ XPathFilterExpr,
2067
+ XPathDigits
2068
+ ];
2069
+
2070
+ // Quantifiers that are used in the productions of the grammar.
2071
+ var Q_01 = { label: "?" };
2072
+ var Q_MM = { label: "*" };
2073
+ var Q_1M = { label: "+" };
2074
+
2075
+ // Tag for left associativity (right assoc is implied by undefined).
2076
+ var ASSOC_LEFT = true;
2077
+
2078
+ // The productions of the grammar. Columns of the table:
2079
+ //
2080
+ // - target nonterminal,
2081
+ // - pattern,
2082
+ // - precedence,
2083
+ // - semantic value factory
2084
+ //
2085
+ // The semantic value factory is a function that receives parse tree
2086
+ // nodes from the stack frames of the matched symbols as arguments and
2087
+ // returns an a node of the parse tree. The node is stored in the top
2088
+ // stack frame along with the target object of the rule. The node in
2089
+ // the parse tree is an expression object that has an evaluate() method
2090
+ // and thus evaluates XPath expressions.
2091
+ //
2092
+ // The precedence is used to decide between reducing and shifting by
2093
+ // comparing the precendence of the rule that is candidate for
2094
+ // reducing with the precedence of the look ahead token. Precedence of
2095
+ // -1 means that the precedence of the tokens in the pattern is used
2096
+ // instead. TODO: It shouldn't be necessary to explicitly assign
2097
+ // precedences to rules.
2098
+
2099
+ // DGF As it stands, these precedences are purely empirical; we're
2100
+ // not sure they can be made to be consistent at all.
2101
+
2102
+ var xpathGrammarRules =
2103
+ [
2104
+ [ XPathLocationPath, [ XPathRelativeLocationPath ], 18,
2105
+ passExpr ],
2106
+ [ XPathLocationPath, [ XPathAbsoluteLocationPath ], 18,
2107
+ passExpr ],
2108
+
2109
+ [ XPathAbsoluteLocationPath, [ TOK_SLASH, XPathRelativeLocationPath ], 18,
2110
+ makeLocationExpr1 ],
2111
+ [ XPathAbsoluteLocationPath, [ TOK_DSLASH, XPathRelativeLocationPath ], 18,
2112
+ makeLocationExpr2 ],
2113
+
2114
+ [ XPathAbsoluteLocationPath, [ TOK_SLASH ], 0,
2115
+ makeLocationExpr3 ],
2116
+ [ XPathAbsoluteLocationPath, [ TOK_DSLASH ], 0,
2117
+ makeLocationExpr4 ],
2118
+
2119
+ [ XPathRelativeLocationPath, [ XPathStep ], 31,
2120
+ makeLocationExpr5 ],
2121
+ [ XPathRelativeLocationPath,
2122
+ [ XPathRelativeLocationPath, TOK_SLASH, XPathStep ], 31,
2123
+ makeLocationExpr6 ],
2124
+ [ XPathRelativeLocationPath,
2125
+ [ XPathRelativeLocationPath, TOK_DSLASH, XPathStep ], 31,
2126
+ makeLocationExpr7 ],
2127
+
2128
+ [ XPathStep, [ TOK_DOT ], 33,
2129
+ makeStepExpr1 ],
2130
+ [ XPathStep, [ TOK_DDOT ], 33,
2131
+ makeStepExpr2 ],
2132
+ [ XPathStep,
2133
+ [ TOK_AXISNAME, TOK_AXIS, XPathNodeTest ], 33,
2134
+ makeStepExpr3 ],
2135
+ [ XPathStep, [ TOK_AT, XPathNodeTest ], 33,
2136
+ makeStepExpr4 ],
2137
+ [ XPathStep, [ XPathNodeTest ], 33,
2138
+ makeStepExpr5 ],
2139
+ [ XPathStep, [ XPathStep, XPathPredicate ], 33,
2140
+ makeStepExpr6 ],
2141
+
2142
+ [ XPathNodeTest, [ TOK_ASTERISK ], 33,
2143
+ makeNodeTestExpr1 ],
2144
+ [ XPathNodeTest, [ TOK_NCNAME, TOK_COLON, TOK_ASTERISK ], 33,
2145
+ makeNodeTestExpr2 ],
2146
+ [ XPathNodeTest, [ TOK_QNAME ], 33,
2147
+ makeNodeTestExpr3 ],
2148
+ [ XPathNodeTest, [ TOK_NODEO, TOK_PARENC ], 33,
2149
+ makeNodeTestExpr4 ],
2150
+ [ XPathNodeTest, [ TOK_NODEO, XPathLiteral, TOK_PARENC ], 33,
2151
+ makeNodeTestExpr5 ],
2152
+
2153
+ [ XPathPredicate, [ TOK_BRACKO, XPathExpr, TOK_BRACKC ], 33,
2154
+ makePredicateExpr ],
2155
+
2156
+ [ XPathPrimaryExpr, [ XPathVariableReference ], 33,
2157
+ passExpr ],
2158
+ [ XPathPrimaryExpr, [ TOK_PARENO, XPathExpr, TOK_PARENC ], 33,
2159
+ makePrimaryExpr ],
2160
+ [ XPathPrimaryExpr, [ XPathLiteral ], 30,
2161
+ passExpr ],
2162
+ [ XPathPrimaryExpr, [ XPathNumber ], 30,
2163
+ passExpr ],
2164
+ [ XPathPrimaryExpr, [ XPathFunctionCall ], 31,
2165
+ passExpr ],
2166
+
2167
+ [ XPathFunctionCall, [ TOK_QNAME, TOK_PARENO, TOK_PARENC ], -1,
2168
+ makeFunctionCallExpr1 ],
2169
+ [ XPathFunctionCall,
2170
+ [ TOK_QNAME, TOK_PARENO, XPathExpr, XPathArgumentRemainder, Q_MM,
2171
+ TOK_PARENC ], -1,
2172
+ makeFunctionCallExpr2 ],
2173
+ [ XPathArgumentRemainder, [ TOK_COMMA, XPathExpr ], -1,
2174
+ makeArgumentExpr ],
2175
+
2176
+ [ XPathUnionExpr, [ XPathPathExpr ], 20,
2177
+ passExpr ],
2178
+ [ XPathUnionExpr, [ XPathUnionExpr, TOK_PIPE, XPathPathExpr ], 20,
2179
+ makeUnionExpr ],
2180
+
2181
+ [ XPathPathExpr, [ XPathLocationPath ], 20,
2182
+ passExpr ],
2183
+ [ XPathPathExpr, [ XPathFilterExpr ], 19,
2184
+ passExpr ],
2185
+ [ XPathPathExpr,
2186
+ [ XPathFilterExpr, TOK_SLASH, XPathRelativeLocationPath ], 19,
2187
+ makePathExpr1 ],
2188
+ [ XPathPathExpr,
2189
+ [ XPathFilterExpr, TOK_DSLASH, XPathRelativeLocationPath ], 19,
2190
+ makePathExpr2 ],
2191
+
2192
+ [ XPathFilterExpr, [ XPathPrimaryExpr, XPathPredicate, Q_MM ], 31,
2193
+ makeFilterExpr ],
2194
+
2195
+ [ XPathExpr, [ XPathPrimaryExpr ], 16,
2196
+ passExpr ],
2197
+ [ XPathExpr, [ XPathUnionExpr ], 16,
2198
+ passExpr ],
2199
+
2200
+ [ XPathExpr, [ TOK_MINUS, XPathExpr ], -1,
2201
+ makeUnaryMinusExpr ],
2202
+
2203
+ [ XPathExpr, [ XPathExpr, TOK_OR, XPathExpr ], -1,
2204
+ makeBinaryExpr ],
2205
+ [ XPathExpr, [ XPathExpr, TOK_AND, XPathExpr ], -1,
2206
+ makeBinaryExpr ],
2207
+
2208
+ [ XPathExpr, [ XPathExpr, TOK_EQ, XPathExpr ], -1,
2209
+ makeBinaryExpr ],
2210
+ [ XPathExpr, [ XPathExpr, TOK_NEQ, XPathExpr ], -1,
2211
+ makeBinaryExpr ],
2212
+
2213
+ [ XPathExpr, [ XPathExpr, TOK_LT, XPathExpr ], -1,
2214
+ makeBinaryExpr ],
2215
+ [ XPathExpr, [ XPathExpr, TOK_LE, XPathExpr ], -1,
2216
+ makeBinaryExpr ],
2217
+ [ XPathExpr, [ XPathExpr, TOK_GT, XPathExpr ], -1,
2218
+ makeBinaryExpr ],
2219
+ [ XPathExpr, [ XPathExpr, TOK_GE, XPathExpr ], -1,
2220
+ makeBinaryExpr ],
2221
+
2222
+ [ XPathExpr, [ XPathExpr, TOK_PLUS, XPathExpr ], -1,
2223
+ makeBinaryExpr, ASSOC_LEFT ],
2224
+ [ XPathExpr, [ XPathExpr, TOK_MINUS, XPathExpr ], -1,
2225
+ makeBinaryExpr, ASSOC_LEFT ],
2226
+
2227
+ [ XPathExpr, [ XPathExpr, TOK_ASTERISK, XPathExpr ], -1,
2228
+ makeBinaryExpr, ASSOC_LEFT ],
2229
+ [ XPathExpr, [ XPathExpr, TOK_DIV, XPathExpr ], -1,
2230
+ makeBinaryExpr, ASSOC_LEFT ],
2231
+ [ XPathExpr, [ XPathExpr, TOK_MOD, XPathExpr ], -1,
2232
+ makeBinaryExpr, ASSOC_LEFT ],
2233
+
2234
+ [ XPathLiteral, [ TOK_LITERALQ ], -1,
2235
+ makeLiteralExpr ],
2236
+ [ XPathLiteral, [ TOK_LITERALQQ ], -1,
2237
+ makeLiteralExpr ],
2238
+
2239
+ [ XPathNumber, [ TOK_NUMBER ], -1,
2240
+ makeNumberExpr ],
2241
+
2242
+ [ XPathVariableReference, [ TOK_DOLLAR, TOK_QNAME ], 200,
2243
+ makeVariableReference ]
2244
+ ];
2245
+
2246
+ // That function computes some optimizations of the above data
2247
+ // structures and will be called right here. It merely takes the
2248
+ // counter variables out of the global scope.
2249
+
2250
+ var xpathRules = [];
2251
+
2252
+ function xpathParseInit() {
2253
+ if (xpathRules.length) {
2254
+ return;
2255
+ }
2256
+
2257
+ // Some simple optimizations for the xpath expression parser: sort
2258
+ // grammar rules descending by length, so that the longest match is
2259
+ // first found.
2260
+
2261
+ xpathGrammarRules.sort(function(a,b) {
2262
+ var la = a[1].length;
2263
+ var lb = b[1].length;
2264
+ if (la < lb) {
2265
+ return 1;
2266
+ } else if (la > lb) {
2267
+ return -1;
2268
+ } else {
2269
+ return 0;
2270
+ }
2271
+ });
2272
+
2273
+ var k = 1;
2274
+ for (var i = 0; i < xpathNonTerminals.length; ++i) {
2275
+ xpathNonTerminals[i].key = k++;
2276
+ }
2277
+
2278
+ for (i = 0; i < xpathTokenRules.length; ++i) {
2279
+ xpathTokenRules[i].key = k++;
2280
+ }
2281
+
2282
+ xpathLog('XPath parse INIT: ' + k + ' rules');
2283
+
2284
+ // Another slight optimization: sort the rules into bins according
2285
+ // to the last element (observing quantifiers), so we can restrict
2286
+ // the match against the stack to the subest of rules that match the
2287
+ // top of the stack.
2288
+ //
2289
+ // TODO(mesch): What we actually want is to compute states as in
2290
+ // bison, so that we don't have to do any explicit and iterated
2291
+ // match against the stack.
2292
+
2293
+ function push_(array, position, element) {
2294
+ if (!array[position]) {
2295
+ array[position] = [];
2296
+ }
2297
+ array[position].push(element);
2298
+ }
2299
+
2300
+ for (i = 0; i < xpathGrammarRules.length; ++i) {
2301
+ var rule = xpathGrammarRules[i];
2302
+ var pattern = rule[1];
2303
+
2304
+ for (var j = pattern.length - 1; j >= 0; --j) {
2305
+ if (pattern[j] == Q_1M) {
2306
+ push_(xpathRules, pattern[j-1].key, rule);
2307
+ break;
2308
+
2309
+ } else if (pattern[j] == Q_MM || pattern[j] == Q_01) {
2310
+ push_(xpathRules, pattern[j-1].key, rule);
2311
+ --j;
2312
+
2313
+ } else {
2314
+ push_(xpathRules, pattern[j].key, rule);
2315
+ break;
2316
+ }
2317
+ }
2318
+ }
2319
+
2320
+ xpathLog('XPath parse INIT: ' + xpathRules.length + ' rule bins');
2321
+
2322
+ var sum = 0;
2323
+ mapExec(xpathRules, function(i) {
2324
+ if (i) {
2325
+ sum += i.length;
2326
+ }
2327
+ });
2328
+
2329
+ xpathLog('XPath parse INIT: ' + (sum / xpathRules.length) +
2330
+ ' average bin size');
2331
+ }
2332
+
2333
+ // Local utility functions that are used by the lexer or parser.
2334
+
2335
+ function xpathCollectDescendants(nodelist, node, opt_tagName) {
2336
+ if (opt_tagName && node.getElementsByTagName) {
2337
+ copyArray(nodelist, node.getElementsByTagName(opt_tagName));
2338
+ return;
2339
+ }
2340
+ for (var n = node.firstChild; n; n = n.nextSibling) {
2341
+ nodelist.push(n);
2342
+ xpathCollectDescendants(nodelist, n);
2343
+ }
2344
+ }
2345
+
2346
+ // DGF extract a tag name suitable for getElementsByTagName
2347
+ function xpathExtractTagNameFromNodeTest(nodetest) {
2348
+ if (nodetest instanceof NodeTestName) {
2349
+ return nodetest.name;
2350
+ } else if (/* nodetest instanceof NodeTestAny || */ nodetest instanceof NodeTestElementOrAttribute) {
2351
+ // HBC - commented out the NodeTestAny in the above condition; it causes
2352
+ // non-element nodes to be excluded! The XPath spec says "node()" must
2353
+ // match all node types.
2354
+ return "*";
2355
+ }
2356
+ }
2357
+
2358
+ function xpathCollectDescendantsReverse(nodelist, node) {
2359
+ for (var n = node.lastChild; n; n = n.previousSibling) {
2360
+ nodelist.push(n);
2361
+ xpathCollectDescendantsReverse(nodelist, n);
2362
+ }
2363
+ }
2364
+
2365
+
2366
+ // The entry point for the library: match an expression against a DOM
2367
+ // node. Returns an XPath value.
2368
+ function xpathDomEval(expr, node) {
2369
+ var expr1 = xpathParse(expr);
2370
+ var ret = expr1.evaluate(new ExprContext(node));
2371
+ return ret;
2372
+ }
2373
+
2374
+ // Utility function to sort a list of nodes. Used by xsltSort() and
2375
+ // nxslSelect().
2376
+ function xpathSort(input, sort) {
2377
+ if (sort.length == 0) {
2378
+ return;
2379
+ }
2380
+
2381
+ var sortlist = [];
2382
+
2383
+ for (var i = 0; i < input.contextSize(); ++i) {
2384
+ var node = input.nodelist[i];
2385
+ var sortitem = { node: node, key: [] };
2386
+ var context = input.clone(node, 0, [ node ]);
2387
+
2388
+ for (var j = 0; j < sort.length; ++j) {
2389
+ var s = sort[j];
2390
+ var value = s.expr.evaluate(context);
2391
+
2392
+ var evalue;
2393
+ if (s.type == 'text') {
2394
+ evalue = value.stringValue();
2395
+ } else if (s.type == 'number') {
2396
+ evalue = value.numberValue();
2397
+ }
2398
+ sortitem.key.push({ value: evalue, order: s.order });
2399
+ }
2400
+
2401
+ // Make the sort stable by adding a lowest priority sort by
2402
+ // id. This is very convenient and furthermore required by the
2403
+ // spec ([XSLT] - Section 10 Sorting).
2404
+ sortitem.key.push({ value: i, order: 'ascending' });
2405
+
2406
+ sortlist.push(sortitem);
2407
+ }
2408
+
2409
+ sortlist.sort(xpathSortByKey);
2410
+
2411
+ var nodes = [];
2412
+ for (var i = 0; i < sortlist.length; ++i) {
2413
+ nodes.push(sortlist[i].node);
2414
+ }
2415
+ input.nodelist = nodes;
2416
+ input.setNode(0);
2417
+ }
2418
+
2419
+
2420
+ // Sorts by all order criteria defined. According to the JavaScript
2421
+ // spec ([ECMA] Section 11.8.5), the compare operators compare strings
2422
+ // as strings and numbers as numbers.
2423
+ //
2424
+ // NOTE: In browsers which do not follow the spec, this breaks only in
2425
+ // the case that numbers should be sorted as strings, which is very
2426
+ // uncommon.
2427
+ function xpathSortByKey(v1, v2) {
2428
+ // NOTE: Sort key vectors of different length never occur in
2429
+ // xsltSort.
2430
+
2431
+ for (var i = 0; i < v1.key.length; ++i) {
2432
+ var o = v1.key[i].order == 'descending' ? -1 : 1;
2433
+ if (v1.key[i].value > v2.key[i].value) {
2434
+ return +1 * o;
2435
+ } else if (v1.key[i].value < v2.key[i].value) {
2436
+ return -1 * o;
2437
+ }
2438
+ }
2439
+
2440
+ return 0;
2441
+ }
2442
+
2443
+
2444
+ // Parses and then evaluates the given XPath expression in the given
2445
+ // input context. Notice that parsed xpath expressions are cached.
2446
+ function xpathEval(select, context) {
2447
+ var expr = xpathParse(select);
2448
+ var ret = expr.evaluate(context);
2449
+ return ret;
2450
+ }