jekyll-theme-centos 2.52.0.beta.55 → 2.52.0.beta.57

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/_config.yml +1 -1
  3. data/_data/base/backtotop_schema.yml +6 -0
  4. data/_data/base/datatable_schema.yml +3 -3
  5. data/_data/base/head_schema.yml +19 -0
  6. data/_data/base/heading_anchor.yml +1 -3
  7. data/_data/base/heading_anchor_schema.yml +1 -1
  8. data/_data/base/highlight_schema.yml +30 -0
  9. data/_data/base/ogp.yml +23 -0
  10. data/_data/base/ogp_schema.yml +14 -22
  11. data/_includes/base/backtotop.html.liquid +10 -2
  12. data/_includes/base/copyvalue.html.liquid +10 -1
  13. data/_includes/base/datatable.html.liquid +8 -3
  14. data/_includes/base/fontawesome.html.liquid +2 -2
  15. data/_includes/base/{partials/footer.html.liquid → footer.html.liquid} +2 -2
  16. data/_includes/base/{partials/head.html.liquid → head.html.liquid} +54 -14
  17. data/_includes/base/heading_anchor.html.liquid +8 -3
  18. data/_includes/base/highlight.html.liquid +14 -9
  19. data/_includes/base/{partials/navbar.html.liquid → navbar.html.liquid} +9 -6
  20. data/_includes/base/navindex.html.liquid +2 -2
  21. data/_includes/base/ogp.html.liquid +58 -56
  22. data/_includes/base/shortcut.html.liquid +2 -1
  23. data/_layouts/base/default.html +7 -10
  24. data/_sass/base/_customization.scss +27 -13
  25. data/assets/img/base/example-ogp-image.png +0 -0
  26. data/assets/img/base/example-ogp-image.svg +297 -0
  27. data/assets/img/base/page-layout-default.png +0 -0
  28. data/assets/img/base/page-layout-default.svg +4 -4
  29. data/assets/img/base/page-with-ogp.png +0 -0
  30. data/assets/img/base/page-with-ogp.svg +333 -300
  31. data/assets/img/centos-social-share.png +0 -0
  32. data/assets/js/base/backtotop.js +7 -10
  33. data/assets/js/base/copyvalue.js +96 -94
  34. data/assets/js/base/datatable.js +28 -42
  35. data/assets/js/base/heading-anchor.js +53 -53
  36. data/assets/js/base/highlight.js +9 -3
  37. data/assets/js/base/init-tooltips.js +10 -4
  38. metadata +15 -13
  39. data/_data/base/partials/head_schema.yml +0 -47
  40. data/_includes/base/partials/script.html.liquid +0 -47
  41. /data/_data/base/{partials/content.yml → content.yml} +0 -0
  42. /data/_data/base/{partials/content_schema.yml → content_schema.yml} +0 -0
  43. /data/_data/base/{partials/footer.yml → footer.yml} +0 -0
  44. /data/_data/base/{partials/footer_schema.yml → footer_schema.yml} +0 -0
  45. /data/_data/base/{partials/navbar.yml → navbar.yml} +0 -0
  46. /data/_data/base/{partials/navbar_schema.yml → navbar_schema.yml} +0 -0
  47. /data/_data/base/{partials/script_schema.yml → script_schema.yml} +0 -0
Binary file
@@ -3,21 +3,18 @@
3
3
  // Applied via backtotop.html.liquid template with data attributes
4
4
 
5
5
  (function () {
6
- // Find this script tag and extract its configuration
7
- const scriptTag = document.getElementById('centos-backtop');
6
+ const scriptTag = document.getElementById('centos-backtop')
8
7
 
9
8
  if (!scriptTag) {
10
- console.warn('Back-to-Top: script tag with id "centos-backtop" not found');
11
- return;
9
+ console.warn('Back-to-Top: script tag with id "centos-backtop" not found')
10
+ return
12
11
  }
13
12
 
14
- // Read configuration from data attributes
15
13
  const config = {
16
- diameter: parseInt(scriptTag.dataset.diameter || 56),
14
+ diameter: parseInt(scriptTag.dataset.diameter || '56', 10),
17
15
  backgroundColor: scriptTag.dataset.backgroundColor || '#A54C93',
18
16
  textColor: scriptTag.dataset.textColor || '#ffffff'
19
- };
17
+ }
20
18
 
21
- // Initialize back-to-top button with configuration
22
- addBackToTop(config);
23
- })();
19
+ addBackToTop(config)
20
+ })()
@@ -3,66 +3,67 @@
3
3
  // Safely handles escaped and unescaped values, HTML entities, and special characters
4
4
  // ARMORED IMPLEMENTATION: Handles any value passed via data-copy-value attribute
5
5
 
6
+ (function () {
6
7
  class CopyButtonHandler {
7
- constructor(button, config = {}) {
8
- this.button = button;
9
- this.originalTitle = this.button.getAttribute('data-bs-title') || this.button.getAttribute('aria-label') || this.button.getAttribute('title');
10
- this.originalSvgHTML = this.button.innerHTML;
11
- this.resetDelay = config.resetDelay || 2000;
12
- this.checkmarkPath = config.checkmarkPath || 'M434.8 70.1c14.3 10.4 17.5 30.4 7.1 44.7l-256 352c-5.5 7.6-14 12.3-23.4 13.1s-18.5-2.7-25.1-9.3l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l101.5 101.5 234-321.7c10.4-14.3 30.4-17.5 44.7-7.1z';
13
- this.checkmarkViewbox = config.checkmarkViewbox || '0 0 448 512';
14
- this.successMessage = config.successMessage || 'Copied!';
15
-
16
- this.button.addEventListener('click', (e) => this.handleClick(e));
8
+ constructor (button, config = {}) {
9
+ this.button = button
10
+ this.originalTitle = this.button.getAttribute('data-bs-title') || this.button.getAttribute('aria-label') || this.button.getAttribute('title')
11
+ this.originalSvgHTML = this.button.innerHTML
12
+ this.resetDelay = config.resetDelay || 2000
13
+ this.checkmarkPath = config.checkmarkPath || 'M434.8 70.1c14.3 10.4 17.5 30.4 7.1 44.7l-256 352c-5.5 7.6-14 12.3-23.4 13.1s-18.5-2.7-25.1-9.3l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l101.5 101.5 234-321.7c10.4-14.3 30.4-17.5 44.7-7.1z'
14
+ this.checkmarkViewbox = config.checkmarkViewbox || '0 0 448 512'
15
+ this.successMessage = config.successMessage || 'Copied!'
16
+
17
+ this.button.addEventListener('click', (e) => this.handleClick(e))
17
18
  }
18
19
 
19
- handleClick(e) {
20
- e.preventDefault();
20
+ handleClick (e) {
21
+ e.preventDefault()
21
22
 
22
23
  // ARMOR: Retrieve the attribute value (may be escaped or unescaped)
23
- let rawValue = this.button.getAttribute('data-copy-value');
24
+ let rawValue = this.button.getAttribute('data-copy-value')
24
25
 
25
26
  // ARMOR: If attribute appears truncated or incomplete, try HTML extraction fallback
26
27
  const appearsTruncated = !rawValue ||
27
28
  rawValue.endsWith('=') ||
28
29
  rawValue.match(/^[a-zA-Z-]+$/) ||
29
- (rawValue.length < 5 && rawValue.includes(' '));
30
+ (rawValue.length < 5 && rawValue.includes(' '))
30
31
 
31
32
  if (appearsTruncated) {
32
- const extractedValue = this.extractDataCopyValueFromHTML();
33
+ const extractedValue = this.extractDataCopyValueFromHTML()
33
34
  if (extractedValue && extractedValue.length > (rawValue?.length || 0)) {
34
- rawValue = extractedValue;
35
+ rawValue = extractedValue
35
36
  }
36
37
  }
37
38
 
38
39
  // Defensive check: ensure we have a value to copy
39
40
  if (!rawValue) {
40
- console.warn('No copy value found in data-copy-value attribute', this.button);
41
- return;
41
+ console.warn('No copy value found in data-copy-value attribute', this.button)
42
+ return
42
43
  }
43
44
 
44
45
  // ARMOR: Auto-detect and handle both escaped and unescaped values
45
- const finalValue = this.normalizeAttributeValue(rawValue);
46
+ const finalValue = this.normalizeAttributeValue(rawValue)
46
47
 
47
- const svg = this.button.querySelector('svg');
48
+ const svg = this.button.querySelector('svg')
48
49
 
49
50
  if (!svg) {
50
- console.error('Icon SVG not found in button', this.button);
51
- return;
51
+ console.error('Icon SVG not found in button', this.button)
52
+ return
52
53
  }
53
54
 
54
55
  // ARMOR: Use trim() to remove accidental whitespace
55
56
  navigator.clipboard.writeText(finalValue.trim())
56
57
  .then(() => this.showSuccess(svg))
57
58
  .catch((err) => {
58
- console.error('Failed to copy value:', finalValue, 'Error:', err);
59
- });
59
+ console.error('Failed to copy value:', finalValue, 'Error:', err)
60
+ })
60
61
  }
61
62
 
62
- extractDataCopyValueFromHTML() {
63
+ extractDataCopyValueFromHTML () {
63
64
  // ARMOR: Extract data-copy-value from raw HTML string
64
65
  try {
65
- const outerHTML = this.button.outerHTML;
66
+ const outerHTML = this.button.outerHTML
66
67
 
67
68
  const patterns = [
68
69
  /data-copy-value="([^"]*)"/,
@@ -70,85 +71,85 @@ class CopyButtonHandler {
70
71
  /data-copy-value="((?:[^"\\]|\\.*)*)"/,
71
72
  /data-copy-value="([^"]*(?:&quot;[^"]*)*?)"/,
72
73
  /data-copy-value=["']?([^"\s>]+?)["'\s>]/,
73
- /data-copy-value="([^"]*(?:[^"]*?)?)"/,
74
- ];
74
+ /data-copy-value="([^"]*(?:[^"]*?)?)"/
75
+ ]
75
76
 
76
77
  for (const pattern of patterns) {
77
- const match = outerHTML.match(pattern);
78
+ const match = outerHTML.match(pattern)
78
79
  if (match && match[1] && match[1].length > 0) {
79
80
  if (!match[1].startsWith('data-') && !match[1].startsWith('class') && !match[1].startsWith('id')) {
80
- return match[1];
81
+ return match[1]
81
82
  }
82
83
  }
83
84
  }
84
85
 
85
- const attrIndex = outerHTML.indexOf('data-copy-value');
86
+ const attrIndex = outerHTML.indexOf('data-copy-value')
86
87
  if (attrIndex !== -1) {
87
- const afterAttr = outerHTML.substring(attrIndex + 15).trim();
88
+ const afterAttr = outerHTML.substring(attrIndex + 15).trim()
88
89
 
89
90
  if (afterAttr.startsWith('=')) {
90
- const afterEquals = afterAttr.substring(1).trim();
91
- const quoteChar = afterEquals[0];
91
+ const afterEquals = afterAttr.substring(1).trim()
92
+ const quoteChar = afterEquals[0]
92
93
 
93
94
  if (quoteChar === '"' || quoteChar === "'") {
94
- let endIndex = 1;
95
- let escaped = false;
95
+ let endIndex = 1
96
+ let escaped = false
96
97
 
97
98
  for (; endIndex < afterEquals.length; endIndex++) {
98
- const char = afterEquals[endIndex];
99
+ const char = afterEquals[endIndex]
99
100
 
100
101
  if (escaped) {
101
- escaped = false;
102
- continue;
102
+ escaped = false
103
+ continue
103
104
  }
104
105
 
105
106
  if (char === '\\') {
106
- escaped = true;
107
- continue;
107
+ escaped = true
108
+ continue
108
109
  }
109
110
 
110
111
  if (char === quoteChar) {
111
- return afterEquals.substring(1, endIndex);
112
+ return afterEquals.substring(1, endIndex)
112
113
  }
113
114
  }
114
115
  }
115
116
  }
116
117
  }
117
118
 
118
- return null;
119
+ return null
119
120
  } catch (err) {
120
- console.warn('Failed to extract data-copy-value from HTML:', err);
121
- return null;
121
+ console.warn('Failed to extract data-copy-value from HTML:', err)
122
+ return null
122
123
  }
123
124
  }
124
125
 
125
- normalizeAttributeValue(text) {
126
+ normalizeAttributeValue (text) {
126
127
  // ARMOR: Smart normalization for escaped and unescaped values
127
128
  if (!text) {
128
- return text;
129
+ return text
129
130
  }
130
131
 
131
- const hasHTMLEntities = /&(?:quot|apos|amp|lt|gt|#34|#39|#47|#60|#62|#x22|#x27|#x2[Ff]|#x3[Cc]|#x3[Ee]);/.test(text);
132
+ const hasHTMLEntities = /&(?:quot|apos|amp|lt|gt|#34|#39|#47|#60|#62|#x22|#x27|#x2[Ff]|#x3[Cc]|#x3[Ee]);/.test(text)
132
133
 
133
134
  if (hasHTMLEntities) {
134
- return this.decodeHTMLEntities(text);
135
+ return this.decodeHTMLEntities(text)
135
136
  }
136
137
 
137
- const hasUnescapedQuotes = /"(?![>\s])|'(?![>\s])/.test(text.trim());
138
- const hasUnescapedSpecialChars = /[<>]/.test(text);
138
+ const hasUnescapedQuotes = /"(?![>\s])|'(?![>\s])/.test(text.trim())
139
+ const hasUnescapedSpecialChars = /[<>]/.test(text)
139
140
 
140
141
  if (hasUnescapedQuotes || hasUnescapedSpecialChars) {
141
- return text;
142
+ return text
142
143
  }
143
144
 
144
- return text;
145
+ return text
145
146
  }
146
147
 
147
- decodeHTMLEntities(text) {
148
+ decodeHTMLEntities (text) {
148
149
  // ARMOR: Comprehensively decode HTML entities
149
- const textarea = document.createElement('textarea');
150
- textarea.innerHTML = text;
151
- let decoded = textarea.value;
150
+ const textarea = document.createElement('textarea')
151
+ textarea.innerHTML = text
152
+ let decoded = textarea.value
152
153
 
153
154
  decoded = decoded
154
155
  .replace(/&quot;/gi, '"')
@@ -165,87 +166,88 @@ class CopyButtonHandler {
165
166
  .replace(/&gt;/gi, '>')
166
167
  .replace(/&#62;/gi, '>')
167
168
  .replace(/&#x3[Ee];/g, '>')
168
- .replace(/&amp;/gi, '&');
169
+ .replace(/&amp;/gi, '&')
169
170
 
170
- return decoded;
171
+ return decoded
171
172
  }
172
173
 
173
- showSuccess(svg) {
174
- this.changeToCheckmark(svg);
175
- this.updateTooltip(this.successMessage);
176
- setTimeout(() => this.reset(svg), this.resetDelay);
174
+ showSuccess (svg) {
175
+ this.changeToCheckmark(svg)
176
+ this.updateTooltip(this.successMessage)
177
+ setTimeout(() => this.reset(svg), this.resetDelay)
177
178
  }
178
179
 
179
- changeToCheckmark(svg) {
180
- const path = svg.querySelector('path');
180
+ changeToCheckmark (svg) {
181
+ const path = svg.querySelector('path')
181
182
  if (path) {
182
- svg.setAttribute('viewBox', this.checkmarkViewbox);
183
- path.setAttribute('d', this.checkmarkPath);
183
+ svg.setAttribute('viewBox', this.checkmarkViewbox)
184
+ path.setAttribute('d', this.checkmarkPath)
184
185
  }
185
186
  }
186
187
 
187
- updateTooltip(message) {
188
+ updateTooltip (message) {
188
189
  if (!message || typeof message !== 'string') {
189
- console.warn('Invalid tooltip message:', message);
190
- return;
190
+ console.warn('Invalid tooltip message:', message)
191
+ return
191
192
  }
192
193
 
193
- this.button.setAttribute('data-bs-title', message);
194
+ this.button.setAttribute('data-bs-title', message)
194
195
 
195
196
  try {
196
- const tooltip = bootstrap.Tooltip.getInstance(this.button);
197
+ const tooltip = bootstrap.Tooltip.getInstance(this.button)
197
198
  if (tooltip) {
198
- tooltip._config.title = message;
199
- tooltip.update();
200
- tooltip.show();
199
+ tooltip._config.title = message
200
+ tooltip.update()
201
+ tooltip.show()
201
202
  }
202
203
  } catch (err) {
203
- console.warn('Bootstrap Tooltip unavailable or error updating tooltip:', err);
204
+ console.warn('Bootstrap Tooltip unavailable or error updating tooltip:', err)
204
205
  }
205
206
  }
206
207
 
207
- reset(svg) {
208
- this.button.innerHTML = this.originalSvgHTML;
209
- this.updateTooltip(this.originalTitle);
208
+ reset (svg) {
209
+ this.button.innerHTML = this.originalSvgHTML
210
+ this.updateTooltip(this.originalTitle)
210
211
 
211
212
  try {
212
- const tooltip = bootstrap.Tooltip.getInstance(this.button);
213
+ const tooltip = bootstrap.Tooltip.getInstance(this.button)
213
214
  if (tooltip) {
214
- tooltip.hide();
215
+ tooltip.hide()
215
216
  }
216
217
  } catch (err) {
217
- console.warn('Bootstrap Tooltip unavailable or error hiding tooltip:', err);
218
+ console.warn('Bootstrap Tooltip unavailable or error hiding tooltip:', err)
218
219
  }
219
220
  }
220
221
  }
221
222
 
222
223
  // Initialize all copy buttons when DOM is ready
223
- document.addEventListener('DOMContentLoaded', function() {
224
+ document.addEventListener('DOMContentLoaded', function () {
224
225
  // Get configuration from script's data attributes
225
- const scriptElement = document.getElementById('centos-copyvalue');
226
- const config = {};
226
+ const scriptElement = document.getElementById('centos-copyvalue')
227
+ const config = {}
227
228
 
228
229
  if (scriptElement) {
229
230
  if (scriptElement.dataset.resetDelay) {
230
- config.resetDelay = parseInt(scriptElement.dataset.resetDelay, 10);
231
+ config.resetDelay = parseInt(scriptElement.dataset.resetDelay, 10)
231
232
  }
232
233
  if (scriptElement.dataset.checkmarkPath) {
233
- config.checkmarkPath = scriptElement.dataset.checkmarkPath;
234
+ config.checkmarkPath = scriptElement.dataset.checkmarkPath
234
235
  }
235
236
  if (scriptElement.dataset.checkmarkViewbox) {
236
- config.checkmarkViewbox = scriptElement.dataset.checkmarkViewbox;
237
+ config.checkmarkViewbox = scriptElement.dataset.checkmarkViewbox
237
238
  }
238
239
  if (scriptElement.dataset.successMessage) {
239
- config.successMessage = scriptElement.dataset.successMessage;
240
+ config.successMessage = scriptElement.dataset.successMessage
240
241
  }
241
242
  }
242
243
 
243
- const copyButtons = document.querySelectorAll('.copy-btn');
244
+ const copyButtons = document.querySelectorAll('.copy-btn')
244
245
  copyButtons.forEach(button => {
245
246
  try {
246
- new CopyButtonHandler(button, config);
247
+ new CopyButtonHandler(button, config)
247
248
  } catch (err) {
248
- console.error('Failed to initialize copy button:', button, 'Error:', err);
249
+ console.error('Failed to initialize copy button:', button, 'Error:', err)
249
250
  }
250
- });
251
- });
251
+ })
252
+ })
253
+ })()
@@ -2,54 +2,40 @@
2
2
  // Reads configuration from data attributes on this script tag
3
3
  // Applied via datatable.html.liquid template with data attributes
4
4
 
5
- (function() {
6
- // Find this script tag and extract its configuration
7
- const scriptTag = document.getElementById('centos-datatable');
5
+ (function () {
6
+ if (typeof DataTable === 'undefined') {
7
+ console.warn('datatable: DataTable not available')
8
+ return
9
+ }
10
+
11
+ const scriptTag = document.getElementById('centos-datatable')
8
12
 
9
13
  if (!scriptTag) {
10
- console.warn('DataTables: script tag with id "centos-datatable" not found');
11
- return;
14
+ console.warn('DataTables: script tag with id "centos-datatable" not found')
15
+ return
12
16
  }
13
17
 
14
- // Read configuration from data attributes
15
- const pageLength = parseInt(scriptTag.dataset.pageLength || 10);
16
- const lengthMenuStr = scriptTag.dataset.lengthMenu || '[10,25,50,100]';
18
+ const pageLength = parseInt(scriptTag.dataset.pageLength || '10', 10)
19
+ const lengthMenuStr = scriptTag.dataset.lengthMenu || '[10,25,50,75]'
17
20
 
18
- // Parse length menu array (stored as JSON string in data attribute)
19
- let lengthMenu = [10, 25, 50, 100];
21
+ let lengthMenu = [10, 25, 50, 100]
20
22
  try {
21
- lengthMenu = JSON.parse(lengthMenuStr);
23
+ lengthMenu = JSON.parse(lengthMenuStr)
22
24
  } catch (e) {
23
- console.warn('DataTables: Invalid lengthMenu format, using default:', e);
25
+ console.warn('DataTables: Invalid lengthMenu format, using default:', e)
24
26
  }
25
27
 
26
- // Initialize on document ready
27
- $(document).ready(function() {
28
- // Target ALL tables that have the 'dataTable' class
29
- const dataTableElements = $('table.dataTable');
30
-
31
- if (dataTableElements.length > 0) {
32
- // Initialize DataTables for ALL targeted tables found on the page
33
- dataTableElements.each(function() {
34
- const tableElement = $(this);
35
-
36
- // Get the number of rows in the table body
37
- const rowCount = tableElement.find('tbody tr').length;
38
-
39
- // Determine if searching should be enabled (only if > 10 rows)
40
- const enabledCondition = (rowCount > 10);
41
-
42
- // Initialize DataTable with configuration
43
- tableElement.DataTable({
44
- responsive: true,
45
- paging: enabledCondition,
46
- info: enabledCondition,
47
- order: [[0, 'asc']], // Order by first column
48
- searching: enabledCondition,
49
- pageLength: pageLength,
50
- lengthMenu: lengthMenu
51
- });
52
- });
53
- }
54
- });
55
- })();
28
+ document.querySelectorAll('table.dataTable').forEach(function (tableEl) {
29
+ const rowCount = tableEl.querySelectorAll('tbody tr').length
30
+ const enabledCondition = rowCount > 10
31
+
32
+ new DataTable(tableEl, {
33
+ paging: enabledCondition,
34
+ info: enabledCondition,
35
+ searching: enabledCondition,
36
+ order: [[0, 'asc']],
37
+ pageLength: pageLength,
38
+ lengthMenu: lengthMenu
39
+ })
40
+ })
41
+ })()
@@ -2,107 +2,107 @@
2
2
 
3
3
  document.addEventListener('DOMContentLoaded', () => {
4
4
  if (typeof bootstrap === 'undefined') {
5
- console.warn('heading-anchor: Bootstrap not available');
6
- return;
5
+ console.warn('heading-anchor: Bootstrap not available')
6
+ return
7
7
  }
8
8
 
9
- const DEFAULT_TEXT = 'Copy link';
10
- const scriptEl = document.getElementById('centos-heading-anchor');
9
+ const DEFAULT_TEXT = 'Copy link'
10
+ const scriptEl = document.getElementById('centos-heading-anchor')
11
11
  const RESET_DELAY = scriptEl?.dataset.resetDelay
12
- ? parseInt(scriptEl.dataset.resetDelay, 10) : 1500;
13
- const SUCCESS_TEXT = scriptEl?.dataset.successMessage || 'Copied!';
12
+ ? parseInt(scriptEl.dataset.resetDelay, 10) : 2000
13
+ const SUCCESS_TEXT = scriptEl?.dataset.successMessage || 'Copied!'
14
14
 
15
15
  // One shared region: multiple regions would cause simultaneous AT announcements per anchor.
16
- let liveRegion = document.getElementById('centos-heading-anchor-live');
16
+ let liveRegion = document.getElementById('centos-heading-anchor-live')
17
17
  if (!liveRegion) {
18
- liveRegion = document.createElement('div');
19
- liveRegion.id = 'centos-heading-anchor-live';
20
- liveRegion.setAttribute('aria-live', 'polite');
21
- liveRegion.setAttribute('aria-atomic', 'true');
22
- liveRegion.className = 'visually-hidden';
23
- document.body.appendChild(liveRegion);
18
+ liveRegion = document.createElement('div')
19
+ liveRegion.id = 'centos-heading-anchor-live'
20
+ liveRegion.setAttribute('aria-live', 'polite')
21
+ liveRegion.setAttribute('aria-atomic', 'true')
22
+ liveRegion.className = 'visually-hidden'
23
+ document.body.appendChild(liveRegion)
24
24
  }
25
25
 
26
26
  document.querySelectorAll('.heading-anchor[data-copy-anchor]').forEach(anchor => {
27
27
  // FA SVG+JS replaces <i> with <svg> before this DOMContentLoaded fires.
28
28
  // Cache innerHTML after FA has processed so it can be restored on reset.
29
- const originalIconHTML = anchor.innerHTML;
29
+ const originalIconHTML = anchor.innerHTML
30
30
  // Guards blur/mouseenter handlers from collapsing the tooltip during the success window.
31
- let isLocked = false;
31
+ let isLocked = false
32
32
 
33
33
  const tooltip = new bootstrap.Tooltip(anchor, {
34
34
  title: DEFAULT_TEXT,
35
35
  trigger: 'manual', // CSS sets pointer-events:none on hidden anchors, defeating Bootstrap's auto hover/focus triggers.
36
36
  placement: 'top',
37
37
  animation: false // Prevents Bootstrap's fade and the CSS opacity transition from desyncing.
38
- });
38
+ })
39
39
 
40
40
  const updateUI = (isSuccess) => {
41
- tooltip.setContent({ '.tooltip-inner': isSuccess ? SUCCESS_TEXT : DEFAULT_TEXT });
41
+ tooltip.setContent({ '.tooltip-inner': isSuccess ? SUCCESS_TEXT : DEFAULT_TEXT })
42
42
  if (isSuccess) {
43
43
  // FA SVG+JS mode: <i> has been replaced by <svg>; swap the path to a checkmark.
44
- const svg = anchor.querySelector('svg');
44
+ const svg = anchor.querySelector('svg')
45
45
  if (svg) {
46
- const path = svg.querySelector('path');
46
+ const path = svg.querySelector('path')
47
47
  if (path) {
48
- svg.setAttribute('viewBox', '0 0 448 512');
49
- path.setAttribute('d', 'M434.8 70.1c14.3 10.4 17.5 30.4 7.1 44.7l-256 352c-5.5 7.6-14 12.3-23.4 13.1s-18.5-2.7-25.1-9.3l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l101.5 101.5 234-321.7c10.4-14.3 30.4-17.5 44.7-7.1z');
48
+ svg.setAttribute('viewBox', '0 0 448 512')
49
+ path.setAttribute('d', 'M434.8 70.1c14.3 10.4 17.5 30.4 7.1 44.7l-256 352c-5.5 7.6-14 12.3-23.4 13.1s-18.5-2.7-25.1-9.3l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l101.5 101.5 234-321.7c10.4-14.3 30.4-17.5 44.7-7.1z')
50
50
  }
51
51
  } else {
52
52
  // FA webfont mode: <i> is still in the DOM; toggle classes normally.
53
- const icon = anchor.querySelector('i');
54
- if (icon) { icon.classList.remove('fa-link'); icon.classList.add('fa-check'); }
53
+ const icon = anchor.querySelector('i')
54
+ if (icon) { icon.classList.remove('fa-hashtag'); icon.classList.add('fa-check') }
55
55
  }
56
56
  } else {
57
- anchor.innerHTML = originalIconHTML;
57
+ anchor.innerHTML = originalIconHTML
58
58
  }
59
- anchor.classList.toggle('is-active', isSuccess);
59
+ anchor.classList.toggle('is-active', isSuccess)
60
60
  // Empty string on reset prevents AT from re-announcing stale text on DOM inspection.
61
- liveRegion.textContent = isSuccess ? SUCCESS_TEXT : '';
62
- if (isSuccess) tooltip.show();
63
- };
61
+ liveRegion.textContent = isSuccess ? SUCCESS_TEXT : ''
62
+ if (isSuccess) tooltip.show()
63
+ }
64
64
 
65
65
  const handleInteraction = (show) => {
66
- if (!isLocked) show ? tooltip.show() : tooltip.hide();
67
- };
66
+ if (!isLocked) show ? tooltip.show() : tooltip.hide()
67
+ }
68
68
 
69
- anchor.addEventListener('mouseenter', () => handleInteraction(true));
70
- anchor.addEventListener('mouseleave', () => handleInteraction(false));
71
- anchor.addEventListener('focus', () => handleInteraction(true));
72
- anchor.addEventListener('blur', () => handleInteraction(false));
69
+ anchor.addEventListener('mouseenter', () => handleInteraction(true))
70
+ anchor.addEventListener('mouseleave', () => handleInteraction(false))
71
+ anchor.addEventListener('focus', () => handleInteraction(true))
72
+ anchor.addEventListener('blur', () => handleInteraction(false))
73
73
 
74
74
  anchor.addEventListener('click', async (e) => {
75
- e.preventDefault();
76
- if (isLocked) return;
75
+ e.preventDefault()
76
+ if (isLocked) return
77
77
 
78
78
  if (!navigator.clipboard) {
79
- console.warn('heading-anchor: Clipboard API unavailable');
80
- return;
79
+ console.warn('heading-anchor: Clipboard API unavailable')
80
+ return
81
81
  }
82
82
 
83
- const url = window.location.origin + window.location.pathname + anchor.dataset.copyAnchor;
83
+ const url = window.location.origin + window.location.pathname + anchor.dataset.copyAnchor
84
84
 
85
85
  try {
86
- await navigator.clipboard.writeText(url);
87
- isLocked = true;
86
+ await navigator.clipboard.writeText(url)
87
+ isLocked = true
88
88
  // isLocked must be true before blur() so the resulting blur event is suppressed
89
89
  // and does not hide the tooltip before the success state has been rendered.
90
- anchor.blur();
91
- updateUI(true);
90
+ anchor.blur()
91
+ updateUI(true)
92
92
 
93
93
  setTimeout(() => {
94
- isLocked = false;
95
- updateUI(false);
94
+ isLocked = false
95
+ updateUI(false)
96
96
  // mouseleave/blur events fired while isLocked was true were suppressed;
97
97
  // hide now if the user already moved away during the success window.
98
- if (!anchor.matches(':hover')) tooltip.hide();
99
- }, RESET_DELAY);
98
+ if (!anchor.matches(':hover')) tooltip.hide()
99
+ }, RESET_DELAY)
100
100
 
101
101
  } catch (err) {
102
- console.warn('heading-anchor: clipboard write failed', err);
103
- isLocked = false;
104
- anchor.innerHTML = originalIconHTML;
102
+ console.warn('heading-anchor: clipboard write failed', err)
103
+ isLocked = false
104
+ anchor.innerHTML = originalIconHTML
105
105
  }
106
- });
107
- });
108
- });
106
+ })
107
+ })
108
+ })
@@ -1,6 +1,12 @@
1
1
  // Highlight.js Initialization
2
2
  // Initializes syntax highlighting on all code blocks with 'hljs' class
3
3
  // Enables copy-to-clipboard functionality via highlightjs-copy plugin
4
- document.querySelectorAll('.content code').forEach(el => el.classList.add('hljs'));
5
- hljs.addPlugin(new CopyButtonPlugin());
6
- hljs.highlightAll();
4
+ (function () {
5
+ if (typeof hljs === 'undefined' || typeof CopyButtonPlugin === 'undefined') {
6
+ console.warn('highlight: hljs or CopyButtonPlugin not available')
7
+ return
8
+ }
9
+ document.querySelectorAll('.content code').forEach(el => el.classList.add('hljs'))
10
+ hljs.addPlugin(new CopyButtonPlugin())
11
+ hljs.highlightAll()
12
+ })()
@@ -1,6 +1,12 @@
1
1
  // Bootstrap Tooltip and Popover Initialization
2
- const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
3
- const tooltipList = [...tooltipTriggerList].map((el) => new bootstrap.Tooltip(el));
2
+ (function () {
3
+ if (typeof bootstrap === 'undefined') {
4
+ console.warn('init-tooltips: Bootstrap not available')
5
+ return
6
+ }
7
+ const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
8
+ ;[...tooltipTriggerList].forEach((el) => new bootstrap.Tooltip(el))
4
9
 
5
- const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
6
- const popoverList = [...popoverTriggerList].map((el) => new bootstrap.Popover(el));
10
+ const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
11
+ ;[...popoverTriggerList].forEach((el) => new bootstrap.Popover(el))
12
+ })()