nokogiri 1.0.0 → 1.6.8.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of nokogiri might be problematic. Click here for more details.

Files changed (309) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +26 -0
  3. data/.cross_rubies +9 -0
  4. data/.editorconfig +17 -0
  5. data/.gemtest +0 -0
  6. data/.travis.yml +51 -0
  7. data/CHANGELOG.rdoc +1160 -0
  8. data/CONTRIBUTING.md +42 -0
  9. data/C_CODING_STYLE.rdoc +33 -0
  10. data/Gemfile +22 -0
  11. data/LICENSE.txt +31 -0
  12. data/Manifest.txt +284 -40
  13. data/README.md +166 -0
  14. data/ROADMAP.md +111 -0
  15. data/Rakefile +310 -199
  16. data/STANDARD_RESPONSES.md +47 -0
  17. data/Y_U_NO_GEMSPEC.md +155 -0
  18. data/appveyor.yml +22 -0
  19. data/bin/nokogiri +118 -0
  20. data/build_all +45 -0
  21. data/dependencies.yml +29 -0
  22. data/ext/nokogiri/depend +358 -0
  23. data/ext/nokogiri/extconf.rb +664 -34
  24. data/ext/nokogiri/html_document.c +120 -33
  25. data/ext/nokogiri/html_document.h +1 -1
  26. data/ext/nokogiri/html_element_description.c +279 -0
  27. data/ext/nokogiri/html_element_description.h +10 -0
  28. data/ext/nokogiri/html_entity_lookup.c +32 -0
  29. data/ext/nokogiri/html_entity_lookup.h +8 -0
  30. data/ext/nokogiri/html_sax_parser_context.c +116 -0
  31. data/ext/nokogiri/html_sax_parser_context.h +11 -0
  32. data/ext/nokogiri/html_sax_push_parser.c +87 -0
  33. data/ext/nokogiri/html_sax_push_parser.h +9 -0
  34. data/ext/nokogiri/nokogiri.c +145 -0
  35. data/ext/nokogiri/nokogiri.h +131 -0
  36. data/ext/nokogiri/xml_attr.c +94 -0
  37. data/ext/nokogiri/xml_attr.h +9 -0
  38. data/ext/nokogiri/xml_attribute_decl.c +70 -0
  39. data/ext/nokogiri/xml_attribute_decl.h +9 -0
  40. data/ext/nokogiri/xml_cdata.c +23 -19
  41. data/ext/nokogiri/xml_cdata.h +1 -1
  42. data/ext/nokogiri/xml_comment.c +69 -0
  43. data/ext/nokogiri/xml_comment.h +9 -0
  44. data/ext/nokogiri/xml_document.c +501 -54
  45. data/ext/nokogiri/xml_document.h +14 -1
  46. data/ext/nokogiri/xml_document_fragment.c +48 -0
  47. data/ext/nokogiri/xml_document_fragment.h +10 -0
  48. data/ext/nokogiri/xml_dtd.c +109 -24
  49. data/ext/nokogiri/xml_dtd.h +3 -1
  50. data/ext/nokogiri/xml_element_content.c +123 -0
  51. data/ext/nokogiri/xml_element_content.h +10 -0
  52. data/ext/nokogiri/xml_element_decl.c +69 -0
  53. data/ext/nokogiri/xml_element_decl.h +9 -0
  54. data/ext/nokogiri/xml_encoding_handler.c +79 -0
  55. data/ext/nokogiri/xml_encoding_handler.h +8 -0
  56. data/ext/nokogiri/xml_entity_decl.c +110 -0
  57. data/ext/nokogiri/xml_entity_decl.h +10 -0
  58. data/ext/nokogiri/xml_entity_reference.c +52 -0
  59. data/ext/nokogiri/xml_entity_reference.h +9 -0
  60. data/ext/nokogiri/xml_io.c +60 -0
  61. data/ext/nokogiri/xml_io.h +11 -0
  62. data/ext/nokogiri/xml_libxml2_hacks.c +112 -0
  63. data/ext/nokogiri/xml_libxml2_hacks.h +12 -0
  64. data/ext/nokogiri/xml_namespace.c +117 -0
  65. data/ext/nokogiri/xml_namespace.h +13 -0
  66. data/ext/nokogiri/xml_node.c +1285 -315
  67. data/ext/nokogiri/xml_node.h +4 -6
  68. data/ext/nokogiri/xml_node_set.c +415 -54
  69. data/ext/nokogiri/xml_node_set.h +6 -2
  70. data/ext/nokogiri/xml_processing_instruction.c +56 -0
  71. data/ext/nokogiri/xml_processing_instruction.h +9 -0
  72. data/ext/nokogiri/xml_reader.c +316 -77
  73. data/ext/nokogiri/xml_reader.h +1 -1
  74. data/ext/nokogiri/xml_relax_ng.c +161 -0
  75. data/ext/nokogiri/xml_relax_ng.h +9 -0
  76. data/ext/nokogiri/xml_sax_parser.c +215 -80
  77. data/ext/nokogiri/xml_sax_parser.h +30 -1
  78. data/ext/nokogiri/xml_sax_parser_context.c +262 -0
  79. data/ext/nokogiri/xml_sax_parser_context.h +10 -0
  80. data/ext/nokogiri/xml_sax_push_parser.c +115 -0
  81. data/ext/nokogiri/xml_sax_push_parser.h +9 -0
  82. data/ext/nokogiri/xml_schema.c +205 -0
  83. data/ext/nokogiri/xml_schema.h +9 -0
  84. data/ext/nokogiri/xml_syntax_error.c +45 -175
  85. data/ext/nokogiri/xml_syntax_error.h +4 -2
  86. data/ext/nokogiri/xml_text.c +37 -14
  87. data/ext/nokogiri/xml_text.h +1 -1
  88. data/ext/nokogiri/xml_xpath_context.c +230 -13
  89. data/ext/nokogiri/xml_xpath_context.h +2 -1
  90. data/ext/nokogiri/xslt_stylesheet.c +196 -34
  91. data/ext/nokogiri/xslt_stylesheet.h +6 -1
  92. data/lib/nokogiri/css/node.rb +18 -61
  93. data/lib/nokogiri/css/parser.rb +725 -17
  94. data/lib/nokogiri/css/parser.y +126 -63
  95. data/lib/nokogiri/css/parser_extras.rb +91 -0
  96. data/lib/nokogiri/css/syntax_error.rb +7 -0
  97. data/lib/nokogiri/css/tokenizer.rb +148 -5
  98. data/lib/nokogiri/css/tokenizer.rex +31 -39
  99. data/lib/nokogiri/css/xpath_visitor.rb +109 -51
  100. data/lib/nokogiri/css.rb +24 -3
  101. data/lib/nokogiri/decorators/slop.rb +42 -0
  102. data/lib/nokogiri/html/builder.rb +27 -1
  103. data/lib/nokogiri/html/document.rb +329 -3
  104. data/lib/nokogiri/html/document_fragment.rb +39 -0
  105. data/lib/nokogiri/html/element_description.rb +23 -0
  106. data/lib/nokogiri/html/element_description_defaults.rb +671 -0
  107. data/lib/nokogiri/html/entity_lookup.rb +13 -0
  108. data/lib/nokogiri/html/sax/parser.rb +35 -4
  109. data/lib/nokogiri/html/sax/parser_context.rb +16 -0
  110. data/lib/nokogiri/html/sax/push_parser.rb +36 -0
  111. data/lib/nokogiri/html.rb +18 -76
  112. data/lib/nokogiri/syntax_error.rb +4 -0
  113. data/lib/nokogiri/version.rb +106 -1
  114. data/lib/nokogiri/xml/attr.rb +14 -0
  115. data/lib/nokogiri/xml/attribute_decl.rb +18 -0
  116. data/lib/nokogiri/xml/builder.rb +395 -31
  117. data/lib/nokogiri/xml/cdata.rb +4 -2
  118. data/lib/nokogiri/xml/character_data.rb +7 -0
  119. data/lib/nokogiri/xml/document.rb +267 -12
  120. data/lib/nokogiri/xml/document_fragment.rb +149 -0
  121. data/lib/nokogiri/xml/dtd.rb +27 -1
  122. data/lib/nokogiri/xml/element_content.rb +36 -0
  123. data/lib/nokogiri/xml/element_decl.rb +13 -0
  124. data/lib/nokogiri/xml/entity_decl.rb +19 -0
  125. data/lib/nokogiri/xml/namespace.rb +13 -0
  126. data/lib/nokogiri/xml/node/save_options.rb +61 -0
  127. data/lib/nokogiri/xml/node.rb +748 -109
  128. data/lib/nokogiri/xml/node_set.rb +200 -72
  129. data/lib/nokogiri/xml/parse_options.rb +120 -0
  130. data/lib/nokogiri/xml/pp/character_data.rb +18 -0
  131. data/lib/nokogiri/xml/pp/node.rb +56 -0
  132. data/lib/nokogiri/xml/pp.rb +2 -0
  133. data/lib/nokogiri/xml/processing_instruction.rb +8 -0
  134. data/lib/nokogiri/xml/reader.rb +102 -4
  135. data/lib/nokogiri/xml/relax_ng.rb +32 -0
  136. data/lib/nokogiri/xml/sax/document.rb +114 -2
  137. data/lib/nokogiri/xml/sax/parser.rb +97 -7
  138. data/lib/nokogiri/xml/sax/parser_context.rb +16 -0
  139. data/lib/nokogiri/xml/sax/push_parser.rb +60 -0
  140. data/lib/nokogiri/xml/sax.rb +2 -7
  141. data/lib/nokogiri/xml/schema.rb +63 -0
  142. data/lib/nokogiri/xml/searchable.rb +221 -0
  143. data/lib/nokogiri/xml/syntax_error.rb +27 -1
  144. data/lib/nokogiri/xml/text.rb +4 -1
  145. data/lib/nokogiri/xml/xpath/syntax_error.rb +11 -0
  146. data/lib/nokogiri/xml/xpath.rb +4 -0
  147. data/lib/nokogiri/xml/xpath_context.rb +3 -1
  148. data/lib/nokogiri/xml.rb +45 -38
  149. data/lib/nokogiri/xslt/stylesheet.rb +19 -0
  150. data/lib/nokogiri/xslt.rb +47 -2
  151. data/lib/nokogiri.rb +117 -24
  152. data/lib/xsd/xmlparser/nokogiri.rb +102 -0
  153. data/patches/sort-patches-by-date +25 -0
  154. data/ports/archives/libxml2-2.9.4.tar.gz +0 -0
  155. data/ports/archives/libxslt-1.1.29.tar.gz +0 -0
  156. data/suppressions/README.txt +1 -0
  157. data/suppressions/nokogiri_ree-1.8.7.358.supp +61 -0
  158. data/suppressions/nokogiri_ruby-1.8.7.370.supp +0 -0
  159. data/suppressions/nokogiri_ruby-1.9.2.320.supp +28 -0
  160. data/suppressions/nokogiri_ruby-1.9.3.327.supp +28 -0
  161. data/tasks/test.rb +100 -0
  162. data/test/css/test_nthiness.rb +73 -6
  163. data/test/css/test_parser.rb +184 -39
  164. data/test/css/test_tokenizer.rb +72 -19
  165. data/test/css/test_xpath_visitor.rb +44 -2
  166. data/test/decorators/test_slop.rb +20 -0
  167. data/test/files/2ch.html +108 -0
  168. data/test/files/GH_1042.html +18 -0
  169. data/test/files/address_book.rlx +12 -0
  170. data/test/files/address_book.xml +10 -0
  171. data/test/files/atom.xml +344 -0
  172. data/test/files/bar/bar.xsd +4 -0
  173. data/test/files/bogus.xml +0 -0
  174. data/test/files/dont_hurt_em_why.xml +422 -0
  175. data/test/files/encoding.html +82 -0
  176. data/test/files/encoding.xhtml +84 -0
  177. data/test/files/exslt.xml +8 -0
  178. data/test/files/exslt.xslt +35 -0
  179. data/test/files/foo/foo.xsd +4 -0
  180. data/test/files/metacharset.html +10 -0
  181. data/test/files/namespace_pressure_test.xml +1684 -0
  182. data/test/files/noencoding.html +47 -0
  183. data/test/files/po.xml +32 -0
  184. data/test/files/po.xsd +66 -0
  185. data/test/files/saml/saml20assertion_schema.xsd +283 -0
  186. data/test/files/saml/saml20protocol_schema.xsd +302 -0
  187. data/test/files/saml/xenc_schema.xsd +146 -0
  188. data/test/files/saml/xmldsig_schema.xsd +318 -0
  189. data/test/files/shift_jis.html +10 -0
  190. data/test/files/shift_jis.xml +5 -0
  191. data/test/files/shift_jis_no_charset.html +9 -0
  192. data/test/files/slow-xpath.xml +25509 -0
  193. data/test/files/snuggles.xml +3 -0
  194. data/test/files/staff.dtd +10 -0
  195. data/test/files/test_document_url/bar.xml +2 -0
  196. data/test/files/test_document_url/document.dtd +4 -0
  197. data/test/files/test_document_url/document.xml +6 -0
  198. data/test/files/tlm.html +2 -1
  199. data/test/files/to_be_xincluded.xml +2 -0
  200. data/test/files/valid_bar.xml +2 -0
  201. data/test/files/xinclude.xml +4 -0
  202. data/test/helper.rb +124 -13
  203. data/test/html/sax/test_parser.rb +118 -4
  204. data/test/html/sax/test_parser_context.rb +46 -0
  205. data/test/html/sax/test_push_parser.rb +87 -0
  206. data/test/html/test_builder.rb +94 -8
  207. data/test/html/test_document.rb +626 -11
  208. data/test/html/test_document_encoding.rb +145 -0
  209. data/test/html/test_document_fragment.rb +301 -0
  210. data/test/html/test_element_description.rb +105 -0
  211. data/test/html/test_named_characters.rb +14 -0
  212. data/test/html/test_node.rb +212 -0
  213. data/test/html/test_node_encoding.rb +85 -0
  214. data/test/namespaces/test_additional_namespaces_in_builder_doc.rb +14 -0
  215. data/test/namespaces/test_namespaces_aliased_default.rb +24 -0
  216. data/test/namespaces/test_namespaces_in_builder_doc.rb +75 -0
  217. data/test/namespaces/test_namespaces_in_cloned_doc.rb +31 -0
  218. data/test/namespaces/test_namespaces_in_created_doc.rb +75 -0
  219. data/test/namespaces/test_namespaces_in_parsed_doc.rb +80 -0
  220. data/test/namespaces/test_namespaces_preservation.rb +31 -0
  221. data/test/test_convert_xpath.rb +2 -47
  222. data/test/test_css_cache.rb +45 -0
  223. data/test/test_encoding_handler.rb +48 -0
  224. data/test/test_memory_leak.rb +156 -0
  225. data/test/test_nokogiri.rb +103 -1
  226. data/test/test_soap4r_sax.rb +52 -0
  227. data/test/test_xslt_transforms.rb +293 -8
  228. data/test/xml/node/test_save_options.rb +28 -0
  229. data/test/xml/node/test_subclass.rb +44 -0
  230. data/test/xml/sax/test_parser.rb +309 -8
  231. data/test/xml/sax/test_parser_context.rb +115 -0
  232. data/test/xml/sax/test_push_parser.rb +157 -0
  233. data/test/xml/test_attr.rb +67 -0
  234. data/test/xml/test_attribute_decl.rb +86 -0
  235. data/test/xml/test_builder.rb +327 -2
  236. data/test/xml/test_c14n.rb +180 -0
  237. data/test/xml/test_cdata.rb +32 -2
  238. data/test/xml/test_comment.rb +40 -0
  239. data/test/xml/test_document.rb +846 -35
  240. data/test/xml/test_document_encoding.rb +31 -0
  241. data/test/xml/test_document_fragment.rb +271 -0
  242. data/test/xml/test_dtd.rb +153 -9
  243. data/test/xml/test_dtd_encoding.rb +31 -0
  244. data/test/xml/test_element_content.rb +56 -0
  245. data/test/xml/test_element_decl.rb +73 -0
  246. data/test/xml/test_entity_decl.rb +122 -0
  247. data/test/xml/test_entity_reference.rb +251 -0
  248. data/test/xml/test_namespace.rb +96 -0
  249. data/test/xml/test_node.rb +1126 -105
  250. data/test/xml/test_node_attributes.rb +115 -0
  251. data/test/xml/test_node_encoding.rb +69 -0
  252. data/test/xml/test_node_inheritance.rb +32 -0
  253. data/test/xml/test_node_reparenting.rb +549 -0
  254. data/test/xml/test_node_set.rb +668 -9
  255. data/test/xml/test_parse_options.rb +64 -0
  256. data/test/xml/test_processing_instruction.rb +30 -0
  257. data/test/xml/test_reader.rb +589 -0
  258. data/test/xml/test_reader_encoding.rb +134 -0
  259. data/test/xml/test_relax_ng.rb +60 -0
  260. data/test/xml/test_schema.rb +142 -0
  261. data/test/xml/test_syntax_error.rb +30 -0
  262. data/test/xml/test_text.rb +49 -2
  263. data/test/xml/test_unparented_node.rb +440 -0
  264. data/test/xml/test_xinclude.rb +83 -0
  265. data/test/xml/test_xpath.rb +445 -0
  266. data/test/xslt/test_custom_functions.rb +133 -0
  267. data/test/xslt/test_exception_handling.rb +37 -0
  268. data/test_all +107 -0
  269. metadata +459 -115
  270. data/History.txt +0 -6
  271. data/README.ja.txt +0 -86
  272. data/README.txt +0 -87
  273. data/ext/nokogiri/html_sax_parser.c +0 -32
  274. data/ext/nokogiri/html_sax_parser.h +0 -11
  275. data/ext/nokogiri/native.c +0 -40
  276. data/ext/nokogiri/native.h +0 -51
  277. data/ext/nokogiri/xml_xpath.c +0 -46
  278. data/ext/nokogiri/xml_xpath.h +0 -11
  279. data/lib/nokogiri/css/generated_parser.rb +0 -653
  280. data/lib/nokogiri/css/generated_tokenizer.rb +0 -159
  281. data/lib/nokogiri/decorators/hpricot/node.rb +0 -58
  282. data/lib/nokogiri/decorators/hpricot/node_set.rb +0 -14
  283. data/lib/nokogiri/decorators/hpricot/xpath_visitor.rb +0 -17
  284. data/lib/nokogiri/decorators/hpricot.rb +0 -3
  285. data/lib/nokogiri/decorators.rb +0 -1
  286. data/lib/nokogiri/hpricot.rb +0 -47
  287. data/lib/nokogiri/xml/after_handler.rb +0 -18
  288. data/lib/nokogiri/xml/before_handler.rb +0 -32
  289. data/lib/nokogiri/xml/element.rb +0 -6
  290. data/lib/nokogiri/xml/entity_declaration.rb +0 -9
  291. data/nokogiri.gemspec +0 -34
  292. data/test/hpricot/files/basic.xhtml +0 -17
  293. data/test/hpricot/files/boingboing.html +0 -2266
  294. data/test/hpricot/files/cy0.html +0 -3653
  295. data/test/hpricot/files/immob.html +0 -400
  296. data/test/hpricot/files/pace_application.html +0 -1320
  297. data/test/hpricot/files/tenderlove.html +0 -16
  298. data/test/hpricot/files/uswebgen.html +0 -220
  299. data/test/hpricot/files/utf8.html +0 -1054
  300. data/test/hpricot/files/week9.html +0 -1723
  301. data/test/hpricot/files/why.xml +0 -19
  302. data/test/hpricot/load_files.rb +0 -7
  303. data/test/hpricot/test_alter.rb +0 -67
  304. data/test/hpricot/test_builder.rb +0 -27
  305. data/test/hpricot/test_parser.rb +0 -423
  306. data/test/hpricot/test_paths.rb +0 -15
  307. data/test/hpricot/test_preserved.rb +0 -78
  308. data/test/hpricot/test_xml.rb +0 -30
  309. data/test/test_reader.rb +0 -222
@@ -1,195 +1,834 @@
1
+ # encoding: UTF-8
2
+ require 'stringio'
3
+ require 'nokogiri/xml/node/save_options'
4
+
1
5
  module Nokogiri
2
6
  module XML
7
+ ####
8
+ # Nokogiri::XML::Node is your window to the fun filled world of dealing
9
+ # with XML and HTML tags. A Nokogiri::XML::Node may be treated similarly
10
+ # to a hash with regard to attributes. For example (from irb):
11
+ #
12
+ # irb(main):004:0> node
13
+ # => <a href="#foo" id="link">link</a>
14
+ # irb(main):005:0> node['href']
15
+ # => "#foo"
16
+ # irb(main):006:0> node.keys
17
+ # => ["href", "id"]
18
+ # irb(main):007:0> node.values
19
+ # => ["#foo", "link"]
20
+ # irb(main):008:0> node['class'] = 'green'
21
+ # => "green"
22
+ # irb(main):009:0> node
23
+ # => <a href="#foo" id="link" class="green">link</a>
24
+ # irb(main):010:0>
25
+ #
26
+ # See Nokogiri::XML::Node#[] and Nokogiri::XML#[]= for more information.
27
+ #
28
+ # Nokogiri::XML::Node also has methods that let you move around your
29
+ # tree. For navigating your tree, see:
30
+ #
31
+ # * Nokogiri::XML::Node#parent
32
+ # * Nokogiri::XML::Node#children
33
+ # * Nokogiri::XML::Node#next
34
+ # * Nokogiri::XML::Node#previous
35
+ #
36
+ #
37
+ # When printing or otherwise emitting a document or a node (and
38
+ # its subtree), there are a few methods you might want to use:
39
+ #
40
+ # * content, text, inner_text, to_str: emit plaintext
41
+ #
42
+ # These methods will all emit the plaintext version of your
43
+ # document, meaning that entities will be replaced (e.g., "&lt;"
44
+ # will be replaced with "<"), meaning that any sanitizing will
45
+ # likely be un-done in the output.
46
+ #
47
+ # * to_s, to_xml, to_html, inner_html: emit well-formed markup
48
+ #
49
+ # These methods will all emit properly-escaped markup, meaning
50
+ # that it's suitable for consumption by browsers, parsers, etc.
51
+ #
52
+ # You may search this node's subtree using Searchable#xpath and Searchable#css
3
53
  class Node
54
+ include Nokogiri::XML::PP::Node
55
+ include Nokogiri::XML::Searchable
56
+ include Enumerable
57
+
58
+ # Element node type, see Nokogiri::XML::Node#element?
59
+ ELEMENT_NODE = 1
60
+ # Attribute node type
61
+ ATTRIBUTE_NODE = 2
62
+ # Text node type, see Nokogiri::XML::Node#text?
63
+ TEXT_NODE = 3
64
+ # CDATA node type, see Nokogiri::XML::Node#cdata?
4
65
  CDATA_SECTION_NODE = 4
5
- COMMENT_NODE = 8
6
- DOCUMENT_NODE = 9
66
+ # Entity reference node type
67
+ ENTITY_REF_NODE = 5
68
+ # Entity node type
69
+ ENTITY_NODE = 6
70
+ # PI node type
71
+ PI_NODE = 7
72
+ # Comment node type, see Nokogiri::XML::Node#comment?
73
+ COMMENT_NODE = 8
74
+ # Document node type, see Nokogiri::XML::Node#xml?
75
+ DOCUMENT_NODE = 9
76
+ # Document type node type
77
+ DOCUMENT_TYPE_NODE = 10
78
+ # Document fragment node type
79
+ DOCUMENT_FRAG_NODE = 11
80
+ # Notation node type
81
+ NOTATION_NODE = 12
82
+ # HTML document node type, see Nokogiri::XML::Node#html?
7
83
  HTML_DOCUMENT_NODE = 13
8
- DTD_NODE = 14
9
- ELEMENT_DECL = 15
10
- ATTRIBUTE_DECL = 16
11
- ENTITY_DECL = 17
12
- NAMESPACE_DECL = 18
13
- XINCLUDE_START = 19
14
- XINCLUDE_END = 20
84
+ # DTD node type
85
+ DTD_NODE = 14
86
+ # Element declaration type
87
+ ELEMENT_DECL = 15
88
+ # Attribute declaration type
89
+ ATTRIBUTE_DECL = 16
90
+ # Entity declaration type
91
+ ENTITY_DECL = 17
92
+ # Namespace declaration type
93
+ NAMESPACE_DECL = 18
94
+ # XInclude start type
95
+ XINCLUDE_START = 19
96
+ # XInclude end type
97
+ XINCLUDE_END = 20
98
+ # DOCB document node type
15
99
  DOCB_DOCUMENT_NODE = 21
16
100
 
17
- @@owned = {}
101
+ def initialize name, document # :nodoc:
102
+ # ... Ya. This is empty on purpose.
103
+ end
18
104
 
19
105
  ###
20
106
  # Decorate this node with the decorators set up in this node's Document
21
107
  def decorate!
22
- document.decorate(self) if document
108
+ document.decorate(self)
23
109
  end
24
110
 
25
111
  ###
26
- # Get the list of children for this node as a NodeSet
27
- def children
28
- list = NodeSet.new
29
- list.document = document
30
- document.decorate(list)
31
-
32
- first = self.child
33
- return list unless first # Empty list
34
-
35
- list << first unless first.blank?
36
- while first = first.next
37
- list << first unless first.blank?
38
- end
39
- list
112
+ # Search this node's immediate children using CSS selector +selector+
113
+ def > selector
114
+ ns = document.root.namespaces
115
+ xpath CSS.xpath_for(selector, :prefix => "./", :ns => ns).first
40
116
  end
41
117
 
42
118
  ###
43
- # Search this node for +paths+. +paths+ can be XPath or CSS, and an
44
- # optional hash of namespaces may be appended.
45
- # See Node#xpath and Node#css.
46
- def search *paths
47
- ns = paths.last.is_a?(Hash) ? paths.pop : {}
48
- xpath(*(paths.map { |path|
49
- path =~ /^(\.\/|\/)/ ? path : CSS::Parser.parse(path).map { |ast|
50
- ast.to_xpath
51
- }
52
- }.flatten.uniq) + [ns])
119
+ # Get the attribute value for the attribute +name+
120
+ def [] name
121
+ get(name.to_s)
53
122
  end
54
- alias :/ :search
55
123
 
56
- def xpath *paths
57
- ns = paths.last.is_a?(Hash) ? paths.pop : {}
124
+ ###
125
+ # Set the attribute value for the attribute +name+ to +value+
126
+ def []= name, value
127
+ set name.to_s, value.to_s
128
+ end
58
129
 
59
- return NodeSet.new unless document.root
130
+ ###
131
+ # Add +node_or_tags+ as a child of this Node.
132
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
133
+ #
134
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
135
+ #
136
+ # Also see related method +<<+.
137
+ def add_child node_or_tags
138
+ node_or_tags = coerce(node_or_tags)
139
+ if node_or_tags.is_a?(XML::NodeSet)
140
+ node_or_tags.each { |n| add_child_node_and_reparent_attrs n }
141
+ else
142
+ add_child_node_and_reparent_attrs node_or_tags
143
+ end
144
+ node_or_tags
145
+ end
60
146
 
61
- sets = paths.map { |path|
62
- ctx = XPathContext.new(self)
63
- ctx.register_namespaces(ns)
64
- set = ctx.evaluate(path).node_set
65
- set.document = document
66
- document.decorate(set)
67
- set
68
- }
69
- return sets.first if sets.length == 1
70
-
71
- NodeSet.new do |combined|
72
- document.decorate(combined)
73
- sets.each do |set|
74
- set.each do |node|
75
- combined << node
76
- end
77
- end
147
+ ###
148
+ # Add +node_or_tags+ as the first child of this Node.
149
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
150
+ #
151
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
152
+ #
153
+ # Also see related method +add_child+.
154
+ def prepend_child node_or_tags
155
+ if first = children.first
156
+ # Mimic the error add_child would raise.
157
+ raise RuntimeError, "Document already has a root node" if document? && !node_or_tags.processing_instruction?
158
+ first.__send__(:add_sibling, :previous, node_or_tags)
159
+ else
160
+ add_child(node_or_tags)
78
161
  end
79
162
  end
80
163
 
81
- def css *rules
82
- xpath(*(rules.map { |rule|
83
- CSS::Parser.parse(rule).map { |ast| "." + ast.to_xpath }
84
- }.flatten.uniq))
164
+ ###
165
+ # Add +node_or_tags+ as a child of this Node.
166
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
167
+ #
168
+ # Returns self, to support chaining of calls (e.g., root << child1 << child2)
169
+ #
170
+ # Also see related method +add_child+.
171
+ def << node_or_tags
172
+ add_child node_or_tags
173
+ self
85
174
  end
175
+ ###
176
+ # Insert +node_or_tags+ before this Node (as a sibling).
177
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
178
+ #
179
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
180
+ #
181
+ # Also see related method +before+.
182
+ def add_previous_sibling node_or_tags
183
+ raise ArgumentError.new("A document may not have multiple root nodes.") if (parent && parent.document?) && !node_or_tags.processing_instruction?
86
184
 
87
- def at path, ns = {}
88
- search("#{path}", ns).first
185
+ add_sibling :previous, node_or_tags
89
186
  end
90
187
 
91
- def [](property)
92
- return nil unless key?(property)
93
- get(property)
188
+ ###
189
+ # Insert +node_or_tags+ after this Node (as a sibling).
190
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
191
+ #
192
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
193
+ #
194
+ # Also see related method +after+.
195
+ def add_next_sibling node_or_tags
196
+ raise ArgumentError.new("A document may not have multiple root nodes.") if (parent && parent.document?) && !node_or_tags.processing_instruction?
197
+
198
+ add_sibling :next, node_or_tags
94
199
  end
95
200
 
96
- def next
97
- next_sibling
201
+ ####
202
+ # Insert +node_or_tags+ before this node (as a sibling).
203
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
204
+ #
205
+ # Returns self, to support chaining of calls.
206
+ #
207
+ # Also see related method +add_previous_sibling+.
208
+ def before node_or_tags
209
+ add_previous_sibling node_or_tags
210
+ self
98
211
  end
99
212
 
100
- def remove
101
- unlink
213
+ ####
214
+ # Insert +node_or_tags+ after this node (as a sibling).
215
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
216
+ #
217
+ # Returns self, to support chaining of calls.
218
+ #
219
+ # Also see related method +add_next_sibling+.
220
+ def after node_or_tags
221
+ add_next_sibling node_or_tags
222
+ self
102
223
  end
103
224
 
104
225
  ####
105
- # Create nodes from +data+ and insert them before this node
106
- # (as a sibling).
107
- def before data
108
- classes = document.class.name.split('::')
109
- classes[-1] = 'SAX::Parser'
226
+ # Set the inner html for this Node to +node_or_tags+
227
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
228
+ #
229
+ # Returns self.
230
+ #
231
+ # Also see related method +children=+
232
+ def inner_html= node_or_tags
233
+ self.children = node_or_tags
234
+ self
235
+ end
110
236
 
111
- parser = eval(classes.join('::')).new(BeforeHandler.new(self, data))
112
- parser.parse(data)
237
+ ####
238
+ # Set the inner html for this Node +node_or_tags+
239
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
240
+ #
241
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
242
+ #
243
+ # Also see related method +inner_html=+
244
+ def children= node_or_tags
245
+ node_or_tags = coerce(node_or_tags)
246
+ children.unlink
247
+ if node_or_tags.is_a?(XML::NodeSet)
248
+ node_or_tags.each { |n| add_child_node_and_reparent_attrs n }
249
+ else
250
+ add_child_node_and_reparent_attrs node_or_tags
251
+ end
252
+ node_or_tags
113
253
  end
114
254
 
115
255
  ####
116
- # Create nodes from +data+ and insert them after this node
117
- # (as a sibling).
118
- def after data
119
- classes = document.class.name.split('::')
120
- classes[-1] = 'SAX::Parser'
256
+ # Replace this Node with +node_or_tags+.
257
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
258
+ #
259
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
260
+ #
261
+ # Also see related method +swap+.
262
+ def replace node_or_tags
263
+ # We cannot replace a text node directly, otherwise libxml will return
264
+ # an internal error at parser.c:13031, I don't know exactly why
265
+ # libxml is trying to find a parent node that is an element or document
266
+ # so I can't tell if this is bug in libxml or not. issue #775.
267
+ if text?
268
+ replacee = Nokogiri::XML::Node.new 'dummy', document
269
+ add_previous_sibling_node replacee
270
+ unlink
271
+ return replacee.replace node_or_tags
272
+ end
273
+
274
+ node_or_tags = coerce(node_or_tags)
121
275
 
122
- handler = AfterHandler.new(self, data)
123
- parser = eval(classes.join('::')).new(handler)
124
- parser.parse(data)
125
- handler.after_nodes.reverse.each do |sibling|
126
- self.add_next_sibling sibling
276
+ if node_or_tags.is_a?(XML::NodeSet)
277
+ node_or_tags.each { |n| add_previous_sibling n }
278
+ unlink
279
+ else
280
+ replace_node node_or_tags
127
281
  end
282
+ node_or_tags
128
283
  end
129
284
 
130
- def has_attribute?(property)
131
- key? property
285
+ ####
286
+ # Swap this Node for +node_or_tags+
287
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
288
+ #
289
+ # Returns self, to support chaining of calls.
290
+ #
291
+ # Also see related method +replace+.
292
+ def swap node_or_tags
293
+ replace node_or_tags
294
+ self
132
295
  end
133
296
 
134
- alias :get_attribute :[]
135
- def set_attribute(name, value)
136
- self[name] = value
297
+ alias :next :next_sibling
298
+ alias :previous :previous_sibling
299
+
300
+ # :stopdoc:
301
+ # HACK: This is to work around an RDoc bug
302
+ alias :next= :add_next_sibling
303
+ # :startdoc:
304
+
305
+ alias :previous= :add_previous_sibling
306
+ alias :remove :unlink
307
+ alias :get_attribute :[]
308
+ alias :attr :[]
309
+ alias :set_attribute :[]=
310
+ alias :text :content
311
+ alias :inner_text :content
312
+ alias :has_attribute? :key?
313
+ alias :name :node_name
314
+ alias :name= :node_name=
315
+ alias :type :node_type
316
+ alias :to_str :text
317
+ alias :clone :dup
318
+ alias :elements :element_children
319
+
320
+ ####
321
+ # Returns a hash containing the node's attributes. The key is
322
+ # the attribute name without any namespace, the value is a Nokogiri::XML::Attr
323
+ # representing the attribute.
324
+ # If you need to distinguish attributes with the same name, with different namespaces
325
+ # use #attribute_nodes instead.
326
+ def attributes
327
+ Hash[attribute_nodes.map { |node|
328
+ [node.node_name, node]
329
+ }]
137
330
  end
138
331
 
139
- def text
140
- content
332
+ ###
333
+ # Get the attribute values for this Node.
334
+ def values
335
+ attribute_nodes.map(&:value)
336
+ end
337
+
338
+ ###
339
+ # Get the attribute names for this Node.
340
+ def keys
341
+ attribute_nodes.map(&:node_name)
342
+ end
343
+
344
+ ###
345
+ # Iterate over each attribute name and value pair for this Node.
346
+ def each
347
+ attribute_nodes.each { |node|
348
+ yield [node.node_name, node.value]
349
+ }
350
+ end
351
+
352
+ ###
353
+ # Remove the attribute named +name+
354
+ def remove_attribute name
355
+ attr = attributes[name].remove if key? name
356
+ clear_xpath_context if Nokogiri.jruby?
357
+ attr
358
+ end
359
+ alias :delete :remove_attribute
360
+
361
+ ###
362
+ # Returns true if this Node matches +selector+
363
+ def matches? selector
364
+ ancestors.last.search(selector).include?(self)
365
+ end
366
+
367
+ ###
368
+ # Create a DocumentFragment containing +tags+ that is relative to _this_
369
+ # context node.
370
+ def fragment tags
371
+ type = document.html? ? Nokogiri::HTML : Nokogiri::XML
372
+ type::DocumentFragment.new(document, tags, self)
373
+ end
374
+
375
+ ###
376
+ # Parse +string_or_io+ as a document fragment within the context of
377
+ # *this* node. Returns a XML::NodeSet containing the nodes parsed from
378
+ # +string_or_io+.
379
+ def parse string_or_io, options = nil
380
+ ##
381
+ # When the current node is unparented and not an element node, use the
382
+ # document as the parsing context instead. Otherwise, the in-context
383
+ # parser cannot find an element or a document node.
384
+ # Document Fragments are also not usable by the in-context parser.
385
+ if !element? && !document? && (!parent || parent.fragment?)
386
+ return document.parse(string_or_io, options)
387
+ end
388
+
389
+ options ||= (document.html? ? ParseOptions::DEFAULT_HTML : ParseOptions::DEFAULT_XML)
390
+ if Fixnum === options
391
+ options = Nokogiri::XML::ParseOptions.new(options)
392
+ end
393
+ # Give the options to the user
394
+ yield options if block_given?
395
+
396
+ contents = string_or_io.respond_to?(:read) ?
397
+ string_or_io.read :
398
+ string_or_io
399
+
400
+ return Nokogiri::XML::NodeSet.new(document) if contents.empty?
401
+
402
+ ##
403
+ # This is a horrible hack, but I don't care. See #313 for background.
404
+ error_count = document.errors.length
405
+ node_set = in_context(contents, options.to_i)
406
+ if node_set.empty? and document.errors.length > error_count and options.recover?
407
+ fragment = Nokogiri::HTML::DocumentFragment.parse contents
408
+ node_set = fragment.children
409
+ end
410
+ node_set
141
411
  end
142
- alias :inner_text :text
143
412
 
144
413
  ####
145
- # Set the content to +string+.
146
- # If +encode+, encode any special characters first.
147
- def content= string, encode = true
148
- self.native_content = encode_special_chars(string)
414
+ # Set the Node's content to a Text node containing +string+. The string gets XML escaped, not interpreted as markup.
415
+ def content= string
416
+ self.native_content = encode_special_chars(string.to_s)
417
+ end
418
+
419
+ ###
420
+ # Set the parent Node for this Node
421
+ def parent= parent_node
422
+ parent_node.add_child(self)
423
+ parent_node
149
424
  end
150
425
 
426
+ ###
427
+ # Returns a Hash of {prefix => value} for all namespaces on this
428
+ # node and its ancestors.
429
+ #
430
+ # This method returns the same namespaces as #namespace_scopes.
431
+ #
432
+ # Returns namespaces in scope for self -- those defined on self
433
+ # element directly or any ancestor node -- as a Hash of
434
+ # attribute-name/value pairs. Note that the keys in this hash
435
+ # XML attributes that would be used to define this namespace,
436
+ # such as "xmlns:prefix", not just the prefix. Default namespace
437
+ # set on self will be included with key "xmlns". However,
438
+ # default namespaces set on ancestor will NOT be, even if self
439
+ # has no explicit default namespace.
440
+ def namespaces
441
+ Hash[namespace_scopes.map { |nd|
442
+ key = ['xmlns', nd.prefix].compact.join(':')
443
+ [key, nd.href]
444
+ }]
445
+ end
446
+
447
+ # Returns true if this is a Comment
151
448
  def comment?
152
449
  type == COMMENT_NODE
153
450
  end
154
451
 
452
+ # Returns true if this is a CDATA
155
453
  def cdata?
156
454
  type == CDATA_SECTION_NODE
157
455
  end
158
456
 
457
+ # Returns true if this is an XML::Document node
159
458
  def xml?
160
459
  type == DOCUMENT_NODE
161
460
  end
162
461
 
462
+ # Returns true if this is an HTML::Document node
163
463
  def html?
164
464
  type == HTML_DOCUMENT_NODE
165
465
  end
166
466
 
167
- def to_html
168
- to_xml
467
+ # Returns true if this is a Document
468
+ def document?
469
+ is_a? XML::Document
470
+ end
471
+
472
+ # Returns true if this is a ProcessingInstruction node
473
+ def processing_instruction?
474
+ type == PI_NODE
475
+ end
476
+
477
+ # Returns true if this is a Text node
478
+ def text?
479
+ type == TEXT_NODE
480
+ end
481
+
482
+ # Returns true if this is a DocumentFragment
483
+ def fragment?
484
+ type == DOCUMENT_FRAG_NODE
485
+ end
486
+
487
+ ###
488
+ # Fetch the Nokogiri::HTML::ElementDescription for this node. Returns
489
+ # nil on XML documents and on unknown tags.
490
+ def description
491
+ return nil if document.xml?
492
+ Nokogiri::HTML::ElementDescription[name]
493
+ end
494
+
495
+ ###
496
+ # Is this a read only node?
497
+ def read_only?
498
+ # According to gdome2, these are read-only node types
499
+ [NOTATION_NODE, ENTITY_NODE, ENTITY_DECL].include?(type)
169
500
  end
170
- alias :to_s :to_html
171
- alias :inner_html :to_html
172
501
 
502
+ # Returns true if this is an Element node
503
+ def element?
504
+ type == ELEMENT_NODE
505
+ end
506
+ alias :elem? :element?
507
+
508
+ ###
509
+ # Turn this node in to a string. If the document is HTML, this method
510
+ # returns html. If the document is XML, this method returns XML.
511
+ def to_s
512
+ document.xml? ? to_xml : to_html
513
+ end
514
+
515
+ # Get the inner_html for this node's Node#children
516
+ def inner_html *args
517
+ children.map { |x| x.to_html(*args) }.join
518
+ end
519
+
520
+ # Get the path to this node as a CSS expression
173
521
  def css_path
174
522
  path.split(/\//).map { |part|
175
523
  part.length == 0 ? nil : part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
176
524
  }.compact.join(' > ')
177
525
  end
178
526
 
179
- # recursively get all namespaces from this node and its subtree
180
- def collect_namespaces
181
- # TODO: print warning message if a prefix refers to more than one URI in the document?
182
- ns = {}
183
- traverse {|j| ns.merge!(j.namespaces)}
184
- ns
527
+ ###
528
+ # Get a list of ancestor Node for this Node. If +selector+ is given,
529
+ # the ancestors must match +selector+
530
+ def ancestors selector = nil
531
+ return NodeSet.new(document) unless respond_to?(:parent)
532
+ return NodeSet.new(document) unless parent
533
+
534
+ parents = [parent]
535
+
536
+ while parents.last.respond_to?(:parent)
537
+ break unless ctx_parent = parents.last.parent
538
+ parents << ctx_parent
539
+ end
540
+
541
+ return NodeSet.new(document, parents) unless selector
542
+
543
+ root = parents.last
544
+ search_results = root.search(selector)
545
+
546
+ NodeSet.new(document, parents.find_all { |parent|
547
+ search_results.include?(parent)
548
+ })
549
+ end
550
+
551
+ ###
552
+ # Adds a default namespace supplied as a string +url+ href, to self.
553
+ # The consequence is as an xmlns attribute with supplied argument were
554
+ # present in parsed XML. A default namespace set with this method will
555
+ # now show up in #attributes, but when this node is serialized to XML an
556
+ # "xmlns" attribute will appear. See also #namespace and #namespace=
557
+ def default_namespace= url
558
+ add_namespace_definition(nil, url)
559
+ end
560
+ alias :add_namespace :add_namespace_definition
561
+
562
+ ###
563
+ # Set the default namespace on this node (as would be defined with an
564
+ # "xmlns=" attribute in XML source), as a Namespace object +ns+. Note that
565
+ # a Namespace added this way will NOT be serialized as an xmlns attribute
566
+ # for this node. You probably want #default_namespace= instead, or perhaps
567
+ # #add_namespace_definition with a nil prefix argument.
568
+ def namespace= ns
569
+ return set_namespace(ns) unless ns
570
+
571
+ unless Nokogiri::XML::Namespace === ns
572
+ raise TypeError, "#{ns.class} can't be coerced into Nokogiri::XML::Namespace"
573
+ end
574
+ if ns.document != document
575
+ raise ArgumentError, 'namespace must be declared on the same document'
576
+ end
577
+
578
+ set_namespace ns
185
579
  end
186
580
 
187
581
  ####
188
582
  # Yields self and all children to +block+ recursively.
189
- def traverse(&block)
583
+ def traverse &block
190
584
  children.each{|j| j.traverse(&block) }
191
585
  block.call(self)
192
586
  end
587
+
588
+ ###
589
+ # Accept a visitor. This method calls "visit" on +visitor+ with self.
590
+ def accept visitor
591
+ visitor.visit(self)
592
+ end
593
+
594
+ ###
595
+ # Test to see if this Node is equal to +other+
596
+ def == other
597
+ return false unless other
598
+ return false unless other.respond_to?(:pointer_id)
599
+ pointer_id == other.pointer_id
600
+ end
601
+
602
+ ###
603
+ # Serialize Node using +options+. Save options can also be set using a
604
+ # block. See SaveOptions.
605
+ #
606
+ # These two statements are equivalent:
607
+ #
608
+ # node.serialize(:encoding => 'UTF-8', :save_with => FORMAT | AS_XML)
609
+ #
610
+ # or
611
+ #
612
+ # node.serialize(:encoding => 'UTF-8') do |config|
613
+ # config.format.as_xml
614
+ # end
615
+ #
616
+ def serialize *args, &block
617
+ options = args.first.is_a?(Hash) ? args.shift : {
618
+ :encoding => args[0],
619
+ :save_with => args[1]
620
+ }
621
+
622
+ encoding = options[:encoding] || document.encoding
623
+ options[:encoding] = encoding
624
+
625
+ outstring = ""
626
+ if encoding && outstring.respond_to?(:force_encoding)
627
+ outstring.force_encoding(Encoding.find(encoding))
628
+ end
629
+ io = StringIO.new(outstring)
630
+ write_to io, options, &block
631
+ io.string
632
+ end
633
+
634
+ ###
635
+ # Serialize this Node to HTML
636
+ #
637
+ # doc.to_html
638
+ #
639
+ # See Node#write_to for a list of +options+. For formatted output,
640
+ # use Node#to_xhtml instead.
641
+ def to_html options = {}
642
+ to_format SaveOptions::DEFAULT_HTML, options
643
+ end
644
+
645
+ ###
646
+ # Serialize this Node to XML using +options+
647
+ #
648
+ # doc.to_xml(:indent => 5, :encoding => 'UTF-8')
649
+ #
650
+ # See Node#write_to for a list of +options+
651
+ def to_xml options = {}
652
+ options[:save_with] ||= SaveOptions::DEFAULT_XML
653
+ serialize(options)
654
+ end
655
+
656
+ ###
657
+ # Serialize this Node to XHTML using +options+
658
+ #
659
+ # doc.to_xhtml(:indent => 5, :encoding => 'UTF-8')
660
+ #
661
+ # See Node#write_to for a list of +options+
662
+ def to_xhtml options = {}
663
+ to_format SaveOptions::DEFAULT_XHTML, options
664
+ end
665
+
666
+ ###
667
+ # Write Node to +io+ with +options+. +options+ modify the output of
668
+ # this method. Valid options are:
669
+ #
670
+ # * +:encoding+ for changing the encoding
671
+ # * +:indent_text+ the indentation text, defaults to one space
672
+ # * +:indent+ the number of +:indent_text+ to use, defaults to 2
673
+ # * +:save_with+ a combination of SaveOptions constants.
674
+ #
675
+ # To save with UTF-8 indented twice:
676
+ #
677
+ # node.write_to(io, :encoding => 'UTF-8', :indent => 2)
678
+ #
679
+ # To save indented with two dashes:
680
+ #
681
+ # node.write_to(io, :indent_text => '-', :indent => 2
682
+ #
683
+ def write_to io, *options
684
+ options = options.first.is_a?(Hash) ? options.shift : {}
685
+ encoding = options[:encoding] || options[0]
686
+ if Nokogiri.jruby?
687
+ save_options = options[:save_with] || options[1]
688
+ indent_times = options[:indent] || 0
689
+ else
690
+ save_options = options[:save_with] || options[1] || SaveOptions::FORMAT
691
+ indent_times = options[:indent] || 2
692
+ end
693
+ indent_text = options[:indent_text] || ' '
694
+
695
+ config = SaveOptions.new(save_options.to_i)
696
+ yield config if block_given?
697
+
698
+ native_write_to(io, encoding, indent_text * indent_times, config.options)
699
+ end
700
+
701
+ ###
702
+ # Write Node as HTML to +io+ with +options+
703
+ #
704
+ # See Node#write_to for a list of +options+
705
+ def write_html_to io, options = {}
706
+ write_format_to SaveOptions::DEFAULT_HTML, io, options
707
+ end
708
+
709
+ ###
710
+ # Write Node as XHTML to +io+ with +options+
711
+ #
712
+ # See Node#write_to for a list of +options+
713
+ def write_xhtml_to io, options = {}
714
+ write_format_to SaveOptions::DEFAULT_XHTML, io, options
715
+ end
716
+
717
+ ###
718
+ # Write Node as XML to +io+ with +options+
719
+ #
720
+ # doc.write_xml_to io, :encoding => 'UTF-8'
721
+ #
722
+ # See Node#write_to for a list of options
723
+ def write_xml_to io, options = {}
724
+ options[:save_with] ||= SaveOptions::DEFAULT_XML
725
+ write_to io, options
726
+ end
727
+
728
+ ###
729
+ # Compare two Node objects with respect to their Document. Nodes from
730
+ # different documents cannot be compared.
731
+ def <=> other
732
+ return nil unless other.is_a?(Nokogiri::XML::Node)
733
+ return nil unless document == other.document
734
+ compare other
735
+ end
736
+
737
+ ###
738
+ # Do xinclude substitution on the subtree below node. If given a block, a
739
+ # Nokogiri::XML::ParseOptions object initialized from +options+, will be
740
+ # passed to it, allowing more convenient modification of the parser options.
741
+ def do_xinclude options = XML::ParseOptions::DEFAULT_XML, &block
742
+ options = Nokogiri::XML::ParseOptions.new(options) if Fixnum === options
743
+
744
+ # give options to user
745
+ yield options if block_given?
746
+
747
+ # call c extension
748
+ process_xincludes(options.to_i)
749
+ end
750
+
751
+ def canonicalize(mode=XML::XML_C14N_1_0,inclusive_namespaces=nil,with_comments=false)
752
+ c14n_root = self
753
+ document.canonicalize(mode, inclusive_namespaces, with_comments) do |node, parent|
754
+ tn = node.is_a?(XML::Node) ? node : parent
755
+ tn == c14n_root || tn.ancestors.include?(c14n_root)
756
+ end
757
+ end
758
+
759
+ private
760
+
761
+ def add_sibling next_or_previous, node_or_tags
762
+ impl = (next_or_previous == :next) ? :add_next_sibling_node : :add_previous_sibling_node
763
+ iter = (next_or_previous == :next) ? :reverse_each : :each
764
+
765
+ node_or_tags = coerce node_or_tags
766
+ if node_or_tags.is_a?(XML::NodeSet)
767
+ if text?
768
+ pivot = Nokogiri::XML::Node.new 'dummy', document
769
+ send impl, pivot
770
+ else
771
+ pivot = self
772
+ end
773
+ node_or_tags.send(iter) { |n| pivot.send impl, n }
774
+ pivot.unlink if text?
775
+ else
776
+ send impl, node_or_tags
777
+ end
778
+ node_or_tags
779
+ end
780
+
781
+ def to_format save_option, options
782
+ # FIXME: this is a hack around broken libxml versions
783
+ return dump_html if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
784
+
785
+ options[:save_with] = save_option unless options[:save_with]
786
+ serialize(options)
787
+ end
788
+
789
+ def write_format_to save_option, io, options
790
+ # FIXME: this is a hack around broken libxml versions
791
+ return (io << dump_html) if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
792
+
793
+ options[:save_with] ||= save_option
794
+ write_to io, options
795
+ end
796
+
797
+ def inspect_attributes
798
+ [:name, :namespace, :attribute_nodes, :children]
799
+ end
800
+
801
+ def coerce data # :nodoc:
802
+ case data
803
+ when XML::NodeSet
804
+ return data
805
+ when XML::DocumentFragment
806
+ return data.children
807
+ when String
808
+ return fragment(data).children
809
+ when Document, XML::Attr
810
+ # unacceptable
811
+ when XML::Node
812
+ return data
813
+ end
814
+
815
+ raise ArgumentError, <<-EOERR
816
+ Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
817
+ (You probably want to select a node from the Document with at() or search(), or create a new Node via Node.new().)
818
+ EOERR
819
+ end
820
+
821
+ def implied_xpath_contexts # :nodoc:
822
+ [".//"]
823
+ end
824
+
825
+ def add_child_node_and_reparent_attrs node # :nodoc:
826
+ add_child_node node
827
+ node.attribute_nodes.find_all { |a| a.name =~ /:/ }.each do |attr_node|
828
+ attr_node.remove
829
+ node[attr_node.name] = attr_node.value
830
+ end
831
+ end
193
832
  end
194
833
  end
195
834
  end