#This class can be used to generate HTML.
#===Examples
# ele = Html_gen::Element.new(:a) #=> #
# ele.classes << "custom_link"
# ele.css["font-weight"] = "bold"
# ele.attr[:href] = "http://www.youtube.com"
#
# b = ele.add_ele(:b)
# b.str = "Title of link"
#
# ele.html #=> "\n\t\n\t\tTitle of link\n\t\n\n"
class Html_gen::Element
FORBIDDEN_SHORT = ["script"]
#Attributes hash which will be used to generate attributes-elements.
#===Example
# element.attr[:href] = "http://www.youtube.com"
attr_reader :attr
#CSS-hash which will be used to generate the 'style'-attribute.
#===Example
# element.css["font-weight"] = "bold"
attr_reader :css
#Classes-array which will be used to generate the 'class'-attribute.
#===Example
# element.classes += ["class1", "class2"]
# element.html #=> ... class="class1 class2"...
attr_reader :classes
#This string is used to generate the value of an element. It will be HTML-escaped.
#===Example
# element = Html_gen::Element.new("b")
# element.str = "Test"
# element.html(:pretty => false) #=> "Te<i>s</i>t"
attr_accessor :str
#This string is used to generate the value of an element. It will not be HTML-escaped.
#===Example
# element = Html_gen::Element.new("b")
# element.str_html = "Test"
# element.html #=> "Test"
attr_accessor :str_html
#An array holding all the sub-elements of this element.
attr_accessor :eles
#The name of the element. "a" for and such.
attr_accessor :name
#You can give various arguments as shortcuts to calling the methods. You can also decide what should be used for newline and indentation.
# Html_gen::Element.new(:b, {
# :css => {"font-weight" => "bold"},
# :attr => {"href" => "http://www.youtube.com"},
# :classes => ["class1", "class2"],
# :str => "A title",
# :str_html => "Some custom URL as title",
# :nl => "\n",
# :inden => "\t",
# :eles => [Html_gen::Element.new("i", :str => "Hello world!")
# })
def initialize(name, args = {})
raise "'name' should be a string or a symbol but was a '#{name.class.name}'."if !name.is_a?(String) and !name.is_a?(Symbol)
@name = name
{:attr => {}, :classes => [], :str_html => "", :str => "", :css => {}, :eles => [], :nl => "\n", :inden => "\t"}.each do |arg, default_val|
if args[arg]
instance_variable_set("@#{arg}", args[arg])
else
instance_variable_set("@#{arg}", default_val)
end
args.delete(arg)
end
raise "Unknown arguments: '#{args.keys.join(",")}'." if !args.empty?
end
#Adds a sub-element to the element.
#===Examples
# element = Html_gen::Element.new("a")
# another_ele = element.add_ele("b")
# another_ele.str = "Hello world!"
# element.html #=> "\n\t\n\t\tHello world!\n\t\n\n"
def add_ele(name, args = {})
ele = Html_gen::Element.new(name, args.merge(:nl => @nl, :inden => @inden))
@eles << ele
return ele
end
alias add add_ele
#Add a text-element to the element.
def add_str(str)
ele = Html_gen::Text_ele.new(:str => str, :inden => @inden, :nl => @nl)
@eles << ele
return ele
end
# Returns the HTML for the element.
# To avoid indentation and newlines you can use the 'pretty'-argument:
# element.html(:pretty => false)
def html(args = {})
if args[:level]
level = args[:level]
else
level = 0
end
if args.key?(:pretty)
pretty = args[:pretty]
else
pretty = true
end
#Used for keeping 'pretty'-value and correct indentation according to parent elements.
pass_args = {:level => (level + 1), :pretty => pretty, :inden => @inden}
#Clone the attributes-hash since we are going to add stuff to it, and it shouldnt be reflected (if 'html' is called multiple times, it will bug unless we clone).
attr = @attr.clone
#Start generating the string with HTML (possible go give a custom 'str'-variable where the content should be pushed to).
if args[:str]
str = args[:str]
else
str = ""
end
str << @inden * level if pretty and level > 0
str << "<#{@name}"
#Add content from the 'css'-hash to the 'style'-attribute in the right format.
if !@css.empty?
style = ""
@css.each do |key, val|
style << "; " if !style.empty?
style << "#{key}: #{val};"
end
if attr[:style] and !attr[:style].empty?
attr[:style] << "; "
attr[:style] << style
else
attr[:style] = style
end
end
#Add content from the 'classes'-array to the 'class'-attribute in the right format.
if !@classes.empty?
class_str = @classes.join(" ")
if @attr[:class] and !@attr[:class].empty?
attr[:class] << " #{class_str}"
else
attr[:class] = class_str
end
end
#Write out the attributes to the string.
attr.each do |key, val|
str << " #{key}=\"#{Html_gen.escape_html(val)}\""
end
forbidden_short = FORBIDDEN_SHORT.include?(@name.to_s)
skip_pretty = false
if @eles.empty? and @str.empty? and @str_html.empty? and !forbidden_short
#If no sub-string, sub-HTML or sub-elements are given, we should end the HTML with " />".
str << " />"
str << @nl if pretty
else
#Write end-of-element and then all sub-elements.
str << ">"
if @eles.empty? and @str.empty? and @str_html.empty? and forbidden_short
skip_pretty = true
end
str << @nl if pretty and !skip_pretty
if !@str.empty?
str << @inden * (level + 1) if pretty
str << Html_gen.escape_html(@str)
str << @nl if pretty
end
if !@str_html.empty?
str << @inden * (level + 1) if pretty
str << @str_html
str << @nl if pretty
end
@eles.each do |subele|
str << subele.html(pass_args)
end
str << @inden * level if pretty and level > 0 and !skip_pretty
str << "#{@name}>"
str << @nl if pretty
end
#Returns the string.
return str
end
#Returns the names of all sub-elements in an array.
def eles_names
names = []
@eles.each do |ele|
names << ele.name
end
return names
end
#Converts the content of the 'style'-attribute to css-hash-content.
def convert_style_to_css
if !@attr[:style].to_s.strip.empty?
style = @attr[:style]
elsif !@attr["style"].to_s.strip.empty?
style = @attr["style"]
else
raise "No style set in element."
end
loop do
if match = style.match(/\A\s*(\S+?):\s*(.+?)\s*(;|\Z)/)
style.gsub!(match[0], "")
key = match[1]
val = match[2]
raise "Such a key already exists in CSS-hash: '#{key}'." if @css.key?(key)
@css[key] = val
elsif match = style.slice!(/\A\s*\Z/)
break
else
raise "Dont know what to do with style-variable: '#{style}'."
end
end
end
end