datalog-theme 0.6.1 → 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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +175 -0
  3. data/CITATION.cff +2 -2
  4. data/README.md +26 -19
  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/author-bio.html +2 -2
  9. data/_includes/components/enhanced-code-block.html +1 -1
  10. data/_includes/components/hero.html +19 -1
  11. data/_includes/csp-meta.html +115 -11
  12. data/_includes/footer.html +19 -20
  13. data/_includes/head.html +61 -29
  14. data/_includes/header/navigation.html +12 -15
  15. data/_includes/header.html +29 -20
  16. data/_includes/layouts/default/article.html +5 -1
  17. data/_includes/meta/math-config.html +27 -1
  18. data/_includes/meta/schema.html +5 -2
  19. data/_includes/meta/scripts-loader.html +16 -31
  20. data/_includes/post/related-posts.html +4 -7
  21. data/_includes/scripts.html +1 -9
  22. data/_includes/search/index-data.json +131 -0
  23. data/_includes/search/page.html +136 -0
  24. data/_layouts/dataset.html +1 -0
  25. data/_layouts/default.html +16 -8
  26. data/_layouts/home.html +6 -0
  27. data/_layouts/notebook.html +1 -0
  28. data/_layouts/package.html +1 -0
  29. data/_layouts/portfolio.html +1 -0
  30. data/_layouts/post.html +20 -5
  31. data/_layouts/project.html +2 -1
  32. data/_plugins/analytics_dashboard.rb +9 -3
  33. data/_plugins/config_validator.rb +12 -7
  34. data/_plugins/csp_generator.rb +18 -28
  35. data/_plugins/datalog_bibliography.rb +9 -7
  36. data/_plugins/datalog_comments.rb +8 -5
  37. data/_plugins/datalog_slides.rb +9 -8
  38. data/_plugins/i18n.rb +7 -8
  39. data/_plugins/image_optimizer.rb +17 -6
  40. data/_plugins/math_preprocessor.rb +36 -3
  41. data/_plugins/notebook_converter.rb +23 -43
  42. data/_plugins/plugin_loader.rb +3 -1
  43. data/_plugins/publications_generator.rb +8 -2
  44. data/_plugins/rouge_highlight_filter.rb +42 -0
  45. data/_plugins/search_code_blocks.rb +30 -0
  46. data/_plugins/search_normalizer.rb +15 -53
  47. data/_plugins/search_pages.rb +72 -0
  48. data/_sass/_academic-dashboard.scss +262 -0
  49. data/_sass/_base.scss +19 -1
  50. data/_sass/_components.scss +77 -1144
  51. data/_sass/_features.scss +17 -0
  52. data/_sass/_font-fallbacks.scss +43 -0
  53. data/_sass/_header.scss +10 -0
  54. data/_sass/_layout.scss +18 -12
  55. data/_sass/_mathematical.scss +1 -1
  56. data/_sass/_notebooks.scss +322 -0
  57. data/_sass/_open-science-badges.scss +56 -0
  58. data/_sass/{_phase1-enhancements.scss → _post-components.scss} +46 -1
  59. data/_sass/_search-page.scss +530 -0
  60. data/_sass/_search.scss +47 -1
  61. data/_sass/_syntax-highlighting.scss +212 -97
  62. data/_sass/_theme.scss +42 -18
  63. data/_sass/_typography.scss +14 -0
  64. data/_sass/_variables.scss +7 -4
  65. data/assets/css/main.scss +14 -0
  66. data/assets/img/hero-detail-640.webp +0 -0
  67. data/assets/img/hero-detail.webp +0 -0
  68. data/assets/img/social-card.png +0 -0
  69. data/assets/js/dist/academic.js +1 -0
  70. data/assets/js/dist/analytics-dashboard.js +1 -0
  71. data/assets/js/dist/chunks/chunk-225H5YXE.js +1 -0
  72. data/assets/js/dist/core.js +1 -0
  73. data/assets/js/dist/loader.js +1 -0
  74. data/assets/js/dist/math.js +1 -0
  75. data/assets/js/dist/notebook.js +1 -0
  76. data/assets/js/dist/search.js +1 -0
  77. data/assets/js/dist/visualizations.js +11 -0
  78. data/assets/js/loader.js +3 -1
  79. data/datalog-theme.gemspec +43 -22
  80. data/lib/datalog/cli.rb +53 -15
  81. data/lib/datalog/plugin_system/dependency_resolver.rb +0 -2
  82. data/lib/datalog/theme/package.rb +57 -0
  83. data/lib/datalog/theme/repository_checkout.rb +90 -0
  84. data/lib/datalog/theme/version.rb +1 -1
  85. data/lib/datalog/warning_filter.rb +5 -11
  86. data/lib/datalog-theme.rb +21 -0
  87. metadata +75 -146
  88. data/_data/academic.yml +0 -217
  89. data/_data/config/author.yml +0 -121
  90. data/_data/config/features.yml +0 -262
  91. data/_data/config/site.yml +0 -42
  92. data/_data/config/theme.yml +0 -181
  93. data/_data/datasets.yml +0 -28
  94. data/_data/js_meta.json +0 -371
  95. data/_data/navigation.yml +0 -145
  96. data/_data/projects.yml +0 -41
  97. data/_data/publications.yml +0 -28
  98. data/_data/social.yml +0 -73
  99. data/_data/visualizations.yml +0 -51
  100. data/_includes/components/advanced-search.html +0 -682
  101. data/_includes/components/bookmark-system.html +0 -96
  102. data/_includes/components/comments.html +0 -244
  103. data/_includes/components/content-recommendations.html +0 -228
  104. data/_includes/components/email-preferences.html +0 -200
  105. data/_includes/components/enhanced-metadata.html +0 -228
  106. data/_includes/components/language-switcher.html +0 -396
  107. data/_includes/components/navigation-enhancements.html +0 -454
  108. data/_includes/components/newsletter-signup.html +0 -178
  109. data/_includes/components/popular-posts.html +0 -233
  110. data/_includes/components/reading-progress.html +0 -133
  111. data/_includes/components/reading-time.html +0 -121
  112. data/_includes/components/series-navigation.html +0 -124
  113. data/_includes/components/social-proof.html +0 -34
  114. data/_includes/components/user-preferences.html +0 -566
  115. data/_layouts/archive.html +0 -282
  116. data/_layouts/post-sidebar.html +0 -183
  117. data/_sass/_phase3-enhancements.scss +0 -874
  118. data/_sass/_phase4-enhancements.scss +0 -1214
  119. data/_sass/_phase5-enhancements.scss +0 -414
  120. data/assets/img/20220607123041_detail.001.png +0 -0
  121. data/assets/js/academic.js +0 -262
  122. data/assets/js/analytics-dashboard.js +0 -382
  123. data/assets/js/core/dark-mode.js +0 -79
  124. data/assets/js/core/github-cards.js +0 -123
  125. data/assets/js/core/language-filter.js +0 -69
  126. data/assets/js/core/navigation.js +0 -184
  127. data/assets/js/core/scroll-progress.js +0 -45
  128. data/assets/js/core/search-hotkeys.js +0 -62
  129. data/assets/js/core/skip-links.js +0 -62
  130. data/assets/js/main.js +0 -23
  131. data/assets/js/math.js +0 -818
  132. data/assets/js/notebook.js +0 -158
  133. data/assets/js/search/analytics.js +0 -91
  134. data/assets/js/search/app.js +0 -271
  135. data/assets/js/search/autocomplete.js +0 -120
  136. data/assets/js/search/engine.js +0 -260
  137. data/assets/js/search/filters.js +0 -38
  138. data/assets/js/search/render.js +0 -217
  139. data/assets/js/search/utils.js +0 -99
  140. data/assets/js/search.js +0 -354
  141. data/assets/js/visualizations.js +0 -816
  142. data/assets/publications/datalog-publications.bib +0 -8
  143. data/assets/publications/datalog-publications.ris +0 -9
  144. data/assets/publications/publications.bib +0 -30
  145. data/assets/templates/diogo-ribeiro-cv.md +0 -31
  146. data/assets/templates/diogo-ribeiro-cv.tex +0 -32
  147. 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
- }