Class: Katalyst::Content::Tables::Importer

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Model
Defined in:
app/models/katalyst/content/tables/importer.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(table) ⇒ Importer

Returns a new instance of Importer.



35
36
37
38
39
# File 'app/models/katalyst/content/tables/importer.rb', line 35

def initialize(table)
  super()

  @table = table
end

Instance Attribute Details

#tableObject (readonly)

Returns the value of attribute table.



13
14
15
# File 'app/models/katalyst/content/tables/importer.rb', line 13

def table
  @table
end

Class Method Details

.call(table, value) ⇒ Object

Update a table from an HTML5 fragment and apply normalisation rules.

Parameters:

  • table (Katalyst::Content::Table)

    the table to update

  • value (Nokogiri::XML::Node, ActionText::RichText, String)

    the provided HTML fragment



19
20
21
# File 'app/models/katalyst/content/tables/importer.rb', line 19

def self.call(table, value)
  new(table).call(wrap(value))
end

.wrap(value) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
# File 'app/models/katalyst/content/tables/importer.rb', line 23

def self.wrap(value)
  case value
  when Nokogiri::XML::Node
    value
  when ActionText::RichText
    # clone body to avoid modifying the original
    Nokogiri::HTML5.fragment(value.body.to_html)
  else
    Nokogiri::HTML5.fragment(value.to_s)
  end
end

Instance Method Details

#call(fragment) ⇒ Object



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
# File 'app/models/katalyst/content/tables/importer.rb', line 41

def call(fragment)
  @node = fragment.name == "table" ? fragment : fragment.at_css("table")

  unless @node&.name == "table"
    table.content.body = nil
    return self
  end

  # Convert b and i to strong and em
  normalize_emphasis!

  # Convert `td > strong` to th headings
  promote_header_cells!

  # Promote any rows with only <th> cells to <thead>
  # This captures the pattern where a table has a header row but its
  # in the tbody instead of the thead
  promote_header_rows!

  # Promote first row to caption if it only has one cell.
  # This captures the pattern where a table has as single cell that spans
  # the entire width of the table, and is used as a heading.
  promote_header_row_caption! if header_row_caption?

  # Update the table heading with the caption, if present, and remove from table
  set_heading! if caption?

  # Update the table's content with the normalized HTML.
  table.content.body = to_html

  table
end

#caption?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'app/models/katalyst/content/tables/importer.rb', line 74

def caption?
  at_css("caption")&.text&.present?
end

#header_row_caption?Boolean

Returns:

  • (Boolean)


78
79
80
81
82
# File 'app/models/katalyst/content/tables/importer.rb', line 78

def header_row_caption?
  !at_css("caption") &&
    (tr = at_css("thead > tr:first-child"))&.elements&.count == 1 &&
    (tr.elements.first.attributes["colspan"]&.value&.to_i&.> 1)
end

#normalize_emphasis!Object



84
85
86
87
88
89
90
91
92
93
94
95
# File 'app/models/katalyst/content/tables/importer.rb', line 84

def normalize_emphasis!
  traverse do |node|
    case node.name
    when "b"
      node.name = "strong"
    when "i"
      node.name = "em"
    else
      node
    end
  end
end

#promote_cell_to_heading!(cell) ⇒ Object

Converts a ‘td > strong` cell to a `th` cell by updating the cell name and replacing the cell’s content with the strong tag’s content.



103
104
105
106
107
108
109
110
111
112
113
# File 'app/models/katalyst/content/tables/importer.rb', line 103

def promote_cell_to_heading!(cell)
  strong = cell.at_css("strong")

  return unless cell.text.strip == strong.text.strip

  cell.name = "th"

  # remove strong by promoting its children
  strong.before(strong.children)
  strong.remove
end

#promote_header_cells!Object



97
98
99
# File 'app/models/katalyst/content/tables/importer.rb', line 97

def promote_header_cells!
  css("td:has(strong)").each { |cell| promote_cell_to_heading!(cell) }
end

#promote_header_row_caption!Object

Promotes the first row to a caption if it only has one cell.



131
132
133
134
135
136
137
138
139
# File 'app/models/katalyst/content/tables/importer.rb', line 131

def promote_header_row_caption!
  tr   = at_css("thead > tr:first-child")
  cell = tr.elements.first
  tr.remove
  thead = at_css("thead")
  thead.before("<caption></caption>")
  thead.remove if thead.elements.empty?
  at_css("caption").inner_html = cell.inner_html.strip
end

#promote_header_rows!Object



115
116
117
118
119
120
121
# File 'app/models/katalyst/content/tables/importer.rb', line 115

def promote_header_rows!
  css("tbody > tr").each do |tr|
    break unless tr.elements.all? { |td| td.name == "th" }

    promote_row_to_head!(tr)
  end
end

#promote_row_to_head!(row) ⇒ Object

Moves a row from <tbody> to <thead>. If the table doesn’t have a <thead>, one will be created.



125
126
127
128
# File 'app/models/katalyst/content/tables/importer.rb', line 125

def promote_row_to_head!(row)
  at_css("tbody").before("<thead></thead>") unless at_css("thead")
  at_css("thead") << row
end

#set_heading!Object

Set heading from caption and remove the caption from the table.



142
143
144
145
146
147
# File 'app/models/katalyst/content/tables/importer.rb', line 142

def set_heading!
  caption = at_css("caption")
  table.heading       = caption.text.strip
  table.heading_style = "default"
  caption.remove
end