2006-08-06 03:18:54 +00:00
|
|
|
require File.dirname(__FILE__) + '/helpers'
|
|
|
|
|
2006-09-12 04:14:21 +00:00
|
|
|
module Haml
|
2006-08-06 03:18:54 +00:00
|
|
|
class Engine
|
2006-09-12 04:14:21 +00:00
|
|
|
include Haml::Helpers
|
|
|
|
|
|
|
|
# Set the maximum length for a line to be considered a one-liner
|
|
|
|
# Lines <= the maximum will be rendered on one line, i.e. +<p>Hello world</p>+
|
|
|
|
ONE_LINER_LENGTH = 50
|
|
|
|
|
|
|
|
def initialize(view)
|
|
|
|
@view = view
|
|
|
|
@result = String.new
|
|
|
|
@to_close_queue = []
|
|
|
|
end
|
|
|
|
|
|
|
|
def render(template, local_assigns={})
|
|
|
|
assigns = @view.assigns.dup
|
|
|
|
|
|
|
|
# Do content for layout on its own to keep things working in partials
|
|
|
|
if content_for_layout = @view.instance_variable_get("@content_for_layout")
|
|
|
|
assigns['content_for_layout'] = content_for_layout
|
2006-07-23 13:57:44 +00:00
|
|
|
end
|
2006-08-06 03:18:54 +00:00
|
|
|
|
2006-09-12 04:14:21 +00:00
|
|
|
# Get inside the view object's world
|
|
|
|
@view.instance_eval do
|
|
|
|
# Set all the instance variables
|
|
|
|
assigns.each do |key,val|
|
|
|
|
instance_variable_set "@#{key}", val
|
|
|
|
end
|
|
|
|
|
|
|
|
# Set all the local assigns
|
|
|
|
local_assigns.each do |key,val|
|
|
|
|
class << self; self; end.send(:define_method, key) { val }
|
|
|
|
end
|
|
|
|
end
|
2006-08-06 03:18:54 +00:00
|
|
|
|
2006-09-12 04:14:21 +00:00
|
|
|
# Process each line of the template returning the resuting string
|
|
|
|
template.split(/\n/).map do |line|
|
|
|
|
count, line = count_soft_tabs(line)
|
|
|
|
|
|
|
|
if 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
|
|
|
|
case line.first
|
2006-07-02 02:09:44 +00:00
|
|
|
when '.', '#'
|
|
|
|
render_div(line)
|
|
|
|
when '%'
|
|
|
|
render_tag(line)
|
|
|
|
when '/'
|
|
|
|
render_comment(line)
|
|
|
|
when '='
|
2006-09-12 04:14:21 +00:00
|
|
|
add template_eval(line[1, line.length]).to_s
|
2006-08-08 13:42:19 +00:00
|
|
|
when '~'
|
2006-09-12 04:14:21 +00:00
|
|
|
add find_and_flatten(template_eval(line[1, line.length])).to_s
|
2006-07-02 02:09:44 +00:00
|
|
|
else
|
2006-08-06 03:18:54 +00:00
|
|
|
add line.strip
|
2006-07-02 02:09:44 +00:00
|
|
|
end
|
2006-09-12 04:14:21 +00:00
|
|
|
end
|
2006-07-02 02:09:44 +00:00
|
|
|
end
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
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-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-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-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
|
|
|
|
|
|
|
|
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-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 04:14:21 +00:00
|
|
|
# Creates single line tags, i.e. +<hello />+
|
2006-07-19 22:23:01 +00:00
|
|
|
def atomic_tag(name, attributes = {})
|
|
|
|
add "<#{name.to_s}#{build_attributes(attributes)} />"
|
|
|
|
end
|
|
|
|
|
|
|
|
def build_attributes(attributes = {})
|
2006-09-12 04:14:21 +00:00
|
|
|
attributes.empty? ? String.new : String.new(' ') << (attributes.collect {|a,v| "#{a.to_s}='#{v.to_s}'" unless v.nil? }).compact.join(' ')
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
2006-08-06 03:18:54 +00:00
|
|
|
|
2006-06-30 15:14:44 +00:00
|
|
|
def close_tag
|
2006-09-12 04:14:21 +00:00
|
|
|
add "</#{@to_close_queue.pop}>"
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
2006-08-06 03:18:54 +00:00
|
|
|
|
2006-06-30 15:14:44 +00:00
|
|
|
def render_div(line)
|
2006-09-12 04:14:21 +00:00
|
|
|
render_tag('%div' + line)
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
2006-08-06 03:18:54 +00:00
|
|
|
|
2006-06-30 15:14:44 +00:00
|
|
|
def render_comment(line)
|
2006-08-06 03:18:54 +00:00
|
|
|
add "<!-- #{line[1..line.length].strip} -->"
|
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-12 04:14:21 +00:00
|
|
|
line.scan(/[%]([-_a-z1-9]+)([-_a-z\.\#]*)(\{.*\})?([=\/\~]?)?(.*)?/).each do |tag_name, attributes, attributes_hash, action, value|
|
2006-08-13 22:02:04 +00:00
|
|
|
attributes = parse_class_and_id(attributes.to_s)
|
2006-07-31 14:28:44 +00:00
|
|
|
attributes.merge!(template_eval(attributes_hash)) unless (attributes_hash.nil? || attributes_hash.empty?)
|
|
|
|
|
2006-09-12 04:14:21 +00:00
|
|
|
if action == '\/'
|
2006-07-20 04:01:23 +00:00
|
|
|
atomic_tag(tag_name, attributes)
|
2006-09-12 04:14:21 +00:00
|
|
|
elsif action == '=' || action == '~'
|
2006-07-20 21:39:06 +00:00
|
|
|
value = template_eval(value)
|
2006-09-12 04:14:21 +00:00
|
|
|
value = find_and_flatten(value) if action == '~'
|
|
|
|
print_tag(tag_name, value.to_s, attributes) if value
|
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-12 04:14:21 +00:00
|
|
|
# Search for `#` and `.` characters indicating id and class attributes
|
|
|
|
# respectively
|
|
|
|
#
|
|
|
|
# Returns the attributes hash
|
2006-08-13 22:02:04 +00:00
|
|
|
def parse_class_and_id(list)
|
2006-06-30 15:14:44 +00:00
|
|
|
attributes = {}
|
|
|
|
list.scan(/([#.])([-a-zA-Z_()]+)/).each do |type, property|
|
2006-09-12 04:14:21 +00:00
|
|
|
case type
|
|
|
|
when '.'
|
|
|
|
attributes[:class] = property
|
|
|
|
when '#'
|
|
|
|
attributes[:id] = property
|
|
|
|
end
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
|
|
|
attributes
|
|
|
|
end
|
2006-08-09 18:12:54 +00:00
|
|
|
|
2006-07-20 04:01:23 +00:00
|
|
|
def one_liner?(value)
|
2006-09-12 04:14:21 +00:00
|
|
|
value.length <= ONE_LINER_LENGTH && value.scan(/\n/).empty?
|
2006-07-20 04:01:23 +00:00
|
|
|
end
|
2006-08-09 18:12:54 +00:00
|
|
|
|
2006-09-12 04:14:21 +00:00
|
|
|
# Evaluate in the context of the view object we recieved in the constructor
|
|
|
|
def template_eval(args)
|
|
|
|
@view.instance_eval(args)
|
2006-07-20 04:01:23 +00:00
|
|
|
end
|
2006-06-30 15:14:44 +00:00
|
|
|
end
|
|
|
|
end
|