module TruncateHtml
class HtmlTruncator
def initialize(original_html)
@original_html = original_html
end
def truncate(options = {})
length = options[:length] || TruncateHtml.configuration.length
@omission = options[:omission] || TruncateHtml.configuration.omission
@word_boundary = (options.has_key?(:word_boundary) ? options[:word_boundary] : TruncateHtml.configuration.word_boundary)
@break_token = options[:break_token] || TruncateHtml.configuration.break_token || nil
@chars_remaining = length - @omission.length
@open_tags, @truncated_html = [], ['']
return @omission if @chars_remaining < 0
@original_html.html_tokens.each do |token|
if @chars_remaining <= 0 || truncate_token?(token)
close_open_tags
break
else
process_token(token)
end
end
out = @truncated_html.join
if @word_boundary
term_regexp = Regexp.new("^.*#{@word_boundary.source}")
match = out.match(term_regexp)
match ? match[0] : out
else
out
end
end
private
def process_token(token)
append_to_result(token)
if token.html_tag?
if token.open_tag?
@open_tags << token
else
remove_latest_open_tag(token)
end
else
@chars_remaining -= (@word_boundary ? token.length : token[0, @chars_remaining].length)
if @chars_remaining <= 0
@truncated_html[-1] = @truncated_html[-1].rstrip + @omission
end
end
end
def append_to_result(token)
if token.html_tag?
@truncated_html << token
elsif @word_boundary
@truncated_html << token if (@chars_remaining - token.length) >= 0
else
@truncated_html << token[0, @chars_remaining]
end
end
def close_open_tags
@open_tags.reverse_each do |open_tag|
@truncated_html << open_tag.matching_close_tag
end
end
def remove_latest_open_tag(close_tag)
(0...@open_tags.length).to_a.reverse.each do |index|
if @open_tags[index].matching_close_tag == close_tag
@open_tags.delete_at(index)
break
end
end
end
def truncate_token?(token)
@break_token and token == @break_token
end
end
end