asciidoctor 1.5.5 → 1.5.6
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.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +216 -1
- data/CONTRIBUTING.adoc +2 -2
- data/Gemfile +20 -1
- data/LICENSE.adoc +1 -1
- data/README-fr.adoc +4 -3
- data/README-jp.adoc +11 -10
- data/README-zh_CN.adoc +4 -3
- data/README.adoc +17 -202
- data/Rakefile +41 -25
- data/asciidoctor.gemspec +9 -10
- data/data/locale/attributes.adoc +216 -34
- data/data/stylesheets/asciidoctor-default.css +23 -16
- data/features/step_definitions.rb +15 -19
- data/features/xref.feature +584 -20
- data/lib/asciidoctor.rb +292 -278
- data/lib/asciidoctor/abstract_block.rb +155 -94
- data/lib/asciidoctor/abstract_node.rb +108 -94
- data/lib/asciidoctor/attribute_list.rb +30 -22
- data/lib/asciidoctor/block.rb +7 -7
- data/lib/asciidoctor/cli/invoker.rb +47 -34
- data/lib/asciidoctor/cli/options.rb +22 -11
- data/lib/asciidoctor/converter.rb +3 -3
- data/lib/asciidoctor/converter/base.rb +2 -2
- data/lib/asciidoctor/converter/composite.rb +1 -1
- data/lib/asciidoctor/converter/docbook45.rb +2 -2
- data/lib/asciidoctor/converter/docbook5.rb +132 -87
- data/lib/asciidoctor/converter/factory.rb +0 -1
- data/lib/asciidoctor/converter/html5.rb +116 -98
- data/lib/asciidoctor/converter/manpage.rb +51 -52
- data/lib/asciidoctor/converter/template.rb +47 -36
- data/lib/asciidoctor/core_ext.rb +8 -2
- data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +4 -0
- data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +6 -0
- data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +5 -0
- data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +1 -1
- data/lib/asciidoctor/core_ext/1.8.7/string/{limit.rb → limit_bytesize.rb} +7 -6
- data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +6 -0
- data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +1 -1
- data/lib/asciidoctor/core_ext/nil_or_empty.rb +5 -5
- data/lib/asciidoctor/core_ext/regexp/is_match.rb +3 -0
- data/lib/asciidoctor/core_ext/string/{limit.rb → limit_bytesize.rb} +2 -2
- data/lib/asciidoctor/document.rb +216 -213
- data/lib/asciidoctor/extensions.rb +318 -185
- data/lib/asciidoctor/helpers.rb +35 -35
- data/lib/asciidoctor/inline.rb +32 -1
- data/lib/asciidoctor/list.rb +22 -6
- data/lib/asciidoctor/parser.rb +1008 -1038
- data/lib/asciidoctor/path_resolver.rb +46 -50
- data/lib/asciidoctor/reader.rb +275 -251
- data/lib/asciidoctor/section.rb +86 -58
- data/lib/asciidoctor/stylesheets.rb +6 -6
- data/lib/asciidoctor/substitutors.rb +567 -649
- data/lib/asciidoctor/table.rb +163 -108
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +18 -16
- data/man/asciidoctor.adoc +15 -13
- data/test/attributes_test.rb +138 -22
- data/test/blocks_test.rb +377 -97
- data/test/converter_test.rb +13 -0
- data/test/document_test.rb +244 -34
- data/test/extensions_test.rb +409 -42
- data/test/fixtures/asciidoc_index.txt +521 -0
- data/test/fixtures/basic-docinfo-footer.html +6 -0
- data/test/fixtures/basic-docinfo-footer.xml +8 -0
- data/test/fixtures/basic-docinfo.html +1 -0
- data/test/fixtures/basic-docinfo.xml +4 -0
- data/test/fixtures/basic.asciidoc +5 -0
- data/test/fixtures/chapter-a.adoc +3 -0
- data/test/fixtures/child-include.adoc +5 -0
- data/test/fixtures/circle.svg +9 -0
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
- data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
- data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
- data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
- data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
- data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
- data/test/fixtures/custom-docinfodir/basic-docinfo.html +1 -0
- data/test/fixtures/custom-docinfodir/docinfo.html +1 -0
- data/test/fixtures/docinfo-footer.html +1 -0
- data/test/fixtures/docinfo-footer.xml +9 -0
- data/test/fixtures/docinfo.html +1 -0
- data/test/fixtures/docinfo.xml +3 -0
- data/test/fixtures/dot.gif +0 -0
- data/test/fixtures/encoding.asciidoc +13 -0
- data/test/fixtures/grandchild-include.adoc +3 -0
- data/test/fixtures/hello-asciidoctor.pdf +69 -0
- data/test/fixtures/include-file.asciidoc +24 -0
- data/test/fixtures/include-file.ml +3 -0
- data/test/fixtures/include-file.xml +5 -0
- data/test/fixtures/master.adoc +5 -0
- data/test/fixtures/mismatched-end-tag.adoc +7 -0
- data/test/fixtures/parent-include-restricted.adoc +5 -0
- data/test/fixtures/parent-include.adoc +5 -0
- data/test/fixtures/sample.asciidoc +26 -0
- data/test/fixtures/stylesheets/custom.css +3 -0
- data/test/fixtures/subs-docinfo.html +2 -0
- data/test/fixtures/subs.adoc +7 -0
- data/test/fixtures/tagged-class-enclosed.rb +26 -0
- data/test/fixtures/tagged-class.rb +23 -0
- data/test/fixtures/tip.gif +0 -0
- data/test/invoker_test.rb +82 -4
- data/test/links_test.rb +312 -37
- data/test/lists_test.rb +204 -25
- data/test/manpage_test.rb +191 -4
- data/test/options_test.rb +18 -1
- data/test/paragraphs_test.rb +32 -7
- data/test/parser_test.rb +150 -30
- data/test/paths_test.rb +47 -13
- data/test/preamble_test.rb +1 -1
- data/test/reader_test.rb +366 -126
- data/test/sections_test.rb +203 -56
- data/test/substitutions_test.rb +339 -131
- data/test/tables_test.rb +315 -15
- data/test/test_helper.rb +400 -0
- data/test/text_test.rb +5 -5
- metadata +110 -22
    
        data/lib/asciidoctor/section.rb
    CHANGED
    
    | @@ -24,10 +24,6 @@ class Section < AbstractBlock | |
| 24 24 | 
             
              # Public: Get/Set the 0-based index order of this section within the parent block
         | 
| 25 25 | 
             
              attr_accessor :index
         | 
| 26 26 |  | 
| 27 | 
            -
              # Public: Get/Set the number of this section within the parent block
         | 
| 28 | 
            -
              # Only relevant if the attribute numbered is true
         | 
| 29 | 
            -
              attr_accessor :number
         | 
| 30 | 
            -
             | 
| 31 27 | 
             
              # Public: Get/Set the section name of this section
         | 
| 32 28 | 
             
              attr_accessor :sectname
         | 
| 33 29 |  | 
| @@ -37,16 +33,15 @@ class Section < AbstractBlock | |
| 37 33 | 
             
              # Public: Get the state of the numbered attribute at this section (need to preserve for creating TOC)
         | 
| 38 34 | 
             
              attr_accessor :numbered
         | 
| 39 35 |  | 
| 36 | 
            +
              # Public: Get the caption for this section (only relevant for appendices)
         | 
| 37 | 
            +
              attr_reader :caption
         | 
| 38 | 
            +
             | 
| 40 39 | 
             
              # Public: Initialize an Asciidoctor::Section object.
         | 
| 41 40 | 
             
              #
         | 
| 42 41 | 
             
              # parent - The parent Asciidoc Object.
         | 
| 43 42 | 
             
              def initialize parent = nil, level = nil, numbered = true, opts = {}
         | 
| 44 43 | 
             
                super parent, :section, opts
         | 
| 45 | 
            -
                 | 
| 46 | 
            -
                  @level = level
         | 
| 47 | 
            -
                else
         | 
| 48 | 
            -
                  @level = parent ? (parent.level + 1) : 1
         | 
| 49 | 
            -
                end
         | 
| 44 | 
            +
                @level = level ? level : (parent ? (parent.level + 1) : 1)
         | 
| 50 45 | 
             
                @numbered = numbered && @level > 0
         | 
| 51 46 | 
             
                @special = parent && parent.context == :section && parent.special
         | 
| 52 47 | 
             
                @index = 0
         | 
| @@ -54,54 +49,13 @@ class Section < AbstractBlock | |
| 54 49 | 
             
              end
         | 
| 55 50 |  | 
| 56 51 | 
             
              # Public: The name of this section, an alias of the section title
         | 
| 57 | 
            -
              alias  | 
| 52 | 
            +
              alias name title
         | 
| 58 53 |  | 
| 59 | 
            -
              # Public: Generate a String  | 
| 60 | 
            -
              #
         | 
| 61 | 
            -
              # The generated id is prefixed with value of the 'idprefix' attribute, which
         | 
| 62 | 
            -
              # is an underscore by default.
         | 
| 63 | 
            -
              #
         | 
| 64 | 
            -
              # Section id synthesis can be disabled by undefining the 'sectids' attribute.
         | 
| 54 | 
            +
              # Public: Generate a String ID from the title of this section.
         | 
| 65 55 | 
             
              #
         | 
| 66 | 
            -
              #  | 
| 67 | 
            -
              # until a unique id is found.
         | 
| 68 | 
            -
              #
         | 
| 69 | 
            -
              # Examples
         | 
| 70 | 
            -
              #
         | 
| 71 | 
            -
              #   section = Section.new(parent)
         | 
| 72 | 
            -
              #   section.title = "Foo"
         | 
| 73 | 
            -
              #   section.generate_id
         | 
| 74 | 
            -
              #   => "_foo"
         | 
| 75 | 
            -
              #
         | 
| 76 | 
            -
              #   another_section = Section.new(parent)
         | 
| 77 | 
            -
              #   another_section.title = "Foo"
         | 
| 78 | 
            -
              #   another_section.generate_id
         | 
| 79 | 
            -
              #   => "_foo_1"
         | 
| 80 | 
            -
              #
         | 
| 81 | 
            -
              #   yet_another_section = Section.new(parent)
         | 
| 82 | 
            -
              #   yet_another_section.title = "Ben & Jerry"
         | 
| 83 | 
            -
              #   yet_another_section.generate_id
         | 
| 84 | 
            -
              #   => "_ben_jerry"
         | 
| 56 | 
            +
              # See Section.generate_id for details.
         | 
| 85 57 | 
             
              def generate_id
         | 
| 86 | 
            -
                 | 
| 87 | 
            -
                  sep = @document.attributes['idseparator'] || '_'
         | 
| 88 | 
            -
                  pre = @document.attributes['idprefix'] || '_'
         | 
| 89 | 
            -
                  base_id = %(#{pre}#{title.downcase.gsub(InvalidSectionIdCharsRx, sep).tr_s(sep, sep).chomp(sep)})
         | 
| 90 | 
            -
                  # ensure id doesn't begin with idseparator if idprefix is empty and idseparator is not empty
         | 
| 91 | 
            -
                  if pre.empty? && !sep.empty? && base_id.start_with?(sep)
         | 
| 92 | 
            -
                    base_id = base_id[1..-1]
         | 
| 93 | 
            -
                    base_id = base_id[1..-1] while base_id.start_with?(sep)
         | 
| 94 | 
            -
                  end
         | 
| 95 | 
            -
                  gen_id = base_id
         | 
| 96 | 
            -
                  cnt = Compliance.unique_id_start_index
         | 
| 97 | 
            -
                  while @document.references[:ids].has_key? gen_id
         | 
| 98 | 
            -
                    gen_id = %(#{base_id}#{sep}#{cnt})
         | 
| 99 | 
            -
                    cnt += 1
         | 
| 100 | 
            -
                  end
         | 
| 101 | 
            -
                  gen_id
         | 
| 102 | 
            -
                else
         | 
| 103 | 
            -
                  nil
         | 
| 104 | 
            -
                end
         | 
| 58 | 
            +
                Section.generate_id title, @document
         | 
| 105 59 | 
             
              end
         | 
| 106 60 |  | 
| 107 61 | 
             
              # Public: Get the section number for the current Section
         | 
| @@ -156,6 +110,41 @@ class Section < AbstractBlock | |
| 156 110 | 
             
                end
         | 
| 157 111 | 
             
              end
         | 
| 158 112 |  | 
| 113 | 
            +
              # (see AbstractBlock#xreftext)
         | 
| 114 | 
            +
              def xreftext xrefstyle = nil
         | 
| 115 | 
            +
                if (val = reftext) && !val.empty?
         | 
| 116 | 
            +
                  val
         | 
| 117 | 
            +
                elsif xrefstyle
         | 
| 118 | 
            +
                  if @numbered
         | 
| 119 | 
            +
                    case xrefstyle
         | 
| 120 | 
            +
                    when 'full'
         | 
| 121 | 
            +
                      if (type = @sectname) == 'chapter' || type == 'appendix'
         | 
| 122 | 
            +
                        quoted_title = sprintf sub_quotes('_%s_'), title
         | 
| 123 | 
            +
                      else
         | 
| 124 | 
            +
                        quoted_title = sprintf sub_quotes(@document.compat_mode ? %q(``%s'') : '"`%s`"'), title
         | 
| 125 | 
            +
                      end
         | 
| 126 | 
            +
                      if (signifier = @document.attributes[%(#{type}-refsig)])
         | 
| 127 | 
            +
                        %(#{signifier} #{sectnum '.', ','} #{quoted_title})
         | 
| 128 | 
            +
                      else
         | 
| 129 | 
            +
                        %(#{sectnum '.', ','} #{quoted_title})
         | 
| 130 | 
            +
                      end
         | 
| 131 | 
            +
                    when 'short'
         | 
| 132 | 
            +
                      if (signifier = @document.attributes[%(#{@sectname}-refsig)])
         | 
| 133 | 
            +
                        %(#{signifier} #{sectnum '.', ''})
         | 
| 134 | 
            +
                      else
         | 
| 135 | 
            +
                        sectnum '.', ''
         | 
| 136 | 
            +
                      end
         | 
| 137 | 
            +
                    else # 'basic'
         | 
| 138 | 
            +
                      (type = @sectname) == 'chapter' || type == 'appendix' ? (sprintf sub_quotes('_%s_'), title) : title
         | 
| 139 | 
            +
                    end
         | 
| 140 | 
            +
                  else # apply basic styling
         | 
| 141 | 
            +
                    (type = @sectname) == 'chapter' || type == 'appendix' ? (sprintf sub_quotes('_%s_'), title) : title
         | 
| 142 | 
            +
                  end
         | 
| 143 | 
            +
                else
         | 
| 144 | 
            +
                  title
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
              end
         | 
| 147 | 
            +
             | 
| 159 148 | 
             
              # Public: Append a content block to this block's list of blocks.
         | 
| 160 149 | 
             
              #
         | 
| 161 150 | 
             
              # If the child block is a Section, assign an index to it.
         | 
| @@ -164,17 +153,56 @@ class Section < AbstractBlock | |
| 164 153 | 
             
              #
         | 
| 165 154 | 
             
              # Returns The parent Block
         | 
| 166 155 | 
             
              def << block
         | 
| 167 | 
            -
                 | 
| 156 | 
            +
                enumerate_section block if block.context == :section
         | 
| 168 157 | 
             
                super
         | 
| 169 158 | 
             
              end
         | 
| 170 159 |  | 
| 171 160 | 
             
              def to_s
         | 
| 172 | 
            -
                if @title | 
| 173 | 
            -
                   | 
| 174 | 
            -
                  %(#<#{self.class}@#{object_id} {level: #{@level}, title: #{ | 
| 161 | 
            +
                if @title
         | 
| 162 | 
            +
                  formal_title = @numbered ? %(#{sectnum} #{@title}) : @title
         | 
| 163 | 
            +
                  %(#<#{self.class}@#{object_id} {level: #{@level}, title: #{formal_title.inspect}, blocks: #{@blocks.size}}>)
         | 
| 175 164 | 
             
                else
         | 
| 176 165 | 
             
                  super
         | 
| 177 166 | 
             
                end
         | 
| 178 167 | 
             
              end
         | 
| 168 | 
            +
             | 
| 169 | 
            +
              # Public: Generate a String ID from the given section title.
         | 
| 170 | 
            +
              #
         | 
| 171 | 
            +
              # The generated ID is prefixed with value of the 'idprefix' attribute, which
         | 
| 172 | 
            +
              # is an underscore by default. Invalid characters are replaced with the
         | 
| 173 | 
            +
              # value of the 'idseparator' attribute, which is an underscore by default.
         | 
| 174 | 
            +
              #
         | 
| 175 | 
            +
              # If the generated ID is already in use in the document, a count is appended
         | 
| 176 | 
            +
              # until a unique id is found.
         | 
| 177 | 
            +
              #
         | 
| 178 | 
            +
              # Section ID generation can be disabled by undefining the 'sectids' attribute.
         | 
| 179 | 
            +
              #
         | 
| 180 | 
            +
              # Examples
         | 
| 181 | 
            +
              #
         | 
| 182 | 
            +
              #   Section.generate_id 'Foo', document
         | 
| 183 | 
            +
              #   => "_foo"
         | 
| 184 | 
            +
              #
         | 
| 185 | 
            +
              def self.generate_id title, document
         | 
| 186 | 
            +
                attrs = document.attributes
         | 
| 187 | 
            +
                sep = attrs['idseparator'] || '_'
         | 
| 188 | 
            +
                pre = attrs['idprefix'] || '_'
         | 
| 189 | 
            +
                gen_id = %(#{pre}#{title.downcase.gsub InvalidSectionIdCharsRx, sep})
         | 
| 190 | 
            +
                unless sep.empty?
         | 
| 191 | 
            +
                  # remove repeat and trailing separator characters
         | 
| 192 | 
            +
                  gen_id = gen_id.tr_s sep, sep
         | 
| 193 | 
            +
                  gen_id = gen_id.chop if gen_id.end_with? sep
         | 
| 194 | 
            +
                  # ensure id doesn't begin with idseparator if idprefix is empty and idseparator is not empty
         | 
| 195 | 
            +
                  if pre.empty?
         | 
| 196 | 
            +
                    gen_id = gen_id.slice 1, gen_id.length while gen_id.start_with? sep
         | 
| 197 | 
            +
                  end
         | 
| 198 | 
            +
                end
         | 
| 199 | 
            +
                if document.catalog[:ids].key? gen_id
         | 
| 200 | 
            +
                  ids, cnt = document.catalog[:ids], Compliance.unique_id_start_index
         | 
| 201 | 
            +
                  cnt += 1 while ids.key?(candidate_id = %(#{gen_id}#{sep}#{cnt}))
         | 
| 202 | 
            +
                  candidate_id
         | 
| 203 | 
            +
                else
         | 
| 204 | 
            +
                  gen_id
         | 
| 205 | 
            +
                end
         | 
| 206 | 
            +
              end
         | 
| 179 207 | 
             
            end
         | 
| 180 208 | 
             
            end
         | 
| @@ -32,8 +32,8 @@ class Stylesheets | |
| 32 32 | 
             
            </style>)
         | 
| 33 33 | 
             
              end
         | 
| 34 34 |  | 
| 35 | 
            -
              def write_primary_stylesheet target_dir
         | 
| 36 | 
            -
                :: | 
| 35 | 
            +
              def write_primary_stylesheet target_dir = '.'
         | 
| 36 | 
            +
                ::IO.write(::File.join(target_dir, primary_stylesheet_name), primary_stylesheet_data)
         | 
| 37 37 | 
             
              end
         | 
| 38 38 |  | 
| 39 39 | 
             
              def coderay_stylesheet_name
         | 
| @@ -57,8 +57,8 @@ class Stylesheets | |
| 57 57 | 
             
            </style>)
         | 
| 58 58 | 
             
              end
         | 
| 59 59 |  | 
| 60 | 
            -
              def write_coderay_stylesheet target_dir
         | 
| 61 | 
            -
                :: | 
| 60 | 
            +
              def write_coderay_stylesheet target_dir = '.'
         | 
| 61 | 
            +
                ::IO.write(::File.join(target_dir, coderay_stylesheet_name), coderay_stylesheet_data)
         | 
| 62 62 | 
             
              end
         | 
| 63 63 |  | 
| 64 64 | 
             
              def pygments_stylesheet_name style = nil
         | 
| @@ -84,8 +84,8 @@ class Stylesheets | |
| 84 84 | 
             
            </style>)
         | 
| 85 85 | 
             
              end
         | 
| 86 86 |  | 
| 87 | 
            -
              def write_pygments_stylesheet target_dir, style = nil
         | 
| 88 | 
            -
                :: | 
| 87 | 
            +
              def write_pygments_stylesheet target_dir = '.', style = nil
         | 
| 88 | 
            +
                ::IO.write(::File.join(target_dir, pygments_stylesheet_name(style)), pygments_stylesheet_data(style))
         | 
| 89 89 | 
             
              end
         | 
| 90 90 |  | 
| 91 91 | 
             
              #def load_coderay
         | 
| @@ -4,34 +4,28 @@ module Asciidoctor | |
| 4 4 | 
             
            # is intented to be mixed-in to Section and Block to provide operations for performing
         | 
| 5 5 | 
             
            # the necessary substitutions.
         | 
| 6 6 | 
             
            module Substitutors
         | 
| 7 | 
            -
             | 
| 8 | 
            -
               | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
               | 
| 15 | 
            -
             | 
| 16 | 
            -
               | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
                 | 
| 23 | 
            -
                 | 
| 24 | 
            -
                : | 
| 7 | 
            +
              SpecialCharsRx = /[<&>]/
         | 
| 8 | 
            +
              SpecialCharsTr = { '>' => '>', '<' => '<', '&' => '&' }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              # Detects if text is a possible candidate for the quotes substitution.
         | 
| 11 | 
            +
              QuotedTextSniffRx = { false => /[*_`#^~]/, true => /[*'_+#^~]/ }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              (BASIC_SUBS = [:specialcharacters]).freeze
         | 
| 14 | 
            +
              (HEADER_SUBS = [:specialcharacters, :attributes]).freeze
         | 
| 15 | 
            +
              (NORMAL_SUBS = [:specialcharacters, :quotes, :attributes, :replacements, :macros, :post_replacements]).freeze
         | 
| 16 | 
            +
              (NONE_SUBS = []).freeze
         | 
| 17 | 
            +
              (TITLE_SUBS = [:specialcharacters, :quotes, :replacements, :macros, :attributes, :post_replacements]).freeze
         | 
| 18 | 
            +
              (REFTEXT_SUBS = [:specialcharacters, :quotes, :replacements]).freeze
         | 
| 19 | 
            +
              (VERBATIM_SUBS = [:specialcharacters, :callouts]).freeze
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              SUB_GROUPS = {
         | 
| 22 | 
            +
                :none => NONE_SUBS,
         | 
| 23 | 
            +
                :normal => NORMAL_SUBS,
         | 
| 24 | 
            +
                :verbatim => VERBATIM_SUBS,
         | 
| 25 | 
            +
                :specialchars => BASIC_SUBS
         | 
| 25 26 | 
             
              }
         | 
| 26 27 |  | 
| 27 | 
            -
               | 
| 28 | 
            -
                :none => [],
         | 
| 29 | 
            -
                :normal => SUBS[:normal],
         | 
| 30 | 
            -
                :verbatim => SUBS[:verbatim],
         | 
| 31 | 
            -
                :specialchars => [:specialcharacters]
         | 
| 32 | 
            -
              }
         | 
| 33 | 
            -
             | 
| 34 | 
            -
              SUB_SYMBOLS = {
         | 
| 28 | 
            +
              SUB_HINTS = {
         | 
| 35 29 | 
             
                :a => :attributes,
         | 
| 36 30 | 
             
                :m => :macros,
         | 
| 37 31 | 
             
                :n => :normal,
         | 
| @@ -43,8 +37,8 @@ module Substitutors | |
| 43 37 | 
             
              }
         | 
| 44 38 |  | 
| 45 39 | 
             
              SUB_OPTIONS = {
         | 
| 46 | 
            -
                :block  =>  | 
| 47 | 
            -
                :inline =>  | 
| 40 | 
            +
                :block  => SUB_GROUPS.keys + NORMAL_SUBS + [:callouts],
         | 
| 41 | 
            +
                :inline => SUB_GROUPS.keys + NORMAL_SUBS
         | 
| 48 42 | 
             
              }
         | 
| 49 43 |  | 
| 50 44 | 
             
              SUB_HIGHLIGHT = ['coderay', 'pygments']
         | 
| @@ -53,52 +47,63 @@ module Substitutors | |
| 53 47 | 
             
              # See http://www.aivosto.com/vbtips/control-characters.html#listabout for characters to use
         | 
| 54 48 |  | 
| 55 49 | 
             
              # SPA, start of guarded protected area (\u0096)
         | 
| 56 | 
            -
              PASS_START =  | 
| 50 | 
            +
              PASS_START = %(\u0096)
         | 
| 57 51 |  | 
| 58 52 | 
             
              # EPA, end of guarded protected area (\u0097)
         | 
| 59 | 
            -
              PASS_END =  | 
| 53 | 
            +
              PASS_END = %(\u0097)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              # match passthrough slot
         | 
| 56 | 
            +
              PassSlotRx = /#{PASS_START}(\d+)#{PASS_END}/
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              # fix passthrough slot after syntax highlighting
         | 
| 59 | 
            +
              HighlightedPassSlotRx = %r(<span\b[^>]*>#{PASS_START}</span>[^\d]*(\d+)[^\d]*<span\b[^>]*>#{PASS_END}</span>)
         | 
| 60 60 |  | 
| 61 | 
            -
               | 
| 62 | 
            -
              PASS_MATCH = /\u0096(\d+)\u0097/
         | 
| 61 | 
            +
              RS = '\\'
         | 
| 63 62 |  | 
| 64 | 
            -
               | 
| 65 | 
            -
             | 
| 63 | 
            +
              R_SB = ']'
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              ESC_R_SB = '\]'
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              PLUS = '+'
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              PygmentsWrapperDivRx = %r(<div class="pyhl">(.*)</div>)m
         | 
| 70 | 
            +
              # NOTE handles all permutations of <pre> wrapper
         | 
| 71 | 
            +
              # NOTE trailing whitespace appears when pygments-linenums-mode=table; <pre> has style attribute when pygments-css=inline
         | 
| 72 | 
            +
              PygmentsWrapperPreRx = %r(<pre\b[^>]*?>(.*?)</pre>\s*)m
         | 
| 66 73 |  | 
| 67 74 | 
             
              # Internal: A String Array of passthough (unprocessed) text captured from this block
         | 
| 68 75 | 
             
              attr_reader :passthroughs
         | 
| 69 76 |  | 
| 70 | 
            -
              # Public: Apply the specified substitutions to the  | 
| 77 | 
            +
              # Public: Apply the specified substitutions to the source.
         | 
| 71 78 | 
             
              #
         | 
| 72 | 
            -
              # source  - The String or String Array of text to process
         | 
| 73 | 
            -
              # subs    - The substitutions to perform | 
| 74 | 
            -
              # expand  | 
| 79 | 
            +
              # source  - The String or String Array of text to process; must not be nil.
         | 
| 80 | 
            +
              # subs    - The substitutions to perform; can be a Symbol or Symbol Array; must not be nil (default: :normal).
         | 
| 81 | 
            +
              # expand  - A Boolean to control whether substitution aliases are expanded (default: false).
         | 
| 75 82 | 
             
              #
         | 
| 76 | 
            -
              #  | 
| 77 | 
            -
              def apply_subs source, subs =  | 
| 78 | 
            -
                if  | 
| 83 | 
            +
              # Returns a String or String Array with substitutions applied, matching the type of source argument.
         | 
| 84 | 
            +
              def apply_subs source, subs = NORMAL_SUBS, expand = false
         | 
| 85 | 
            +
                if source.empty? || subs.empty?
         | 
| 79 86 | 
             
                  return source
         | 
| 80 | 
            -
                elsif subs == :normal
         | 
| 81 | 
            -
                  subs = SUBS[:normal]
         | 
| 82 87 | 
             
                elsif expand
         | 
| 83 88 | 
             
                  if ::Symbol === subs
         | 
| 84 | 
            -
                    subs =  | 
| 89 | 
            +
                    subs = SUB_GROUPS[subs] || [subs]
         | 
| 85 90 | 
             
                  else
         | 
| 86 91 | 
             
                    effective_subs = []
         | 
| 87 92 | 
             
                    subs.each do |key|
         | 
| 88 | 
            -
                      if  | 
| 89 | 
            -
                        effective_subs +=  | 
| 93 | 
            +
                      if (sub_group = SUB_GROUPS[key])
         | 
| 94 | 
            +
                        effective_subs += sub_group unless sub_group.empty?
         | 
| 90 95 | 
             
                      else
         | 
| 91 96 | 
             
                        effective_subs << key
         | 
| 92 97 | 
             
                      end
         | 
| 93 98 | 
             
                    end
         | 
| 94 99 |  | 
| 95 | 
            -
                    subs = effective_subs
         | 
| 100 | 
            +
                    if (subs = effective_subs).empty?
         | 
| 101 | 
            +
                      return source
         | 
| 102 | 
            +
                    end
         | 
| 96 103 | 
             
                  end
         | 
| 97 104 | 
             
                end
         | 
| 98 105 |  | 
| 99 | 
            -
                 | 
| 100 | 
            -
             | 
| 101 | 
            -
                text = (multiline = ::Array === source) ? source * EOL : source
         | 
| 106 | 
            +
                text = (multiline = ::Array === source) ? source * LF : source
         | 
| 102 107 |  | 
| 103 108 | 
             
                if (has_passthroughs = subs.include? :macros)
         | 
| 104 109 | 
             
                  text = extract_passthroughs text
         | 
| @@ -112,7 +117,7 @@ module Substitutors | |
| 112 117 | 
             
                  when :quotes
         | 
| 113 118 | 
             
                    text = sub_quotes text
         | 
| 114 119 | 
             
                  when :attributes
         | 
| 115 | 
            -
                    text = sub_attributes(text.split  | 
| 120 | 
            +
                    text = sub_attributes(text.split LF, -1) * LF if text.include? '{'
         | 
| 116 121 | 
             
                  when :replacements
         | 
| 117 122 | 
             
                    text = sub_replacements text
         | 
| 118 123 | 
             
                  when :macros
         | 
| @@ -129,16 +134,18 @@ module Substitutors | |
| 129 134 | 
             
                end
         | 
| 130 135 | 
             
                text = restore_passthroughs text if has_passthroughs
         | 
| 131 136 |  | 
| 132 | 
            -
                multiline ? (text.split  | 
| 137 | 
            +
                multiline ? (text.split LF, -1) : text
         | 
| 133 138 | 
             
              end
         | 
| 134 139 |  | 
| 135 140 | 
             
              # Public: Apply normal substitutions.
         | 
| 136 141 | 
             
              #
         | 
| 137 | 
            -
              #  | 
| 142 | 
            +
              # An alias for apply_subs with default remaining arguments.
         | 
| 143 | 
            +
              #
         | 
| 144 | 
            +
              # text  - The String text to which to apply normal substitutions
         | 
| 138 145 | 
             
              #
         | 
| 139 | 
            -
              #  | 
| 140 | 
            -
              def apply_normal_subs | 
| 141 | 
            -
                apply_subs | 
| 146 | 
            +
              # Returns the String with normal substitutions applied.
         | 
| 147 | 
            +
              def apply_normal_subs text
         | 
| 148 | 
            +
                apply_subs text
         | 
| 142 149 | 
             
              end
         | 
| 143 150 |  | 
| 144 151 | 
             
              # Public: Apply substitutions for titles.
         | 
| @@ -147,7 +154,16 @@ module Substitutors | |
| 147 154 | 
             
              #
         | 
| 148 155 | 
             
              # returns - A String with title substitutions performed
         | 
| 149 156 | 
             
              def apply_title_subs(title)
         | 
| 150 | 
            -
                apply_subs title,  | 
| 157 | 
            +
                apply_subs title, TITLE_SUBS
         | 
| 158 | 
            +
              end
         | 
| 159 | 
            +
             | 
| 160 | 
            +
              # Public: Apply substitutions for reftext.
         | 
| 161 | 
            +
              #
         | 
| 162 | 
            +
              # text - The String to process
         | 
| 163 | 
            +
              #
         | 
| 164 | 
            +
              # Returns a String with all substitutions from the reftext substitution group applied
         | 
| 165 | 
            +
              def apply_reftext_subs text
         | 
| 166 | 
            +
                apply_subs text, REFTEXT_SUBS
         | 
| 151 167 | 
             
              end
         | 
| 152 168 |  | 
| 153 169 | 
             
              # Public: Apply substitutions for header metadata and attribute assignments
         | 
| @@ -156,7 +172,7 @@ module Substitutors | |
| 156 172 | 
             
              #
         | 
| 157 173 | 
             
              # returns - A String with header substitutions performed
         | 
| 158 174 | 
             
              def apply_header_subs(text)
         | 
| 159 | 
            -
                apply_subs text,  | 
| 175 | 
            +
                apply_subs text, HEADER_SUBS
         | 
| 160 176 | 
             
              end
         | 
| 161 177 |  | 
| 162 178 | 
             
              # Internal: Extract the passthrough text from the document for reinsertion after processing.
         | 
| @@ -166,19 +182,12 @@ module Substitutors | |
| 166 182 | 
             
              # returns - The text with the passthrough region substituted with placeholders
         | 
| 167 183 | 
             
              def extract_passthroughs(text)
         | 
| 168 184 | 
             
                compat_mode = @document.compat_mode
         | 
| 169 | 
            -
                text = text.gsub( | 
| 185 | 
            +
                text = text.gsub(InlinePassMacroRx) {
         | 
| 170 186 | 
             
                  # alias match for Ruby 1.8.7 compat
         | 
| 171 187 | 
             
                  m = $~
         | 
| 172 188 | 
             
                  preceding = nil
         | 
| 173 189 |  | 
| 174 | 
            -
                  if (boundary = m[4]) | 
| 175 | 
            -
                    if m[6] == '\\'
         | 
| 176 | 
            -
                      # NOTE we don't look for nested pass:[] macros
         | 
| 177 | 
            -
                      next m[0][1..-1]
         | 
| 178 | 
            -
                    end
         | 
| 179 | 
            -
             | 
| 180 | 
            -
                    @passthroughs[pass_key = @passthroughs.size] = {:text => (unescape_brackets m[8]), :subs => (m[7].nil_or_empty? ? [] : (resolve_pass_subs m[7]))}
         | 
| 181 | 
            -
                  else # $$, ++ or +++
         | 
| 190 | 
            +
                  if (boundary = m[4]) # $$, ++, or +++
         | 
| 182 191 | 
             
                    # skip ++ in compat mode, handled as normal quoted text
         | 
| 183 192 | 
             
                    if compat_mode && boundary == '++'
         | 
| 184 193 | 
             
                      next m[2].nil_or_empty? ?
         | 
| @@ -187,22 +196,15 @@ module Substitutors | |
| 187 196 | 
             
                    end
         | 
| 188 197 |  | 
| 189 198 | 
             
                    attributes = m[2]
         | 
| 190 | 
            -
             | 
| 191 | 
            -
                    # fix non-matching group results in Opal under Firefox
         | 
| 192 | 
            -
                    if ::RUBY_ENGINE_OPAL
         | 
| 193 | 
            -
                      attributes = nil if attributes == ''
         | 
| 194 | 
            -
                    end
         | 
| 195 | 
            -
             | 
| 196 | 
            -
                    escape_count = m[3].size
         | 
| 199 | 
            +
                    escape_count = m[3].length
         | 
| 197 200 | 
             
                    content = m[5]
         | 
| 198 201 | 
             
                    old_behavior = false
         | 
| 199 202 |  | 
| 200 203 | 
             
                    if attributes
         | 
| 201 204 | 
             
                      if escape_count > 0
         | 
| 202 205 | 
             
                        # NOTE we don't look for nested unconstrained pass macros
         | 
| 203 | 
            -
                        #  | 
| 204 | 
            -
             | 
| 205 | 
            -
                      elsif m[1] == '\\'
         | 
| 206 | 
            +
                        next %(#{m[1]}[#{attributes}]#{RS * (escape_count - 1)}#{boundary}#{m[5]}#{boundary})
         | 
| 207 | 
            +
                      elsif m[1] == RS
         | 
| 206 208 | 
             
                        preceding = %([#{attributes}])
         | 
| 207 209 | 
             
                        attributes = nil
         | 
| 208 210 | 
             
                      else
         | 
| @@ -214,21 +216,27 @@ module Substitutors | |
| 214 216 | 
             
                      end
         | 
| 215 217 | 
             
                    elsif escape_count > 0
         | 
| 216 218 | 
             
                      # NOTE we don't look for nested unconstrained pass macros
         | 
| 217 | 
            -
                      #  | 
| 218 | 
            -
                      next "#{m[1]}[#{attributes}]#{'\\' * (escape_count - 1)}#{boundary}#{m[5]}#{boundary}"
         | 
| 219 | 
            +
                      next %(#{RS * (escape_count - 1)}#{boundary}#{m[5]}#{boundary})
         | 
| 219 220 | 
             
                    end
         | 
| 220 | 
            -
                    subs = (boundary == '+++' ? [] :  | 
| 221 | 
            +
                    subs = (boundary == '+++' ? [] : BASIC_SUBS)
         | 
| 221 222 |  | 
| 222 223 | 
             
                    pass_key = @passthroughs.size
         | 
| 223 224 | 
             
                    if attributes
         | 
| 224 225 | 
             
                      if old_behavior
         | 
| 225 | 
            -
                        @passthroughs[pass_key] = {:text => content, :subs =>  | 
| 226 | 
            +
                        @passthroughs[pass_key] = {:text => content, :subs => NORMAL_SUBS, :type => :monospaced, :attributes => attributes}
         | 
| 226 227 | 
             
                      else
         | 
| 227 228 | 
             
                        @passthroughs[pass_key] = {:text => content, :subs => subs, :type => :unquoted, :attributes => attributes}
         | 
| 228 229 | 
             
                      end
         | 
| 229 230 | 
             
                    else
         | 
| 230 231 | 
             
                      @passthroughs[pass_key] = {:text => content, :subs => subs}
         | 
| 231 232 | 
             
                    end
         | 
| 233 | 
            +
                  else # pass:[]
         | 
| 234 | 
            +
                    if m[6] == RS
         | 
| 235 | 
            +
                      # NOTE we don't look for nested pass:[] macros
         | 
| 236 | 
            +
                      next m[0][1..-1]
         | 
| 237 | 
            +
                    end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                    @passthroughs[pass_key = @passthroughs.size] = {:text => (unescape_brackets m[8]), :subs => (m[7] ? (resolve_pass_subs m[7]) : [])}
         | 
| 232 240 | 
             
                  end
         | 
| 233 241 |  | 
| 234 242 | 
             
                  %(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
         | 
| @@ -240,15 +248,10 @@ module Substitutors | |
| 240 248 | 
             
                  m = $~
         | 
| 241 249 | 
             
                  preceding = m[1]
         | 
| 242 250 | 
             
                  attributes = m[2]
         | 
| 243 | 
            -
                  escape_mark =  | 
| 251 | 
            +
                  escape_mark = RS if m[3].start_with? RS
         | 
| 244 252 | 
             
                  format_mark = m[4]
         | 
| 245 253 | 
             
                  content = m[5]
         | 
| 246 254 |  | 
| 247 | 
            -
                  # fix non-matching group results in Opal under Firefox
         | 
| 248 | 
            -
                  if ::RUBY_ENGINE_OPAL
         | 
| 249 | 
            -
                    attributes = nil if attributes == ''
         | 
| 250 | 
            -
                  end
         | 
| 251 | 
            -
             | 
| 252 255 | 
             
                  if compat_mode
         | 
| 253 256 | 
             
                    old_behavior = true
         | 
| 254 257 | 
             
                  else
         | 
| @@ -259,14 +262,13 @@ module Substitutors | |
| 259 262 |  | 
| 260 263 | 
             
                  if attributes
         | 
| 261 264 | 
             
                    if format_mark == '`' && !old_behavior
         | 
| 262 | 
            -
                       | 
| 263 | 
            -
                      next "#{preceding}[#{attributes}]#{escape_mark}`#{extract_passthroughs content}`"
         | 
| 265 | 
            +
                      next %(#{preceding}[#{attributes}]#{escape_mark}`#{extract_passthroughs content}`)
         | 
| 264 266 | 
             
                    end
         | 
| 265 267 |  | 
| 266 268 | 
             
                    if escape_mark
         | 
| 267 | 
            -
                      # honor the escape of the formatting mark | 
| 268 | 
            -
                      next  | 
| 269 | 
            -
                    elsif preceding ==  | 
| 269 | 
            +
                      # honor the escape of the formatting mark
         | 
| 270 | 
            +
                      next %(#{preceding}[#{attributes}]#{m[3][1..-1]})
         | 
| 271 | 
            +
                    elsif preceding == RS
         | 
| 270 272 | 
             
                      # honor the escape of the attributes
         | 
| 271 273 | 
             
                      preceding = %([#{attributes}])
         | 
| 272 274 | 
             
                      attributes = nil
         | 
| @@ -274,49 +276,43 @@ module Substitutors | |
| 274 276 | 
             
                      attributes = parse_attributes attributes
         | 
| 275 277 | 
             
                    end
         | 
| 276 278 | 
             
                  elsif format_mark == '`' && !old_behavior
         | 
| 277 | 
            -
                     | 
| 278 | 
            -
                    next "#{preceding}#{escape_mark}`#{extract_passthroughs content}`"
         | 
| 279 | 
            +
                    next %(#{preceding}#{escape_mark}`#{extract_passthroughs content}`)
         | 
| 279 280 | 
             
                  elsif escape_mark
         | 
| 280 | 
            -
                    # honor the escape of the formatting mark | 
| 281 | 
            -
                    next  | 
| 281 | 
            +
                    # honor the escape of the formatting mark
         | 
| 282 | 
            +
                    next %(#{preceding}#{m[3][1..-1]})
         | 
| 282 283 | 
             
                  end
         | 
| 283 284 |  | 
| 284 285 | 
             
                  pass_key = @passthroughs.size
         | 
| 285 286 | 
             
                  if compat_mode
         | 
| 286 | 
            -
                    @passthroughs[pass_key] = {:text => content, :subs =>  | 
| 287 | 
            +
                    @passthroughs[pass_key] = {:text => content, :subs => BASIC_SUBS, :attributes => attributes, :type => :monospaced}
         | 
| 287 288 | 
             
                  elsif attributes
         | 
| 288 289 | 
             
                    if old_behavior
         | 
| 289 | 
            -
                      subs = (format_mark == '`' ?  | 
| 290 | 
            +
                      subs = (format_mark == '`' ? BASIC_SUBS : NORMAL_SUBS)
         | 
| 290 291 | 
             
                      @passthroughs[pass_key] = {:text => content, :subs => subs, :attributes => attributes, :type => :monospaced}
         | 
| 291 292 | 
             
                    else
         | 
| 292 | 
            -
                      @passthroughs[pass_key] = {:text => content, :subs =>  | 
| 293 | 
            +
                      @passthroughs[pass_key] = {:text => content, :subs => BASIC_SUBS, :attributes => attributes, :type => :unquoted}
         | 
| 293 294 | 
             
                    end
         | 
| 294 295 | 
             
                  else
         | 
| 295 | 
            -
                    @passthroughs[pass_key] = {:text => content, :subs =>  | 
| 296 | 
            +
                    @passthroughs[pass_key] = {:text => content, :subs => BASIC_SUBS}
         | 
| 296 297 | 
             
                  end
         | 
| 297 298 |  | 
| 298 299 | 
             
                  %(#{preceding}#{PASS_START}#{pass_key}#{PASS_END})
         | 
| 299 300 | 
             
                } if (text.include? pass_inline_char1) || (pass_inline_char2 && (text.include? pass_inline_char2))
         | 
| 300 301 |  | 
| 301 302 | 
             
                # NOTE we need to do the stem in a subsequent step to allow it to be escaped by the former
         | 
| 302 | 
            -
                text = text.gsub( | 
| 303 | 
            +
                text = text.gsub(InlineStemMacroRx) {
         | 
| 303 304 | 
             
                  # alias match for Ruby 1.8.7 compat
         | 
| 304 305 | 
             
                  m = $~
         | 
| 305 306 | 
             
                  # honor the escape
         | 
| 306 | 
            -
                  if m[0].start_with?  | 
| 307 | 
            +
                  if m[0].start_with? RS
         | 
| 307 308 | 
             
                    next m[0][1..-1]
         | 
| 308 309 | 
             
                  end
         | 
| 309 310 |  | 
| 310 311 | 
             
                  if (type = m[1].to_sym) == :stem
         | 
| 311 | 
            -
                    type = ((default_stem_type = document.attributes['stem']).nil_or_empty? ? 'asciimath' : default_stem_type).to_sym
         | 
| 312 | 
            +
                    type = ((default_stem_type = @document.attributes['stem']).nil_or_empty? ? 'asciimath' : default_stem_type).to_sym
         | 
| 312 313 | 
             
                  end
         | 
| 313 314 | 
             
                  content = unescape_brackets m[3]
         | 
| 314 | 
            -
                   | 
| 315 | 
            -
                    subs = (@document.basebackend? 'html') ? [:specialcharacters] : []
         | 
| 316 | 
            -
                  else
         | 
| 317 | 
            -
                    subs = resolve_pass_subs m[2]
         | 
| 318 | 
            -
                  end
         | 
| 319 | 
            -
             | 
| 315 | 
            +
                  subs = m[2] ? (resolve_pass_subs m[2]) : ((@document.basebackend? 'html') ? BASIC_SUBS : [])
         | 
| 320 316 | 
             
                  @passthroughs[pass_key = @passthroughs.size] = {:text => content, :subs => subs, :type => type}
         | 
| 321 317 | 
             
                  %(#{PASS_START}#{pass_key}#{PASS_END})
         | 
| 322 318 | 
             
                } if (text.include? ':') && ((text.include? 'stem:') || (text.include? 'math:'))
         | 
| @@ -335,10 +331,10 @@ module Substitutors | |
| 335 331 | 
             
                  return text
         | 
| 336 332 | 
             
                end
         | 
| 337 333 |  | 
| 338 | 
            -
                text.gsub( | 
| 334 | 
            +
                text.gsub(PassSlotRx) {
         | 
| 339 335 | 
             
                  # NOTE we can't remove entry from map because placeholder may have been duplicated by other substitutions
         | 
| 340 | 
            -
                  pass = @passthroughs[ | 
| 341 | 
            -
                  subbed_text =  | 
| 336 | 
            +
                  pass = @passthroughs[$1.to_i]
         | 
| 337 | 
            +
                  subbed_text = apply_subs(pass[:text], pass[:subs])
         | 
| 342 338 | 
             
                  if (type = pass[:type])
         | 
| 343 339 | 
             
                    subbed_text = Inline.new(self, :quoted, subbed_text, :type => type, :attributes => pass[:attributes]).convert
         | 
| 344 340 | 
             
                  end
         | 
| @@ -349,34 +345,29 @@ module Substitutors | |
| 349 345 | 
             
                @passthroughs.clear if outer
         | 
| 350 346 | 
             
              end
         | 
| 351 347 |  | 
| 352 | 
            -
              # Public: Substitute special characters (i.e., encode XML)
         | 
| 353 | 
            -
              #
         | 
| 354 | 
            -
              # Special characters are defined in the Asciidoctor::SPECIAL_CHARS Array constant
         | 
| 355 | 
            -
              #
         | 
| 356 | 
            -
              # text - The String text to process
         | 
| 357 | 
            -
              #
         | 
| 358 | 
            -
              # returns The String text with special characters replaced
         | 
| 359 | 
            -
              def sub_specialchars(text)
         | 
| 360 | 
            -
                SUPPORTS_GSUB_RESULT_HASH ?
         | 
| 361 | 
            -
                  text.gsub(SPECIAL_CHARS_PATTERN, SPECIAL_CHARS) :
         | 
| 362 | 
            -
                  text.gsub(SPECIAL_CHARS_PATTERN) { SPECIAL_CHARS[$&] }
         | 
| 363 | 
            -
              end
         | 
| 364 | 
            -
              alias :sub_specialcharacters :sub_specialchars
         | 
| 365 348 |  | 
| 366 349 | 
             
              if RUBY_ENGINE == 'opal'
         | 
| 367 350 | 
             
                def sub_quotes text
         | 
| 368 | 
            -
                   | 
| 369 | 
            -
                     | 
| 351 | 
            +
                  if QuotedTextSniffRx[compat = @document.compat_mode].match? text
         | 
| 352 | 
            +
                    QUOTE_SUBS[compat].each do |type, scope, pattern|
         | 
| 353 | 
            +
                      text = text.gsub(pattern) { convert_quoted_text $~, type, scope }
         | 
| 354 | 
            +
                    end
         | 
| 370 355 | 
             
                  end
         | 
| 371 356 | 
             
                  text
         | 
| 372 357 | 
             
                end
         | 
| 373 358 |  | 
| 374 359 | 
             
                def sub_replacements text
         | 
| 375 | 
            -
                   | 
| 376 | 
            -
                     | 
| 360 | 
            +
                  if ReplaceableTextRx.match? text
         | 
| 361 | 
            +
                    REPLACEMENTS.each do |pattern, replacement, restore|
         | 
| 362 | 
            +
                      text = text.gsub(pattern) { do_replacement $~, replacement, restore }
         | 
| 363 | 
            +
                    end
         | 
| 377 364 | 
             
                  end
         | 
| 378 365 | 
             
                  text
         | 
| 379 366 | 
             
                end
         | 
| 367 | 
            +
             | 
| 368 | 
            +
                def sub_specialchars text
         | 
| 369 | 
            +
                  (text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub SpecialCharsRx, SpecialCharsTr) : text
         | 
| 370 | 
            +
                end
         | 
| 380 371 | 
             
              else
         | 
| 381 372 | 
             
                # Public: Substitute quoted text (includes emphasis, strong, monospaced, etc)
         | 
| 382 373 | 
             
                #
         | 
| @@ -384,11 +375,13 @@ module Substitutors | |
| 384 375 | 
             
                #
         | 
| 385 376 | 
             
                # returns The converted String text
         | 
| 386 377 | 
             
                def sub_quotes text
         | 
| 387 | 
            -
                   | 
| 388 | 
            -
             | 
| 389 | 
            -
             | 
| 390 | 
            -
             | 
| 391 | 
            -
             | 
| 378 | 
            +
                  if QuotedTextSniffRx[compat = @document.compat_mode].match? text
         | 
| 379 | 
            +
                    # NOTE interpolation is faster than String#dup
         | 
| 380 | 
            +
                    text = %(#{text})
         | 
| 381 | 
            +
                    QUOTE_SUBS[compat].each do |type, scope, pattern|
         | 
| 382 | 
            +
                      # NOTE using gsub! here as an MRI Ruby optimization
         | 
| 383 | 
            +
                      text.gsub!(pattern) { convert_quoted_text $~, type, scope }
         | 
| 384 | 
            +
                    end
         | 
| 392 385 | 
             
                  end
         | 
| 393 386 | 
             
                  text
         | 
| 394 387 | 
             
                end
         | 
| @@ -399,30 +392,52 @@ module Substitutors | |
| 399 392 | 
             
                #
         | 
| 400 393 | 
             
                # returns The String text with the replacement characters substituted
         | 
| 401 394 | 
             
                def sub_replacements text
         | 
| 402 | 
            -
                   | 
| 403 | 
            -
             | 
| 404 | 
            -
             | 
| 405 | 
            -
             | 
| 406 | 
            -
             | 
| 395 | 
            +
                  if ReplaceableTextRx.match? text
         | 
| 396 | 
            +
                    # NOTE interpolation is faster than String#dup
         | 
| 397 | 
            +
                    text = %(#{text})
         | 
| 398 | 
            +
                    REPLACEMENTS.each do |pattern, replacement, restore|
         | 
| 399 | 
            +
                      # NOTE Using gsub! as optimization
         | 
| 400 | 
            +
                      text.gsub!(pattern) { do_replacement $~, replacement, restore }
         | 
| 401 | 
            +
                    end
         | 
| 407 402 | 
             
                  end
         | 
| 408 403 | 
             
                  text
         | 
| 409 404 | 
             
                end
         | 
| 405 | 
            +
             | 
| 406 | 
            +
                # Public: Substitute special characters (i.e., encode XML)
         | 
| 407 | 
            +
                #
         | 
| 408 | 
            +
                # The special characters are <, &, and >, which get replaced with <,
         | 
| 409 | 
            +
                # &, and >, respectively.
         | 
| 410 | 
            +
                #
         | 
| 411 | 
            +
                # text - The String text to process
         | 
| 412 | 
            +
                #
         | 
| 413 | 
            +
                # returns The String text with special characters replaced
         | 
| 414 | 
            +
                if ::RUBY_MIN_VERSION_1_9
         | 
| 415 | 
            +
                  def sub_specialchars text
         | 
| 416 | 
            +
                    (text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub! SpecialCharsRx, SpecialCharsTr) : text
         | 
| 417 | 
            +
                  end
         | 
| 418 | 
            +
                else
         | 
| 419 | 
            +
                  def sub_specialchars text
         | 
| 420 | 
            +
                    (text.include? '<') || (text.include? '&') || (text.include? '>') ? (text.gsub!(SpecialCharsRx) { SpecialCharsTr[$&] }) : text
         | 
| 421 | 
            +
                  end
         | 
| 422 | 
            +
                end
         | 
| 410 423 | 
             
              end
         | 
| 424 | 
            +
              alias sub_specialcharacters sub_specialchars
         | 
| 411 425 |  | 
| 412 426 | 
             
              # Internal: Substitute replacement text for matched location
         | 
| 413 427 | 
             
              #
         | 
| 414 428 | 
             
              # returns The String text with the replacement characters substituted
         | 
| 415 429 | 
             
              def do_replacement m, replacement, restore
         | 
| 416 | 
            -
                if ( | 
| 417 | 
            -
                   | 
| 430 | 
            +
                if (captured = m[0]).include? RS
         | 
| 431 | 
            +
                  # we have to use sub since we aren't sure it's the first char
         | 
| 432 | 
            +
                  captured.sub RS, ''
         | 
| 418 433 | 
             
                else
         | 
| 419 434 | 
             
                  case restore
         | 
| 420 435 | 
             
                  when :none
         | 
| 421 436 | 
             
                    replacement
         | 
| 422 | 
            -
                  when :leading
         | 
| 423 | 
            -
                    %(#{m[1]}#{replacement})
         | 
| 424 437 | 
             
                  when :bounding
         | 
| 425 438 | 
             
                    %(#{m[1]}#{replacement}#{m[2]})
         | 
| 439 | 
            +
                  else # :leading
         | 
| 440 | 
            +
                    %(#{m[1]}#{replacement})
         | 
| 426 441 | 
             
                  end
         | 
| 427 442 | 
             
                end
         | 
| 428 443 | 
             
              end
         | 
| @@ -441,74 +456,52 @@ module Substitutors | |
| 441 456 | 
             
              # so that a missing key doesn't wipe out the whole block of data
         | 
| 442 457 | 
             
              # when attribute-undefined and/or attribute-missing is drop-line
         | 
| 443 458 | 
             
              def sub_attributes data, opts = {}
         | 
| 444 | 
            -
                return data if data.nil_or_empty?
         | 
| 445 | 
            -
             | 
| 446 459 | 
             
                # normalizes data type to an array (string becomes single-element array)
         | 
| 447 | 
            -
                if ( | 
| 448 | 
            -
             | 
| 449 | 
            -
                end
         | 
| 450 | 
            -
             | 
| 451 | 
            -
                doc_attrs = @document.attributes
         | 
| 452 | 
            -
                attribute_missing = nil
         | 
| 453 | 
            -
                result = []
         | 
| 460 | 
            +
                data = [data] if (input_is_string = ::String === data)
         | 
| 461 | 
            +
                doc_attrs, result = @document.attributes, []
         | 
| 454 462 | 
             
                data.each do |line|
         | 
| 455 | 
            -
                  reject = false
         | 
| 456 | 
            -
                  reject_if_empty = false
         | 
| 463 | 
            +
                  reject = reject_if_empty = false
         | 
| 457 464 | 
             
                  line = line.gsub(AttributeReferenceRx) {
         | 
| 458 | 
            -
                    # alias match for Ruby 1.8.7 compat
         | 
| 459 | 
            -
                    m = $~
         | 
| 460 465 | 
             
                    # escaped attribute, return unescaped
         | 
| 461 | 
            -
                    if  | 
| 462 | 
            -
                      %({#{ | 
| 463 | 
            -
                    elsif  | 
| 464 | 
            -
                       | 
| 465 | 
            -
                      expr = m[2][offset..-1]
         | 
| 466 | 
            -
                      case directive
         | 
| 466 | 
            +
                    if $1 == RS || $4 == RS
         | 
| 467 | 
            +
                      %({#{$2}})
         | 
| 468 | 
            +
                    elsif $3
         | 
| 469 | 
            +
                      case (args = $2.split ':', 3).shift
         | 
| 467 470 | 
             
                      when 'set'
         | 
| 468 | 
            -
                         | 
| 469 | 
            -
                         | 
| 470 | 
            -
                         | 
| 471 | 
            -
                           | 
| 472 | 
            -
                           | 
| 473 | 
            -
             | 
| 474 | 
            -
                            break ''
         | 
| 475 | 
            -
                          end
         | 
| 476 | 
            -
                        end
         | 
| 471 | 
            +
                        _, value = Parser.store_attribute args[0], args[1] || '', @document
         | 
| 472 | 
            +
                        # since this is an assignment, only drop-line applies here (skip and drop imply the same result)
         | 
| 473 | 
            +
                        if (doc_attrs.fetch 'attribute-undefined', Compliance.attribute_undefined) == 'drop-line'
         | 
| 474 | 
            +
                          reject = true
         | 
| 475 | 
            +
                          break ''
         | 
| 476 | 
            +
                        end unless value
         | 
| 477 477 | 
             
                        reject_if_empty = true
         | 
| 478 478 | 
             
                        ''
         | 
| 479 | 
            -
                      when ' | 
| 480 | 
            -
                         | 
| 481 | 
            -
                         | 
| 482 | 
            -
                         | 
| 483 | 
            -
             | 
| 484 | 
            -
             | 
| 485 | 
            -
                        else
         | 
| 486 | 
            -
                          val
         | 
| 487 | 
            -
                        end
         | 
| 488 | 
            -
                      else
         | 
| 489 | 
            -
                        # if we get here, our AttributeReference regex is too loose
         | 
| 490 | 
            -
                        warn %(asciidoctor: WARNING: illegal attribute directive: #{m[3]})
         | 
| 491 | 
            -
                        m[0]
         | 
| 479 | 
            +
                      when 'counter2'
         | 
| 480 | 
            +
                        @document.counter(*args)
         | 
| 481 | 
            +
                        reject_if_empty = true
         | 
| 482 | 
            +
                        ''
         | 
| 483 | 
            +
                      else # 'counter'
         | 
| 484 | 
            +
                        @document.counter(*args)
         | 
| 492 485 | 
             
                      end
         | 
| 493 | 
            -
                    elsif doc_attrs.key?(key =  | 
| 486 | 
            +
                    elsif doc_attrs.key?(key = $2.downcase)
         | 
| 494 487 | 
             
                      doc_attrs[key]
         | 
| 495 488 | 
             
                    elsif INTRINSIC_ATTRIBUTES.key? key
         | 
| 496 489 | 
             
                      INTRINSIC_ATTRIBUTES[key]
         | 
| 497 490 | 
             
                    else
         | 
| 498 | 
            -
                      case (attribute_missing ||=  | 
| 499 | 
            -
                      when ' | 
| 500 | 
            -
                         | 
| 491 | 
            +
                      case (attribute_missing ||= opts[:attribute_missing] || (doc_attrs.fetch 'attribute-missing', Compliance.attribute_missing))
         | 
| 492 | 
            +
                      when 'drop'
         | 
| 493 | 
            +
                        # QUESTION should we warn in this case?
         | 
| 494 | 
            +
                        reject_if_empty = true
         | 
| 495 | 
            +
                        ''
         | 
| 501 496 | 
             
                      when 'drop-line'
         | 
| 502 497 | 
             
                        warn %(asciidoctor: WARNING: dropping line containing reference to missing attribute: #{key})
         | 
| 503 498 | 
             
                        reject = true
         | 
| 504 499 | 
             
                        break ''
         | 
| 505 500 | 
             
                      when 'warn'
         | 
| 506 501 | 
             
                        warn %(asciidoctor: WARNING: skipping reference to missing attribute: #{key})
         | 
| 507 | 
            -
                         | 
| 508 | 
            -
                      else # ' | 
| 509 | 
            -
                         | 
| 510 | 
            -
                        reject_if_empty = true
         | 
| 511 | 
            -
                        ''
         | 
| 502 | 
            +
                        $&
         | 
| 503 | 
            +
                      else # 'skip'
         | 
| 504 | 
            +
                        $&
         | 
| 512 505 | 
             
                      end
         | 
| 513 506 | 
             
                    end
         | 
| 514 507 | 
             
                  } if line.include? '{'
         | 
| @@ -516,7 +509,7 @@ module Substitutors | |
| 516 509 | 
             
                  result << line unless reject || (reject_if_empty && line.empty?)
         | 
| 517 510 | 
             
                end
         | 
| 518 511 |  | 
| 519 | 
            -
                 | 
| 512 | 
            +
                input_is_string ? result * LF : result
         | 
| 520 513 | 
             
              end
         | 
| 521 514 |  | 
| 522 515 | 
             
              # Public: Substitute inline macros (e.g., links, images, etc)
         | 
| @@ -527,91 +520,80 @@ module Substitutors | |
| 527 520 | 
             
              #
         | 
| 528 521 | 
             
              # returns The converted String text
         | 
| 529 522 | 
             
              def sub_macros(source)
         | 
| 530 | 
            -
                return source if source.nil_or_empty?
         | 
| 531 | 
            -
             | 
| 523 | 
            +
                #return source if source.nil_or_empty?
         | 
| 532 524 | 
             
                # some look ahead assertions to cut unnecessary regex calls
         | 
| 533 525 | 
             
                found = {}
         | 
| 534 | 
            -
                found[:square_bracket] = source.include? | 
| 535 | 
            -
                 | 
| 536 | 
            -
                found[: | 
| 537 | 
            -
                 | 
| 538 | 
            -
                 | 
| 539 | 
            -
                use_link_attrs =  | 
| 540 | 
            -
                 | 
| 541 | 
            -
             | 
| 542 | 
            -
                # NOTE interpolation is faster than String#dup
         | 
| 543 | 
            -
                result = %(#{source})
         | 
| 544 | 
            -
             | 
| 545 | 
            -
                if experimental
         | 
| 546 | 
            -
                  if found[:macroish_short_form] && (result.include?('kbd:') || result.include?('btn:'))
         | 
| 547 | 
            -
                    result = result.gsub(KbdBtnInlineMacroRx) {
         | 
| 548 | 
            -
                      # alias match for Ruby 1.8.7 compat
         | 
| 549 | 
            -
                      m = $~
         | 
| 550 | 
            -
                      # honor the escape
         | 
| 551 | 
            -
                      if (captured = m[0]).start_with? '\\'
         | 
| 552 | 
            -
                        next captured[1..-1]
         | 
| 553 | 
            -
                      end
         | 
| 554 | 
            -
             | 
| 555 | 
            -
                      if captured.start_with?('kbd')
         | 
| 556 | 
            -
                        keys = unescape_bracketed_text m[1]
         | 
| 526 | 
            +
                found_square_bracket = found[:square_bracket] = (source.include? '[')
         | 
| 527 | 
            +
                found_colon = source.include? ':'
         | 
| 528 | 
            +
                found_macroish = found[:macroish] = found_square_bracket && found_colon
         | 
| 529 | 
            +
                found_macroish_short = found_macroish && (source.include? ':[')
         | 
| 530 | 
            +
                doc_attrs = @document.attributes
         | 
| 531 | 
            +
                use_link_attrs = doc_attrs.key? 'linkattrs'
         | 
| 532 | 
            +
                result = source
         | 
| 557 533 |  | 
| 558 | 
            -
             | 
| 559 | 
            -
             | 
| 534 | 
            +
                if doc_attrs.key? 'experimental'
         | 
| 535 | 
            +
                  if found_macroish_short && ((result.include? 'kbd:') || (result.include? 'btn:'))
         | 
| 536 | 
            +
                    result = result.gsub(InlineKbdBtnMacroRx) {
         | 
| 537 | 
            +
                      # honor the escape
         | 
| 538 | 
            +
                      if $1
         | 
| 539 | 
            +
                        $&.slice 1, $&.length
         | 
| 540 | 
            +
                      elsif $2 == 'kbd'
         | 
| 541 | 
            +
                        if (keys = $3.strip).include? R_SB
         | 
| 542 | 
            +
                          keys = keys.gsub ESC_R_SB, R_SB
         | 
| 543 | 
            +
                        end
         | 
| 544 | 
            +
                        if keys.length > 1 && (delim_idx = (delim_idx = keys.index ',', 1) ?
         | 
| 545 | 
            +
                            [delim_idx, (keys.index '+', 1)].compact.min : (keys.index '+', 1))
         | 
| 546 | 
            +
                          delim = keys.slice delim_idx, 1
         | 
| 547 | 
            +
                          # NOTE handle special case where keys ends with delimiter (e.g., Ctrl++ or Ctrl,,)
         | 
| 548 | 
            +
                          if keys.end_with? delim
         | 
| 549 | 
            +
                            keys = (keys.chop.split delim, -1).map {|key| key.strip }
         | 
| 550 | 
            +
                            keys[-1] = %(#{keys[-1]}#{delim})
         | 
| 551 | 
            +
                          else
         | 
| 552 | 
            +
                            keys = keys.split(delim).map {|key| key.strip }
         | 
| 553 | 
            +
                          end
         | 
| 560 554 | 
             
                        else
         | 
| 561 | 
            -
                           | 
| 562 | 
            -
                          keys = keys.split(KbdDelimiterRx).inject([]) {|c, key|
         | 
| 563 | 
            -
                            if key.end_with?('++')
         | 
| 564 | 
            -
                              c << key[0..-3].strip
         | 
| 565 | 
            -
                              c << '+'
         | 
| 566 | 
            -
                            else
         | 
| 567 | 
            -
                              c << key.strip
         | 
| 568 | 
            -
                            end
         | 
| 569 | 
            -
                            c
         | 
| 570 | 
            -
                          }
         | 
| 555 | 
            +
                          keys = [keys]
         | 
| 571 556 | 
             
                        end
         | 
| 572 | 
            -
                        Inline.new | 
| 573 | 
            -
                       | 
| 574 | 
            -
                         | 
| 575 | 
            -
                        Inline.new(self, :button, label).convert
         | 
| 557 | 
            +
                        (Inline.new self, :kbd, nil, :attributes => { 'keys' => keys }).convert
         | 
| 558 | 
            +
                      else # $2 == 'btn'
         | 
| 559 | 
            +
                        (Inline.new self, :button, (unescape_bracketed_text $3)).convert
         | 
| 576 560 | 
             
                      end
         | 
| 577 561 | 
             
                    }
         | 
| 578 562 | 
             
                  end
         | 
| 579 563 |  | 
| 580 | 
            -
                  if  | 
| 581 | 
            -
                    result = result.gsub( | 
| 564 | 
            +
                  if found_macroish && (result.include? 'menu:')
         | 
| 565 | 
            +
                    result = result.gsub(InlineMenuMacroRx) {
         | 
| 582 566 | 
             
                      # alias match for Ruby 1.8.7 compat
         | 
| 583 567 | 
             
                      m = $~
         | 
| 584 568 | 
             
                      # honor the escape
         | 
| 585 | 
            -
                      if (captured = m[0]).start_with?  | 
| 569 | 
            +
                      if (captured = m[0]).start_with? RS
         | 
| 586 570 | 
             
                        next captured[1..-1]
         | 
| 587 571 | 
             
                      end
         | 
| 588 572 |  | 
| 589 | 
            -
                      menu = m[1]
         | 
| 590 | 
            -
                      items = m[2]
         | 
| 573 | 
            +
                      menu, items = m[1], m[2]
         | 
| 591 574 |  | 
| 592 | 
            -
                      if  | 
| 593 | 
            -
                         | 
| 594 | 
            -
                        menuitem = nil
         | 
| 595 | 
            -
                      else
         | 
| 575 | 
            +
                      if items
         | 
| 576 | 
            +
                        items = items.gsub ESC_R_SB, R_SB if items.include? R_SB
         | 
| 596 577 | 
             
                        if (delim = items.include?('>') ? '>' : (items.include?(',') ? ',' : nil))
         | 
| 597 578 | 
             
                          submenus = items.split(delim).map {|it| it.strip }
         | 
| 598 579 | 
             
                          menuitem = submenus.pop
         | 
| 599 580 | 
             
                        else
         | 
| 600 | 
            -
                          submenus = []
         | 
| 601 | 
            -
                          menuitem = items.rstrip
         | 
| 581 | 
            +
                          submenus, menuitem = [], items.rstrip
         | 
| 602 582 | 
             
                        end
         | 
| 583 | 
            +
                      else
         | 
| 584 | 
            +
                        submenus, menuitem = [], nil
         | 
| 603 585 | 
             
                      end
         | 
| 604 586 |  | 
| 605 587 | 
             
                      Inline.new(self, :menu, nil, :attributes => {'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem}).convert
         | 
| 606 588 | 
             
                    }
         | 
| 607 589 | 
             
                  end
         | 
| 608 590 |  | 
| 609 | 
            -
                  if result.include? | 
| 591 | 
            +
                  if (result.include? '"') && (result.include? '>')
         | 
| 610 592 | 
             
                    result = result.gsub(MenuInlineRx) {
         | 
| 611 593 | 
             
                      # alias match for Ruby 1.8.7 compat
         | 
| 612 594 | 
             
                      m = $~
         | 
| 613 595 | 
             
                      # honor the escape
         | 
| 614 | 
            -
                      if (captured = m[0]).start_with?  | 
| 596 | 
            +
                      if (captured = m[0]).start_with? RS
         | 
| 615 597 | 
             
                        next captured[1..-1]
         | 
| 616 598 | 
             
                      end
         | 
| 617 599 |  | 
| @@ -626,114 +608,117 @@ module Substitutors | |
| 626 608 |  | 
| 627 609 | 
             
                # FIXME this location is somewhat arbitrary, probably need to be able to control ordering
         | 
| 628 610 | 
             
                # TODO this handling needs some cleanup
         | 
| 629 | 
            -
                if (extensions = @document.extensions) && extensions.inline_macros? # &&  | 
| 611 | 
            +
                if (extensions = @document.extensions) && extensions.inline_macros? # && found_macroish
         | 
| 630 612 | 
             
                  extensions.inline_macros.each do |extension|
         | 
| 631 613 | 
             
                    result = result.gsub(extension.instance.regexp) {
         | 
| 632 614 | 
             
                      # alias match for Ruby 1.8.7 compat
         | 
| 633 615 | 
             
                      m = $~
         | 
| 634 616 | 
             
                      # honor the escape
         | 
| 635 | 
            -
                      if m[0].start_with?  | 
| 617 | 
            +
                      if m[0].start_with? RS
         | 
| 636 618 | 
             
                        next m[0][1..-1]
         | 
| 637 619 | 
             
                      end
         | 
| 638 620 |  | 
| 639 | 
            -
                       | 
| 640 | 
            -
             | 
| 641 | 
            -
             | 
| 642 | 
            -
                         | 
| 643 | 
            -
             | 
| 621 | 
            +
                      if (m.names rescue []).empty?
         | 
| 622 | 
            +
                        target, content, extconf = m[1], m[2], extension.config
         | 
| 623 | 
            +
                      else
         | 
| 624 | 
            +
                        target, content, extconf = (m[:target] rescue nil), (m[:content] rescue nil), extension.config
         | 
| 625 | 
            +
                      end
         | 
| 626 | 
            +
                      attributes = (attributes = extconf[:default_attrs]) ? attributes.dup : {}
         | 
| 627 | 
            +
                      if content.nil_or_empty?
         | 
| 628 | 
            +
                        attributes['text'] = content if content && extconf[:content_model] != :attributes
         | 
| 644 629 | 
             
                      else
         | 
| 645 | 
            -
                         | 
| 646 | 
            -
             | 
| 630 | 
            +
                        content = unescape_bracketed_text content
         | 
| 631 | 
            +
                        if extconf[:content_model] == :attributes
         | 
| 632 | 
            +
                          # QUESTION should we store the text in the _text key?
         | 
| 633 | 
            +
                          # QUESTION why is the sub_result option false? why isn't the unescape_input option true?
         | 
| 634 | 
            +
                          parse_attributes content, extconf[:pos_attrs] || [], :sub_result => false, :into => attributes
         | 
| 647 635 | 
             
                        else
         | 
| 648 | 
            -
                           | 
| 636 | 
            +
                          attributes['text'] = content
         | 
| 649 637 | 
             
                        end
         | 
| 650 638 | 
             
                      end
         | 
| 651 | 
            -
                       | 
| 639 | 
            +
                      # NOTE use content if target is not set (short form only); deprecated - remove in 1.6.0
         | 
| 640 | 
            +
                      replacement = extension.process_method[self, target || content, attributes]
         | 
| 641 | 
            +
                      Inline === replacement ? replacement.convert : replacement
         | 
| 652 642 | 
             
                    }
         | 
| 653 643 | 
             
                  end
         | 
| 654 644 | 
             
                end
         | 
| 655 645 |  | 
| 656 | 
            -
                if  | 
| 646 | 
            +
                if found_macroish && ((result.include? 'image:') || (result.include? 'icon:'))
         | 
| 657 647 | 
             
                  # image:filename.png[Alt Text]
         | 
| 658 | 
            -
                  result = result.gsub( | 
| 648 | 
            +
                  result = result.gsub(InlineImageMacroRx) {
         | 
| 659 649 | 
             
                    # alias match for Ruby 1.8.7 compat
         | 
| 660 650 | 
             
                    m = $~
         | 
| 661 651 | 
             
                    # honor the escape
         | 
| 662 | 
            -
                    if m[0].start_with?  | 
| 663 | 
            -
                      next  | 
| 652 | 
            +
                    if (captured = m[0]).start_with? RS
         | 
| 653 | 
            +
                      next captured[1..-1]
         | 
| 664 654 | 
             
                    end
         | 
| 665 655 |  | 
| 666 | 
            -
                     | 
| 667 | 
            -
             | 
| 668 | 
            -
                      type = 'icon'
         | 
| 669 | 
            -
                      posattrs = ['size']
         | 
| 656 | 
            +
                    if captured.start_with? 'icon:'
         | 
| 657 | 
            +
                      type, posattrs = 'icon', ['size']
         | 
| 670 658 | 
             
                    else
         | 
| 671 | 
            -
                      type = 'image'
         | 
| 672 | 
            -
                      posattrs = ['alt', 'width', 'height']
         | 
| 659 | 
            +
                      type, posattrs = 'image', ['alt', 'width', 'height']
         | 
| 673 660 | 
             
                    end
         | 
| 674 | 
            -
                    target =  | 
| 675 | 
            -
                    unless type == 'icon'
         | 
| 676 | 
            -
             | 
| 677 | 
            -
                     | 
| 678 | 
            -
                    attrs = parse_attributes(raw_attrs, posattrs)
         | 
| 679 | 
            -
                    attrs['alt'] ||= Helpers.basename(target, true).tr('_-', ' ')
         | 
| 661 | 
            +
                    target = m[1]
         | 
| 662 | 
            +
                    @document.register(:images, target) unless type == 'icon'
         | 
| 663 | 
            +
                    attrs = parse_attributes(m[2], posattrs, :unescape_input => true)
         | 
| 664 | 
            +
                    attrs['alt'] ||= (attrs['default-alt'] = Helpers.basename(target, true).tr('_-', ' '))
         | 
| 680 665 | 
             
                    Inline.new(self, :image, nil, :type => type, :target => target, :attributes => attrs).convert
         | 
| 681 666 | 
             
                  }
         | 
| 682 667 | 
             
                end
         | 
| 683 668 |  | 
| 684 | 
            -
                if  | 
| 685 | 
            -
             | 
| 669 | 
            +
                if ((result.include? '((') && (result.include? '))')) ||
         | 
| 670 | 
            +
                    (found_macroish_short && (result.include? 'indexterm'))
         | 
| 686 671 | 
             
                  # (((Tigers,Big cats)))
         | 
| 687 | 
            -
                  #  | 
| 672 | 
            +
                  # indexterm:[Tigers,Big cats]
         | 
| 688 673 | 
             
                  # ((Tigers))
         | 
| 689 | 
            -
                   | 
| 674 | 
            +
                  # indexterm2:[Tigers]
         | 
| 675 | 
            +
                  result = result.gsub(InlineIndextermMacroRx) {
         | 
| 690 676 | 
             
                    # alias match for Ruby 1.8.7 compat
         | 
| 691 677 | 
             
                    m = $~
         | 
| 692 678 |  | 
| 693 679 | 
             
                    # honor the escape
         | 
| 694 | 
            -
                    if m[0].start_with?  | 
| 680 | 
            +
                    if m[0].start_with? RS
         | 
| 695 681 | 
             
                      next m[0][1..-1]
         | 
| 696 682 | 
             
                    end
         | 
| 697 683 |  | 
| 698 | 
            -
                     | 
| 699 | 
            -
                     | 
| 700 | 
            -
                       | 
| 701 | 
            -
             | 
| 702 | 
            -
             | 
| 703 | 
            -
             | 
| 704 | 
            -
                     | 
| 705 | 
            -
             | 
| 706 | 
            -
                       | 
| 707 | 
            -
                       | 
| 708 | 
            -
             | 
| 709 | 
            -
                        num_brackets = 3
         | 
| 710 | 
            -
                      else
         | 
| 711 | 
            -
                        num_brackets = 2
         | 
| 712 | 
            -
                      end
         | 
| 713 | 
            -
                    end
         | 
| 714 | 
            -
             | 
| 715 | 
            -
                    # non-visible
         | 
| 716 | 
            -
                    if macro_name == 'indexterm' || num_brackets == 3
         | 
| 717 | 
            -
                      if !macro_name
         | 
| 718 | 
            -
                        # (((Tigers,Big cats)))
         | 
| 719 | 
            -
                        terms = split_simple_csv normalize_string(text_in_brackets)
         | 
| 720 | 
            -
                      else
         | 
| 721 | 
            -
                        # indexterm:[Tigers,Big cats]
         | 
| 722 | 
            -
                        terms = split_simple_csv normalize_string(m[2], true)
         | 
| 723 | 
            -
                      end
         | 
| 724 | 
            -
                      @document.register(:indexterms, [*terms])
         | 
| 725 | 
            -
                      Inline.new(self, :indexterm, nil, :attributes => {'terms' => terms}).convert
         | 
| 726 | 
            -
                    # visible
         | 
| 684 | 
            +
                    case m[1]
         | 
| 685 | 
            +
                    when 'indexterm'
         | 
| 686 | 
            +
                      # indexterm:[Tigers,Big cats]
         | 
| 687 | 
            +
                      terms = split_simple_csv(normalize_string m[2], true)
         | 
| 688 | 
            +
                      @document.register :indexterms, terms
         | 
| 689 | 
            +
                      (Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
         | 
| 690 | 
            +
                    when 'indexterm2'
         | 
| 691 | 
            +
                      # indexterm2:[Tigers]
         | 
| 692 | 
            +
                      term = normalize_string m[2], true
         | 
| 693 | 
            +
                      @document.register :indexterms, [term]
         | 
| 694 | 
            +
                      (Inline.new self, :indexterm, term, :type => :visible).convert
         | 
| 727 695 | 
             
                    else
         | 
| 728 | 
            -
                       | 
| 696 | 
            +
                      text, visible, before, after = m[3], true, nil, nil
         | 
| 697 | 
            +
                      if text.start_with? '('
         | 
| 698 | 
            +
                        if text.end_with? ')'
         | 
| 699 | 
            +
                          text, visible = (text.slice 1, text.length - 2), false
         | 
| 700 | 
            +
                        else
         | 
| 701 | 
            +
                          text, before, after = (text.slice 1, text.length - 1), '(', ''
         | 
| 702 | 
            +
                        end
         | 
| 703 | 
            +
                      elsif text.end_with? ')'
         | 
| 704 | 
            +
                        if text.start_with? '('
         | 
| 705 | 
            +
                          text, visible = (text.slice 1, text.length - 2), false
         | 
| 706 | 
            +
                        else
         | 
| 707 | 
            +
                          text, before, after = (text.slice 0, text.length - 1), '', ')'
         | 
| 708 | 
            +
                        end
         | 
| 709 | 
            +
                      end
         | 
| 710 | 
            +
                      if visible
         | 
| 729 711 | 
             
                        # ((Tigers))
         | 
| 730 | 
            -
                         | 
| 712 | 
            +
                        term = normalize_string text
         | 
| 713 | 
            +
                        @document.register :indexterms, [term]
         | 
| 714 | 
            +
                        result = (Inline.new self, :indexterm, term, :type => :visible).convert
         | 
| 731 715 | 
             
                      else
         | 
| 732 | 
            -
                        #  | 
| 733 | 
            -
                         | 
| 716 | 
            +
                        # (((Tigers,Big cats)))
         | 
| 717 | 
            +
                        terms = split_simple_csv(normalize_string text)
         | 
| 718 | 
            +
                        @document.register :indexterms, terms
         | 
| 719 | 
            +
                        result = (Inline.new self, :indexterm, nil, :attributes => { 'terms' => terms }).convert
         | 
| 734 720 | 
             
                      end
         | 
| 735 | 
            -
                       | 
| 736 | 
            -
                      Inline.new(self, :indexterm, text, :type => :visible).convert
         | 
| 721 | 
            +
                      before ? %(#{before}#{result}#{after}) : result
         | 
| 737 722 | 
             
                    end
         | 
| 738 723 | 
             
                  }
         | 
| 739 724 | 
             
                end
         | 
| @@ -744,71 +729,65 @@ module Substitutors | |
| 744 729 | 
             
                    # alias match for Ruby 1.8.7 compat
         | 
| 745 730 | 
             
                    m = $~
         | 
| 746 731 | 
             
                    # honor the escape
         | 
| 747 | 
            -
                    if m[2].start_with?  | 
| 748 | 
            -
                       | 
| 749 | 
            -
                      next "#{m[1]}#{m[2][1..-1]}#{m[3]}"
         | 
| 732 | 
            +
                    if m[2].start_with? RS
         | 
| 733 | 
            +
                      next %(#{m[1]}#{m[2][1..-1]}#{m[3]})
         | 
| 750 734 | 
             
                    end
         | 
| 751 | 
            -
                    #  | 
| 752 | 
            -
                     | 
| 753 | 
            -
             | 
| 754 | 
            -
             | 
| 755 | 
            -
             | 
| 756 | 
            -
             | 
| 757 | 
            -
             | 
| 758 | 
            -
             | 
| 735 | 
            +
                    # NOTE if text is non-nil, then we've matched a formal macro (i.e., trailing square brackets)
         | 
| 736 | 
            +
                    prefix, target, text, suffix = m[1], m[2], (macro = m[3]) || '', ''
         | 
| 737 | 
            +
                    if prefix == 'link:'
         | 
| 738 | 
            +
                      if macro
         | 
| 739 | 
            +
                        prefix = ''
         | 
| 740 | 
            +
                      else
         | 
| 741 | 
            +
                        # invalid macro syntax (link: prefix w/o trailing square brackets)
         | 
| 742 | 
            +
                        # we probably shouldn't even get here...our regex is doing too much
         | 
| 743 | 
            +
                        next m[0]
         | 
| 744 | 
            +
                      end
         | 
| 759 745 | 
             
                    end
         | 
| 760 | 
            -
                     | 
| 761 | 
            -
             | 
| 762 | 
            -
                    suffix = ''
         | 
| 763 | 
            -
                    unless m[3] || target !~ UriTerminator
         | 
| 764 | 
            -
                      case $~[0]
         | 
| 746 | 
            +
                    unless macro || UriTerminatorRx !~ target
         | 
| 747 | 
            +
                      case $&
         | 
| 765 748 | 
             
                      when ')'
         | 
| 766 | 
            -
                        # strip  | 
| 767 | 
            -
                        target = target | 
| 749 | 
            +
                        # strip trailing )
         | 
| 750 | 
            +
                        target = target.chop
         | 
| 768 751 | 
             
                        suffix = ')'
         | 
| 769 752 | 
             
                      when ';'
         | 
| 770 | 
            -
                        # strip  | 
| 753 | 
            +
                        # strip <> around URI
         | 
| 771 754 | 
             
                        if prefix.start_with?('<') && target.end_with?('>')
         | 
| 772 755 | 
             
                          prefix = prefix[4..-1]
         | 
| 773 | 
            -
                          target = target[0 | 
| 774 | 
            -
                        # strip the ); from the end of the link
         | 
| 775 | 
            -
                        elsif target.end_with?(');')
         | 
| 776 | 
            -
                          target = target[0..-3]
         | 
| 777 | 
            -
                          suffix = ');'
         | 
| 756 | 
            +
                          target = target[0...-4]
         | 
| 778 757 | 
             
                        else
         | 
| 779 | 
            -
                           | 
| 780 | 
            -
                           | 
| 758 | 
            +
                          # strip trailing ;
         | 
| 759 | 
            +
                          # check for trailing );
         | 
| 760 | 
            +
                          if (target = target.chop).end_with?(')')
         | 
| 761 | 
            +
                            target = target.chop
         | 
| 762 | 
            +
                            suffix = ');'
         | 
| 763 | 
            +
                          else
         | 
| 764 | 
            +
                            suffix = ';'
         | 
| 765 | 
            +
                          end
         | 
| 781 766 | 
             
                        end
         | 
| 782 767 | 
             
                      when ':'
         | 
| 783 | 
            -
                        # strip  | 
| 784 | 
            -
                         | 
| 785 | 
            -
             | 
| 768 | 
            +
                        # strip trailing :
         | 
| 769 | 
            +
                        # check for trailing ):
         | 
| 770 | 
            +
                        if (target = target.chop).end_with?(')')
         | 
| 771 | 
            +
                          target = target.chop
         | 
| 786 772 | 
             
                          suffix = '):'
         | 
| 787 773 | 
             
                        else
         | 
| 788 | 
            -
                          target = target[0..-2]
         | 
| 789 774 | 
             
                          suffix = ':'
         | 
| 790 775 | 
             
                        end
         | 
| 791 776 | 
             
                      end
         | 
| 792 777 | 
             
                    end
         | 
| 793 | 
            -
                    @document.register(:links, target)
         | 
| 794 778 |  | 
| 795 | 
            -
                    link_opts = { :type => :link | 
| 796 | 
            -
                     | 
| 797 | 
            -
             | 
| 798 | 
            -
             | 
| 799 | 
            -
             | 
| 800 | 
            -
             | 
| 801 | 
            -
                      if use_link_attrs && (m[3].start_with?('"') || (m[3].include?(',') && m[3].include?('=')))
         | 
| 802 | 
            -
                        attrs = parse_attributes(sub_attributes(m[3].gsub('\]', ']')), [])
         | 
| 803 | 
            -
                        link_opts[:id] = (attrs.delete 'id') if attrs.has_key? 'id'
         | 
| 779 | 
            +
                    attrs, link_opts = nil, { :type => :link }
         | 
| 780 | 
            +
                    unless text.empty?
         | 
| 781 | 
            +
                      text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
         | 
| 782 | 
            +
                      if use_link_attrs && ((text.start_with? '"') || ((text.include? ',') && (text.include? '=')))
         | 
| 783 | 
            +
                        attrs = parse_attributes text, []
         | 
| 784 | 
            +
                        link_opts[:id] = attrs.delete 'id' if attrs.key? 'id'
         | 
| 804 785 | 
             
                        text = attrs[1] || ''
         | 
| 805 | 
            -
                      else
         | 
| 806 | 
            -
                        text = sub_attributes(m[3].gsub('\]', ']'))
         | 
| 807 786 | 
             
                      end
         | 
| 808 787 |  | 
| 809 | 
            -
                      # TODO enable in Asciidoctor 1. | 
| 788 | 
            +
                      # TODO enable in Asciidoctor 1.6.x
         | 
| 810 789 | 
             
                      # support pipe-separated text and title
         | 
| 811 | 
            -
                      #unless attrs && (attrs. | 
| 790 | 
            +
                      #unless attrs && (attrs.key? 'title')
         | 
| 812 791 | 
             
                      #  if text.include? '|'
         | 
| 813 792 | 
             
                      #    attrs ||= {}
         | 
| 814 793 | 
             
                      #    text, attrs['title'] = text.split '|', 2
         | 
| @@ -820,120 +799,99 @@ module Substitutors | |
| 820 799 | 
             
                        if attrs
         | 
| 821 800 | 
             
                          attrs['window'] ||= '_blank'
         | 
| 822 801 | 
             
                        else
         | 
| 823 | 
            -
                          attrs = {'window' => '_blank'}
         | 
| 802 | 
            +
                          attrs = { 'window' => '_blank' }
         | 
| 824 803 | 
             
                        end
         | 
| 825 804 | 
             
                      end
         | 
| 826 805 | 
             
                    end
         | 
| 827 806 |  | 
| 828 807 | 
             
                    if text.empty?
         | 
| 829 | 
            -
                       | 
| 830 | 
            -
                        text = target.sub UriSniffRx, ''
         | 
| 831 | 
            -
                      else
         | 
| 832 | 
            -
                        text = target
         | 
| 833 | 
            -
                      end
         | 
| 834 | 
            -
             | 
| 808 | 
            +
                      text = (doc_attrs.key? 'hide-uri-scheme') ? (target.sub UriSniffRx, '') : target
         | 
| 835 809 | 
             
                      if attrs
         | 
| 836 | 
            -
                        attrs['role'] = %(bare #{attrs['role']}) | 
| 810 | 
            +
                        attrs['role'] = (attrs.key? 'role') ? %(bare #{attrs['role']}) : 'bare'
         | 
| 837 811 | 
             
                      else
         | 
| 838 | 
            -
                        attrs = {'role' => 'bare'}
         | 
| 812 | 
            +
                        attrs = { 'role' => 'bare' }
         | 
| 839 813 | 
             
                      end
         | 
| 840 814 | 
             
                    end
         | 
| 841 815 |  | 
| 816 | 
            +
                    @document.register :links, (link_opts[:target] = target)
         | 
| 842 817 | 
             
                    link_opts[:attributes] = attrs if attrs
         | 
| 843 818 | 
             
                    %(#{prefix}#{Inline.new(self, :anchor, text, link_opts).convert}#{suffix})
         | 
| 844 819 | 
             
                  }
         | 
| 845 820 | 
             
                end
         | 
| 846 821 |  | 
| 847 | 
            -
                if  | 
| 822 | 
            +
                if found_macroish && ((result.include? 'link:') || (result.include? 'mailto:'))
         | 
| 848 823 | 
             
                  # inline link macros, link:target[text]
         | 
| 849 | 
            -
                  result = result.gsub( | 
| 824 | 
            +
                  result = result.gsub(InlineLinkMacroRx) {
         | 
| 850 825 | 
             
                    # alias match for Ruby 1.8.7 compat
         | 
| 851 826 | 
             
                    m = $~
         | 
| 852 827 | 
             
                    # honor the escape
         | 
| 853 | 
            -
                    if m[0].start_with?  | 
| 828 | 
            +
                    if m[0].start_with? RS
         | 
| 854 829 | 
             
                      next m[0][1..-1]
         | 
| 855 830 | 
             
                    end
         | 
| 856 | 
            -
                     | 
| 857 | 
            -
                     | 
| 858 | 
            -
                     | 
| 859 | 
            -
             | 
| 860 | 
            -
             | 
| 861 | 
            -
             | 
| 862 | 
            -
             | 
| 863 | 
            -
             | 
| 864 | 
            -
             | 
| 865 | 
            -
             | 
| 866 | 
            -
             | 
| 867 | 
            -
             | 
| 868 | 
            -
             | 
| 869 | 
            -
             | 
| 870 | 
            -
                          if attrs.key? 3
         | 
| 871 | 
            -
                            target = link_opts[:target] = "#{target}&body=#{Helpers.encode_uri(attrs[3])}"
         | 
| 831 | 
            +
                    target = (mailto = m[1]) ? %(mailto:#{m[2]}) : m[2]
         | 
| 832 | 
            +
                    attrs, link_opts = nil, { :type => :link }
         | 
| 833 | 
            +
                    unless (text = m[3]).empty?
         | 
| 834 | 
            +
                      text = text.gsub ESC_R_SB, R_SB if text.include? R_SB
         | 
| 835 | 
            +
                      if use_link_attrs && ((text.start_with? '"') || ((text.include? ',') && (mailto || (text.include? '='))))
         | 
| 836 | 
            +
                        attrs = parse_attributes text, []
         | 
| 837 | 
            +
                        link_opts[:id] = attrs.delete 'id' if attrs.key? 'id'
         | 
| 838 | 
            +
                        if mailto
         | 
| 839 | 
            +
                          if attrs.key? 2
         | 
| 840 | 
            +
                            if attrs.key? 3
         | 
| 841 | 
            +
                              target = %(#{target}?subject=#{Helpers.uri_encode attrs[2]}&body=#{Helpers.uri_encode attrs[3]})
         | 
| 842 | 
            +
                            else
         | 
| 843 | 
            +
                              target = %(#{target}?subject=#{Helpers.uri_encode attrs[2]})
         | 
| 844 | 
            +
                            end
         | 
| 872 845 | 
             
                          end
         | 
| 873 846 | 
             
                        end
         | 
| 847 | 
            +
                        text = attrs[1] || ''
         | 
| 874 848 | 
             
                      end
         | 
| 875 | 
            -
                      attrs[1]
         | 
| 876 | 
            -
                    else
         | 
| 877 | 
            -
                      sub_attributes(m[2].gsub('\]', ']'))
         | 
| 878 | 
            -
                    end
         | 
| 879 849 |  | 
| 880 | 
            -
             | 
| 881 | 
            -
             | 
| 850 | 
            +
                      # TODO enable in Asciidoctor 1.6.x
         | 
| 851 | 
            +
                      # support pipe-separated text and title
         | 
| 852 | 
            +
                      #unless attrs && (attrs.key? 'title')
         | 
| 853 | 
            +
                      #  if text.include? '|'
         | 
| 854 | 
            +
                      #    attrs ||= {}
         | 
| 855 | 
            +
                      #    text, attrs['title'] = text.split '|', 2
         | 
| 856 | 
            +
                      #  end
         | 
| 857 | 
            +
                      #end
         | 
| 882 858 |  | 
| 883 | 
            -
             | 
| 884 | 
            -
             | 
| 885 | 
            -
             | 
| 886 | 
            -
             | 
| 887 | 
            -
             | 
| 888 | 
            -
             | 
| 889 | 
            -
             | 
| 890 | 
            -
                    #end
         | 
| 891 | 
            -
             | 
| 892 | 
            -
                    if text.end_with? '^'
         | 
| 893 | 
            -
                      text = text.chop
         | 
| 894 | 
            -
                      if attrs
         | 
| 895 | 
            -
                        attrs['window'] ||= '_blank'
         | 
| 896 | 
            -
                      else
         | 
| 897 | 
            -
                        attrs = {'window' => '_blank'}
         | 
| 859 | 
            +
                      if text.end_with? '^'
         | 
| 860 | 
            +
                        text = text.chop
         | 
| 861 | 
            +
                        if attrs
         | 
| 862 | 
            +
                          attrs['window'] ||= '_blank'
         | 
| 863 | 
            +
                        else
         | 
| 864 | 
            +
                          attrs = { 'window' => '_blank' }
         | 
| 865 | 
            +
                        end
         | 
| 898 866 | 
             
                      end
         | 
| 899 867 | 
             
                    end
         | 
| 900 868 |  | 
| 901 869 | 
             
                    if text.empty?
         | 
| 902 870 | 
             
                      # mailto is a special case, already processed
         | 
| 903 871 | 
             
                      if mailto
         | 
| 904 | 
            -
                        text =  | 
| 872 | 
            +
                        text = m[2]
         | 
| 905 873 | 
             
                      else
         | 
| 906 | 
            -
                         | 
| 907 | 
            -
                          text = raw_target.sub UriSniffRx, ''
         | 
| 908 | 
            -
                        else
         | 
| 909 | 
            -
                          text = raw_target
         | 
| 910 | 
            -
                        end
         | 
| 911 | 
            -
             | 
| 874 | 
            +
                        text = (doc_attrs.key? 'hide-uri-scheme') ? (target.sub UriSniffRx, '') : target
         | 
| 912 875 | 
             
                        if attrs
         | 
| 913 | 
            -
                          attrs['role'] = %(bare #{attrs['role']}) | 
| 876 | 
            +
                          attrs['role'] = (attrs.key? 'role') ? %(bare #{attrs['role']}) : 'bare'
         | 
| 914 877 | 
             
                        else
         | 
| 915 | 
            -
                          attrs = {'role' => 'bare'}
         | 
| 878 | 
            +
                          attrs = { 'role' => 'bare' }
         | 
| 916 879 | 
             
                        end
         | 
| 917 880 | 
             
                      end
         | 
| 918 881 | 
             
                    end
         | 
| 919 882 |  | 
| 883 | 
            +
                    # QUESTION should a mailto be registered as an e-mail address?
         | 
| 884 | 
            +
                    @document.register :links, (link_opts[:target] = target)
         | 
| 920 885 | 
             
                    link_opts[:attributes] = attrs if attrs
         | 
| 921 886 | 
             
                    Inline.new(self, :anchor, text, link_opts).convert
         | 
| 922 887 | 
             
                  }
         | 
| 923 888 | 
             
                end
         | 
| 924 889 |  | 
| 925 890 | 
             
                if result.include? '@'
         | 
| 926 | 
            -
                  result = result.gsub( | 
| 927 | 
            -
                     | 
| 928 | 
            -
                     | 
| 929 | 
            -
             | 
| 930 | 
            -
                    if (lead = m[1])
         | 
| 931 | 
            -
                      case lead
         | 
| 932 | 
            -
                      when '\\'
         | 
| 933 | 
            -
                        next address[1..-1]
         | 
| 934 | 
            -
                      else
         | 
| 935 | 
            -
                        next address
         | 
| 936 | 
            -
                      end
         | 
| 891 | 
            +
                  result = result.gsub(EmailInlineRx) {
         | 
| 892 | 
            +
                    address, tip = $&, $1
         | 
| 893 | 
            +
                    if tip
         | 
| 894 | 
            +
                      next (tip == RS ? address[1..-1] : address)
         | 
| 937 895 | 
             
                    end
         | 
| 938 896 |  | 
| 939 897 | 
             
                    target = %(mailto:#{address})
         | 
| @@ -944,12 +902,12 @@ module Substitutors | |
| 944 902 | 
             
                  }
         | 
| 945 903 | 
             
                end
         | 
| 946 904 |  | 
| 947 | 
            -
                if  | 
| 948 | 
            -
                  result = result.gsub( | 
| 905 | 
            +
                if found_macroish_short && (result.include? 'footnote')
         | 
| 906 | 
            +
                  result = result.gsub(InlineFootnoteMacroRx) {
         | 
| 949 907 | 
             
                    # alias match for Ruby 1.8.7 compat
         | 
| 950 908 | 
             
                    m = $~
         | 
| 951 909 | 
             
                    # honor the escape
         | 
| 952 | 
            -
                    if m[0].start_with?  | 
| 910 | 
            +
                    if m[0].start_with? RS
         | 
| 953 911 | 
             
                      next m[0][1..-1]
         | 
| 954 912 | 
             
                    end
         | 
| 955 913 | 
             
                    if m[1] == 'footnote'
         | 
| @@ -963,9 +921,15 @@ module Substitutors | |
| 963 921 | 
             
                    else
         | 
| 964 922 | 
             
                      id, text = m[2].split(',', 2)
         | 
| 965 923 | 
             
                      id = id.strip
         | 
| 966 | 
            -
                       | 
| 967 | 
            -
             | 
| 968 | 
            -
                         | 
| 924 | 
            +
                      if text
         | 
| 925 | 
            +
                        # REVIEW it's a dirty job, but somebody's gotta do it
         | 
| 926 | 
            +
                        text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)), false)
         | 
| 927 | 
            +
                        index = @document.counter('footnote-number')
         | 
| 928 | 
            +
                        @document.register(:footnotes, Document::Footnote.new(index, id, text))
         | 
| 929 | 
            +
                        type = :ref
         | 
| 930 | 
            +
                        target = nil
         | 
| 931 | 
            +
                      else
         | 
| 932 | 
            +
                        if (footnote = @document.footnotes.find {|fn| fn.id == id })
         | 
| 969 933 | 
             
                          index = footnote.index
         | 
| 970 934 | 
             
                          text = footnote.text
         | 
| 971 935 | 
             
                        else
         | 
| @@ -975,13 +939,6 @@ module Substitutors | |
| 975 939 | 
             
                        target = id
         | 
| 976 940 | 
             
                        id = nil
         | 
| 977 941 | 
             
                        type = :xref
         | 
| 978 | 
            -
                      else
         | 
| 979 | 
            -
                        # REVIEW it's a dirty job, but somebody's gotta do it
         | 
| 980 | 
            -
                        text = restore_passthroughs(sub_inline_xrefs(sub_inline_anchors(normalize_string text, true)), false)
         | 
| 981 | 
            -
                        index = @document.counter('footnote-number')
         | 
| 982 | 
            -
                        @document.register(:footnotes, Document::Footnote.new(index, id, text))
         | 
| 983 | 
            -
                        type = :ref
         | 
| 984 | 
            -
                        target = nil
         | 
| 985 942 | 
             
                      end
         | 
| 986 943 | 
             
                    end
         | 
| 987 944 | 
             
                    Inline.new(self, :footnote, text, :attributes => {'index' => index}, :id => id, :target => target, :type => type).convert
         | 
| @@ -993,44 +950,29 @@ module Substitutors | |
| 993 950 |  | 
| 994 951 | 
             
              # Internal: Substitute normal and bibliographic anchors
         | 
| 995 952 | 
             
              def sub_inline_anchors(text, found = nil)
         | 
| 996 | 
            -
                if  | 
| 997 | 
            -
                  text = text. | 
| 998 | 
            -
                    #  | 
| 999 | 
            -
                     | 
| 1000 | 
            -
                    # honor the escape
         | 
| 1001 | 
            -
                    if m[0].start_with? '\\'
         | 
| 1002 | 
            -
                      next m[0][1..-1]
         | 
| 1003 | 
            -
                    end
         | 
| 1004 | 
            -
                    id = reftext = m[1]
         | 
| 1005 | 
            -
                    Inline.new(self, :anchor, reftext, :type => :bibref, :target => id).convert
         | 
| 953 | 
            +
                if @context == :list_item && @parent.style == 'bibliography'
         | 
| 954 | 
            +
                  text = text.sub(InlineBiblioAnchorRx) {
         | 
| 955 | 
            +
                    # NOTE target property on :bibref is deprecated
         | 
| 956 | 
            +
                    Inline.new(self, :anchor, %([#{$2 || $1}]), :type => :bibref, :id => $1, :target => $1).convert
         | 
| 1006 957 | 
             
                  }
         | 
| 1007 958 | 
             
                end
         | 
| 1008 959 |  | 
| 1009 960 | 
             
                if ((!found || found[:square_bracket]) && text.include?('[[')) ||
         | 
| 1010 | 
            -
                    ((!found || found[:macroish]) && text.include?(' | 
| 961 | 
            +
                    ((!found || found[:macroish]) && text.include?('or:'))
         | 
| 1011 962 | 
             
                  text = text.gsub(InlineAnchorRx) {
         | 
| 1012 | 
            -
                    # alias match for Ruby 1.8.7 compat
         | 
| 1013 | 
            -
                    m = $~
         | 
| 1014 963 | 
             
                    # honor the escape
         | 
| 1015 | 
            -
                    if  | 
| 1016 | 
            -
             | 
| 1017 | 
            -
                     | 
| 1018 | 
            -
             | 
| 1019 | 
            -
                     | 
| 1020 | 
            -
                       | 
| 1021 | 
            -
                       | 
| 1022 | 
            -
             | 
| 964 | 
            +
                    next $&.slice 1, $&.length if $1
         | 
| 965 | 
            +
                    # NOTE reftext is only relevant for DocBook output; used as value of xreflabel attribute
         | 
| 966 | 
            +
                    if (id = $2)
         | 
| 967 | 
            +
                      reftext = $3
         | 
| 968 | 
            +
                    else
         | 
| 969 | 
            +
                      id = $4
         | 
| 970 | 
            +
                      if (reftext = $5) && (reftext.include? R_SB)
         | 
| 971 | 
            +
                        reftext = reftext.gsub ESC_R_SB, R_SB
         | 
| 972 | 
            +
                      end
         | 
| 1023 973 | 
             
                    end
         | 
| 1024 | 
            -
                     | 
| 1025 | 
            -
                    reftext  | 
| 1026 | 
            -
                    # enable if we want to allow double quoted values
         | 
| 1027 | 
            -
                    #id = id.sub(DoubleQuotedRx, '\2')
         | 
| 1028 | 
            -
                    #if reftext
         | 
| 1029 | 
            -
                    #  reftext = reftext.sub(DoubleQuotedMultiRx, '\2')
         | 
| 1030 | 
            -
                    #else
         | 
| 1031 | 
            -
                    #  reftext = "[#{id}]"
         | 
| 1032 | 
            -
                    #end
         | 
| 1033 | 
            -
                    Inline.new(self, :anchor, reftext, :type => :ref, :target => id).convert
         | 
| 974 | 
            +
                    # NOTE target property on :ref is deprecated
         | 
| 975 | 
            +
                    Inline.new(self, :anchor, reftext, :type => :ref, :id => id, :target => id).convert
         | 
| 1034 976 | 
             
                  }
         | 
| 1035 977 | 
             
                end
         | 
| 1036 978 |  | 
| @@ -1039,65 +981,67 @@ module Substitutors | |
| 1039 981 |  | 
| 1040 982 | 
             
              # Internal: Substitute cross reference links
         | 
| 1041 983 | 
             
              def sub_inline_xrefs(text, found = nil)
         | 
| 1042 | 
            -
                if ( | 
| 1043 | 
            -
             | 
| 984 | 
            +
                if ((found ? found[:macroish] : (text.include? '[')) && (text.include? 'xref:')) ||
         | 
| 985 | 
            +
                    ((text.include? '&') && (text.include? '<<'))
         | 
| 986 | 
            +
                  text = text.gsub(InlineXrefMacroRx) {
         | 
| 1044 987 | 
             
                    # alias match for Ruby 1.8.7 compat
         | 
| 1045 988 | 
             
                    m = $~
         | 
| 1046 989 | 
             
                    # honor the escape
         | 
| 1047 | 
            -
                    if m[0].start_with?  | 
| 990 | 
            +
                    if m[0].start_with? RS
         | 
| 1048 991 | 
             
                      next m[0][1..-1]
         | 
| 1049 992 | 
             
                    end
         | 
| 1050 | 
            -
                     | 
| 1051 | 
            -
             | 
| 1052 | 
            -
                       | 
| 1053 | 
            -
                    end
         | 
| 1054 | 
            -
                    if m[1]
         | 
| 1055 | 
            -
                      id, reftext = m[1].split(',', 2).map {|it| it.strip }
         | 
| 1056 | 
            -
                      id = id.sub(DoubleQuotedRx, '\2')
         | 
| 1057 | 
            -
                      # NOTE In Opal, reftext is set to empty string if comma is missing
         | 
| 1058 | 
            -
                      reftext = if reftext.nil_or_empty?
         | 
| 1059 | 
            -
                        nil
         | 
| 1060 | 
            -
                      else
         | 
| 1061 | 
            -
                        reftext.sub(DoubleQuotedMultiRx, '\2')
         | 
| 1062 | 
            -
                      end
         | 
| 993 | 
            +
                    if (id = m[1])
         | 
| 994 | 
            +
                      id, reftext = id.split ',', 2
         | 
| 995 | 
            +
                      reftext = reftext.lstrip if reftext
         | 
| 1063 996 | 
             
                    else
         | 
| 1064 997 | 
             
                      id = m[2]
         | 
| 1065 | 
            -
                      reftext = m[3]  | 
| 998 | 
            +
                      if (reftext = m[3]) && (reftext.include? R_SB)
         | 
| 999 | 
            +
                        reftext = reftext.gsub ESC_R_SB, R_SB
         | 
| 1000 | 
            +
                      end
         | 
| 1066 1001 | 
             
                    end
         | 
| 1067 1002 |  | 
| 1068 | 
            -
                    if id. | 
| 1069 | 
            -
                       | 
| 1070 | 
            -
             | 
| 1071 | 
            -
             | 
| 1072 | 
            -
             | 
| 1003 | 
            +
                    if (hash_idx = id.index '#')
         | 
| 1004 | 
            +
                      if hash_idx > 0
         | 
| 1005 | 
            +
                        if (fragment_len = id.length - hash_idx - 1) > 0
         | 
| 1006 | 
            +
                          path, fragment = (id.slice 0, hash_idx), (id.slice hash_idx + 1, fragment_len)
         | 
| 1007 | 
            +
                        else
         | 
| 1008 | 
            +
                          path, fragment = (id.slice 0, hash_idx), nil
         | 
| 1009 | 
            +
                        end
         | 
| 1010 | 
            +
                      else
         | 
| 1011 | 
            +
                        target, path, fragment = id, nil, (id.slice 1, id.length)
         | 
| 1012 | 
            +
                      end
         | 
| 1073 1013 | 
             
                    else
         | 
| 1074 | 
            -
                      path = nil
         | 
| 1075 | 
            -
                      fragment = id
         | 
| 1014 | 
            +
                      path, fragment = nil, id
         | 
| 1076 1015 | 
             
                    end
         | 
| 1077 1016 |  | 
| 1078 | 
            -
                    # handles | 
| 1079 | 
            -
                    if  | 
| 1080 | 
            -
                       | 
| 1017 | 
            +
                    # handles: #id
         | 
| 1018 | 
            +
                    if target
         | 
| 1019 | 
            +
                      refid = fragment
         | 
| 1020 | 
            +
                    # handles: path#, path.adoc#, path#id, or path.adoc#id
         | 
| 1021 | 
            +
                    elsif path
         | 
| 1022 | 
            +
                      if (ext_idx = path.rindex '.') && ASCIIDOC_EXTENSIONS[path.slice ext_idx, path.length]
         | 
| 1023 | 
            +
                        path = path.slice 0, ext_idx
         | 
| 1024 | 
            +
                      end
         | 
| 1081 1025 | 
             
                      # the referenced path is this document, or its contents has been included in this document
         | 
| 1082 | 
            -
                      if @document.attributes['docname'] == path || @document. | 
| 1083 | 
            -
                        refid = fragment
         | 
| 1084 | 
            -
                        path = nil
         | 
| 1085 | 
            -
                        target = %(##{fragment})
         | 
| 1026 | 
            +
                      if @document.attributes['docname'] == path || @document.catalog[:includes].include?(path)
         | 
| 1027 | 
            +
                        refid, path, target = fragment, nil, %(##{fragment})
         | 
| 1086 1028 | 
             
                      else
         | 
| 1087 1029 | 
             
                        refid = fragment ? %(#{path}##{fragment}) : path
         | 
| 1088 | 
            -
                        path =  | 
| 1030 | 
            +
                        path = %(#{@document.attributes['relfileprefix']}#{path}#{@document.attributes.fetch 'outfilesuffix', '.html'})
         | 
| 1089 1031 | 
             
                        target = fragment ? %(#{path}##{fragment}) : path
         | 
| 1090 1032 | 
             
                      end
         | 
| 1091 | 
            -
                    # handles | 
| 1033 | 
            +
                    # handles: id or Section Title
         | 
| 1092 1034 | 
             
                    else
         | 
| 1093 | 
            -
                      # resolve fragment as reftext if  | 
| 1094 | 
            -
                       | 
| 1095 | 
            -
             | 
| 1096 | 
            -
             | 
| 1097 | 
            -
             | 
| 1035 | 
            +
                      # resolve fragment as reftext if it's not a known ID and resembles reftext (includes space or has uppercase char)
         | 
| 1036 | 
            +
                      unless @document.catalog[:ids].key? fragment
         | 
| 1037 | 
            +
                        if ((fragment.include? ' ') || fragment.downcase != fragment) &&
         | 
| 1038 | 
            +
                            (resolved_id = @document.catalog[:ids].key fragment)
         | 
| 1039 | 
            +
                          fragment = resolved_id
         | 
| 1040 | 
            +
                        elsif $VERBOSE
         | 
| 1041 | 
            +
                          warn %(asciidoctor: WARNING: invalid reference: #{fragment})
         | 
| 1042 | 
            +
                        end
         | 
| 1098 1043 | 
             
                      end
         | 
| 1099 | 
            -
                      refid = fragment
         | 
| 1100 | 
            -
                      target = %(##{fragment})
         | 
| 1044 | 
            +
                      refid, target = fragment, %(##{fragment})
         | 
| 1101 1045 | 
             
                    end
         | 
| 1102 1046 | 
             
                    Inline.new(self, :anchor, reftext, :type => :xref, :target => target, :attributes => {'path' => path, 'fragment' => fragment, 'refid' => refid}).convert
         | 
| 1103 1047 | 
             
                  }
         | 
| @@ -1115,14 +1059,11 @@ module Substitutors | |
| 1115 1059 | 
             
                # FIXME cache this dynamic regex
         | 
| 1116 1060 | 
             
                callout_rx = (attr? 'line-comment') ? /(?:#{::Regexp.escape(attr 'line-comment')} )?#{CalloutSourceRxt}/ : CalloutSourceRx
         | 
| 1117 1061 | 
             
                text.gsub(callout_rx) {
         | 
| 1118 | 
            -
                   | 
| 1119 | 
            -
             | 
| 1120 | 
            -
             | 
| 1121 | 
            -
                  if m[1] == '\\'
         | 
| 1122 | 
            -
                    # we have to do a sub since we aren't sure it's the first char
         | 
| 1123 | 
            -
                    next m[0].sub('\\', '')
         | 
| 1062 | 
            +
                  if $1
         | 
| 1063 | 
            +
                    # we have to use sub since we aren't sure it's the first char
         | 
| 1064 | 
            +
                    next $&.sub(RS, '')
         | 
| 1124 1065 | 
             
                  end
         | 
| 1125 | 
            -
                  Inline.new(self, :callout,  | 
| 1066 | 
            +
                  Inline.new(self, :callout, $3, :id => @document.callouts.read_next_id).convert
         | 
| 1126 1067 | 
             
                }
         | 
| 1127 1068 | 
             
              end
         | 
| 1128 1069 |  | 
| @@ -1132,13 +1073,15 @@ module Substitutors | |
| 1132 1073 | 
             
              #
         | 
| 1133 1074 | 
             
              # Returns the converted String text
         | 
| 1134 1075 | 
             
              def sub_post_replacements(text)
         | 
| 1135 | 
            -
                if (@document.attributes. | 
| 1136 | 
            -
                  lines =  | 
| 1137 | 
            -
                  return text if lines.size  | 
| 1076 | 
            +
                if (@document.attributes.key? 'hardbreaks') || (@attributes.key? 'hardbreaks-option')
         | 
| 1077 | 
            +
                  lines = text.split LF, -1
         | 
| 1078 | 
            +
                  return text if lines.size < 2
         | 
| 1138 1079 | 
             
                  last = lines.pop
         | 
| 1139 | 
            -
                  lines.map {|line| | 
| 1140 | 
            -
             | 
| 1141 | 
            -
                   | 
| 1080 | 
            +
                  (lines.map {|line|
         | 
| 1081 | 
            +
                    Inline.new(self, :break, (line.end_with? HARD_LINE_BREAK) ? (line.slice 0, line.length - 2) : line, :type => :line).convert
         | 
| 1082 | 
            +
                  } << last) * LF
         | 
| 1083 | 
            +
                elsif (text.include? PLUS) && (text.include? HARD_LINE_BREAK)
         | 
| 1084 | 
            +
                  text.gsub(HardLineBreakRx) { Inline.new(self, :break, $1, :type => :line).convert }
         | 
| 1142 1085 | 
             
                else
         | 
| 1143 1086 | 
             
                  text
         | 
| 1144 1087 | 
             
                end
         | 
| @@ -1152,8 +1095,7 @@ module Substitutors | |
| 1152 1095 | 
             
              #
         | 
| 1153 1096 | 
             
              # Returns The converted String text for the quoted text region
         | 
| 1154 1097 | 
             
              def convert_quoted_text(match, type, scope)
         | 
| 1155 | 
            -
                 | 
| 1156 | 
            -
                if match[0].start_with? '\\'
         | 
| 1098 | 
            +
                if match[0].start_with? RS
         | 
| 1157 1099 | 
             
                  if scope == :constrained && !(attrs = match[2]).nil_or_empty?
         | 
| 1158 1100 | 
             
                    unescaped_attrs = %([#{attrs}])
         | 
| 1159 1101 | 
             
                  else
         | 
| @@ -1165,44 +1107,39 @@ module Substitutors | |
| 1165 1107 | 
             
                  if unescaped_attrs
         | 
| 1166 1108 | 
             
                    %(#{unescaped_attrs}#{Inline.new(self, :quoted, match[3], :type => type).convert})
         | 
| 1167 1109 | 
             
                  else
         | 
| 1168 | 
            -
                    if ( | 
| 1169 | 
            -
                      id = attributes.delete 'id'
         | 
| 1110 | 
            +
                    if (attrlist = match[2])
         | 
| 1111 | 
            +
                      id = (attributes = parse_quoted_text_attributes attrlist).delete 'id'
         | 
| 1170 1112 | 
             
                      type = :unquoted if type == :mark
         | 
| 1171 | 
            -
                    else
         | 
| 1172 | 
            -
                      id = nil
         | 
| 1173 1113 | 
             
                    end
         | 
| 1174 1114 | 
             
                    %(#{match[1]}#{Inline.new(self, :quoted, match[3], :type => type, :id => id, :attributes => attributes).convert})
         | 
| 1175 1115 | 
             
                  end
         | 
| 1176 1116 | 
             
                else
         | 
| 1177 | 
            -
                  if ( | 
| 1178 | 
            -
                    id = attributes.delete 'id'
         | 
| 1117 | 
            +
                  if (attrlist = match[1])
         | 
| 1118 | 
            +
                    id = (attributes = parse_quoted_text_attributes attrlist).delete 'id'
         | 
| 1179 1119 | 
             
                    type = :unquoted if type == :mark
         | 
| 1180 | 
            -
                  else
         | 
| 1181 | 
            -
                    id = nil
         | 
| 1182 1120 | 
             
                  end
         | 
| 1183 1121 | 
             
                  Inline.new(self, :quoted, match[2], :type => type, :id => id, :attributes => attributes).convert
         | 
| 1184 1122 | 
             
                end
         | 
| 1185 1123 | 
             
              end
         | 
| 1186 1124 |  | 
| 1187 | 
            -
              # Internal: Parse the attributes that are defined on quoted text
         | 
| 1125 | 
            +
              # Internal: Parse the attributes that are defined on quoted (aka formatted) text
         | 
| 1188 1126 | 
             
              #
         | 
| 1189 | 
            -
              # str | 
| 1127 | 
            +
              # str - A non-nil String of unprocessed attributes;
         | 
| 1128 | 
            +
              #       space-separated roles (e.g., role1 role2) or the id/role shorthand syntax (e.g., #idname.role)
         | 
| 1190 1129 | 
             
              #
         | 
| 1191 | 
            -
              #  | 
| 1192 | 
            -
              def parse_quoted_text_attributes | 
| 1193 | 
            -
                 | 
| 1194 | 
            -
                 | 
| 1195 | 
            -
                str = sub_attributes(str) if str.include?('{')
         | 
| 1196 | 
            -
                str = str.strip
         | 
| 1130 | 
            +
              # Returns a Hash of attributes (role and id only)
         | 
| 1131 | 
            +
              def parse_quoted_text_attributes str
         | 
| 1132 | 
            +
                # NOTE attributes are typically resolved after quoted text, so substitute eagerly
         | 
| 1133 | 
            +
                str = sub_attributes str if str.include? '{'
         | 
| 1197 1134 | 
             
                # for compliance, only consider first positional attribute
         | 
| 1198 | 
            -
                str | 
| 1135 | 
            +
                str = str.slice 0, (str.index ',') if str.include? ','
         | 
| 1199 1136 |  | 
| 1200 | 
            -
                if str.empty?
         | 
| 1137 | 
            +
                if (str = str.strip).empty?
         | 
| 1201 1138 | 
             
                  {}
         | 
| 1202 | 
            -
                elsif (str.start_with? | 
| 1139 | 
            +
                elsif (str.start_with? '.', '#') && Compliance.shorthand_property_syntax
         | 
| 1203 1140 | 
             
                  segments = str.split('#', 2)
         | 
| 1204 1141 |  | 
| 1205 | 
            -
                  if segments. | 
| 1142 | 
            +
                  if segments.size > 1
         | 
| 1206 1143 | 
             
                    id, *more_roles = segments[1].split('.')
         | 
| 1207 1144 | 
             
                  else
         | 
| 1208 1145 | 
             
                    id = nil
         | 
| @@ -1210,11 +1147,11 @@ module Substitutors | |
| 1210 1147 | 
             
                  end
         | 
| 1211 1148 |  | 
| 1212 1149 | 
             
                  roles = segments[0].empty? ? [] : segments[0].split('.')
         | 
| 1213 | 
            -
                  if roles. | 
| 1150 | 
            +
                  if roles.size > 1
         | 
| 1214 1151 | 
             
                    roles.shift
         | 
| 1215 1152 | 
             
                  end
         | 
| 1216 1153 |  | 
| 1217 | 
            -
                  if more_roles. | 
| 1154 | 
            +
                  if more_roles.size > 0
         | 
| 1218 1155 | 
             
                    roles.concat more_roles
         | 
| 1219 1156 | 
             
                  end
         | 
| 1220 1157 |  | 
| @@ -1236,14 +1173,10 @@ module Substitutors | |
| 1236 1173 | 
             
              def parse_attributes(attrline, posattrs = ['role'], opts = {})
         | 
| 1237 1174 | 
             
                return unless attrline
         | 
| 1238 1175 | 
             
                return {} if attrline.empty?
         | 
| 1239 | 
            -
                attrline = @document.sub_attributes(attrline) if opts[:sub_input]
         | 
| 1176 | 
            +
                attrline = @document.sub_attributes(attrline) if opts[:sub_input] && (attrline.include? '{')
         | 
| 1240 1177 | 
             
                attrline = unescape_bracketed_text(attrline) if opts[:unescape_input]
         | 
| 1241 | 
            -
                block  | 
| 1242 | 
            -
                 | 
| 1243 | 
            -
                  # substitutions are only performed on attribute values if block is not nil
         | 
| 1244 | 
            -
                  block = self
         | 
| 1245 | 
            -
                end
         | 
| 1246 | 
            -
             | 
| 1178 | 
            +
                # substitutions are only performed on attribute values if block is not nil
         | 
| 1179 | 
            +
                block = opts.fetch(:sub_result, true) ? self : nil
         | 
| 1247 1180 | 
             
                if (into = opts[:into])
         | 
| 1248 1181 | 
             
                  AttributeList.new(attrline, block).parse_into(into, posattrs)
         | 
| 1249 1182 | 
             
                else
         | 
| @@ -1253,28 +1186,29 @@ module Substitutors | |
| 1253 1186 |  | 
| 1254 1187 | 
             
              # Internal: Strip bounding whitespace, fold endlines and unescaped closing
         | 
| 1255 1188 | 
             
              # square brackets from text extracted from brackets
         | 
| 1256 | 
            -
              def unescape_bracketed_text | 
| 1257 | 
            -
                 | 
| 1258 | 
            -
             | 
| 1259 | 
            -
                 | 
| 1189 | 
            +
              def unescape_bracketed_text text
         | 
| 1190 | 
            +
                if (text = text.strip.tr LF, ' ').include? R_SB
         | 
| 1191 | 
            +
                  text = text.gsub ESC_R_SB, R_SB
         | 
| 1192 | 
            +
                end unless text.empty?
         | 
| 1193 | 
            +
                text
         | 
| 1260 1194 | 
             
              end
         | 
| 1261 1195 |  | 
| 1262 1196 | 
             
              # Internal: Strip bounding whitespace and fold endlines
         | 
| 1263 1197 | 
             
              def normalize_string str, unescape_brackets = false
         | 
| 1264 | 
            -
                 | 
| 1265 | 
            -
                  ''
         | 
| 1266 | 
            -
             | 
| 1267 | 
            -
                  unescape_brackets str.strip.tr(EOL, ' ')
         | 
| 1268 | 
            -
                else
         | 
| 1269 | 
            -
                  str.strip.tr(EOL, ' ')
         | 
| 1198 | 
            +
                unless str.empty?
         | 
| 1199 | 
            +
                  str = str.strip.tr LF, ' '
         | 
| 1200 | 
            +
                  str = str.gsub ESC_R_SB, R_SB if unescape_brackets && (str.include? R_SB)
         | 
| 1270 1201 | 
             
                end
         | 
| 1202 | 
            +
                str
         | 
| 1271 1203 | 
             
              end
         | 
| 1272 1204 |  | 
| 1273 1205 | 
             
              # Internal: Unescape closing square brackets.
         | 
| 1274 1206 | 
             
              # Intended for text extracted from square brackets.
         | 
| 1275 1207 | 
             
              def unescape_brackets str
         | 
| 1276 | 
            -
                 | 
| 1277 | 
            -
             | 
| 1208 | 
            +
                if str.include? RS
         | 
| 1209 | 
            +
                  str = str.gsub ESC_R_SB, R_SB
         | 
| 1210 | 
            +
                end unless str.empty?
         | 
| 1211 | 
            +
                str
         | 
| 1278 1212 | 
             
              end
         | 
| 1279 1213 |  | 
| 1280 1214 | 
             
              # Internal: Split text formatted as CSV with support
         | 
| @@ -1290,7 +1224,7 @@ module Substitutors | |
| 1290 1224 | 
             
                    case c
         | 
| 1291 1225 | 
             
                    when ','
         | 
| 1292 1226 | 
             
                      if quote_open
         | 
| 1293 | 
            -
                        current | 
| 1227 | 
            +
                        current << c
         | 
| 1294 1228 | 
             
                      else
         | 
| 1295 1229 | 
             
                        values << current.join.strip
         | 
| 1296 1230 | 
             
                        current = []
         | 
| @@ -1298,7 +1232,7 @@ module Substitutors | |
| 1298 1232 | 
             
                    when '"'
         | 
| 1299 1233 | 
             
                      quote_open = !quote_open
         | 
| 1300 1234 | 
             
                    else
         | 
| 1301 | 
            -
                      current | 
| 1235 | 
            +
                      current << c
         | 
| 1302 1236 | 
             
                    end
         | 
| 1303 1237 | 
             
                  end
         | 
| 1304 1238 |  | 
| @@ -1317,9 +1251,11 @@ module Substitutors | |
| 1317 1251 | 
             
              # returns An Array of Symbols representing the substitution operation
         | 
| 1318 1252 | 
             
              def resolve_subs subs, type = :block, defaults = nil, subject = nil
         | 
| 1319 1253 | 
             
                return [] if subs.nil_or_empty?
         | 
| 1254 | 
            +
                # QUESTION should we store candidates as a Set instead of an Array?
         | 
| 1320 1255 | 
             
                candidates = nil
         | 
| 1321 | 
            -
                 | 
| 1322 | 
            -
                 | 
| 1256 | 
            +
                subs = subs.delete ' ' if subs.include? ' '
         | 
| 1257 | 
            +
                modifiers_present = SubModifierSniffRx.match? subs
         | 
| 1258 | 
            +
                subs.split(',').each do |key|
         | 
| 1323 1259 | 
             
                  modifier_operation = nil
         | 
| 1324 1260 | 
             
                  if modifiers_present
         | 
| 1325 1261 | 
             
                    if (first = key.chr) == '+'
         | 
| @@ -1336,12 +1272,12 @@ module Substitutors | |
| 1336 1272 | 
             
                  key = key.to_sym
         | 
| 1337 1273 | 
             
                  # special case to disable callouts for inline subs
         | 
| 1338 1274 | 
             
                  if type == :inline && (key == :verbatim || key == :v)
         | 
| 1339 | 
            -
                    resolved_keys =  | 
| 1340 | 
            -
                  elsif  | 
| 1341 | 
            -
                    resolved_keys =  | 
| 1342 | 
            -
                  elsif type == :inline && key.length == 1 && ( | 
| 1343 | 
            -
                    resolved_key =  | 
| 1344 | 
            -
                    if (candidate =  | 
| 1275 | 
            +
                    resolved_keys = BASIC_SUBS
         | 
| 1276 | 
            +
                  elsif SUB_GROUPS.key? key
         | 
| 1277 | 
            +
                    resolved_keys = SUB_GROUPS[key]
         | 
| 1278 | 
            +
                  elsif type == :inline && key.length == 1 && (SUB_HINTS.key? key)
         | 
| 1279 | 
            +
                    resolved_key = SUB_HINTS[key]
         | 
| 1280 | 
            +
                    if (candidate = SUB_GROUPS[resolved_key])
         | 
| 1345 1281 | 
             
                      resolved_keys = candidate
         | 
| 1346 1282 | 
             
                    else
         | 
| 1347 1283 | 
             
                      resolved_keys = [resolved_key]
         | 
| @@ -1365,8 +1301,8 @@ module Substitutors | |
| 1365 1301 | 
             
                    candidates += resolved_keys
         | 
| 1366 1302 | 
             
                  end
         | 
| 1367 1303 | 
             
                end
         | 
| 1368 | 
            -
                 | 
| 1369 | 
            -
                #  | 
| 1304 | 
            +
                return [] unless candidates
         | 
| 1305 | 
            +
                # weed out invalid options and remove duplicates (order is preserved; first occurence wins)
         | 
| 1370 1306 | 
             
                resolved = candidates & SUB_OPTIONS[type]
         | 
| 1371 1307 | 
             
                unless (candidates - resolved).empty?
         | 
| 1372 1308 | 
             
                  invalid = candidates - resolved
         | 
| @@ -1401,7 +1337,7 @@ module Substitutors | |
| 1401 1337 | 
             
                  unless (highlighter_loaded = defined? ::CodeRay) || @document.attributes['coderay-unavailable']
         | 
| 1402 1338 | 
             
                    if (Helpers.require_library 'coderay', true, :warn).nil?
         | 
| 1403 1339 | 
             
                      # prevent further attempts to load CodeRay
         | 
| 1404 | 
            -
                      @document.set_attr 'coderay-unavailable' | 
| 1340 | 
            +
                      @document.set_attr 'coderay-unavailable'
         | 
| 1405 1341 | 
             
                    else
         | 
| 1406 1342 | 
             
                      highlighter_loaded = true
         | 
| 1407 1343 | 
             
                    end
         | 
| @@ -1410,7 +1346,7 @@ module Substitutors | |
| 1410 1346 | 
             
                  unless (highlighter_loaded = defined? ::Pygments) || @document.attributes['pygments-unavailable']
         | 
| 1411 1347 | 
             
                    if (Helpers.require_library 'pygments', 'pygments.rb', :warn).nil?
         | 
| 1412 1348 | 
             
                      # prevent further attempts to load Pygments
         | 
| 1413 | 
            -
                      @document.set_attr 'pygments-unavailable' | 
| 1349 | 
            +
                      @document.set_attr 'pygments-unavailable'
         | 
| 1414 1350 | 
             
                    else
         | 
| 1415 1351 | 
             
                      highlighter_loaded = true
         | 
| 1416 1352 | 
             
                    end
         | 
| @@ -1430,21 +1366,22 @@ module Substitutors | |
| 1430 1366 | 
             
                  # FIXME cache this dynamic regex
         | 
| 1431 1367 | 
             
                  callout_rx = (attr? 'line-comment') ? /(?:#{::Regexp.escape(attr 'line-comment')} )?#{CalloutExtractRxt}/ : CalloutExtractRx
         | 
| 1432 1368 | 
             
                  # extract callout marks, indexed by line number
         | 
| 1433 | 
            -
                  source = source.split( | 
| 1369 | 
            +
                  source = source.split(LF, -1).map {|line|
         | 
| 1434 1370 | 
             
                    lineno = lineno + 1
         | 
| 1435 1371 | 
             
                    line.gsub(callout_rx) {
         | 
| 1436 1372 | 
             
                      # alias match for Ruby 1.8.7 compat
         | 
| 1437 1373 | 
             
                      m = $~
         | 
| 1438 1374 | 
             
                      # honor the escape
         | 
| 1439 | 
            -
                      if m[1] ==  | 
| 1440 | 
            -
                         | 
| 1375 | 
            +
                      if m[1] == RS
         | 
| 1376 | 
            +
                        # we have to use sub since we aren't sure it's the first char
         | 
| 1377 | 
            +
                        m[0].sub RS, ''
         | 
| 1441 1378 | 
             
                      else
         | 
| 1442 1379 | 
             
                        (callout_marks[lineno] ||= []) << m[3]
         | 
| 1443 1380 | 
             
                        last = lineno
         | 
| 1444 1381 | 
             
                        nil
         | 
| 1445 1382 | 
             
                      end
         | 
| 1446 1383 | 
             
                    }
         | 
| 1447 | 
            -
                  } *  | 
| 1384 | 
            +
                  } * LF
         | 
| 1448 1385 | 
             
                  callout_on_last = (last == lineno)
         | 
| 1449 1386 | 
             
                  callout_marks = nil if callout_marks.empty?
         | 
| 1450 1387 | 
             
                else
         | 
| @@ -1456,7 +1393,7 @@ module Substitutors | |
| 1456 1393 |  | 
| 1457 1394 | 
             
                case highlighter
         | 
| 1458 1395 | 
             
                when 'coderay'
         | 
| 1459 | 
            -
                  if (linenums_mode = (attr? 'linenums') ? (@document.attributes['coderay-linenums-mode'] || :table).to_sym : nil)
         | 
| 1396 | 
            +
                  if (linenums_mode = (attr? 'linenums', nil, false) ? (@document.attributes['coderay-linenums-mode'] || :table).to_sym : nil)
         | 
| 1460 1397 | 
             
                    if attr? 'highlight', nil, false
         | 
| 1461 1398 | 
             
                      highlight_lines = resolve_highlight_lines(attr 'highlight', nil, false)
         | 
| 1462 1399 | 
             
                    end
         | 
| @@ -1468,8 +1405,9 @@ module Substitutors | |
| 1468 1405 | 
             
                      :highlight_lines => highlight_lines,
         | 
| 1469 1406 | 
             
                      :bold_every => false}].highlight source
         | 
| 1470 1407 | 
             
                when 'pygments'
         | 
| 1471 | 
            -
                  lexer = ::Pygments::Lexer | 
| 1472 | 
            -
                  opts = { :cssclass => 'pyhl', :classprefix => 'tok-', :nobackground => true }
         | 
| 1408 | 
            +
                  lexer = ::Pygments::Lexer.find_by_alias(attr 'language', 'text', false) || ::Pygments::Lexer.find_by_mimetype('text/plain')
         | 
| 1409 | 
            +
                  opts = { :cssclass => 'pyhl', :classprefix => 'tok-', :nobackground => true, :stripnl => false }
         | 
| 1410 | 
            +
                  opts[:startinline] = !(option? 'mixed') if lexer.name == 'PHP'
         | 
| 1473 1411 | 
             
                  unless (@document.attributes['pygments-css'] || 'class') == 'class'
         | 
| 1474 1412 | 
             
                    opts[:noclasses] = true
         | 
| 1475 1413 | 
             
                    opts[:style] = (@document.attributes['pygments-style'] || Stylesheets::DEFAULT_PYGMENTS_STYLE)
         | 
| @@ -1479,56 +1417,42 @@ module Substitutors | |
| 1479 1417 | 
             
                      opts[:hl_lines] = highlight_lines * ' '
         | 
| 1480 1418 | 
             
                    end
         | 
| 1481 1419 | 
             
                  end
         | 
| 1482 | 
            -
                   | 
| 1483 | 
            -
             | 
| 1484 | 
            -
                     | 
| 1485 | 
            -
                     | 
| 1486 | 
            -
                      linenums_mode = :table
         | 
| 1487 | 
            -
                      # NOTE these subs clean out HTML that messes up our styles
         | 
| 1488 | 
            -
                      result = lexer.highlight(source, :options => opts).
         | 
| 1489 | 
            -
                          sub(/<div class="pyhl">(.*)<\/div>/m, '\1').
         | 
| 1490 | 
            -
                          gsub(/<pre[^>]*>(.*?)<\/pre>\s*/m, '\1')
         | 
| 1491 | 
            -
                    else
         | 
| 1492 | 
            -
                      result = lexer.highlight(source, :options => opts).
         | 
| 1493 | 
            -
                          sub(/<div class="pyhl"><pre[^>]*>(.*?)<\/pre><\/div>/m, '\1')
         | 
| 1494 | 
            -
                    end
         | 
| 1420 | 
            +
                  # TODO we could add the line numbers in ourselves instead of having to strip out the junk
         | 
| 1421 | 
            +
                  if (attr? 'linenums', nil, false) && (opts[:linenos] = @document.attributes['pygments-linenums-mode'] || 'table') == 'table'
         | 
| 1422 | 
            +
                    linenums_mode = :table
         | 
| 1423 | 
            +
                    result = lexer.highlight(source, :options => opts).sub(PygmentsWrapperDivRx, '\1').gsub(PygmentsWrapperPreRx, '\1')
         | 
| 1495 1424 | 
             
                  else
         | 
| 1496 | 
            -
                     | 
| 1497 | 
            -
             | 
| 1498 | 
            -
                     | 
| 1425 | 
            +
                    if PygmentsWrapperPreRx =~ (result = lexer.highlight(source, :options => opts))
         | 
| 1426 | 
            +
                      result = $1
         | 
| 1427 | 
            +
                    end
         | 
| 1499 1428 | 
             
                  end
         | 
| 1500 1429 | 
             
                end
         | 
| 1501 1430 |  | 
| 1502 1431 | 
             
                # fix passthrough placeholders that got caught up in syntax highlighting
         | 
| 1503 1432 | 
             
                unless @passthroughs.empty?
         | 
| 1504 | 
            -
                  result = result.gsub  | 
| 1433 | 
            +
                  result = result.gsub HighlightedPassSlotRx, %(#{PASS_START}\\1#{PASS_END})
         | 
| 1505 1434 | 
             
                end
         | 
| 1506 1435 |  | 
| 1507 1436 | 
             
                if process_callouts && callout_marks
         | 
| 1508 1437 | 
             
                  lineno = 0
         | 
| 1509 1438 | 
             
                  reached_code = linenums_mode != :table
         | 
| 1510 | 
            -
                  result.split( | 
| 1439 | 
            +
                  result.split(LF, -1).map {|line|
         | 
| 1511 1440 | 
             
                    unless reached_code
         | 
| 1512 | 
            -
                      unless line.include?('<td class="code">')
         | 
| 1513 | 
            -
                        next line
         | 
| 1514 | 
            -
                      end
         | 
| 1441 | 
            +
                      next line unless line.include?('<td class="code">')
         | 
| 1515 1442 | 
             
                      reached_code = true
         | 
| 1516 1443 | 
             
                    end
         | 
| 1517 | 
            -
                    lineno  | 
| 1444 | 
            +
                    lineno += 1
         | 
| 1518 1445 | 
             
                    if (conums = callout_marks.delete(lineno))
         | 
| 1519 1446 | 
             
                      tail = nil
         | 
| 1520 | 
            -
                      if callout_on_last && callout_marks.empty?
         | 
| 1521 | 
            -
                         | 
| 1522 | 
            -
             | 
| 1523 | 
            -
             | 
| 1524 | 
            -
                          line =  | 
| 1525 | 
            -
                        else
         | 
| 1526 | 
            -
                          # Give conum on final line breathing room if trailing space in source is dropped
         | 
| 1527 | 
            -
                          line = %(#{line.chomp ' '} )
         | 
| 1447 | 
            +
                      if callout_on_last && callout_marks.empty? && linenums_mode == :table
         | 
| 1448 | 
            +
                        if highlighter == 'coderay' && (pos = line.index '</pre>')
         | 
| 1449 | 
            +
                          line, tail = (line.slice 0, pos), (line.slice pos, line.length)
         | 
| 1450 | 
            +
                        elsif highlighter == 'pygments' && (pos = line.start_with? '</td>')
         | 
| 1451 | 
            +
                          line, tail = '', line
         | 
| 1528 1452 | 
             
                        end
         | 
| 1529 1453 | 
             
                      end
         | 
| 1530 1454 | 
             
                      if conums.size == 1
         | 
| 1531 | 
            -
                        %(#{line}#{Inline.new(self, :callout, conums[0], :id => @document.callouts.read_next_id).convert | 
| 1455 | 
            +
                        %(#{line}#{Inline.new(self, :callout, conums[0], :id => @document.callouts.read_next_id).convert}#{tail})
         | 
| 1532 1456 | 
             
                      else
         | 
| 1533 1457 | 
             
                        conums_markup = conums.map {|conum| Inline.new(self, :callout, conum, :id => @document.callouts.read_next_id).convert } * ' '
         | 
| 1534 1458 | 
             
                        %(#{line}#{conums_markup}#{tail})
         | 
| @@ -1536,7 +1460,7 @@ module Substitutors | |
| 1536 1460 | 
             
                    else
         | 
| 1537 1461 | 
             
                      line
         | 
| 1538 1462 | 
             
                    end
         | 
| 1539 | 
            -
                  } *  | 
| 1463 | 
            +
                  } * LF
         | 
| 1540 1464 | 
             
                else
         | 
| 1541 1465 | 
             
                  result
         | 
| 1542 1466 | 
             
                end
         | 
| @@ -1545,7 +1469,7 @@ module Substitutors | |
| 1545 1469 | 
             
              # e.g., highlight="1-5, !2, 10" or highlight=1-5;!2,10
         | 
| 1546 1470 | 
             
              def resolve_highlight_lines spec
         | 
| 1547 1471 | 
             
                lines = []
         | 
| 1548 | 
            -
                spec.delete | 
| 1472 | 
            +
                ((spec.include? ' ') ? (spec.delete ' ') : spec).split(DataDelimiterRx).map do |entry|
         | 
| 1549 1473 | 
             
                  negate = false
         | 
| 1550 1474 | 
             
                  if entry.start_with? '!'
         | 
| 1551 1475 | 
             
                    entry = entry[1..-1]
         | 
| @@ -1577,54 +1501,48 @@ module Substitutors | |
| 1577 1501 | 
             
              #
         | 
| 1578 1502 | 
             
              # returns the substituted source
         | 
| 1579 1503 | 
             
              def sub_source source, process_callouts
         | 
| 1580 | 
            -
                 | 
| 1504 | 
            +
                process_callouts ? sub_callouts(sub_specialchars source) : (sub_specialchars source)
         | 
| 1581 1505 | 
             
              end
         | 
| 1582 1506 |  | 
| 1583 1507 | 
             
              # Internal: Lock-in the substitutions for this block
         | 
| 1584 1508 | 
             
              #
         | 
| 1585 | 
            -
              # Looks for an attribute named "subs". If present, resolves  | 
| 1586 | 
            -
              #  | 
| 1587 | 
            -
              # Otherwise,  | 
| 1588 | 
            -
              #  | 
| 1509 | 
            +
              # Looks for an attribute named "subs". If present, resolves substitutions
         | 
| 1510 | 
            +
              # from the value of that attribute and assigns them to the subs property on
         | 
| 1511 | 
            +
              # this block. Otherwise, uses the substitutions assigned to the default_subs
         | 
| 1512 | 
            +
              # property, if specified, or selects a default set of substitutions based on
         | 
| 1513 | 
            +
              # the content model of the block.
         | 
| 1589 1514 | 
             
              #
         | 
| 1590 | 
            -
              # Returns  | 
| 1515 | 
            +
              # Returns The Array of resolved substitutions now assigned to this block
         | 
| 1591 1516 | 
             
              def lock_in_subs
         | 
| 1592 | 
            -
                 | 
| 1593 | 
            -
                  default_subs = @default_subs
         | 
| 1594 | 
            -
                else
         | 
| 1517 | 
            +
                unless (default_subs = @default_subs)
         | 
| 1595 1518 | 
             
                  case @content_model
         | 
| 1596 1519 | 
             
                  when :simple
         | 
| 1597 | 
            -
                    default_subs =  | 
| 1520 | 
            +
                    default_subs = NORMAL_SUBS
         | 
| 1598 1521 | 
             
                  when :verbatim
         | 
| 1599 1522 | 
             
                    if @context == :listing || (@context == :literal && !(option? 'listparagraph'))
         | 
| 1600 | 
            -
                      default_subs =  | 
| 1523 | 
            +
                      default_subs = VERBATIM_SUBS
         | 
| 1601 1524 | 
             
                    elsif @context == :verse
         | 
| 1602 | 
            -
                      default_subs =  | 
| 1525 | 
            +
                      default_subs = NORMAL_SUBS
         | 
| 1603 1526 | 
             
                    else
         | 
| 1604 | 
            -
                      default_subs =  | 
| 1527 | 
            +
                      default_subs = BASIC_SUBS
         | 
| 1605 1528 | 
             
                    end
         | 
| 1606 1529 | 
             
                  when :raw
         | 
| 1607 | 
            -
                     | 
| 1608 | 
            -
             | 
| 1609 | 
            -
                    else
         | 
| 1610 | 
            -
                      default_subs = SUBS[:pass]
         | 
| 1611 | 
            -
                    end
         | 
| 1530 | 
            +
                    # TODO make pass subs a compliance setting; AsciiDoc Python performs :attributes and :macros on a pass block
         | 
| 1531 | 
            +
                    default_subs = @context == :stem ? BASIC_SUBS : NONE_SUBS
         | 
| 1612 1532 | 
             
                  else
         | 
| 1613 | 
            -
                    return
         | 
| 1533 | 
            +
                    return @subs
         | 
| 1614 1534 | 
             
                  end
         | 
| 1615 1535 | 
             
                end
         | 
| 1616 1536 |  | 
| 1617 | 
            -
                 | 
| 1618 | 
            -
                  @subs = resolve_block_subs custom_subs, default_subs, @context
         | 
| 1619 | 
            -
                else
         | 
| 1620 | 
            -
                  @subs = default_subs.dup
         | 
| 1621 | 
            -
                end
         | 
| 1537 | 
            +
                @subs = (custom_subs = @attributes['subs']) ? (resolve_block_subs custom_subs, default_subs, @context) : default_subs.dup
         | 
| 1622 1538 |  | 
| 1623 1539 | 
             
                # QUESION delegate this logic to a method?
         | 
| 1624 | 
            -
                if @context == :listing && @style == 'source' && @attributes | 
| 1625 | 
            -
                     | 
| 1626 | 
            -
                  @subs =  | 
| 1540 | 
            +
                if @context == :listing && @style == 'source' && (@attributes.key? 'language') && (@document.basebackend? 'html') &&
         | 
| 1541 | 
            +
                    (SUB_HIGHLIGHT.include? @document.attributes['source-highlighter']) && (idx = @subs.index :specialcharacters)
         | 
| 1542 | 
            +
                  @subs[idx] = :highlight
         | 
| 1627 1543 | 
             
                end
         | 
| 1544 | 
            +
             | 
| 1545 | 
            +
                @subs
         | 
| 1628 1546 | 
             
              end
         | 
| 1629 1547 | 
             
            end
         | 
| 1630 1548 | 
             
            end
         |