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.rb
    CHANGED
    
    | @@ -7,10 +7,10 @@ RUBY_MIN_VERSION_2 = (RUBY_VERSION >= '2') | |
| 7 7 |  | 
| 8 8 | 
             
            require 'set'
         | 
| 9 9 |  | 
| 10 | 
            -
            # NOTE RUBY_ENGINE == 'opal' conditional blocks are filtered by the Opal preprocessor
         | 
| 10 | 
            +
            # NOTE RUBY_ENGINE == 'opal' conditional blocks like this are filtered by the Opal preprocessor
         | 
| 11 11 | 
             
            if RUBY_ENGINE == 'opal'
         | 
| 12 | 
            -
              #  | 
| 13 | 
            -
              require 'asciidoctor/ | 
| 12 | 
            +
              # this require is satisfied by the Asciidoctor.js build; it augments the Ruby environment for Asciidoctor.js
         | 
| 13 | 
            +
              require 'asciidoctor/js'
         | 
| 14 14 | 
             
            else
         | 
| 15 15 | 
             
              autoload :Base64, 'base64'
         | 
| 16 16 | 
             
              autoload :OpenURI, 'open-uri'
         | 
| @@ -62,7 +62,7 @@ module Asciidoctor | |
| 62 62 |  | 
| 63 63 | 
             
                # A safe mode level that closely parallels safe mode in AsciiDoc. This value
         | 
| 64 64 | 
             
                # prevents access to files which reside outside of the parent directory of
         | 
| 65 | 
            -
                # the source file and disables any macro other than the include::[]  | 
| 65 | 
            +
                # the source file and disables any macro other than the include::[] directive.
         | 
| 66 66 | 
             
                SAFE = 1;
         | 
| 67 67 |  | 
| 68 68 | 
             
                # A safe mode level that disallows the document from setting attributes
         | 
| @@ -76,7 +76,7 @@ module Asciidoctor | |
| 76 76 | 
             
                # A safe mode level that disallows the document from attempting to read
         | 
| 77 77 | 
             
                # files from the file system and including the contents of them into the
         | 
| 78 78 | 
             
                # document, in additional to all the security features of SafeMode::SERVER.
         | 
| 79 | 
            -
                # For instance, this level disallows use of the include::[]  | 
| 79 | 
            +
                # For instance, this level disallows use of the include::[] directive and the
         | 
| 80 80 | 
             
                # embedding of binary content (data uri), stylesheets and JavaScripts
         | 
| 81 81 | 
             
                # referenced by the document.(Asciidoctor and trusted extensions may still
         | 
| 82 82 | 
             
                # be allowed to embed trusted content into the document).
         | 
| @@ -93,13 +93,28 @@ module Asciidoctor | |
| 93 93 | 
             
                # enforced)!
         | 
| 94 94 | 
             
                #PARANOID = 100;
         | 
| 95 95 |  | 
| 96 | 
            +
                rec = {}
         | 
| 97 | 
            +
                constants.each {|sym| rec[const_get sym] = sym.to_s.downcase }
         | 
| 98 | 
            +
                @names_by_value = rec
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def self.value_for_name name
         | 
| 101 | 
            +
                  const_get name.upcase
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                def self.name_for_value value
         | 
| 105 | 
            +
                  @names_by_value[value]
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                def self.names
         | 
| 109 | 
            +
                  @names_by_value.values
         | 
| 110 | 
            +
                end
         | 
| 96 111 | 
             
              end
         | 
| 97 112 |  | 
| 98 113 | 
             
              # Flags to control compliance with the behavior of AsciiDoc
         | 
| 99 114 | 
             
              module Compliance
         | 
| 100 115 | 
             
                @keys = ::Set.new
         | 
| 101 116 | 
             
                class << self
         | 
| 102 | 
            -
                   | 
| 117 | 
            +
                  attr_reader :keys
         | 
| 103 118 | 
             
                end
         | 
| 104 119 |  | 
| 105 120 | 
             
                # Defines a new compliance key and assigns an initial value.
         | 
| @@ -117,8 +132,8 @@ module Asciidoctor | |
| 117 132 | 
             
                # Compliance value: true
         | 
| 118 133 | 
             
                define :block_terminates_paragraph, true
         | 
| 119 134 |  | 
| 120 | 
            -
                # AsciiDoc does not  | 
| 121 | 
            -
                # (literal, listing, source, verse) as verbatim
         | 
| 135 | 
            +
                # AsciiDoc does not parse paragraphs with a verbatim style
         | 
| 136 | 
            +
                # (i.e., literal, listing, source, verse) as verbatim content.
         | 
| 122 137 | 
             
                # This options allows this behavior to be modified
         | 
| 123 138 | 
             
                # Compliance value: false
         | 
| 124 139 | 
             
                define :strict_verbatim_paragraphs, true
         | 
| @@ -172,7 +187,7 @@ module Asciidoctor | |
| 172 187 | 
             
              ROOT_PATH = ::File.dirname ::File.dirname ::File.expand_path __FILE__
         | 
| 173 188 |  | 
| 174 189 | 
             
              # The absolute lib path of the Asciidoctor RubyGem
         | 
| 175 | 
            -
              LIB_PATH = ::File.join ROOT_PATH, 'lib'
         | 
| 190 | 
            +
              #LIB_PATH = ::File.join ROOT_PATH, 'lib'
         | 
| 176 191 |  | 
| 177 192 | 
             
              # The absolute data path of the Asciidoctor RubyGem
         | 
| 178 193 | 
             
              DATA_PATH = ::File.join ROOT_PATH, 'data'
         | 
| @@ -195,19 +210,15 @@ module Asciidoctor | |
| 195 210 | 
             
              FORCE_ENCODING = COERCE_ENCODING && ::Encoding.default_external != ::Encoding::UTF_8
         | 
| 196 211 |  | 
| 197 212 | 
             
              # Byte arrays for UTF-* Byte Order Marks
         | 
| 198 | 
            -
               | 
| 199 | 
            -
               | 
| 200 | 
            -
               | 
| 201 | 
            -
              BOM_BYTES_UTF_16BE = "\xfe\xff".bytes.to_a
         | 
| 213 | 
            +
              BOM_BYTES_UTF_8 = [0xef, 0xbb, 0xbf]
         | 
| 214 | 
            +
              BOM_BYTES_UTF_16LE = [0xff, 0xfe]
         | 
| 215 | 
            +
              BOM_BYTES_UTF_16BE = [0xfe, 0xff]
         | 
| 202 216 |  | 
| 203 217 | 
             
              # Flag to indicate that line length should be calculated using a unicode mode hint
         | 
| 204 218 | 
             
              FORCE_UNICODE_LINE_LENGTH = !::RUBY_MIN_VERSION_1_9
         | 
| 205 219 |  | 
| 206 | 
            -
              # Flag to indicate whether gsub can use a Hash to map matches to replacements
         | 
| 207 | 
            -
              SUPPORTS_GSUB_RESULT_HASH = ::RUBY_MIN_VERSION_1_9 && !::RUBY_ENGINE_OPAL
         | 
| 208 | 
            -
             | 
| 209 220 | 
             
              # The endline character used for output; stored in constant table as an optimization
         | 
| 210 | 
            -
              EOL = "\n"
         | 
| 221 | 
            +
              LF = EOL = "\n"
         | 
| 211 222 |  | 
| 212 223 | 
             
              # The null character to use for splitting attribute values
         | 
| 213 224 | 
             
              NULL = "\0"
         | 
| @@ -243,6 +254,7 @@ module Asciidoctor | |
| 243 254 | 
             
                'docbook' => '.xml',
         | 
| 244 255 | 
             
                'pdf' => '.pdf',
         | 
| 245 256 | 
             
                'epub' => '.epub',
         | 
| 257 | 
            +
                'manpage' => '.man',
         | 
| 246 258 | 
             
                'asciidoc' => '.adoc'
         | 
| 247 259 | 
             
              }
         | 
| 248 260 |  | 
| @@ -256,7 +268,7 @@ module Asciidoctor | |
| 256 268 | 
             
                '.txt' => true
         | 
| 257 269 | 
             
              }
         | 
| 258 270 |  | 
| 259 | 
            -
               | 
| 271 | 
            +
              SETEXT_SECTION_LEVELS = {
         | 
| 260 272 | 
             
                '=' => 0,
         | 
| 261 273 | 
             
                '-' => 1,
         | 
| 262 274 | 
             
                '~' => 2,
         | 
| @@ -266,6 +278,10 @@ module Asciidoctor | |
| 266 278 |  | 
| 267 279 | 
             
              ADMONITION_STYLES = ['NOTE', 'TIP', 'IMPORTANT', 'WARNING', 'CAUTION'].to_set
         | 
| 268 280 |  | 
| 281 | 
            +
              ADMONITION_STYLE_LEADERS = ['N', 'T', 'I', 'W', 'C'].to_set
         | 
| 282 | 
            +
             | 
| 283 | 
            +
              CALLOUT_LIST_LEADERS = ['<', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'].to_set
         | 
| 284 | 
            +
             | 
| 269 285 | 
             
              PARAGRAPH_STYLES = ['comment', 'example', 'literal', 'listing', 'normal', 'pass', 'quote', 'sidebar', 'source', 'verse', 'abstract', 'partintro'].to_set
         | 
| 270 286 |  | 
| 271 287 | 
             
              VERBATIM_STYLES = ['literal', 'listing', 'source', 'verse'].to_set
         | 
| @@ -287,16 +303,21 @@ module Asciidoctor | |
| 287 303 | 
             
                '```'  => [:fenced_code, ::Set.new]
         | 
| 288 304 | 
             
              }
         | 
| 289 305 |  | 
| 290 | 
            -
              DELIMITED_BLOCK_LEADERS = DELIMITED_BLOCKS.keys.map {|key| key | 
| 306 | 
            +
              DELIMITED_BLOCK_LEADERS = DELIMITED_BLOCKS.keys.map {|key| key.slice 0, 2 }.to_set
         | 
| 291 307 |  | 
| 292 | 
            -
               | 
| 308 | 
            +
              LAYOUT_BREAK_CHARS = {
         | 
| 293 309 | 
             
                '\'' => :thematic_break,
         | 
| 310 | 
            +
                '<'  => :page_break
         | 
| 311 | 
            +
              }
         | 
| 312 | 
            +
             | 
| 313 | 
            +
              MARKDOWN_THEMATIC_BREAK_CHARS = {
         | 
| 294 314 | 
             
                '-'  => :thematic_break,
         | 
| 295 315 | 
             
                '*'  => :thematic_break,
         | 
| 296 | 
            -
                '_'  => :thematic_break | 
| 297 | 
            -
                '<'  => :page_break
         | 
| 316 | 
            +
                '_'  => :thematic_break
         | 
| 298 317 | 
             
              }
         | 
| 299 318 |  | 
| 319 | 
            +
              HYBRID_LAYOUT_BREAK_CHARS = LAYOUT_BREAK_CHARS.merge MARKDOWN_THEMATIC_BREAK_CHARS
         | 
| 320 | 
            +
             | 
| 300 321 | 
             
              #LIST_CONTEXTS = [:ulist, :olist, :dlist, :colist]
         | 
| 301 322 |  | 
| 302 323 | 
             
              NESTABLE_LIST_CONTEXTS = [:ulist, :olist, :dlist]
         | 
| @@ -305,37 +326,48 @@ module Asciidoctor | |
| 305 326 | 
             
              ORDERED_LIST_STYLES = [:arabic, :loweralpha, :lowerroman, :upperalpha, :upperroman] #, :lowergreek]
         | 
| 306 327 |  | 
| 307 328 | 
             
              ORDERED_LIST_KEYWORDS = {
         | 
| 329 | 
            +
                #'arabic'     => '1',
         | 
| 330 | 
            +
                #'decimal'    => '1',
         | 
| 308 331 | 
             
                'loweralpha' => 'a',
         | 
| 309 332 | 
             
                'lowerroman' => 'i',
         | 
| 333 | 
            +
                #'lowergreek' => 'a',
         | 
| 310 334 | 
             
                'upperalpha' => 'A',
         | 
| 311 335 | 
             
                'upperroman' => 'I'
         | 
| 312 | 
            -
                #'lowergreek' => 'a'
         | 
| 313 | 
            -
                #'arabic'     => '1'
         | 
| 314 | 
            -
                #'decimal'    => '1'
         | 
| 315 336 | 
             
              }
         | 
| 316 337 |  | 
| 317 338 | 
             
              LIST_CONTINUATION = '+'
         | 
| 318 339 |  | 
| 319 | 
            -
              # NOTE AsciiDoc Python  | 
| 320 | 
            -
               | 
| 340 | 
            +
              # NOTE AsciiDoc Python allows + to be preceded by TAB; Asciidoctor does not
         | 
| 341 | 
            +
              HARD_LINE_BREAK = ' +'
         | 
| 321 342 |  | 
| 322 343 | 
             
              LINE_CONTINUATION = ' \\'
         | 
| 323 344 |  | 
| 324 345 | 
             
              LINE_CONTINUATION_LEGACY = ' +'
         | 
| 325 346 |  | 
| 326 347 | 
             
              BLOCK_MATH_DELIMITERS = {
         | 
| 327 | 
            -
                :asciimath => [' | 
| 328 | 
            -
                :latexmath => [' | 
| 348 | 
            +
                :asciimath => ['\$', '\$'],
         | 
| 349 | 
            +
                :latexmath => ['\[', '\]'],
         | 
| 329 350 | 
             
              }
         | 
| 330 351 |  | 
| 331 352 | 
             
              INLINE_MATH_DELIMITERS = {
         | 
| 332 | 
            -
                :asciimath => [' | 
| 333 | 
            -
                :latexmath => [' | 
| 353 | 
            +
                :asciimath => ['\$', '\$'],
         | 
| 354 | 
            +
                :latexmath => ['\(', '\)'],
         | 
| 334 355 | 
             
              }
         | 
| 335 356 |  | 
| 336 357 | 
             
              # attributes which be changed within the content of the document (but not
         | 
| 337 358 | 
             
              # header) because it has semantic meaning; ex. sectnums
         | 
| 338 | 
            -
              FLEXIBLE_ATTRIBUTES =  | 
| 359 | 
            +
              FLEXIBLE_ATTRIBUTES = ['sectnums']
         | 
| 360 | 
            +
             | 
| 361 | 
            +
              # map of file extension to comment affixes for languages that only support circumfix comments
         | 
| 362 | 
            +
              CIRCUMFIX_COMMENTS = {
         | 
| 363 | 
            +
                ['/*', '*/'] => ['.css'],
         | 
| 364 | 
            +
                ['(*', '*)'] => ['.ml', '.mli', '.nb'],
         | 
| 365 | 
            +
                ['<!--', '-->'] => ['.html', '.xhtml', '.xml', '.xsl'],
         | 
| 366 | 
            +
                ['<%--', '--%>'] => ['.asp', '.jsp']
         | 
| 367 | 
            +
              }.inject({}) {|accum, (affixes, exts)|
         | 
| 368 | 
            +
                exts.each {|ext| accum[ext] = { :prefix => affixes[0], :suffix => affixes[-1] } }
         | 
| 369 | 
            +
                accum
         | 
| 370 | 
            +
              }
         | 
| 339 371 |  | 
| 340 372 | 
             
              # A collection of regular expressions used by the parser.
         | 
| 341 373 | 
             
              #
         | 
| @@ -343,8 +375,8 @@ module Asciidoctor | |
| 343 375 | 
             
              # contents between square brackets, ignoring escaped closing brackets
         | 
| 344 376 | 
             
              # (closing brackets prefixed with a backslash '\' character)
         | 
| 345 377 | 
             
              #
         | 
| 346 | 
            -
              #   Pattern:  | 
| 347 | 
            -
              #   Matches: [enclosed text | 
| 378 | 
            +
              #   Pattern: \[(|.*?[^\\])\]
         | 
| 379 | 
            +
              #   Matches: [enclosed text] and [enclosed [text\]], not [enclosed text \\] or [\\] (as these require a trailing space)
         | 
| 348 380 | 
             
              #
         | 
| 349 381 | 
             
              #(pseudo)module Rx
         | 
| 350 382 |  | 
| @@ -353,26 +385,13 @@ module Asciidoctor | |
| 353 385 |  | 
| 354 386 | 
             
                # NOTE \w matches only the ASCII word characters, whereas [[:word:]] or \p{Word} matches any character in the Unicode word category.
         | 
| 355 387 |  | 
| 356 | 
            -
                # character classes for the Regexp engine(s) in JavaScript
         | 
| 357 | 
            -
                if RUBY_ENGINE == 'opal'
         | 
| 358 | 
            -
                  CC_ALPHA = 'a-zA-Z'
         | 
| 359 | 
            -
                  CG_ALPHA = '[a-zA-Z]'
         | 
| 360 | 
            -
                  CC_ALNUM = 'a-zA-Z0-9'
         | 
| 361 | 
            -
                  CG_ALNUM = '[a-zA-Z0-9]'
         | 
| 362 | 
            -
                  CG_BLANK = '[ \\t]'
         | 
| 363 | 
            -
                  CC_EOL   = '(?=\\n|$)'
         | 
| 364 | 
            -
                  CG_GRAPH = '[\\x21-\\x7E]' # non-blank character
         | 
| 365 | 
            -
                  CC_ALL   = '[\s\S]' # any character, including newlines (alternatively, [^])
         | 
| 366 | 
            -
                  CC_WORD  = 'a-zA-Z0-9_'
         | 
| 367 | 
            -
                  CG_WORD  = '[a-zA-Z0-9_]'
         | 
| 368 388 | 
             
                # character classes for the Regexp engine in Ruby >= 2 (Ruby 1.9 supports \p{} but has problems w/ encoding)
         | 
| 369 | 
            -
                 | 
| 389 | 
            +
                if ::RUBY_MIN_VERSION_2
         | 
| 370 390 | 
             
                  CC_ALPHA = CG_ALPHA = '\p{Alpha}'
         | 
| 371 391 | 
             
                  CC_ALNUM = CG_ALNUM = '\p{Alnum}'
         | 
| 372 392 | 
             
                  CC_ALL   = '.'
         | 
| 373 393 | 
             
                  CG_BLANK = '\p{Blank}'
         | 
| 374 394 | 
             
                  CC_EOL   = '$'
         | 
| 375 | 
            -
                  CG_GRAPH = '\p{Graph}'
         | 
| 376 395 | 
             
                  CC_WORD  = CG_WORD = '\p{Word}'
         | 
| 377 396 | 
             
                # character classes for the Regexp engine in Ruby < 2
         | 
| 378 397 | 
             
                else
         | 
| @@ -381,18 +400,18 @@ module Asciidoctor | |
| 381 400 | 
             
                  CC_ALL   = '.'
         | 
| 382 401 | 
             
                  CC_ALNUM = '[:alnum:]'
         | 
| 383 402 | 
             
                  CG_ALNUM = '[[:alnum:]]'
         | 
| 384 | 
            -
                  CG_BLANK = '[[:blank:]]'
         | 
| 385 403 | 
             
                  CC_EOL   = '$'
         | 
| 386 | 
            -
                  CG_GRAPH = '[[:graph:]]' # non-blank character
         | 
| 387 404 | 
             
                  if ::RUBY_MIN_VERSION_1_9
         | 
| 405 | 
            +
                    CG_BLANK = '[[:blank:]]'
         | 
| 388 406 | 
             
                    CC_WORD = '[:word:]'
         | 
| 389 407 | 
             
                    CG_WORD = '[[:word:]]'
         | 
| 390 408 | 
             
                  else
         | 
| 391 409 | 
             
                    # NOTE Ruby 1.8 cannot match word characters beyond the ASCII range; if you need this feature, upgrade!
         | 
| 410 | 
            +
                    CG_BLANK = '[ \t]'
         | 
| 392 411 | 
             
                    CC_WORD = '[:alnum:]_'
         | 
| 393 412 | 
             
                    CG_WORD = '[[:alnum:]_]'
         | 
| 394 413 | 
             
                  end
         | 
| 395 | 
            -
                end
         | 
| 414 | 
            +
                end unless RUBY_ENGINE == 'opal'
         | 
| 396 415 |  | 
| 397 416 | 
             
                ## Document header
         | 
| 398 417 |  | 
| @@ -415,15 +434,16 @@ module Asciidoctor | |
| 415 434 | 
             
                #   v1.0, 2013-01-01: Ring in the new year release
         | 
| 416 435 | 
             
                #   1.0, Jan 01, 2013
         | 
| 417 436 | 
             
                #
         | 
| 418 | 
            -
                RevisionInfoLineRx = /^(?:\D*(.*?),)?( | 
| 437 | 
            +
                RevisionInfoLineRx = /^(?:\D*(.*?),)?(?: *(?!:)(.*?))(?: *(?!^): *(.*))?$/
         | 
| 419 438 |  | 
| 420 439 | 
             
                # Matches the title and volnum in the manpage doctype.
         | 
| 421 440 | 
             
                #
         | 
| 422 441 | 
             
                # Examples
         | 
| 423 442 | 
             
                #
         | 
| 443 | 
            +
                #   = asciidoctor(1)
         | 
| 424 444 | 
             
                #   = asciidoctor ( 1 )
         | 
| 425 445 | 
             
                #
         | 
| 426 | 
            -
                ManpageTitleVolnumRx = /^( | 
| 446 | 
            +
                ManpageTitleVolnumRx = /^(.+?) *\( *(.+?) *\)$/
         | 
| 427 447 |  | 
| 428 448 | 
             
                # Matches the name and purpose in the manpage doctype.
         | 
| 429 449 | 
             
                #
         | 
| @@ -431,7 +451,7 @@ module Asciidoctor | |
| 431 451 | 
             
                #
         | 
| 432 452 | 
             
                #   asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
         | 
| 433 453 | 
             
                #
         | 
| 434 | 
            -
                ManpageNamePurposeRx = /^( | 
| 454 | 
            +
                ManpageNamePurposeRx = /^(.+?) +- +(.+)$/
         | 
| 435 455 |  | 
| 436 456 | 
             
                ## Preprocessor directives
         | 
| 437 457 |  | 
| @@ -447,7 +467,7 @@ module Asciidoctor | |
| 447 467 | 
             
                #   endif::basebackend-html[]
         | 
| 448 468 | 
             
                #   endif::[]
         | 
| 449 469 | 
             
                #
         | 
| 450 | 
            -
                ConditionalDirectiveRx =  | 
| 470 | 
            +
                ConditionalDirectiveRx = /^(\\)?(ifdef|ifndef|ifeval|endif)::(\S*?(?:([,+])\S*?)?)\[(.+)?\]$/
         | 
| 451 471 |  | 
| 452 472 | 
             
                # Matches a restricted (read as safe) eval expression.
         | 
| 453 473 | 
             
                #
         | 
| @@ -455,7 +475,7 @@ module Asciidoctor | |
| 455 475 | 
             
                #
         | 
| 456 476 | 
             
                #   "{asciidoctor-version}" >= "0.1.0"
         | 
| 457 477 | 
             
                #
         | 
| 458 | 
            -
                EvalExpressionRx = /^( | 
| 478 | 
            +
                EvalExpressionRx = /^(.+?) *([=!><]=|[><]) *(.+)$/
         | 
| 459 479 |  | 
| 460 480 | 
             
                # Matches an include preprocessor directive.
         | 
| 461 481 | 
             
                #
         | 
| @@ -464,7 +484,7 @@ module Asciidoctor | |
| 464 484 | 
             
                #   include::chapter1.ad[]
         | 
| 465 485 | 
             
                #   include::example.txt[lines=1;2;5..10]
         | 
| 466 486 | 
             
                #
         | 
| 467 | 
            -
                IncludeDirectiveRx =  | 
| 487 | 
            +
                IncludeDirectiveRx = /^(\\)?include::([^\[][^\[]*)\[(.*)\]$/
         | 
| 468 488 |  | 
| 469 489 | 
             
                # Matches a trailing tag directive in an include file.
         | 
| 470 490 | 
             
                #
         | 
| @@ -477,7 +497,7 @@ module Asciidoctor | |
| 477 497 | 
             
                #     log(e);
         | 
| 478 498 | 
             
                #   }
         | 
| 479 499 | 
             
                #   // end::try-catch[]
         | 
| 480 | 
            -
                TagDirectiveRx = /\b(?:tag|end) | 
| 500 | 
            +
                TagDirectiveRx = /\b(?:tag|(end))::(\S+)\[\]$/
         | 
| 481 501 |  | 
| 482 502 | 
             
                ## Attribute entries and references
         | 
| 483 503 |  | 
| @@ -489,34 +509,41 @@ module Asciidoctor | |
| 489 509 | 
             
                #   :First Name: Dan
         | 
| 490 510 | 
             
                #   :sectnums!:
         | 
| 491 511 | 
             
                #   :!toc:
         | 
| 492 | 
            -
                #   :long-entry: Attribute value lines ending in '  | 
| 493 | 
            -
                #                are joined together as a single value,
         | 
| 494 | 
            -
                #                collapsing the line breaks and indentation to
         | 
| 512 | 
            +
                #   :long-entry: Attribute value lines ending in ' \' \
         | 
| 513 | 
            +
                #                are joined together as a single value, \
         | 
| 514 | 
            +
                #                collapsing the line breaks and indentation to \
         | 
| 495 515 | 
             
                #                a single space.
         | 
| 496 516 | 
             
                #
         | 
| 497 | 
            -
                AttributeEntryRx = /^:(!?\w.*?):( | 
| 517 | 
            +
                AttributeEntryRx = /^:(!?\w.*?):(?:[ \t]+(.*))?$/
         | 
| 498 518 |  | 
| 499 519 | 
             
                # Matches invalid characters in an attribute name.
         | 
| 500 520 | 
             
                InvalidAttributeNameCharsRx = /[^\w\-]/
         | 
| 501 521 |  | 
| 502 | 
            -
                # Matches  | 
| 522 | 
            +
                # Matches a pass inline macro that surrounds the value of an attribute
         | 
| 523 | 
            +
                # entry once it has been parsed.
         | 
| 503 524 | 
             
                #
         | 
| 504 525 | 
             
                # Examples
         | 
| 505 526 | 
             
                #
         | 
| 506 527 | 
             
                #   pass:[text]
         | 
| 528 | 
            +
                #   pass:a[{a} {b} {c}]
         | 
| 507 529 | 
             
                #
         | 
| 508 | 
            -
                 | 
| 530 | 
            +
                if RUBY_ENGINE == 'opal'
         | 
| 531 | 
            +
                  # In JavaScript, ^ and $ match the boundaries of the string when the m flag is not set
         | 
| 532 | 
            +
                  AttributeEntryPassMacroRx = /^pass:([a-z]+(?:,[a-z]+)*)?\[([\S\s]*)\]$/
         | 
| 533 | 
            +
                else
         | 
| 534 | 
            +
                  AttributeEntryPassMacroRx = /\Apass:([a-z]+(?:,[a-z]+)*)?\[(.*)\]\Z/m
         | 
| 535 | 
            +
                end
         | 
| 509 536 |  | 
| 510 537 | 
             
                # Matches an inline attribute reference.
         | 
| 511 538 | 
             
                #
         | 
| 512 539 | 
             
                # Examples
         | 
| 513 540 | 
             
                #
         | 
| 514 | 
            -
                #   { | 
| 515 | 
            -
                #   {counter: | 
| 541 | 
            +
                #   {foobar} or {app_name} or {product-version}
         | 
| 542 | 
            +
                #   {counter:sequence-name:1}
         | 
| 516 543 | 
             
                #   {set:foo:bar}
         | 
| 517 544 | 
             
                #   {set:name!}
         | 
| 518 545 | 
             
                #
         | 
| 519 | 
            -
                AttributeReferenceRx = /(\\)?\{((set|counter2?) | 
| 546 | 
            +
                AttributeReferenceRx = /(\\)?\{(\w+[-\w]*|(set|counter2?):.+?)(\\)?\}/
         | 
| 520 547 |  | 
| 521 548 | 
             
                ## Paragraphs and delimited blocks
         | 
| 522 549 |  | 
| @@ -527,7 +554,7 @@ module Asciidoctor | |
| 527 554 | 
             
                #   [[idname]]
         | 
| 528 555 | 
             
                #   [[idname,Reference Text]]
         | 
| 529 556 | 
             
                #
         | 
| 530 | 
            -
                BlockAnchorRx = /^\[\[(?:|([#{CC_ALPHA}: | 
| 557 | 
            +
                BlockAnchorRx = /^\[\[(?:|([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+))?)\]\]$/
         | 
| 531 558 |  | 
| 532 559 | 
             
                # Matches an attribute list above a block element.
         | 
| 533 560 | 
             
                #
         | 
| @@ -542,12 +569,12 @@ module Asciidoctor | |
| 542 569 | 
             
                #   # as attribute reference
         | 
| 543 570 | 
             
                #   [{lead}]
         | 
| 544 571 | 
             
                #
         | 
| 545 | 
            -
                BlockAttributeListRx = /^\[( | 
| 572 | 
            +
                BlockAttributeListRx = /^\[(|[#{CC_WORD}.#%{,"'].*)\]$/
         | 
| 546 573 |  | 
| 547 574 | 
             
                # A combined pattern that matches either a block anchor or a block attribute list.
         | 
| 548 575 | 
             
                #
         | 
| 549 576 | 
             
                # TODO this one gets hit a lot, should be optimized as much as possible
         | 
| 550 | 
            -
                BlockAttributeLineRx = /^\[( | 
| 577 | 
            +
                BlockAttributeLineRx = /^\[(?:|[#{CC_WORD}.#%{,"'].*|\[(?:|[#{CC_ALPHA}_:][#{CC_WORD}:.-]*(?:, *.+)?)\])\]$/
         | 
| 551 578 |  | 
| 552 579 | 
             
                # Matches a title above a block.
         | 
| 553 580 | 
             
                #
         | 
| @@ -555,7 +582,7 @@ module Asciidoctor | |
| 555 582 | 
             
                #
         | 
| 556 583 | 
             
                #   .Title goes here
         | 
| 557 584 | 
             
                #
         | 
| 558 | 
            -
                BlockTitleRx = /^\.([ | 
| 585 | 
            +
                BlockTitleRx = /^\.([^ \t.].*)$/
         | 
| 559 586 |  | 
| 560 587 | 
             
                # Matches an admonition label at the start of a paragraph.
         | 
| 561 588 | 
             
                #
         | 
| @@ -564,7 +591,7 @@ module Asciidoctor | |
| 564 591 | 
             
                #   NOTE: Just a little note.
         | 
| 565 592 | 
             
                #   TIP: Don't forget!
         | 
| 566 593 | 
             
                #
         | 
| 567 | 
            -
                AdmonitionParagraphRx = /^(#{ADMONITION_STYLES.to_a * '|'}) | 
| 594 | 
            +
                AdmonitionParagraphRx = /^(#{ADMONITION_STYLES.to_a * '|'}):[ \t]+/
         | 
| 568 595 |  | 
| 569 596 | 
             
                # Matches a literal paragraph, which is a line of text preceded by at least one space.
         | 
| 570 597 | 
             
                #
         | 
| @@ -572,7 +599,7 @@ module Asciidoctor | |
| 572 599 | 
             
                #
         | 
| 573 600 | 
             
                #   <SPACE>Foo
         | 
| 574 601 | 
             
                #   <TAB>Foo
         | 
| 575 | 
            -
                LiteralParagraphRx = /^( | 
| 602 | 
            +
                LiteralParagraphRx = /^([ \t]+.*)$/
         | 
| 576 603 |  | 
| 577 604 | 
             
                # Matches a comment block.
         | 
| 578 605 | 
             
                #
         | 
| @@ -582,7 +609,7 @@ module Asciidoctor | |
| 582 609 | 
             
                #   This is a block comment.
         | 
| 583 610 | 
             
                #   It can span one or more lines.
         | 
| 584 611 | 
             
                #   ////
         | 
| 585 | 
            -
                CommentBlockRx = %r | 
| 612 | 
            +
                CommentBlockRx = %r(^/{4,}$)
         | 
| 586 613 |  | 
| 587 614 | 
             
                # Matches a comment line.
         | 
| 588 615 | 
             
                #
         | 
| @@ -590,7 +617,7 @@ module Asciidoctor | |
| 590 617 | 
             
                #
         | 
| 591 618 | 
             
                #   // an then whatever
         | 
| 592 619 | 
             
                #
         | 
| 593 | 
            -
                CommentLineRx = %r | 
| 620 | 
            +
                CommentLineRx = %r(^//(?=[^/]|$))
         | 
| 594 621 |  | 
| 595 622 | 
             
                ## Section titles
         | 
| 596 623 |  | 
| @@ -607,20 +634,12 @@ module Asciidoctor | |
| 607 634 | 
             
                # match[1] is the delimiter, whose length determines the level
         | 
| 608 635 | 
             
                # match[2] is the title itself
         | 
| 609 636 | 
             
                # match[3] is an inline anchor, which becomes the section id
         | 
| 610 | 
            -
                AtxSectionRx = /^( | 
| 637 | 
            +
                AtxSectionRx = /^(=={0,5}|#\#{0,5})[ \t]+(.+?)(?:[ \t]+\1)?$/
         | 
| 611 638 |  | 
| 612 639 | 
             
                # Matches the restricted section name for a two-line (Setext-style) section title.
         | 
| 613 640 | 
             
                # The name cannot begin with a dot and has at least one alphanumeric character.
         | 
| 614 641 | 
             
                SetextSectionTitleRx = /^((?=.*#{CG_WORD}+.*)[^.].*?)$/
         | 
| 615 642 |  | 
| 616 | 
            -
                # Matches the underline in a two-line (Setext-style) section title.
         | 
| 617 | 
            -
                #
         | 
| 618 | 
            -
                # Examples
         | 
| 619 | 
            -
                #
         | 
| 620 | 
            -
                #   ======  || ------ || ~~~~~~ || ^^^^^^ || ++++++
         | 
| 621 | 
            -
                #
         | 
| 622 | 
            -
                SetextSectionLineRx = /^(?:=|-|~|\^|\+)+$/
         | 
| 623 | 
            -
             | 
| 624 643 | 
             
                # Matches an anchor (i.e., id + optional reference text) inside a section title.
         | 
| 625 644 | 
             
                #
         | 
| 626 645 | 
             
                # Examples
         | 
| @@ -628,10 +647,12 @@ module Asciidoctor | |
| 628 647 | 
             
                #   Section Title [[idname]]
         | 
| 629 648 | 
             
                #   Section Title [[idname,Reference Text]]
         | 
| 630 649 | 
             
                #
         | 
| 631 | 
            -
                InlineSectionAnchorRx =  | 
| 650 | 
            +
                InlineSectionAnchorRx = / (\\)?\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+))?\]\]$/
         | 
| 632 651 |  | 
| 633 652 | 
             
                # Matches invalid characters in a section id.
         | 
| 634 | 
            -
                 | 
| 653 | 
            +
                #
         | 
| 654 | 
            +
                # NOTE uppercase chars are not included since the expression is used on a lowercased string
         | 
| 655 | 
            +
                InvalidSectionIdCharsRx = /&(?:[a-z][a-z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-f][\da-f][\da-f]{0,3});|[^#{CC_WORD}]+?/
         | 
| 635 656 |  | 
| 636 657 | 
             
                # Matches the block style used to designate a section title as a floating title.
         | 
| 637 658 | 
             
                #
         | 
| @@ -647,7 +668,7 @@ module Asciidoctor | |
| 647 668 | 
             
                # Detects the start of any list item.
         | 
| 648 669 | 
             
                #
         | 
| 649 670 | 
             
                # NOTE we only have to check as far as the blank character because we know it means non-whitespace follows.
         | 
| 650 | 
            -
                AnyListRx = /^( | 
| 671 | 
            +
                AnyListRx = /^(?:[ \t]*(?:-|\*\*{0,4}|\.\.{0,4}|\u2022\u2022{0,4}|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|[ \t]*.*?(?::{2,4}|;;)(?:$|[ \t])|<?\d+>[ \t])/
         | 
| 651 672 |  | 
| 652 673 | 
             
                # Matches an unordered list item (one level for hyphens, up to 5 levels for asterisks).
         | 
| 653 674 | 
             
                #
         | 
| @@ -657,8 +678,7 @@ module Asciidoctor | |
| 657 678 | 
             
                #   - Foo
         | 
| 658 679 | 
             
                #
         | 
| 659 680 | 
             
                # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
         | 
| 660 | 
            -
                 | 
| 661 | 
            -
                UnorderedListRx = /^#{CG_BLANK}*(-|\*{1,5}|\u2022{1,5})#{CG_BLANK}+(.*)$/
         | 
| 681 | 
            +
                UnorderedListRx = /^[ \t]*(-|\*\*{0,4}|\u2022\u2022{0,4})[ \t]+(.*)$/
         | 
| 662 682 |  | 
| 663 683 | 
             
                # Matches an ordered list item (explicit numbering or up to 5 consecutive dots).
         | 
| 664 684 | 
             
                #
         | 
| @@ -674,11 +694,11 @@ module Asciidoctor | |
| 674 694 | 
             
                #
         | 
| 675 695 | 
             
                # NOTE leading space match is not always necessary, but is used for list reader
         | 
| 676 696 | 
             
                # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
         | 
| 677 | 
            -
                OrderedListRx =  | 
| 697 | 
            +
                OrderedListRx = /^[ \t]*(\.\.{0,4}|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]+(.*)$/
         | 
| 678 698 |  | 
| 679 699 | 
             
                # Matches the ordinals for each type of ordered list.
         | 
| 680 700 | 
             
                OrderedListMarkerRxMap = {
         | 
| 681 | 
            -
                  :arabic => /\d | 
| 701 | 
            +
                  :arabic => /\d+\./,
         | 
| 682 702 | 
             
                  :loweralpha => /[a-z]\./,
         | 
| 683 703 | 
             
                  :lowerroman => /[ivx]+\)/,
         | 
| 684 704 | 
             
                  :upperalpha => /[A-Z]\./,
         | 
| @@ -711,15 +731,15 @@ module Asciidoctor | |
| 711 731 | 
             
                # NOTE negative match for comment line is intentional since that isn't handled when looking for next list item
         | 
| 712 732 | 
             
                # TODO check for line comment when scanning lines instead of in regex
         | 
| 713 733 | 
             
                #
         | 
| 714 | 
            -
                DescriptionListRx =  | 
| 734 | 
            +
                DescriptionListRx = %r(^(?!//)[ \t]*(.*?)(:{2,4}|;;)(?:[ \t]+(.*))?$)
         | 
| 715 735 |  | 
| 716 736 | 
             
                # Matches a sibling description list item (which does not include the type in the key).
         | 
| 717 737 | 
             
                DescriptionListSiblingRx = {
         | 
| 718 738 | 
             
                  # (?:.*?[^:])? - a non-capturing group which grabs longest sequence of characters that doesn't end w/ colon
         | 
| 719 | 
            -
                  '::' =>  | 
| 720 | 
            -
                  ':::' =>  | 
| 721 | 
            -
                  '::::' =>  | 
| 722 | 
            -
                  ';;' =>  | 
| 739 | 
            +
                  '::' => %r(^(?!//)[ \t]*((?:.*[^:])?)(::)(?:[ \t]+(.*))?$),
         | 
| 740 | 
            +
                  ':::' => %r(^(?!//)[ \t]*((?:.*[^:])?)(:::)(?:[ \t]+(.*))?$),
         | 
| 741 | 
            +
                  '::::' => %r(^(?!//)[ \t]*((?:.*[^:])?)(::::)(?:[ \t]+(.*))?$),
         | 
| 742 | 
            +
                  ';;' => %r(^(?!//)[ \t]*(.*)(;;)(?:[ \t]+(.*))?$)
         | 
| 723 743 | 
             
                }
         | 
| 724 744 |  | 
| 725 745 | 
             
                # Matches a callout list item.
         | 
| @@ -729,7 +749,10 @@ module Asciidoctor | |
| 729 749 | 
             
                #   <1> Foo
         | 
| 730 750 | 
             
                #
         | 
| 731 751 | 
             
                # NOTE we know trailing (.*) will match at least one character because we strip trailing spaces
         | 
| 732 | 
            -
                CalloutListRx = /^<?(\d+) | 
| 752 | 
            +
                CalloutListRx = /^<?(\d+)>[ \t]+(.*)$/
         | 
| 753 | 
            +
             | 
| 754 | 
            +
                # Detects a potential callout list item.
         | 
| 755 | 
            +
                CalloutListSniffRx = /^<?\d+>/
         | 
| 733 756 |  | 
| 734 757 | 
             
                # Matches a callout reference inside literal text.
         | 
| 735 758 | 
             
                #
         | 
| @@ -739,12 +762,12 @@ module Asciidoctor | |
| 739 762 | 
             
                #   <!--1--> (for XML-based languages)
         | 
| 740 763 | 
             
                #
         | 
| 741 764 | 
             
                # NOTE extract regexps are applied line-by-line, so we can use $ as end-of-line char
         | 
| 742 | 
            -
                CalloutExtractRx =  | 
| 765 | 
            +
                CalloutExtractRx = %r((?:(?://|#|--|;;) ?)?(\\)?<!?(|--)(\d+)\2>(?=(?: ?\\?<!?\2\d+\2>)*$))
         | 
| 743 766 | 
             
                CalloutExtractRxt = '(\\\\)?<()(\\d+)>(?=(?: ?\\\\?<\\d+>)*$)'
         | 
| 744 767 | 
             
                # NOTE special characters have not been replaced when scanning
         | 
| 745 | 
            -
                 | 
| 768 | 
            +
                CalloutScanRx = /\\?<!?(|--)(\d+)\1>(?=(?: ?\\?<!?\1\d+\1>)*#{CC_EOL})/
         | 
| 746 769 | 
             
                # NOTE special characters have already been replaced when converting to an SGML format
         | 
| 747 | 
            -
                CalloutSourceRx =  | 
| 770 | 
            +
                CalloutSourceRx = %r((?:(?://|#|--|;;) ?)?(\\)?<!?(|--)(\d+)\2>(?=(?: ?\\?<!?\2\d+\2>)*#{CC_EOL}))
         | 
| 748 771 | 
             
                CalloutSourceRxt = "(\\\\)?<()(\\d+)>(?=(?: ?\\\\?<\\d+>)*#{CC_EOL})"
         | 
| 749 772 |  | 
| 750 773 | 
             
                # A Hash of regexps for lists used for dynamic access.
         | 
| @@ -772,12 +795,12 @@ module Asciidoctor | |
| 772 795 | 
             
                #   2.3+<.>m
         | 
| 773 796 | 
             
                #
         | 
| 774 797 | 
             
                # FIXME use step-wise scan (or treetop) rather than this mega-regexp
         | 
| 775 | 
            -
                CellSpecStartRx =  | 
| 776 | 
            -
                CellSpecEndRx =  | 
| 798 | 
            +
                CellSpecStartRx = /^[ \t]*(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
         | 
| 799 | 
            +
                CellSpecEndRx = /[ \t]+(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
         | 
| 777 800 |  | 
| 778 801 | 
             
                # Block macros
         | 
| 779 802 |  | 
| 780 | 
            -
                # Matches the  | 
| 803 | 
            +
                # Matches the custom block macro pattern.
         | 
| 781 804 | 
             
                #
         | 
| 782 805 | 
             
                # Examples
         | 
| 783 806 | 
             
                #
         | 
| @@ -785,7 +808,7 @@ module Asciidoctor | |
| 785 808 | 
             
                #
         | 
| 786 809 | 
             
                #--
         | 
| 787 810 | 
             
                # NOTE we've relaxed the match for target to accomodate the short format (e.g., name::[attrlist])
         | 
| 788 | 
            -
                 | 
| 811 | 
            +
                CustomBlockMacroRx = /^(#{CG_WORD}+)::(|\S|\S.*?\S)\[(.*)\]$/
         | 
| 789 812 |  | 
| 790 813 | 
             
                # Matches an image, video or audio block macro.
         | 
| 791 814 | 
             
                #
         | 
| @@ -794,7 +817,7 @@ module Asciidoctor | |
| 794 817 | 
             
                #   image::filename.png[Caption]
         | 
| 795 818 | 
             
                #   video::http://youtube.com/12345[Cats vs Dogs]
         | 
| 796 819 | 
             
                #
         | 
| 797 | 
            -
                 | 
| 820 | 
            +
                BlockMediaMacroRx = /^(image|video|audio)::(\S|\S.*?\S)\[(.*)\]$/
         | 
| 798 821 |  | 
| 799 822 | 
             
                # Matches the TOC block macro.
         | 
| 800 823 | 
             
                #
         | 
| @@ -803,7 +826,7 @@ module Asciidoctor | |
| 803 826 | 
             
                #   toc::[]
         | 
| 804 827 | 
             
                #   toc::[levels=2]
         | 
| 805 828 | 
             
                #
         | 
| 806 | 
            -
                 | 
| 829 | 
            +
                BlockTocMacroRx = /^toc::\[(.*)\]$/
         | 
| 807 830 |  | 
| 808 831 | 
             
                ## Inline macros
         | 
| 809 832 |  | 
| @@ -816,21 +839,24 @@ module Asciidoctor | |
| 816 839 | 
             
                #   anchor:idname[]
         | 
| 817 840 | 
             
                #   anchor:idname[Reference Text]
         | 
| 818 841 | 
             
                #
         | 
| 819 | 
            -
                InlineAnchorRx =  | 
| 842 | 
            +
                InlineAnchorRx = /(\\)?(?:\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+?))?\]\]|anchor:([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)\[(?:\]|(.*?[^\\])\]))/
         | 
| 843 | 
            +
             | 
| 844 | 
            +
                # Scans for a non-escaped anchor (i.e., id + optional reference text) in the flow of text.
         | 
| 845 | 
            +
                InlineAnchorScanRx = /(?:^|[^\\\[])\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+?))?\]\]|(?:^|[^\\])anchor:([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)\[(?:\]|(.*?[^\\])\])/
         | 
| 820 846 |  | 
| 821 | 
            -
                # Matches a bibliography anchor  | 
| 847 | 
            +
                # Matches a bibliography anchor at the start of the list item text (in a bibliography list).
         | 
| 822 848 | 
             
                #
         | 
| 823 849 | 
             
                # Examples
         | 
| 824 850 | 
             
                #
         | 
| 825 | 
            -
                #   [[[ | 
| 851 | 
            +
                #   [[[Fowler_1997]]] Fowler M. ...
         | 
| 826 852 | 
             
                #
         | 
| 827 | 
            -
                InlineBiblioAnchorRx =  | 
| 853 | 
            +
                InlineBiblioAnchorRx = /^\[\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+?))?\]\]\]/
         | 
| 828 854 |  | 
| 829 855 | 
             
                # Matches an inline e-mail address.
         | 
| 830 856 | 
             
                #
         | 
| 831 857 | 
             
                #   doc.writer@example.com
         | 
| 832 858 | 
             
                #
         | 
| 833 | 
            -
                 | 
| 859 | 
            +
                EmailInlineRx = %r(([\\>:/])?#{CG_WORD}[#{CC_WORD}.%+-]*@#{CG_ALNUM}[#{CC_ALNUM}.-]*\.#{CG_ALPHA}{2,4}\b)
         | 
| 834 860 |  | 
| 835 861 | 
             
                # Matches an inline footnote macro, which is allowed to span multiple lines.
         | 
| 836 862 | 
             
                #
         | 
| @@ -839,7 +865,7 @@ module Asciidoctor | |
| 839 865 | 
             
                #   footnoteref:[id,text]
         | 
| 840 866 | 
             
                #   footnoteref:[id]
         | 
| 841 867 | 
             
                #
         | 
| 842 | 
            -
                 | 
| 868 | 
            +
                InlineFootnoteMacroRx = /\\?(footnote(?:ref)?):\[(#{CC_ALL}*?[^\\])\]/m
         | 
| 843 869 |  | 
| 844 870 | 
             
                # Matches an image or icon inline macro.
         | 
| 845 871 | 
             
                #
         | 
| @@ -850,7 +876,8 @@ module Asciidoctor | |
| 850 876 | 
             
                #   image:filename.png[More [Alt\] Text] (alt text becomes "More [Alt] Text")
         | 
| 851 877 | 
             
                #   icon:github[large]
         | 
| 852 878 | 
             
                #
         | 
| 853 | 
            -
                 | 
| 879 | 
            +
                # NOTE be as non-greedy as possible by not allowing endline or left square bracket in target
         | 
| 880 | 
            +
                InlineImageMacroRx = /\\?i(?:mage|con):([^:\s\[](?:[^\n\[]*[^\s\[])?)\[(|#{CC_ALL}*?[^\\])\]/m
         | 
| 854 881 |  | 
| 855 882 | 
             
                # Matches an indexterm inline macro, which may span multiple lines.
         | 
| 856 883 | 
             
                #
         | 
| @@ -861,7 +888,7 @@ module Asciidoctor | |
| 861 888 | 
             
                #   indexterm2:[Tigers]
         | 
| 862 889 | 
             
                #   ((Tigers))
         | 
| 863 890 | 
             
                #
         | 
| 864 | 
            -
                 | 
| 891 | 
            +
                InlineIndextermMacroRx = /\\?(?:(indexterm2?):\[(#{CC_ALL}*?[^\\])\]|\(\((#{CC_ALL}+?)\)\)(?!\)))/m
         | 
| 865 892 |  | 
| 866 893 | 
             
                # Matches either the kbd or btn inline macro.
         | 
| 867 894 | 
             
                #
         | 
| @@ -873,26 +900,19 @@ module Asciidoctor | |
| 873 900 | 
             
                #   kbd:[Ctrl,T]
         | 
| 874 901 | 
             
                #   btn:[Save]
         | 
| 875 902 | 
             
                #
         | 
| 876 | 
            -
                 | 
| 877 | 
            -
             | 
| 878 | 
            -
                # Matches the delimiter used for kbd value.
         | 
| 879 | 
            -
                #
         | 
| 880 | 
            -
                # Examples
         | 
| 881 | 
            -
                #
         | 
| 882 | 
            -
                #   Ctrl + Alt+T
         | 
| 883 | 
            -
                #   Ctrl,T
         | 
| 884 | 
            -
                #
         | 
| 885 | 
            -
                KbdDelimiterRx = /(?:\+|,)(?=#{CG_BLANK}*[^\1])/
         | 
| 903 | 
            +
                InlineKbdBtnMacroRx = /(\\)?(kbd|btn):\[(#{CC_ALL}*?[^\\])\]/m
         | 
| 886 904 |  | 
| 887 905 | 
             
                # Matches an implicit link and some of the link inline macro.
         | 
| 888 906 | 
             
                #
         | 
| 889 907 | 
             
                # Examples
         | 
| 890 908 | 
             
                #
         | 
| 891 | 
            -
                #    | 
| 892 | 
            -
                #    | 
| 909 | 
            +
                #   https://github.com
         | 
| 910 | 
            +
                #   https://github.com[GitHub]
         | 
| 911 | 
            +
                #   <https://github.com>
         | 
| 912 | 
            +
                #   link:https://github.com[]
         | 
| 893 913 | 
             
                #
         | 
| 894 914 | 
             
                # FIXME revisit! the main issue is we need different rules for implicit vs explicit
         | 
| 895 | 
            -
                LinkInlineRx = %r | 
| 915 | 
            +
                LinkInlineRx = %r((^|link:|#{CG_BLANK}|<|[>\(\)\[\];])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*[^\s.,\[\]<])(?:\[(|#{CC_ALL}*?[^\\])\])?)m
         | 
| 896 916 |  | 
| 897 917 | 
             
                # Match a link or e-mail inline macro.
         | 
| 898 918 | 
             
                #
         | 
| @@ -901,7 +921,12 @@ module Asciidoctor | |
| 901 921 | 
             
                #   link:path[label]
         | 
| 902 922 | 
             
                #   mailto:doc.writer@example.com[]
         | 
| 903 923 | 
             
                #
         | 
| 904 | 
            -
                 | 
| 924 | 
            +
                # NOTE be as non-greedy as possible by not allowing space or left square bracket in target
         | 
| 925 | 
            +
                InlineLinkMacroRx = /\\?(?:link|(mailto)):(|[^:\s\[][^\s\[]*)\[(|#{CC_ALL}*?[^\\])\]/m
         | 
| 926 | 
            +
             | 
| 927 | 
            +
                # Matches the name of a macro.
         | 
| 928 | 
            +
                #
         | 
| 929 | 
            +
                MacroNameRx = /^#{CG_WORD}+$/
         | 
| 905 930 |  | 
| 906 931 | 
             
                # Matches a stem (and alternatives, asciimath and latexmath) inline macro, which may span multiple lines.
         | 
| 907 932 | 
             
                #
         | 
| @@ -911,17 +936,17 @@ module Asciidoctor | |
| 911 936 | 
             
                #   asciimath:[x != 0]
         | 
| 912 937 | 
             
                #   latexmath:[\sqrt{4} = 2]
         | 
| 913 938 | 
             
                #
         | 
| 914 | 
            -
                 | 
| 939 | 
            +
                InlineStemMacroRx = /\\?(stem|(?:latex|ascii)math):([a-z]+(?:,[a-z]+)*)?\[(#{CC_ALL}*?[^\\])\]/m
         | 
| 915 940 |  | 
| 916 941 | 
             
                # Matches a menu inline macro.
         | 
| 917 942 | 
             
                #
         | 
| 918 943 | 
             
                # Examples
         | 
| 919 944 | 
             
                #
         | 
| 920 | 
            -
                #   menu:File[ | 
| 945 | 
            +
                #   menu:File[Save As...]
         | 
| 921 946 | 
             
                #   menu:View[Page Style > No Style]
         | 
| 922 947 | 
             
                #   menu:View[Page Style, No Style]
         | 
| 923 948 | 
             
                #
         | 
| 924 | 
            -
                 | 
| 949 | 
            +
                InlineMenuMacroRx = /\\?menu:(#{CG_WORD}|[#{CC_WORD}&][^\n\[]*[^\s\[])\[ *(#{CC_ALL}*?[^\\])?\]/m
         | 
| 925 950 |  | 
| 926 951 | 
             
                # Matches an implicit menu inline macro.
         | 
| 927 952 | 
             
                #
         | 
| @@ -929,7 +954,7 @@ module Asciidoctor | |
| 929 954 | 
             
                #
         | 
| 930 955 | 
             
                #   "File > New..."
         | 
| 931 956 | 
             
                #
         | 
| 932 | 
            -
                MenuInlineRx = /\\?"(#{ | 
| 957 | 
            +
                MenuInlineRx = /\\?"([#{CC_WORD}&][^"]*?[ \n]+>[ \n]+[^"]*)"/
         | 
| 933 958 |  | 
| 934 959 | 
             
                # Matches an inline passthrough value, which may span multiple lines.
         | 
| 935 960 | 
             
                #
         | 
| @@ -940,8 +965,8 @@ module Asciidoctor | |
| 940 965 | 
             
                #
         | 
| 941 966 | 
             
                # NOTE we always capture the attributes so we know when to use compatible (i.e., legacy) behavior
         | 
| 942 967 | 
             
                PassInlineRx = {
         | 
| 943 | 
            -
                  false => ['+', '`', /(^|[^#{CC_WORD};:])(?:\[([^\]] | 
| 944 | 
            -
                  true  => ['`', nil, /(^|[^`#{CC_WORD}])(?:\[([^\]] | 
| 968 | 
            +
                  false => ['+', '`', /(^|[^#{CC_WORD};:])(?:\[([^\]]+)\])?(\\?(\+|`)(\S|\S#{CC_ALL}*?\S)\4)(?!#{CG_WORD})/m],
         | 
| 969 | 
            +
                  true  => ['`', nil, /(^|[^`#{CC_WORD}])(?:\[([^\]]+)\])?(\\?(`)([^`\s]|[^`\s]#{CC_ALL}*?\S)\4)(?![`#{CC_WORD}])/m]
         | 
| 945 970 | 
             
                }
         | 
| 946 971 |  | 
| 947 972 | 
             
                # Matches several variants of the passthrough inline macro, which may span multiple lines.
         | 
| @@ -952,7 +977,8 @@ module Asciidoctor | |
| 952 977 | 
             
                #   $$text$$
         | 
| 953 978 | 
             
                #   pass:quotes[text]
         | 
| 954 979 | 
             
                #
         | 
| 955 | 
            -
                 | 
| 980 | 
            +
                # NOTE we have to support an empty pass:[] for compatibility with AsciiDoc Python
         | 
| 981 | 
            +
                InlinePassMacroRx = /(?:(?:(\\?)\[([^\]]+)\])?(\\{0,2})(\+\+\+?|\$\$)(#{CC_ALL}*?)\4|(\\?)pass:([a-z]+(?:,[a-z]+)*)?\[(|#{CC_ALL}*?[^\\])\])/m
         | 
| 956 982 |  | 
| 957 983 | 
             
                # Matches an xref (i.e., cross-reference) inline macro, which may span multiple lines.
         | 
| 958 984 | 
             
                #
         | 
| @@ -962,78 +988,70 @@ module Asciidoctor | |
| 962 988 | 
             
                #   xref:id[reftext]
         | 
| 963 989 | 
             
                #
         | 
| 964 990 | 
             
                # NOTE special characters have already been escaped, hence the entity references
         | 
| 965 | 
            -
                 | 
| 991 | 
            +
                # NOTE { is included in start characters to support target that begins with attribute reference in title content
         | 
| 992 | 
            +
                InlineXrefMacroRx = %r(\\?(?:<<([#{CC_WORD}#/.:{]#{CC_ALL}*?)>>|xref:([#{CC_WORD}#/.:{]#{CC_ALL}*?)\[(#{CC_ALL}*?[^\\])?\]))m
         | 
| 966 993 |  | 
| 967 994 | 
             
                ## Layout
         | 
| 968 995 |  | 
| 969 996 | 
             
                # Matches a trailing + preceded by at least one space character,
         | 
| 970 997 | 
             
                # which forces a hard line break (<br> tag in HTML output).
         | 
| 971 998 | 
             
                #
         | 
| 999 | 
            +
                # NOTE AsciiDoc Python allows + to be preceded by TAB; Asciidoctor does not
         | 
| 1000 | 
            +
                #
         | 
| 972 1001 | 
             
                # Examples
         | 
| 973 1002 | 
             
                #
         | 
| 974 1003 | 
             
                #   Humpty Dumpty sat on a wall, +
         | 
| 975 1004 | 
             
                #   Humpty Dumpty had a great fall.
         | 
| 976 1005 | 
             
                #
         | 
| 977 1006 | 
             
                if RUBY_ENGINE == 'opal'
         | 
| 978 | 
            -
                  # NOTE  | 
| 979 | 
            -
                   | 
| 1007 | 
            +
                  # NOTE In Ruby, ^ and $ always match start and end of line, respectively; JavaScript only does so in multiline mode
         | 
| 1008 | 
            +
                  HardLineBreakRx = /^(.*) \+$/m
         | 
| 980 1009 | 
             
                else
         | 
| 981 | 
            -
                   | 
| 1010 | 
            +
                  HardLineBreakRx = /^(.*) \+$/
         | 
| 982 1011 | 
             
                end
         | 
| 983 1012 |  | 
| 984 | 
            -
                # Matches  | 
| 1013 | 
            +
                # Matches a Markdown horizontal rule.
         | 
| 985 1014 | 
             
                #
         | 
| 986 1015 | 
             
                # Examples
         | 
| 987 1016 | 
             
                #
         | 
| 988 | 
            -
                #    | 
| 989 | 
            -
                #    | 
| 1017 | 
            +
                #   --- or - - -
         | 
| 1018 | 
            +
                #   *** or * * *
         | 
| 1019 | 
            +
                #   ___ or _ _ _
         | 
| 990 1020 | 
             
                #
         | 
| 991 | 
            -
                 | 
| 1021 | 
            +
                MarkdownThematicBreakRx = /^ {0,3}([-*_])( *)\1\2\1$/
         | 
| 992 1022 |  | 
| 993 1023 | 
             
                # Matches an AsciiDoc or Markdown horizontal rule or AsciiDoc page break.
         | 
| 994 1024 | 
             
                #
         | 
| 995 1025 | 
             
                # Examples
         | 
| 996 1026 | 
             
                #
         | 
| 997 | 
            -
                #   '''  | 
| 998 | 
            -
                #   --- or - - - (horizontal rule)
         | 
| 999 | 
            -
                #   *** or * * * (horizontal rule)
         | 
| 1027 | 
            +
                #   ''' (horizontal rule)
         | 
| 1000 1028 | 
             
                #   <<< (page break)
         | 
| 1029 | 
            +
                #   --- or - - - (horizontal rule, Markdown)
         | 
| 1030 | 
            +
                #   *** or * * * (horizontal rule, Markdown)
         | 
| 1031 | 
            +
                #   ___ or _ _ _ (horizontal rule, Markdown)
         | 
| 1001 1032 | 
             
                #
         | 
| 1002 | 
            -
                 | 
| 1033 | 
            +
                HybridLayoutBreakRx = /^(?:'{3,}|<{3,}|([-*_])( *)\1\2\1)$/
         | 
| 1003 1034 |  | 
| 1004 1035 | 
             
                ## General
         | 
| 1005 1036 |  | 
| 1006 | 
            -
                # Matches  | 
| 1007 | 
            -
                #
         | 
| 1008 | 
            -
                # NOTE allows for empty space in line as it could be left by the template engine
         | 
| 1009 | 
            -
                BlankLineRx = /^#{CG_BLANK}*\n/
         | 
| 1010 | 
            -
             | 
| 1011 | 
            -
                # Matches a comma or semi-colon delimiter.
         | 
| 1037 | 
            +
                # Matches consecutive blank lines.
         | 
| 1012 1038 | 
             
                #
         | 
| 1013 1039 | 
             
                # Examples
         | 
| 1014 1040 | 
             
                #
         | 
| 1015 | 
            -
                #   one | 
| 1016 | 
            -
                #   three;four
         | 
| 1017 | 
            -
                #
         | 
| 1018 | 
            -
                DataDelimiterRx = /,|;/
         | 
| 1019 | 
            -
             | 
| 1020 | 
            -
                # Matches a single-line of text enclosed in double quotes, capturing the quote char and text.
         | 
| 1021 | 
            -
                #
         | 
| 1022 | 
            -
                # Examples
         | 
| 1041 | 
            +
                #   one
         | 
| 1023 1042 | 
             
                #
         | 
| 1024 | 
            -
                #    | 
| 1043 | 
            +
                #   two
         | 
| 1025 1044 | 
             
                #
         | 
| 1026 | 
            -
                 | 
| 1045 | 
            +
                BlankLineRx = /\n{2,}/
         | 
| 1027 1046 |  | 
| 1028 | 
            -
                # Matches  | 
| 1047 | 
            +
                # Matches a comma or semi-colon delimiter.
         | 
| 1029 1048 | 
             
                #
         | 
| 1030 1049 | 
             
                # Examples
         | 
| 1031 1050 | 
             
                #
         | 
| 1032 | 
            -
                #    | 
| 1033 | 
            -
                #    | 
| 1034 | 
            -
                #   still want to be matched."
         | 
| 1051 | 
            +
                #   one,two
         | 
| 1052 | 
            +
                #   three;four
         | 
| 1035 1053 | 
             
                #
         | 
| 1036 | 
            -
                 | 
| 1054 | 
            +
                DataDelimiterRx = /[,;]/
         | 
| 1037 1055 |  | 
| 1038 1056 | 
             
                # Matches one or more consecutive digits at the end of a line.
         | 
| 1039 1057 | 
             
                #
         | 
| @@ -1044,21 +1062,28 @@ module Asciidoctor | |
| 1044 1062 | 
             
                #
         | 
| 1045 1063 | 
             
                TrailingDigitsRx = /\d+$/
         | 
| 1046 1064 |  | 
| 1047 | 
            -
                # Matches  | 
| 1065 | 
            +
                # Matches whitespace (space, tab, newline) escaped by a backslash.
         | 
| 1048 1066 | 
             
                #
         | 
| 1049 1067 | 
             
                # Examples
         | 
| 1050 1068 | 
             
                #
         | 
| 1051 | 
            -
                #    | 
| 1069 | 
            +
                #   three\ blind\ mice
         | 
| 1070 | 
            +
                #
         | 
| 1071 | 
            +
                EscapedSpaceRx = /\\([ \t\n])/
         | 
| 1072 | 
            +
             | 
| 1073 | 
            +
                # Detects if text is a possible candidate for the replacements substitution.
         | 
| 1052 1074 | 
             
                #
         | 
| 1053 | 
            -
                 | 
| 1075 | 
            +
                ReplaceableTextRx = /[&']|--|\.\.\.|\([CRT]M?\)/
         | 
| 1054 1076 |  | 
| 1055 | 
            -
                # Matches a  | 
| 1077 | 
            +
                # Matches a whitespace delimiter, a sequence of spaces, tabs, and/or newlines.
         | 
| 1078 | 
            +
            	# Matches the parsing rules of %w strings in Ruby.
         | 
| 1056 1079 | 
             
                #
         | 
| 1057 1080 | 
             
                # Examples
         | 
| 1058 1081 | 
             
                #
         | 
| 1059 | 
            -
                #   one two | 
| 1082 | 
            +
                #   one two	 three   four
         | 
| 1083 | 
            +
                #   five	six
         | 
| 1060 1084 | 
             
                #
         | 
| 1061 | 
            -
                 | 
| 1085 | 
            +
                # TODO change to /(?<!\\)[ \t\n]+/ after dropping support for Ruby 1.8.7
         | 
| 1086 | 
            +
                SpaceDelimiterRx = /([^\\])[ \t\n]+/
         | 
| 1062 1087 |  | 
| 1063 1088 | 
             
                # Matches a + or - modifier in a subs list
         | 
| 1064 1089 | 
             
                #
         | 
| @@ -1066,10 +1091,8 @@ module Asciidoctor | |
| 1066 1091 |  | 
| 1067 1092 | 
             
                # Matches any character with multibyte support explicitly enabled (length of multibyte char = 1)
         | 
| 1068 1093 | 
             
                #
         | 
| 1069 | 
            -
                 | 
| 1070 | 
            -
             | 
| 1071 | 
            -
                UnicodeCharScanRx = unless RUBY_ENGINE == 'opal'
         | 
| 1072 | 
            -
                  FORCE_UNICODE_LINE_LENGTH ? /./u : nil
         | 
| 1094 | 
            +
                unless RUBY_ENGINE == 'opal'
         | 
| 1095 | 
            +
                  UnicodeCharScanRx = /./u if FORCE_UNICODE_LINE_LENGTH
         | 
| 1073 1096 | 
             
                end
         | 
| 1074 1097 |  | 
| 1075 1098 | 
             
                # Detects strings that resemble URIs.
         | 
| @@ -1082,7 +1105,7 @@ module Asciidoctor | |
| 1082 1105 | 
             
                #
         | 
| 1083 1106 | 
             
                #   not c:/sample.adoc or c:\sample.adoc
         | 
| 1084 1107 | 
             
                #
         | 
| 1085 | 
            -
                UriSniffRx = %r | 
| 1108 | 
            +
                UriSniffRx = %r(^#{CG_ALPHA}[#{CC_ALNUM}.+-]+:/{0,2})
         | 
| 1086 1109 |  | 
| 1087 1110 | 
             
                # Detects the end of an implicit URI in the text
         | 
| 1088 1111 | 
             
                #
         | 
| @@ -1092,36 +1115,10 @@ module Asciidoctor | |
| 1092 1115 | 
             
                #   >http://google.com<
         | 
| 1093 1116 | 
             
                #   (See http://google.com):
         | 
| 1094 1117 | 
             
                #
         | 
| 1095 | 
            -
                 | 
| 1118 | 
            +
                UriTerminatorRx = /[);:]$/
         | 
| 1096 1119 |  | 
| 1097 1120 | 
             
                # Detects XML tags
         | 
| 1098 1121 | 
             
                XmlSanitizeRx = /<[^>]+>/
         | 
| 1099 | 
            -
             | 
| 1100 | 
            -
                # Unused
         | 
| 1101 | 
            -
             | 
| 1102 | 
            -
                # Detects any fenced block delimiter, including:
         | 
| 1103 | 
            -
                #   listing, literal, example, sidebar, quote, passthrough, table and fenced code
         | 
| 1104 | 
            -
                # Does not match open blocks or air quotes
         | 
| 1105 | 
            -
                # TIP position the most common blocks towards the front of the pattern
         | 
| 1106 | 
            -
                #BlockDelimiterRx = %r{^(?:(?:-|\.|=|\*|_|\+|/){4,}|[\|,;!]={3,}|(?:`|~){3,}.*)$}
         | 
| 1107 | 
            -
             | 
| 1108 | 
            -
                # Matches an escaped single quote within a word
         | 
| 1109 | 
            -
                #
         | 
| 1110 | 
            -
                # Examples
         | 
| 1111 | 
            -
                #
         | 
| 1112 | 
            -
                #   Here\'s Johnny!
         | 
| 1113 | 
            -
                #
         | 
| 1114 | 
            -
                #EscapedSingleQuoteRx = /(#{CG_WORD})\\'(#{CG_WORD})/
         | 
| 1115 | 
            -
                # an alternative if our backend generates single-quoted html/xml attributes
         | 
| 1116 | 
            -
                #EscapedSingleQuoteRx = /(#{CG_WORD}|=)\\'(#{CG_WORD})/
         | 
| 1117 | 
            -
             | 
| 1118 | 
            -
                # Matches whitespace at the beginning of the line
         | 
| 1119 | 
            -
                #LeadingSpacesRx = /^(#{CG_BLANK}*)/
         | 
| 1120 | 
            -
             | 
| 1121 | 
            -
                # Matches parent directory references at the beginning of a path
         | 
| 1122 | 
            -
                #LeadingParentDirsRx = /^(?:\.\.\/)*/
         | 
| 1123 | 
            -
             | 
| 1124 | 
            -
                #StripLineWise = /\A(?:\s*\n)?(#{CC_ALL}*?)\s*\z/m
         | 
| 1125 1122 | 
             
              #end
         | 
| 1126 1123 |  | 
| 1127 1124 | 
             
              INTRINSIC_ATTRIBUTES = {
         | 
| @@ -1162,57 +1159,57 @@ module Asciidoctor | |
| 1162 1159 | 
             
              # the order in which they are replaced is important
         | 
| 1163 1160 | 
             
              quote_subs = [
         | 
| 1164 1161 | 
             
                # **strong**
         | 
| 1165 | 
            -
                [:strong, :unconstrained, /\\?(?:\[([^\]] | 
| 1162 | 
            +
                [:strong, :unconstrained, /\\?(?:\[([^\]]+)\])?\*\*(#{CC_ALL}+?)\*\*/m],
         | 
| 1166 1163 |  | 
| 1167 1164 | 
             
                # *strong*
         | 
| 1168 | 
            -
                [:strong, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]] | 
| 1165 | 
            +
                [:strong, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?\*(\S|\S#{CC_ALL}*?\S)\*(?!#{CG_WORD})/m],
         | 
| 1169 1166 |  | 
| 1170 1167 | 
             
                # "`double-quoted`"
         | 
| 1171 | 
            -
                [:double, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]] | 
| 1168 | 
            +
                [:double, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?"`(\S|\S#{CC_ALL}*?\S)`"(?!#{CG_WORD})/m],
         | 
| 1172 1169 |  | 
| 1173 1170 | 
             
                # '`single-quoted`'
         | 
| 1174 | 
            -
                [:single, :constrained, /(^|[^#{CC_WORD};:`}])(?:\[([^\]] | 
| 1171 | 
            +
                [:single, :constrained, /(^|[^#{CC_WORD};:`}])(?:\[([^\]]+)\])?'`(\S|\S#{CC_ALL}*?\S)`'(?!#{CG_WORD})/m],
         | 
| 1175 1172 |  | 
| 1176 1173 | 
             
                # ``monospaced``
         | 
| 1177 | 
            -
                [:monospaced, :unconstrained, /\\?(?:\[([^\]] | 
| 1174 | 
            +
                [:monospaced, :unconstrained, /\\?(?:\[([^\]]+)\])?``(#{CC_ALL}+?)``/m],
         | 
| 1178 1175 |  | 
| 1179 1176 | 
             
                # `monospaced`
         | 
| 1180 | 
            -
                [:monospaced, :constrained, /(^|[^#{CC_WORD};:"'`}])(?:\[([^\]] | 
| 1177 | 
            +
                [:monospaced, :constrained, /(^|[^#{CC_WORD};:"'`}])(?:\[([^\]]+)\])?`(\S|\S#{CC_ALL}*?\S)`(?![#{CC_WORD}"'`])/m],
         | 
| 1181 1178 |  | 
| 1182 1179 | 
             
                # __emphasis__
         | 
| 1183 | 
            -
                [:emphasis, :unconstrained, /\\?(?:\[([^\]] | 
| 1180 | 
            +
                [:emphasis, :unconstrained, /\\?(?:\[([^\]]+)\])?__(#{CC_ALL}+?)__/m],
         | 
| 1184 1181 |  | 
| 1185 1182 | 
             
                # _emphasis_
         | 
| 1186 | 
            -
                [:emphasis, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]] | 
| 1183 | 
            +
                [:emphasis, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?_(\S|\S#{CC_ALL}*?\S)_(?!#{CG_WORD})/m],
         | 
| 1187 1184 |  | 
| 1188 1185 | 
             
                # ##mark## (referred to in AsciiDoc Python as unquoted)
         | 
| 1189 | 
            -
                [:mark, :unconstrained, /\\?(?:\[([^\]] | 
| 1186 | 
            +
                [:mark, :unconstrained, /\\?(?:\[([^\]]+)\])?##(#{CC_ALL}+?)##/m],
         | 
| 1190 1187 |  | 
| 1191 1188 | 
             
                # #mark# (referred to in AsciiDoc Python as unquoted)
         | 
| 1192 | 
            -
                [:mark, :constrained, /(^|[^#{CC_WORD}&;:}])(?:\[([^\]] | 
| 1189 | 
            +
                [:mark, :constrained, /(^|[^#{CC_WORD}&;:}])(?:\[([^\]]+)\])?#(\S|\S#{CC_ALL}*?\S)#(?!#{CG_WORD})/m],
         | 
| 1193 1190 |  | 
| 1194 1191 | 
             
                # ^superscript^
         | 
| 1195 | 
            -
                [:superscript, :unconstrained, /\\?(?:\[([^\]] | 
| 1192 | 
            +
                [:superscript, :unconstrained, /\\?(?:\[([^\]]+)\])?\^(\S+?)\^/],
         | 
| 1196 1193 |  | 
| 1197 1194 | 
             
                # ~subscript~
         | 
| 1198 | 
            -
                [:subscript, :unconstrained, /\\?(?:\[([^\]] | 
| 1195 | 
            +
                [:subscript, :unconstrained, /\\?(?:\[([^\]]+)\])?~(\S+?)~/]
         | 
| 1199 1196 | 
             
              ]
         | 
| 1200 1197 |  | 
| 1201 1198 | 
             
              compat_quote_subs = quote_subs.dup
         | 
| 1202 1199 | 
             
              # ``quoted''
         | 
| 1203 | 
            -
              compat_quote_subs[2] = [:double, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]] | 
| 1200 | 
            +
              compat_quote_subs[2] = [:double, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?``(\S|\S#{CC_ALL}*?\S)''(?!#{CG_WORD})/m]
         | 
| 1204 1201 | 
             
              # `quoted'
         | 
| 1205 | 
            -
              compat_quote_subs[3] = [:single, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]] | 
| 1202 | 
            +
              compat_quote_subs[3] = [:single, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?`(\S|\S#{CC_ALL}*?\S)'(?!#{CG_WORD})/m]
         | 
| 1206 1203 | 
             
              # ++monospaced++
         | 
| 1207 | 
            -
              compat_quote_subs[4] = [:monospaced, :unconstrained, /\\?(?:\[([^\]] | 
| 1204 | 
            +
              compat_quote_subs[4] = [:monospaced, :unconstrained, /\\?(?:\[([^\]]+)\])?\+\+(#{CC_ALL}+?)\+\+/m]
         | 
| 1208 1205 | 
             
              # +monospaced+
         | 
| 1209 | 
            -
              compat_quote_subs[5] = [:monospaced, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]] | 
| 1206 | 
            +
              compat_quote_subs[5] = [:monospaced, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?\+(\S|\S#{CC_ALL}*?\S)\+(?!#{CG_WORD})/m]
         | 
| 1210 1207 | 
             
              # #unquoted#
         | 
| 1211 1208 | 
             
              #compat_quote_subs[8] = [:unquoted, *compat_quote_subs[8][1..-1]]
         | 
| 1212 1209 | 
             
              # ##unquoted##
         | 
| 1213 1210 | 
             
              #compat_quote_subs[9] = [:unquoted, *compat_quote_subs[9][1..-1]]
         | 
| 1214 1211 | 
             
              # 'emphasis'
         | 
| 1215 | 
            -
              compat_quote_subs.insert 3, [:emphasis, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]] | 
| 1212 | 
            +
              compat_quote_subs.insert 3, [:emphasis, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?'(\S|\S#{CC_ALL}*?\S)'(?!#{CG_WORD})/m]
         | 
| 1216 1213 |  | 
| 1217 1214 | 
             
              QUOTE_SUBS = {
         | 
| 1218 1215 | 
             
                false => quote_subs,
         | 
| @@ -1251,7 +1248,7 @@ module Asciidoctor | |
| 1251 1248 | 
             
                # left double arrow <=
         | 
| 1252 1249 | 
             
                [/\\?<=/, '⇐', :none],
         | 
| 1253 1250 | 
             
                # restore entities
         | 
| 1254 | 
            -
                [/\\?(&)amp;((?:[a-zA-Z]{2 | 
| 1251 | 
            +
                [/\\?(&)amp;((?:[a-zA-Z][a-zA-Z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-fA-F][\da-fA-F][\da-fA-F]{0,3});)/, '', :bounding]
         | 
| 1255 1252 | 
             
              ]
         | 
| 1256 1253 |  | 
| 1257 1254 | 
             
              class << self
         | 
| @@ -1274,36 +1271,28 @@ module Asciidoctor | |
| 1274 1271 | 
             
                  timings.start :read
         | 
| 1275 1272 | 
             
                end
         | 
| 1276 1273 |  | 
| 1277 | 
            -
                 | 
| 1278 | 
            -
                  {}
         | 
| 1274 | 
            +
                if !(attrs = options[:attributes])
         | 
| 1275 | 
            +
                  attrs = {}
         | 
| 1279 1276 | 
             
                elsif ::Hash === attrs || (::RUBY_ENGINE_JRUBY && ::Java::JavaUtil::Map === attrs)
         | 
| 1280 | 
            -
                  attrs.dup
         | 
| 1277 | 
            +
                  attrs = attrs.dup
         | 
| 1281 1278 | 
             
                elsif ::Array === attrs
         | 
| 1282 | 
            -
                  attrs | 
| 1279 | 
            +
                  attrs, attrs_arr = {}, attrs
         | 
| 1280 | 
            +
                  attrs_arr.each do |entry|
         | 
| 1283 1281 | 
             
                    k, v = entry.split '=', 2
         | 
| 1284 | 
            -
                     | 
| 1285 | 
            -
                    accum
         | 
| 1282 | 
            +
                    attrs[k] = v || ''
         | 
| 1286 1283 | 
             
                  end
         | 
| 1287 1284 | 
             
                elsif ::String === attrs
         | 
| 1288 | 
            -
                  # convert non-escaped spaces  | 
| 1289 | 
            -
                   | 
| 1290 | 
            -
                   | 
| 1291 | 
            -
                  attrs = attrs.gsub(SpaceDelimiterRx, %(#{capture_1}#{NULL})).gsub(EscapedSpaceRx, capture_1)
         | 
| 1292 | 
            -
                  attrs.split(NULL).inject({}) do |accum, entry|
         | 
| 1285 | 
            +
                  # condense and convert non-escaped spaces to null, unescape escaped spaces, then split on null
         | 
| 1286 | 
            +
                  attrs, attrs_arr = {}, attrs.gsub(SpaceDelimiterRx, %(\\1#{NULL})).gsub(EscapedSpaceRx, '\1').split(NULL)
         | 
| 1287 | 
            +
                  attrs_arr.each do |entry|
         | 
| 1293 1288 | 
             
                    k, v = entry.split '=', 2
         | 
| 1294 | 
            -
                     | 
| 1295 | 
            -
                    accum
         | 
| 1289 | 
            +
                    attrs[k] = v || ''
         | 
| 1296 1290 | 
             
                  end
         | 
| 1297 1291 | 
             
                elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[])
         | 
| 1298 1292 | 
             
                  # convert it to a Hash as we know it
         | 
| 1299 | 
            -
                   | 
| 1300 | 
            -
                  attrs = {}
         | 
| 1301 | 
            -
                  original_attrs.keys.each do |key|
         | 
| 1302 | 
            -
                    attrs[key] = original_attrs[key]
         | 
| 1303 | 
            -
                  end
         | 
| 1304 | 
            -
                  attrs
         | 
| 1293 | 
            +
                  attrs = ::Hash[attrs.keys.map {|k| [k, attrs[k]] }]
         | 
| 1305 1294 | 
             
                else
         | 
| 1306 | 
            -
                  raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors})
         | 
| 1295 | 
            +
                  raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors * ' < '})
         | 
| 1307 1296 | 
             
                end
         | 
| 1308 1297 |  | 
| 1309 1298 | 
             
                lines = nil
         | 
| @@ -1311,15 +1300,21 @@ module Asciidoctor | |
| 1311 1300 | 
             
                  # TODO cli checks if input path can be read and is file, but might want to add check to API
         | 
| 1312 1301 | 
             
                  input_path = ::File.expand_path input.path
         | 
| 1313 1302 | 
             
                  # See https://reproducible-builds.org/specs/source-date-epoch/
         | 
| 1314 | 
            -
                   | 
| 1303 | 
            +
                  # NOTE Opal can't call key? on ENV
         | 
| 1304 | 
            +
                  input_mtime = ::ENV['SOURCE_DATE_EPOCH'] ? ::Time.at(Integer ::ENV['SOURCE_DATE_EPOCH']).utc : input.mtime
         | 
| 1315 1305 | 
             
                  lines = input.readlines
         | 
| 1316 1306 | 
             
                  # hold off on setting infile and indir until we get a better sense of their purpose
         | 
| 1317 | 
            -
                   | 
| 1318 | 
            -
                   | 
| 1319 | 
            -
                   | 
| 1320 | 
            -
                  docdate =  | 
| 1321 | 
            -
             | 
| 1322 | 
            -
                   | 
| 1307 | 
            +
                  attrs['docfile'] = input_path
         | 
| 1308 | 
            +
                  attrs['docdir'] = ::File.dirname input_path
         | 
| 1309 | 
            +
                  attrs['docname'] = Helpers.basename input_path, (attrs['docfilesuffix'] = ::File.extname input_path)
         | 
| 1310 | 
            +
                  if (docdate = attrs['docdate'])
         | 
| 1311 | 
            +
                    attrs['docyear'] ||= ((docdate.index '-') == 4 ? (docdate.slice 0, 4) : nil)
         | 
| 1312 | 
            +
                  else
         | 
| 1313 | 
            +
                    docdate = attrs['docdate'] = (input_mtime.strftime '%Y-%m-%d')
         | 
| 1314 | 
            +
                    attrs['docyear'] ||= input_mtime.year.to_s
         | 
| 1315 | 
            +
                  end
         | 
| 1316 | 
            +
                  doctime = (attrs['doctime'] ||= input_mtime.strftime('%H:%M:%S %Z'))
         | 
| 1317 | 
            +
                  attrs['docdatetime'] = %(#{docdate} #{doctime})
         | 
| 1323 1318 | 
             
                elsif input.respond_to? :readlines
         | 
| 1324 1319 | 
             
                  # NOTE tty, pipes & sockets can't be rewound, but can't be sniffed easily either
         | 
| 1325 1320 | 
             
                  # just fail the rewind operation silently to handle all cases
         | 
| @@ -1329,7 +1324,7 @@ module Asciidoctor | |
| 1329 1324 | 
             
                  end
         | 
| 1330 1325 | 
             
                  lines = input.readlines
         | 
| 1331 1326 | 
             
                elsif ::String === input
         | 
| 1332 | 
            -
                  lines = input.lines. | 
| 1327 | 
            +
                  lines = ::RUBY_MIN_VERSION_2 ? input.lines : input.each_line.to_a
         | 
| 1333 1328 | 
             
                elsif ::Array === input
         | 
| 1334 1329 | 
             
                  lines = input.dup
         | 
| 1335 1330 | 
             
                else
         | 
| @@ -1341,17 +1336,14 @@ module Asciidoctor | |
| 1341 1336 | 
             
                  timings.start :parse
         | 
| 1342 1337 | 
             
                end
         | 
| 1343 1338 |  | 
| 1344 | 
            -
                 | 
| 1345 | 
            -
             | 
| 1346 | 
            -
                else
         | 
| 1347 | 
            -
                  doc = (Document.new lines, options).parse
         | 
| 1348 | 
            -
                end
         | 
| 1339 | 
            +
                options[:attributes] = attrs
         | 
| 1340 | 
            +
                doc = options[:parse] == false ? (Document.new lines, options) : (Document.new lines, options).parse
         | 
| 1349 1341 |  | 
| 1350 1342 | 
             
                timings.record :parse if timings
         | 
| 1351 1343 | 
             
                doc
         | 
| 1352 1344 | 
             
              rescue => ex
         | 
| 1353 1345 | 
             
                begin
         | 
| 1354 | 
            -
                  context = %(asciidoctor: FAILED: #{ | 
| 1346 | 
            +
                  context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document)
         | 
| 1355 1347 | 
             
                  if ex.respond_to? :exception
         | 
| 1356 1348 | 
             
                    # The original message must be explicitely preserved when wrapping a Ruby exception
         | 
| 1357 1349 | 
             
                    wrapped_ex = ex.exception %(#{context} - #{ex.message})
         | 
| @@ -1381,7 +1373,7 @@ module Asciidoctor | |
| 1381 1373 | 
             
              #
         | 
| 1382 1374 | 
             
              # Returns the Asciidoctor::Document
         | 
| 1383 1375 | 
             
              def load_file filename, options = {}
         | 
| 1384 | 
            -
                 | 
| 1376 | 
            +
                ::File.open(filename) {|file| self.load file, options }
         | 
| 1385 1377 | 
             
              end
         | 
| 1386 1378 |  | 
| 1387 1379 | 
             
              # Public: Parse the AsciiDoc source input into an Asciidoctor::Document and
         | 
| @@ -1436,27 +1428,26 @@ module Asciidoctor | |
| 1436 1428 | 
             
                  return self.load input, options
         | 
| 1437 1429 | 
             
                else
         | 
| 1438 1430 | 
             
                  write_to_same_dir = false
         | 
| 1439 | 
            -
                  stream_output = to_file.respond_to? :write
         | 
| 1440 | 
            -
                  write_to_target = stream_output ? false : to_file
         | 
| 1431 | 
            +
                  write_to_target = (stream_output = to_file.respond_to? :write) ? false : to_file
         | 
| 1441 1432 | 
             
                end
         | 
| 1442 1433 |  | 
| 1443 1434 | 
             
                unless options.key? :header_footer
         | 
| 1444 1435 | 
             
                  options[:header_footer] = true if write_to_same_dir || write_to_target
         | 
| 1445 1436 | 
             
                end
         | 
| 1446 1437 |  | 
| 1447 | 
            -
                # NOTE  | 
| 1438 | 
            +
                # NOTE outfile may be controlled by document attributes, so resolve outfile after loading
         | 
| 1448 1439 | 
             
                if write_to_same_dir
         | 
| 1449 1440 | 
             
                  input_path = ::File.expand_path input.path
         | 
| 1450 1441 | 
             
                  options[:to_dir] = (outdir = ::File.dirname input_path)
         | 
| 1451 1442 | 
             
                elsif write_to_target
         | 
| 1452 1443 | 
             
                  if to_dir
         | 
| 1453 1444 | 
             
                    if to_file
         | 
| 1454 | 
            -
                      options[:to_dir] = ::File. | 
| 1445 | 
            +
                      options[:to_dir] = ::File.expand_path ::File.join to_dir, to_file, '..'
         | 
| 1455 1446 | 
             
                    else
         | 
| 1456 1447 | 
             
                      options[:to_dir] = ::File.expand_path to_dir
         | 
| 1457 1448 | 
             
                    end
         | 
| 1458 1449 | 
             
                  elsif to_file
         | 
| 1459 | 
            -
                    options[:to_dir] = ::File. | 
| 1450 | 
            +
                    options[:to_dir] = ::File.expand_path to_file, '..'
         | 
| 1460 1451 | 
             
                  end
         | 
| 1461 1452 | 
             
                else
         | 
| 1462 1453 | 
             
                  options[:to_dir] = nil
         | 
| @@ -1464,13 +1455,13 @@ module Asciidoctor | |
| 1464 1455 |  | 
| 1465 1456 | 
             
                doc = self.load input, options
         | 
| 1466 1457 |  | 
| 1467 | 
            -
                if write_to_same_dir
         | 
| 1458 | 
            +
                if write_to_same_dir # write to file in same directory
         | 
| 1468 1459 | 
             
                  outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix})
         | 
| 1469 1460 | 
             
                  if outfile == input_path
         | 
| 1470 1461 | 
             
                    raise ::IOError, %(input file and output file cannot be the same: #{outfile})
         | 
| 1471 1462 | 
             
                  end
         | 
| 1472 | 
            -
                elsif write_to_target
         | 
| 1473 | 
            -
                  working_dir = options. | 
| 1463 | 
            +
                elsif write_to_target # write to explicit file or directory
         | 
| 1464 | 
            +
                  working_dir = (options.key? :base_dir) ? (::File.expand_path options[:base_dir]) : (::File.expand_path ::Dir.pwd)
         | 
| 1474 1465 | 
             
                  # QUESTION should the jail be the working_dir or doc.base_dir???
         | 
| 1475 1466 | 
             
                  jail = doc.safe >= SafeMode::SAFE ? working_dir : nil
         | 
| 1476 1467 | 
             
                  if to_dir
         | 
| @@ -1488,6 +1479,10 @@ module Asciidoctor | |
| 1488 1479 | 
             
                    outdir = ::File.dirname outfile
         | 
| 1489 1480 | 
             
                  end
         | 
| 1490 1481 |  | 
| 1482 | 
            +
                  if ::File === input && outfile == (::File.expand_path input.path)
         | 
| 1483 | 
            +
                    raise ::IOError, %(input file and output file cannot be the same: #{outfile})
         | 
| 1484 | 
            +
                  end
         | 
| 1485 | 
            +
             | 
| 1491 1486 | 
             
                  unless ::File.directory? outdir
         | 
| 1492 1487 | 
             
                    if mkdirs
         | 
| 1493 1488 | 
             
                      Helpers.mkdir_p outdir
         | 
| @@ -1496,7 +1491,7 @@ module Asciidoctor | |
| 1496 1491 | 
             
                      raise ::IOError, %(target directory does not exist: #{to_dir})
         | 
| 1497 1492 | 
             
                    end
         | 
| 1498 1493 | 
             
                  end
         | 
| 1499 | 
            -
                else
         | 
| 1494 | 
            +
                else # write to stream
         | 
| 1500 1495 | 
             
                  outfile = to_file
         | 
| 1501 1496 | 
             
                  outdir = nil
         | 
| 1502 1497 | 
             
                end
         | 
| @@ -1542,10 +1537,8 @@ module Asciidoctor | |
| 1542 1537 | 
             
                          stylesheet_src = doc.normalize_system_path stylesheet_src
         | 
| 1543 1538 | 
             
                        end
         | 
| 1544 1539 | 
             
                        stylesheet_dst = doc.normalize_system_path stylesheet, stylesoutdir, (doc.safe >= SafeMode::SAFE ? outdir : nil)
         | 
| 1545 | 
            -
                         | 
| 1546 | 
            -
                          :: | 
| 1547 | 
            -
                            f.write stylesheet_content
         | 
| 1548 | 
            -
                          }
         | 
| 1540 | 
            +
                        if stylesheet_src != stylesheet_dst && (stylesheet_content = doc.read_asset stylesheet_src, :warn_on_failure => true, :label => 'stylesheet')
         | 
| 1541 | 
            +
                          ::IO.write stylesheet_dst, stylesheet_content
         | 
| 1549 1542 | 
             
                        end
         | 
| 1550 1543 | 
             
                      end
         | 
| 1551 1544 |  | 
| @@ -1563,7 +1556,7 @@ module Asciidoctor | |
| 1563 1556 | 
             
              end
         | 
| 1564 1557 |  | 
| 1565 1558 | 
             
              # Alias render to convert to maintain backwards compatibility
         | 
| 1566 | 
            -
              alias  | 
| 1559 | 
            +
              alias render convert
         | 
| 1567 1560 |  | 
| 1568 1561 | 
             
              # Public: Parse the contents of the AsciiDoc source file into an
         | 
| 1569 1562 | 
             
              # Asciidoctor::Document and convert it to the specified backend format.
         | 
| @@ -1576,20 +1569,39 @@ module Asciidoctor | |
| 1576 1569 | 
             
              # Returns the Document object if the converted String is written to a
         | 
| 1577 1570 | 
             
              # file, otherwise the converted String
         | 
| 1578 1571 | 
             
              def convert_file filename, options = {}
         | 
| 1579 | 
            -
                 | 
| 1572 | 
            +
                ::File.open(filename) {|file| self.convert file, options }
         | 
| 1580 1573 | 
             
              end
         | 
| 1581 1574 |  | 
| 1582 1575 | 
             
              # Alias render_file to convert_file to maintain backwards compatibility
         | 
| 1583 | 
            -
              alias  | 
| 1576 | 
            +
              alias render_file convert_file
         | 
| 1577 | 
            +
             | 
| 1578 | 
            +
              # Internal: Automatically load the Asciidoctor::Extensions module.
         | 
| 1579 | 
            +
              #
         | 
| 1580 | 
            +
              # Requires the Asciidoctor::Extensions module if the name is :Extensions.
         | 
| 1581 | 
            +
              # Otherwise, delegates to the super method.
         | 
| 1582 | 
            +
              #
         | 
| 1583 | 
            +
              # This method provides the same functionality as using autoload on
         | 
| 1584 | 
            +
              # Asciidoctor::Extensions, except that the constant isn't recognized as
         | 
| 1585 | 
            +
              # defined prior to it being loaded.
         | 
| 1586 | 
            +
              #
         | 
| 1587 | 
            +
              # Returns the resolved constant, if resolved, otherwise nothing.
         | 
| 1588 | 
            +
              def const_missing name
         | 
| 1589 | 
            +
                if name == :Extensions
         | 
| 1590 | 
            +
                  require 'asciidoctor/extensions'
         | 
| 1591 | 
            +
                  Extensions
         | 
| 1592 | 
            +
                else
         | 
| 1593 | 
            +
                  super
         | 
| 1594 | 
            +
                end
         | 
| 1595 | 
            +
              end unless RUBY_ENGINE == 'opal'
         | 
| 1584 1596 |  | 
| 1585 1597 | 
             
              end
         | 
| 1586 1598 |  | 
| 1587 1599 | 
             
              if RUBY_ENGINE == 'opal'
         | 
| 1588 | 
            -
                require 'asciidoctor/version'
         | 
| 1589 1600 | 
             
                require 'asciidoctor/timings'
         | 
| 1601 | 
            +
                require 'asciidoctor/version'
         | 
| 1590 1602 | 
             
              else
         | 
| 1591 | 
            -
                autoload :VERSION, 'asciidoctor/version'
         | 
| 1592 1603 | 
             
                autoload :Timings, 'asciidoctor/timings'
         | 
| 1604 | 
            +
                autoload :VERSION, 'asciidoctor/version'
         | 
| 1593 1605 | 
             
              end
         | 
| 1594 1606 | 
             
            end
         | 
| 1595 1607 |  | 
| @@ -1609,7 +1621,6 @@ require 'asciidoctor/attribute_list' | |
| 1609 1621 | 
             
            require 'asciidoctor/block'
         | 
| 1610 1622 | 
             
            require 'asciidoctor/callouts'
         | 
| 1611 1623 | 
             
            require 'asciidoctor/converter'
         | 
| 1612 | 
            -
            require 'asciidoctor/converter/html5' if RUBY_ENGINE_OPAL
         | 
| 1613 1624 | 
             
            require 'asciidoctor/document'
         | 
| 1614 1625 | 
             
            require 'asciidoctor/inline'
         | 
| 1615 1626 | 
             
            require 'asciidoctor/list'
         | 
| @@ -1619,3 +1630,6 @@ require 'asciidoctor/reader' | |
| 1619 1630 | 
             
            require 'asciidoctor/section'
         | 
| 1620 1631 | 
             
            require 'asciidoctor/stylesheets'
         | 
| 1621 1632 | 
             
            require 'asciidoctor/table'
         | 
| 1633 | 
            +
             | 
| 1634 | 
            +
            # this require is satisfied by the Asciidoctor.js build; it supplies compile and runtime overrides for Asciidoctor.js
         | 
| 1635 | 
            +
            require 'asciidoctor/js/postscript' if RUBY_ENGINE == 'opal'
         |