require "date" require "htmlentities" module Asciidoctor module Standoc module Cleanup def para_cleanup(xmldoc) ["//p[not(ancestor::bibdata)]", "//ol[not(ancestor::bibdata)]", "//ul[not(ancestor::bibdata)]", "//quote[not(ancestor::bibdata)]", "//note[not(ancestor::bibitem or ancestor::table or ancestor::bibdata)]" ].each { |w| inject_id(xmldoc, w) } end def inject_id(xmldoc, path) xmldoc.xpath(path).each do |x| x["id"] ||= Metanorma::Utils::anchor_or_uuid end end def dl1_table_cleanup(xmldoc) q = "//table/following-sibling::*[1][self::dl]" xmldoc.xpath(q).each do |s| s["key"] == "true" and s.previous_element << s.remove end end # move Key dl after table footer def dl2_table_cleanup(xmldoc) q = "//table/following-sibling::*[1][self::p]" xmldoc.xpath(q).each do |s| if s.text =~ /^\s*key[^a-z]*$/i && s&.next_element&.name == "dl" s.next_element["key"] = "true" s.previous_element << s.next_element.remove s.remove end end end def insert_thead(table) thead = table.at("./thead") return thead unless thead.nil? if tname = table.at("./name") thead = tname.add_next_sibling("").first return thead end table.children.first.add_previous_sibling("").first end def header_rows_cleanup(xmldoc) xmldoc.xpath("//table[@headerrows]").each do |s| thead = insert_thead(s) (thead.xpath("./tr").size...s["headerrows"].to_i).each do row = s.at("./tbody/tr") row.parent = thead end thead.xpath(".//td").each { |n| n.name = "th" } s.delete("headerrows") end end def table_cleanup(xmldoc) dl1_table_cleanup(xmldoc) dl2_table_cleanup(xmldoc) notes_table_cleanup(xmldoc) header_rows_cleanup(xmldoc) end # move notes into table def notes_table_cleanup(xmldoc) nomatches = false until nomatches q = "//table/following-sibling::*[1][self::note]" nomatches = true xmldoc.xpath(q).each do |n| n.previous_element << n.remove nomatches = false end end end # include where definition list inside stem block def formula_cleanup(formula) formula_cleanup_where1(formula) formula_cleanup_where2(formula) end def formula_cleanup_where1(formula) q = "//formula/following-sibling::*[1][self::dl]" formula.xpath(q).each do |s| s["key"] == "true" and s.previous_element << s.remove end end def formula_cleanup_where2(formula) q = "//formula/following-sibling::*[1][self::p]" formula.xpath(q).each do |s| if s.text =~ /^\s*where[^a-z]*$/i && s&.next_element&.name == "dl" s.next_element["key"] = "true" s.previous_element << s.next_element.remove s.remove end end end def figure_dl_cleanup1(xmldoc) q = "//figure/following-sibling::*[self::dl]" xmldoc.xpath(q).each do |s| s["key"] == "true" and s.previous_element << s.remove end end # include key definition list inside figure def figure_dl_cleanup2(xmldoc) q = "//figure/following-sibling::*[self::p]" xmldoc.xpath(q).each do |s| if s.text =~ /^\s*key[^a-z]*$/i && s&.next_element&.name == "dl" s.next_element["key"] = "true" s.previous_element << s.next_element.remove s.remove end end end # examples containing only figures become subfigures of figures def subfigure_cleanup(xmldoc) xmldoc.xpath("//example[figure]").each do |e| next unless e.elements.map(&:name).reject do |m| %w(name figure).include? m end.empty? e.name = "figure" end end def figure_cleanup(xmldoc) figure_footnote_cleanup(xmldoc) figure_dl_cleanup1(xmldoc) figure_dl_cleanup2(xmldoc) subfigure_cleanup(xmldoc) end ELEMS_ALLOW_NOTES = %w[p formula ul ol dl figure].freeze # if a note is at the end of a section, it is left alone # if a note is followed by a non-note block, # it is moved inside its preceding block if it is not delimited # (so there was no way of making that block include the note) def note_cleanup(xmldoc) q = "//note[following-sibling::*[not(local-name() = 'note')]]" xmldoc.xpath(q).each do |n| next if n["keep-separate"] == "true" next unless n.ancestors("table").empty? prev = n.previous_element || next n.parent = prev if ELEMS_ALLOW_NOTES.include? prev.name end xmldoc.xpath("//note[@keep-separate] | "\ "//termnote[@keep-separate]").each do |n| n.delete("keep-separate") end end def link_callouts_to_annotations(callouts, annotations) callouts.each_with_index do |c, i| c["target"] = "_#{UUIDTools::UUID.random_create}" annotations[i]["id"] = c["target"] end end def align_callouts_to_annotations(xmldoc) xmldoc.xpath("//sourcecode").each do |x| callouts = x.elements.select { |e| e.name == "callout" } annotations = x.elements.select { |e| e.name == "annotation" } callouts.size == annotations.size and link_callouts_to_annotations(callouts, annotations) end end def merge_annotations_into_sourcecode(xmldoc) xmldoc.xpath("//sourcecode").each do |x| while x&.next_element&.name == "annotation" x.next_element.parent = x end end end def callout_cleanup(xmldoc) merge_annotations_into_sourcecode(xmldoc) align_callouts_to_annotations(xmldoc) end def sourcecode_cleanup(xmldoc) xmldoc.xpath("//sourcecode").each do |x| x.traverse do |n| next unless n.text? next unless /#{Regexp.escape(@sourcecode_markup_start)}/ .match?(n.text) n.replace(sourcecode_markup(n)) end end end def safe_noko(text, doc) Nokogiri::XML::Text.new(text, doc).to_xml( encoding: "US-ASCII", save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION ) end def sourcecode_markup(node) acc = [] node.text.split(/(#{Regexp.escape(@sourcecode_markup_start)}| #{Regexp.escape(@sourcecode_markup_end)})/x) .each_slice(4).map do |a| acc << safe_noko(a[0], node.document) next unless a.size == 4 acc << Asciidoctor.convert( a[2], doctype: :inline, backend: (self&.backend&.to_sym || :standoc) ) end acc.join end def form_cleanup(xmldoc) xmldoc.xpath("//select").each do |s| while s&.next_element&.name == "option" s << s.next_element end end end end end end