module AsciiMath
class HTMLBuilder
def initialize(prefix)
@prefix = prefix
@html = ''
end
def to_s
@html
end
def append_expression(expression, inline, attrs = {})
if inline
inline('', attrs) do
append(expression, :single_child => true)
end
else
block('', attrs) do
append(expression, :single_child => true)
end
end
end
private
ZWJ = "\u8205"
def append(expression, opts = {})
case expression
when Array
row do
expression.each { |e| append(e) }
end
when Hash
case expression[:type]
when :operator
operator(expression[:c])
when :identifier
identifier(expression[:c])
when :number
number(expression[:c])
when :text
text(expression[:c])
when :paren
paren = !opts[:strip_paren]
if paren
if opts[:single_child]
brace(expression[:lparen]) if expression[:lparen]
append(expression[:e], :single_child => true)
brace(expression[:rparen]) if expression[:rparen]
else
row do
brace(expression[:lparen]) if expression[:lparen]
append(expression[:e], :single_child => true)
brace(expression[:rparen]) if expression[:rparen]
end
end
else
append(expression[:e])
end
when :font
#TODO - currently ignored
when :unary
operator = expression[:operator]
tag(operator) do
append(expression[:s], :single_child => true, :strip_paren => true)
end
when :binary
operator = expression[:operator]
if operator == :frac
append_fraction(expression[:s1],expression[:s2])
elsif operator == :sub
append_subsup(expression[:s1],expression[:s2],nil)
elsif operator == :sup
append_subsup(expression[:s1],nil,expression[:s2])
elsif operator == :under
append_underover(expression[:s1],expression[:s2],nil)
elsif operator == :over
append_underover(expression[:s1],nil,expression[:s2])
else
tag(operator) do
append(expression[:s1], :strip_paren => true)
append(expression[:s2], :strip_paren => true)
end
end
when :ternary
operator = expression[:operator]
if operator == :subsup
append_subsup(expression[:s1],expression[:s2],expression[:s3])
elsif operator == :underover
# TODO: Handle over/under braces in some way? SVG maybe?
append_underover(expression[:s1],expression[:s2],expression[:s3])
end
when :matrix
row do
# Figures out a font size for the braces, based on the height of the matrix.
# NOTE: This does not currently consider the size of each element within the matrix.
brace_height = "font-size: " + expression[:rows].length.to_s + "00%;"
if expression[:lparen]
brace(expression[:lparen], {:style => brace_height})
else
blank(ZWJ)
end
matrix_width = "grid-template-columns:repeat(" + expression[:rows][0].length.to_s + ",1fr);"
matrix_height = "grid-template-rows:repeat(" + expression[:rows].length.to_s + ",1fr);"
matrix({:style => (matrix_width + matrix_height)}) do
expression[:rows].each do |row|
row.each do |col|
row do
append(col)
end
end
end
end
if expression[:rparen]
brace(expression[:rparen], {:style => brace_height})
else
blank(ZWJ)
end
end
end
end
end
def append_subsup(base, sub, sup)
append(base)
subsup do
if sup
smaller do
append(sup, :strip_paren => true)
end
else
smaller(ZWJ)
end
if sub
smaller do
append(sub, :strip_paren => true)
end
else
smaller(ZWJ)
end
end
end
def append_underover(base, under, over)
blank(ZWJ)
underover do
smaller do
if over
append(over, :strip_paren => true)
else
blank(ZWJ)
end
end
append(base)
smaller do
if under
append(under, :strip_paren => true)
else
blank(ZWJ)
end
end
end
end
def append_fraction(numerator, denominator)
blank(ZWJ)
fraction do
fraction_row do
fraction_cell do
smaller do
row do
append(numerator, :strip_paren => true)
end
end
end
end
fraction_row do
fraction_cell do
smaller do
row do
append(denominator, :strip_paren => true)
end
end
end
end
end
end
def method_missing(meth, *args, &block)
tag(meth, *args, &block)
end
def tag(tag, *args)
attrs = args.last.is_a?(Hash) ? args.pop : {}
text = args.last.is_a?(String) ? args.pop : ''
@html << ''
@html << text.encode(Encoding::US_ASCII, :xml => :text) if text
yield if block_given?
@html << ''
else
@html << '/>'
end
end
end
class Expression
def to_html(prefix = "", inline = true, attrs = {})
HTMLBuilder.new(prefix).append_expression(@parsed_expression, inline, attrs).to_s
end
end
end