canon 0.3.98 → 0.3.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4e993fe0bac445ec82e1a02983bfa2f297c1b7d9d112bcb7c724f3f010a1b353
|
|
4
|
+
data.tar.gz: 869324e74378af4f4cb9493fbfdc04ac1ba8e76c0378159547bc7e23ebf66084
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 70c6dcb6bb9da7ef764f9aed9e1e8d02fb95193c446102df62496a10dbe33010d29b5d0e90293d929cf3adc267b5128ad1dd7e9666dff38b892d221bc681f20c
|
|
7
|
+
data.tar.gz: dc45835bdab2c58479e8413eb87aee5c8615237ebf6bd2c435ae725b58b7df247c3740c25a3ab192fb0aaf86c6776d3634c5a66170ea6834a3f3bedf5582fd27
|
|
@@ -295,8 +295,8 @@ module Canon
|
|
|
295
295
|
Canon::Xml::C14n.canonicalize(html_string,
|
|
296
296
|
with_comments: false)
|
|
297
297
|
when :format
|
|
298
|
-
#
|
|
299
|
-
Canon.
|
|
298
|
+
# Engine single-line structural normalizer
|
|
299
|
+
Canon::Html::DataModel.engine_html_format(html_string)
|
|
300
300
|
else
|
|
301
301
|
# :none or unrecognized
|
|
302
302
|
html_string
|
|
@@ -375,8 +375,8 @@ module Canon
|
|
|
375
375
|
Canon::Xml::C14n.canonicalize(node,
|
|
376
376
|
with_comments: false)
|
|
377
377
|
when :format
|
|
378
|
-
#
|
|
379
|
-
Canon.
|
|
378
|
+
# Engine single-line structural normalizer
|
|
379
|
+
Canon::Html::DataModel.engine_html_format(node)
|
|
380
380
|
else
|
|
381
381
|
# :none, :rendered or unrecognized - use as-is
|
|
382
382
|
node
|
|
@@ -60,29 +60,10 @@ module Canon
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
# Parse an HTML string through the engine's WHATWG fragment
|
|
63
|
-
# parsing and build a Canon tree
|
|
64
|
-
#
|
|
65
|
-
# whitespace significance is the comparison profile's decision
|
|
66
|
-
# (#112/#137 ride on stray whitespace surviving the parse),
|
|
67
|
-
# unlike the XML DOM lane's parse-time policy. Top-level
|
|
68
|
-
# element children convert through the materialize-records
|
|
69
|
-
# fast path; the wrapper walk serves the rare non-element
|
|
70
|
-
# top-level nodes (text, comments).
|
|
63
|
+
# parsing and build a Canon tree. Canon::Html::DataModel owns
|
|
64
|
+
# the conversion (the single home for HTML→Canon-tree building).
|
|
71
65
|
def engine_fragment(html)
|
|
72
|
-
|
|
73
|
-
root = Canon::Xml::Nodes::RootNode.new
|
|
74
|
-
frag.children.each do |child|
|
|
75
|
-
node =
|
|
76
|
-
if child.is_a?(Moxml::Element)
|
|
77
|
-
Canon::Xml::DataModel.build_moxml_subtree_from_records(
|
|
78
|
-
child, preserve_whitespace: true
|
|
79
|
-
)
|
|
80
|
-
else
|
|
81
|
-
Canon::Xml::DataModel.walk_moxml(child, preserve_whitespace: true)
|
|
82
|
-
end
|
|
83
|
-
root.add_child(node) if node
|
|
84
|
-
end
|
|
85
|
-
root
|
|
66
|
+
Canon::Html::DataModel.engine_fragment(html)
|
|
86
67
|
end
|
|
87
68
|
|
|
88
69
|
# Engine HTML fragments are served by adapters with an engine
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
Canon::NokogiriLoader.require!("HTML support")
|
|
4
|
-
|
|
5
3
|
module Canon
|
|
6
4
|
module Html
|
|
7
5
|
# Builds XPath data model from HTML
|
|
@@ -10,58 +8,53 @@ module Canon
|
|
|
10
8
|
class DataModel < Canon::DataModel
|
|
11
9
|
# Build XPath data model from HTML string
|
|
12
10
|
#
|
|
11
|
+
# The engine lane: moxml Context#parse_html_fragment (WHATWG
|
|
12
|
+
# fragment parsing) → Canon tree. The +version:+ parameter is
|
|
13
|
+
# accepted for API compatibility — the engine serves every input
|
|
14
|
+
# through one WHATWG parse (the #118 html4/html5 unification).
|
|
15
|
+
# Full documents keep their head content at the fragment top
|
|
16
|
+
# level (the in-body rules ignore the html/body start tags but
|
|
17
|
+
# preserve every other node); the former Nokogiri body-extraction
|
|
18
|
+
# dropped head content entirely.
|
|
19
|
+
#
|
|
13
20
|
# @param html_string [String] HTML content to parse
|
|
14
|
-
# @param version [Symbol]
|
|
21
|
+
# @param version [Symbol] accepted for compatibility; unused
|
|
15
22
|
# @return [Canon::Xml::Nodes::RootNode] Root of the data model tree
|
|
23
|
+
# rubocop:disable-next Lint/UnusedMethodArgument -- API compatibility
|
|
16
24
|
def self.from_html(html_string, version: :html4)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# Fragments should use fragment parser to avoid adding implicit wrappers
|
|
20
|
-
is_full_document = html_string.match?(%r{<html[\s>]}i)
|
|
21
|
-
|
|
22
|
-
# Parse with Nokogiri using appropriate parser
|
|
23
|
-
doc = if is_full_document
|
|
24
|
-
# CRITICAL FIX: For full HTML documents, parse as document first
|
|
25
|
-
# and extract the body element. This avoids Nokogiri::HTML.fragment()
|
|
26
|
-
# incorrectly moving head elements (like meta) to the body.
|
|
27
|
-
# Parse as full document to get proper structure
|
|
28
|
-
full_doc = if version == :html5
|
|
29
|
-
Nokogiri::HTML5(html_string)
|
|
30
|
-
else
|
|
31
|
-
Nokogiri::HTML4(html_string)
|
|
32
|
-
end
|
|
33
|
-
# Extract body element and create fragment from it
|
|
34
|
-
body = full_doc.at_css("body")
|
|
35
|
-
if body
|
|
36
|
-
# Create a fragment and copy body children to it
|
|
37
|
-
# This preserves the body structure without head elements
|
|
38
|
-
frag = if version == :html5
|
|
39
|
-
Nokogiri::HTML5::DocumentFragment.new(full_doc)
|
|
40
|
-
else
|
|
41
|
-
Nokogiri::HTML4::DocumentFragment.new(full_doc)
|
|
42
|
-
end
|
|
43
|
-
body.children.each do |child|
|
|
44
|
-
frag.add_child(child.dup)
|
|
45
|
-
end
|
|
46
|
-
frag
|
|
47
|
-
elsif version == :html5
|
|
48
|
-
# No body found, fall back to fragment parsing
|
|
49
|
-
Nokogiri::HTML5.fragment(html_string)
|
|
50
|
-
else
|
|
51
|
-
Nokogiri::HTML4.fragment(html_string)
|
|
52
|
-
end
|
|
53
|
-
elsif version == :html5
|
|
54
|
-
# Fragment - use fragment parser to avoid implicit wrappers
|
|
55
|
-
Nokogiri::HTML5.fragment(html_string)
|
|
56
|
-
else
|
|
57
|
-
Nokogiri::HTML4.fragment(html_string)
|
|
58
|
-
end
|
|
25
|
+
engine_fragment(html_string)
|
|
26
|
+
end
|
|
59
27
|
|
|
60
|
-
|
|
61
|
-
|
|
28
|
+
# Parse an HTML string through the engine's WHATWG fragment
|
|
29
|
+
# parsing and build a Canon tree from the fragment children.
|
|
30
|
+
# Whitespace text nodes are preserved unconditionally — HTML
|
|
31
|
+
# whitespace significance is the comparison profile's decision
|
|
32
|
+
# (#112/#137), unlike the XML DOM lane's parse-time policy.
|
|
33
|
+
# Top-level element children convert through the
|
|
34
|
+
# materialize-records fast path; the wrapper walk serves the
|
|
35
|
+
# rare non-element top-level nodes (text, comments).
|
|
36
|
+
def self.engine_fragment(html)
|
|
37
|
+
frag = Canon::XmlParsing.moxml_context.parse_html_fragment(html)
|
|
38
|
+
root = Canon::Xml::Nodes::RootNode.new
|
|
39
|
+
frag.children.each do |child|
|
|
40
|
+
node =
|
|
41
|
+
if child.is_a?(Moxml::Element)
|
|
42
|
+
Canon::Xml::DataModel.build_moxml_subtree_from_records(
|
|
43
|
+
child, preserve_whitespace: true
|
|
44
|
+
)
|
|
45
|
+
else
|
|
46
|
+
Canon::Xml::DataModel.walk_moxml(child, preserve_whitespace: true)
|
|
47
|
+
end
|
|
48
|
+
root.add_child(node) if node
|
|
49
|
+
end
|
|
50
|
+
root
|
|
51
|
+
end
|
|
62
52
|
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
# Deterministic single-line engine formatting for the :format
|
|
54
|
+
# string preprocessor — parse + re-serialize as a pure function
|
|
55
|
+
# of the parsed tree.
|
|
56
|
+
def self.engine_html_format(html)
|
|
57
|
+
Canon::XmlParsing.moxml_context.parse_html(html).to_html
|
|
65
58
|
end
|
|
66
59
|
|
|
67
60
|
# Alias for compatibility
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
Canon::NokogiriLoader.require!("HTML tree diffing")
|
|
4
|
-
|
|
5
3
|
module Canon
|
|
6
4
|
module TreeDiff
|
|
7
5
|
module Adapters
|
|
@@ -46,17 +44,22 @@ module Canon
|
|
|
46
44
|
return to_tree_from_canon_comment(node)
|
|
47
45
|
end
|
|
48
46
|
|
|
49
|
-
# Fallback to Nokogiri (legacy support
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
# Fallback to Nokogiri (legacy support for user-supplied
|
|
48
|
+
# nodes) — the Canon tree branches above run nokogiri-less.
|
|
49
|
+
if defined?(Nokogiri)
|
|
50
|
+
case node
|
|
51
|
+
when Nokogiri::HTML::Document, Nokogiri::HTML4::Document, Nokogiri::HTML5::Document
|
|
52
|
+
# Start from html element or root element
|
|
53
|
+
root = node.at_css("html") || node.root
|
|
54
|
+
root ? to_tree(root) : nil
|
|
55
|
+
when Nokogiri::HTML4::DocumentFragment, Nokogiri::HTML5::DocumentFragment, Nokogiri::XML::DocumentFragment
|
|
56
|
+
# For DocumentFragment, create a wrapper root node and add all fragment children
|
|
57
|
+
convert_fragment(node)
|
|
58
|
+
when Nokogiri::XML::Element
|
|
59
|
+
convert_element(node)
|
|
60
|
+
else
|
|
61
|
+
raise ArgumentError, "Unsupported node type: #{node.class}"
|
|
62
|
+
end
|
|
60
63
|
else
|
|
61
64
|
raise ArgumentError, "Unsupported node type: #{node.class}"
|
|
62
65
|
end
|
|
@@ -68,7 +71,10 @@ module Canon
|
|
|
68
71
|
# @param doc [Nokogiri::HTML::Document] Optional document to use
|
|
69
72
|
# @return [Nokogiri::HTML::Document, Nokogiri::XML::Element]
|
|
70
73
|
def from_tree(tree_node, doc = nil)
|
|
71
|
-
doc
|
|
74
|
+
unless doc
|
|
75
|
+
Canon::NokogiriLoader.require!("HTML tree reconstruction")
|
|
76
|
+
doc = Nokogiri::HTML::Document.new
|
|
77
|
+
end
|
|
72
78
|
|
|
73
79
|
element = build_element(tree_node, doc)
|
|
74
80
|
|
data/lib/canon/version.rb
CHANGED