def sub_macros(source)
return source if source.nil_or_empty?
found = {}
found[:square_bracket] = source.include?('[')
found[:round_bracket] = source.include?('(')
found[:colon] = found_colon = source.include?(':')
found[:macroish] = (found[:square_bracket] && found_colon)
found[:macroish_short_form] = (found[:square_bracket] && found_colon && source.include?(':['))
use_link_attrs = @document.attributes.has_key?('linkattrs')
experimental = @document.attributes.has_key?('experimental')
result = %(#{source})
if experimental
if found[:macroish_short_form] && (result.include?('kbd:') || result.include?('btn:'))
result = result.gsub(KbdBtnInlineMacroRx) {
m = $~
if (captured = m[0]).start_with? '\\'
next captured[1..-1]
end
if captured.start_with?('kbd')
keys = unescape_bracketed_text m[1]
if keys == '+'
keys = ['+']
else
keys = keys.split(KbdDelimiterRx).inject([]) {|c, key|
if key.end_with?('++')
c << key[0..-3].strip
c << '+'
else
c << key.strip
end
c
}
end
Inline.new(self, :kbd, nil, :attributes => {'keys' => keys}).convert
elsif captured.start_with?('btn')
label = unescape_bracketed_text m[1]
Inline.new(self, :button, label).convert
end
}
end
if found[:macroish] && result.include?('menu:')
result = result.gsub(MenuInlineMacroRx) {
m = $~
if (captured = m[0]).start_with? '\\'
next captured[1..-1]
end
menu = m[1]
items = m[2]
if !items
submenus = []
menuitem = nil
else
if (delim = items.include?('>') ? '>' : (items.include?(',') ? ',' : nil))
submenus = items.split(delim).map {|it| it.strip }
menuitem = submenus.pop
else
submenus = []
menuitem = items.rstrip
end
end
Inline.new(self, :menu, nil, :attributes => {'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem}).convert
}
end
if result.include?('"') && result.include?('>')
result = result.gsub(MenuInlineRx) {
m = $~
if (captured = m[0]).start_with? '\\'
next captured[1..-1]
end
input = m[1]
menu, *submenus = input.split('>').map {|it| it.strip }
menuitem = submenus.pop
Inline.new(self, :menu, nil, :attributes => {'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem}).convert
}
end
end
if (extensions = @document.extensions) && extensions.inline_macros?
extensions.inline_macros.each do |extension|
result = result.gsub(extension.instance.regexp) {
m = $~
if m[0].start_with? '\\'
next m[0][1..-1]
end
target = m[1]
attributes = if extension.config[:format] == :short
{}
else
if extension.config[:content_model] == :attributes
parse_attributes m[2], (extension.config[:pos_attrs] || []), :sub_input => true, :unescape_input => true
else
{ 'text' => (unescape_bracketed_text m[2]) }
end
end
extension.process_method[self, target, attributes]
}
end
end
if found[:macroish] && (result.include?('image:') || result.include?('icon:'))
result = result.gsub(ImageInlineMacroRx) {
m = $~
if m[0].start_with? '\\'
next m[0][1..-1]
end
raw_attrs = unescape_bracketed_text m[2]
if m[0].start_with? 'icon:'
type = 'icon'
posattrs = ['size']
else
type = 'image'
posattrs = ['alt', 'width', 'height']
end
target = sub_attributes(m[1])
unless type == 'icon'
@document.register(:images, target)
end
attrs = parse_attributes(raw_attrs, posattrs)
attrs['alt'] ||= Helpers.basename(target, true).tr('_-', ' ')
Inline.new(self, :image, nil, :type => type, :target => target, :attributes => attrs).convert
}
end
if found[:macroish_short_form] || found[:round_bracket]
result = result.gsub(IndextermInlineMacroRx) {
m = $~
if m[0].start_with? '\\'
next m[0][1..-1]
end
if ::RUBY_ENGINE_OPAL
m[1] = nil if m[1] == ''
end
num_brackets = 0
text_in_brackets = nil
unless (macro_name = m[1])
text_in_brackets = m[3]
if (text_in_brackets.start_with? '(') && (text_in_brackets.end_with? ')')
text_in_brackets = text_in_brackets[1...-1]
num_brackets = 3
else
num_brackets = 2
end
end
if macro_name == 'indexterm' || num_brackets == 3
if !macro_name
terms = split_simple_csv normalize_string(text_in_brackets)
else
terms = split_simple_csv normalize_string(m[2], true)
end
@document.register(:indexterms, [*terms])
Inline.new(self, :indexterm, nil, :attributes => {'terms' => terms}).convert
else
if !macro_name
text = normalize_string text_in_brackets
else
text = normalize_string m[2], true
end
@document.register(:indexterms, [text])
Inline.new(self, :indexterm, text, :type => :visible).convert
end
}
end
if found_colon && (result.include? '://')
result = result.gsub(LinkInlineRx) {
m = $~
if m[2].start_with? '\\'
next "#{m[1]}#{m[2][1..-1]}#{m[3]}"
end
if ::RUBY_ENGINE_OPAL
m[3] = nil if m[3] == ''
end
if m[1] == 'link:' && !m[3]
next m[0]
end
prefix = (m[1] != 'link:' ? m[1] : '')
target = m[2]
suffix = ''
unless m[3] || target !~ UriTerminator
case $~[0]
when ')'
target = target[0..-2]
suffix = ')'
when ';'
if prefix.start_with?('<') && target.end_with?('>')
prefix = prefix[4..-1]
target = target[0..-5]
elsif target.end_with?(');')
target = target[0..-3]
suffix = ');'
else
target = target[0..-2]
suffix = ';'
end
when ':'
if target.end_with?('):')
target = target[0..-3]
suffix = '):'
else
target = target[0..-2]
suffix = ':'
end
end
end
@document.register(:links, target)
link_opts = { :type => :link, :target => target }
attrs = nil
if m[3].nil_or_empty?
text = ''
else
if use_link_attrs && (m[3].start_with?('"') || (m[3].include?(',') && m[3].include?('=')))
attrs = parse_attributes(sub_attributes(m[3].gsub('\]', ']')), [])
link_opts[:id] = (attrs.delete 'id') if attrs.has_key? 'id'
text = attrs[1] || ''
else
text = sub_attributes(m[3].gsub('\]', ']'))
end
if text.end_with? '^'
text = text.chop
if attrs
attrs['window'] ||= '_blank'
else
attrs = {'window' => '_blank'}
end
end
end
if text.empty?
if @document.attr? 'hide-uri-scheme'
text = target.sub UriSniffRx, ''
else
text = target
end
if attrs
attrs['role'] = %(bare #{attrs['role']}).chomp ' '
else
attrs = {'role' => 'bare'}
end
end
link_opts[:attributes] = attrs if attrs
%(#{prefix}#{Inline.new(self, :anchor, text, link_opts).convert}#{suffix})
}
end
if found[:macroish] && (result.include? 'link:') || (result.include? 'mailto:')
result = result.gsub(LinkInlineMacroRx) {
m = $~
if m[0].start_with? '\\'
next m[0][1..-1]
end
raw_target = m[1]
mailto = m[0].start_with?('mailto:')
target = mailto ? %(mailto
link_opts = { :type => :link, :target => target }
attrs = nil
text = if use_link_attrs && (m[2].start_with?('"') || m[2].include?(','))
attrs = parse_attributes(sub_attributes(m[2].gsub('\]', ']')), [])
link_opts[:id] = (attrs.delete 'id') if attrs.key? 'id'
if mailto
if attrs.key? 2
target = link_opts[:target] = "#{target}?subject=#{Helpers.encode_uri(attrs[2])}"
if attrs.key? 3
target = link_opts[:target] = "#{target}&body=#{Helpers.encode_uri(attrs[3])}"
end
end
end
attrs[1]
else
sub_attributes(m[2].gsub('\]', ']'))
end
@document.register(:links, target)
if text.end_with? '^'
text = text.chop
if attrs
attrs['window'] ||= '_blank'
else
attrs = {'window' => '_blank'}
end
end
if text.empty?
if mailto
text = raw_target
else
if @document.attr? 'hide-uri-scheme'
text = raw_target.sub UriSniffRx, ''
else
text = raw_target
end
if attrs
attrs['role'] = %(bare #{attrs['role']}).chomp ' '
else
attrs = {'role' => 'bare'}
end
end
end
link_opts[:attributes] = attrs if attrs
Inline.new(self, :anchor, text, link_opts).convert
}
end
if result.include? '@'
result = result.gsub(EmailInlineMacroRx) {
m = $~
address = m[0]
if (lead = m[1])
case lead
when '\\'
next address[1..-1]
else
next address
end
end
target = %(mailto:#{address})
@document.register(:links, target)
Inline.new(self, :anchor, address, :type => :link, :target => target).convert
}
end
if found[:macroish_short_form] && result.include?('footnote')
result = result.gsub(FootnoteInlineMacroRx) {
m = $~
if m[0].start_with? '\\'
next m[0][1..-1]
end
if m[1] == 'footnote'
id = nil
text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string m[2], true)), false)
index = @document.counter('footnote-number')
@document.register(:footnotes, Document::Footnote.new(index, id, text))
type = nil
target = nil
else
id, text = m[2].split(',', 2)
id = id.strip
if text.nil_or_empty?
if (footnote = @document.references[:footnotes].find {|fn| fn.id == id })
index = footnote.index
text = footnote.text
else
index = nil
text = id
end
target = id
id = nil
type = :xref
else
text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)), false)
index = @document.counter('footnote-number')
@document.register(:footnotes, Document::Footnote.new(index, id, text))
type = :ref
target = nil
end
end
Inline.new(self, :footnote, text, :attributes => {'index' => index}, :id => id, :target => target, :type => type).convert
}
end
sub_inline_xrefs(sub_inline_anchors(result, found), found)
end