Welcome to mirror list, hosted at ThFree Co, Russian Federation.

truncate_visible_filter.rb « filter « banzai « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: edd6efd4706a6a9695552ec0a857674d5aa195de (plain)
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
# frozen_string_literal: true

module Banzai
  module Filter
    class TruncateVisibleFilter < HTML::Pipeline::Filter
      # Truncates the document to `truncate_visible_max_chars` characters,
      # excluding any HTML tags.

      MATCH_CODE = 'pre > code > .line'

      def call
        return doc unless context[:truncate_visible_max_chars].present?

        max_chars = context[:truncate_visible_max_chars]
        content_length = 0
        @truncated = false

        doc.traverse do |node|
          if node.text? || node.content.empty?
            if truncated
              node.remove
              next
            end

            handle_line_breaks(node)
            truncate_content(content_length, max_chars, node)

            content_length += node.content.length
          end

          truncate_if_block(node)
        end

        doc
      end

      private

      attr_reader :truncated

      def truncate_content(content_length, max_chars, node)
        num_remaining = max_chars - content_length
        return unless node.content.length > num_remaining

        node.content = node.content.truncate(num_remaining)
        @truncated = true
      end

      # Handle line breaks within a node
      def handle_line_breaks(node)
        return unless node.content.strip.lines.length > 1

        node.content = "#{node.content.lines.first.chomp}..."
        @truncated = true
      end

      # If `node` is the first block element, and the
      # text hasn't already been truncated, then append "..." to the node contents
      # and return true.  Otherwise return false.
      def truncate_if_block(node)
        return if truncated
        return unless node.element? && (node.description&.block? || node.matches?(MATCH_CODE))

        node.inner_html = "#{node.inner_html}..." if node.next_sibling
        @truncated = true
      end
    end
  end
end