nokolexbor 0.7.0-aarch64-linux

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1853f48d77d8dc95236e11a3a41e2e38e276c28e6cc89a18100786892c359707
4
+ data.tar.gz: d4ebcf31410396478657ecedc405622b5c97ed431c2dd9afea8c199efb2d27cd
5
+ SHA512:
6
+ metadata.gz: 588c9575348817533f15249f6fab58ea84823b4e33d6300db70b7b1a4052e278868b19263f1fc9ec822d3098e9314422b792440aa8e8976e859fcbb0ccc21c43
7
+ data.tar.gz: bee83d0ff2ecc3bc390f574d26eeef91df0791deac17caa80fe4bdfc206a7f419030cea1c70dc1273d6e71d68c39178fc356efa2c1b0e6fa5d2d9756f3727584
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,223 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Nokolexbor
6
+ # A DSL for programmatically building HTML documents.
7
+ #
8
+ # == No-arg block (instance_eval style) – tag names are called bare:
9
+ #
10
+ # Nokolexbor do
11
+ # body do
12
+ # h1 'Hello world'
13
+ # p 'This little p'
14
+ # ul do
15
+ # li 'Go to market'
16
+ # li 'Go to bed'
17
+ # end
18
+ # end
19
+ # end
20
+ #
21
+ # == Block-parameter style – tag names are called on the builder argument:
22
+ #
23
+ # Nokolexbor::Builder.new do |b|
24
+ # b.body do
25
+ # b.h1 'Hello world'
26
+ # end
27
+ # end
28
+ #
29
+ # == Building into an existing node:
30
+ #
31
+ # Nokolexbor::Builder.with(existing_node) do
32
+ # span 'injected'
33
+ # end
34
+ #
35
+ class Builder
36
+ # The Document used to create new nodes
37
+ attr_accessor :doc
38
+
39
+ # The node that currently receives new children
40
+ attr_accessor :parent
41
+
42
+ # Arity of the outermost block (drives instance_eval vs yield dispatch)
43
+ attr_accessor :arity
44
+
45
+ # A context object for use when the block has no arguments
46
+ attr_accessor :context
47
+
48
+ # Build into an existing +root+ node.
49
+ def self.with(root, &block)
50
+ new(root, &block)
51
+ end
52
+
53
+ # @param root [Node, nil]
54
+ # When given, new nodes are appended under +root+ and +root.document+
55
+ # is used as the owning document. When omitted a fresh {Document} and
56
+ # a {DocumentFragment} container are created.
57
+ def initialize(root = nil, &block)
58
+ if root
59
+ @doc = root.document
60
+ @parent = root
61
+ else
62
+ @doc = Document.new
63
+ @parent = DocumentFragment.new(@doc)
64
+ end
65
+
66
+ @context = nil
67
+ @arity = nil
68
+
69
+ return unless block
70
+
71
+ @arity = block.arity
72
+ if @arity <= 0
73
+ # Capture outer self so that outer methods / ivars remain accessible
74
+ # via method_missing delegation while the builder acts as self.
75
+ @context = eval("self", block.binding)
76
+ instance_eval(&block)
77
+ else
78
+ yield self
79
+ end
80
+ end
81
+
82
+ # Insert a Text node containing +string+.
83
+ def text(string)
84
+ insert(@doc.create_text_node(string))
85
+ end
86
+
87
+ # Insert a CDATA node containing +string+.
88
+ def cdata(string)
89
+ insert(@doc.create_cdata(string))
90
+ end
91
+
92
+ # Insert a Comment node containing +string+.
93
+ def comment(string)
94
+ insert(@doc.create_comment(string))
95
+ end
96
+
97
+ # Append raw HTML (parsed into nodes) under the current parent.
98
+ def <<(string)
99
+ @doc.fragment(string).children.each { |x| insert(x) }
100
+ end
101
+
102
+ # Serialize the built content to an HTML string.
103
+ def to_html
104
+ @parent.to_html
105
+ end
106
+ alias_method :to_s, :to_html
107
+
108
+ # Ruby's Kernel#p, Kernel#pp, etc. are defined on Object and therefore on
109
+ # Builder itself. They must be overridden so they fall through to
110
+ # method_missing and create the corresponding HTML element instead of
111
+ # printing values to stdout.
112
+ def p(*args, &block) # :nodoc:
113
+ method_missing(:p, *args, &block)
114
+ end
115
+
116
+ def method_missing(method, *args, &block) # :nodoc:
117
+ # Only delegate to the outer context for methods the user defined there.
118
+ # Standard Object / Kernel methods (like :p, :pp, :puts …) must not be
119
+ # forwarded to @context because all objects respond to them, which would
120
+ # bypass element creation entirely.
121
+ if @context && !OBJECT_INSTANCE_METHODS.include?(method) && @context.respond_to?(method)
122
+ @context.send(method, *args, &block)
123
+ else
124
+ node = @doc.create_element(method.to_s.sub(/[_!]$/, ""), *args)
125
+ insert(node, &block)
126
+ end
127
+ end
128
+
129
+ def respond_to_missing?(method, include_private = false) # :nodoc:
130
+ (@context && !OBJECT_INSTANCE_METHODS.include?(method) && @context.respond_to?(method)) || super
131
+ end
132
+
133
+ # Methods defined on plain Object that should never be forwarded to @context
134
+ # as HTML element creation fallbacks.
135
+ OBJECT_INSTANCE_METHODS = Object.instance_methods.to_set.freeze
136
+
137
+ private
138
+
139
+ # Add +node+ as a child of @parent; if a block is given, push @parent
140
+ # to +node+ for the duration of that block, then restore it.
141
+ # Returns a {NodeBuilder} for the inserted node so attributes / classes
142
+ # can be chained: <tt>div.container.hero!</tt>
143
+ def insert(node, &block)
144
+ node = @parent.add_child(node)
145
+ if block
146
+ old_parent = @parent
147
+ @parent = node
148
+ @arity ||= block.arity
149
+ begin
150
+ if @arity <= 0
151
+ instance_eval(&block)
152
+ else
153
+ yield(self)
154
+ end
155
+ ensure
156
+ @parent = old_parent
157
+ end
158
+ end
159
+ NodeBuilder.new(node, self)
160
+ end
161
+
162
+ # Wraps a built node and exposes a fluent API for setting classes and ids:
163
+ #
164
+ # div.container # => <div class="container">
165
+ # div.box.highlight # => <div class="box highlight">
166
+ # div.thing! # => <div id="thing">
167
+ # div.container.hero! # => <div class="container" id="hero">
168
+ #
169
+ class NodeBuilder # :nodoc:
170
+ def initialize(node, doc_builder)
171
+ @node = node
172
+ @doc_builder = doc_builder
173
+ end
174
+
175
+ def []=(k, v)
176
+ @node[k] = v
177
+ end
178
+
179
+ def [](k)
180
+ @node[k]
181
+ end
182
+
183
+ def method_missing(method, *args, &block)
184
+ opts = args.last.is_a?(Hash) ? args.pop : {}
185
+
186
+ case method.to_s
187
+ when /^(.*)!$/
188
+ @node["id"] = Regexp.last_match(1)
189
+ @node.content = args.first if args.first
190
+ when /^(.*)=$/
191
+ @node[Regexp.last_match(1)] = args.first
192
+ else
193
+ @node["class"] =
194
+ ((@node["class"] || "").split(/\s/) + [method.to_s]).join(" ")
195
+ @node.content = args.first if args.first
196
+ end
197
+
198
+ opts.each do |k, v|
199
+ @node[k.to_s] = ((@node[k.to_s] || "").split(/\s/) + [v.to_s]).join(" ")
200
+ end
201
+
202
+ if block
203
+ old_parent = @doc_builder.parent
204
+ @doc_builder.parent = @node
205
+ arity = @doc_builder.arity || block.arity
206
+ value = if arity <= 0
207
+ @doc_builder.instance_eval(&block)
208
+ else
209
+ yield(@doc_builder)
210
+ end
211
+ @doc_builder.parent = old_parent
212
+ return value
213
+ end
214
+
215
+ self
216
+ end
217
+
218
+ def respond_to_missing?(_method, _include_private = false) # :nodoc:
219
+ true
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nokolexbor
4
+ class Document < Nokolexbor::Node
5
+ # Create an {Element} with +name+ belonging to this document, optionally setting contents or
6
+ # attributes.
7
+ #
8
+ # @param name [String]
9
+ # @param contents_or_attrs [#to_s, Hash]
10
+ #
11
+ # @return [Element]
12
+ #
13
+ # @example An empty element without attributes
14
+ # doc.create_element("div")
15
+ # # => <div></div>
16
+ #
17
+ # @example An element with contents
18
+ # doc.create_element("div", "contents")
19
+ # # => <div>contents</div>
20
+ #
21
+ # @example An element with attributes
22
+ # doc.create_element("div", {"class" => "container"})
23
+ # # => <div class='container'></div>
24
+ #
25
+ # @example An element with contents and attributes
26
+ # doc.create_element("div", "contents", {"class" => "container"})
27
+ # # => <div class='container'>contents</div>
28
+ #
29
+ # @example Passing a block to mutate the element
30
+ # doc.create_element("div") { |node| node["class"] = "blue" }
31
+ # # => <div class='blue'></div>
32
+ def create_element(name, *contents_or_attrs, &block)
33
+ elm = Nokolexbor::Element.new(name, self, &block)
34
+ contents_or_attrs.each do |arg|
35
+ case arg
36
+ when Hash
37
+ arg.each do |k, v|
38
+ elm[k.to_s] = v.to_s
39
+ end
40
+ else
41
+ elm.content = arg.to_s
42
+ end
43
+ end
44
+ elm
45
+ end
46
+
47
+ # Create a {Text} with +string+.
48
+ #
49
+ # @return [Text]
50
+ def create_text_node(string, &block)
51
+ Nokolexbor::Text.new(string.to_s, self, &block)
52
+ end
53
+
54
+ # Create a {CDATA} containing +string+.
55
+ #
56
+ # @return [CDATA]
57
+ def create_cdata(string, &block)
58
+ Nokolexbor::CDATA.new(string.to_s, self, &block)
59
+ end
60
+
61
+ # Create a {Comment} containing +string+.
62
+ #
63
+ # @return [Comment]
64
+ def create_comment(string, &block)
65
+ Nokolexbor::Comment.new(string.to_s, self, &block)
66
+ end
67
+
68
+ # A reference to +self+.
69
+ #
70
+ # @return [Document]
71
+ def document
72
+ self
73
+ end
74
+
75
+ # Get the meta tag encoding for this document. If there is no meta tag, nil is returned.
76
+ #
77
+ # @return [String]
78
+ def meta_encoding
79
+ if (meta = at_css("meta[charset]"))
80
+ meta[:charset]
81
+ elsif (meta = meta_content_type)
82
+ meta["content"][/charset\s*=\s*([\w-]+)/i, 1]
83
+ end
84
+ end
85
+
86
+ # Set the meta tag encoding for this document.
87
+ #
88
+ # If an meta encoding tag is already present, its content is
89
+ # replaced with the given text.
90
+ #
91
+ # Otherwise, this method tries to create one at an appropriate
92
+ # place supplying head and/or html elements as necessary, which
93
+ # is inside a head element if any, and before any text node or
94
+ # content element (typically <body>) if any.
95
+ def meta_encoding=(encoding)
96
+ if (meta = meta_content_type)
97
+ meta["content"] = format("text/html; charset=%s", encoding)
98
+ encoding
99
+ elsif (meta = at_css("meta[charset]"))
100
+ meta["charset"] = encoding
101
+ else
102
+ meta = Nokolexbor::Node.new("meta", self)
103
+ meta["charset"] = encoding
104
+
105
+ if (head = at_css("head"))
106
+ head.prepend_child(meta)
107
+ else
108
+ set_metadata_element(meta)
109
+ end
110
+ encoding
111
+ end
112
+ end
113
+
114
+ def meta_content_type
115
+ xpath("//meta[@http-equiv and boolean(@content)]").find do |node|
116
+ node["http-equiv"] =~ /\AContent-Type\z/i
117
+ end
118
+ end
119
+ private :meta_content_type
120
+
121
+ def set_metadata_element(element)
122
+ if (head = at_css("head"))
123
+ head << element
124
+ elsif (html = at_css("html"))
125
+ head = html.prepend_child(Nokolexbor::Node.new("head", self))
126
+ head.prepend_child(element)
127
+ elsif (first = children.find do |node|
128
+ case node
129
+ when Nokolexbor::Node
130
+ true
131
+ end
132
+ end)
133
+ # We reach here only if the underlying document model
134
+ # allows <html>/<head> elements to be omitted and does not
135
+ # automatically supply them.
136
+ first.add_previous_sibling(element)
137
+ else
138
+ html = add_child(Nokolexbor::Node.new("html", self))
139
+ head = html.add_child(Nokolexbor::Node.new("head", self))
140
+ head.prepend_child(element)
141
+ end
142
+ end
143
+
144
+ # Parse HTML into a {Document}.
145
+ #
146
+ # @param string_or_io [String, #read]
147
+ # The HTML to be parsed. It may be a String, or any object that
148
+ # responds to #read such as an IO, or StringIO.
149
+ #
150
+ # @return [Document]
151
+ def self.parse(string_or_io)
152
+ html = string_or_io
153
+ if string_or_io.respond_to?(:read)
154
+ html = string_or_io.read
155
+ end
156
+
157
+ if html.respond_to?(:encoding) && html.encoding != Encoding::UTF_8
158
+ html = html.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
159
+ end
160
+
161
+ parse_native(html)
162
+ end
163
+
164
+ private
165
+
166
+ IMPLIED_XPATH_CONTEXTS = ["//"].freeze # :nodoc:
167
+ end
168
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nokolexbor
4
+ class DocumentFragment < Nokolexbor::Node
5
+ # Create a {DocumentFragment} from +tags+.
6
+ #
7
+ # @return [DocumentFragment]
8
+ def self.parse(tags)
9
+ new(Nokolexbor::Document.new, tags, nil)
10
+ end
11
+
12
+ # Create a new {DocumentFragment} from +tags+.
13
+ #
14
+ # If +ctx+ is present, it is used as a context node for the
15
+ # subtree created.
16
+ def initialize(document, tags = nil, ctx = nil)
17
+ return self unless tags
18
+
19
+ ctx ||= document
20
+ node_set = ctx.parse(tags)
21
+ node_set.each { |child| child.parent = self } unless node_set.empty?
22
+ nil
23
+ end
24
+
25
+ # @return [String] The name of {DocumentFragment}
26
+ def name
27
+ "#document-fragment"
28
+ end
29
+
30
+ alias_method :outer_html, :inner_html
31
+ alias_method :to_html, :outer_html
32
+ alias_method :to_s, :outer_html
33
+ alias_method :serialize, :outer_html
34
+
35
+ # Create a {DocumentFragment} from +data+.
36
+ #
37
+ # @return [DocumentFragment]
38
+ def fragment(data)
39
+ document.fragment(data)
40
+ end
41
+ end
42
+ end