|
#!/usr/bin/ruby
|
|
|
|
class HtmlBuilder::StackingTable
|
|
attr_accessor :inner_table
|
|
|
|
def initialize(caption = nil, html_options = {})
|
|
@caption = caption
|
|
@html_options = html_options
|
|
@inner_table = false
|
|
end
|
|
|
|
def build
|
|
options = ""
|
|
@html_options.each_pair {|opt, val| options += " #{opt}=\"#{val}\"" }
|
|
html = "<table#{options}>\n"
|
|
t = create_table()
|
|
t.inner_table = @inner_table
|
|
yield(t)
|
|
t.calculate_depths
|
|
t.propagate_parameters
|
|
html += t.build
|
|
html += "</table>\n"
|
|
return html
|
|
end
|
|
|
|
def create_table
|
|
Table.new(@caption)
|
|
end
|
|
|
|
class Container
|
|
def initialize(name)
|
|
@name = name
|
|
@elements = []
|
|
@x_depth = 0
|
|
@y_depth = 0
|
|
@fill_x_depth = nil
|
|
end
|
|
|
|
def content(text, html_options = {})
|
|
text = [text] unless text.kind_of? Array
|
|
|
|
if html_options.empty?
|
|
@elements << text
|
|
else
|
|
options = ""
|
|
html_options.each_pair {|opt, val| options += " #{opt}=\"#{val}\"" }
|
|
prepared_text = text.collect {|t| "<span#{options}>#{t}</span>" }
|
|
@elements << prepared_text
|
|
end
|
|
end
|
|
|
|
def category(cat_name)
|
|
c = create_category(cat_name)
|
|
yield(c)
|
|
@elements << c
|
|
end
|
|
|
|
def calculate_depths
|
|
y_depth = 0
|
|
@elements.each do |el|
|
|
if el.respond_to? :build
|
|
e_x_depth, e_y_depth = el.calculate_depths
|
|
x_depth = e_x_depth + 1
|
|
y_depth += e_y_depth
|
|
elsif el.kind_of? Array
|
|
x_depth = el.size
|
|
y_depth += 1
|
|
else
|
|
x_depth = 1
|
|
y_depth += 1
|
|
end
|
|
@x_depth = x_depth if x_depth > @x_depth
|
|
@y_depth = y_depth
|
|
end
|
|
|
|
# in case category is empty
|
|
@y_depth = [1, @y_depth].max
|
|
|
|
return @x_depth, @y_depth
|
|
end
|
|
|
|
def propagate_parameters(depth = nil)
|
|
if depth.nil?
|
|
depth = @x_depth
|
|
else
|
|
depth -= 1
|
|
end
|
|
|
|
@fill_x_depth = depth
|
|
|
|
@elements.each do |el|
|
|
el.propagate_parameters(depth) if el.respond_to? :build
|
|
end
|
|
end
|
|
|
|
def build
|
|
lines = fetch_lines()
|
|
options = (@y_depth > 1) ? " rowspan=\"#{@y_depth}\"" : ""
|
|
lines << [] if lines.empty?
|
|
lines.first.unshift "<th#{options}>#{@name}</th>"
|
|
return lines
|
|
end
|
|
|
|
private
|
|
|
|
def create_category(cat_name)
|
|
Category.new(cat_name)
|
|
end
|
|
|
|
def fetch_lines
|
|
lines = []
|
|
@elements.each do |el|
|
|
if el.respond_to? :build
|
|
lines += el.build
|
|
elsif el.kind_of? Array
|
|
l = ""
|
|
last_el = el.pop
|
|
x_depth_left = @fill_x_depth - el.size
|
|
first_el = el.shift
|
|
tag = ((self.kind_of?(Table) and not @inner_table) ? "th" : "td")
|
|
l += "<#{tag}>#{first_el}</#{tag}>" unless first_el.nil?
|
|
el.each {|s_el| l += "<td>#{s_el}</td>"}
|
|
options = (x_depth_left > 1) ? " colspan=\"#{x_depth_left}\"" : ""
|
|
l += "<td#{options}>#{last_el}</td>" unless last_el.nil?
|
|
lines << [ l ]
|
|
else
|
|
options = (@x_depth > 1) ? " colspan=\"#{@x_depth}\"" : ""
|
|
lines << [ "<td#{options}>#{el}</td>" ]
|
|
end
|
|
end
|
|
return lines
|
|
end
|
|
end
|
|
|
|
class Category < Container
|
|
end
|
|
|
|
class Table < Container
|
|
attr_accessor :inner_table
|
|
|
|
def initialize(name = nil)
|
|
super
|
|
|
|
@inner_table = false
|
|
end
|
|
|
|
def build
|
|
html = ""
|
|
html += "<caption>#{@name}</caption>\n" if @name
|
|
lines = fetch_lines()
|
|
lines.each {|l| html += "<tr>#{l}</tr>\n"}
|
|
return html
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
# example
|
|
if $0 == __FILE__
|
|
h = {"z" => "PLOP", "coin" => {"toto" => "erb", "titi" => {"gtk" => "gh"}}}
|
|
|
|
z = StackingTable.build("Super Caption !!!") do |table|
|
|
table.content("coucou")
|
|
table.category("Cat 1") do |cat|
|
|
cat.content("plop")
|
|
cat.content("toto")
|
|
cat.category("Cat 2") do |cat|
|
|
cat.content("titi")
|
|
end
|
|
end
|
|
end
|
|
|
|
puts z
|
|
end
|
|
|