leptris 1.9.222.0 → 1.9.222.1

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: 403ad2f7e9b542b03e8f770c1b070e0e972ac0b4aca5ae16e5ed8f675b7599e4
4
- data.tar.gz: 5e5c49bb4ffdda23e1466b653346e6c20de604212a2941c97a9fa2c6b2297f83
3
+ metadata.gz: cf0ea790ec16e127a73ad58973fe745a6bd2c571cc410b6cb362e67491e985b0
4
+ data.tar.gz: a357e60e6bef11e695bc44ab3f20e6017acb015802aec4b263aca6453204085e
5
5
  SHA512:
6
- metadata.gz: 751de9f301ea9157169b546b3d81c8c65942e6c13fd8516bbd4e7ec6e940877146e634bccb92ab1b043c63f0b77bca5c3ea7cb83d81d539228a38cba6eaba072
7
- data.tar.gz: e38a36cfdcf7d4d52d007389dbc6ca5ab4e5f84f8794af81667837005a5928a776dc532b86b50dc8dc32644e8d0cbde979f9edfaa89695564e91e33dd5320d02
6
+ metadata.gz: b08936fcefef29f5f550d59b67f563c76b116aa703037cd921402fd9eb7d162d16d865a9977c20ab0386a85daa7422fd5a25f803a94bec3c3e0a959610ddd9cc
7
+ data.tar.gz: da92ef6e730969414e48d33ee23f3b8e845bcad7413fdf78ef9a436188741503cfc050f123a61394de67ea51ce29d2aa43554599807d863642be534b800ce3d9
data/lib/leptris/html.rb CHANGED
@@ -10,9 +10,15 @@
10
10
  # Leptris::HTML(html) # the html4 lane (Nokogiri::HTML parity)
11
11
  # Leptris::HTML5.parse(html) # the WHATWG engine (html5lib zero reds)
12
12
  # Leptris::HTML4.parse(html) # explicit html4 lane
13
+ # Leptris::HTML5.create # fresh document, <!DOCTYPE html> preset
13
14
  #
14
15
  # These are pure delegates — no parallel class hierarchy, no second
15
16
  # wrapper cache; identity and the whole XML surface carry over.
17
+ # Construction flows through the same face as XML (create_element,
18
+ # add_child, root=, set_doctype) — `create` presets the doctype so a
19
+ # programmatically built document serializes with it; pair with
20
+ # `to_xml(no_decl: true)` for declaration-free HTML output (void
21
+ # elements serialize with the HTML5-legal trailing slash).
16
22
  module Leptris
17
23
  module HTML
18
24
  module_function
@@ -22,6 +28,37 @@ module Leptris
22
28
  def parse(html)
23
29
  Leptris::XML.parse_html(html, mode: :html4)
24
30
  end
31
+
32
+ # Fresh document with the HTML 4.01 Transitional doctype preset
33
+ # (Nokogiri::HTML::Builder's .doc carries the same). Pass
34
+ # +doctype: nil+ for a bare document.
35
+ def create(doctype: :transitional, skeleton: true)
36
+ create_document(doctype, skeleton: skeleton)
37
+ end
38
+
39
+ def create_document(doctype, skeleton: true)
40
+ doc = Leptris::XML::Document.create
41
+ case doctype
42
+ when :html5, true
43
+ doc.set_doctype("html")
44
+ when :transitional
45
+ doc.set_doctype(
46
+ "HTML",
47
+ public_id: "-//W3C//DTD HTML 4.01 Transitional//EN",
48
+ system_id: "http://www.w3.org/TR/html4/loose.dtd")
49
+ when nil, false then nil
50
+ else raise ArgumentError,
51
+ "doctype must be :html5, :transitional, nil, or false, got #{doctype.inspect}"
52
+ end
53
+ return doc unless skeleton
54
+ html = doc.create_element("html")
55
+ head = doc.create_element("head")
56
+ body = doc.create_element("body")
57
+ html.add_child(head)
58
+ html.add_child(body)
59
+ doc.root = html
60
+ doc
61
+ end
25
62
  end
26
63
 
27
64
  module HTML4
@@ -30,6 +67,10 @@ module Leptris
30
67
  def parse(html)
31
68
  Leptris::XML.parse_html(html, mode: :html4)
32
69
  end
70
+
71
+ def create(doctype: :transitional, skeleton: true)
72
+ Leptris::HTML.create_document(doctype, skeleton: skeleton)
73
+ end
33
74
  end
34
75
 
35
76
  module HTML5
@@ -41,5 +82,11 @@ module Leptris
41
82
  def parse(html)
42
83
  Leptris::XML.parse_html(html, mode: :whatwg)
43
84
  end
85
+
86
+ # Fresh document with the HTML5 doctype (`<!DOCTYPE html>`)
87
+ # preset; pass +doctype: nil+ for a bare document.
88
+ def create(doctype: :html5, skeleton: true)
89
+ Leptris::HTML.create_document(doctype, skeleton: skeleton)
90
+ end
44
91
  end
45
92
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.222.0"
4
+ VERSION = "1.9.222.1"
5
5
  end
@@ -474,6 +474,16 @@ class Leptris::XML::Document
474
474
  alias_method :to_s, :to_xml
475
475
  alias_method :serialize, :to_xml
476
476
 
477
+ # HTML5 serialization (#309): void elements without a trailing
478
+ # slash (`<br>`), script/style content as raw text, text/attr
479
+ # entity escaping per HTML rules, the DOCTYPE emitted first.
480
+ # Walks the tree in Ruby — the engine-side serialization mode is
481
+ # the perf headroom this face gates on.
482
+ def to_html
483
+ raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
484
+ Leptris::XML::HTMLSerialize.document(self)
485
+ end
486
+
477
487
  def save(path, **opts)
478
488
  opts_struct, _encoding_anchor = Leptris::XML::Serialization.build_options(
479
489
  indent: opts.fetch(:indent, 0),
@@ -756,6 +756,13 @@ class Leptris::XML::Element < Leptris::XML::Node
756
756
  indent: indent, no_decl: no_decl, encoding: encoding)
757
757
  end
758
758
 
759
+ # HTML5 serialization of this element subtree (#309 — see
760
+ # Document#to_html for the semantics).
761
+ def to_html
762
+ ensure_alive!
763
+ Leptris::XML::HTMLSerialize.element(self)
764
+ end
765
+
759
766
  def canonicalize(version = Leptris::XML::FFI::C14N_1_0,
760
767
  inclusive_namespaces = nil,
761
768
  with_comments: false,
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The HTML5 tree serializer (#309): Ruby-level walk emitting
4
+ # HTML serialization semantics the XML face can't express —
5
+ #
6
+ # - VOID elements (br, meta, img, …) emit without the trailing
7
+ # slash and without a closing tag (`<br>`, `<meta charset="utf-8">`)
8
+ # - script/style content is RAW TEXT — emitted verbatim, never
9
+ # entity-escaped (`a > b` stays `a > b` inside `<style>`)
10
+ # - childless non-void elements get explicit closing tags
11
+ # (`<div></div>`, never `<div/>`)
12
+ # - text escapes &, <, > (and U+00A0 as &nbsp;); attributes escape
13
+ # &, ", <, >
14
+ #
15
+ # `Document#to_html` / `Element#to_html` are one-line delegates.
16
+ # The walk is Ruby-level by design for now — the engine-side
17
+ # serialization mode (raw-text + void handling in C) is the
18
+ # perf headroom this face gates on.
19
+ module Leptris::XML::HTMLSerialize
20
+ VOID = %w[
21
+ area base br col embed hr img input link meta param source
22
+ track wbr
23
+ ].freeze
24
+ RAW_TEXT = %w[script style].freeze
25
+
26
+ def self.document(doc)
27
+ out = +""
28
+ if (dt = doc.doctype)
29
+ out << "<!DOCTYPE" << " #{dt.name}"
30
+ out << " PUBLIC \"#{dt.public_id}\"" if dt.public_id
31
+ out << " \"#{dt.system_id}\"" if dt.system_id
32
+ out << ">"
33
+ end
34
+ doc.children.each { |child| node(child, out) }
35
+ out
36
+ end
37
+
38
+ def self.element(element)
39
+ out = +""
40
+ node(element, out)
41
+ out
42
+ end
43
+
44
+ def self.node(node, out)
45
+ case node
46
+ when Leptris::XML::Element then element_node(node, out)
47
+ when Leptris::XML::Comment then out << "<!--" << node.content.to_s << "-->"
48
+ when Leptris::XML::CDATA
49
+ # HTML5: CDATA sections in HTML serialize as bogus comments.
50
+ out << "<!--[CDATA[" << node.content.to_s << "]]-->"
51
+ when Leptris::XML::ProcessingInstruction
52
+ # HTML5: PIs serialize as bogus comments (?> is not a thing).
53
+ out << "<?" << node.target.to_s
54
+ out << " " << node.data.to_s unless node.data.to_s.empty?
55
+ out << ">"
56
+ when Leptris::XML::Text then text(node, out)
57
+ end
58
+ end
59
+
60
+ def self.element_node(el, out)
61
+ name = el.name
62
+ out << "<" << name
63
+ el.attributes.each do |attr_name, attr|
64
+ out << " " << attr_name << "=\"" << escape_attr(attr.value) << "\""
65
+ end
66
+ out << ">"
67
+ return if VOID.include?(name)
68
+
69
+ if RAW_TEXT.include?(name)
70
+ # Raw text: the element's text content verbatim — no entity
71
+ # escaping (CSS child selectors, JS comparisons survive).
72
+ el.children.each do |child|
73
+ out << child.content.to_s if child.is_a?(Leptris::XML::Text)
74
+ end
75
+ else
76
+ el.children.each { |child| node(child, out) }
77
+ end
78
+ out << "</" << name << ">"
79
+ end
80
+
81
+ def self.text(node, out)
82
+ parent_raw = node.parent && RAW_TEXT.include?(node.parent.name)
83
+ s = node.content.to_s
84
+ out << (parent_raw ? s : escape_text(s))
85
+ end
86
+
87
+ def self.escape_text(s)
88
+ s.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;")
89
+ .gsub("\u00A0", "&nbsp;")
90
+ end
91
+
92
+ def self.escape_attr(s)
93
+ s.to_s.gsub("&", "&amp;").gsub("\"", "&quot;")
94
+ .gsub("<", "&lt;").gsub(">", "&gt;")
95
+ .gsub("\u00A0", "&nbsp;")
96
+ end
97
+ end
data/lib/leptris/xml.rb CHANGED
@@ -21,6 +21,7 @@ module Leptris
21
21
  autoload :CssToXPath, "leptris/xml/css_to_xpath"
22
22
  autoload :CStringArray, "leptris/xml/c_string_array"
23
23
  autoload :Serialization, "leptris/xml/serialization"
24
+ autoload :HTMLSerialize, "leptris/xml/html_serialize"
24
25
  autoload :SAX, "leptris/xml/sax"
25
26
  autoload :XPath, "leptris/xml/xpath"
26
27
  autoload :EvaluationContext, "leptris/xml/evaluation_context"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.222.0
4
+ version: 1.9.222.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -179,6 +179,7 @@ files:
179
179
  - lib/leptris/xml/entity_reference.rb
180
180
  - lib/leptris/xml/evaluation_context.rb
181
181
  - lib/leptris/xml/ffi.rb
182
+ - lib/leptris/xml/html_serialize.rb
182
183
  - lib/leptris/xml/iteration_scope.rb
183
184
  - lib/leptris/xml/iterparse.rb
184
185
  - lib/leptris/xml/namespace.rb