Nokogiri_precompiled_aarch64_dedshit 1.14.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (263) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +44 -0
  3. data/LICENSE-DEPENDENCIES.md +2224 -0
  4. data/LICENSE.md +9 -0
  5. data/README.md +287 -0
  6. data/bin/nokogiri +131 -0
  7. data/dependencies.yml +41 -0
  8. data/ext/java/nokogiri/Html4Document.java +157 -0
  9. data/ext/java/nokogiri/Html4ElementDescription.java +133 -0
  10. data/ext/java/nokogiri/Html4EntityLookup.java +63 -0
  11. data/ext/java/nokogiri/Html4SaxParserContext.java +289 -0
  12. data/ext/java/nokogiri/Html4SaxPushParser.java +213 -0
  13. data/ext/java/nokogiri/NokogiriService.java +613 -0
  14. data/ext/java/nokogiri/XmlAttr.java +154 -0
  15. data/ext/java/nokogiri/XmlAttributeDecl.java +119 -0
  16. data/ext/java/nokogiri/XmlCdata.java +60 -0
  17. data/ext/java/nokogiri/XmlComment.java +77 -0
  18. data/ext/java/nokogiri/XmlDocument.java +705 -0
  19. data/ext/java/nokogiri/XmlDocumentFragment.java +163 -0
  20. data/ext/java/nokogiri/XmlDtd.java +516 -0
  21. data/ext/java/nokogiri/XmlElement.java +44 -0
  22. data/ext/java/nokogiri/XmlElementContent.java +412 -0
  23. data/ext/java/nokogiri/XmlElementDecl.java +148 -0
  24. data/ext/java/nokogiri/XmlEntityDecl.java +151 -0
  25. data/ext/java/nokogiri/XmlEntityReference.java +79 -0
  26. data/ext/java/nokogiri/XmlNamespace.java +193 -0
  27. data/ext/java/nokogiri/XmlNode.java +1938 -0
  28. data/ext/java/nokogiri/XmlNodeSet.java +463 -0
  29. data/ext/java/nokogiri/XmlProcessingInstruction.java +79 -0
  30. data/ext/java/nokogiri/XmlReader.java +615 -0
  31. data/ext/java/nokogiri/XmlRelaxng.java +133 -0
  32. data/ext/java/nokogiri/XmlSaxParserContext.java +329 -0
  33. data/ext/java/nokogiri/XmlSaxPushParser.java +288 -0
  34. data/ext/java/nokogiri/XmlSchema.java +423 -0
  35. data/ext/java/nokogiri/XmlSyntaxError.java +137 -0
  36. data/ext/java/nokogiri/XmlText.java +90 -0
  37. data/ext/java/nokogiri/XmlXpathContext.java +305 -0
  38. data/ext/java/nokogiri/XsltStylesheet.java +368 -0
  39. data/ext/java/nokogiri/internals/ClosedStreamException.java +13 -0
  40. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +252 -0
  41. data/ext/java/nokogiri/internals/IgnoreSchemaErrorsErrorHandler.java +27 -0
  42. data/ext/java/nokogiri/internals/NokogiriBlockingQueueInputStream.java +178 -0
  43. data/ext/java/nokogiri/internals/NokogiriDomParser.java +99 -0
  44. data/ext/java/nokogiri/internals/NokogiriEntityResolver.java +140 -0
  45. data/ext/java/nokogiri/internals/NokogiriErrorHandler.java +65 -0
  46. data/ext/java/nokogiri/internals/NokogiriHandler.java +339 -0
  47. data/ext/java/nokogiri/internals/NokogiriHelpers.java +817 -0
  48. data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +228 -0
  49. data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +110 -0
  50. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java +86 -0
  51. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java +107 -0
  52. data/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java +62 -0
  53. data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +165 -0
  54. data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +50 -0
  55. data/ext/java/nokogiri/internals/NokogiriXPathVariableResolver.java +37 -0
  56. data/ext/java/nokogiri/internals/NokogiriXsltErrorListener.java +70 -0
  57. data/ext/java/nokogiri/internals/ParserContext.java +262 -0
  58. data/ext/java/nokogiri/internals/ReaderNode.java +564 -0
  59. data/ext/java/nokogiri/internals/SaveContextVisitor.java +865 -0
  60. data/ext/java/nokogiri/internals/SchemaErrorHandler.java +50 -0
  61. data/ext/java/nokogiri/internals/XalanDTMManagerPatch.java +174 -0
  62. data/ext/java/nokogiri/internals/XmlDeclHandler.java +11 -0
  63. data/ext/java/nokogiri/internals/XmlDomParserContext.java +265 -0
  64. data/ext/java/nokogiri/internals/XmlSaxParser.java +40 -0
  65. data/ext/java/nokogiri/internals/c14n/AttrCompare.java +122 -0
  66. data/ext/java/nokogiri/internals/c14n/C14nHelper.java +178 -0
  67. data/ext/java/nokogiri/internals/c14n/CanonicalFilter.java +43 -0
  68. data/ext/java/nokogiri/internals/c14n/CanonicalizationException.java +106 -0
  69. data/ext/java/nokogiri/internals/c14n/Canonicalizer.java +278 -0
  70. data/ext/java/nokogiri/internals/c14n/Canonicalizer11.java +664 -0
  71. data/ext/java/nokogiri/internals/c14n/Canonicalizer11_OmitComments.java +45 -0
  72. data/ext/java/nokogiri/internals/c14n/Canonicalizer11_WithComments.java +45 -0
  73. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315.java +388 -0
  74. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315Excl.java +308 -0
  75. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclOmitComments.java +47 -0
  76. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315ExclWithComments.java +51 -0
  77. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315OmitComments.java +51 -0
  78. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315WithComments.java +50 -0
  79. data/ext/java/nokogiri/internals/c14n/CanonicalizerBase.java +660 -0
  80. data/ext/java/nokogiri/internals/c14n/CanonicalizerPhysical.java +194 -0
  81. data/ext/java/nokogiri/internals/c14n/CanonicalizerSpi.java +77 -0
  82. data/ext/java/nokogiri/internals/c14n/Constants.java +45 -0
  83. data/ext/java/nokogiri/internals/c14n/ElementProxy.java +325 -0
  84. data/ext/java/nokogiri/internals/c14n/HelperNodeList.java +106 -0
  85. data/ext/java/nokogiri/internals/c14n/IgnoreAllErrorHandler.java +86 -0
  86. data/ext/java/nokogiri/internals/c14n/InclusiveNamespaces.java +181 -0
  87. data/ext/java/nokogiri/internals/c14n/InvalidCanonicalizerException.java +87 -0
  88. data/ext/java/nokogiri/internals/c14n/NameSpaceSymbTable.java +452 -0
  89. data/ext/java/nokogiri/internals/c14n/NodeFilter.java +52 -0
  90. data/ext/java/nokogiri/internals/c14n/UtfHelpper.java +190 -0
  91. data/ext/java/nokogiri/internals/c14n/XMLUtils.java +540 -0
  92. data/ext/java/nokogiri/internals/dom2dtm/DOM2DTM.java +1712 -0
  93. data/ext/java/nokogiri/internals/dom2dtm/DOM2DTMdefaultNamespaceDeclarationNode.java +737 -0
  94. data/ext/nokogiri/depend +38 -0
  95. data/ext/nokogiri/extconf.rb +1086 -0
  96. data/ext/nokogiri/gumbo.c +594 -0
  97. data/ext/nokogiri/html4_document.c +167 -0
  98. data/ext/nokogiri/html4_element_description.c +294 -0
  99. data/ext/nokogiri/html4_entity_lookup.c +37 -0
  100. data/ext/nokogiri/html4_sax_parser_context.c +116 -0
  101. data/ext/nokogiri/html4_sax_push_parser.c +95 -0
  102. data/ext/nokogiri/libxml2_backwards_compat.c +121 -0
  103. data/ext/nokogiri/nokogiri.c +265 -0
  104. data/ext/nokogiri/nokogiri.h +235 -0
  105. data/ext/nokogiri/test_global_handlers.c +42 -0
  106. data/ext/nokogiri/xml_attr.c +103 -0
  107. data/ext/nokogiri/xml_attribute_decl.c +70 -0
  108. data/ext/nokogiri/xml_cdata.c +57 -0
  109. data/ext/nokogiri/xml_comment.c +62 -0
  110. data/ext/nokogiri/xml_document.c +689 -0
  111. data/ext/nokogiri/xml_document_fragment.c +44 -0
  112. data/ext/nokogiri/xml_dtd.c +210 -0
  113. data/ext/nokogiri/xml_element_content.c +128 -0
  114. data/ext/nokogiri/xml_element_decl.c +69 -0
  115. data/ext/nokogiri/xml_encoding_handler.c +104 -0
  116. data/ext/nokogiri/xml_entity_decl.c +112 -0
  117. data/ext/nokogiri/xml_entity_reference.c +50 -0
  118. data/ext/nokogiri/xml_namespace.c +186 -0
  119. data/ext/nokogiri/xml_node.c +2426 -0
  120. data/ext/nokogiri/xml_node_set.c +496 -0
  121. data/ext/nokogiri/xml_processing_instruction.c +54 -0
  122. data/ext/nokogiri/xml_reader.c +794 -0
  123. data/ext/nokogiri/xml_relax_ng.c +164 -0
  124. data/ext/nokogiri/xml_sax_parser.c +316 -0
  125. data/ext/nokogiri/xml_sax_parser_context.c +283 -0
  126. data/ext/nokogiri/xml_sax_push_parser.c +166 -0
  127. data/ext/nokogiri/xml_schema.c +260 -0
  128. data/ext/nokogiri/xml_syntax_error.c +85 -0
  129. data/ext/nokogiri/xml_text.c +48 -0
  130. data/ext/nokogiri/xml_xpath_context.c +415 -0
  131. data/ext/nokogiri/xslt_stylesheet.c +363 -0
  132. data/gumbo-parser/CHANGES.md +63 -0
  133. data/gumbo-parser/Makefile +111 -0
  134. data/gumbo-parser/THANKS +27 -0
  135. data/gumbo-parser/src/Makefile +34 -0
  136. data/gumbo-parser/src/README.md +41 -0
  137. data/gumbo-parser/src/ascii.c +75 -0
  138. data/gumbo-parser/src/ascii.h +115 -0
  139. data/gumbo-parser/src/attribute.c +42 -0
  140. data/gumbo-parser/src/attribute.h +17 -0
  141. data/gumbo-parser/src/char_ref.c +22225 -0
  142. data/gumbo-parser/src/char_ref.h +29 -0
  143. data/gumbo-parser/src/char_ref.rl +2154 -0
  144. data/gumbo-parser/src/error.c +626 -0
  145. data/gumbo-parser/src/error.h +148 -0
  146. data/gumbo-parser/src/foreign_attrs.c +104 -0
  147. data/gumbo-parser/src/foreign_attrs.gperf +27 -0
  148. data/gumbo-parser/src/insertion_mode.h +33 -0
  149. data/gumbo-parser/src/macros.h +91 -0
  150. data/gumbo-parser/src/nokogiri_gumbo.h +944 -0
  151. data/gumbo-parser/src/parser.c +4878 -0
  152. data/gumbo-parser/src/parser.h +41 -0
  153. data/gumbo-parser/src/replacement.h +33 -0
  154. data/gumbo-parser/src/string_buffer.c +103 -0
  155. data/gumbo-parser/src/string_buffer.h +68 -0
  156. data/gumbo-parser/src/string_piece.c +48 -0
  157. data/gumbo-parser/src/svg_attrs.c +174 -0
  158. data/gumbo-parser/src/svg_attrs.gperf +77 -0
  159. data/gumbo-parser/src/svg_tags.c +137 -0
  160. data/gumbo-parser/src/svg_tags.gperf +55 -0
  161. data/gumbo-parser/src/tag.c +223 -0
  162. data/gumbo-parser/src/tag_lookup.c +382 -0
  163. data/gumbo-parser/src/tag_lookup.gperf +170 -0
  164. data/gumbo-parser/src/tag_lookup.h +13 -0
  165. data/gumbo-parser/src/token_buffer.c +79 -0
  166. data/gumbo-parser/src/token_buffer.h +71 -0
  167. data/gumbo-parser/src/token_type.h +17 -0
  168. data/gumbo-parser/src/tokenizer.c +3463 -0
  169. data/gumbo-parser/src/tokenizer.h +112 -0
  170. data/gumbo-parser/src/tokenizer_states.h +339 -0
  171. data/gumbo-parser/src/utf8.c +245 -0
  172. data/gumbo-parser/src/utf8.h +164 -0
  173. data/gumbo-parser/src/util.c +66 -0
  174. data/gumbo-parser/src/util.h +34 -0
  175. data/gumbo-parser/src/vector.c +111 -0
  176. data/gumbo-parser/src/vector.h +45 -0
  177. data/lib/nokogiri/class_resolver.rb +67 -0
  178. data/lib/nokogiri/css/node.rb +54 -0
  179. data/lib/nokogiri/css/parser.rb +770 -0
  180. data/lib/nokogiri/css/parser.y +277 -0
  181. data/lib/nokogiri/css/parser_extras.rb +96 -0
  182. data/lib/nokogiri/css/syntax_error.rb +9 -0
  183. data/lib/nokogiri/css/tokenizer.rb +155 -0
  184. data/lib/nokogiri/css/tokenizer.rex +56 -0
  185. data/lib/nokogiri/css/xpath_visitor.rb +359 -0
  186. data/lib/nokogiri/css.rb +66 -0
  187. data/lib/nokogiri/decorators/slop.rb +44 -0
  188. data/lib/nokogiri/encoding_handler.rb +57 -0
  189. data/lib/nokogiri/extension.rb +32 -0
  190. data/lib/nokogiri/gumbo.rb +15 -0
  191. data/lib/nokogiri/html.rb +48 -0
  192. data/lib/nokogiri/html4/builder.rb +37 -0
  193. data/lib/nokogiri/html4/document.rb +214 -0
  194. data/lib/nokogiri/html4/document_fragment.rb +54 -0
  195. data/lib/nokogiri/html4/element_description.rb +25 -0
  196. data/lib/nokogiri/html4/element_description_defaults.rb +572 -0
  197. data/lib/nokogiri/html4/encoding_reader.rb +121 -0
  198. data/lib/nokogiri/html4/entity_lookup.rb +15 -0
  199. data/lib/nokogiri/html4/sax/parser.rb +63 -0
  200. data/lib/nokogiri/html4/sax/parser_context.rb +20 -0
  201. data/lib/nokogiri/html4/sax/push_parser.rb +37 -0
  202. data/lib/nokogiri/html4.rb +47 -0
  203. data/lib/nokogiri/html5/document.rb +168 -0
  204. data/lib/nokogiri/html5/document_fragment.rb +90 -0
  205. data/lib/nokogiri/html5/node.rb +98 -0
  206. data/lib/nokogiri/html5.rb +389 -0
  207. data/lib/nokogiri/jruby/dependencies.rb +3 -0
  208. data/lib/nokogiri/jruby/isorelax/isorelax/20030108/isorelax-20030108.jar +0 -0
  209. data/lib/nokogiri/jruby/net/sf/saxon/Saxon-HE/9.6.0-4/Saxon-HE-9.6.0-4.jar +0 -0
  210. data/lib/nokogiri/jruby/net/sourceforge/htmlunit/neko-htmlunit/2.63.0/neko-htmlunit-2.63.0.jar +0 -0
  211. data/lib/nokogiri/jruby/nokogiri_jars.rb +43 -0
  212. data/lib/nokogiri/jruby/nu/validator/jing/20200702VNU/jing-20200702VNU.jar +0 -0
  213. data/lib/nokogiri/jruby/org/nokogiri/nekodtd/0.1.11.noko2/nekodtd-0.1.11.noko2.jar +0 -0
  214. data/lib/nokogiri/jruby/xalan/serializer/2.7.3/serializer-2.7.3.jar +0 -0
  215. data/lib/nokogiri/jruby/xalan/xalan/2.7.3/xalan-2.7.3.jar +0 -0
  216. data/lib/nokogiri/jruby/xerces/xercesImpl/2.12.2/xercesImpl-2.12.2.jar +0 -0
  217. data/lib/nokogiri/jruby/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar +0 -0
  218. data/lib/nokogiri/syntax_error.rb +6 -0
  219. data/lib/nokogiri/version/constant.rb +6 -0
  220. data/lib/nokogiri/version/info.rb +223 -0
  221. data/lib/nokogiri/version.rb +4 -0
  222. data/lib/nokogiri/xml/attr.rb +66 -0
  223. data/lib/nokogiri/xml/attribute_decl.rb +20 -0
  224. data/lib/nokogiri/xml/builder.rb +487 -0
  225. data/lib/nokogiri/xml/cdata.rb +13 -0
  226. data/lib/nokogiri/xml/character_data.rb +9 -0
  227. data/lib/nokogiri/xml/document.rb +471 -0
  228. data/lib/nokogiri/xml/document_fragment.rb +205 -0
  229. data/lib/nokogiri/xml/dtd.rb +34 -0
  230. data/lib/nokogiri/xml/element_content.rb +38 -0
  231. data/lib/nokogiri/xml/element_decl.rb +15 -0
  232. data/lib/nokogiri/xml/entity_decl.rb +21 -0
  233. data/lib/nokogiri/xml/entity_reference.rb +20 -0
  234. data/lib/nokogiri/xml/namespace.rb +58 -0
  235. data/lib/nokogiri/xml/node/save_options.rb +68 -0
  236. data/lib/nokogiri/xml/node.rb +1563 -0
  237. data/lib/nokogiri/xml/node_set.rb +447 -0
  238. data/lib/nokogiri/xml/notation.rb +19 -0
  239. data/lib/nokogiri/xml/parse_options.rb +213 -0
  240. data/lib/nokogiri/xml/pp/character_data.rb +21 -0
  241. data/lib/nokogiri/xml/pp/node.rb +57 -0
  242. data/lib/nokogiri/xml/pp.rb +4 -0
  243. data/lib/nokogiri/xml/processing_instruction.rb +11 -0
  244. data/lib/nokogiri/xml/reader.rb +105 -0
  245. data/lib/nokogiri/xml/relax_ng.rb +38 -0
  246. data/lib/nokogiri/xml/sax/document.rb +167 -0
  247. data/lib/nokogiri/xml/sax/parser.rb +125 -0
  248. data/lib/nokogiri/xml/sax/parser_context.rb +21 -0
  249. data/lib/nokogiri/xml/sax/push_parser.rb +61 -0
  250. data/lib/nokogiri/xml/sax.rb +6 -0
  251. data/lib/nokogiri/xml/schema.rb +73 -0
  252. data/lib/nokogiri/xml/searchable.rb +270 -0
  253. data/lib/nokogiri/xml/syntax_error.rb +72 -0
  254. data/lib/nokogiri/xml/text.rb +11 -0
  255. data/lib/nokogiri/xml/xpath/syntax_error.rb +13 -0
  256. data/lib/nokogiri/xml/xpath.rb +21 -0
  257. data/lib/nokogiri/xml/xpath_context.rb +16 -0
  258. data/lib/nokogiri/xml.rb +76 -0
  259. data/lib/nokogiri/xslt/stylesheet.rb +27 -0
  260. data/lib/nokogiri/xslt.rb +65 -0
  261. data/lib/nokogiri.rb +120 -0
  262. data/lib/xsd/xmlparser/nokogiri.rb +106 -0
  263. metadata +391 -0
@@ -0,0 +1,1563 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "stringio"
5
+
6
+ module Nokogiri
7
+ module XML
8
+ # Nokogiri::XML::Node is the primary API you'll use to interact with your Document.
9
+ #
10
+ # == Attributes
11
+ #
12
+ # A Nokogiri::XML::Node may be treated similarly to a hash with regard to attributes. For
13
+ # example:
14
+ #
15
+ # node = Nokogiri::XML::DocumentFragment.parse("<a href='#foo' id='link'>link</a>").at_css("a")
16
+ # node.to_html # => "<a href=\"#foo\" id=\"link\">link</a>"
17
+ # node['href'] # => "#foo"
18
+ # node.keys # => ["href", "id"]
19
+ # node.values # => ["#foo", "link"]
20
+ # node['class'] = 'green' # => "green"
21
+ # node.to_html # => "<a href=\"#foo\" id=\"link\" class=\"green\">link</a>"
22
+ #
23
+ # See the method group entitled Node@Working+With+Node+Attributes for the full set of methods.
24
+ #
25
+ # == Navigation
26
+ #
27
+ # Nokogiri::XML::Node also has methods that let you move around your tree:
28
+ #
29
+ # [#parent, #children, #next, #previous]
30
+ # Navigate up, down, or through siblings.
31
+ #
32
+ # See the method group entitled Node@Traversing+Document+Structure for the full set of methods.
33
+ #
34
+ # == Serialization
35
+ #
36
+ # When printing or otherwise emitting a document or a node (and its subtree), there are a few
37
+ # methods you might want to use:
38
+ #
39
+ # [#content, #text, #inner_text, #to_str]
40
+ # These methods will all **emit plaintext**,
41
+ # meaning that entities will be replaced (e.g., +&lt;+ will be replaced with +<+), meaning
42
+ # that any sanitizing will likely be un-done in the output.
43
+ #
44
+ # [#to_s, #to_xml, #to_html, #inner_html]
45
+ # These methods will all **emit properly-escaped markup**, meaning that it's suitable for
46
+ # consumption by browsers, parsers, etc.
47
+ #
48
+ # See the method group entitled Node@Serialization+and+Generating+Output for the full set of methods.
49
+ #
50
+ # == Searching
51
+ #
52
+ # You may search this node's subtree using methods like #xpath and #css.
53
+ #
54
+ # See the method group entitled Node@Searching+via+XPath+or+CSS+Queries for the full set of methods.
55
+ #
56
+ class Node
57
+ include Nokogiri::XML::PP::Node
58
+ include Nokogiri::XML::Searchable
59
+ include Nokogiri::ClassResolver
60
+ include Enumerable
61
+
62
+ # Element node type, see Nokogiri::XML::Node#element?
63
+ ELEMENT_NODE = 1
64
+ # Attribute node type
65
+ ATTRIBUTE_NODE = 2
66
+ # Text node type, see Nokogiri::XML::Node#text?
67
+ TEXT_NODE = 3
68
+ # CDATA node type, see Nokogiri::XML::Node#cdata?
69
+ CDATA_SECTION_NODE = 4
70
+ # Entity reference node type
71
+ ENTITY_REF_NODE = 5
72
+ # Entity node type
73
+ ENTITY_NODE = 6
74
+ # PI node type
75
+ PI_NODE = 7
76
+ # Comment node type, see Nokogiri::XML::Node#comment?
77
+ COMMENT_NODE = 8
78
+ # Document node type, see Nokogiri::XML::Node#xml?
79
+ DOCUMENT_NODE = 9
80
+ # Document type node type
81
+ DOCUMENT_TYPE_NODE = 10
82
+ # Document fragment node type
83
+ DOCUMENT_FRAG_NODE = 11
84
+ # Notation node type
85
+ NOTATION_NODE = 12
86
+ # HTML document node type, see Nokogiri::XML::Node#html?
87
+ HTML_DOCUMENT_NODE = 13
88
+ # DTD node type
89
+ DTD_NODE = 14
90
+ # Element declaration type
91
+ ELEMENT_DECL = 15
92
+ # Attribute declaration type
93
+ ATTRIBUTE_DECL = 16
94
+ # Entity declaration type
95
+ ENTITY_DECL = 17
96
+ # Namespace declaration type
97
+ NAMESPACE_DECL = 18
98
+ # XInclude start type
99
+ XINCLUDE_START = 19
100
+ # XInclude end type
101
+ XINCLUDE_END = 20
102
+ # DOCB document node type
103
+ DOCB_DOCUMENT_NODE = 21
104
+
105
+ #
106
+ # :call-seq:
107
+ # new(name, document) -> Nokogiri::XML::Node
108
+ # new(name, document) { |node| ... } -> Nokogiri::XML::Node
109
+ #
110
+ # Create a new node with +name+ that belongs to +document+.
111
+ #
112
+ # If you intend to add a node to a document tree, it's likely that you will prefer one of the
113
+ # Nokogiri::XML::Node methods like #add_child, #add_next_sibling, #replace, etc. which will
114
+ # both create an element (or subtree) and place it in the document tree.
115
+ #
116
+ # Another alternative, if you are concerned about performance, is
117
+ # Nokogiri::XML::Document#create_element which accepts additional arguments for contents or
118
+ # attributes but (like this method) avoids parsing markup.
119
+ #
120
+ # [Parameters]
121
+ # - +name+ (String)
122
+ # - +document+ (Nokogiri::XML::Document) The document to which the the returned node will belong.
123
+ # [Yields] Nokogiri::XML::Node
124
+ # [Returns] Nokogiri::XML::Node
125
+ #
126
+ def initialize(name, document)
127
+ # This is intentionally empty, and sets the method signature for subclasses.
128
+ end
129
+
130
+ ###
131
+ # Decorate this node with the decorators set up in this node's Document
132
+ def decorate!
133
+ document.decorate(self)
134
+ end
135
+
136
+ # :section: Manipulating Document Structure
137
+
138
+ ###
139
+ # Add +node_or_tags+ as a child of this Node.
140
+ #
141
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
142
+ # containing markup.
143
+ #
144
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is
145
+ # a DocumentFragment, NodeSet, or String).
146
+ #
147
+ # Also see related method +<<+.
148
+ def add_child(node_or_tags)
149
+ node_or_tags = coerce(node_or_tags)
150
+ if node_or_tags.is_a?(XML::NodeSet)
151
+ node_or_tags.each { |n| add_child_node_and_reparent_attrs(n) }
152
+ else
153
+ add_child_node_and_reparent_attrs(node_or_tags)
154
+ end
155
+ node_or_tags
156
+ end
157
+
158
+ ###
159
+ # Add +node_or_tags+ as the first child of this Node.
160
+ #
161
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
162
+ # containing markup.
163
+ #
164
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is
165
+ # a DocumentFragment, NodeSet, or String).
166
+ #
167
+ # Also see related method +add_child+.
168
+ def prepend_child(node_or_tags)
169
+ if (first = children.first)
170
+ # Mimic the error add_child would raise.
171
+ raise "Document already has a root node" if document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
172
+
173
+ first.__send__(:add_sibling, :previous, node_or_tags)
174
+ else
175
+ add_child(node_or_tags)
176
+ end
177
+ end
178
+
179
+ # :call-seq:
180
+ # wrap(markup) -> self
181
+ # wrap(node) -> self
182
+ #
183
+ # Wrap this Node with the node parsed from +markup+ or a dup of the +node+.
184
+ #
185
+ # [Parameters]
186
+ # - *markup* (String)
187
+ # Markup that is parsed and used as the wrapper. This node's parent, if it exists, is used
188
+ # as the context node for parsing; otherwise the associated document is used. If the parsed
189
+ # fragment has multiple roots, the first root node is used as the wrapper.
190
+ # - *node* (Nokogiri::XML::Node)
191
+ # An element that is `#dup`ed and used as the wrapper.
192
+ #
193
+ # [Returns] +self+, to support chaining.
194
+ #
195
+ # Also see NodeSet#wrap
196
+ #
197
+ # *Example* with a +String+ argument:
198
+ #
199
+ # doc = Nokogiri::HTML5(<<~HTML)
200
+ # <html><body>
201
+ # <a>asdf</a>
202
+ # </body></html>
203
+ # HTML
204
+ # doc.at_css("a").wrap("<div></div>")
205
+ # doc.to_html
206
+ # # => <html><head></head><body>
207
+ # # <div><a>asdf</a></div>
208
+ # # </body></html>
209
+ #
210
+ # *Example* with a +Node+ argument:
211
+ #
212
+ # doc = Nokogiri::HTML5(<<~HTML)
213
+ # <html><body>
214
+ # <a>asdf</a>
215
+ # </body></html>
216
+ # HTML
217
+ # doc.at_css("a").wrap(doc.create_element("div"))
218
+ # doc.to_html
219
+ # # <html><head></head><body>
220
+ # # <div><a>asdf</a></div>
221
+ # # </body></html>
222
+ #
223
+ def wrap(node_or_tags)
224
+ case node_or_tags
225
+ when String
226
+ context_node = parent || document
227
+ new_parent = context_node.coerce(node_or_tags).first
228
+ if new_parent.nil?
229
+ raise "Failed to parse '#{node_or_tags}' in the context of a '#{context_node.name}' element"
230
+ end
231
+ when XML::Node
232
+ new_parent = node_or_tags.dup
233
+ else
234
+ raise ArgumentError, "Requires a String or Node argument, and cannot accept a #{node_or_tags.class}"
235
+ end
236
+
237
+ if parent
238
+ add_next_sibling(new_parent)
239
+ else
240
+ new_parent.unlink
241
+ end
242
+ new_parent.add_child(self)
243
+
244
+ self
245
+ end
246
+
247
+ ###
248
+ # Add +node_or_tags+ as a child of this Node.
249
+ #
250
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
251
+ # containing markup.
252
+ #
253
+ # Returns +self+, to support chaining of calls (e.g., root << child1 << child2)
254
+ #
255
+ # Also see related method +add_child+.
256
+ def <<(node_or_tags)
257
+ add_child(node_or_tags)
258
+ self
259
+ end
260
+
261
+ ###
262
+ # Insert +node_or_tags+ before this Node (as a sibling).
263
+ #
264
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
265
+ # containing markup.
266
+ #
267
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is
268
+ # a DocumentFragment, NodeSet, or String).
269
+ #
270
+ # Also see related method +before+.
271
+ def add_previous_sibling(node_or_tags)
272
+ raise ArgumentError,
273
+ "A document may not have multiple root nodes." if parent&.document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
274
+
275
+ add_sibling(:previous, node_or_tags)
276
+ end
277
+
278
+ ###
279
+ # Insert +node_or_tags+ after this Node (as a sibling).
280
+ #
281
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
282
+ # containing markup.
283
+ #
284
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is
285
+ # a DocumentFragment, NodeSet, or String).
286
+ #
287
+ # Also see related method +after+.
288
+ def add_next_sibling(node_or_tags)
289
+ raise ArgumentError,
290
+ "A document may not have multiple root nodes." if parent&.document? && !(node_or_tags.comment? || node_or_tags.processing_instruction?)
291
+
292
+ add_sibling(:next, node_or_tags)
293
+ end
294
+
295
+ ####
296
+ # Insert +node_or_tags+ before this node (as a sibling).
297
+ #
298
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
299
+ # containing markup.
300
+ #
301
+ # Returns +self+, to support chaining of calls.
302
+ #
303
+ # Also see related method +add_previous_sibling+.
304
+ def before(node_or_tags)
305
+ add_previous_sibling(node_or_tags)
306
+ self
307
+ end
308
+
309
+ ####
310
+ # Insert +node_or_tags+ after this node (as a sibling).
311
+ #
312
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a String
313
+ # containing markup.
314
+ #
315
+ # Returns +self+, to support chaining of calls.
316
+ #
317
+ # Also see related method +add_next_sibling+.
318
+ def after(node_or_tags)
319
+ add_next_sibling(node_or_tags)
320
+ self
321
+ end
322
+
323
+ ####
324
+ # Set the content for this Node to +node_or_tags+.
325
+ #
326
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a String
327
+ # containing markup.
328
+ #
329
+ # ⚠ Please note that despite the name, this method will *not* always parse a String argument
330
+ # as HTML. A String argument will be parsed with the +DocumentFragment+ parser related to this
331
+ # node's document.
332
+ #
333
+ # For example, if the document is an HTML4::Document then the string will be parsed as HTML4
334
+ # using HTML4::DocumentFragment; but if the document is an XML::Document then it will
335
+ # parse the string as XML using XML::DocumentFragment.
336
+ #
337
+ # Also see related method +children=+
338
+ def inner_html=(node_or_tags)
339
+ self.children = node_or_tags
340
+ end
341
+
342
+ ####
343
+ # Set the content for this Node +node_or_tags+
344
+ #
345
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a String
346
+ # containing markup.
347
+ #
348
+ # Also see related method +inner_html=+
349
+ def children=(node_or_tags)
350
+ node_or_tags = coerce(node_or_tags)
351
+ children.unlink
352
+ if node_or_tags.is_a?(XML::NodeSet)
353
+ node_or_tags.each { |n| add_child_node_and_reparent_attrs(n) }
354
+ else
355
+ add_child_node_and_reparent_attrs(node_or_tags)
356
+ end
357
+ end
358
+
359
+ ####
360
+ # Replace this Node with +node_or_tags+.
361
+ #
362
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
363
+ # containing markup.
364
+ #
365
+ # Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is
366
+ # a DocumentFragment, NodeSet, or String).
367
+ #
368
+ # Also see related method +swap+.
369
+ def replace(node_or_tags)
370
+ raise("Cannot replace a node with no parent") unless parent
371
+
372
+ # We cannot replace a text node directly, otherwise libxml will return
373
+ # an internal error at parser.c:13031, I don't know exactly why
374
+ # libxml is trying to find a parent node that is an element or document
375
+ # so I can't tell if this is bug in libxml or not. issue #775.
376
+ if text?
377
+ replacee = Nokogiri::XML::Node.new("dummy", document)
378
+ add_previous_sibling_node(replacee)
379
+ unlink
380
+ return replacee.replace(node_or_tags)
381
+ end
382
+
383
+ node_or_tags = parent.coerce(node_or_tags)
384
+
385
+ if node_or_tags.is_a?(XML::NodeSet)
386
+ node_or_tags.each { |n| add_previous_sibling(n) }
387
+ unlink
388
+ else
389
+ replace_node(node_or_tags)
390
+ end
391
+ node_or_tags
392
+ end
393
+
394
+ ####
395
+ # Swap this Node for +node_or_tags+
396
+ #
397
+ # +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a String
398
+ # Containing markup.
399
+ #
400
+ # Returns self, to support chaining of calls.
401
+ #
402
+ # Also see related method +replace+.
403
+ def swap(node_or_tags)
404
+ replace(node_or_tags)
405
+ self
406
+ end
407
+
408
+ ####
409
+ # Set the Node's content to a Text node containing +string+. The string gets XML escaped, not
410
+ # interpreted as markup.
411
+ def content=(string)
412
+ self.native_content = encode_special_chars(string.to_s)
413
+ end
414
+
415
+ ###
416
+ # Set the parent Node for this Node
417
+ def parent=(parent_node)
418
+ parent_node.add_child(self)
419
+ end
420
+
421
+ ###
422
+ # Adds a default namespace supplied as a string +url+ href, to self.
423
+ # The consequence is as an xmlns attribute with supplied argument were
424
+ # present in parsed XML. A default namespace set with this method will
425
+ # now show up in #attributes, but when this node is serialized to XML an
426
+ # "xmlns" attribute will appear. See also #namespace and #namespace=
427
+ def default_namespace=(url)
428
+ add_namespace_definition(nil, url)
429
+ end
430
+
431
+ ###
432
+ # Set the default namespace on this node (as would be defined with an
433
+ # "xmlns=" attribute in XML source), as a Namespace object +ns+. Note that
434
+ # a Namespace added this way will NOT be serialized as an xmlns attribute
435
+ # for this node. You probably want #default_namespace= instead, or perhaps
436
+ # #add_namespace_definition with a nil prefix argument.
437
+ def namespace=(ns)
438
+ return set_namespace(ns) unless ns
439
+
440
+ unless Nokogiri::XML::Namespace === ns
441
+ raise TypeError, "#{ns.class} can't be coerced into Nokogiri::XML::Namespace"
442
+ end
443
+ if ns.document != document
444
+ raise ArgumentError, "namespace must be declared on the same document"
445
+ end
446
+
447
+ set_namespace(ns)
448
+ end
449
+
450
+ ###
451
+ # Do xinclude substitution on the subtree below node. If given a block, a
452
+ # Nokogiri::XML::ParseOptions object initialized from +options+, will be
453
+ # passed to it, allowing more convenient modification of the parser options.
454
+ def do_xinclude(options = XML::ParseOptions::DEFAULT_XML)
455
+ options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
456
+ yield options if block_given?
457
+
458
+ # call c extension
459
+ process_xincludes(options.to_i)
460
+ end
461
+
462
+ alias_method :next, :next_sibling
463
+ alias_method :previous, :previous_sibling
464
+ alias_method :next=, :add_next_sibling
465
+ alias_method :previous=, :add_previous_sibling
466
+ alias_method :remove, :unlink
467
+ alias_method :name=, :node_name=
468
+ alias_method :add_namespace, :add_namespace_definition
469
+
470
+ # :section:
471
+
472
+ alias_method :inner_text, :content
473
+ alias_method :text, :content
474
+ alias_method :to_str, :content
475
+ alias_method :name, :node_name
476
+ alias_method :type, :node_type
477
+ alias_method :clone, :dup
478
+ alias_method :elements, :element_children
479
+
480
+ # :section: Working With Node Attributes
481
+
482
+ # :call-seq: [](name) → (String, nil)
483
+ #
484
+ # Fetch an attribute from this node.
485
+ #
486
+ # ⚠ Note that attributes with namespaces cannot be accessed with this method. To access
487
+ # namespaced attributes, use #attribute_with_ns.
488
+ #
489
+ # [Returns] (String, nil) value of the attribute +name+, or +nil+ if no matching attribute exists
490
+ #
491
+ # *Example*
492
+ #
493
+ # doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
494
+ # child = doc.at_css("child")
495
+ # child["size"] # => "large"
496
+ # child["class"] # => "big wide tall"
497
+ #
498
+ # *Example:* Namespaced attributes will not be returned.
499
+ #
500
+ # ⚠ Note namespaced attributes may be accessed with #attribute or #attribute_with_ns
501
+ #
502
+ # doc = Nokogiri::XML(<<~EOF)
503
+ # <root xmlns:width='http://example.com/widths'>
504
+ # <child width:size='broad'/>
505
+ # </root>
506
+ # EOF
507
+ # doc.at_css("child")["size"] # => nil
508
+ # doc.at_css("child").attribute("size").value # => "broad"
509
+ # doc.at_css("child").attribute_with_ns("size", "http://example.com/widths").value
510
+ # # => "broad"
511
+ #
512
+ def [](name)
513
+ get(name.to_s)
514
+ end
515
+
516
+ # :call-seq: []=(name, value) → value
517
+ #
518
+ # Update the attribute +name+ to +value+, or create the attribute if it does not exist.
519
+ #
520
+ # ⚠ Note that attributes with namespaces cannot be accessed with this method. To access
521
+ # namespaced attributes for update, use #attribute_with_ns. To add a namespaced attribute,
522
+ # see the example below.
523
+ #
524
+ # [Returns] +value+
525
+ #
526
+ # *Example*
527
+ #
528
+ # doc = Nokogiri::XML("<root><child/></root>")
529
+ # child = doc.at_css("child")
530
+ # child["size"] = "broad"
531
+ # child.to_html
532
+ # # => "<child size=\"broad\"></child>"
533
+ #
534
+ # *Example:* Add a namespaced attribute.
535
+ #
536
+ # doc = Nokogiri::XML(<<~EOF)
537
+ # <root xmlns:width='http://example.com/widths'>
538
+ # <child/>
539
+ # </root>
540
+ # EOF
541
+ # child = doc.at_css("child")
542
+ # child["size"] = "broad"
543
+ # ns = doc.root.namespace_definitions.find { |ns| ns.prefix == "width" }
544
+ # child.attribute("size").namespace = ns
545
+ # doc.to_html
546
+ # # => "<root xmlns:width=\"http://example.com/widths\">\n" +
547
+ # # " <child width:size=\"broad\"></child>\n" +
548
+ # # "</root>\n"
549
+ #
550
+ def []=(name, value)
551
+ set(name.to_s, value.to_s)
552
+ end
553
+
554
+ #
555
+ # :call-seq: attributes() → Hash<String ⇒ Nokogiri::XML::Attr>
556
+ #
557
+ # Fetch this node's attributes.
558
+ #
559
+ # ⚠ Because the keys do not include any namespace information for the attribute, in case of a
560
+ # simple name collision, not all attributes will be returned. In this case, you will need to
561
+ # use #attribute_nodes.
562
+ #
563
+ # [Returns]
564
+ # Hash containing attributes belonging to +self+. The hash keys are String attribute
565
+ # names (without the namespace), and the hash values are Nokogiri::XML::Attr.
566
+ #
567
+ # *Example* with no namespaces:
568
+ #
569
+ # doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
570
+ # doc.at_css("child").attributes
571
+ # # => {"size"=>#(Attr:0x550 { name = "size", value = "large" }),
572
+ # # "class"=>#(Attr:0x564 { name = "class", value = "big wide tall" })}
573
+ #
574
+ # *Example* with a namespace:
575
+ #
576
+ # doc = Nokogiri::XML("<root xmlns:desc='http://example.com/sizes'><child desc:size='large'/></root>")
577
+ # doc.at_css("child").attributes
578
+ # # => {"size"=>
579
+ # # #(Attr:0x550 {
580
+ # # name = "size",
581
+ # # namespace = #(Namespace:0x564 {
582
+ # # prefix = "desc",
583
+ # # href = "http://example.com/sizes"
584
+ # # }),
585
+ # # value = "large"
586
+ # # })}
587
+ #
588
+ # *Example* with an attribute name collision:
589
+ #
590
+ # ⚠ Note that only one of the attributes is returned in the Hash.
591
+ #
592
+ # doc = Nokogiri::XML(<<~EOF)
593
+ # <root xmlns:width='http://example.com/widths'
594
+ # xmlns:height='http://example.com/heights'>
595
+ # <child width:size='broad' height:size='tall'/>
596
+ # </root>
597
+ # EOF
598
+ # doc.at_css("child").attributes
599
+ # # => {"size"=>
600
+ # # #(Attr:0x550 {
601
+ # # name = "size",
602
+ # # namespace = #(Namespace:0x564 {
603
+ # # prefix = "height",
604
+ # # href = "http://example.com/heights"
605
+ # # }),
606
+ # # value = "tall"
607
+ # # })}
608
+ #
609
+ def attributes
610
+ attribute_nodes.each_with_object({}) do |node, hash|
611
+ hash[node.node_name] = node
612
+ end
613
+ end
614
+
615
+ ###
616
+ # Get the attribute values for this Node.
617
+ def values
618
+ attribute_nodes.map(&:value)
619
+ end
620
+
621
+ ###
622
+ # Does this Node's attributes include <value>
623
+ def value?(value)
624
+ values.include?(value)
625
+ end
626
+
627
+ ###
628
+ # Get the attribute names for this Node.
629
+ def keys
630
+ attribute_nodes.map(&:node_name)
631
+ end
632
+
633
+ ###
634
+ # Iterate over each attribute name and value pair for this Node.
635
+ def each
636
+ attribute_nodes.each do |node|
637
+ yield [node.node_name, node.value]
638
+ end
639
+ end
640
+
641
+ ###
642
+ # Remove the attribute named +name+
643
+ def remove_attribute(name)
644
+ attr = attributes[name].remove if key?(name)
645
+ clear_xpath_context if Nokogiri.jruby?
646
+ attr
647
+ end
648
+
649
+ #
650
+ # :call-seq: classes() → Array<String>
651
+ #
652
+ # Fetch CSS class names of a Node.
653
+ #
654
+ # This is a convenience function and is equivalent to:
655
+ #
656
+ # node.kwattr_values("class")
657
+ #
658
+ # See related: #kwattr_values, #add_class, #append_class, #remove_class
659
+ #
660
+ # [Returns]
661
+ # The CSS classes (Array of String) present in the Node's "class" attribute. If the
662
+ # attribute is empty or non-existent, the return value is an empty array.
663
+ #
664
+ # *Example*
665
+ #
666
+ # node # => <div class="section title header"></div>
667
+ # node.classes # => ["section", "title", "header"]
668
+ #
669
+ def classes
670
+ kwattr_values("class")
671
+ end
672
+
673
+ #
674
+ # :call-seq: add_class(names) → self
675
+ #
676
+ # Ensure HTML CSS classes are present on +self+. Any CSS classes in +names+ that already exist
677
+ # in the "class" attribute are _not_ added. Note that any existing duplicates in the
678
+ # "class" attribute are not removed. Compare with #append_class.
679
+ #
680
+ # This is a convenience function and is equivalent to:
681
+ #
682
+ # node.kwattr_add("class", names)
683
+ #
684
+ # See related: #kwattr_add, #classes, #append_class, #remove_class
685
+ #
686
+ # [Parameters]
687
+ # - +names+ (String, Array<String>)
688
+ #
689
+ # CSS class names to be added to the Node's "class" attribute. May be a string containing
690
+ # whitespace-delimited names, or an Array of String names. Any class names already present
691
+ # will not be added. Any class names not present will be added. If no "class" attribute
692
+ # exists, one is created.
693
+ #
694
+ # [Returns] +self+ (Node) for ease of chaining method calls.
695
+ #
696
+ # *Example:* Ensure that the node has CSS class "section"
697
+ #
698
+ # node # => <div></div>
699
+ # node.add_class("section") # => <div class="section"></div>
700
+ # node.add_class("section") # => <div class="section"></div> # duplicate not added
701
+ #
702
+ # *Example:* Ensure that the node has CSS classes "section" and "header", via a String argument
703
+ #
704
+ # Note that the CSS class "section" is not added because it is already present.
705
+ # Note also that the pre-existing duplicate CSS class "section" is not removed.
706
+ #
707
+ # node # => <div class="section section"></div>
708
+ # node.add_class("section header") # => <div class="section section header"></div>
709
+ #
710
+ # *Example:* Ensure that the node has CSS classes "section" and "header", via an Array argument
711
+ #
712
+ # node # => <div></div>
713
+ # node.add_class(["section", "header"]) # => <div class="section header"></div>
714
+ #
715
+ def add_class(names)
716
+ kwattr_add("class", names)
717
+ end
718
+
719
+ #
720
+ # :call-seq: append_class(names) → self
721
+ #
722
+ # Add HTML CSS classes to +self+, regardless of duplication. Compare with #add_class.
723
+ #
724
+ # This is a convenience function and is equivalent to:
725
+ #
726
+ # node.kwattr_append("class", names)
727
+ #
728
+ # See related: #kwattr_append, #classes, #add_class, #remove_class
729
+ #
730
+ # [Parameters]
731
+ # - +names+ (String, Array<String>)
732
+ #
733
+ # CSS class names to be appended to the Node's "class" attribute. May be a string containing
734
+ # whitespace-delimited names, or an Array of String names. All class names passed in will be
735
+ # appended to the "class" attribute even if they are already present in the attribute
736
+ # value. If no "class" attribute exists, one is created.
737
+ #
738
+ # [Returns] +self+ (Node) for ease of chaining method calls.
739
+ #
740
+ # *Example:* Append "section" to the node's CSS "class" attribute
741
+ #
742
+ # node # => <div></div>
743
+ # node.append_class("section") # => <div class="section"></div>
744
+ # node.append_class("section") # => <div class="section section"></div> # duplicate added!
745
+ #
746
+ # *Example:* Append "section" and "header" to the noded's CSS "class" attribute, via a String argument
747
+ #
748
+ # Note that the CSS class "section" is appended even though it is already present
749
+ #
750
+ # node # => <div class="section section"></div>
751
+ # node.append_class("section header") # => <div class="section section section header"></div>
752
+ #
753
+ # *Example:* Append "section" and "header" to the node's CSS "class" attribute, via an Array argument
754
+ #
755
+ # node # => <div></div>
756
+ # node.append_class(["section", "header"]) # => <div class="section header"></div>
757
+ # node.append_class(["section", "header"]) # => <div class="section header section header"></div>
758
+ #
759
+ def append_class(names)
760
+ kwattr_append("class", names)
761
+ end
762
+
763
+ # :call-seq:
764
+ # remove_class(css_classes) → self
765
+ #
766
+ # Remove HTML CSS classes from this node. Any CSS class names in +css_classes+ that exist in
767
+ # this node's "class" attribute are removed, including any multiple entries.
768
+ #
769
+ # If no CSS classes remain after this operation, or if +css_classes+ is +nil+, the "class"
770
+ # attribute is deleted from the node.
771
+ #
772
+ # This is a convenience function and is equivalent to:
773
+ #
774
+ # node.kwattr_remove("class", css_classes)
775
+ #
776
+ # Also see #kwattr_remove, #classes, #add_class, #append_class
777
+ #
778
+ # [Parameters]
779
+ # - +css_classes+ (String, Array<String>)
780
+ #
781
+ # CSS class names to be removed from the Node's
782
+ # "class" attribute. May be a string containing whitespace-delimited names, or an Array of
783
+ # String names. Any class names already present will be removed. If no CSS classes remain,
784
+ # the "class" attribute is deleted.
785
+ #
786
+ # [Returns] +self+ (Nokogiri::XML::Node) for ease of chaining method calls.
787
+ #
788
+ # *Example*: Deleting a CSS class
789
+ #
790
+ # Note that all instances of the class "section" are removed from the "class" attribute.
791
+ #
792
+ # node # => <div class="section header section"></div>
793
+ # node.remove_class("section") # => <div class="header"></div>
794
+ #
795
+ # *Example*: Deleting the only remaining CSS class
796
+ #
797
+ # Note that the attribute is removed once there are no remaining classes.
798
+ #
799
+ # node # => <div class="section"></div>
800
+ # node.remove_class("section") # => <div></div>
801
+ #
802
+ # *Example*: Deleting multiple CSS classes
803
+ #
804
+ # Note that the "class" attribute is deleted once it's empty.
805
+ #
806
+ # node # => <div class="section header float"></div>
807
+ # node.remove_class(["section", "float"]) # => <div class="header"></div>
808
+ #
809
+ def remove_class(names = nil)
810
+ kwattr_remove("class", names)
811
+ end
812
+
813
+ # :call-seq:
814
+ # kwattr_values(attribute_name) → Array<String>
815
+ #
816
+ # Fetch values from a keyword attribute of a Node.
817
+ #
818
+ # A "keyword attribute" is a node attribute that contains a set of space-delimited
819
+ # values. Perhaps the most familiar example of this is the HTML "class" attribute used to
820
+ # contain CSS classes. But other keyword attributes exist, for instance
821
+ # {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
822
+ #
823
+ # See also #classes, #kwattr_add, #kwattr_append, #kwattr_remove
824
+ #
825
+ # [Parameters]
826
+ # - +attribute_name+ (String) The name of the keyword attribute to be inspected.
827
+ #
828
+ # [Returns]
829
+ # (Array<String>) The values present in the Node's +attribute_name+ attribute. If the
830
+ # attribute is empty or non-existent, the return value is an empty array.
831
+ #
832
+ # *Example:*
833
+ #
834
+ # node # => <a rel="nofollow noopener external">link</a>
835
+ # node.kwattr_values("rel") # => ["nofollow", "noopener", "external"]
836
+ #
837
+ # Since v1.11.0
838
+ def kwattr_values(attribute_name)
839
+ keywordify(get_attribute(attribute_name) || [])
840
+ end
841
+
842
+ # :call-seq:
843
+ # kwattr_add(attribute_name, keywords) → self
844
+ #
845
+ # Ensure that values are present in a keyword attribute.
846
+ #
847
+ # Any values in +keywords+ that already exist in the Node's attribute values are _not_
848
+ # added. Note that any existing duplicates in the attribute values are not removed. Compare
849
+ # with #kwattr_append.
850
+ #
851
+ # A "keyword attribute" is a node attribute that contains a set of space-delimited
852
+ # values. Perhaps the most familiar example of this is the HTML "class" attribute used to
853
+ # contain CSS classes. But other keyword attributes exist, for instance
854
+ # {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
855
+ #
856
+ # See also #add_class, #kwattr_values, #kwattr_append, #kwattr_remove
857
+ #
858
+ # [Parameters]
859
+ # - +attribute_name+ (String) The name of the keyword attribute to be modified.
860
+ # - +keywords+ (String, Array<String>)
861
+ # Keywords to be added to the attribute named +attribute_name+. May be a string containing
862
+ # whitespace-delimited values, or an Array of String values. Any values already present will
863
+ # not be added. Any values not present will be added. If the named attribute does not exist,
864
+ # it is created.
865
+ #
866
+ # [Returns] +self+ (Nokogiri::XML::Node) for ease of chaining method calls.
867
+ #
868
+ # *Example:* Ensure that a +Node+ has "nofollow" in its +rel+ attribute.
869
+ #
870
+ # Note that duplicates are not added.
871
+ #
872
+ # node # => <a></a>
873
+ # node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a>
874
+ # node.kwattr_add("rel", "nofollow") # => <a rel="nofollow"></a>
875
+ #
876
+ # *Example:* Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via a
877
+ # String argument.
878
+ #
879
+ # Note that "nofollow" is not added because it is already present. Note also that the
880
+ # pre-existing duplicate "nofollow" is not removed.
881
+ #
882
+ # node # => <a rel="nofollow nofollow"></a>
883
+ # node.kwattr_add("rel", "nofollow noreferrer") # => <a rel="nofollow nofollow noreferrer"></a>
884
+ #
885
+ # *Example:* Ensure that a +Node+ has "nofollow" and "noreferrer" in its +rel+ attribute, via
886
+ # an Array argument.
887
+ #
888
+ # node # => <a></a>
889
+ # node.kwattr_add("rel", ["nofollow", "noreferrer"]) # => <a rel="nofollow noreferrer"></a>
890
+ #
891
+ # Since v1.11.0
892
+ def kwattr_add(attribute_name, keywords)
893
+ keywords = keywordify(keywords)
894
+ current_kws = kwattr_values(attribute_name)
895
+ new_kws = (current_kws + (keywords - current_kws)).join(" ")
896
+ set_attribute(attribute_name, new_kws)
897
+ self
898
+ end
899
+
900
+ # :call-seq:
901
+ # kwattr_append(attribute_name, keywords) → self
902
+ #
903
+ # Add keywords to a Node's keyword attribute, regardless of duplication. Compare with
904
+ # #kwattr_add.
905
+ #
906
+ # A "keyword attribute" is a node attribute that contains a set of space-delimited
907
+ # values. Perhaps the most familiar example of this is the HTML "class" attribute used to
908
+ # contain CSS classes. But other keyword attributes exist, for instance
909
+ # {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
910
+ #
911
+ # See also #append_class, #kwattr_values, #kwattr_add, #kwattr_remove
912
+ #
913
+ # [Parameters]
914
+ # - +attribute_name+ (String) The name of the keyword attribute to be modified.
915
+ # - +keywords+ (String, Array<String>)
916
+ # Keywords to be added to the attribute named +attribute_name+. May be a string containing
917
+ # whitespace-delimited values, or an Array of String values. All values passed in will be
918
+ # appended to the named attribute even if they are already present in the attribute. If the
919
+ # named attribute does not exist, it is created.
920
+ #
921
+ # [Returns] +self+ (Node) for ease of chaining method calls.
922
+ #
923
+ # *Example:* Append "nofollow" to the +rel+ attribute.
924
+ #
925
+ # Note that duplicates are added.
926
+ #
927
+ # node # => <a></a>
928
+ # node.kwattr_append("rel", "nofollow") # => <a rel="nofollow"></a>
929
+ # node.kwattr_append("rel", "nofollow") # => <a rel="nofollow nofollow"></a>
930
+ #
931
+ # *Example:* Append "nofollow" and "noreferrer" to the +rel+ attribute, via a String argument.
932
+ #
933
+ # Note that "nofollow" is appended even though it is already present.
934
+ #
935
+ # node # => <a rel="nofollow"></a>
936
+ # node.kwattr_append("rel", "nofollow noreferrer") # => <a rel="nofollow nofollow noreferrer"></a>
937
+ #
938
+ #
939
+ # *Example:* Append "nofollow" and "noreferrer" to the +rel+ attribute, via an Array argument.
940
+ #
941
+ # node # => <a></a>
942
+ # node.kwattr_append("rel", ["nofollow", "noreferrer"]) # => <a rel="nofollow noreferrer"></a>
943
+ #
944
+ # Since v1.11.0
945
+ def kwattr_append(attribute_name, keywords)
946
+ keywords = keywordify(keywords)
947
+ current_kws = kwattr_values(attribute_name)
948
+ new_kws = (current_kws + keywords).join(" ")
949
+ set_attribute(attribute_name, new_kws)
950
+ self
951
+ end
952
+
953
+ # :call-seq:
954
+ # kwattr_remove(attribute_name, keywords) → self
955
+ #
956
+ # Remove keywords from a keyword attribute. Any matching keywords that exist in the named
957
+ # attribute are removed, including any multiple entries.
958
+ #
959
+ # If no keywords remain after this operation, or if +keywords+ is +nil+, the attribute is
960
+ # deleted from the node.
961
+ #
962
+ # A "keyword attribute" is a node attribute that contains a set of space-delimited
963
+ # values. Perhaps the most familiar example of this is the HTML "class" attribute used to
964
+ # contain CSS classes. But other keyword attributes exist, for instance
965
+ # {the "rel" attribute}[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel].
966
+ #
967
+ # See also #remove_class, #kwattr_values, #kwattr_add, #kwattr_append
968
+ #
969
+ # [Parameters]
970
+ # - +attribute_name+ (String) The name of the keyword attribute to be modified.
971
+ # - +keywords+ (String, Array<String>)
972
+ # Keywords to be removed from the attribute named +attribute_name+. May be a string
973
+ # containing whitespace-delimited values, or an Array of String values. Any keywords present
974
+ # in the named attribute will be removed. If no keywords remain, or if +keywords+ is nil,
975
+ # the attribute is deleted.
976
+ #
977
+ # [Returns] +self+ (Node) for ease of chaining method calls.
978
+ #
979
+ # *Example:*
980
+ #
981
+ # Note that the +rel+ attribute is deleted when empty.
982
+ #
983
+ # node # => <a rel="nofollow noreferrer">link</a>
984
+ # node.kwattr_remove("rel", "nofollow") # => <a rel="noreferrer">link</a>
985
+ # node.kwattr_remove("rel", "noreferrer") # => <a>link</a>
986
+ #
987
+ # Since v1.11.0
988
+ def kwattr_remove(attribute_name, keywords)
989
+ if keywords.nil?
990
+ remove_attribute(attribute_name)
991
+ return self
992
+ end
993
+
994
+ keywords = keywordify(keywords)
995
+ current_kws = kwattr_values(attribute_name)
996
+ new_kws = current_kws - keywords
997
+ if new_kws.empty?
998
+ remove_attribute(attribute_name)
999
+ else
1000
+ set_attribute(attribute_name, new_kws.join(" "))
1001
+ end
1002
+ self
1003
+ end
1004
+
1005
+ alias_method :delete, :remove_attribute
1006
+ alias_method :get_attribute, :[]
1007
+ alias_method :attr, :[]
1008
+ alias_method :set_attribute, :[]=
1009
+ alias_method :has_attribute?, :key?
1010
+
1011
+ # :section:
1012
+
1013
+ ###
1014
+ # Returns true if this Node matches +selector+
1015
+ def matches?(selector)
1016
+ ancestors.last.search(selector).include?(self)
1017
+ end
1018
+
1019
+ ###
1020
+ # Create a DocumentFragment containing +tags+ that is relative to _this_
1021
+ # context node.
1022
+ def fragment(tags)
1023
+ document.related_class("DocumentFragment").new(document, tags, self)
1024
+ end
1025
+
1026
+ ###
1027
+ # Parse +string_or_io+ as a document fragment within the context of
1028
+ # *this* node. Returns a XML::NodeSet containing the nodes parsed from
1029
+ # +string_or_io+.
1030
+ def parse(string_or_io, options = nil)
1031
+ ##
1032
+ # When the current node is unparented and not an element node, use the
1033
+ # document as the parsing context instead. Otherwise, the in-context
1034
+ # parser cannot find an element or a document node.
1035
+ # Document Fragments are also not usable by the in-context parser.
1036
+ if !element? && !document? && (!parent || parent.fragment?)
1037
+ return document.parse(string_or_io, options)
1038
+ end
1039
+
1040
+ options ||= (document.html? ? ParseOptions::DEFAULT_HTML : ParseOptions::DEFAULT_XML)
1041
+ options = Nokogiri::XML::ParseOptions.new(options) if Integer === options
1042
+ yield options if block_given?
1043
+
1044
+ contents = if string_or_io.respond_to?(:read)
1045
+ string_or_io.read
1046
+ else
1047
+ string_or_io
1048
+ end
1049
+
1050
+ return Nokogiri::XML::NodeSet.new(document) if contents.empty?
1051
+
1052
+ # libxml2 does not obey the +recover+ option after encountering errors during +in_context+
1053
+ # parsing, and so this horrible hack is here to try to emulate recovery behavior.
1054
+ #
1055
+ # Unfortunately, this means we're no longer parsing "in context" and so namespaces that
1056
+ # would have been inherited from the context node won't be handled correctly. This hack was
1057
+ # written in 2010, and I regret it, because it's silently degrading functionality in a way
1058
+ # that's not easily prevented (or even detected).
1059
+ #
1060
+ # I think preferable behavior would be to either:
1061
+ #
1062
+ # a. add an error noting that we "fell back" and pointing the user to turning off the +recover+ option
1063
+ # b. don't recover, but raise a sensible exception
1064
+ #
1065
+ # For context and background: https://github.com/sparklemotion/nokogiri/issues/313
1066
+ # FIXME bug report: https://github.com/sparklemotion/nokogiri/issues/2092
1067
+ error_count = document.errors.length
1068
+ node_set = in_context(contents, options.to_i)
1069
+ if node_set.empty? && (document.errors.length > error_count)
1070
+ if options.recover?
1071
+ fragment = document.related_class("DocumentFragment").parse(contents)
1072
+ node_set = fragment.children
1073
+ else
1074
+ raise document.errors[error_count]
1075
+ end
1076
+ end
1077
+ node_set
1078
+ end
1079
+
1080
+ # :call-seq:
1081
+ # namespaces() → Hash<String(Namespace#prefix) ⇒ String(Namespace#href)>
1082
+ #
1083
+ # Fetch all the namespaces on this node and its ancestors.
1084
+ #
1085
+ # Note that the keys in this hash XML attributes that would be used to define this namespace,
1086
+ # such as "xmlns:prefix", not just the prefix.
1087
+ #
1088
+ # The default namespace for this node will be included with key "xmlns".
1089
+ #
1090
+ # See also #namespace_scopes
1091
+ #
1092
+ # [Returns]
1093
+ # Hash containing all the namespaces on this node and its ancestors. The hash keys are the
1094
+ # namespace prefix, and the hash value for each key is the namespace URI.
1095
+ #
1096
+ # *Example:*
1097
+ #
1098
+ # doc = Nokogiri::XML(<<~EOF)
1099
+ # <root xmlns="http://example.com/root" xmlns:in_scope="http://example.com/in_scope">
1100
+ # <first/>
1101
+ # <second xmlns="http://example.com/child"/>
1102
+ # <third xmlns:foo="http://example.com/foo"/>
1103
+ # </root>
1104
+ # EOF
1105
+ # doc.at_xpath("//root:first", "root" => "http://example.com/root").namespaces
1106
+ # # => {"xmlns"=>"http://example.com/root",
1107
+ # # "xmlns:in_scope"=>"http://example.com/in_scope"}
1108
+ # doc.at_xpath("//child:second", "child" => "http://example.com/child").namespaces
1109
+ # # => {"xmlns"=>"http://example.com/child",
1110
+ # # "xmlns:in_scope"=>"http://example.com/in_scope"}
1111
+ # doc.at_xpath("//root:third", "root" => "http://example.com/root").namespaces
1112
+ # # => {"xmlns:foo"=>"http://example.com/foo",
1113
+ # # "xmlns"=>"http://example.com/root",
1114
+ # # "xmlns:in_scope"=>"http://example.com/in_scope"}
1115
+ #
1116
+ def namespaces
1117
+ namespace_scopes.each_with_object({}) do |ns, hash|
1118
+ prefix = ns.prefix
1119
+ key = prefix ? "xmlns:#{prefix}" : "xmlns"
1120
+ hash[key] = ns.href
1121
+ end
1122
+ end
1123
+
1124
+ # Returns true if this is a Comment
1125
+ def comment?
1126
+ type == COMMENT_NODE
1127
+ end
1128
+
1129
+ # Returns true if this is a CDATA
1130
+ def cdata?
1131
+ type == CDATA_SECTION_NODE
1132
+ end
1133
+
1134
+ # Returns true if this is an XML::Document node
1135
+ def xml?
1136
+ type == DOCUMENT_NODE
1137
+ end
1138
+
1139
+ # Returns true if this is an HTML4::Document or HTML5::Document node
1140
+ def html?
1141
+ type == HTML_DOCUMENT_NODE
1142
+ end
1143
+
1144
+ # Returns true if this is a Document
1145
+ def document?
1146
+ is_a?(XML::Document)
1147
+ end
1148
+
1149
+ # Returns true if this is a ProcessingInstruction node
1150
+ def processing_instruction?
1151
+ type == PI_NODE
1152
+ end
1153
+
1154
+ # Returns true if this is a Text node
1155
+ def text?
1156
+ type == TEXT_NODE
1157
+ end
1158
+
1159
+ # Returns true if this is a DocumentFragment
1160
+ def fragment?
1161
+ type == DOCUMENT_FRAG_NODE
1162
+ end
1163
+
1164
+ ###
1165
+ # Fetch the Nokogiri::HTML4::ElementDescription for this node. Returns
1166
+ # nil on XML documents and on unknown tags.
1167
+ def description
1168
+ return nil if document.xml?
1169
+
1170
+ Nokogiri::HTML4::ElementDescription[name]
1171
+ end
1172
+
1173
+ ###
1174
+ # Is this a read only node?
1175
+ def read_only?
1176
+ # According to gdome2, these are read-only node types
1177
+ [NOTATION_NODE, ENTITY_NODE, ENTITY_DECL].include?(type)
1178
+ end
1179
+
1180
+ # Returns true if this is an Element node
1181
+ def element?
1182
+ type == ELEMENT_NODE
1183
+ end
1184
+
1185
+ alias_method :elem?, :element?
1186
+
1187
+ ###
1188
+ # Turn this node in to a string. If the document is HTML, this method
1189
+ # returns html. If the document is XML, this method returns XML.
1190
+ def to_s
1191
+ document.xml? ? to_xml : to_html
1192
+ end
1193
+
1194
+ # Get the inner_html for this node's Node#children
1195
+ def inner_html(*args)
1196
+ children.map { |x| x.to_html(*args) }.join
1197
+ end
1198
+
1199
+ # Get the path to this node as a CSS expression
1200
+ def css_path
1201
+ path.split(%r{/}).filter_map do |part|
1202
+ part.empty? ? nil : part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
1203
+ end.join(" > ")
1204
+ end
1205
+
1206
+ ###
1207
+ # Get a list of ancestor Node for this Node. If +selector+ is given,
1208
+ # the ancestors must match +selector+
1209
+ def ancestors(selector = nil)
1210
+ return NodeSet.new(document) unless respond_to?(:parent)
1211
+ return NodeSet.new(document) unless parent
1212
+
1213
+ parents = [parent]
1214
+
1215
+ while parents.last.respond_to?(:parent)
1216
+ break unless (ctx_parent = parents.last.parent)
1217
+
1218
+ parents << ctx_parent
1219
+ end
1220
+
1221
+ return NodeSet.new(document, parents) unless selector
1222
+
1223
+ root = parents.last
1224
+ search_results = root.search(selector)
1225
+
1226
+ NodeSet.new(document, parents.find_all do |parent|
1227
+ search_results.include?(parent)
1228
+ end)
1229
+ end
1230
+
1231
+ ####
1232
+ # Yields self and all children to +block+ recursively.
1233
+ def traverse(&block)
1234
+ children.each { |j| j.traverse(&block) }
1235
+ yield(self)
1236
+ end
1237
+
1238
+ ###
1239
+ # Accept a visitor. This method calls "visit" on +visitor+ with self.
1240
+ def accept(visitor)
1241
+ visitor.visit(self)
1242
+ end
1243
+
1244
+ ###
1245
+ # Test to see if this Node is equal to +other+
1246
+ def ==(other)
1247
+ return false unless other
1248
+ return false unless other.respond_to?(:pointer_id)
1249
+
1250
+ pointer_id == other.pointer_id
1251
+ end
1252
+
1253
+ ###
1254
+ # Compare two Node objects with respect to their Document. Nodes from
1255
+ # different documents cannot be compared.
1256
+ def <=>(other)
1257
+ return nil unless other.is_a?(Nokogiri::XML::Node)
1258
+ return nil unless document == other.document
1259
+
1260
+ compare(other)
1261
+ end
1262
+
1263
+ # :section: Serialization and Generating Output
1264
+
1265
+ ###
1266
+ # Serialize Node using +options+. Save options can also be set using a block.
1267
+ #
1268
+ # See also Nokogiri::XML::Node::SaveOptions and Node@Serialization+and+Generating+Output.
1269
+ #
1270
+ # These two statements are equivalent:
1271
+ #
1272
+ # node.serialize(:encoding => 'UTF-8', :save_with => FORMAT | AS_XML)
1273
+ #
1274
+ # or
1275
+ #
1276
+ # node.serialize(:encoding => 'UTF-8') do |config|
1277
+ # config.format.as_xml
1278
+ # end
1279
+ #
1280
+ def serialize(*args, &block)
1281
+ options = if args.first.is_a?(Hash)
1282
+ args.shift
1283
+ else
1284
+ {
1285
+ encoding: args[0],
1286
+ save_with: args[1],
1287
+ }
1288
+ end
1289
+
1290
+ options[:encoding] ||= document.encoding
1291
+ encoding = Encoding.find(options[:encoding] || "UTF-8")
1292
+
1293
+ io = StringIO.new(String.new(encoding: encoding))
1294
+
1295
+ write_to(io, options, &block)
1296
+ io.string
1297
+ end
1298
+
1299
+ ###
1300
+ # Serialize this Node to HTML
1301
+ #
1302
+ # doc.to_html
1303
+ #
1304
+ # See Node#write_to for a list of +options+. For formatted output,
1305
+ # use Node#to_xhtml instead.
1306
+ def to_html(options = {})
1307
+ to_format(SaveOptions::DEFAULT_HTML, options)
1308
+ end
1309
+
1310
+ ###
1311
+ # Serialize this Node to XML using +options+
1312
+ #
1313
+ # doc.to_xml(:indent => 5, :encoding => 'UTF-8')
1314
+ #
1315
+ # See Node#write_to for a list of +options+
1316
+ def to_xml(options = {})
1317
+ options[:save_with] ||= SaveOptions::DEFAULT_XML
1318
+ serialize(options)
1319
+ end
1320
+
1321
+ ###
1322
+ # Serialize this Node to XHTML using +options+
1323
+ #
1324
+ # doc.to_xhtml(:indent => 5, :encoding => 'UTF-8')
1325
+ #
1326
+ # See Node#write_to for a list of +options+
1327
+ def to_xhtml(options = {})
1328
+ to_format(SaveOptions::DEFAULT_XHTML, options)
1329
+ end
1330
+
1331
+ ###
1332
+ # Write Node to +io+ with +options+. +options+ modify the output of
1333
+ # this method. Valid options are:
1334
+ #
1335
+ # * +:encoding+ for changing the encoding
1336
+ # * +:indent_text+ the indentation text, defaults to one space
1337
+ # * +:indent+ the number of +:indent_text+ to use, defaults to 2
1338
+ # * +:save_with+ a combination of SaveOptions constants.
1339
+ #
1340
+ # To save with UTF-8 indented twice:
1341
+ #
1342
+ # node.write_to(io, :encoding => 'UTF-8', :indent => 2)
1343
+ #
1344
+ # To save indented with two dashes:
1345
+ #
1346
+ # node.write_to(io, :indent_text => '-', :indent => 2)
1347
+ #
1348
+ def write_to(io, *options)
1349
+ options = options.first.is_a?(Hash) ? options.shift : {}
1350
+ encoding = options[:encoding] || options[0]
1351
+ if Nokogiri.jruby?
1352
+ save_options = options[:save_with] || options[1]
1353
+ indent_times = options[:indent] || 0
1354
+ else
1355
+ save_options = options[:save_with] || options[1] || SaveOptions::FORMAT
1356
+ indent_times = options[:indent] || 2
1357
+ end
1358
+ indent_text = options[:indent_text] || " "
1359
+
1360
+ # Any string times 0 returns an empty string. Therefore, use the same
1361
+ # string instead of generating a new empty string for every node with
1362
+ # zero indentation.
1363
+ indentation = indent_times.zero? ? "" : (indent_text * indent_times)
1364
+
1365
+ config = SaveOptions.new(save_options.to_i)
1366
+ yield config if block_given?
1367
+
1368
+ native_write_to(io, encoding, indentation, config.options)
1369
+ end
1370
+
1371
+ ###
1372
+ # Write Node as HTML to +io+ with +options+
1373
+ #
1374
+ # See Node#write_to for a list of +options+
1375
+ def write_html_to(io, options = {})
1376
+ write_format_to(SaveOptions::DEFAULT_HTML, io, options)
1377
+ end
1378
+
1379
+ ###
1380
+ # Write Node as XHTML to +io+ with +options+
1381
+ #
1382
+ # See Node#write_to for a list of +options+
1383
+ def write_xhtml_to(io, options = {})
1384
+ write_format_to(SaveOptions::DEFAULT_XHTML, io, options)
1385
+ end
1386
+
1387
+ ###
1388
+ # Write Node as XML to +io+ with +options+
1389
+ #
1390
+ # doc.write_xml_to io, :encoding => 'UTF-8'
1391
+ #
1392
+ # See Node#write_to for a list of options
1393
+ def write_xml_to(io, options = {})
1394
+ options[:save_with] ||= SaveOptions::DEFAULT_XML
1395
+ write_to(io, options)
1396
+ end
1397
+
1398
+ def canonicalize(mode = XML::XML_C14N_1_0, inclusive_namespaces = nil, with_comments = false)
1399
+ c14n_root = self
1400
+ document.canonicalize(mode, inclusive_namespaces, with_comments) do |node, parent|
1401
+ tn = node.is_a?(XML::Node) ? node : parent
1402
+ tn == c14n_root || tn.ancestors.include?(c14n_root)
1403
+ end
1404
+ end
1405
+
1406
+ DECONSTRUCT_KEYS = [:name, :attributes, :children, :namespace, :content, :elements, :inner_html].freeze # :nodoc:
1407
+ DECONSTRUCT_METHODS = { attributes: :attribute_nodes }.freeze # :nodoc:
1408
+
1409
+ #
1410
+ # :call-seq: deconstruct_keys(array_of_names) → Hash
1411
+ #
1412
+ # Returns a hash describing the Node, to use in pattern matching.
1413
+ #
1414
+ # Valid keys and their values:
1415
+ # - +name+ → (String) The name of this node, or "text" if it is a Text node.
1416
+ # - +namespace+ → (Namespace, nil) The namespace of this node, or nil if there is no namespace.
1417
+ # - +attributes+ → (Array<Attr>) The attributes of this node.
1418
+ # - +children+ → (Array<Node>) The children of this node. 💡 Note this includes text nodes.
1419
+ # - +elements+ → (Array<Node>) The child elements of this node. 💡 Note this does not include text nodes.
1420
+ # - +content+ → (String) The contents of all the text nodes in this node's subtree. See #content.
1421
+ # - +inner_html+ → (String) The inner markup for the children of this node. See #inner_html.
1422
+ #
1423
+ # ⚡ This is an experimental feature, available since v1.14.0
1424
+ #
1425
+ # *Example*
1426
+ #
1427
+ # doc = Nokogiri::XML.parse(<<~XML)
1428
+ # <?xml version="1.0"?>
1429
+ # <parent xmlns="http://nokogiri.org/ns/default" xmlns:noko="http://nokogiri.org/ns/noko">
1430
+ # <child1 foo="abc" noko:bar="def">First</child1>
1431
+ # <noko:child2 foo="qwe" noko:bar="rty">Second</noko:child2>
1432
+ # </parent>
1433
+ # XML
1434
+ #
1435
+ # doc.root.deconstruct_keys([:name, :namespace])
1436
+ # # => {:name=>"parent",
1437
+ # # :namespace=>
1438
+ # # #(Namespace:0x35c { href = "http://nokogiri.org/ns/default" })}
1439
+ #
1440
+ # doc.root.deconstruct_keys([:inner_html, :content])
1441
+ # # => {:content=>"\n" + " First\n" + " Second\n",
1442
+ # # :inner_html=>
1443
+ # # "\n" +
1444
+ # # " <child1 foo=\"abc\" noko:bar=\"def\">First</child1>\n" +
1445
+ # # " <noko:child2 foo=\"qwe\" noko:bar=\"rty\">Second</noko:child2>\n"}
1446
+ #
1447
+ # doc.root.elements.first.deconstruct_keys([:attributes])
1448
+ # # => {:attributes=>
1449
+ # # [#(Attr:0x370 { name = "foo", value = "abc" }),
1450
+ # # #(Attr:0x384 {
1451
+ # # name = "bar",
1452
+ # # namespace = #(Namespace:0x398 {
1453
+ # # prefix = "noko",
1454
+ # # href = "http://nokogiri.org/ns/noko"
1455
+ # # }),
1456
+ # # value = "def"
1457
+ # # })]}
1458
+ #
1459
+ def deconstruct_keys(keys)
1460
+ requested_keys = DECONSTRUCT_KEYS & keys
1461
+ {}.tap do |values|
1462
+ requested_keys.each do |key|
1463
+ method = DECONSTRUCT_METHODS[key] || key
1464
+ values[key] = send(method)
1465
+ end
1466
+ end
1467
+ end
1468
+
1469
+ # :section:
1470
+
1471
+ protected
1472
+
1473
+ def coerce(data)
1474
+ case data
1475
+ when XML::NodeSet
1476
+ return data
1477
+ when XML::DocumentFragment
1478
+ return data.children
1479
+ when String
1480
+ return fragment(data).children
1481
+ when Document, XML::Attr
1482
+ # unacceptable
1483
+ when XML::Node
1484
+ return data
1485
+ end
1486
+
1487
+ raise ArgumentError, <<~EOERR
1488
+ Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
1489
+ (You probably want to select a node from the Document with at() or search(), or create a new Node via Node.new().)
1490
+ EOERR
1491
+ end
1492
+
1493
+ private
1494
+
1495
+ def keywordify(keywords)
1496
+ case keywords
1497
+ when Enumerable
1498
+ keywords
1499
+ when String
1500
+ keywords.scan(/\S+/)
1501
+ else
1502
+ raise ArgumentError,
1503
+ "Keyword attributes must be passed as either a String or an Enumerable, but received #{keywords.class}"
1504
+ end
1505
+ end
1506
+
1507
+ def add_sibling(next_or_previous, node_or_tags)
1508
+ raise("Cannot add sibling to a node with no parent") unless parent
1509
+
1510
+ impl = next_or_previous == :next ? :add_next_sibling_node : :add_previous_sibling_node
1511
+ iter = next_or_previous == :next ? :reverse_each : :each
1512
+
1513
+ node_or_tags = parent.coerce(node_or_tags)
1514
+ if node_or_tags.is_a?(XML::NodeSet)
1515
+ if text?
1516
+ pivot = Nokogiri::XML::Node.new("dummy", document)
1517
+ send(impl, pivot)
1518
+ else
1519
+ pivot = self
1520
+ end
1521
+ node_or_tags.send(iter) { |n| pivot.send(impl, n) }
1522
+ pivot.unlink if text?
1523
+ else
1524
+ send(impl, node_or_tags)
1525
+ end
1526
+ node_or_tags
1527
+ end
1528
+
1529
+ USING_LIBXML_WITH_BROKEN_SERIALIZATION = Nokogiri.uses_libxml?("~> 2.6.0").freeze
1530
+ private_constant :USING_LIBXML_WITH_BROKEN_SERIALIZATION
1531
+
1532
+ def to_format(save_option, options)
1533
+ return dump_html if USING_LIBXML_WITH_BROKEN_SERIALIZATION
1534
+
1535
+ options[:save_with] = save_option unless options[:save_with]
1536
+ serialize(options)
1537
+ end
1538
+
1539
+ def write_format_to(save_option, io, options)
1540
+ return (io << dump_html) if USING_LIBXML_WITH_BROKEN_SERIALIZATION
1541
+
1542
+ options[:save_with] ||= save_option
1543
+ write_to(io, options)
1544
+ end
1545
+
1546
+ def inspect_attributes
1547
+ [:name, :namespace, :attribute_nodes, :children]
1548
+ end
1549
+
1550
+ IMPLIED_XPATH_CONTEXTS = [".//"].freeze
1551
+
1552
+ def add_child_node_and_reparent_attrs(node)
1553
+ add_child_node(node)
1554
+ node.attribute_nodes.find_all { |a| a.name.include?(":") }.each do |attr_node|
1555
+ attr_node.remove
1556
+ node[attr_node.name] = attr_node.value
1557
+ end
1558
+ end
1559
+ end
1560
+ end
1561
+ end
1562
+
1563
+ require_relative "node/save_options"