swedbank-pay-design-guide-jekyll-theme 1.8 → 1.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The String class
4
+ class String
5
+ # Sanitizes a filename
6
+ def sanitized
7
+ match = match(/(?m)(?<=\b_site).*$/)
8
+ sanitized_filename = match ? match[0] : self
9
+ sanitized_filename = sanitized_filename.gsub('index.html', '')
10
+ sanitized_filename.gsub('.html', '')
11
+ end
12
+ end
@@ -3,209 +3,38 @@
3
3
  require 'jekyll'
4
4
  require 'nokogiri'
5
5
  require 'json'
6
-
7
- module Jekyll
8
- class Sidebar
9
- attr_accessor :hash_pre_render
10
- attr_accessor :filename_with_headers
11
-
12
- def initialize
13
- @hash_pre_render = {}
14
- @filename_with_headers = {}
15
- end
16
-
17
- def pre_render(page)
18
- menu_order = page['menu-order'].nil? ? 0 : page['menu-order']
19
- hide_from_sidebar = page['hide_from_sidebar'].nil? ? false : page['hide_from_sidebar']
20
- url = page['url'].gsub('index.html', '').gsub('.html', '')
21
- @hash_pre_render[url] = {
22
- title: page['title'],
23
- url: page['url'].gsub('.html', ''),
24
- name: page['name'],
25
- menu_order: menu_order,
26
- hide_from_sidebar: hide_from_sidebar
27
- }
28
- end
29
-
30
- def post_write(site)
31
- files = []
32
- Dir.glob("#{site.config['destination']}/**/*.html") do |filename|
33
- doc = File.open(filename) { |f| Nokogiri::HTML(f) }
34
- files.push(doc)
35
-
36
- headers = []
37
- doc.xpath('//h2 ').each do |header|
38
- next unless header['id']
39
-
40
- child = header.last_element_child
41
- header = {
42
- id: header['id'],
43
- title: header.content.strip,
44
- hash: (child['href']).to_s
45
- }
46
- headers.push(header)
47
- end
48
- sanitized_filename = sanitize_filename(filename)
49
- @filename_with_headers[sanitized_filename] = { headers: headers }
50
- end
51
-
52
- Dir.glob("#{site.config['destination']}/**/*.html") do |filename|
53
- sanitized_filename = sanitize_filename(filename)
54
- sidebar = render(sanitized_filename)
55
- file = File.open(filename) { |f| Nokogiri::HTML(f) }
56
- file.xpath('//*[@id="dx-sidebar-main-nav-ul"]').each do |location|
57
- location.inner_html = sidebar
58
- end
59
- File.open(filename, 'w') { |f| f.write(file.to_html(encoding: 'UTF-8')) }
60
- end
61
-
62
- # File.open('_site/sidebar.html', 'w') { |f| f.write(sidebar) }
63
- end
64
-
65
- private
66
-
67
- def sanitize_filename(filename)
68
- sanitized_filename = filename.match(/(?m)(?<=\b_site).*$/)[0]
69
- sanitized_filename = sanitized_filename.gsub('index.html', '')
70
- sanitized_filename.gsub('.html', '')
71
- end
72
-
73
- def generateSubgroup(filename, key, value, all_subgroups, level)
74
- title = value[:title].split('–').last
75
- subsubgroup_list = all_subgroups.select do |subsubgroup_key, _subsubgroup_value|
76
- subsubgroup_key.include? key and subsubgroup_key != key and \
77
- key.split('/').length > level
78
- end
79
-
80
- subgroup = ''
81
- has_subgroups = !all_subgroups.empty?
82
- if value[:headers].any? || !subsubgroup_list.empty?
83
- if has_subgroups
84
- url = value[:url]
85
- active = active?(filename, url, true)
86
- # puts "#{url}, #{filename}, #{key}" if active
87
- item_class = active || (url.split('/').length > level && filename.start_with?(url)) ? 'nav-subgroup active' : 'nav-subgroup'
88
- subgroup << "<li class=\"#{item_class}\">"
89
- subgroup << "<div class=\"nav-subgroup-heading\"><i class=\"material-icons\">arrow_right</i><a href=\"#{url}\">#{title}</a></div>"
90
- subgroup << '<ul class="nav-ul">'
91
-
92
- if subsubgroup_list.empty?
93
- value[:headers].each do |header|
94
- subgroup << "<li class=\"nav-leaf\"><a href=\"#{value[:url]}#{header[:hash]}\">#{header[:title]}</a></li>"
95
- end
96
- else
97
- subgroup_leaf_class = active ? 'nav-leaf nav-subgroup-leaf active' : 'nav-leaf nav-subgroup-leaf'
98
- subgroup << "<li class=\"#{subgroup_leaf_class}\"><a href=\"#{value[:url]}\">#{title} overview</a></li>"
99
-
100
- subsubgroup_list.each do |subsubgroup_key, subsubgroup_value|
101
- subgroup << generateSubgroup(filename, subsubgroup_key, subsubgroup_value, subsubgroup_list, 3)
102
- end
103
- end
104
-
105
- subgroup << '</ul>'
106
- subgroup << '</li>'
107
- else
108
- value[:headers].each do |header|
109
- subgroup << "<li class=\"nav-leaf\"><a href=\"#{value[:url]}#{header[:hash]}\">#{header[:title]}</a></li>"
110
- end
111
- end
112
- else
113
- subgroup << if has_subgroups
114
- "<li class=\"nav-leaf nav-subgroup-leaf\"><a href=\"#{value[:url]}\">#{title}</a></li>"
115
- else
116
- "<li class=\"nav-leaf\"><a href=\"#{value[:url]}\">#{title}</a></li>"
117
- end
118
- end
119
-
120
- subgroup
121
- end
122
-
123
- def render(filename)
124
- sidebar = ''
125
-
126
- merged = merge(@hash_pre_render, @filename_with_headers).sort_by { |_key, value| value[:menu_order] }
127
- merged.select { |key, _value| key.split('/').length <= 2 }.each do |key, value|
128
- next if value[:title].nil?
129
- next if value[:hide_from_sidebar]
130
-
131
- subgroups = merged.select { |subgroup_key, _subgroup_value| subgroup_key.include? key and subgroup_key != key and key != '/' }
132
-
133
- active = active?(filename, key)
134
- # puts "#{filename}, #{key}" if active
135
- item_class = active ? 'nav-group active' : 'nav-group'
136
-
137
- child = "<li class=\"#{item_class}\">"
138
- child << "<div class=\"nav-group-heading\"><i class=\"material-icons\">arrow_right</i><span>#{value[:title].split('–').first}</span></div>"
139
-
140
- child << '<ul class="nav-ul">'
141
-
142
- subgroup = generateSubgroup(filename, key, value, subgroups, 2)
143
-
144
- child << subgroup
145
-
146
- if subgroups.any?
147
- subgroups.select { |subgroup_key, _subgroup_value| subgroup_key.split('/').length <= 3 }.each do |subgroup_key, subgroup_value|
148
- subgroup = generateSubgroup(filename, subgroup_key, subgroup_value, subgroups, 2)
149
- child << subgroup
150
- end
151
- end
152
-
153
- child << '</ul>'
154
- child << '</li>'
155
- sidebar << child
6
+ require_relative 'sidebar_page'
7
+ require_relative 'sidebar_parser'
8
+ require_relative 'sidebar_renderer'
9
+ require_relative 'sidebar_tree_builder'
10
+
11
+ module SwedbankPay
12
+ # A nice sidebar
13
+ module Sidebar
14
+ class << self
15
+ attr_reader :pages
16
+
17
+ def pre_render(site)
18
+ Jekyll.logger.debug(' Sidebar: pre_render')
19
+ @parser = SidebarParser.new(site)
20
+ @pages = SidebarTreeBuilder.new(@parser.pages)
21
+ Jekyll.logger.debug(" Sidebar: #{@pages.inspect}")
156
22
  end
157
23
 
158
- File.open('_site/sidebar.html', 'w') { |f| f.write(sidebar) }
159
- sidebar
160
- end
161
-
162
- def active?(filename, url, exact = false)
163
- if filename == '/' || url == '/'
164
- if filename == '/' && url == '/'
165
- return true
166
- else
167
- return false
168
- end
24
+ def post_write
25
+ @sidebar_renderer = SidebarRenderer.new(@pages)
26
+ @parser.parse(@pages)
27
+ Jekyll.logger.debug(' Sidebar: post_write')
28
+ @sidebar_renderer.render
169
29
  end
170
-
171
- exact ? filename == url : filename.start_with?(url)
172
- end
173
-
174
- def merge(hash1, hash2)
175
- all_keys = hash1.keys | hash2.keys
176
- result_hash = {}
177
-
178
- all_keys.each do |key|
179
- hash_value = {}
180
-
181
- if hash1.key? key
182
- value = hash1[key]
183
-
184
- hash_value = value unless value.nil?
185
- end
186
-
187
- if hash2.key? key
188
- value = hash2[key]
189
-
190
- hash_value = hash_value.merge(value) unless value.nil?
191
- end
192
-
193
- result_hash[key] = hash_value
194
- end
195
-
196
- result_hash
197
30
  end
198
31
  end
199
32
  end
200
33
 
201
- sidebar = Jekyll::Sidebar.new
202
-
203
- Jekyll::Hooks.register :site, :pre_render do |site, _payload|
204
- site.pages.each do |page|
205
- sidebar.pre_render page
206
- end
34
+ Jekyll::Hooks.register :site, :pre_render do |site, _|
35
+ SwedbankPay::Sidebar.pre_render site
207
36
  end
208
37
 
209
- Jekyll::Hooks.register :site, :post_write do |site|
210
- sidebar.post_write site
38
+ Jekyll::Hooks.register :site, :post_write do
39
+ SwedbankPay::Sidebar.post_write
211
40
  end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: false
2
+
3
+ require_relative 'sidebar_page'
4
+
5
+ module SwedbankPay
6
+ # The builder of HTML for the Sidebar
7
+ class SidebarHTMLBuilder
8
+ def initialize(tree)
9
+ @tree = tree
10
+ end
11
+
12
+ def build(current_page)
13
+ raise ArgumentError, 'current_page cannot be nil' if current_page.nil?
14
+ raise ArgumentError, "#{current_page.class} is not a #{SidebarPage}" unless current_page.is_a? SidebarPage
15
+
16
+ build_markup(@tree, current_page)
17
+ end
18
+
19
+ private
20
+
21
+ def build_markup(pages, current_page)
22
+ return '' if pages.empty?
23
+
24
+ markup = ''
25
+
26
+ pages.each do |page|
27
+ current_page_name = current_page.respond_to?(:name) ? current_page.name : current_page.to_s
28
+
29
+ if page.hidden_for?(current_page)
30
+ Jekyll.logger.debug(" Sidebar: #{page.name} is hidden for #{current_page_name}")
31
+ next
32
+ elsif page.hidden?
33
+ Jekyll.logger.debug(" Sidebar: Hidden page #{page.name} is not hidden for #{current_page_name}")
34
+ end
35
+
36
+ sub_items_markup = sub_items_markup(page, current_page)
37
+ markup << item_markup(page, current_page, sub_items_markup, false)
38
+ end
39
+
40
+ markup
41
+ end
42
+
43
+ def item_markup(page, current_page, sub_items_markup, is_leaf)
44
+ # If we're rendering a leaf node, just set the level to a non-zero value
45
+ # to get the 'nav-subgroup' class and such.
46
+ level = is_leaf ? -1 : page.level
47
+ title_markup = title_markup(page, level, is_leaf)
48
+ item_class = item_class(page, current_page, level, is_leaf)
49
+ group_heading_class = group_heading_class(level)
50
+
51
+ "<li class=\"#{item_class}\">
52
+ <div class=\"#{group_heading_class}\">
53
+ <i class=\"material-icons\">arrow_right</i>
54
+ #{title_markup}
55
+ </div>
56
+ #{sub_items_markup}
57
+ </li>"
58
+ end
59
+
60
+ def item_class(page, current_page, level, is_leaf)
61
+ active = page.active?(current_page, is_leaf: is_leaf)
62
+ item_class = group_class(level)
63
+ item_class += ' active' if active
64
+ item_class
65
+ end
66
+
67
+ def group_class(level)
68
+ level.zero? ? 'nav-group' : 'nav-subgroup'
69
+ end
70
+
71
+ def group_heading_class(level)
72
+ group_class = group_class(level)
73
+ "#{group_class}-heading"
74
+ end
75
+
76
+ def title_markup(page, level, is_leaf)
77
+ lead_title = lead_title(page)
78
+ return "<span>#{lead_title}</span>" if level.zero?
79
+
80
+ main_title = main_title(page, is_leaf)
81
+
82
+ "<a href=\"#{page.path}\">#{main_title}</a>"
83
+ end
84
+
85
+ def sub_items_markup(page, current_page)
86
+ headers_markup = headers_markup(page, current_page)
87
+ child_markup = build_markup(page.children, current_page)
88
+
89
+ return '' if headers_markup.empty? && child_markup.empty?
90
+
91
+ "<ul class=\"nav-ul\">
92
+ #{headers_markup}
93
+ #{child_markup}
94
+ </ul>"
95
+ end
96
+
97
+ def headers_markup(page, current_page)
98
+ # If there's no page headers, only return a leaf item for the page itself.
99
+ main_title = page.title.nil? ? nil : page.title.main
100
+ return leaf_markup(page.path, main_title, page.level) unless page.headers?
101
+
102
+ # If there's no children, only return the headers as leaf node items.
103
+ return page.headers.map { |h| header_markup(page, h) }.join('') unless page.children?
104
+
105
+ headers_markup = page.headers.map { |h| header_markup(page, h) }.join('')
106
+ headers_markup = "<ul class=\"nav-ul\">#{headers_markup}</ul>"
107
+
108
+ item_markup(page, current_page, headers_markup, true)
109
+ end
110
+
111
+ def header_markup(page, header)
112
+ hash = header[:hash]
113
+ subtitle = header[:title]
114
+ href = "#{page.path}#{hash}"
115
+ leaf_markup(href, subtitle)
116
+ end
117
+
118
+ def leaf_markup(href, title, level = 0)
119
+ leaf_class = level.positive? ? 'nav-leaf nav-subgroup-leaf' : 'nav-leaf'
120
+ "<li class=\"#{leaf_class}\"><a href=\"#{href}\">#{title}</a></li>"
121
+ end
122
+
123
+ def lead_title(page)
124
+ return page.title.lead unless page.title.nil? || page.title.lead.nil?
125
+ return page.parent.title.to_s unless page.parent.nil? || page.parent.title.nil?
126
+
127
+ ''
128
+ end
129
+
130
+ def main_title(page, is_leaf)
131
+ unless page.nil? || page.title.nil?
132
+ lead_title = lead_title(page)
133
+ parent_lead_title = parent_lead_title(page)
134
+ main_title = page.title.main
135
+
136
+ # If the lead title is different to the parent's or we're not on a leaf
137
+ # node item, use the lead title as the main title. This causes 'section: Card'
138
+ # to be used as title for the Card item, but allows the nav-subgroup-heading
139
+ # 'Introduction' item to use the main title 'Introduction'.
140
+ main_title = lead_title unless lead_title == parent_lead_title || is_leaf
141
+
142
+ return main_title || page.title.to_s
143
+ end
144
+
145
+ ''
146
+ end
147
+
148
+ def parent_lead_title(page)
149
+ return page.parent.title.lead unless page.parent.nil? || page.parent.title.nil?
150
+
151
+ nil
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'jekyll'
4
+ require 'nokogiri'
5
+ require_relative 'sidebar_path'
6
+ require_relative 'sidebar_page_title'
7
+ require_relative 'sidebar_page_collection'
8
+ require_relative 'sidebar_text_builder'
9
+
10
+ module SwedbankPay
11
+ # Represents a jekyll_page in the Sidebar
12
+ class SidebarPage
13
+ FIXNUM_MAX = (2**(0.size * 8 - 2) - 1)
14
+
15
+ attr_reader :path, :title, :level, :order, :children, :name, :filename, :doc
16
+ attr_accessor :headers, :sidebar_container, :number, :parent
17
+
18
+ def initialize(jekyll_page)
19
+ raise ArgumentError, 'jekyll_page cannot be nil' if jekyll_page.nil?
20
+ raise ArgumentError, 'jekyll_page must be a Jekyll::Page' unless jekyll_page.is_a? Jekyll::Page
21
+
22
+ @filename = jekyll_page.destination('')
23
+ @jekyll_page = jekyll_page
24
+ sidebar_path = SidebarPath.new(jekyll_page['url'])
25
+ @path = sidebar_path.to_s
26
+ @parent = sidebar_path.parent
27
+ @level = sidebar_path.level
28
+ @name = sidebar_path.name
29
+ @hide_from_sidebar = jekyll_page['hide_from_sidebar'].nil? ? false : jekyll_page['hide_from_sidebar']
30
+ @title = SidebarPageTitle.parse(jekyll_page, self)
31
+ @order = menu_order(jekyll_page)
32
+ @children = SidebarPageCollection.new(self)
33
+ end
34
+
35
+ def active?(current, is_leaf: false)
36
+ current_path = find_path(current)
37
+
38
+ return true if @path == current_path
39
+
40
+ # If we're on a leaf node item, such as when rendering the first header
41
+ # item of a sub-group, its children's active state must be disregarded.
42
+ unless is_leaf
43
+ @children.each do |child|
44
+ return true if child.active?(current_path, is_leaf: is_leaf)
45
+ end
46
+ end
47
+
48
+ false
49
+ end
50
+
51
+ def hidden?
52
+ return true if @title.nil?
53
+ return true if @hide_from_sidebar
54
+
55
+ false
56
+ end
57
+
58
+ def hidden_for?(other_page)
59
+ # The current page should be hidden for the other page unless the
60
+ # other page is also hidden.
61
+ hidden = hidden?
62
+
63
+ if other_page.nil? || !other_page.is_a?(SidebarPage)
64
+ Jekyll.logger.debug(" Sidebar: Other page '#{other_page}' is nil or not a SidebarPage")
65
+ return hidden
66
+ end
67
+
68
+ # If the other page is hidden, the current page should not be hidden
69
+ # from it.
70
+ return false if other_page.hidden? && in_same_section_as?(other_page)
71
+
72
+ hidden
73
+ end
74
+
75
+ def children=(children)
76
+ @children = SidebarPageCollection.new(self, children)
77
+ end
78
+
79
+ def to_s
80
+ SidebarTextBuilder.new(self).to_s
81
+ end
82
+
83
+ def inspect
84
+ to_s
85
+ end
86
+
87
+ def <=>(other)
88
+ return -1 if other.nil?
89
+
90
+ if @order == FIXNUM_MAX && other.order == FIXNUM_MAX
91
+ return 0 if @title.nil? && other.title.nil?
92
+ return -1 if other.title.nil?
93
+ return 1 if title.nil?
94
+
95
+ return @title <=> other.title
96
+ end
97
+
98
+ @order <=> other.order
99
+ end
100
+
101
+ def enrich_jekyll
102
+ if @title.nil?
103
+ Jekyll.logger.debug(" Sidebar: No title for #{@name}")
104
+ return
105
+ end
106
+
107
+ Jekyll.logger.debug(" Sidebar: <#{@path}>.lead_title('#{@title.lead}').main_title('#{@title.main}')")
108
+
109
+ @jekyll_page.data['lead_title'] = @title.lead
110
+ @jekyll_page.data['main_title'] = @title.main
111
+ end
112
+
113
+ def save
114
+ Jekyll.logger.debug(" Writing Sidebar: #{filename}")
115
+
116
+ File.open(@filename, 'w') do |file|
117
+ html = @doc.to_html(encoding: 'UTF-8')
118
+ file.write(html)
119
+ end
120
+ end
121
+
122
+ def children?
123
+ !children.nil? && children.any?
124
+ end
125
+
126
+ def headers?
127
+ !headers.nil? && headers.any?
128
+ end
129
+
130
+ def coordinate
131
+ return @number.to_s if @parent.nil?
132
+ return @number.to_s unless @parent.respond_to? :coordinate
133
+
134
+ "#{@parent.coordinate}.#{@number}"
135
+ end
136
+
137
+ def load
138
+ @doc = File.open(@filename) { |f| Nokogiri::HTML(f) }
139
+ @doc
140
+ end
141
+
142
+ private
143
+
144
+ def menu_order(jekyll_page)
145
+ order = jekyll_page['menu_order']
146
+ return FIXNUM_MAX if order.nil? || order.to_s.empty?
147
+
148
+ order.to_i
149
+ end
150
+
151
+ def eq?(path)
152
+ @path == path
153
+ end
154
+
155
+ def child_of?(path)
156
+ @path.split('/').length > @level && path.start_with?(@path)
157
+ end
158
+
159
+ def find_path(current)
160
+ if current.nil?
161
+ Jekyll.logger.warn(' Sidebar: Nil current_page')
162
+ return ''
163
+ end
164
+
165
+ return current if current.is_a? String
166
+ return current.path if current.respond_to?(:path)
167
+
168
+ Jekyll.logger.warn(" Sidebar: #{current.class} ('#{current}') does not respond to :path.")
169
+
170
+ ''
171
+ end
172
+
173
+ def in_same_section_as?(other_page)
174
+ # If this or the other page is the root index page, just ignore the
175
+ # hidden state completely
176
+ return false if other_page.path == '/' || @path == '/'
177
+
178
+ other_page.path.start_with?(@path) || @path.start_with?(other_page.path)
179
+ end
180
+ end
181
+ end