# File lib/maruku/input/parse_span.rb, line 22
  def read_span(src, escaped, exit_on_chars=nil, exit_on_strings=nil)
    escaped = Array(escaped)
    con = SpanContext.new
    dquote_state = squote_state = :closed
    c = d = prev_char = nil
    while true
      c = src.cur_char

      # This is only an optimization which cuts 50% of the time used.
      # (but you can't use a-zA-z in exit_on_chars)
      if c && c =~  /[[:alnum:]]/
        con.push_char src.shift_char
        prev_char = c
        next
      end

      break if Array(exit_on_chars).include?(c)
      if Array(exit_on_strings).any? {|x| src.cur_chars_are x }
        # Special case: bold nested in italic
        break unless !(['*', '_'] & Array(exit_on_strings)).empty? &&
          ['**', '__'].include?(src.cur_chars(2)) &&
          !['***', '___'].include?(src.cur_chars(3))
      end

      # check if there are extensions
      next if check_span_extensions(src, con)

      case c = src.cur_char
      when ' '
        if src.cur_chars_are "  \n"
          src.ignore_chars(3)
          con.push_element md_br
          prev_char = ' '
          next
        elsif src.cur_chars_are ' >>' # closing guillemettes
          src.ignore_chars(3)
          con.push_element md_entity('nbsp')
          con.push_element md_entity('raquo')
        elsif src.cur_chars(5) =~ / '\d\ds/ # special case: '80s
          src.ignore_chars(2)
          con.push_space
          con.push_element md_entity('rsquo')
        elsif src.cur_chars_are " '" # opening single-quote
          src.ignore_chars(2)
          con.push_space
          con.push_element md_entity('lsquo')
          squote_state = :open
        else
          src.ignore_char
          con.push_space
        end
      when "\n", "\t"
        src.ignore_char
        con.push_space
      when '`'
        read_inline_code(src, con)
      when '<'
        # It could be:
        # 1) HTML "<div ..."
        # 2) HTML "<!-- ..."
        # 3) url "<http:// ", "<ftp:// ..."
        # 4) email "<andrea@... ", "<mailto:andrea@..."
        # 5) on itself! "a < b  "
        # 6) Start of <<guillemettes>>

        case d = src.next_char
        when '<'  # guillemettes
          if src.cur_chars_are '<< '
            src.ignore_chars(3)
            con.push_element md_entity('laquo')
            con.push_element md_entity('nbsp')
          else
            src.ignore_chars(2)
            con.push_element md_entity('laquo')
          end
        when '!'
          if src.cur_chars_are '<!--'
            read_inline_html(src, con)
          else
            con.push_char src.shift_char
          end
        when '?'
          read_xml_instr_span(src, con)
        when ' ', "\t"
          con.push_char src.shift_char
        else
          if src.next_matches(/<mailto:/) ||
              src.next_matches(/<[\w\.]+\@/)
            read_email_el(src, con)
          elsif src.next_matches(/<\w+:/)
            read_url_el(src, con)
          elsif src.next_matches(/<\w/)
            #puts "This is HTML: #{src.cur_chars(20)}"
            read_inline_html(src, con)
          else
            #puts "This is NOT HTML: #{src.cur_chars(20)}"
            con.push_char src.shift_char
          end
        end
      when '>'
        if src.next_char == '>'
          src.ignore_chars(2)
          con.push_element md_entity('raquo')
        else
          con.push_char src.shift_char
        end
      when "\\"
        d = src.next_char
        if d == "'"
          src.ignore_chars(2)
          con.push_element md_entity('apos')
        elsif d == '"'
          src.ignore_chars(2)
          con.push_element md_entity('quot')
        elsif escaped.include? d
          src.ignore_chars(2)
          con.push_char d
        else
          con.push_char src.shift_char
        end
      when '['
        if markdown_extra? && src.next_char == '^'
          read_footnote_ref(src,con)
        elsif IgnoreWikiLinks && src.next_char == '['
          con.push_char src.shift_char
          con.push_char src.shift_char
        else
          read_link(src, con)
        end
      when '!'
        if src.next_char == '['
          read_image(src, con)
        else
          con.push_char src.shift_char
        end
      when '&'
        # named references
        if m = src.read_regexp(/\&(\w+);/)
          con.push_element md_entity(m[1])
          # numeric
        elsif m = src.read_regexp(/\&\#(x)?(\w+);/)
          num = m[1] ? m[2].hex : m[2].to_i
          con.push_element md_entity(num)
        else
          con.push_char src.shift_char
        end
      when '*'
        if !src.next_char
          maruku_error "Opening * as last char.", src, con, 'Treating as literal'
          con.push_char src.shift_char
        else
          follows = src.cur_chars(4)
          if follows =~ /^\*\*\*[^\s\*]/
            con.push_element read_emstrong(src, '***')
          elsif follows  =~ /^\*\*[^\s\*]/
            con.push_element read_strong(src, '**')
          elsif follows =~ /^\*[^\s\*]/
            con.push_element read_em(src, '*')
          else # * is just a normal char
            con.push_char src.shift_char
          end
        end
      when '_'
        if !src.next_char
          maruku_error "Opening _ as last char", src, con, 'Treating as literal'
          con.push_char src.shift_char
        else
          # we don't want "mod_ruby" to start an emphasis
          # so we start one only if
          # 1) there's nothing else in the span (first char)
          # or 2) the last char was a space
          # or 3) the current string is empty
          #if con.elements.empty? ||
          if con.is_end?
            # also, we check the next characters
            follows = src.cur_chars(4)
            if  follows =~ /^\_\_\_[^\s\_]/
              con.push_element read_emstrong(src, '___')
            elsif follows  =~ /^\_\_[^\s\_]/
              con.push_element read_strong(src, '__')
            elsif follows =~ /^\_[^\s\_]/
              con.push_element read_em(src, '_')
            else # _ is just a normal char
              con.push_char src.shift_char
            end
          else
            # _ is just a normal char
            con.push_char src.shift_char
          end
        end
      when '{' # extension
        if ['#', '.', ':'].include? src.next_char
          src.ignore_char # {
          interpret_extension(src, con, '}')
          src.ignore_char # }
        else
          con.push_char src.shift_char
        end
      when nil
        maruku_error( ("Unclosed span (waiting for %s" +
                       "#{exit_on_strings.inspect})") %
                      [ exit_on_chars ? "#{exit_on_chars.inspect} or" : "" ],
                      src, con)
        break
      when '-' # dashes
        if src.next_char == '-'
          if src.cur_chars_are '---'
            src.ignore_chars(3)
            con.push_element md_entity('mdash')
          else
            src.ignore_chars(2)
            con.push_element md_entity('ndash')
          end
        else
          con.push_char src.shift_char
        end
      when '.' # ellipses
        if src.cur_chars_are '...'
          src.ignore_chars(3)
          con.push_element md_entity('hellip')
        elsif src.cur_chars_are '. . .'
          src.ignore_chars(5)
          con.push_element md_entity('hellip')
        else
          con.push_char src.shift_char
        end
      when '"'
        if dquote_state == :closed
          dquote_state = :open
          src.ignore_char
          con.push_element md_entity('ldquo')
        else
          dquote_state = :closed
          src.ignore_char
          con.push_element md_entity('rdquo')
        end
      when "'"
        if src.cur_chars(4) =~ /'\d\ds/ # special case: '80s
          src.ignore_char
          con.push_element md_entity('rsquo')
        elsif squote_state == :open
          squote_state = :closed unless src.next_char =~ /[[:alpha:]]/
          src.ignore_char
          con.push_element md_entity('rsquo')
        else
          if prev_char =~ /[[:alpha:]]/
            src.ignore_char
            con.push_element md_entity('rsquo')
          else
            src.ignore_char
            con.push_element md_entity('lsquo')
            squote_state = :open
          end
        end
      else # normal text
        con.push_char src.shift_char
      end # end case
      prev_char = c
    end # end while true

    con.push_string_if_present

    # Assign IAL to elements
    merge_ial(con.elements, src, con)

    # Remove leading space
    if (s = con.elements.first).kind_of? String
      if s[0, 1] == ' '
        con.elements[0] = s[1..-1]
      end
      con.elements.shift if s.empty?
    end
    
    con.elements.shift if (con.elements.first.kind_of?(String) && con.elements.first.empty?)

    # Remove final spaces
    if (s = con.elements.last).kind_of? String
      s.chop! if s[-1, 1] == ' '
      con.elements.pop if s.empty?
    end

    con.elements
  end