kueda (owner)

Revisions

gist: 180056 Download_button fork
public
Public Clone URL: git://gist.github.com/180056.git
Embed All Files: show embed
Ruby #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# By Henrik Nyh <http://henrik.nyh.se> 2008-01-30.
# Free to modify and redistribute with credit.
 
# modified by Dave Nolan <http://textgoeshere.org.uk> 2008-02-06
# Ellipsis appended to text of last HTML node
# Ellipsis inserted after final word break
 
# modified by Mark Dickson <mark@sitesteaders.com> 2008-12-18
# Option to truncate to last full word
# Option to include a 'more' link
# Check for nil last child
 
# modified by Ken-ichi Ueda <http://kueda.net> 2009-09-02
# Rails 2.3 compatability (chars -> mb_chars), via Henrik
# Hpricot 0.8 compatability (avoid dup on Hpricot::Elem)
 
require "rubygems"
require "hpricot"
 
module TextHelper
 
  # Like the Rails _truncate_ helper but doesn't break HTML tags, entities, and optionally. words.
  def truncate_html(text, options={})
    return if text.nil?
    
    max_length = options[:max_length] || 30
    ellipsis = options[:ellipsis] || "..."
    words = options[:words] || false
    # use :link => link_to('more', post_path), or something to that effect
    
    doc = Hpricot(text.to_s)
    ellipsis_length = Hpricot(ellipsis).inner_text.mb_chars.length
    content_length = doc.inner_text.mb_chars.length
    actual_length = max_length - ellipsis_length
 
    if content_length > max_length
      truncated_doc = doc.truncate(actual_length)
      
      if words
        word_length = actual_length - (truncated_doc.inner_html.mb_chars.length - truncated_doc.inner_html.rindex(' '))
        truncated_doc = doc.truncate(word_length)
      end
 
      last_child = truncated_doc.children.last
      if last_child.inner_html.nil?
        truncated_doc.inner_html + ellipsis + options[:link] if options[:link]
      else
        last_child.inner_html = last_child.inner_html.gsub(/\W.[^\s]+$/, "") + ellipsis
        last_child.inner_html += options[:link] if options[:link]
        truncated_doc
      end
    else
      if options[:link]
        last_child = doc.children.last
        if last_child.inner_html.nil?
          doc.inner_html + options[:link]
        else
          last_child.inner_html = last_child.inner_html.gsub(/\W.[^\s]+$/, "") + options[:link]
          doc
        end
      else
        text.to_s
      end
    end
  end
 
 
end
 
module HpricotTruncator
  module NodeWithChildren
    def truncate(max_length)
      return self if inner_text.mb_chars.length <= max_length
      truncated_node = if self.is_a?(Hpricot::Doc)
        self.dup
      else
        self.class.send(:new, self.name, self.attributes)
      end
      truncated_node.children = []
      each_child do |node|
        remaining_length = max_length - truncated_node.inner_text.mb_chars.length
        break if remaining_length <= 0
        truncated_node.children << node.truncate(remaining_length)
      end
      truncated_node
    end
  end
 
  module TextNode
    def truncate(max_length)
      # We're using String#scan because Hpricot doesn't distinguish entities.
      Hpricot::Text.new(content.scan(/&#?[^\W_]+;|./).first(max_length).join)
    end
  end
 
  module IgnoredTag
    def truncate(max_length)
      self
    end
  end
end
 
Hpricot::Doc.send(:include, HpricotTruncator::NodeWithChildren)
Hpricot::Elem.send(:include, HpricotTruncator::NodeWithChildren)
Hpricot::Text.send(:include, HpricotTruncator::TextNode)
Hpricot::BogusETag.send(:include, HpricotTruncator::IgnoredTag)
Hpricot::Comment.send(:include, HpricotTruncator::IgnoredTag)