Module: Twitter::HitHighlighter

Extended by:
HitHighlighter
Included in:
HitHighlighter
Defined in:
lib/twitter-text/hit_highlighter.rb

Overview

Module for doing “hit highlighting” on tweets that have been auto-linked already. Useful with the results returned from the Search API.

Constant Summary collapse

DEFAULT_HIGHLIGHT_TAG =

Default Tag used for hit highlighting

"em"

Instance Method Summary collapse

Instance Method Details

#hit_highlight(text, hits = [], options = {}) ⇒ Object

Add tags around the hits provided in the text. The hits should be an array of (start, end) index pairs, relative to the original text, before auto-linking (but the text may already be auto-linked if desired)

The tags can be overridden using the :tag option. For example:

irb> hit_highlight("test hit here", [[5, 8]], :tag => 'strong')
=> "test <strong>hit</strong> here"


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
# File 'lib/twitter-text/hit_highlighter.rb', line 16

def hit_highlight(text, hits = [], options = {})
  if hits.empty?
    return text
  end

  tag_name = options[:tag] || DEFAULT_HIGHLIGHT_TAG
  tags = ["<" + tag_name + ">", "</" + tag_name + ">"]

  chunks = text.split(/[<>]/)

  result = []
  chunk_index, chunk = 0, chunks[0]
  chunk_chars = chunk.to_s.to_char_a
  prev_chunks_len = 0
  chunk_cursor = 0
  start_in_chunk = false
  for hit, index in hits.flatten.each_with_index do
    tag = tags[index % 2]

    placed = false
    until chunk.nil? || hit < prev_chunks_len + chunk.length do
      result << chunk_chars[chunk_cursor..-1]
      if start_in_chunk && hit == prev_chunks_len + chunk_chars.length
        result << tag
        placed = true
      end

      # correctly handle highlights that end on the final character.
      if tag_text = chunks[chunk_index+1]
        result << "<#{tag_text}>"
      end

      prev_chunks_len += chunk_chars.length
      chunk_cursor = 0
      chunk_index += 2
      chunk = chunks[chunk_index]
      chunk_chars = chunk.to_s.to_char_a
      start_in_chunk = false
    end

    if !placed && !chunk.nil?
      hit_spot = hit - prev_chunks_len
      result << chunk_chars[chunk_cursor...hit_spot] << tag
      chunk_cursor = hit_spot
      if index % 2 == 0
        start_in_chunk = true
      else
        start_in_chunk = false
      end
      placed = true
    end

    # ultimate fallback, hits that run off the end get a closing tag
    if !placed
      result << tag
    end
  end

  if chunk
    if chunk_cursor < chunk_chars.length
      result << chunk_chars[chunk_cursor..-1]
    end
    (chunk_index+1).upto(chunks.length-1).each do |i|
      result << (i.even? ? chunks[i] : "<#{chunks[i]}>")
    end
  end

  result.flatten.join
end