2006-08-06 03:18:54 +00:00
|
|
|
require File.dirname(__FILE__) + '/helpers'
|
|
|
|
|
2006-09-12 13:05:28 +00:00
|
|
|
module Haml #:nodoc:
|
2006-08-06 03:18:54 +00:00
|
|
|
class Engine
|
2006-09-12 04:14:21 +00:00
|
|
|
include Haml::Helpers
|
2006-09-29 19:59:21 +00:00
|
|
|
|
2006-09-12 04:14:21 +00:00
|
|
|
# Set the maximum length for a line to be considered a one-liner
|
2006-09-12 13:05:28 +00:00
|
|
|
# Lines <= the maximum will be rendered on one line,
|
|
|
|
# i.e. <tt><p>Hello world</p></tt>
|
2006-09-29 19:20:53 +00:00
|
|
|
ONE_LINER_LENGTH = 50
|
|
|
|
SPECIAL_CHARACTERS = %w(# . = ~ % /).collect { |c| c[0] }
|
2006-09-22 02:18:43 +00:00
|
|
|
MULTILINE_CHAR_VALUE = '|'[0]
|
2006-09-29 19:20:53 +00:00
|
|
|
MULTILINE_STARTERS = SPECIAL_CHARACTERS - ["/"[0]]
|
2006-09-29 18:39:13 +00:00
|
|
|
|
2006-09-29 19:57:35 +00:00
|
|
|
def initialize(template, options = {})
|
|
|
|
#turn each of the options into instance variables for the object
|
|
|
|
options.each { |k,v| eval("@#{k} = v") }
|
|
|
|
|
2006-09-29 18:39:13 +00:00
|
|
|
@template = template #String
|
2006-09-29 19:59:21 +00:00
|
|
|
@result, @to_close_queue = String.new, []
|
2006-09-12 04:14:21 +00:00
|
|
|
end
|
2006-09-29 19:59:21 +00:00
|
|
|
|
2006-09-29 18:39:13 +00:00
|
|
|
def to_html
|
|
|
|
# Process each line of the template
|
|
|
|
@template.each_with_index do |line, index|
|
2006-09-12 04:14:21 +00:00
|
|
|
count, line = count_soft_tabs(line)
|
2006-09-22 02:18:43 +00:00
|
|
|
surpress_render, line, count = handle_multiline(count, line)
|
|
|
|
|
|
|
|
if !surpress_render && count && line
|
|
|
|
count, line = process_line(count, line)
|
2006-07-02 02:09:44 +00:00
|
|
|
end
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
2006-09-29 18:39:13 +00:00
|
|
|
|
2006-09-12 04:14:21 +00:00
|
|
|
# Close all the open tags
|
2006-07-02 02:09:44 +00:00
|
|
|
@to_close_queue.length.times { close_tag }
|
2006-09-29 18:39:13 +00:00
|
|
|
|
2006-09-12 04:14:21 +00:00
|
|
|
# Return the result string
|
2006-06-30 15:14:44 +00:00
|
|
|
@result
|
|
|
|
end
|
2006-06-30 21:40:07 +00:00
|
|
|
|
2006-09-22 02:18:43 +00:00
|
|
|
def process_line(count, line)
|
|
|
|
if line.strip[0, 3] == '!!!'
|
|
|
|
@result << %|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n|
|
|
|
|
else
|
|
|
|
if count <= @to_close_queue.size && @to_close_queue.size > 0
|
|
|
|
(@to_close_queue.size - count).times { close_tag }
|
|
|
|
end
|
2006-09-29 19:20:53 +00:00
|
|
|
|
|
|
|
case line[0..0]
|
2006-09-22 02:18:43 +00:00
|
|
|
when '.', '#'
|
|
|
|
render_div(line)
|
|
|
|
when '%'
|
|
|
|
render_tag(line)
|
|
|
|
when '/'
|
|
|
|
render_comment(line)
|
|
|
|
when '='
|
2006-09-29 19:57:35 +00:00
|
|
|
add template_eval(line[1, line.length]).to_s
|
2006-09-22 02:18:43 +00:00
|
|
|
when '~'
|
2006-09-29 19:57:35 +00:00
|
|
|
add find_and_flatten(template_eval(line[1, line.length])).to_s
|
2006-09-22 02:18:43 +00:00
|
|
|
else
|
|
|
|
add line.strip
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return count, line
|
|
|
|
end
|
|
|
|
|
|
|
|
def handle_multiline(count, line)
|
2006-09-29 18:39:13 +00:00
|
|
|
# Multilines are denoting by ending with a `|` (124)
|
2006-09-29 19:20:53 +00:00
|
|
|
if (line[-1] == MULTILINE_CHAR_VALUE) && @multiline_buffer
|
2006-09-22 02:18:43 +00:00
|
|
|
# A multiline string is active, and is being continued
|
|
|
|
@multiline_buffer += line[0...-1]
|
|
|
|
supress_render = true
|
2006-09-29 19:20:53 +00:00
|
|
|
elsif (line[-1] == MULTILINE_CHAR_VALUE) && (MULTILINE_STARTERS.include? line[0])
|
2006-09-22 02:18:43 +00:00
|
|
|
# A multiline string has just been activated, start adding the lines
|
|
|
|
@multiline_buffer = line[0...-1]
|
|
|
|
@multiline_count = count
|
|
|
|
supress_render = true
|
|
|
|
elsif @multiline_buffer
|
|
|
|
# A multiline string has just ended, make line into the result
|
|
|
|
process_line(@multiline_count, @multiline_buffer)
|
|
|
|
@multiline_buffer = nil
|
|
|
|
supress_render = false
|
|
|
|
end
|
2006-09-29 18:39:13 +00:00
|
|
|
|
2006-09-22 02:18:43 +00:00
|
|
|
return supress_render, line, count
|
|
|
|
end
|
|
|
|
|
2006-06-30 15:14:44 +00:00
|
|
|
def add(line)
|
2006-09-12 04:14:21 +00:00
|
|
|
return if line.nil?
|
|
|
|
line.to_s.each_line do |me|
|
|
|
|
@result << tabs(@to_close_queue.size) << me.chomp << "\n"
|
|
|
|
end
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
2006-08-06 03:18:54 +00:00
|
|
|
|
2006-09-29 18:39:13 +00:00
|
|
|
def build_attributes(attributes = {})
|
2006-09-29 19:57:35 +00:00
|
|
|
result = attributes.collect { |a,v|
|
|
|
|
unless v.nil?
|
|
|
|
first_quote_type = v.to_s.scan(/['"]/).first
|
|
|
|
quote_type = (first_quote_type == "'") ? '"' : "'"
|
|
|
|
"#{a.to_s}=#{quote_type}#{v.to_s}#{quote_type}"
|
|
|
|
end
|
|
|
|
}
|
|
|
|
result = result.compact.join(' ')
|
|
|
|
(attributes.empty? ? String.new : String.new(' ')) + result
|
2006-09-29 18:39:13 +00:00
|
|
|
end
|
|
|
|
|
2006-06-30 15:14:44 +00:00
|
|
|
def open_tag(name, attributes = {})
|
2006-07-19 22:23:01 +00:00
|
|
|
add "<#{name.to_s}#{build_attributes(attributes)}>"
|
2006-09-12 04:14:21 +00:00
|
|
|
@to_close_queue.push name
|
2006-07-19 22:23:01 +00:00
|
|
|
end
|
|
|
|
|
2006-09-29 18:39:13 +00:00
|
|
|
def close_tag
|
|
|
|
add "</#{@to_close_queue.pop}>"
|
|
|
|
end
|
|
|
|
|
2006-07-19 22:23:01 +00:00
|
|
|
def one_line_tag(name, value, attributes = {})
|
|
|
|
add "<#{name.to_s}#{build_attributes(attributes)}>#{value}</#{name.to_s}>"
|
|
|
|
end
|
2006-08-06 03:18:54 +00:00
|
|
|
|
2006-09-29 18:39:13 +00:00
|
|
|
def one_liner?(value)
|
|
|
|
value.length <= ONE_LINER_LENGTH && value.scan(/\n/).empty?
|
|
|
|
end
|
|
|
|
|
2006-07-20 04:01:23 +00:00
|
|
|
def print_tag(name, value, attributes = {})
|
|
|
|
unless value.empty?
|
2006-09-12 04:14:21 +00:00
|
|
|
if one_liner? value
|
2006-07-20 04:01:23 +00:00
|
|
|
one_line_tag(name, value, attributes)
|
|
|
|
else
|
|
|
|
open_tag(name, attributes)
|
2006-09-12 04:14:21 +00:00
|
|
|
add value
|
2006-07-20 04:01:23 +00:00
|
|
|
close_tag
|
|
|
|
end
|
|
|
|
else
|
|
|
|
open_tag(name, attributes)
|
2006-09-12 04:14:21 +00:00
|
|
|
add value
|
2006-07-20 04:01:23 +00:00
|
|
|
end
|
|
|
|
end
|
2006-07-19 22:23:01 +00:00
|
|
|
|
2006-09-12 13:05:28 +00:00
|
|
|
# Creates single line tags, i.e. <tt><hello /></tt>
|
2006-07-19 22:23:01 +00:00
|
|
|
def atomic_tag(name, attributes = {})
|
|
|
|
add "<#{name.to_s}#{build_attributes(attributes)} />"
|
|
|
|
end
|
|
|
|
|
2006-09-29 18:39:13 +00:00
|
|
|
def parse_class_and_id(list)
|
|
|
|
attributes = {}
|
|
|
|
list.scan(/([#.])([-a-zA-Z_()]+)/).each do |type, property|
|
|
|
|
case type
|
|
|
|
when '.'
|
|
|
|
attributes[:class] = property
|
|
|
|
when '#'
|
|
|
|
attributes[:id] = property
|
|
|
|
end
|
|
|
|
end
|
|
|
|
attributes
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
2006-08-06 03:18:54 +00:00
|
|
|
|
2006-06-30 21:40:07 +00:00
|
|
|
def render_tag(line)
|
2006-09-14 19:09:49 +00:00
|
|
|
line.scan(/[%]([-_a-z1-9]+)([-_a-z\.\#]*)(\{.*\})?(\[.*\])?([=\/\~]?)?(.*)?/).each do |tag_name, attributes, attributes_hash, object_ref, action, value|
|
2006-08-13 22:02:04 +00:00
|
|
|
attributes = parse_class_and_id(attributes.to_s)
|
2006-09-29 18:39:13 +00:00
|
|
|
|
2006-09-29 18:54:25 +00:00
|
|
|
#SimplyHelpful style logic with the [@model] helper
|
2006-09-29 19:57:35 +00:00
|
|
|
if object_ref && (object_ref = template_eval(object_ref).first)
|
|
|
|
class_name = object_ref.class.to_s.underscore
|
|
|
|
attributes.merge!(:id => "#{class_name}_#{object_ref.id}", :class => class_name)
|
2006-09-29 18:39:13 +00:00
|
|
|
end
|
2006-09-29 18:54:25 +00:00
|
|
|
|
|
|
|
unless (attributes_hash.nil? || attributes_hash.empty?)
|
|
|
|
# Determine whether to eval the attributes hash in the context of a template
|
2006-09-29 19:57:35 +00:00
|
|
|
add_attributes = template_eval(attributes_hash)
|
2006-09-29 18:54:25 +00:00
|
|
|
attributes.merge!(add_attributes)
|
|
|
|
end
|
|
|
|
|
2006-09-29 18:39:13 +00:00
|
|
|
case action
|
|
|
|
when '/'
|
2006-07-20 04:01:23 +00:00
|
|
|
atomic_tag(tag_name, attributes)
|
2006-09-29 18:39:13 +00:00
|
|
|
when '=', '~'
|
2006-09-29 19:57:35 +00:00
|
|
|
value = template_eval(value)
|
|
|
|
value = find_and_flatten(value) if action == '~'
|
|
|
|
print_tag(tag_name, value.to_s, attributes)
|
2006-07-19 22:23:01 +00:00
|
|
|
else
|
2006-08-06 03:18:54 +00:00
|
|
|
print_tag(tag_name, value.to_s.strip, attributes)
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2006-07-20 04:01:23 +00:00
|
|
|
|
2006-09-29 18:39:13 +00:00
|
|
|
def render_div(line)
|
|
|
|
render_tag('%div' + line)
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
2006-08-09 18:12:54 +00:00
|
|
|
|
2006-09-29 18:39:13 +00:00
|
|
|
def render_comment(line)
|
|
|
|
add "<!-- #{line[1..line.length].strip} -->"
|
2006-07-20 04:01:23 +00:00
|
|
|
end
|
2006-09-29 18:39:13 +00:00
|
|
|
|
2006-09-12 04:14:21 +00:00
|
|
|
def template_eval(args)
|
2006-09-29 19:57:35 +00:00
|
|
|
!@supress_eval ? @scope_object.instance_eval(args) : ""
|
2006-07-20 04:01:23 +00:00
|
|
|
end
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
2006-09-14 19:12:45 +00:00
|
|
|
end
|