2006-10-14 23:50:07 +00:00
|
|
|
module Haml
|
|
|
|
# This class is used only internally. It holds the buffer of XHTML that
|
|
|
|
# is eventually output by Haml::Engine's to_html method. It's called
|
|
|
|
# from within the precompiled code, and helps reduce the amount of
|
|
|
|
# processing done within instance_eval'd code.
|
|
|
|
class Buffer
|
|
|
|
include Haml::Helpers
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2006-10-14 23:50:07 +00:00
|
|
|
# Set the maximum length for a line to be considered a one-liner.
|
|
|
|
# Lines <= the maximum will be rendered on one line,
|
|
|
|
# i.e. <tt><p>Hello world</p></tt>
|
|
|
|
ONE_LINER_LENGTH = 50
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2006-10-14 23:50:07 +00:00
|
|
|
# The string that holds the compiled XHTML. This is aliased as
|
|
|
|
# _erbout for compatibility with ERB-specific code.
|
|
|
|
attr_accessor :buffer
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2007-03-16 02:28:03 +00:00
|
|
|
# Gets the current tabulation of the document.
|
|
|
|
def tabulation
|
|
|
|
@real_tabs + @tabulation
|
|
|
|
end
|
|
|
|
|
|
|
|
# Sets the current tabulation of the document.
|
|
|
|
def tabulation=(val)
|
|
|
|
val = val - @real_tabs
|
|
|
|
@tabulation = val > -1 ? val : 0
|
|
|
|
end
|
2006-11-08 09:14:24 +00:00
|
|
|
|
2006-10-14 23:50:07 +00:00
|
|
|
# Creates a new buffer.
|
2006-10-27 21:20:19 +00:00
|
|
|
def initialize(options = {})
|
|
|
|
@options = options
|
2006-10-30 06:10:26 +00:00
|
|
|
@quote_escape = options[:attr_wrapper] == '"' ? """ : "'"
|
2006-12-10 21:56:05 +00:00
|
|
|
@other_quote_char = options[:attr_wrapper] == '"' ? "'" : '"'
|
2006-10-14 23:50:07 +00:00
|
|
|
@buffer = ""
|
|
|
|
@one_liner_pending = false
|
2006-11-08 09:14:24 +00:00
|
|
|
@tabulation = 0
|
2007-03-16 02:28:03 +00:00
|
|
|
|
|
|
|
# The number of tabs that Engine thinks we should have
|
|
|
|
# @real_tabs + @tabulation is the number of tabs actually output
|
|
|
|
@real_tabs = 0
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2006-10-14 23:50:07 +00:00
|
|
|
# Renders +text+ with the proper tabulation. This also deals with
|
|
|
|
# making a possible one-line tag one line or not.
|
2007-07-13 01:09:10 +00:00
|
|
|
def push_text(text, tab_change = 0)
|
2007-06-22 20:17:55 +00:00
|
|
|
if @one_liner_pending && Buffer.one_liner?(text)
|
2006-10-14 23:50:07 +00:00
|
|
|
@buffer << text
|
|
|
|
else
|
|
|
|
if @one_liner_pending
|
|
|
|
@buffer << "\n"
|
|
|
|
@one_liner_pending = false
|
|
|
|
end
|
2007-07-13 01:09:10 +00:00
|
|
|
if(@tabulation > 0)
|
|
|
|
text.gsub!(/^/m, ' ')
|
|
|
|
end
|
|
|
|
|
|
|
|
@buffer << "#{text}"
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
2007-07-13 01:09:10 +00:00
|
|
|
@real_tabs += tab_change
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2006-10-14 23:50:07 +00:00
|
|
|
# Properly formats the output of a script that was run in the
|
|
|
|
# instance_eval.
|
2007-07-13 01:09:10 +00:00
|
|
|
def push_script(result, tabulation, flattened, close_tag = nil)
|
2006-10-14 23:50:07 +00:00
|
|
|
if flattened
|
2007-02-06 08:08:32 +00:00
|
|
|
result = Haml::Helpers.find_and_preserve(result)
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
|
|
|
unless result.nil?
|
2006-11-16 05:46:28 +00:00
|
|
|
result = result.to_s
|
|
|
|
while result[-1] == 10 # \n
|
|
|
|
# String#chomp is slow
|
|
|
|
result = result[0...-1]
|
|
|
|
end
|
|
|
|
|
2007-07-13 01:09:10 +00:00
|
|
|
if @one_liner_pending && Buffer.one_liner?(result)
|
|
|
|
@buffer << result
|
|
|
|
@buffer << "</#{close_tag}>\n"
|
|
|
|
@one_liner_pending = false
|
|
|
|
@real_tabs -= 1
|
|
|
|
else
|
|
|
|
if @one_liner_pending
|
|
|
|
@buffer << "\n"
|
|
|
|
tabulation += 1
|
|
|
|
end
|
|
|
|
|
|
|
|
result = result.gsub("\n", "\n#{tabs(tabulation)}")
|
|
|
|
@buffer << "#{tabs(tabulation)}#{result}\n"
|
|
|
|
|
|
|
|
if @one_liner_pending
|
|
|
|
@one_liner_pending = false
|
|
|
|
@buffer << "#{tabs(tabulation-1)}</#{close_tag}>\n"
|
|
|
|
end
|
|
|
|
end
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
|
|
|
nil
|
|
|
|
end
|
2007-06-06 08:30:40 +00:00
|
|
|
|
2007-07-12 01:08:23 +00:00
|
|
|
def open_prerendered_tag(tag, tabulation, try_one_liner, tag_closed)
|
2007-06-06 08:30:40 +00:00
|
|
|
@buffer << "#{tabs(tabulation)}#{tag}"
|
2007-07-12 01:08:23 +00:00
|
|
|
@one_liner_pending = true if try_one_liner
|
|
|
|
@real_tabs += 1 unless tag_closed
|
2007-06-06 08:30:40 +00:00
|
|
|
end
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2006-10-14 23:50:07 +00:00
|
|
|
# Takes the various information about the opening tag for an
|
|
|
|
# element, formats it, and adds it to the buffer.
|
2007-07-13 01:09:10 +00:00
|
|
|
def open_tag(name, tabulation, atomic, try_one_line, class_id, obj_ref, content, attributes_hash)
|
2007-05-31 03:46:37 +00:00
|
|
|
attributes = class_id
|
2007-06-06 09:03:21 +00:00
|
|
|
if attributes_hash
|
|
|
|
attributes_hash.keys.each { |key| attributes_hash[key.to_s] = attributes_hash.delete(key) }
|
|
|
|
self.class.merge_attrs(attributes, attributes_hash)
|
|
|
|
end
|
|
|
|
self.class.merge_attrs(attributes, parse_object_ref(obj_ref)) if obj_ref
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2006-10-14 23:50:07 +00:00
|
|
|
@one_liner_pending = false
|
|
|
|
if atomic
|
2006-11-16 03:05:01 +00:00
|
|
|
str = " />\n"
|
2006-11-04 06:36:16 +00:00
|
|
|
elsif try_one_line
|
|
|
|
@one_liner_pending = true
|
2006-11-16 03:05:01 +00:00
|
|
|
str = ">"
|
2006-10-14 23:50:07 +00:00
|
|
|
else
|
2006-11-16 03:05:01 +00:00
|
|
|
str = ">\n"
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
2006-11-16 03:05:01 +00:00
|
|
|
@buffer << "#{tabs(tabulation)}<#{name}#{build_attributes(attributes)}#{str}"
|
2007-07-13 01:09:10 +00:00
|
|
|
if content
|
|
|
|
if Buffer.one_liner?(content)
|
|
|
|
@buffer << "#{content}</#{name}>\n"
|
|
|
|
else
|
|
|
|
@buffer << "\n#{tabs(@real_tabs+1)}#{content}\n#{tabs(@real_tabs)}</#{name}>\n"
|
|
|
|
end
|
|
|
|
@one_liner_pending = false
|
|
|
|
else
|
|
|
|
@real_tabs += 1
|
|
|
|
end
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2007-06-06 09:03:21 +00:00
|
|
|
def self.merge_attrs(to, from)
|
|
|
|
if to['id'] && from['id']
|
|
|
|
to['id'] << '_' << from.delete('id')
|
|
|
|
end
|
|
|
|
|
|
|
|
if to['class'] && from['class']
|
|
|
|
# Make sure we don't duplicate class names
|
|
|
|
from['class'] = (from['class'].split(' ') | to['class'].split(' ')).join(' ')
|
|
|
|
end
|
|
|
|
|
|
|
|
to.merge!(from)
|
|
|
|
end
|
|
|
|
|
2006-10-14 23:50:07 +00:00
|
|
|
# Creates a closing tag with the given name.
|
|
|
|
def close_tag(name, tabulation)
|
2007-07-13 01:09:10 +00:00
|
|
|
@real_tabs = tabulation
|
2006-10-14 23:50:07 +00:00
|
|
|
if @one_liner_pending
|
|
|
|
@buffer << "</#{name}>\n"
|
|
|
|
@one_liner_pending = false
|
|
|
|
else
|
2007-07-13 01:09:10 +00:00
|
|
|
push_text("#{' ' * tabulation}</#{name}>\n")
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
|
|
|
end
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2007-03-16 02:28:03 +00:00
|
|
|
# Some of these methods are exposed as public class methods
|
|
|
|
# so they can be re-used in helpers.
|
|
|
|
|
|
|
|
# Takes a hash and builds a list of XHTML attributes from it, returning
|
|
|
|
# the result.
|
2007-03-16 02:30:57 +00:00
|
|
|
def build_attributes(attributes = {})
|
2007-03-16 02:28:03 +00:00
|
|
|
result = attributes.collect do |a,v|
|
|
|
|
v = v.to_s
|
|
|
|
unless v.nil? || v.empty?
|
|
|
|
attr_wrapper = @options[:attr_wrapper]
|
|
|
|
if v.include? attr_wrapper
|
|
|
|
if v.include? @other_quote_char
|
|
|
|
v = v.gsub(attr_wrapper, @quote_escape)
|
|
|
|
else
|
|
|
|
attr_wrapper = @other_quote_char
|
|
|
|
end
|
|
|
|
end
|
|
|
|
" #{a}=#{attr_wrapper}#{v}#{attr_wrapper}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
result.sort.join
|
|
|
|
end
|
2007-06-06 08:30:40 +00:00
|
|
|
|
|
|
|
# Returns whether or not the given value is short enough to be rendered
|
|
|
|
# on one line.
|
|
|
|
def self.one_liner?(value)
|
|
|
|
value.length <= ONE_LINER_LENGTH && value.scan(/\n/).empty?
|
|
|
|
end
|
2007-03-16 02:28:03 +00:00
|
|
|
|
2006-10-14 23:50:07 +00:00
|
|
|
private
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2007-05-08 07:44:22 +00:00
|
|
|
@@tab_cache = {}
|
2006-10-14 23:50:07 +00:00
|
|
|
# Gets <tt>count</tt> tabs. Mostly for internal use.
|
|
|
|
def tabs(count)
|
2007-05-08 07:44:22 +00:00
|
|
|
tabs = count + @tabulation
|
|
|
|
' ' * tabs
|
|
|
|
@@tab_cache[tabs] ||= ' ' * tabs
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2006-10-14 23:50:07 +00:00
|
|
|
# Takes an array of objects and uses the class and id of the first
|
|
|
|
# one to create an attributes hash.
|
2007-06-06 09:03:21 +00:00
|
|
|
def parse_object_ref(ref)
|
2006-10-14 23:50:07 +00:00
|
|
|
ref = ref[0]
|
2006-11-06 02:23:32 +00:00
|
|
|
# Let's make sure the value isn't nil. If it is, return the default Hash.
|
|
|
|
return {} if ref.nil?
|
2007-03-30 18:59:30 +00:00
|
|
|
class_name = underscore(ref.class)
|
2007-04-17 06:49:07 +00:00
|
|
|
id = "#{class_name}_#{ref.id || 'new'}"
|
2007-02-04 05:09:00 +00:00
|
|
|
|
2007-06-02 10:45:24 +00:00
|
|
|
{'id' => id, 'class' => class_name}
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
2006-11-04 06:36:16 +00:00
|
|
|
|
2007-03-30 18:59:30 +00:00
|
|
|
# Changes a word from camel case to underscores.
|
|
|
|
# Based on the method of the same name in Rails' Inflector,
|
|
|
|
# but copied here so it'll run properly without Rails.
|
|
|
|
def underscore(camel_cased_word)
|
|
|
|
camel_cased_word.to_s.gsub(/::/, '_').
|
|
|
|
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
|
|
|
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
|
|
|
tr("-", "_").
|
|
|
|
downcase
|
|
|
|
end
|
2006-10-14 23:50:07 +00:00
|
|
|
end
|
|
|
|
end
|
2006-11-16 05:46:28 +00:00
|
|
|
|
2007-01-19 17:03:47 +00:00
|
|
|
unless String.methods.include? 'old_comp'
|
|
|
|
class String # :nodoc
|
|
|
|
alias_method :old_comp, :<=>
|
|
|
|
|
|
|
|
def <=>(other)
|
|
|
|
if other.is_a? NilClass
|
|
|
|
-1
|
|
|
|
else
|
|
|
|
old_comp(other)
|
|
|
|
end
|
2006-11-16 05:46:28 +00:00
|
|
|
end
|
|
|
|
end
|
2007-01-19 17:03:47 +00:00
|
|
|
|
|
|
|
class NilClass # :nodoc:
|
|
|
|
include Comparable
|
|
|
|
|
|
|
|
def <=>(other)
|
|
|
|
other.nil? ? 0 : 1
|
|
|
|
end
|
2006-11-17 01:05:56 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|