module Temple
module HTML
# @api public
class Fast < Filter
XHTML_DOCTYPES = {
'1.1' => '',
'5' => '',
'html' => '',
'strict' => '',
'frameset' => '',
'mobile' => '',
'basic' => '',
'transitional' => '',
}.freeze
HTML4_DOCTYPES = {
'strict' => '',
'frameset' => '',
'transitional' => '',
}.freeze
set_default_options :format => :xhtml,
:attr_wrapper => "'",
:autoclose => %w[meta img link br hr input area param col base],
:attr_delimiter => {'id' => '_', 'class' => ' '}
def initialize(opts = {})
super
# html5 is now called html only
options[:format] = :html5 if options[:format] == :html
unless [:xhtml, :html4, :html5].include?(options[:format])
raise "Invalid format #{options[:format].inspect}"
end
end
def xhtml?
options[:format] == :xhtml
end
def html?
options[:format] == :html5 || options[:format] == :html4
end
def on_html_doctype(type)
type = type.to_s
trailing_newlines = type[/(\A|[^\r])(\n+)\Z/, 2].to_s
text = type.downcase.strip
if text =~ /^xml/
raise 'Invalid xml directive in html mode' if html?
wrapper = options[:attr_wrapper]
str = ""
else
case options[:format]
when :html5
str = ''
when :html4
str = HTML4_DOCTYPES[text] || HTML4_DOCTYPES['transitional']
when :xhtml
str = XHTML_DOCTYPES[text] || XHTML_DOCTYPES['transitional']
end
end
str << trailing_newlines
[:static, str]
end
def on_html_comment(content)
[:multi,
[:static, '']]
end
def on_html_tag(name, attrs, content = nil)
name = name.to_s
closed = !content || (empty_exp?(content) && options[:autoclose].include?(name))
result = [:multi, [:static, "<#{name}"], compile(attrs)]
result << [:static, (closed && xhtml? ? ' /' : '') + '>']
result << compile(content) if content
result << [:static, "#{name}>"] if !closed
result
end
def on_html_attrs(*attrs)
result = {}
attrs.each do |attr|
raise(InvalidExpression, 'Attribute is not a html attr') if attr[0] != :html || attr[1] != :attr
name, value = attr[2].to_s, attr[3]
next if empty_exp?(value)
if result[name]
delimiter = options[:attr_delimiter][name]
raise "Multiple #{name} attributes specified" unless delimiter
if contains_static?(value)
result[name] = [:html, :attr, name,
[:multi,
result[name][3],
[:static, delimiter],
value]]
else
tmp = unique_name
result[name] = [:html, :attr, name,
[:multi,
result[name][3],
[:capture, tmp, value],
[:if, "!#{tmp}.empty?",
[:multi,
[:static, delimiter],
[:dynamic, tmp]]]]]
end
else
result[name] = attr
end
end
[:multi, *result.sort.map {|name,attr| compile(attr) }]
end
def on_html_attr(name, value)
if empty_exp?(value)
compile(value)
elsif contains_static?(value)
attribute(name, value)
else
tmp = unique_name
[:multi,
[:capture, tmp, compile(value)],
[:if, "!#{tmp}.empty?",
attribute(name, [:dynamic, tmp])]]
end
end
protected
def attribute(name, value)
[:multi,
[:static, " #{name}=#{options[:attr_wrapper]}"],
compile(value),
[:static, options[:attr_wrapper]]]
end
def contains_static?(exp)
case exp[0]
when :multi
exp[1..-1].any? {|e| contains_static?(e) }
when :escape
contains_static?(exp[2])
when :static
true
else
false
end
end
end
end
end