lib/xml/mixup.rb in xml-mixup-0.1.6 vs lib/xml/mixup.rb in xml-mixup-0.1.8
- old
+ new
@@ -302,13 +302,17 @@
# now go over the attributes and set any missing namespaces to nil
at.keys.each do |k|
p, _ = /^(?:([^:]+):)?(.+)$/.match(k).captures
ns[p] ||= nil
end
+
# also do the tag prefix but only if there is a local name
ns[prefix] ||= nil if local
+ # unconditionally remove ns['xml'], we never want it in there
+ ns.delete 'xml'
+
# pseudo is a stand-in for non-parent adjacent nodes
pseudo = nodes[:pseudo] || nodes[:parent]
# now get the final namespace mapping
ns.keys.each do |k|
@@ -322,11 +326,11 @@
ns.delete(nil)
end
# there should be no nil namespace declarations now
if ns.has_value? nil
- raise 'INTERNAL ERROR: nil namespace declaration'
+ raise "INTERNAL ERROR: nil namespace declaration: #{ns}"
end
# generate the node
node = element name, doc: doc, ns: ns, attr: at, args: args
@@ -371,34 +375,41 @@
# @param prefix [Hash] the contents of the root node's +prefix=+
# and +xmlns:*+ attributes.
#
# @param vocab [#to_s] the contents of the root node's +vocab=+.
#
- # @param lang [#to_s] the contents of +lang=+ and when applicable, +xml:lang+.
+ # @param lang [#to_s] the contents of +lang=+ and when applicable,
+ # +xml:lang+.
#
# @param title [#to_s, #to_a, Hash] the contents of the +<title>+
# tag. When given as an array-like object, all elements after the
# first one will be flattened to a single string and inserted into
# the +property=+ attribute. When given as a {Hash}, it will be
# coerced into a snippet of spec that produces the appropriate tag.
#
- # @param link [#to_a, Hash] A spec describing one or more +<link/>+ elements.
+ # @param link [#to_a, Hash] A spec describing one or more +<link/>+
+ # elements.
#
- # @param meta [#to_a, Hash] A spec describing one or more +<meta/>+ elements.
+ # @param meta [#to_a, Hash] A spec describing one or more +<meta/>+
+ # elements.
#
# @param style [#to_a, Hash] A spec describing one or more
# +<style/>+ elements.
#
# @param script [#to_a, Hash] A spec describing one or more
# +<script/>+ elements.
#
+ # @param extra [#to_a, Hash] A spec describing any extra elements
+ # inside +<head>+ that aren't in the previous categories.
+ #
# @param attr [Hash] A spec containing attributes for the +<body>+.
#
# @param content [Hash, Array, Nokogiri::XML::Node, ...] A spec which
# will be attached underneath the +<body>+.
#
- # @param head [Hash] A spec which overrides the entire +<head>+.
+ # @param head [Hash, Array] A Hash spec which overrides the entire
+ # +<head>+ element, or otherwise an array of its children.
#
# @param body [Hash] A spec which overrides the entire +<body>+.
#
# @param transform [#to_s] An optional XSLT transform.
#
@@ -416,83 +427,131 @@
#
# @return [Nokogiri::XML::Node] the last node generated, in document order.
def xhtml_stub doc: nil, base: nil, ns: {}, prefix: {}, vocab: nil,
lang: nil, title: nil, link: [], meta: [], style: [], script: [],
- head: {}, body: {}, attr: {}, content: [],
+ extra: [], head: {}, body: {}, attr: {}, content: [],
transform: nil, dtd: true, xmlns: true, args: []
spec = []
# add xslt stylesheet
if transform
spec << (transform.is_a?(Hash) ? transform :
- { nil => ['#pi', 'xml-stylesheet'],
- type: 'text/xsl', href: transform.to_s })
+ { '#pi' => 'xml-stylesheet', type: 'text/xsl', href: transform })
end
# add doctype declaration
if dtd
ps = dtd.respond_to?(:to_a) ? dtd.to_a : []
spec << { nil => %w{#dtd html} + ps }
end
+ # normalize title
+
+ if title.nil? or (title.respond_to?(:empty?) and title.empty?)
+ title = { '#title' => '' } # add an empty string for closing tag
+ elsif title.is_a? Hash
+ # nothing
+ elsif title.respond_to? :to_a
+ title = title.to_a
+ text = title.shift
+ props = title
+ title = { '#title' => text }
+ title[:property] = props unless props.empty?
+ else
+ title = { '#title' => title }
+ end
+
+ # normalize base
+
+ if base and not base.is_a? Hash
+ base = { nil => :base, href: base }
+ end
+
+ # TODO normalize link, meta, style, script elements
+
# construct document tree
head ||= {}
- if head.respond_to?(:empty) and head.empty?
- head[nil] = [:head, title, base, link, meta, style, script]
+ if head.is_a? Hash and head.empty?
+ head[nil] = [:head, title, base, link, meta, style, script, extra]
+ elsif head.is_a? Array
+ # children of unmarked head element
+ head = { nil => [:head] + head }
end
body ||= {}
- if body.respond_to?(:empty?) and body.empty?
+ if body.is_a? Hash and body.empty?
body[nil] = [:body, content]
+ body = attr.merge(body) if attr and attr.is_a?(Hash)
end
- root = { nil => [:html, [head, body]] }
+ root = { nil => [:html, head, body] }
root[:vocab] = vocab if vocab
root[:lang] = lang if lang
# deal with namespaces
if xmlns
root['xmlns'] = 'http://www.w3.org/1999/xhtml'
# namespaced language attribute
root['xml:lang'] = lang if lang
+
+ if !prefix and xmlns.is_a? Hash
+ x = prefix.transform_keys { |k| "xmlns:#{k}" }
+ root = x.merge(root)
+ end
end
# deal with prefixes distinct from namespaces
- if prefix
+ prefix ||= {}
+ if prefix.respond_to? :to_h
+ prefix = prefix.to_h
+ unless prefix.empty?
+ # this will get automatically smushed into the right shape
+ root[:prefix] = prefix
+
+ if xmlns
+ x = prefix.transform_keys { |k| "xmlns:#{k}" }
+ root = x.merge(root)
+ end
+ end
end
# add the document structure to the spec
spec << root
# as usual this will return the last innermost node
- markup spec: spec, doc: doc
+ markup spec: spec, doc: doc, args: args
end
private
def element tag, doc: nil, ns: {}, attr: {}, args: []
raise 'Document node must be present' unless doc
- prefix = local = nil
+ pfx = nil
if tag.respond_to? :to_a
- prefix, local = tag
- tag = tag[0..1].join ':'
+ pfx = tag[0]
+ tag = tag.to_a[0..1].join ':'
+ elsif m = tag.match(/([^:]+):/)
+ pfx = m[1]
end
+
elem = doc.create_element tag.to_s
- ns.sort.each do |p, u|
- elem.add_namespace((p.nil? ? p : p.to_s), u.to_s)
+ elem.default_namespace = ns[pfx] if ns[pfx]
+
+ ns.keys.sort { |a, b| a.to_s <=> b.to_s }.each do |p|
+ elem.add_namespace((p.nil? ? p : p.to_s), ns[p].to_s)
end
attr.sort.each do |k, v|
elem[k.to_s] = flatten(v, args)
end
elem
end
- ATOMS = [String, Symbol, Numeric, NilClass, FalseClass, TrueClass]
+ ATOMS = [String, Symbol, Numeric, NilClass, FalseClass, TrueClass].freeze
# yo dawg
def flatten obj, args
# early bailout for most likely condition