datalog-theme 0.7.0 → 0.9.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 (203) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +188 -0
  3. data/CITATION.cff +2 -2
  4. data/README.md +25 -17
  5. data/_data/cdn-integrity.yml +0 -30
  6. data/_data/i18n/en.yml +297 -0
  7. data/_data/i18n/es.yml +297 -0
  8. data/_data/i18n/pt.yml +297 -0
  9. data/_data/js_manifest.json +16 -0
  10. data/_includes/analytics/dashboard.html +3 -1
  11. data/_includes/components/api-function.html +20 -1
  12. data/_includes/components/author-bio.html +21 -11
  13. data/_includes/components/author-list.html +32 -0
  14. data/_includes/components/citation-tools.html +33 -25
  15. data/_includes/components/comments-thread.html +90 -0
  16. data/_includes/components/contact-form.html +120 -0
  17. data/_includes/components/correction-report.html +82 -0
  18. data/_includes/components/enhanced-code-block.html +1 -1
  19. data/_includes/components/enhanced-toc.html +6 -7
  20. data/_includes/components/license-link.html +13 -0
  21. data/_includes/components/license-notice.html +28 -0
  22. data/_includes/components/moderation-inbox.html +139 -0
  23. data/_includes/components/reactions.html +49 -0
  24. data/_includes/components/reading-list.html +31 -0
  25. data/_includes/components/reading-mode-toggle.html +65 -0
  26. data/_includes/components/reading-state-bookmark.html +27 -0
  27. data/_includes/components/reading-state-panel.html +71 -0
  28. data/_includes/components/reproducibility.html +61 -0
  29. data/_includes/components/responsive-image.html +3 -3
  30. data/_includes/components/revision-history.html +39 -0
  31. data/_includes/components/revision-notice.html +35 -0
  32. data/_includes/components/series-nav.html +64 -0
  33. data/_includes/components/subscribe-form.html +88 -0
  34. data/_includes/components/subscription-manage.html +68 -0
  35. data/_includes/components/webmentions.html +48 -0
  36. data/_includes/csp-meta.html +135 -11
  37. data/_includes/footer.html +22 -20
  38. data/_includes/head.html +111 -63
  39. data/_includes/header/navigation.html +12 -15
  40. data/_includes/header.html +28 -19
  41. data/_includes/layouts/default/article.html +9 -7
  42. data/_includes/meta/dynamic-services-config.html +14 -0
  43. data/_includes/meta/math-config.html +18 -10
  44. data/_includes/meta/person-json.html +27 -0
  45. data/_includes/meta/publisher.html +45 -0
  46. data/_includes/meta/schema.html +67 -30
  47. data/_includes/meta/scholarly.html +121 -0
  48. data/_includes/meta/scripts-loader.html +18 -32
  49. data/_includes/meta/webmention-discovery.html +14 -0
  50. data/_includes/post/related-posts.html +4 -7
  51. data/_includes/scripts.html +57 -0
  52. data/_includes/search/index-data.json +9 -34
  53. data/_layouts/dataset.html +5 -3
  54. data/_layouts/default.html +16 -8
  55. data/_layouts/notebook.html +1 -0
  56. data/_layouts/package.html +4 -2
  57. data/_layouts/page.html +15 -0
  58. data/_layouts/portfolio.html +1 -0
  59. data/_layouts/post.html +102 -23
  60. data/_layouts/project.html +4 -3
  61. data/_layouts/research.html +20 -8
  62. data/_plugins/analytics_dashboard.rb +9 -3
  63. data/_plugins/authors.rb +133 -0
  64. data/_plugins/config_validator.rb +231 -23
  65. data/_plugins/critical_css_check.rb +42 -0
  66. data/_plugins/csp_generator.rb +18 -28
  67. data/_plugins/datalog_bibliography.rb +9 -7
  68. data/_plugins/datalog_comments.rb +8 -5
  69. data/_plugins/datalog_slides.rb +9 -8
  70. data/_plugins/i18n.rb +13 -12
  71. data/_plugins/image_optimizer.rb +241 -178
  72. data/_plugins/licenses.rb +135 -0
  73. data/_plugins/math_preprocessor.rb +59 -7
  74. data/_plugins/notebook_converter.rb +23 -4
  75. data/_plugins/plugin_loader.rb +3 -1
  76. data/_plugins/publications_generator.rb +8 -2
  77. data/_plugins/references.rb +238 -0
  78. data/_plugins/reproducibility.rb +150 -0
  79. data/_plugins/revisions.rb +101 -0
  80. data/_plugins/rouge_highlight_filter.rb +42 -0
  81. data/_plugins/scholarly.rb +50 -0
  82. data/_plugins/search_code_blocks.rb +30 -0
  83. data/_plugins/search_normalizer.rb +15 -53
  84. data/_plugins/search_pages.rb +3 -4
  85. data/_plugins/series.rb +104 -0
  86. data/_plugins/statements.rb +87 -0
  87. data/_sass/_academic-dashboard.scss +262 -0
  88. data/_sass/_base.scss +20 -1
  89. data/_sass/_comments-thread.scss +159 -0
  90. data/_sass/_components.scss +64 -1153
  91. data/_sass/_features.scss +17 -0
  92. data/_sass/_layout.scss +385 -1
  93. data/_sass/_mathematical.scss +27 -0
  94. data/_sass/_moderation.scss +222 -0
  95. data/_sass/_notebooks.scss +322 -0
  96. data/_sass/_open-science-badges.scss +56 -0
  97. data/_sass/{_phase1-enhancements.scss → _post-components.scss} +5 -3
  98. data/_sass/_print.scss +291 -0
  99. data/_sass/_reactions.scss +89 -0
  100. data/_sass/_reading-state.scss +290 -0
  101. data/_sass/_search-page.scss +530 -0
  102. data/_sass/_search.scss +46 -0
  103. data/_sass/_service-forms.scss +204 -0
  104. data/_sass/_subscriptions.scss +140 -0
  105. data/_sass/_syntax-highlighting.scss +212 -97
  106. data/_sass/_theme.scss +40 -19
  107. data/_sass/_typography.scss +130 -0
  108. data/_sass/_utilities.scss +5 -0
  109. data/_sass/_variables.scss +6 -0
  110. data/_sass/_webmentions.scss +125 -0
  111. data/assets/css/main.scss +14 -0
  112. data/assets/js/dist/academic.js +1 -1
  113. data/assets/js/dist/analytics-dashboard.js +1 -1
  114. data/assets/js/dist/chunks/chunk-2DYDWUFX.js +1 -0
  115. data/assets/js/dist/chunks/chunk-PATLC23F.js +1 -0
  116. data/assets/js/dist/chunks/chunk-V7734B2G.js +1 -0
  117. data/assets/js/dist/comments.js +2 -0
  118. data/assets/js/dist/contact.js +1 -0
  119. data/assets/js/dist/core.js +1 -1
  120. data/assets/js/dist/corrections.js +1 -0
  121. data/assets/js/dist/loader.js +1 -1
  122. data/assets/js/dist/math.js +1 -1
  123. data/assets/js/dist/moderation.js +1 -0
  124. data/assets/js/dist/notebook.js +1 -1
  125. data/assets/js/dist/reactions.js +1 -0
  126. data/assets/js/dist/reading-state.js +1 -0
  127. data/assets/js/dist/search.js +1 -1
  128. data/assets/js/dist/sources.json +52 -0
  129. data/assets/js/dist/subscriptions.js +1 -0
  130. data/assets/js/dist/visualizations.js +11 -2
  131. data/assets/js/dist/webmentions.js +1 -0
  132. data/assets/js/loader.js +37 -1
  133. data/datalog-theme.gemspec +35 -23
  134. data/lib/datalog/cli.rb +43 -15
  135. data/lib/datalog/critical_css.rb +168 -0
  136. data/lib/datalog/plugin_system/dependency_resolver.rb +0 -2
  137. data/lib/datalog/plugins/comments.rb +33 -3
  138. data/lib/datalog/theme/installed_files.rb +113 -0
  139. data/lib/datalog/theme/package.rb +57 -0
  140. data/lib/datalog/theme/repository_checkout.rb +94 -0
  141. data/lib/datalog/theme/version.rb +5 -1
  142. data/lib/datalog/warning_filter.rb +5 -11
  143. data/lib/datalog-theme.rb +6 -0
  144. metadata +96 -147
  145. data/_data/academic.yml +0 -217
  146. data/_data/config/author.yml +0 -121
  147. data/_data/datasets.yml +0 -28
  148. data/_data/js_meta.json +0 -371
  149. data/_data/navigation.yml +0 -145
  150. data/_data/projects.yml +0 -41
  151. data/_data/publications.yml +0 -28
  152. data/_data/social.yml +0 -73
  153. data/_data/visualizations.yml +0 -51
  154. data/_includes/components/advanced-search.html +0 -682
  155. data/_includes/components/bookmark-system.html +0 -96
  156. data/_includes/components/comments.html +0 -244
  157. data/_includes/components/content-recommendations.html +0 -228
  158. data/_includes/components/email-preferences.html +0 -200
  159. data/_includes/components/enhanced-metadata.html +0 -228
  160. data/_includes/components/language-switcher.html +0 -396
  161. data/_includes/components/navigation-enhancements.html +0 -454
  162. data/_includes/components/newsletter-signup.html +0 -178
  163. data/_includes/components/popular-posts.html +0 -233
  164. data/_includes/components/reading-progress.html +0 -133
  165. data/_includes/components/reading-time.html +0 -121
  166. data/_includes/components/series-navigation.html +0 -124
  167. data/_includes/components/social-proof.html +0 -34
  168. data/_includes/components/user-preferences.html +0 -566
  169. data/_includes/meta/syntax-config.html +0 -19
  170. data/_layouts/archive.html +0 -282
  171. data/_layouts/post-sidebar.html +0 -183
  172. data/_sass/_phase3-enhancements.scss +0 -874
  173. data/_sass/_phase4-enhancements.scss +0 -1214
  174. data/_sass/_phase5-enhancements.scss +0 -414
  175. data/assets/js/academic.js +0 -262
  176. data/assets/js/analytics-dashboard.js +0 -382
  177. data/assets/js/core/dark-mode.js +0 -79
  178. data/assets/js/core/github-cards.js +0 -123
  179. data/assets/js/core/language-filter.js +0 -69
  180. data/assets/js/core/navigation.js +0 -184
  181. data/assets/js/core/scroll-progress.js +0 -45
  182. data/assets/js/core/search-hotkeys.js +0 -62
  183. data/assets/js/core/skip-links.js +0 -62
  184. data/assets/js/dist/manifest.json +0 -22
  185. data/assets/js/dist/meta.json +0 -371
  186. data/assets/js/main.js +0 -23
  187. data/assets/js/math.js +0 -818
  188. data/assets/js/notebook.js +0 -158
  189. data/assets/js/search/analytics.js +0 -91
  190. data/assets/js/search/app.js +0 -271
  191. data/assets/js/search/autocomplete.js +0 -120
  192. data/assets/js/search/engine.js +0 -260
  193. data/assets/js/search/filters.js +0 -45
  194. data/assets/js/search/render.js +0 -217
  195. data/assets/js/search/utils.js +0 -99
  196. data/assets/js/search.js +0 -354
  197. data/assets/js/visualizations.js +0 -816
  198. data/assets/publications/datalog-publications.bib +0 -8
  199. data/assets/publications/datalog-publications.ris +0 -9
  200. data/assets/publications/publications.bib +0 -30
  201. data/assets/templates/diogo-ribeiro-cv.md +0 -31
  202. data/assets/templates/diogo-ribeiro-cv.tex +0 -32
  203. 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
- }