def sub_macros(source)
found = {}
found_square_bracket = found[:square_bracket] = (source.include? '[')
found_colon = source.include? ':'
found_macroish = found[:macroish] = found_square_bracket && found_colon
found_macroish_short = found_macroish && (source.include? ':[')
doc_attrs = @document.attributes
use_link_attrs = doc_attrs.key? 'linkattrs'
result = source
if doc_attrs.key? 'experimental'
if found_macroish_short && ((result.include? 'kbd:') || (result.include? 'btn:'))
result = result.gsub(InlineKbdBtnMacroRx) {
if $1
$&.slice 1, $&.length
elsif $2 == 'kbd'
if (keys = $3.strip).include? R_SB
keys = keys.gsub ESC_R_SB, R_SB
end
if keys.length > 1 && (delim_idx = (delim_idx = keys.index ',', 1) ?
[delim_idx, (keys.index '+', 1)].compact.min : (keys.index '+', 1))
delim = keys.slice delim_idx, 1
if keys.end_with? delim
keys = (keys.chop.split delim, -1).map {|key| key.strip }
keys[-1] = %(#{keys[-1]}#{delim})
else
keys = keys.split(delim).map {|key| key.strip }
end
else
keys = [keys]
end
(Inline.new self, :kbd, nil, :attributes => { 'keys' => keys }).convert
else
(Inline.new self, :button, (unescape_bracketed_text $3)).convert
end
}
end
if found_macroish && (result.include? 'menu:')
result = result.gsub(InlineMenuMacroRx) {
m = $~
if (captured = m[0]).start_with? RS
next captured[1..-1]
end
menu, items = m[1], m[2]
if items
items = items.gsub ESC_R_SB, R_SB if items.include? R_SB
if (delim = items.include?('>') ? '>' : (items.include?(',') ? ',' : nil))
submenus = items.split(delim).map {|it| it.strip }
menuitem = submenus.pop
else
submenus, menuitem = [], items.rstrip
end
else
submenus, menuitem = [], nil
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? RS
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? RS
next m[0][1..-1]
end
if (m.names rescue []).empty?
target, content, extconf = m[1], m[2], extension.config
else
target, content, extconf = (m[:target] rescue nil), (m[:content] rescue nil), extension.config
end
attributes = (attributes = extconf[:default_attrs]) ? attributes.dup : {}
if content.nil_or_empty?
attributes['text'] = content if content && extconf[:content_model] != :attributes
else
content = unescape_bracketed_text content
if extconf[:content_model] == :attributes
parse_attributes content, extconf[:pos_attrs] || [], :sub_result => false, :into => attributes
else
attributes['text'] = content
end
end
replacement = extension.process_method[self, target || content, attributes]
Inline === replacement ? replacement.convert : replacement
}
end
end
if found_macroish && ((result.include? 'image:') || (result.include? 'icon:'))
result = result.gsub(InlineImageMacroRx) {
m = $~
if (captured = $&).start_with? RS
next captured[1..-1]
end
if captured.start_with? 'icon:'
type, posattrs = 'icon', ['size']
else
type, posattrs = 'image', ['alt', 'width', 'height']
end
if (target = m[1]).include? ATTR_REF_HEAD
target = sub_attributes target
end
@document.register(:images, target) unless type == 'icon'
attrs = parse_attributes(m[2], posattrs, :unescape_input => true)
attrs['alt'] ||= (attrs['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
Inline.new(self, :image, nil, :type => type, :target => target, :attributes => attrs).convert
}
end
if ((result.include? '((') && (result.include? '))')) ||
(found_macroish_short && (result.include? 'indexterm'))
result = result.gsub(InlineIndextermMacroRx) {
m = $~
if m[0].start_with? RS
next m[0][1..-1]
end
case m[1]
when 'indexterm'
terms = split_simple_csv(normalize_string m[2], true)
@document.register :indexterms, terms
(Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
when 'indexterm2'
term = normalize_string m[2], true
@document.register :indexterms, [term]
(Inline.new self, :indexterm, term, :type => :visible).convert
else
text, visible, before, after = m[3], true, nil, nil
if text.start_with? '('
if text.end_with? ')'
text, visible = (text.slice 1, text.length - 2), false
else
text, before, after = (text.slice 1, text.length - 1), '(', ''
end
elsif text.end_with? ')'
if text.start_with? '('
text, visible = (text.slice 1, text.length - 2), false
else
text, before, after = (text.slice 0, text.length - 1), '', ')'
end
end
if visible
term = normalize_string text
@document.register :indexterms, [term]
result = (Inline.new self, :indexterm, term, :type => :visible).convert
else
terms = split_simple_csv(normalize_string text)
@document.register :indexterms, terms
result = (Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
end
before ? %(
end
}
end
if found_colon && (result.include? '://')
result = result.gsub(LinkInlineRx) {
m = $~
if m[2].start_with? RS
next %(
end
prefix, target, text, suffix = m[1], m[2], (macro = m[3]) || '', ''
if prefix == 'link:'
if macro
prefix = ''
else
next m[0]
end
end
unless macro || UriTerminatorRx !~ target
case $&
when ')'
target = target.chop
suffix = ')'
when ';'
if prefix.start_with?('<') && target.end_with?('>')
prefix = prefix[4..-1]
target = target[0...-4]
else
if (target = target.chop).end_with?(')')
target = target.chop
suffix = ');'
else
suffix = ';'
end
end
when ':'
if (target = target.chop).end_with?(')')
target = target.chop
suffix = '):'
else
suffix = ':'
end
end
end
attrs, link_opts = nil, { :type => :link }
unless text.empty?
text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
if use_link_attrs && ((text.start_with? '"') || ((text.include? ',') && (text.include? '=')))
attrs = parse_attributes text, []
link_opts[:id] = attrs.delete 'id' if attrs.key? 'id'
text = attrs[1] || ''
end
if text.end_with? '^'
text = text.chop
if attrs
attrs['window'] ||= '_blank'
else
attrs = { 'window' => '_blank' }
end
end
end
if text.empty?
text = (doc_attrs.key? 'hide-uri-scheme') ? (target.sub UriSniffRx, '') : target
if attrs
attrs['role'] = (attrs.key? 'role') ? %(bare #{attrs['role']}) : 'bare'
else
attrs = { 'role' => 'bare' }
end
end
@document.register :links, (link_opts[:target] = target)
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(InlineLinkMacroRx) {
m = $~
if m[0].start_with? RS
next m[0][1..-1]
end
target = (mailto = m[1]) ? %(mailto:#{m[2]}) : m[2]
attrs, link_opts = nil, { :type => :link }
unless (text = m[3]).empty?
text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
if use_link_attrs && ((text.start_with? '"') || ((text.include? ',') && (mailto || (text.include? '='))))
attrs = parse_attributes text, []
link_opts[:id] = attrs.delete 'id' if attrs.key? 'id'
if mailto
if attrs.key? 2
if attrs.key? 3
target = %(#{target}?subject=#{Helpers.uri_encode attrs[2]}&body=#{Helpers.uri_encode attrs[3]})
else
target = %(#{target}?subject=#{Helpers.uri_encode attrs[2]})
end
end
end
text = attrs[1] || ''
end
if text.end_with? '^'
text = text.chop
if attrs
attrs['window'] ||= '_blank'
else
attrs = { 'window' => '_blank' }
end
end
end
if text.empty?
if mailto
text = m[2]
else
text = (doc_attrs.key? 'hide-uri-scheme') ? (target.sub UriSniffRx, '') : target
if attrs
attrs['role'] = (attrs.key? 'role') ? %(bare #{attrs['role']}) : 'bare'
else
attrs = { 'role' => 'bare' }
end
end
end
@document.register :links, (link_opts[:target] = target)
link_opts[:attributes] = attrs if attrs
Inline.new(self, :anchor, text, link_opts).convert
}
end
if result.include? '@'
result = result.gsub(EmailInlineRx) {
address, tip = $&, $1
if tip
next (tip == RS ? address[1..-1] : address)
end
target = %(mailto:#{address})
@document.register(:links, target)
Inline.new(self, :anchor, address, :type => :link, :target => target).convert
}
end
if found_macroish_short && (result.include? 'footnote')
result = result.gsub(InlineFootnoteMacroRx) {
m = $~
if m[0].start_with? RS
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
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
else
if (footnote = @document.footnotes.find {|fn| fn.id == id })
index = footnote.index
text = footnote.text
else
index = nil
text = id
end
target = id
id = nil
type = :xref
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