datalog-theme 0.7.0 → 0.8.0

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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +125 -0
  3. data/CITATION.cff +2 -2
  4. data/README.md +23 -17
  5. data/_data/cdn-integrity.yml +0 -30
  6. data/_includes/analytics/dashboard.html +3 -1
  7. data/_includes/components/api-function.html +20 -1
  8. data/_includes/components/enhanced-code-block.html +1 -1
  9. data/_includes/csp-meta.html +115 -11
  10. data/_includes/footer.html +19 -20
  11. data/_includes/head.html +60 -23
  12. data/_includes/header/navigation.html +12 -15
  13. data/_includes/header.html +28 -19
  14. data/_includes/layouts/default/article.html +5 -1
  15. data/_includes/meta/math-config.html +9 -5
  16. data/_includes/meta/schema.html +5 -2
  17. data/_includes/meta/scripts-loader.html +16 -32
  18. data/_includes/post/related-posts.html +4 -7
  19. data/_includes/search/index-data.json +9 -34
  20. data/_layouts/dataset.html +1 -0
  21. data/_layouts/default.html +16 -8
  22. data/_layouts/notebook.html +1 -0
  23. data/_layouts/package.html +1 -0
  24. data/_layouts/portfolio.html +1 -0
  25. data/_layouts/post.html +20 -5
  26. data/_layouts/project.html +2 -1
  27. data/_plugins/analytics_dashboard.rb +9 -3
  28. data/_plugins/config_validator.rb +12 -7
  29. data/_plugins/csp_generator.rb +18 -28
  30. data/_plugins/datalog_bibliography.rb +9 -7
  31. data/_plugins/datalog_comments.rb +8 -5
  32. data/_plugins/datalog_slides.rb +9 -8
  33. data/_plugins/i18n.rb +7 -8
  34. data/_plugins/image_optimizer.rb +17 -6
  35. data/_plugins/math_preprocessor.rb +33 -3
  36. data/_plugins/notebook_converter.rb +23 -4
  37. data/_plugins/plugin_loader.rb +3 -1
  38. data/_plugins/publications_generator.rb +8 -2
  39. data/_plugins/rouge_highlight_filter.rb +42 -0
  40. data/_plugins/search_code_blocks.rb +30 -0
  41. data/_plugins/search_normalizer.rb +15 -53
  42. data/_plugins/search_pages.rb +3 -4
  43. data/_sass/_academic-dashboard.scss +262 -0
  44. data/_sass/_base.scss +12 -0
  45. data/_sass/_components.scss +64 -1153
  46. data/_sass/_features.scss +17 -0
  47. data/_sass/_layout.scss +1 -1
  48. data/_sass/_notebooks.scss +322 -0
  49. data/_sass/_open-science-badges.scss +56 -0
  50. data/_sass/{_phase1-enhancements.scss → _post-components.scss} +1 -1
  51. data/_sass/_search-page.scss +530 -0
  52. data/_sass/_search.scss +46 -0
  53. data/_sass/_syntax-highlighting.scss +212 -97
  54. data/_sass/_theme.scss +29 -19
  55. data/_sass/_typography.scss +14 -0
  56. data/assets/css/main.scss +14 -0
  57. data/assets/js/dist/academic.js +1 -1
  58. data/assets/js/dist/analytics-dashboard.js +1 -1
  59. data/assets/js/dist/chunks/chunk-225H5YXE.js +1 -0
  60. data/assets/js/dist/core.js +1 -1
  61. data/assets/js/dist/loader.js +1 -1
  62. data/assets/js/dist/notebook.js +1 -1
  63. data/assets/js/dist/search.js +1 -1
  64. data/assets/js/dist/visualizations.js +11 -2
  65. data/assets/js/loader.js +3 -1
  66. data/datalog-theme.gemspec +36 -23
  67. data/lib/datalog/cli.rb +34 -14
  68. data/lib/datalog/plugin_system/dependency_resolver.rb +0 -2
  69. data/lib/datalog/theme/package.rb +57 -0
  70. data/lib/datalog/theme/repository_checkout.rb +90 -0
  71. data/lib/datalog/theme/version.rb +1 -1
  72. data/lib/datalog/warning_filter.rb +5 -11
  73. data/lib/datalog-theme.rb +5 -0
  74. metadata +48 -133
  75. data/_data/academic.yml +0 -217
  76. data/_data/config/author.yml +0 -121
  77. data/_data/datasets.yml +0 -28
  78. data/_data/js_meta.json +0 -371
  79. data/_data/navigation.yml +0 -145
  80. data/_data/projects.yml +0 -41
  81. data/_data/publications.yml +0 -28
  82. data/_data/social.yml +0 -73
  83. data/_data/visualizations.yml +0 -51
  84. data/_includes/components/advanced-search.html +0 -682
  85. data/_includes/components/bookmark-system.html +0 -96
  86. data/_includes/components/comments.html +0 -244
  87. data/_includes/components/content-recommendations.html +0 -228
  88. data/_includes/components/email-preferences.html +0 -200
  89. data/_includes/components/enhanced-metadata.html +0 -228
  90. data/_includes/components/language-switcher.html +0 -396
  91. data/_includes/components/navigation-enhancements.html +0 -454
  92. data/_includes/components/newsletter-signup.html +0 -178
  93. data/_includes/components/popular-posts.html +0 -233
  94. data/_includes/components/reading-progress.html +0 -133
  95. data/_includes/components/reading-time.html +0 -121
  96. data/_includes/components/series-navigation.html +0 -124
  97. data/_includes/components/social-proof.html +0 -34
  98. data/_includes/components/user-preferences.html +0 -566
  99. data/_includes/meta/syntax-config.html +0 -19
  100. data/_layouts/archive.html +0 -282
  101. data/_layouts/post-sidebar.html +0 -183
  102. data/_sass/_phase3-enhancements.scss +0 -874
  103. data/_sass/_phase4-enhancements.scss +0 -1214
  104. data/_sass/_phase5-enhancements.scss +0 -414
  105. data/assets/js/academic.js +0 -262
  106. data/assets/js/analytics-dashboard.js +0 -382
  107. data/assets/js/core/dark-mode.js +0 -79
  108. data/assets/js/core/github-cards.js +0 -123
  109. data/assets/js/core/language-filter.js +0 -69
  110. data/assets/js/core/navigation.js +0 -184
  111. data/assets/js/core/scroll-progress.js +0 -45
  112. data/assets/js/core/search-hotkeys.js +0 -62
  113. data/assets/js/core/skip-links.js +0 -62
  114. data/assets/js/dist/manifest.json +0 -22
  115. data/assets/js/dist/meta.json +0 -371
  116. data/assets/js/main.js +0 -23
  117. data/assets/js/math.js +0 -818
  118. data/assets/js/notebook.js +0 -158
  119. data/assets/js/search/analytics.js +0 -91
  120. data/assets/js/search/app.js +0 -271
  121. data/assets/js/search/autocomplete.js +0 -120
  122. data/assets/js/search/engine.js +0 -260
  123. data/assets/js/search/filters.js +0 -45
  124. data/assets/js/search/render.js +0 -217
  125. data/assets/js/search/utils.js +0 -99
  126. data/assets/js/search.js +0 -354
  127. data/assets/js/visualizations.js +0 -816
  128. data/assets/publications/datalog-publications.bib +0 -8
  129. data/assets/publications/datalog-publications.ris +0 -9
  130. data/assets/publications/publications.bib +0 -30
  131. data/assets/templates/diogo-ribeiro-cv.md +0 -31
  132. data/assets/templates/diogo-ribeiro-cv.tex +0 -32
  133. data/lib/datalog/theme/theme.rb +0 -18
@@ -1,414 +0,0 @@
1
- // ============================================================================
2
- // Phase 5 Enhancements Stylesheet
3
- // Advanced features: i18n, search, bookmarks, performance, email
4
- // ============================================================================
5
-
6
- @use 'mixins' as *;
7
-
8
- // Language Switcher
9
- .language-switcher {
10
- position: relative;
11
-
12
- &__trigger {
13
- display: flex;
14
- align-items: center;
15
- gap: 0.5rem;
16
- padding: 0.5rem 1rem;
17
- background: transparent;
18
- border: 1px solid var(--color-border, #cbd5e0);
19
- border-radius: 6px;
20
- cursor: pointer;
21
- transition: all 0.2s ease;
22
-
23
- &:hover {
24
- background: var(--color-background-secondary, #f7fafc);
25
- border-color: var(--color-accent, #3b82f6);
26
- }
27
- }
28
-
29
- &__menu {
30
- position: absolute;
31
- top: calc(100% + 0.5rem);
32
- right: 0;
33
- min-width: 200px;
34
- background: white;
35
- border: 1px solid var(--color-border, #e2e8f0);
36
- border-radius: 8px;
37
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
38
- list-style: none;
39
- padding: 0.5rem 0;
40
- margin: 0;
41
- z-index: 100;
42
- }
43
-
44
- &__option {
45
- display: flex;
46
- align-items: center;
47
- gap: 0.75rem;
48
- width: 100%;
49
- padding: 0.75rem 1rem;
50
- background: transparent;
51
- border: none;
52
- cursor: pointer;
53
- transition: background 0.2s ease;
54
- text-align: left;
55
-
56
- &:hover {
57
- background: var(--color-background-secondary, #f7fafc);
58
- }
59
-
60
- &--active {
61
- background: var(--color-background-secondary, #f7fafc);
62
- font-weight: 600;
63
- }
64
- }
65
-
66
- &__flag {
67
- font-size: 1.25rem;
68
- }
69
-
70
- &__check {
71
- margin-left: auto;
72
- color: var(--color-accent, #3b82f6);
73
- }
74
- }
75
-
76
- // Advanced Search
77
- .advanced-search {
78
- max-width: 1000px;
79
- margin: 2rem auto;
80
- padding: 2rem;
81
-
82
- &__header {
83
- display: flex;
84
- gap: 1rem;
85
- margin-bottom: 1.5rem;
86
- }
87
-
88
- &__filters {
89
- padding: 1.5rem;
90
- background: var(--color-background-secondary, #f7fafc);
91
- border-radius: 8px;
92
- margin-bottom: 1.5rem;
93
- }
94
-
95
- &__toolbar {
96
- display: flex;
97
- justify-content: space-between;
98
- align-items: center;
99
- margin-bottom: 1.5rem;
100
- padding: 1rem;
101
- background: var(--color-background-secondary, #f7fafc);
102
- border-radius: 8px;
103
- }
104
- }
105
-
106
- .search-input-wrapper {
107
- flex: 1;
108
- position: relative;
109
- display: flex;
110
- align-items: center;
111
-
112
- &__icon {
113
- position: absolute;
114
- left: 1rem;
115
- color: var(--color-text-secondary, #4a5568);
116
- }
117
-
118
- &__input {
119
- width: 100%;
120
- padding: 0.875rem 3rem;
121
- border: 2px solid var(--color-border, #e2e8f0);
122
- border-radius: 8px;
123
- font-size: 1rem;
124
-
125
- &:focus {
126
- outline: none;
127
- border-color: var(--color-accent, #3b82f6);
128
- }
129
- }
130
-
131
- &__clear {
132
- position: absolute;
133
- right: 1rem;
134
- background: transparent;
135
- border: none;
136
- cursor: pointer;
137
- padding: 0.25rem;
138
- color: var(--color-text-secondary, #4a5568);
139
-
140
- &:hover {
141
- color: var(--color-text-primary, #1a202c);
142
- }
143
- }
144
- }
145
-
146
- .search-result {
147
- padding: 1.5rem;
148
- border: 1px solid var(--color-border, #e2e8f0);
149
- border-radius: 8px;
150
- margin-bottom: 1rem;
151
- transition: all 0.2s ease;
152
-
153
- &:hover {
154
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
155
- }
156
-
157
- &__title a {
158
- color: var(--color-text-primary, #1a202c);
159
- text-decoration: none;
160
- font-size: 1.25rem;
161
-
162
- &:hover {
163
- color: var(--color-accent, #3b82f6);
164
- }
165
- }
166
-
167
- &__excerpt {
168
- margin: 0.75rem 0;
169
- color: var(--color-text-secondary, #4a5568);
170
-
171
- mark {
172
- background: #fef3c7;
173
- padding: 0.125rem 0.25rem;
174
- border-radius: 2px;
175
- }
176
- }
177
-
178
- &__meta {
179
- display: flex;
180
- gap: 0.75rem;
181
- align-items: center;
182
- font-size: 0.875rem;
183
- color: var(--color-text-secondary, #4a5568);
184
- }
185
- }
186
-
187
- .search-filter {
188
- margin-bottom: 1.5rem;
189
-
190
- &__title {
191
- font-size: 0.9375rem;
192
- margin: 0 0 0.75rem;
193
- font-weight: 600;
194
- }
195
-
196
- &__options {
197
- display: flex;
198
- flex-direction: column;
199
- gap: 0.5rem;
200
-
201
- &--scrollable {
202
- max-height: 200px;
203
- overflow-y: auto;
204
- }
205
- }
206
- }
207
-
208
- .filter-checkbox, .filter-radio {
209
- display: flex;
210
- align-items: center;
211
- gap: 0.5rem;
212
- cursor: pointer;
213
- padding: 0.5rem;
214
- border-radius: 4px;
215
-
216
- &:hover {
217
- background: white;
218
- }
219
-
220
- input {
221
- cursor: pointer;
222
- }
223
-
224
- &__count {
225
- margin-left: auto;
226
- font-size: 0.8125rem;
227
- color: var(--color-text-secondary, #4a5568);
228
- }
229
- }
230
-
231
- // Bookmark System
232
- .bookmark-button {
233
- display: inline-flex;
234
- align-items: center;
235
- gap: 0.5rem;
236
- padding: 0.75rem 1.25rem;
237
- background: transparent;
238
- border: 2px solid var(--color-border, #cbd5e0);
239
- border-radius: 8px;
240
- cursor: pointer;
241
- transition: all 0.2s ease;
242
-
243
- &:hover {
244
- background: var(--color-background-secondary, #f7fafc);
245
- border-color: var(--color-accent, #3b82f6);
246
- }
247
-
248
- &--active {
249
- background: var(--color-accent, #3b82f6);
250
- border-color: var(--color-accent, #3b82f6);
251
- color: white;
252
-
253
- .bookmark-button__icon {
254
- fill: white;
255
- }
256
- }
257
-
258
- &__icon {
259
- transition: fill 0.2s ease;
260
- }
261
- }
262
-
263
- // Email Preferences
264
- .email-preferences {
265
- max-width: 600px;
266
- margin: 2rem auto;
267
- padding: 2rem;
268
- background: white;
269
- border-radius: 12px;
270
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
271
-
272
- h3 {
273
- margin-top: 0;
274
- }
275
-
276
- &__form {
277
- display: flex;
278
- flex-direction: column;
279
- gap: 1.5rem;
280
- }
281
- }
282
-
283
- .form-group {
284
- display: flex;
285
- flex-direction: column;
286
- gap: 0.5rem;
287
-
288
- label {
289
- font-weight: 600;
290
- font-size: 0.9375rem;
291
- }
292
-
293
- input[type="email"],
294
- select {
295
- padding: 0.75rem;
296
- border: 1px solid var(--color-border, #cbd5e0);
297
- border-radius: 6px;
298
-
299
- &:focus {
300
- outline: none;
301
- border-color: var(--color-accent, #3b82f6);
302
- }
303
- }
304
- }
305
-
306
- fieldset {
307
- border: 1px solid var(--color-border, #e2e8f0);
308
- border-radius: 8px;
309
- padding: 1rem;
310
-
311
- legend {
312
- padding: 0 0.5rem;
313
- font-weight: 600;
314
- }
315
- }
316
-
317
- .checkbox-label {
318
- display: flex;
319
- flex-direction: column;
320
- gap: 0.25rem;
321
- padding: 0.75rem;
322
- border-radius: 6px;
323
- cursor: pointer;
324
-
325
- &:hover {
326
- background: var(--color-background-secondary, #f7fafc);
327
- }
328
-
329
- input {
330
- cursor: pointer;
331
- }
332
-
333
- small {
334
- color: var(--color-text-secondary, #4a5568);
335
- font-size: 0.8125rem;
336
- }
337
- }
338
-
339
- .form-actions {
340
- display: flex;
341
- gap: 1rem;
342
- margin-top: 1rem;
343
- }
344
-
345
- .button {
346
- padding: 0.75rem 1.5rem;
347
- border: none;
348
- border-radius: 8px;
349
- cursor: pointer;
350
- font-weight: 600;
351
- transition: all 0.2s ease;
352
-
353
- &--primary {
354
- background: var(--color-accent, #3b82f6);
355
- color: white;
356
-
357
- &:hover {
358
- background: var(--color-accent-dark, #2563eb);
359
- }
360
- }
361
-
362
- &--secondary {
363
- background: transparent;
364
- border: 1px solid var(--color-border, #cbd5e0);
365
- color: var(--color-text-primary, #1a202c);
366
-
367
- &:hover {
368
- background: var(--color-background-secondary, #f7fafc);
369
- }
370
- }
371
- }
372
-
373
- .form-message {
374
- padding: 1rem;
375
- border-radius: 6px;
376
- text-align: center;
377
-
378
- &--success {
379
- background: #d1fae5;
380
- color: #065f46;
381
- }
382
-
383
- &--error {
384
- background: #fee2e2;
385
- color: #991b1b;
386
- }
387
- }
388
-
389
- // Responsive
390
- @include respond-below(md) {
391
- .advanced-search__header {
392
- flex-direction: column;
393
- }
394
-
395
- .advanced-search__toolbar {
396
- flex-direction: column;
397
- align-items: flex-start;
398
- gap: 1rem;
399
- }
400
-
401
- .form-actions {
402
- flex-direction: column;
403
- }
404
- }
405
-
406
- // Print
407
- @media print {
408
- .language-switcher,
409
- .advanced-search__filters,
410
- .bookmark-button,
411
- .email-preferences {
412
- display: none !important;
413
- }
414
- }
@@ -1,262 +0,0 @@
1
- /**
2
- * @fileoverview Academic features for citation display and research tools.
3
- * Handles citation metrics, publication lists, and academic integrations.
4
- * @module academic
5
- */
6
-
7
- /**
8
- * Populates citation metric elements with data from global academic data.
9
- * Supports total citations, h-index, i10-index, and yearly breakdowns.
10
- * @returns {void}
11
- */
12
- export function populateCitationMetrics() {
13
- const academicData = window.DatalogAcademic || {};
14
- const publicationsData = window.DatalogPublications || {};
15
- const metrics = publicationsData.metrics || (academicData.citations ? academicData.citations.metrics : {}) || {};
16
- const yearlyTotals = publicationsData.yearly_totals || (academicData.citations ? academicData.citations.yearly_totals : {}) || {};
17
-
18
- document.querySelectorAll('[data-citation-metric]').forEach((element) => {
19
- const key = element.getAttribute('data-citation-metric');
20
- if (!key) {
21
- return;
22
- }
23
- let value;
24
- switch (key) {
25
- case 'total':
26
- value = metrics.total;
27
- break;
28
- case 'h_index':
29
- case 'h-index':
30
- value = metrics.h_index;
31
- break;
32
- case 'i10_index':
33
- case 'i10-index':
34
- value = metrics.i10_index;
35
- break;
36
- case 'since_total':
37
- value = metrics.since_2019 ? metrics.since_2019.total : undefined;
38
- break;
39
- case 'since_h_index':
40
- value = metrics.since_2019 ? metrics.since_2019.h_index : undefined;
41
- break;
42
- case 'since_i10':
43
- value = metrics.since_2019 ? metrics.since_2019.i10_index : undefined;
44
- break;
45
- default:
46
- if (key.startsWith('year_')) {
47
- const yearKey = key.replace('year_', '');
48
- value = yearlyTotals[yearKey];
49
- }
50
- break;
51
- }
52
- if (typeof value === 'number') {
53
- element.textContent = value.toLocaleString();
54
- } else if (value) {
55
- element.textContent = value;
56
- }
57
- });
58
- }
59
-
60
- /**
61
- * Populates citation counts for individual publications.
62
- * @returns {void}
63
- */
64
- export function populatePublicationCitations() {
65
- const academicData = window.DatalogAcademic || {};
66
- const publicationsData = window.DatalogPublications || {};
67
- const perPublication = publicationsData.citation_totals || (academicData.citations ? academicData.citations.per_publication : {}) || {};
68
-
69
- document.querySelectorAll('[data-publication-citations]').forEach((element) => {
70
- const publicationId = element.getAttribute('data-publication-citations');
71
- if (!publicationId) {
72
- return;
73
- }
74
- const lower = publicationId.toLowerCase();
75
- let value;
76
- if (perPublication[publicationId]) {
77
- value = perPublication[publicationId].total || perPublication[publicationId];
78
- } else if (perPublication[lower]) {
79
- value = perPublication[lower].total || perPublication[lower];
80
- }
81
- if (typeof value === 'number') {
82
- element.textContent = value.toLocaleString();
83
- }
84
- });
85
- }
86
-
87
- /**
88
- * Populates the citation timeline chart with yearly data.
89
- * @returns {void}
90
- */
91
- export function populateCitationTimeline() {
92
- const academicData = window.DatalogAcademic || {};
93
- const publicationsData = window.DatalogPublications || {};
94
- const yearlyTotals = publicationsData.yearly_totals || (academicData.citations ? academicData.citations.yearly_totals : {}) || {};
95
-
96
- const timelineContainer = document.querySelector('[data-citation-timeline]');
97
- if (timelineContainer && yearlyTotals) {
98
- const listItems = timelineContainer.querySelectorAll('li[data-year]');
99
- listItems.forEach((item) => {
100
- const year = item.getAttribute('data-year');
101
- if (!year) {
102
- return;
103
- }
104
- const display = item.querySelector('[data-year-total]');
105
- const total = yearlyTotals[year];
106
- if (display && typeof total !== 'undefined') {
107
- display.textContent = Number(total).toLocaleString();
108
- item.style.setProperty('--timeline-value', Math.min(Number(total) / 5, 100));
109
- }
110
- });
111
- }
112
- }
113
-
114
- /**
115
- * Attaches copy-to-clipboard handlers to elements matching the selector.
116
- * @param {string} selector - CSS selector for trigger elements
117
- * @param {function(HTMLElement): string|string} getText - Function to get text or static text
118
- * @returns {void}
119
- */
120
- export function attachCopyHandler(selector, getText) {
121
- document.querySelectorAll(selector).forEach((trigger) => {
122
- trigger.addEventListener('click', () => {
123
- const text = typeof getText === 'function' ? getText(trigger) : getText;
124
- if (!text) {
125
- return;
126
- }
127
- if (navigator.clipboard && navigator.clipboard.writeText) {
128
- navigator.clipboard
129
- .writeText(text)
130
- .then(() => {
131
- trigger.dataset.copied = 'true';
132
- trigger.textContent = 'Copied!';
133
- setTimeout(() => {
134
- trigger.dataset.copied = 'false';
135
- trigger.textContent = trigger.dataset.originalText || 'Copy';
136
- }, 2000);
137
- })
138
- .catch(() => {
139
- trigger.dataset.copied = 'error';
140
- trigger.textContent = 'Error';
141
- });
142
- }
143
- });
144
- trigger.dataset.originalText = trigger.textContent;
145
- });
146
- }
147
-
148
- /**
149
- * Initializes copy handlers for citation and bibliography buttons.
150
- * @returns {void}
151
- */
152
- export function initCopyHandlers() {
153
- attachCopyHandler('[data-copy-citation]', (trigger) => {
154
- const targetId = trigger.getAttribute('data-target');
155
- if (!targetId) {
156
- return '';
157
- }
158
- const target = document.getElementById(targetId);
159
- return target ? target.value : '';
160
- });
161
-
162
- attachCopyHandler('[data-copy-bibliography]', (trigger) => {
163
- const targetId = trigger.getAttribute('data-target');
164
- if (!targetId) {
165
- return '';
166
- }
167
- const target = document.getElementById(targetId);
168
- return target ? target.value : '';
169
- });
170
- }
171
-
172
- /**
173
- * Initializes the submission status filter for paper tracking.
174
- * @returns {void}
175
- */
176
- export function initSubmissionFilter() {
177
- const submissionFilter = document.querySelector('[data-submission-filter]');
178
- if (submissionFilter) {
179
- const cards = document.querySelectorAll('.submission-card');
180
- const filterCards = () => {
181
- const value = submissionFilter.value;
182
- cards.forEach((card) => {
183
- if (!value) {
184
- card.hidden = false;
185
- return;
186
- }
187
- const status = card.getAttribute('data-submission-status');
188
- card.hidden = status !== value;
189
- });
190
- };
191
- submissionFilter.addEventListener('change', filterCards);
192
- filterCards();
193
- }
194
- }
195
-
196
- /**
197
- * Initializes the academic calendar event type filter.
198
- * @returns {void}
199
- */
200
- export function initCalendarFilter() {
201
- const calendarFilter = document.querySelector('[data-calendar-filter]');
202
- const calendarList = document.querySelector('[data-academic-calendar]');
203
- if (calendarFilter && calendarList) {
204
- const events = calendarList.querySelectorAll('li[data-event-type]');
205
- const filterEvents = () => {
206
- const value = calendarFilter.value;
207
- events.forEach((event) => {
208
- if (!value) {
209
- event.hidden = false;
210
- return;
211
- }
212
- const type = event.getAttribute('data-event-type');
213
- event.hidden = type !== value;
214
- });
215
- };
216
- calendarFilter.addEventListener('change', filterEvents);
217
- filterEvents();
218
- }
219
- }
220
-
221
- /**
222
- * Initializes open science badges with tooltip descriptions.
223
- * @returns {void}
224
- */
225
- export function initOpenScienceBadges() {
226
- const badges = document.querySelectorAll('.open-science-badge');
227
- badges.forEach((badge) => {
228
- const description = badge.querySelector('.open-science-badge__description');
229
- const criteria = badge.querySelector('.open-science-badge__criteria');
230
- const tooltipParts = [];
231
- if (description) {
232
- tooltipParts.push(description.textContent.trim());
233
- }
234
- if (criteria) {
235
- tooltipParts.push(criteria.textContent.trim());
236
- }
237
- if (tooltipParts.length) {
238
- badge.setAttribute('title', tooltipParts.join(' • '));
239
- }
240
- });
241
- }
242
-
243
- /**
244
- * Initializes all academic features on the page.
245
- * @returns {void}
246
- */
247
- export function initAcademicFeatures() {
248
- populateCitationMetrics();
249
- populatePublicationCitations();
250
- populateCitationTimeline();
251
- initCopyHandlers();
252
- initSubmissionFilter();
253
- initCalendarFilter();
254
- initOpenScienceBadges();
255
- }
256
-
257
- // Auto-initialize when module loads (backward compatibility)
258
- if (typeof window !== 'undefined' && document.readyState !== 'loading') {
259
- initAcademicFeatures();
260
- } else if (typeof window !== 'undefined') {
261
- document.addEventListener('DOMContentLoaded', initAcademicFeatures);
262
- }