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
| @@ -13,14 +13,16 @@ class AbstractBlock < AbstractNode | |
| 13 13 | 
             
              # Public: Set the Integer level of this Section or the Section level in which this Block resides
         | 
| 14 14 | 
             
              attr_accessor :level
         | 
| 15 15 |  | 
| 16 | 
            -
              # Public: Set the String block title.
         | 
| 17 | 
            -
              attr_writer :title
         | 
| 18 | 
            -
             | 
| 19 16 | 
             
              # Public: Get/Set the String style (block type qualifier) for this block.
         | 
| 20 17 | 
             
              attr_accessor :style
         | 
| 21 18 |  | 
| 22 | 
            -
              # Public:  | 
| 23 | 
            -
               | 
| 19 | 
            +
              # Public: Set the caption for this block
         | 
| 20 | 
            +
              attr_writer :caption
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              # Public: Get/Set the number of this block (if section, relative to parent, otherwise absolute)
         | 
| 23 | 
            +
              # Only assigned to section if automatic section numbering is enabled
         | 
| 24 | 
            +
              # Only assigned to formal block (block with title) if corresponding caption attribute is present
         | 
| 25 | 
            +
              attr_accessor :number
         | 
| 24 26 |  | 
| 25 27 | 
             
              # Public: Gets/Sets the location in the AsciiDoc source where this block begins
         | 
| 26 28 | 
             
              attr_accessor :source_location
         | 
| @@ -28,21 +30,18 @@ class AbstractBlock < AbstractNode | |
| 28 30 | 
             
              def initialize parent, context, opts = {}
         | 
| 29 31 | 
             
                super
         | 
| 30 32 | 
             
                @content_model = :compound
         | 
| 31 | 
            -
                @subs = []
         | 
| 32 | 
            -
                @default_subs = nil
         | 
| 33 33 | 
             
                @blocks = []
         | 
| 34 | 
            -
                @ | 
| 35 | 
            -
                @title = nil
         | 
| 36 | 
            -
                 | 
| 37 | 
            -
             | 
| 38 | 
            -
                @level = if context == :document
         | 
| 39 | 
            -
                  0
         | 
| 34 | 
            +
                @subs = []
         | 
| 35 | 
            +
                @id = @title = @title_converted = @caption = @number = @style = @default_subs = @source_location = nil
         | 
| 36 | 
            +
                if context == :document
         | 
| 37 | 
            +
                  @level = 0
         | 
| 40 38 | 
             
                elsif parent && context != :section
         | 
| 41 | 
            -
                  parent.level
         | 
| 39 | 
            +
                  @level = parent.level
         | 
| 40 | 
            +
                else
         | 
| 41 | 
            +
                  @level = nil
         | 
| 42 42 | 
             
                end
         | 
| 43 43 | 
             
                @next_section_index = 0
         | 
| 44 44 | 
             
                @next_section_number = 1
         | 
| 45 | 
            -
                @source_location = nil
         | 
| 46 45 | 
             
              end
         | 
| 47 46 |  | 
| 48 47 | 
             
              def block?
         | 
| @@ -72,12 +71,12 @@ class AbstractBlock < AbstractNode | |
| 72 71 | 
             
              end
         | 
| 73 72 |  | 
| 74 73 | 
             
              # Alias render to convert to maintain backwards compatibility
         | 
| 75 | 
            -
              alias  | 
| 74 | 
            +
              alias render convert
         | 
| 76 75 |  | 
| 77 76 | 
             
              # Public: Get the converted result of the child blocks by converting the
         | 
| 78 77 | 
             
              # children appropriate to content model that this block supports.
         | 
| 79 78 | 
             
              def content
         | 
| 80 | 
            -
                @blocks.map {|b| b.convert } *  | 
| 79 | 
            +
                @blocks.map {|b| b.convert } * LF
         | 
| 81 80 | 
             
              end
         | 
| 82 81 |  | 
| 83 82 | 
             
              # Public: Get the source file where this block started
         | 
| @@ -101,10 +100,11 @@ class AbstractBlock < AbstractNode | |
| 101 100 | 
             
                @subs.include? name
         | 
| 102 101 | 
             
              end
         | 
| 103 102 |  | 
| 104 | 
            -
              # Public: A convenience method that  | 
| 105 | 
            -
              # | 
| 103 | 
            +
              # Public: A convenience method that checks whether the title of this block is defined.
         | 
| 104 | 
            +
              #
         | 
| 105 | 
            +
              # Returns a [Boolean] indicating whether this block has a title.
         | 
| 106 106 | 
             
              def title?
         | 
| 107 | 
            -
                 | 
| 107 | 
            +
                @title ? true : false
         | 
| 108 108 | 
             
              end
         | 
| 109 109 |  | 
| 110 110 | 
             
              # Public: Get the String title of this Block with title substitions applied
         | 
| @@ -119,16 +119,28 @@ class AbstractBlock < AbstractNode | |
| 119 119 | 
             
              #   block.title
         | 
| 120 120 | 
             
              #   => "Foo 3^ # :: Bar(1)"
         | 
| 121 121 | 
             
              #
         | 
| 122 | 
            -
              # Returns the String title  | 
| 122 | 
            +
              # Returns the converted String title for this Block, or nil if the source title is falsy
         | 
| 123 123 | 
             
              def title
         | 
| 124 | 
            -
                # prevent substitutions from being applied multiple times
         | 
| 125 | 
            -
                 | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 124 | 
            +
                # prevent substitutions from being applied to title multiple times
         | 
| 125 | 
            +
                @title_converted ? @converted_title : (@converted_title = (@title_converted = true) && @title && (apply_title_subs @title))
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              # Public: Set the String block title.
         | 
| 129 | 
            +
              #
         | 
| 130 | 
            +
              # Returns the new String title assigned to this Block
         | 
| 131 | 
            +
              def title= val
         | 
| 132 | 
            +
                @title, @title_converted = val, nil
         | 
| 133 | 
            +
              end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
              # Gets the caption for this block.
         | 
| 136 | 
            +
              #
         | 
| 137 | 
            +
              # This method routes the deprecated use of the caption method on an
         | 
| 138 | 
            +
              # admonition block to the textlabel attribute.
         | 
| 139 | 
            +
              #
         | 
| 140 | 
            +
              # Returns the [String] caption for this block (or the value of the textlabel
         | 
| 141 | 
            +
              # attribute if this is an admonition block).
         | 
| 142 | 
            +
              def caption
         | 
| 143 | 
            +
                @context == :admonition ? @attributes['textlabel'] : @caption
         | 
| 132 144 | 
             
              end
         | 
| 133 145 |  | 
| 134 146 | 
             
              # Public: Convenience method that returns the interpreted title of the Block
         | 
| @@ -139,12 +151,70 @@ class AbstractBlock < AbstractNode | |
| 139 151 | 
             
              # two values. If the Block does not have a caption, the interpreted title is
         | 
| 140 152 | 
             
              # returned.
         | 
| 141 153 | 
             
              #
         | 
| 142 | 
            -
              # Returns the String title prefixed with the caption, or just the | 
| 143 | 
            -
              # caption is set
         | 
| 154 | 
            +
              # Returns the converted String title prefixed with the caption, or just the
         | 
| 155 | 
            +
              # converted String title if no caption is set
         | 
| 144 156 | 
             
              def captioned_title
         | 
| 145 157 | 
             
                %(#{@caption}#{title})
         | 
| 146 158 | 
             
              end
         | 
| 147 159 |  | 
| 160 | 
            +
              # Public: Returns the converted alt text for this block image.
         | 
| 161 | 
            +
              #
         | 
| 162 | 
            +
              # Returns the [String] value of the alt attribute with XML special character
         | 
| 163 | 
            +
              # and replacement substitutions applied.
         | 
| 164 | 
            +
              def alt
         | 
| 165 | 
            +
                if (text = @attributes['alt'])
         | 
| 166 | 
            +
                  if text == @attributes['default-alt']
         | 
| 167 | 
            +
                    sub_specialchars text
         | 
| 168 | 
            +
                  else
         | 
| 169 | 
            +
                    text = sub_specialchars text
         | 
| 170 | 
            +
                    (ReplaceableTextRx.match? text) ? (sub_replacements text) : text
         | 
| 171 | 
            +
                  end
         | 
| 172 | 
            +
                end
         | 
| 173 | 
            +
              end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
              # Public: Generate cross reference text (xreftext) that can be used to refer
         | 
| 176 | 
            +
              # to this block.
         | 
| 177 | 
            +
              #
         | 
| 178 | 
            +
              # Use the explicit reftext for this block, if specified, retrieved from the
         | 
| 179 | 
            +
              # {#reftext} method. Otherwise, if this is a section or captioned block (a
         | 
| 180 | 
            +
              # block with both a title and caption), generate the xreftext according to
         | 
| 181 | 
            +
              # the value of the xrefstyle argument (e.g., full, short). This logic may
         | 
| 182 | 
            +
              # leverage the {Substitutors#sub_quotes} method to apply formatting to the
         | 
| 183 | 
            +
              # text. If this is not a captioned block, return the title, if present, or
         | 
| 184 | 
            +
              # nil otherwise.
         | 
| 185 | 
            +
              #
         | 
| 186 | 
            +
              # xrefstyle - An optional String that specifies the style to use to format
         | 
| 187 | 
            +
              #             the xreftext ('full', 'short', or 'basic') (default: nil).
         | 
| 188 | 
            +
              #
         | 
| 189 | 
            +
              # Returns the generated [String] xreftext used to refer to this block or
         | 
| 190 | 
            +
              # nothing if there isn't sufficient information to generate one.
         | 
| 191 | 
            +
              def xreftext xrefstyle = nil
         | 
| 192 | 
            +
                if (val = reftext) && !val.empty?
         | 
| 193 | 
            +
                  val
         | 
| 194 | 
            +
                # NOTE xrefstyle only applies to blocks with a title and a caption or number
         | 
| 195 | 
            +
                elsif xrefstyle && @title && @caption
         | 
| 196 | 
            +
                  case xrefstyle
         | 
| 197 | 
            +
                  when 'full'
         | 
| 198 | 
            +
                    quoted_title = sprintf sub_quotes(@document.compat_mode ? %q(``%s'') : '"`%s`"'), title
         | 
| 199 | 
            +
                    if @number && (prefix = @document.attributes[@context == :image ? 'figure-caption' : %(#{@context}-caption)])
         | 
| 200 | 
            +
                      %(#{prefix} #{@number}, #{quoted_title})
         | 
| 201 | 
            +
                    else
         | 
| 202 | 
            +
                      %(#{@caption.chomp '. '}, #{quoted_title})
         | 
| 203 | 
            +
                    end
         | 
| 204 | 
            +
                  when 'short'
         | 
| 205 | 
            +
                    if @number && (prefix = @document.attributes[@context == :image ? 'figure-caption' : %(#{@context}-caption)])
         | 
| 206 | 
            +
                      %(#{prefix} #{@number})
         | 
| 207 | 
            +
                    else
         | 
| 208 | 
            +
                      @caption.chomp '. '
         | 
| 209 | 
            +
                    end
         | 
| 210 | 
            +
                  else # 'basic'
         | 
| 211 | 
            +
                    title
         | 
| 212 | 
            +
                  end
         | 
| 213 | 
            +
                else
         | 
| 214 | 
            +
                  title
         | 
| 215 | 
            +
                end
         | 
| 216 | 
            +
              end
         | 
| 217 | 
            +
             | 
| 148 218 | 
             
              # Public: Determine whether this Block contains block content
         | 
| 149 219 | 
             
              #
         | 
| 150 220 | 
             
              # Returns A Boolean indicating whether this Block has block content
         | 
| @@ -176,7 +246,7 @@ class AbstractBlock < AbstractNode | |
| 176 246 | 
             
              end
         | 
| 177 247 |  | 
| 178 248 | 
             
              # NOTE append alias required for adapting to a Java API
         | 
| 179 | 
            -
              alias  | 
| 249 | 
            +
              alias append <<
         | 
| 180 250 |  | 
| 181 251 | 
             
              # Public: Get the Array of child Section objects
         | 
| 182 252 | 
             
              #
         | 
| @@ -307,7 +377,13 @@ class AbstractBlock < AbstractNode | |
| 307 377 | 
             
                end
         | 
| 308 378 | 
             
                result
         | 
| 309 379 | 
             
              end
         | 
| 310 | 
            -
              alias  | 
| 380 | 
            +
              alias query find_by
         | 
| 381 | 
            +
             | 
| 382 | 
            +
              # Move to the next adjacent block in document order. If the current block is the last
         | 
| 383 | 
            +
              # item in a list, this method will return the following sibling of the list block.
         | 
| 384 | 
            +
              def next_adjacent_block
         | 
| 385 | 
            +
                (sib = (p = parent).blocks[(p.blocks.find_index self) + 1]) ? sib : p.next_adjacent_block unless @context == :document
         | 
| 386 | 
            +
              end
         | 
| 311 387 |  | 
| 312 388 | 
             
              # Public: Remove a substitution from this block
         | 
| 313 389 | 
             
              #
         | 
| @@ -319,39 +395,53 @@ class AbstractBlock < AbstractNode | |
| 319 395 | 
             
                nil
         | 
| 320 396 | 
             
              end
         | 
| 321 397 |  | 
| 322 | 
            -
              # Public: Generate  | 
| 323 | 
            -
              # is not already assigned.
         | 
| 398 | 
            +
              # Public: Generate and assign caption to block if not already assigned.
         | 
| 324 399 | 
             
              #
         | 
| 325 | 
            -
              # If the block has a title and a caption prefix is available
         | 
| 326 | 
            -
              #  | 
| 327 | 
            -
              #  | 
| 328 | 
            -
              # the block.
         | 
| 400 | 
            +
              # If the block has a title and a caption prefix is available for this block,
         | 
| 401 | 
            +
              # then build a caption from this information, assign it a number and store it
         | 
| 402 | 
            +
              # to the caption attribute on the block.
         | 
| 329 403 | 
             
              #
         | 
| 330 | 
            -
              # If  | 
| 331 | 
            -
              # do nothing.
         | 
| 404 | 
            +
              # If a caption has already been assigned to this block, do nothing.
         | 
| 332 405 | 
             
              #
         | 
| 333 | 
            -
              #  | 
| 334 | 
            -
              # | 
| 335 | 
            -
              #               is used. (default: nil).
         | 
| 406 | 
            +
              # The parts of a complete caption are: <prefix> <number>. <title>
         | 
| 407 | 
            +
              # This partial caption represents the part the precedes the title.
         | 
| 336 408 | 
             
              #
         | 
| 337 | 
            -
              #  | 
| 338 | 
            -
               | 
| 339 | 
            -
             | 
| 340 | 
            -
             | 
| 341 | 
            -
             | 
| 342 | 
            -
             | 
| 343 | 
            -
             | 
| 344 | 
            -
             | 
| 345 | 
            -
             | 
| 346 | 
            -
             | 
| 347 | 
            -
                     | 
| 348 | 
            -
                    caption_key = "#{key}-caption"
         | 
| 349 | 
            -
                    if (caption_title = @document.attributes[caption_key])
         | 
| 350 | 
            -
                      caption_num = @document.counter_increment("#{key}-number", self)
         | 
| 351 | 
            -
                      @caption = "#{caption_title} #{caption_num}. "
         | 
| 352 | 
            -
                    end
         | 
| 409 | 
            +
              # value - The explicit String caption to assign to this block (default: nil).
         | 
| 410 | 
            +
              # key   - The String prefix for the caption and counter attribute names.
         | 
| 411 | 
            +
              #         If not provided, the name of the context for this block is used.
         | 
| 412 | 
            +
              #         (default: nil)
         | 
| 413 | 
            +
              #
         | 
| 414 | 
            +
              # Returns nothing.
         | 
| 415 | 
            +
              def assign_caption value = nil, key = nil
         | 
| 416 | 
            +
                unless @caption || !@title || (@caption = value || @document.attributes['caption'])
         | 
| 417 | 
            +
                  if (prefix = @document.attributes[%(#{key ||= @context}-caption)])
         | 
| 418 | 
            +
                    @caption = %(#{prefix} #{@number = @document.increment_and_store_counter "#{key}-number", self}. )
         | 
| 419 | 
            +
                    nil
         | 
| 353 420 | 
             
                  end
         | 
| 354 421 | 
             
                end
         | 
| 422 | 
            +
              end
         | 
| 423 | 
            +
             | 
| 424 | 
            +
              # Internal: Assign the next index (0-based) and number (1-based) to the section
         | 
| 425 | 
            +
              #
         | 
| 426 | 
            +
              # Assign to the specified section the next index and, if the section is
         | 
| 427 | 
            +
              # numbered, number within this block (its parent).
         | 
| 428 | 
            +
              #
         | 
| 429 | 
            +
              # Returns nothing
         | 
| 430 | 
            +
              def enumerate_section section
         | 
| 431 | 
            +
                @next_section_index = (section.index = @next_section_index) + 1
         | 
| 432 | 
            +
                if (sectname = section.sectname) == 'appendix'
         | 
| 433 | 
            +
                  section.number = @document.counter 'appendix-number', 'A'
         | 
| 434 | 
            +
                  if (caption = @document.attributes['appendix-caption'])
         | 
| 435 | 
            +
                    section.caption = %(#{caption} #{section.number}: )
         | 
| 436 | 
            +
                  else
         | 
| 437 | 
            +
                    section.caption = %(#{section.number}. )
         | 
| 438 | 
            +
                  end
         | 
| 439 | 
            +
                # NOTE currently chapters in a book doctype are sequential even for multi-part books (see #979)
         | 
| 440 | 
            +
                elsif sectname == 'chapter'
         | 
| 441 | 
            +
                  section.number = @document.counter 'chapter-number', 1
         | 
| 442 | 
            +
                else
         | 
| 443 | 
            +
                  @next_section_number = (section.number = @next_section_number) + 1
         | 
| 444 | 
            +
                end if section.numbered
         | 
| 355 445 | 
             
                nil
         | 
| 356 446 | 
             
              end
         | 
| 357 447 |  | 
| @@ -366,35 +456,6 @@ class AbstractBlock < AbstractNode | |
| 366 456 | 
             
                ORDERED_LIST_KEYWORDS[list_type || @style]
         | 
| 367 457 | 
             
              end
         | 
| 368 458 |  | 
| 369 | 
            -
              # Internal: Assign the next index (0-based) to this section
         | 
| 370 | 
            -
              #
         | 
| 371 | 
            -
              # Assign the next index of this section within the parent
         | 
| 372 | 
            -
              # Block (in document order)
         | 
| 373 | 
            -
              #
         | 
| 374 | 
            -
              # Returns nothing
         | 
| 375 | 
            -
              def assign_index(section)
         | 
| 376 | 
            -
                section.index = @next_section_index
         | 
| 377 | 
            -
                @next_section_index += 1
         | 
| 378 | 
            -
             | 
| 379 | 
            -
                if section.sectname == 'appendix'
         | 
| 380 | 
            -
                  appendix_number = @document.counter 'appendix-number', 'A'
         | 
| 381 | 
            -
                  section.number = appendix_number if section.numbered
         | 
| 382 | 
            -
                  if (caption = @document.attr 'appendix-caption', '').empty?
         | 
| 383 | 
            -
                    section.caption = %(#{appendix_number}. )
         | 
| 384 | 
            -
                  else
         | 
| 385 | 
            -
                    section.caption = %(#{caption} #{appendix_number}: )
         | 
| 386 | 
            -
                  end
         | 
| 387 | 
            -
                elsif section.numbered
         | 
| 388 | 
            -
                  # chapters in a book doctype should be sequential even when divided into parts
         | 
| 389 | 
            -
                  if (section.level == 1 || (section.level == 0 && section.special)) && @document.doctype == 'book'
         | 
| 390 | 
            -
                    section.number = @document.counter('chapter-number', 1)
         | 
| 391 | 
            -
                  else
         | 
| 392 | 
            -
                    section.number = @next_section_number
         | 
| 393 | 
            -
                    @next_section_number += 1
         | 
| 394 | 
            -
                  end
         | 
| 395 | 
            -
                end
         | 
| 396 | 
            -
              end
         | 
| 397 | 
            -
             | 
| 398 459 | 
             
              # Internal: Reassign the section indexes
         | 
| 399 460 | 
             
              #
         | 
| 400 461 | 
             
              # Walk the descendents of the current Document or Section
         | 
| @@ -403,17 +464,17 @@ class AbstractBlock < AbstractNode | |
| 403 464 | 
             
              #
         | 
| 404 465 | 
             
              # IMPORTANT You must invoke this method on a node after removing
         | 
| 405 466 | 
             
              # child sections or else the internal counters will be off.
         | 
| 406 | 
            -
              # | 
| 467 | 
            +
              #
         | 
| 407 468 | 
             
              # Returns nothing
         | 
| 408 469 | 
             
              def reindex_sections
         | 
| 409 470 | 
             
                @next_section_index = 0
         | 
| 410 | 
            -
                @next_section_number =  | 
| 411 | 
            -
                @blocks.each  | 
| 471 | 
            +
                @next_section_number = 1
         | 
| 472 | 
            +
                @blocks.each do |block|
         | 
| 412 473 | 
             
                  if block.context == :section
         | 
| 413 | 
            -
                     | 
| 474 | 
            +
                    enumerate_section block
         | 
| 414 475 | 
             
                    block.reindex_sections
         | 
| 415 476 | 
             
                  end
         | 
| 416 | 
            -
                 | 
| 477 | 
            +
                end
         | 
| 417 478 | 
             
              end
         | 
| 418 479 | 
             
            end
         | 
| 419 480 | 
             
            end
         | 
| @@ -26,20 +26,17 @@ class AbstractNode | |
| 26 26 | 
             
              attr_reader :attributes
         | 
| 27 27 |  | 
| 28 28 | 
             
              def initialize parent, context, opts = {}
         | 
| 29 | 
            -
                # document is a special case, should refer to itself
         | 
| 30 29 | 
             
                if context == :document
         | 
| 31 | 
            -
                   | 
| 30 | 
            +
                  # document is a special case, should refer to itself
         | 
| 31 | 
            +
                  @document, @parent = self, nil
         | 
| 32 32 | 
             
                else
         | 
| 33 33 | 
             
                  if parent
         | 
| 34 | 
            -
                    @parent = parent
         | 
| 35 | 
            -
                    @document = parent.document
         | 
| 34 | 
            +
                    @document, @parent = parent.document, parent
         | 
| 36 35 | 
             
                  else
         | 
| 37 | 
            -
                    @parent = nil
         | 
| 38 | 
            -
                    @document = nil
         | 
| 36 | 
            +
                    @document = @parent = nil
         | 
| 39 37 | 
             
                  end
         | 
| 40 38 | 
             
                end
         | 
| 41 | 
            -
                @context = context
         | 
| 42 | 
            -
                @node_name = context.to_s
         | 
| 39 | 
            +
                @node_name = (@context = context).to_s
         | 
| 43 40 | 
             
                # QUESTION are we correct in duplicating the attributes (seems to be just as fast)
         | 
| 44 41 | 
             
                @attributes = (opts.key? :attributes) ? opts[:attributes].dup : {}
         | 
| 45 42 | 
             
                @passthroughs = {}
         | 
| @@ -51,8 +48,7 @@ class AbstractNode | |
| 51 48 | 
             
              #
         | 
| 52 49 | 
             
              # Returns nothing
         | 
| 53 50 | 
             
              def parent=(parent)
         | 
| 54 | 
            -
                @parent = parent
         | 
| 55 | 
            -
                @document = parent.document
         | 
| 51 | 
            +
                @parent, @document = parent, parent.document
         | 
| 56 52 | 
             
                nil
         | 
| 57 53 | 
             
              end
         | 
| 58 54 |  | 
| @@ -82,21 +78,17 @@ class AbstractNode | |
| 82 78 | 
             
              # Document node and return the value of the attribute if found. Otherwise,
         | 
| 83 79 | 
             
              # return the default value, which defaults to nil.
         | 
| 84 80 | 
             
              #
         | 
| 85 | 
            -
              # name | 
| 86 | 
            -
              #  | 
| 87 | 
            -
              # inherit | 
| 88 | 
            -
              # | 
| 81 | 
            +
              # name        - the String or Symbol name of the attribute to lookup
         | 
| 82 | 
            +
              # default_val - the Object value to return if the attribute is not found (default: nil)
         | 
| 83 | 
            +
              # inherit     - a Boolean indicating whether to check for the attribute on the
         | 
| 84 | 
            +
              #               AsciiDoctor::Document if not found on this node (default: false)
         | 
| 89 85 | 
             
              #
         | 
| 90 86 | 
             
              # return the value of the attribute or the default value if the attribute
         | 
| 91 87 | 
             
              # is not found in the attributes of this node or the document node
         | 
| 92 | 
            -
              def attr | 
| 93 | 
            -
                name = name.to_s | 
| 94 | 
            -
                 | 
| 95 | 
            -
                 | 
| 96 | 
            -
                  @attributes[name] || @document.attributes[name] || default_value
         | 
| 97 | 
            -
                else
         | 
| 98 | 
            -
                  @attributes[name] || default_value
         | 
| 99 | 
            -
                end
         | 
| 88 | 
            +
              def attr name, default_val = nil, inherit = true
         | 
| 89 | 
            +
                name = name.to_s
         | 
| 90 | 
            +
                # NOTE if @parent is set, it means @document is also set
         | 
| 91 | 
            +
                @attributes[name] || (inherit && @parent ? @document.attributes[name] || default_val : default_val)
         | 
| 100 92 | 
             
              end
         | 
| 101 93 |  | 
| 102 94 | 
             
              # Public: Check if the attribute is defined, optionally performing a
         | 
| @@ -108,35 +100,33 @@ class AbstractNode | |
| 108 100 | 
             
              # comparison value is specified (not nil), return whether the two values match.
         | 
| 109 101 | 
             
              # Otherwise, return whether the attribute was found.
         | 
| 110 102 | 
             
              #
         | 
| 111 | 
            -
              # name | 
| 112 | 
            -
              #  | 
| 113 | 
            -
              # inherit | 
| 114 | 
            -
              # | 
| 103 | 
            +
              # name       - the String or Symbol name of the attribute to lookup
         | 
| 104 | 
            +
              # expect_val - the expected Object value of the attribute (default: nil)
         | 
| 105 | 
            +
              # inherit    - a Boolean indicating whether to check for the attribute on the
         | 
| 106 | 
            +
              #              AsciiDoctor::Document if not found on this node (default: false)
         | 
| 115 107 | 
             
              #
         | 
| 116 108 | 
             
              # return a Boolean indicating whether the attribute exists and, if a
         | 
| 117 109 | 
             
              # comparison value is specified, whether the value of the attribute matches
         | 
| 118 110 | 
             
              # the comparison value
         | 
| 119 | 
            -
              def attr? | 
| 120 | 
            -
                name = name.to_s | 
| 121 | 
            -
                 | 
| 122 | 
            -
                if  | 
| 123 | 
            -
                  @attributes. | 
| 124 | 
            -
                elsif inherit
         | 
| 125 | 
            -
                  expect == (@attributes[name] || @document.attributes[name])
         | 
| 111 | 
            +
              def attr? name, expect_val = nil, inherit = true
         | 
| 112 | 
            +
                name = name.to_s
         | 
| 113 | 
            +
                # NOTE if @parent is set, it means @document is also set
         | 
| 114 | 
            +
                if expect_val.nil?
         | 
| 115 | 
            +
                  (@attributes.key? name) || (inherit && @parent && (@document.attributes.key? name))
         | 
| 126 116 | 
             
                else
         | 
| 127 | 
            -
                   | 
| 117 | 
            +
                  expect_val == (@attributes[name] || (inherit && @parent ? @document.attributes[name] : nil))
         | 
| 128 118 | 
             
                end
         | 
| 129 119 | 
             
              end
         | 
| 130 120 |  | 
| 131 121 | 
             
              # Public: Assign the value to the attribute name for the current node.
         | 
| 132 122 | 
             
              #
         | 
| 133 123 | 
             
              # name      - The String attribute name to assign
         | 
| 134 | 
            -
              # value     - The Object value to assign to the attribute
         | 
| 124 | 
            +
              # value     - The Object value to assign to the attribute (default: '')
         | 
| 135 125 | 
             
              # overwrite - A Boolean indicating whether to assign the attribute
         | 
| 136 126 | 
             
              #             if currently present in the attributes Hash (default: true)
         | 
| 137 127 | 
             
              #
         | 
| 138 128 | 
             
              # Returns a [Boolean] indicating whether the assignment was performed
         | 
| 139 | 
            -
              def set_attr name, value, overwrite = true
         | 
| 129 | 
            +
              def set_attr name, value = '', overwrite = true
         | 
| 140 130 | 
             
                if overwrite == false && (@attributes.key? name)
         | 
| 141 131 | 
             
                  false
         | 
| 142 132 | 
             
                else
         | 
| @@ -145,14 +135,23 @@ class AbstractNode | |
| 145 135 | 
             
                end
         | 
| 146 136 | 
             
              end
         | 
| 147 137 |  | 
| 138 | 
            +
              # Public: Remove the attribute from the current node.
         | 
| 139 | 
            +
              #
         | 
| 140 | 
            +
              # name      - The String attribute name to remove
         | 
| 141 | 
            +
              #
         | 
| 142 | 
            +
              # Returns the previous [String] value, or nil if the attribute was not present.
         | 
| 143 | 
            +
              def remove_attr name
         | 
| 144 | 
            +
                @attributes.delete name
         | 
| 145 | 
            +
              end
         | 
| 146 | 
            +
             | 
| 148 147 | 
             
              # TODO document me
         | 
| 149 148 | 
             
              def set_option(name)
         | 
| 150 | 
            -
                if @attributes. | 
| 151 | 
            -
                  @attributes['options'] =  | 
| 149 | 
            +
                if @attributes.key? 'options'
         | 
| 150 | 
            +
                  @attributes['options'] = %(#{@attributes['options']},#{name})
         | 
| 152 151 | 
             
                else
         | 
| 153 152 | 
             
                  @attributes['options'] = name
         | 
| 154 153 | 
             
                end
         | 
| 155 | 
            -
                @attributes[ | 
| 154 | 
            +
                @attributes[%(#{name}-option)] = ''
         | 
| 156 155 | 
             
              end
         | 
| 157 156 |  | 
| 158 157 | 
             
              # Public: A convenience method to check if the specified option attribute is
         | 
| @@ -165,7 +164,7 @@ class AbstractNode | |
| 165 164 | 
             
              #
         | 
| 166 165 | 
             
              # return a Boolean indicating whether the option has been specified
         | 
| 167 166 | 
             
              def option?(name)
         | 
| 168 | 
            -
                @attributes. | 
| 167 | 
            +
                @attributes.key? %(#{name}-option)
         | 
| 169 168 | 
             
              end
         | 
| 170 169 |  | 
| 171 170 | 
             
              # Public: Update the attributes of this node with the new values in
         | 
| @@ -189,11 +188,11 @@ class AbstractNode | |
| 189 188 | 
             
              end
         | 
| 190 189 |  | 
| 191 190 | 
             
              # Public: A convenience method that checks if the role attribute is specified
         | 
| 192 | 
            -
              def role? | 
| 193 | 
            -
                if  | 
| 194 | 
            -
                   | 
| 191 | 
            +
              def role? expect_val = nil
         | 
| 192 | 
            +
                if expect_val
         | 
| 193 | 
            +
                  expect_val == (@attributes['role'] || @document.attributes['role'])
         | 
| 195 194 | 
             
                else
         | 
| 196 | 
            -
                  @attributes. | 
| 195 | 
            +
                  @attributes.key?('role') || @document.attributes.key?('role')
         | 
| 197 196 | 
             
                end
         | 
| 198 197 | 
             
              end
         | 
| 199 198 |  | 
| @@ -205,45 +204,59 @@ class AbstractNode | |
| 205 204 | 
             
              # Public: A convenience method that checks if the specified role is present
         | 
| 206 205 | 
             
              # in the list of roles on this node
         | 
| 207 206 | 
             
              def has_role?(name)
         | 
| 208 | 
            -
                 | 
| 209 | 
            -
             | 
| 210 | 
            -
                else
         | 
| 211 | 
            -
                  false
         | 
| 212 | 
            -
                end
         | 
| 207 | 
            +
                # NOTE center + include? is faster than split + include?
         | 
| 208 | 
            +
                (val = @attributes['role'] || @document.attributes['role']).nil_or_empty? ? false : %( #{val} ).include?(%( #{name} ))
         | 
| 213 209 | 
             
              end
         | 
| 214 210 |  | 
| 215 211 | 
             
              # Public: A convenience method that returns the role names as an Array
         | 
| 212 | 
            +
              #
         | 
| 213 | 
            +
              # Returns the role names as an Array or an empty Array if the role attribute is absent.
         | 
| 216 214 | 
             
              def roles
         | 
| 217 | 
            -
                 | 
| 218 | 
            -
                  val.split(' ')
         | 
| 219 | 
            -
                else
         | 
| 220 | 
            -
                  []
         | 
| 221 | 
            -
                end
         | 
| 215 | 
            +
                (val = @attributes['role'] || @document.attributes['role']).nil_or_empty? ? [] : val.split
         | 
| 222 216 | 
             
              end
         | 
| 223 217 |  | 
| 224 218 | 
             
              # Public: A convenience method that adds the given role directly to this node
         | 
| 219 | 
            +
              #
         | 
| 220 | 
            +
              # Returns a Boolean indicating whether the role was added.
         | 
| 225 221 | 
             
              def add_role(name)
         | 
| 226 | 
            -
                 | 
| 227 | 
            -
                  @attributes['role'] =  | 
| 222 | 
            +
                if (val = @attributes['role']).nil_or_empty?
         | 
| 223 | 
            +
                  @attributes['role'] = name
         | 
| 224 | 
            +
                  true
         | 
| 225 | 
            +
                # NOTE center + include? is faster than split + include?
         | 
| 226 | 
            +
                elsif %( #{val} ).include?(%( #{name} ))
         | 
| 227 | 
            +
                  false
         | 
| 228 | 
            +
                else
         | 
| 229 | 
            +
                  @attributes['role'] = %(#{val} #{name})
         | 
| 230 | 
            +
                  true
         | 
| 228 231 | 
             
                end
         | 
| 229 232 | 
             
              end
         | 
| 230 233 |  | 
| 231 234 | 
             
              # Public: A convenience method that removes the given role directly from this node
         | 
| 235 | 
            +
              #
         | 
| 236 | 
            +
              # Returns a Boolean indicating whether the role was removed.
         | 
| 232 237 | 
             
              def remove_role(name)
         | 
| 233 | 
            -
                if ( | 
| 234 | 
            -
                   | 
| 235 | 
            -
             | 
| 238 | 
            +
                if (val = @attributes['role']).nil_or_empty?
         | 
| 239 | 
            +
                  false
         | 
| 240 | 
            +
                elsif (val = val.split).delete name
         | 
| 241 | 
            +
                  if val.empty?
         | 
| 242 | 
            +
                    @attributes.delete('role')
         | 
| 243 | 
            +
                  else
         | 
| 244 | 
            +
                    @attributes['role'] = val * ' '
         | 
| 245 | 
            +
                  end
         | 
| 246 | 
            +
                  true
         | 
| 247 | 
            +
                else
         | 
| 248 | 
            +
                  false
         | 
| 236 249 | 
             
                end
         | 
| 237 250 | 
             
              end
         | 
| 238 251 |  | 
| 239 | 
            -
              # Public: A convenience method that checks if the reftext attribute is  | 
| 252 | 
            +
              # Public: A convenience method that checks if the reftext attribute is defined.
         | 
| 240 253 | 
             
              def reftext?
         | 
| 241 | 
            -
                @attributes. | 
| 254 | 
            +
                @attributes.key? 'reftext'
         | 
| 242 255 | 
             
              end
         | 
| 243 256 |  | 
| 244 | 
            -
              # Public: A convenience method that returns the value of the reftext attribute
         | 
| 257 | 
            +
              # Public: A convenience method that returns the value of the reftext attribute with substitutions applied.
         | 
| 245 258 | 
             
              def reftext
         | 
| 246 | 
            -
                @attributes['reftext']  | 
| 259 | 
            +
                (val = @attributes['reftext']) ? (apply_reftext_subs val) : nil
         | 
| 247 260 | 
             
              end
         | 
| 248 261 |  | 
| 249 262 | 
             
              # Public: Construct a reference or data URI to an icon image for the
         | 
| @@ -315,12 +328,12 @@ class AbstractNode | |
| 315 328 | 
             
              #
         | 
| 316 329 | 
             
              # Returns A String reference or data URI for the target image
         | 
| 317 330 | 
             
              def image_uri(target_image, asset_dir_key = 'imagesdir')
         | 
| 318 | 
            -
                if (doc = @document).safe < SafeMode::SECURE && doc.attr? | 
| 319 | 
            -
                  if (Helpers.uriish? target_image) ||
         | 
| 320 | 
            -
                      (asset_dir_key && (images_base = doc.attr | 
| 321 | 
            -
                      (target_image = normalize_web_path | 
| 322 | 
            -
                    if doc.attr? | 
| 323 | 
            -
                      generate_data_uri_from_uri target_image, doc.attr? | 
| 331 | 
            +
                if (doc = @document).safe < SafeMode::SECURE && (doc.attr? 'data-uri')
         | 
| 332 | 
            +
                  if ((Helpers.uriish? target_image) && (target_image = uri_encode_spaces target_image)) ||
         | 
| 333 | 
            +
                      (asset_dir_key && (images_base = doc.attr asset_dir_key) && (Helpers.uriish? images_base) &&
         | 
| 334 | 
            +
                      (target_image = normalize_web_path target_image, images_base, false))
         | 
| 335 | 
            +
                    if doc.attr? 'allow-uri-read'
         | 
| 336 | 
            +
                      generate_data_uri_from_uri target_image, (doc.attr? 'cache-uri')
         | 
| 324 337 | 
             
                    else
         | 
| 325 338 | 
             
                      target_image
         | 
| 326 339 | 
             
                    end
         | 
| @@ -328,7 +341,7 @@ class AbstractNode | |
| 328 341 | 
             
                    generate_data_uri target_image, asset_dir_key
         | 
| 329 342 | 
             
                  end
         | 
| 330 343 | 
             
                else
         | 
| 331 | 
            -
                  normalize_web_path target_image, (asset_dir_key ? doc.attr | 
| 344 | 
            +
                  normalize_web_path target_image, (asset_dir_key ? (doc.attr asset_dir_key) : nil)
         | 
| 332 345 | 
             
                end
         | 
| 333 346 | 
             
              end
         | 
| 334 347 |  | 
| @@ -356,20 +369,13 @@ class AbstractNode | |
| 356 369 |  | 
| 357 370 | 
             
                unless ::File.readable? image_path
         | 
| 358 371 | 
             
                  warn %(asciidoctor: WARNING: image to embed not found or not readable: #{image_path})
         | 
| 359 | 
            -
                   | 
| 360 | 
            -
                  return "data:#{mimetype}:base64,"
         | 
| 372 | 
            +
                  return %(data:#{mimetype};base64,)
         | 
| 361 373 | 
             
                  # uncomment to return 1 pixel white dot instead
         | 
| 362 374 | 
             
                  #return 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
         | 
| 363 375 | 
             
                end
         | 
| 364 376 |  | 
| 365 | 
            -
                bindata = nil
         | 
| 366 | 
            -
                if ::IO.respond_to? :binread
         | 
| 367 | 
            -
                  bindata = ::IO.binread(image_path)
         | 
| 368 | 
            -
                else
         | 
| 369 | 
            -
                  bindata = ::File.open(image_path, 'rb') {|file| file.read }
         | 
| 370 | 
            -
                end
         | 
| 371 377 | 
             
                # NOTE base64 is autoloaded by reference to ::Base64
         | 
| 372 | 
            -
                %(data:#{mimetype};base64,#{::Base64.encode64( | 
| 378 | 
            +
                %(data:#{mimetype};base64,#{::Base64.encode64(::IO.binread image_path).delete LF})
         | 
| 373 379 | 
             
              end
         | 
| 374 380 |  | 
| 375 381 | 
             
              # Public: Read the image data from the specified URI and generate a data URI
         | 
| @@ -396,12 +402,12 @@ class AbstractNode | |
| 396 402 |  | 
| 397 403 | 
             
                begin
         | 
| 398 404 | 
             
                  mimetype = nil
         | 
| 399 | 
            -
                  bindata = open(image_uri, 'rb') {| | 
| 400 | 
            -
                    mimetype =  | 
| 401 | 
            -
                     | 
| 405 | 
            +
                  bindata = open(image_uri, 'rb') {|fd|
         | 
| 406 | 
            +
                    mimetype = fd.content_type
         | 
| 407 | 
            +
                    fd.read
         | 
| 402 408 | 
             
                  }
         | 
| 403 409 | 
             
                  # NOTE base64 is autoloaded by reference to ::Base64
         | 
| 404 | 
            -
                  %(data:#{mimetype};base64,#{::Base64.encode64(bindata).delete  | 
| 410 | 
            +
                  %(data:#{mimetype};base64,#{::Base64.encode64(bindata).delete LF})
         | 
| 405 411 | 
             
                rescue
         | 
| 406 412 | 
             
                  warn %(asciidoctor: WARNING: could not retrieve image data from URI: #{image_uri})
         | 
| 407 413 | 
             
                  image_uri
         | 
| @@ -436,7 +442,7 @@ class AbstractNode | |
| 436 442 | 
             
                    Helpers.require_library 'open-uri/cached', 'open-uri-cached' if doc.attr? 'cache-uri'
         | 
| 437 443 | 
             
                    begin
         | 
| 438 444 | 
             
                      data = ::OpenURI.open_uri(target) {|fd| fd.read }
         | 
| 439 | 
            -
                      data = (Helpers.normalize_lines_from_string data) *  | 
| 445 | 
            +
                      data = (Helpers.normalize_lines_from_string data) * LF if opts[:normalize]
         | 
| 440 446 | 
             
                    rescue
         | 
| 441 447 | 
             
                      warn %(asciidoctor: WARNING: could not retrieve contents of #{opts[:label] || 'asset'} at URI: #{target}) if opts.fetch :warn_on_failure, true
         | 
| 442 448 | 
             
                      data = nil
         | 
| @@ -447,7 +453,7 @@ class AbstractNode | |
| 447 453 | 
             
                  end
         | 
| 448 454 | 
             
                else
         | 
| 449 455 | 
             
                  target = normalize_system_path target, opts[:start], nil, :target_name => (opts[:label] || 'asset')
         | 
| 450 | 
            -
                  data = read_asset target, :normalize => opts[:normalize], :warn_on_failure => (opts.fetch :warn_on_failure, true)
         | 
| 456 | 
            +
                  data = read_asset target, :normalize => opts[:normalize], :warn_on_failure => (opts.fetch :warn_on_failure, true), :label => opts[:label]
         | 
| 451 457 | 
             
                end
         | 
| 452 458 | 
             
                data
         | 
| 453 459 | 
             
              end
         | 
| @@ -465,25 +471,24 @@ class AbstractNode | |
| 465 471 | 
             
              #
         | 
| 466 472 | 
             
              # Returns the [String] content of the file at the specified path, or nil
         | 
| 467 473 | 
             
              # if the file does not exist.
         | 
| 468 | 
            -
              def read_asset | 
| 474 | 
            +
              def read_asset path, opts = {}
         | 
| 469 475 | 
             
                # remap opts for backwards compatibility
         | 
| 470 476 | 
             
                opts = { :warn_on_failure => (opts != false) } unless ::Hash === opts
         | 
| 471 477 | 
             
                if ::File.readable? path
         | 
| 472 478 | 
             
                  if opts[:normalize]
         | 
| 473 | 
            -
                    Helpers.normalize_lines_from_string(::IO.read | 
| 479 | 
            +
                    Helpers.normalize_lines_from_string(::IO.read path) * LF
         | 
| 474 480 | 
             
                  else
         | 
| 475 481 | 
             
                    # QUESTION should we chomp or rstrip content?
         | 
| 476 | 
            -
                    ::IO.read | 
| 482 | 
            +
                    ::IO.read path
         | 
| 477 483 | 
             
                  end
         | 
| 478 | 
            -
                 | 
| 479 | 
            -
                  warn %(asciidoctor: WARNING: file does not exist or cannot be read: #{path}) | 
| 480 | 
            -
                  nil
         | 
| 484 | 
            +
                elsif opts[:warn_on_failure]
         | 
| 485 | 
            +
                  warn %(asciidoctor: WARNING: #{(attr 'docfile') || '<stdin>'}: #{opts[:label] || 'file'} does not exist or cannot be read: #{path})
         | 
| 481 486 | 
             
                end
         | 
| 482 487 | 
             
              end
         | 
| 483 488 |  | 
| 484 | 
            -
              # Public: Normalize the web  | 
| 489 | 
            +
              # Public: Normalize the web path using the PathResolver.
         | 
| 485 490 | 
             
              #
         | 
| 486 | 
            -
              # See {PathResolver#web_path} for details.
         | 
| 491 | 
            +
              # See {PathResolver#web_path} for details about path resolution and encoding.
         | 
| 487 492 | 
             
              #
         | 
| 488 493 | 
             
              # target              - the String target path
         | 
| 489 494 | 
             
              # start               - the String start (i.e, parent) path (optional, default: nil)
         | 
| @@ -492,12 +497,21 @@ class AbstractNode | |
| 492 497 | 
             
              # Returns the resolved [String] path
         | 
| 493 498 | 
             
              def normalize_web_path(target, start = nil, preserve_uri_target = true)
         | 
| 494 499 | 
             
                if preserve_uri_target && (Helpers.uriish? target)
         | 
| 495 | 
            -
                  target
         | 
| 500 | 
            +
                  uri_encode_spaces target
         | 
| 496 501 | 
             
                else
         | 
| 497 502 | 
             
                  (@path_resolver ||= PathResolver.new).web_path target, start
         | 
| 498 503 | 
             
                end
         | 
| 499 504 | 
             
              end
         | 
| 500 505 |  | 
| 506 | 
            +
              # Internal: URI encode spaces in a String
         | 
| 507 | 
            +
              #
         | 
| 508 | 
            +
              # str - the String to encode
         | 
| 509 | 
            +
              #
         | 
| 510 | 
            +
              # Returns the String with all spaces replaced with %20.
         | 
| 511 | 
            +
              def uri_encode_spaces str
         | 
| 512 | 
            +
                (str.include? ' ') ? (str.gsub ' ', '%20') : str
         | 
| 513 | 
            +
              end
         | 
| 514 | 
            +
             | 
| 501 515 | 
             
              # Public: Resolve and normalize a secure path from the target and start paths
         | 
| 502 516 | 
             
              # using the PathResolver.
         | 
| 503 517 | 
             
              #
         |