199 lines
4.5 KiB
Ruby
199 lines
4.5 KiB
Ruby
|
#--
|
||
|
# Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
|
||
|
#
|
||
|
# This file is part of Maruku.
|
||
|
#
|
||
|
# Maruku is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation; either version 2 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# Maruku is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with Maruku; if not, write to the Free Software
|
||
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
#++
|
||
|
|
||
|
|
||
|
module MaRuKu
|
||
|
|
||
|
class MDDocument
|
||
|
# an instance of Section (see below)
|
||
|
attr_accessor :toc
|
||
|
end
|
||
|
|
||
|
# This represents a section in the TOC.
|
||
|
class Section
|
||
|
# a Fixnum, is == header_element.level
|
||
|
attr_accessor :section_level
|
||
|
|
||
|
# An array of fixnum, like [1,2,5] for Section 1.2.5
|
||
|
attr_accessor :section_number
|
||
|
|
||
|
# reference to header (header has h.meta[:section] to self)
|
||
|
attr_accessor :header_element
|
||
|
|
||
|
# Array of immediate children of this element
|
||
|
attr_accessor :immediate_children
|
||
|
|
||
|
# Array of Section inside this section
|
||
|
attr_accessor :section_children
|
||
|
|
||
|
def initialize
|
||
|
@immediate_children = []
|
||
|
@section_children = []
|
||
|
end
|
||
|
end
|
||
|
|
||
|
class Section
|
||
|
def inspect(indent=1)
|
||
|
s = ""
|
||
|
if @header_element
|
||
|
s += "\_"*indent + "(#{@section_level})>\t #{@section_number.join('.')} : "
|
||
|
s += @header_element.children_to_s +
|
||
|
" (id: '#{@header_element.attributes[:id]}')\n"
|
||
|
else
|
||
|
s += "Master\n"
|
||
|
end
|
||
|
|
||
|
@section_children.each do |c|
|
||
|
s+=c.inspect(indent+1)
|
||
|
end
|
||
|
s
|
||
|
end
|
||
|
|
||
|
# Numerate this section and its children
|
||
|
def numerate(a=[])
|
||
|
self.section_number = a
|
||
|
section_children.each_with_index do |c,i|
|
||
|
c.numerate(a.clone.push(i+1))
|
||
|
end
|
||
|
if h = self.header_element
|
||
|
h.attributes[:section_number] = self.section_number
|
||
|
end
|
||
|
end
|
||
|
|
||
|
include REXML
|
||
|
# Creates an HTML toc.
|
||
|
# Call this on the root
|
||
|
def to_html
|
||
|
div = Element.new 'div'
|
||
|
div.attributes['class'] = 'maruku_toc'
|
||
|
div << create_toc
|
||
|
div
|
||
|
end
|
||
|
|
||
|
def create_toc
|
||
|
ul = Element.new 'ul'
|
||
|
# let's remove the bullets
|
||
|
ul.attributes['style'] = 'list-style: none;'
|
||
|
@section_children.each do |c|
|
||
|
li = Element.new 'li'
|
||
|
if span = c.header_element.render_section_number
|
||
|
li << span
|
||
|
end
|
||
|
a = c.header_element.wrap_as_element('a')
|
||
|
a.delete_attribute 'id'
|
||
|
a.attributes['href'] = "##{c.header_element.attributes[:id]}"
|
||
|
li << a
|
||
|
li << c.create_toc if c.section_children.size>0
|
||
|
ul << li
|
||
|
end
|
||
|
ul
|
||
|
end
|
||
|
|
||
|
# Creates a latex toc.
|
||
|
# Call this on the root
|
||
|
def to_latex
|
||
|
to_latex_rec + "\n\n"
|
||
|
end
|
||
|
|
||
|
def to_latex_rec
|
||
|
s = ""
|
||
|
@section_children.each do |c|
|
||
|
s += "\\noindent"
|
||
|
number = c.header_element.section_number
|
||
|
s += number if number
|
||
|
text = c.header_element.children_to_latex
|
||
|
id = c.header_element.attributes[:id]
|
||
|
s += "\\hyperlink{#{id}}{#{text}}"
|
||
|
s += "\\dotfill \\pageref*{#{id}} \\linebreak\n"
|
||
|
s += c.to_latex_rec if c.section_children.size>0
|
||
|
|
||
|
end
|
||
|
s
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
class MDDocument
|
||
|
|
||
|
def create_toc
|
||
|
each_element(:header) do |h|
|
||
|
h.attributes[:id] ||= h.generate_id
|
||
|
end
|
||
|
|
||
|
stack = []
|
||
|
|
||
|
# the ancestor section
|
||
|
s = Section.new
|
||
|
s.section_level = 0
|
||
|
|
||
|
stack.push s
|
||
|
|
||
|
i = 0;
|
||
|
while i < @children.size
|
||
|
while i < @children.size
|
||
|
if @children[i].node_type == :header
|
||
|
level = @children[i].level
|
||
|
break if level <= stack.last.section_level+1
|
||
|
end
|
||
|
|
||
|
stack.last.immediate_children.push @children[i]
|
||
|
i += 1
|
||
|
end
|
||
|
|
||
|
break if i>=@children.size
|
||
|
|
||
|
header = @children[i]
|
||
|
level = header.level
|
||
|
|
||
|
if level > stack.last.section_level
|
||
|
# this level is inside
|
||
|
|
||
|
s2 = Section.new
|
||
|
s2.section_level = level
|
||
|
s2.header_element = header
|
||
|
header.instance_variable_set :@section, s2
|
||
|
|
||
|
stack.last.section_children.push s2
|
||
|
stack.push s2
|
||
|
|
||
|
i+=1
|
||
|
elsif level == stack.last.section_level
|
||
|
# this level is a sibling
|
||
|
stack.pop
|
||
|
else
|
||
|
# this level is a parent
|
||
|
stack.pop
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
# If there is only one big header, then assume
|
||
|
# it is the master
|
||
|
if s.section_children.size == 1
|
||
|
s = s.section_children.first
|
||
|
end
|
||
|
|
||
|
# Assign section numbers
|
||
|
s.numerate
|
||
|
|
||
|
s
|
||
|
end
|
||
|
end
|
||
|
end
|