class Asciidoctor::Document

noheader - The header block (h1 heading, author, revision info) should not be shown
notitle - The h1 heading should not be shown
Keep in mind that you’ll want to honor these document settings:
header.title - title of section level 0
first_section.title - title of first section in document, if present
title - value of the title attribute, or nil if not present
name - an alias of doctitle
otherwise nil
otherwise title of first section in document, if present
doctitle - value of title attribute, if assigned and non-empty,
There are several strategies for getting the title of the document:
using erb templates.
Public: Methods for parsing Asciidoc documents and rendering them

def apply_attribute_value_subs(value)

Returns The String value with substitutions performed.

value - The String attribute value on which to perform substitutions

apply the verbatim substitutions to the value.
apply the substitutions defined on the macro to the text. Otherwise,
If the value is an inline passthrough macro (e.g., pass:[text]), then

Internal: Apply substitutions to the attribute value
def apply_attribute_value_subs(value)
  if value.match(REGEXP[:pass_macro_basic])
    # copy match for Ruby 1.8.7 compat
    m = $~
    subs = []
    if !m[1].empty?
      subs = resolve_subs(m[1])
    end
    if !subs.empty?
      apply_subs(m[2], subs)
    else
      m[2]
    end
  else
    apply_header_subs(value)
  end
end

def attribute_locked?(name)

Returns true if the attribute is locked, false otherwise

key - The attribute key to check

Public: Determine if the attribute has been locked by being assigned in document options
def attribute_locked?(name)
  @attribute_overrides.has_key?(name) || @attribute_overrides.has_key?("#{name}!")
end

def author

returns the full name of the author as a String

Public: Convenience method to retrieve the document attribute 'author'
def author
  @attributes['author']
end

def backend

def backend
  @attributes['backend']
end

def clear_playback_attributes(attributes)

Internal: Delete any attributes stored for playback
def clear_playback_attributes(attributes)
  attributes.delete(:attribute_entries)
end

def content

def content
  # per AsciiDoc-spec, remove the title after rendering the header
  @attributes.delete('title')
  @blocks.map {|b| b.render }.join
end

def counter(name, seed = nil)

returns the next number in the sequence for the specified counter

seed - the initial value as a String or Integer
name - the String name of the counter

Public: Get the named counter and take the next number in the sequence.
def counter(name, seed = nil)
  if !@counters.has_key? name
    if seed.nil?
      seed = nextval(@attributes.has_key?(name) ? @attributes[name] : 0)
    elsif seed.to_i.to_s == seed
      seed = seed.to_i
    end
    @counters[name] = seed
  else
    @counters[name] = nextval(@counters[name])
  end
  (@attributes[name] = @counters[name])
end

def delete_attribute(name)

returns true if the attribute was deleted, false if it was not because it's locked

name - the String attribute name

If the attribute is locked, false is returned. Otherwise, the attribute is deleted.

Public: Delete the specified attribute from the document if the name is not locked
def delete_attribute(name)
  if attribute_locked?(name)
    false
  else
    @attributes.delete(name)
    true
  end
end

def doctitle

We need to be able to return some semblance of a title
def doctitle
  if !(title = @attributes.fetch('title', '')).empty?
    title
  elsif !(sect = first_section).nil? && sect.title?
    sect.title
  else
    nil
  end
end

def doctype

def doctype
  @attributes['doctype']
end

def first_section

QUESTION move to AbstractBlock?
def first_section
  has_header? ? @header : (@blocks || []).detect{|e| e.is_a? Section}
end

def footnotes

def footnotes
  @references[:footnotes]
end

def footnotes?

def footnotes?
  not @references[:footnotes].empty?
end

def has_header?

def has_header?
  !@header.nil?
end

def initialize(data = [], options = {}, &block)

puts doc.render
doc = Asciidoctor::Document.new(data)
data = File.readlines(filename)

Examples

data to include in this document.
block - A block that can be used to retrieve external Asciidoc
(default: {})
suppressing the header/footer (:header_footer) and attribute overrides (:attributes)
options - A Hash of options to control processing, such as setting the safe mode (:safe),
data - The Array of Strings holding the Asciidoc source document. (default: [])

Public: Initialize an Asciidoc object.
def initialize(data = [], options = {}, &block)
  super(self, :document)
  @renderer = nil
  if options[:parent]
    @parent_document = options.delete(:parent)
    # should we dup here?
    options[:attributes] = @parent_document.attributes
    options[:safe] ||= @parent_document.safe
    options[:base_dir] ||= @parent_document.base_dir
    @renderer = @parent_document.renderer
  else
    @parent_document = nil
  end
  @header = nil
  @references = {
    :ids => {},
    :footnotes => [],
    :links => [],
    :images => [],
    :indexterms => []
  }
  @counters = {}
  @callouts = Callouts.new
  @options = options
  @safe = @options.fetch(:safe, SafeMode::SECURE).to_i
  @options[:header_footer] = @options.fetch(:header_footer, false)
  @attributes['asciidoctor'] = ''
  @attributes['asciidoctor-version'] = VERSION
  @attributes['sectids'] = ''
  @attributes['encoding'] = 'UTF-8'
  @attributes['notitle'] = '' if !@options[:header_footer]
  # language strings
  # TODO load these based on language settings
  @attributes['caution-caption'] = 'Caution'
  @attributes['important-caption'] = 'Important'
  @attributes['note-caption'] = 'Note'
  @attributes['tip-caption'] = 'Tip'
  @attributes['warning-caption'] = 'Warning'
  @attributes['appendix-caption'] = 'Appendix'
  @attributes['example-caption'] = 'Example'
  @attributes['figure-caption'] = 'Figure'
  @attributes['table-caption'] = 'Table'
  @attributes['toc-title'] = 'Table of Contents'
  @attribute_overrides = options[:attributes] || {}
  # the only way to set the include-depth attribute is via the document options
  # 10 is the AsciiDoc default, though currently Asciidoctor only supports 1 level
  @attribute_overrides['include-depth'] ||= 10
  # if the base_dir option is specified, it overrides docdir as the root for relative paths
  # otherwise, the base_dir is the directory of the source file (docdir) or the current
  # directory of the input is a string
  if options[:base_dir].nil?
    if @attribute_overrides['docdir']
      @base_dir = @attribute_overrides['docdir'] = File.expand_path(@attribute_overrides['docdir'])
    else
      # perhaps issue a warning here?
      @base_dir = @attribute_overrides['docdir'] = Dir.pwd
    end
  else
    @base_dir = @attribute_overrides['docdir'] = File.expand_path(options[:base_dir])
  end
  # allow common attributes backend and doctype to be set using options hash
  unless @options[:backend].nil?
    @attribute_overrides['backend'] = @options[:backend]
  end
  unless @options[:doctype].nil?
    @attribute_overrides['doctype'] = @options[:doctype]
  end
  if @safe >= SafeMode::SERVER
    # restrict document from setting source-highlighter and backend
    @attribute_overrides['source-highlighter'] ||= nil
    @attribute_overrides['backend'] ||= DEFAULT_BACKEND
    # restrict document from seeing the docdir and trim docfile to relative path
    if @attribute_overrides.has_key?('docfile') && @parent_document.nil?
      @attribute_overrides['docfile'] = @attribute_overrides['docfile'][(@attribute_overrides['docdir'].length + 1)..-1]
    end
    @attribute_overrides['docdir'] = ''
    # restrict document from enabling icons
    if @safe >= SafeMode::SECURE
      @attribute_overrides['icons'] ||= nil
    end
  end
  
  @attribute_overrides.delete_if {|key, val|
    verdict = false
    # a nil or negative key undefines the attribute 
    if val.nil? || key[-1..-1] == '!'
      @attributes.delete(key.chomp '!')
    # otherwise it's an attribute assignment
    else
      # a value ending in @ indicates this attribute does not override
      # an attribute with the same key in the document souce
      if val.is_a?(String) && val.end_with?('@')
        val.chop!
        verdict = true
      end
      @attributes[key] = val
    end
    verdict
  }
  @attributes['backend'] ||= DEFAULT_BACKEND
  @attributes['doctype'] ||= DEFAULT_DOCTYPE
  update_backend_attributes
  if !@parent_document.nil?
    # don't need to do the extra processing within our own document
    @reader = Reader.new(data)
  else
    @reader = Reader.new(data, self, true, &block)
  end
  # dynamic intrinstic attribute values
  now = Time.new
  @attributes['localdate'] ||= now.strftime('%Y-%m-%d')
  @attributes['localtime'] ||= now.strftime('%H:%M:%S %Z')
  @attributes['localdatetime'] ||= [@attributes['localdate'], @attributes['localtime']] * ' '
  
  # docdate, doctime and docdatetime should default to
  # localdate, localtime and localdatetime if not otherwise set
  @attributes['docdate'] ||= @attributes['localdate']
  @attributes['doctime'] ||= @attributes['localtime']
  @attributes['docdatetime'] ||= @attributes['localdatetime']
  
  @attributes['iconsdir'] ||= File.join(@attributes.fetch('imagesdir', 'images'), 'icons')
  # Now parse the lines in the reader into blocks
  Lexer.parse(@reader, self, :header_only => @options.fetch(:parse_header_only, false)) 
  @callouts.rewind
  Debug.debug {
    msg = []
    msg << "Found #{@blocks.size} blocks in this document:"
    @blocks.each {|b|
      msg << b
    }
    msg * "\n"
  }
end

def nested?

def nested?
  !@parent_document.nil?
end

def nextval(current)

returns the next value in the sequence according to the current value's type

current - the value to increment as a String or Integer

Handles both integer and character sequences.

Internal: Get the next value in the sequence.
def nextval(current)
  if current.is_a?(Integer)
    current + 1
  else
    intval = current.to_i
    if intval.to_s != current.to_s
      (current[0].ord + 1).chr
    else
      intval + 1 
    end
  end
end

def noheader

def noheader
  @attributes.has_key? 'noheader'
end

def notitle

def notitle
  @attributes.has_key? 'notitle'
end

def playback_attributes(block_attributes)

Internal: Replay attribute assignments at the block level
def playback_attributes(block_attributes)
  if block_attributes.has_key? :attribute_entries
    block_attributes[:attribute_entries].each do |entry|
      if entry.negate
        @attributes.delete(entry.name)
      else
        @attributes[entry.name] = entry.value
      end
    end
  end
end

def register(type, value)

def register(type, value)
  case type
  when :ids
    if value.is_a?(Array)
      @references[:ids][value[0]] = (value[1] || '[' + value[0] + ']')
    else
      @references[:ids][value] = '[' + value + ']'
    end
  when :footnotes, :indexterms
    @references[type] << value
  else
    if @options[:catalog_assets]
      @references[type] << value
    end
  end
end

def render(opts = {})

using the appropriate built-in template.
or a template is missing, the renderer will fall back to
loaded by Renderer. If a :template_dir is not specified,
Public: Render the Asciidoc document using the templates
def render(opts = {})
  restore_attributes
  r = renderer(opts)
  @options.merge(opts)[:header_footer] ? r.render('document', self).strip : r.render('embedded', self)
end

def renderer(opts = {})

def renderer(opts = {})
  return @renderer if @renderer
  
  render_options = {}
  # Load up relevant Document @options
  if @options.has_key? :template_dir
    render_options[:template_dir] = @options[:template_dir]
  end
  
  render_options[:backend] = @attributes.fetch('backend', 'html5')
  render_options[:template_engine] = @options[:template_engine]
  render_options[:eruby] = @options.fetch(:eruby, 'erb')
  render_options[:compact] = @options.fetch(:compact, false)
  
  # Override Document @option settings with options passed in
  render_options.merge! opts
  @renderer = Renderer.new(render_options)
end

def restore_attributes

Internal: Restore the attributes to the previously saved state
def restore_attributes
  @attributes = @original_attributes
end

def revdate

returns the date of last revision for the document as a String

Public: Convenience method to retrieve the document attribute 'revdate'
def revdate
  @attributes['revdate']
end

def save_attributes

at a future time.
Internal: Branch the attributes so that the original state can be restored
def save_attributes
  # css-signature cannot be updated after header attributes are processed
  if @id.nil? && @attributes.has_key?('css-signature')
    @id = @attributes['css-signature']
  end
  @original_attributes = @attributes.dup
end

def set_attribute(name, value)

returns true if the attribute was set, false if it was not set because it's locked

value - the String attribute value
name - the String attribute name

value of backend-related attributes are updated.
substitutions on the value. If the attribute name is 'backend', then the
assigned to the attribute name after first performing attribute
If the attribute is locked, false is returned. Otherwise, the value is

Public: Set the specified attribute on the document if the name is not locked
def set_attribute(name, value)
  if attribute_locked?(name)
    false
  else
    @attributes[name] = apply_attribute_value_subs(value)
    if name == 'backend'
      update_backend_attributes()
    end
    true
  end
end

def source

Make the raw source for the Document available.
def source
  @reader.source.join if @reader
end

def source_lines

Make the raw source lines for the Document available.
def source_lines
  @reader.source if @reader
end

def splain

def splain
  Debug.debug {
    msg = ''
    if @header
      msg = "Header is #{@header}"
    else
      msg = "No header"
    end
    msg += "I have #{@blocks.count} blocks"
    @blocks.each_with_index do |block, i|
      msg += "v" * 60
      msg += "Block ##{i} is a #{block.class}"
      msg += "Name is #{block.title rescue 'n/a'}"
      block.splain(0) if block.respond_to? :splain
      msg += "^" * 60
    end
  }
  nil
end

def title

The title explicitly defined in the document attributes
def title
  @attributes['title']
end

def title=(title)

def title=(title)
  @header ||= Section.new self
  @header.title = title
end

def to_s

def to_s
  %[#{super.to_s} - #{doctitle}]  
end

def update_backend_attributes()

Public: Update the backend attributes to reflect a change in the selected backend
def update_backend_attributes()
  backend = @attributes['backend']
  if BACKEND_ALIASES.has_key? backend
    backend = @attributes['backend'] = BACKEND_ALIASES[backend]
  end
  basebackend = backend.sub(/[[:digit:]]+$/, '')
  page_width = DEFAULT_PAGE_WIDTHS[basebackend]
  if page_width
    @attributes['pagewidth'] = page_width
  else
    @attributes.delete('pagewidth')
  end
  @attributes["backend-#{backend}"] = ''
  @attributes['basebackend'] = basebackend
  @attributes["basebackend-#{basebackend}"] = ''
  # REVIEW cases for the next two assignments
  @attributes["#{backend}-#{@attributes['doctype']}"] = ''
  @attributes["#{basebackend}-#{@attributes['doctype']}"] = ''
  ext = DEFAULT_EXTENSIONS[basebackend] || '.html'
  @attributes['outfilesuffix'] = ext
  file_type = ext[1..-1]
  @attributes['filetype'] = file_type
  @attributes["filetype-#{file_type}"] = ''
end