Class: Brief::Document::Structure

Inherits:
Object
  • Object
show all
Defined in:
lib/brief/document/structure.rb

Defined Under Namespace

Classes: Util

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(fragment, content_lines = []) ⇒ Structure

Returns a new instance of Structure.



29
30
31
32
# File 'lib/brief/document/structure.rb', line 29

def initialize(fragment, content_lines = [])
  @fragment = fragment
  @content_lines = content_lines
end

Instance Attribute Details

#content_linesObject

Returns the value of attribute content_lines.



27
28
29
# File 'lib/brief/document/structure.rb', line 27

def content_lines
  @content_lines
end

#fragmentObject

Returns the value of attribute fragment.



27
28
29
# File 'lib/brief/document/structure.rb', line 27

def fragment
  @fragment
end

Instance Method Details

#create_wrappersObject

Markdown rendered HTML comes in the forms of a bunch of siblings, and no parents. We need to introduce the concept of ownership of sections of the document, by using the heading level (h1 - h6) as a form of rank.

All h1 elements will ‘own’ the h2,h3,h4,h5,h6 elements underneath them.



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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/brief/document/structure.rb', line 60

def create_wrappers
  return if @elements_have_been_wrapped

  elements      = fragment.children

  # The different groups of elements
  mapping = []

  # The current bucket of elements that is being
  # collected, will get reset whenever it runs into
  # an element that is a greater heading rank
  bucket = []

  current_level = Util.level(elements.first)

  elements.each_cons(2) do |element, next_element|
    bucket << element

    # We will have run into a greater header, so close up the bucket
    # and put it into the mapping
    if Util.is_header?(next_element) && Util.level(next_element) >= current_level
      mapping.push([current_level, bucket])
      bucket = []
    end

    if Util.is_header?(element)
      current_level = Util.level(element)
    end
  end

  # we never ended up reaching a header, so close up and move on
  if !mapping.include?(bucket)
    mapping.push([current_level, bucket])
  end

  base_fragment = Nokogiri::HTML.fragment("<div class='brief top level' />")

  mapping.map! do |item|
    level, group = item
    group.reject! { |i| i.text == "\n" }

    #puts "Mapping! #{ level } group length: #{ group.length }"

    if level == 0
      #puts "== Condition A"
      base_fragment = fragment = Nokogiri::HTML.fragment("<div class='brief top level'>#{ group.map(&:to_html).join('') }</div>")
    elsif level <= lowest_level
      #puts "== Condition B"
      fragment = Nokogiri::HTML.fragment("<section>#{ group.map(&:to_html).join('') }</section>")
    elsif level > lowest_level
      #puts "== Condition C"
      # should be able to look at the document section mappings and
      # apply custom css classes to these based on the name of the section
      fragment = Nokogiri::HTML.fragment("<article>#{ group.map(&:to_html).join('') }</article>")
    end

    [level, [fragment]]
  end

  begin
    self.fragment = Brief::Document::Section::Builder.run(mapping, low: lowest_level, high: highest_level)
  rescue Brief::Document::Section::BuilderError
    ##puts "== Error, returning default fragment: #{ $! }"
    @fragment
  end
end

#find_heading_by(level, heading) ⇒ Object



166
167
168
169
170
# File 'lib/brief/document/structure.rb', line 166

def find_heading_by(level, heading)
  heading_elements.find do |el|
    el.level.to_s == level.to_s && heading.to_s.strip == el.heading.to_s.strip
  end
end

#heading_elementsObject



172
173
174
175
176
177
178
179
180
181
182
# File 'lib/brief/document/structure.rb', line 172

def heading_elements
  @heading_elements ||= fragment.css('h1,h2,h3,h4,h5,h6').map do |el|
    if el.attr('data-level').to_i > 0
      {
        level: el.attr('data-level'),
        heading: el.attr('data-heading'),
        element: el
      }.to_mash
    end
  end.compact
end

#heading_with_text(text) ⇒ Object



153
154
155
156
157
158
# File 'lib/brief/document/structure.rb', line 153

def heading_with_text(text)
  headings_with_text(text).tap do |results|
    fail 'no section found with content: ' + text if results.length == 0
    fail 'more than one section found with content: ' + text if results.length >= 2
  end.first
end

#headings_at_level(level, options = {}) ⇒ Object



143
144
145
146
147
148
149
150
151
# File 'lib/brief/document/structure.rb', line 143

def headings_at_level(level, options = {})
  matches = heading_elements.select { |el| el.level.to_i == level.to_i }

  if options[:text]
    matches.map(&:text)
  else
    matches
  end
end

#headings_with_text(text) ⇒ Object



160
161
162
163
164
# File 'lib/brief/document/structure.rb', line 160

def headings_with_text(text)
  heading_elements.select do |el|
    el.heading.to_s.strip == text.to_s.strip
  end
end

#highest_levelObject



135
136
137
# File 'lib/brief/document/structure.rb', line 135

def highest_level
  levels.max
end

#levelsObject



127
128
129
130
131
132
133
# File 'lib/brief/document/structure.rb', line 127

def levels
  l = fragment.css('[data-level]').map { |el| el.attr('data-level').to_i }
  l.reject!(&:nil?)
  l.reject! { |v| v.to_i == 0 }
  l.uniq!
  l
end

#lowest_levelObject



139
140
141
# File 'lib/brief/document/structure.rb', line 139

def lowest_level
  levels.min
end

#prescanObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/brief/document/structure.rb', line 34

def prescan
  content_lines.each_with_index do |line, index|
    if line.match(/^#/)
      line = line.strip
      level = line.count('#')
      text = line.gsub('#', '').strip

      if level > 0 && text.length > 0
        line_number = index + 1
        heading = find_heading_by(level, text)

        if heading
          heading.element.set_attribute('data-line-number', line_number)
        end
      end
    end
  end
end