datalog-theme 0.6.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +162 -0
- data/CITATION.cff +29 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/_data/academic.yml +217 -0
- data/_data/cdn-integrity.yml +51 -0
- data/_data/config/author.yml +121 -0
- data/_data/config/features.yml +262 -0
- data/_data/config/site.yml +42 -0
- data/_data/config/theme.yml +181 -0
- data/_data/datasets.yml +28 -0
- data/_data/i18n/en.yml +438 -0
- data/_data/i18n/es.yml +438 -0
- data/_data/i18n/pt.yml +438 -0
- data/_data/js_manifest.json +22 -0
- data/_data/js_meta.json +371 -0
- data/_data/navigation.yml +145 -0
- data/_data/projects.yml +41 -0
- data/_data/publications.yml +28 -0
- data/_data/social.yml +73 -0
- data/_data/visualizations.yml +51 -0
- data/_includes/analytics/dashboard.html +149 -0
- data/_includes/analytics.html +9 -0
- data/_includes/components/academic-dashboard.html +347 -0
- data/_includes/components/advanced-search.html +682 -0
- data/_includes/components/api-function.html +94 -0
- data/_includes/components/author-bio.html +166 -0
- data/_includes/components/bookmark-system.html +96 -0
- data/_includes/components/breadcrumbs.html +89 -0
- data/_includes/components/citation-tools.html +94 -0
- data/_includes/components/comments.html +244 -0
- data/_includes/components/content-provenance.html +43 -0
- data/_includes/components/content-recommendations.html +228 -0
- data/_includes/components/difficulty-badge.html +84 -0
- data/_includes/components/email-preferences.html +200 -0
- data/_includes/components/enhanced-code-block.html +212 -0
- data/_includes/components/enhanced-metadata.html +228 -0
- data/_includes/components/enhanced-toc.html +209 -0
- data/_includes/components/hero.html +15 -0
- data/_includes/components/language-switcher.html +396 -0
- data/_includes/components/math-fallback.html +19 -0
- data/_includes/components/navigation-enhancements.html +454 -0
- data/_includes/components/newsletter-signup.html +178 -0
- data/_includes/components/open-science-badges.html +16 -0
- data/_includes/components/package-install.html +194 -0
- data/_includes/components/performance-monitor.html +170 -0
- data/_includes/components/popular-posts.html +233 -0
- data/_includes/components/post-hero.html +62 -0
- data/_includes/components/reading-progress.html +133 -0
- data/_includes/components/reading-time.html +121 -0
- data/_includes/components/responsive-image.html +63 -0
- data/_includes/components/search-facets.html +307 -0
- data/_includes/components/series-navigation.html +124 -0
- data/_includes/components/social-proof.html +34 -0
- data/_includes/components/social-share.html +119 -0
- data/_includes/components/user-preferences.html +566 -0
- data/_includes/components/visualization-card.html +24 -0
- data/_includes/components/viz-table-fallback.html +19 -0
- data/_includes/critical-css/default.html +0 -0
- data/_includes/critical-css/home.html +0 -0
- data/_includes/critical-css/post.html +0 -0
- data/_includes/csp-meta.html +17 -0
- data/_includes/footer/nav-column.html +31 -0
- data/_includes/footer.html +76 -0
- data/_includes/head.html +337 -0
- data/_includes/header/navigation.html +48 -0
- data/_includes/header.html +70 -0
- data/_includes/helpers/array-to-sentence.html +21 -0
- data/_includes/helpers/date-format.html +16 -0
- data/_includes/helpers/link-with-icon.html +26 -0
- data/_includes/layouts/default/article.html +67 -0
- data/_includes/meta/language-attributes.html +25 -0
- data/_includes/meta/math-config.html +21 -0
- data/_includes/meta/schema.html +153 -0
- data/_includes/meta/scripts-loader.html +52 -0
- data/_includes/post/related-posts.html +63 -0
- data/_includes/scripts.html +72 -0
- data/_includes/skip-link.html +1 -0
- data/_includes/toc.html +2 -0
- data/_layouts/archive.html +282 -0
- data/_layouts/dataset.html +52 -0
- data/_layouts/default.html +33 -0
- data/_layouts/home.html +48 -0
- data/_layouts/notebook.html +173 -0
- data/_layouts/package.html +238 -0
- data/_layouts/page.html +16 -0
- data/_layouts/portfolio.html +41 -0
- data/_layouts/post-sidebar.html +183 -0
- data/_layouts/post.html +338 -0
- data/_layouts/project.html +252 -0
- data/_layouts/research.html +504 -0
- data/_plugins/analytics_dashboard.rb +315 -0
- data/_plugins/config_validator.rb +397 -0
- data/_plugins/csp_generator.rb +115 -0
- data/_plugins/datalog_bibliography.rb +19 -0
- data/_plugins/datalog_comments.rb +18 -0
- data/_plugins/datalog_slides.rb +20 -0
- data/_plugins/front_matter_compat.rb +109 -0
- data/_plugins/i18n.rb +160 -0
- data/_plugins/image_optimizer.rb +445 -0
- data/_plugins/math_preprocessor.rb +185 -0
- data/_plugins/notebook_converter.rb +1107 -0
- data/_plugins/plugin_loader.rb +185 -0
- data/_plugins/publications_generator.rb +234 -0
- data/_plugins/reading_time.rb +15 -0
- data/_plugins/search_normalizer.rb +74 -0
- data/_plugins/sri_filter.rb +18 -0
- data/_plugins/toc_filter.rb +68 -0
- data/_plugins/warning_filter.rb +3 -0
- data/_sass/_base.scss +142 -0
- data/_sass/_components.scss +2380 -0
- data/_sass/_header.scss +470 -0
- data/_sass/_interactive.scss +344 -0
- data/_sass/_layout.scss +1760 -0
- data/_sass/_mathematical.scss +368 -0
- data/_sass/_mixins.scss +99 -0
- data/_sass/_package-docs.scss +715 -0
- data/_sass/_phase1-enhancements.scss +721 -0
- data/_sass/_phase3-enhancements.scss +874 -0
- data/_sass/_phase4-enhancements.scss +1214 -0
- data/_sass/_phase5-enhancements.scss +414 -0
- data/_sass/_search.scss +654 -0
- data/_sass/_syntax-highlighting.scss +128 -0
- data/_sass/_theme.scss +79 -0
- data/_sass/_typography.scss +148 -0
- data/_sass/_utilities.scss +334 -0
- data/_sass/_variables.scss +153 -0
- data/_sass/_visualizations.scss +242 -0
- data/assets/css/main.scss +4 -0
- data/assets/img/20220607123041_detail.001.png +0 -0
- data/assets/img/favicons/android-chrome-192x192.png +0 -0
- data/assets/img/favicons/android-chrome-512x512.png +0 -0
- data/assets/img/favicons/apple-touch-icon.png +0 -0
- data/assets/img/favicons/favicon-16x16.png +0 -0
- data/assets/img/favicons/favicon-32x32.png +0 -0
- data/assets/img/favicons/favicon.ico +0 -0
- data/assets/img/favicons/site.webmanifest +21 -0
- data/assets/img/portfolio-placeholder.svg +20 -0
- data/assets/js/academic.js +262 -0
- data/assets/js/analytics-dashboard.js +382 -0
- data/assets/js/core/dark-mode.js +79 -0
- data/assets/js/core/github-cards.js +123 -0
- data/assets/js/core/language-filter.js +69 -0
- data/assets/js/core/navigation.js +184 -0
- data/assets/js/core/scroll-progress.js +45 -0
- data/assets/js/core/search-hotkeys.js +62 -0
- data/assets/js/core/skip-links.js +62 -0
- data/assets/js/loader.js +163 -0
- data/assets/js/main.js +23 -0
- data/assets/js/math.js +818 -0
- data/assets/js/notebook.js +158 -0
- data/assets/js/search/analytics.js +91 -0
- data/assets/js/search/app.js +271 -0
- data/assets/js/search/autocomplete.js +120 -0
- data/assets/js/search/engine.js +260 -0
- data/assets/js/search/filters.js +38 -0
- data/assets/js/search/render.js +217 -0
- data/assets/js/search/utils.js +99 -0
- data/assets/js/search.js +354 -0
- data/assets/js/visualizations.js +816 -0
- data/assets/publications/datalog-publications.bib +8 -0
- data/assets/publications/datalog-publications.ris +9 -0
- data/assets/publications/publications.bib +30 -0
- data/assets/templates/diogo-ribeiro-cv.md +31 -0
- data/assets/templates/diogo-ribeiro-cv.tex +32 -0
- data/bin/datalog +26 -0
- data/datalog-theme.gemspec +72 -0
- data/lib/datalog/cli.rb +563 -0
- data/lib/datalog/plugin_system/dependency_resolver.rb +223 -0
- data/lib/datalog/plugin_system.rb +237 -0
- data/lib/datalog/plugins/citations.rb +182 -0
- data/lib/datalog/plugins/comments.rb +183 -0
- data/lib/datalog/plugins/search.rb +45 -0
- data/lib/datalog/plugins/slides.rb +121 -0
- data/lib/datalog/theme/theme.rb +18 -0
- data/lib/datalog/theme/version.rb +7 -0
- data/lib/datalog/warning_filter.rb +28 -0
- data/lib/datalog-theme.rb +17 -0
- metadata +555 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Navigation Enhancements Component
|
|
3
|
+
Provides back-to-top button and keyboard shortcuts
|
|
4
|
+
|
|
5
|
+
Parameters:
|
|
6
|
+
- show_back_to_top: Show back-to-top button (default: true)
|
|
7
|
+
- show_keyboard_shortcuts: Enable keyboard shortcuts (default: true)
|
|
8
|
+
- back_to_top_threshold: Scroll threshold in pixels (default: 300)
|
|
9
|
+
{%- endcomment -%}
|
|
10
|
+
|
|
11
|
+
{%- assign show_back_to_top = include.show_back_to_top | default: true -%}
|
|
12
|
+
{%- assign show_keyboard_shortcuts = include.show_keyboard_shortcuts | default: true -%}
|
|
13
|
+
{%- assign back_to_top_threshold = include.back_to_top_threshold | default: 300 -%}
|
|
14
|
+
|
|
15
|
+
{%- if show_back_to_top -%}
|
|
16
|
+
<button type="button"
|
|
17
|
+
class="back-to-top"
|
|
18
|
+
data-back-to-top
|
|
19
|
+
aria-label="Back to top"
|
|
20
|
+
hidden>
|
|
21
|
+
<svg class="back-to-top__icon"
|
|
22
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
23
|
+
width="20"
|
|
24
|
+
height="20"
|
|
25
|
+
viewBox="0 0 24 24"
|
|
26
|
+
fill="none"
|
|
27
|
+
stroke="currentColor"
|
|
28
|
+
stroke-width="2"
|
|
29
|
+
stroke-linecap="round"
|
|
30
|
+
stroke-linejoin="round"
|
|
31
|
+
aria-hidden="true">
|
|
32
|
+
<line x1="12" y1="19" x2="12" y2="5"></line>
|
|
33
|
+
<polyline points="5 12 12 5 19 12"></polyline>
|
|
34
|
+
</svg>
|
|
35
|
+
<span class="back-to-top__text">Top</span>
|
|
36
|
+
<svg class="back-to-top__progress"
|
|
37
|
+
width="52"
|
|
38
|
+
height="52"
|
|
39
|
+
viewBox="0 0 52 52"
|
|
40
|
+
aria-hidden="true">
|
|
41
|
+
<circle class="back-to-top__progress-bg"
|
|
42
|
+
cx="26"
|
|
43
|
+
cy="26"
|
|
44
|
+
r="24"
|
|
45
|
+
fill="none"
|
|
46
|
+
stroke="currentColor"
|
|
47
|
+
stroke-width="2"
|
|
48
|
+
opacity="0.2"></circle>
|
|
49
|
+
<circle class="back-to-top__progress-indicator"
|
|
50
|
+
cx="26"
|
|
51
|
+
cy="26"
|
|
52
|
+
r="24"
|
|
53
|
+
fill="none"
|
|
54
|
+
stroke="currentColor"
|
|
55
|
+
stroke-width="2"
|
|
56
|
+
stroke-linecap="round"
|
|
57
|
+
data-progress-circle></circle>
|
|
58
|
+
</svg>
|
|
59
|
+
</button>
|
|
60
|
+
{%- endif -%}
|
|
61
|
+
|
|
62
|
+
{%- if show_keyboard_shortcuts -%}
|
|
63
|
+
<div class="keyboard-shortcuts"
|
|
64
|
+
data-keyboard-shortcuts
|
|
65
|
+
role="dialog"
|
|
66
|
+
aria-modal="true"
|
|
67
|
+
aria-labelledby="shortcuts-title"
|
|
68
|
+
hidden>
|
|
69
|
+
|
|
70
|
+
<div class="keyboard-shortcuts__content">
|
|
71
|
+
<div class="keyboard-shortcuts__header">
|
|
72
|
+
<h3 class="keyboard-shortcuts__title" id="shortcuts-title">
|
|
73
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
|
74
|
+
width="20"
|
|
75
|
+
height="20"
|
|
76
|
+
viewBox="0 0 24 24"
|
|
77
|
+
fill="none"
|
|
78
|
+
stroke="currentColor"
|
|
79
|
+
stroke-width="2"
|
|
80
|
+
aria-hidden="true">
|
|
81
|
+
<rect x="2" y="4" width="20" height="16" rx="2"></rect>
|
|
82
|
+
<path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10"></path>
|
|
83
|
+
</svg>
|
|
84
|
+
Keyboard Shortcuts
|
|
85
|
+
</h3>
|
|
86
|
+
|
|
87
|
+
<button type="button"
|
|
88
|
+
class="keyboard-shortcuts__close"
|
|
89
|
+
data-shortcuts-close
|
|
90
|
+
aria-label="Close keyboard shortcuts">
|
|
91
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
|
92
|
+
width="20"
|
|
93
|
+
height="20"
|
|
94
|
+
viewBox="0 0 24 24"
|
|
95
|
+
fill="none"
|
|
96
|
+
stroke="currentColor"
|
|
97
|
+
stroke-width="2"
|
|
98
|
+
aria-hidden="true">
|
|
99
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
100
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
101
|
+
</svg>
|
|
102
|
+
</button>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div class="keyboard-shortcuts__body">
|
|
106
|
+
<div class="shortcuts-section">
|
|
107
|
+
<h4 class="shortcuts-section__title">Navigation</h4>
|
|
108
|
+
<dl class="shortcuts-list">
|
|
109
|
+
<div class="shortcuts-list__item">
|
|
110
|
+
<dt class="shortcuts-list__keys">
|
|
111
|
+
<kbd>g</kbd> <kbd>h</kbd>
|
|
112
|
+
</dt>
|
|
113
|
+
<dd class="shortcuts-list__description">Go to home page</dd>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div class="shortcuts-list__item">
|
|
117
|
+
<dt class="shortcuts-list__keys">
|
|
118
|
+
<kbd>g</kbd> <kbd>a</kbd>
|
|
119
|
+
</dt>
|
|
120
|
+
<dd class="shortcuts-list__description">Go to archive</dd>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="shortcuts-list__item">
|
|
124
|
+
<dt class="shortcuts-list__keys">
|
|
125
|
+
<kbd>←</kbd> / <kbd>→</kbd>
|
|
126
|
+
</dt>
|
|
127
|
+
<dd class="shortcuts-list__description">Previous / Next post</dd>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div class="shortcuts-list__item">
|
|
131
|
+
<dt class="shortcuts-list__keys">
|
|
132
|
+
<kbd>↑</kbd> / <kbd>↓</kbd>
|
|
133
|
+
</dt>
|
|
134
|
+
<dd class="shortcuts-list__description">Scroll up / down</dd>
|
|
135
|
+
</div>
|
|
136
|
+
</dl>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<div class="shortcuts-section">
|
|
140
|
+
<h4 class="shortcuts-section__title">Actions</h4>
|
|
141
|
+
<dl class="shortcuts-list">
|
|
142
|
+
<div class="shortcuts-list__item">
|
|
143
|
+
<dt class="shortcuts-list__keys">
|
|
144
|
+
<kbd>/</kbd>
|
|
145
|
+
</dt>
|
|
146
|
+
<dd class="shortcuts-list__description">Focus search</dd>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<div class="shortcuts-list__item">
|
|
150
|
+
<dt class="shortcuts-list__keys">
|
|
151
|
+
<kbd>?</kbd>
|
|
152
|
+
</dt>
|
|
153
|
+
<dd class="shortcuts-list__description">Show this help</dd>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<div class="shortcuts-list__item">
|
|
157
|
+
<dt class="shortcuts-list__keys">
|
|
158
|
+
<kbd>Esc</kbd>
|
|
159
|
+
</dt>
|
|
160
|
+
<dd class="shortcuts-list__description">Close dialogs</dd>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<div class="shortcuts-list__item">
|
|
164
|
+
<dt class="shortcuts-list__keys">
|
|
165
|
+
<kbd>s</kbd>
|
|
166
|
+
</dt>
|
|
167
|
+
<dd class="shortcuts-list__description">Share current page</dd>
|
|
168
|
+
</div>
|
|
169
|
+
</dl>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div class="shortcuts-section">
|
|
173
|
+
<h4 class="shortcuts-section__title">Reading</h4>
|
|
174
|
+
<dl class="shortcuts-list">
|
|
175
|
+
<div class="shortcuts-list__item">
|
|
176
|
+
<dt class="shortcuts-list__keys">
|
|
177
|
+
<kbd>r</kbd>
|
|
178
|
+
</dt>
|
|
179
|
+
<dd class="shortcuts-list__description">Toggle reading mode</dd>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div class="shortcuts-list__item">
|
|
183
|
+
<dt class="shortcuts-list__keys">
|
|
184
|
+
<kbd>t</kbd>
|
|
185
|
+
</dt>
|
|
186
|
+
<dd class="shortcuts-list__description">Toggle theme</dd>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div class="shortcuts-list__item">
|
|
190
|
+
<dt class="shortcuts-list__keys">
|
|
191
|
+
<kbd>+</kbd> / <kbd>-</kbd>
|
|
192
|
+
</dt>
|
|
193
|
+
<dd class="shortcuts-list__description">Increase / decrease font size</dd>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div class="shortcuts-list__item">
|
|
197
|
+
<dt class="shortcuts-list__keys">
|
|
198
|
+
<kbd>0</kbd>
|
|
199
|
+
</dt>
|
|
200
|
+
<dd class="shortcuts-list__description">Reset font size</dd>
|
|
201
|
+
</div>
|
|
202
|
+
</dl>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<div class="keyboard-shortcuts__footer">
|
|
207
|
+
<p class="keyboard-shortcuts__hint">
|
|
208
|
+
Press <kbd>?</kbd> anytime to show shortcuts
|
|
209
|
+
</p>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<div class="keyboard-shortcuts__backdrop"
|
|
214
|
+
data-shortcuts-backdrop></div>
|
|
215
|
+
</div>
|
|
216
|
+
{%- endif -%}
|
|
217
|
+
|
|
218
|
+
<script>
|
|
219
|
+
(function() {
|
|
220
|
+
'use strict';
|
|
221
|
+
|
|
222
|
+
// Back to Top functionality
|
|
223
|
+
const initBackToTop = () => {
|
|
224
|
+
const button = document.querySelector('[data-back-to-top]');
|
|
225
|
+
if (!button) return;
|
|
226
|
+
|
|
227
|
+
const progressCircle = button.querySelector('[data-progress-circle]');
|
|
228
|
+
const threshold = {{ back_to_top_threshold }};
|
|
229
|
+
const circumference = 2 * Math.PI * 24; // radius is 24
|
|
230
|
+
|
|
231
|
+
if (progressCircle) {
|
|
232
|
+
progressCircle.style.strokeDasharray = circumference;
|
|
233
|
+
progressCircle.style.strokeDashoffset = circumference;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
let ticking = false;
|
|
237
|
+
|
|
238
|
+
const updateButton = () => {
|
|
239
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
240
|
+
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
241
|
+
const scrollPercent = (scrollTop / docHeight) * 100;
|
|
242
|
+
|
|
243
|
+
// Show/hide button
|
|
244
|
+
button.hidden = scrollTop < threshold;
|
|
245
|
+
|
|
246
|
+
// Update progress circle
|
|
247
|
+
if (progressCircle) {
|
|
248
|
+
const offset = circumference - (scrollPercent / 100) * circumference;
|
|
249
|
+
progressCircle.style.strokeDashoffset = offset;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
ticking = false;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const requestTick = () => {
|
|
256
|
+
if (!ticking) {
|
|
257
|
+
requestAnimationFrame(updateButton);
|
|
258
|
+
ticking = true;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
window.addEventListener('scroll', requestTick, { passive: true });
|
|
263
|
+
|
|
264
|
+
button.addEventListener('click', () => {
|
|
265
|
+
window.scrollTo({
|
|
266
|
+
top: 0,
|
|
267
|
+
behavior: 'smooth'
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Initial check
|
|
272
|
+
updateButton();
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// Keyboard Shortcuts functionality
|
|
276
|
+
const initKeyboardShortcuts = () => {
|
|
277
|
+
const panel = document.querySelector('[data-keyboard-shortcuts]');
|
|
278
|
+
if (!panel) return;
|
|
279
|
+
|
|
280
|
+
const backdrop = panel.querySelector('[data-shortcuts-backdrop]');
|
|
281
|
+
const closeBtn = panel.querySelector('[data-shortcuts-close]');
|
|
282
|
+
|
|
283
|
+
const openPanel = () => {
|
|
284
|
+
panel.hidden = false;
|
|
285
|
+
document.body.style.overflow = 'hidden';
|
|
286
|
+
closeBtn?.focus();
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const closePanel = () => {
|
|
290
|
+
panel.hidden = true;
|
|
291
|
+
document.body.style.overflow = '';
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
closeBtn?.addEventListener('click', closePanel);
|
|
295
|
+
backdrop?.addEventListener('click', closePanel);
|
|
296
|
+
|
|
297
|
+
// Keyboard shortcut handling
|
|
298
|
+
let keySequence = [];
|
|
299
|
+
let sequenceTimeout;
|
|
300
|
+
|
|
301
|
+
document.addEventListener('keydown', (e) => {
|
|
302
|
+
// Ignore if typing in input/textarea
|
|
303
|
+
if (e.target.matches('input, textarea, select')) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ESC to close dialogs
|
|
308
|
+
if (e.key === 'Escape') {
|
|
309
|
+
if (!panel.hidden) {
|
|
310
|
+
closePanel();
|
|
311
|
+
e.preventDefault();
|
|
312
|
+
}
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ? to show shortcuts
|
|
317
|
+
if (e.key === '?' && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
318
|
+
openPanel();
|
|
319
|
+
e.preventDefault();
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Handle key sequences (g + h, g + a)
|
|
324
|
+
clearTimeout(sequenceTimeout);
|
|
325
|
+
keySequence.push(e.key.toLowerCase());
|
|
326
|
+
keySequence = keySequence.slice(-2); // Keep last 2 keys
|
|
327
|
+
|
|
328
|
+
sequenceTimeout = setTimeout(() => {
|
|
329
|
+
keySequence = [];
|
|
330
|
+
}, 1000);
|
|
331
|
+
|
|
332
|
+
const sequence = keySequence.join(' ');
|
|
333
|
+
|
|
334
|
+
// Navigation shortcuts
|
|
335
|
+
if (sequence === 'g h') {
|
|
336
|
+
window.location.href = '{{ "/" | relative_url }}';
|
|
337
|
+
e.preventDefault();
|
|
338
|
+
} else if (sequence === 'g a') {
|
|
339
|
+
window.location.href = '{{ "/archive/" | relative_url }}';
|
|
340
|
+
e.preventDefault();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Single key shortcuts
|
|
344
|
+
switch (e.key) {
|
|
345
|
+
case '/':
|
|
346
|
+
// Focus search
|
|
347
|
+
const searchInput = document.querySelector('[data-search-input], input[type="search"]');
|
|
348
|
+
if (searchInput) {
|
|
349
|
+
searchInput.focus();
|
|
350
|
+
e.preventDefault();
|
|
351
|
+
}
|
|
352
|
+
break;
|
|
353
|
+
|
|
354
|
+
case 'ArrowLeft':
|
|
355
|
+
// Previous post
|
|
356
|
+
const prevLink = document.querySelector('.post-navigation__prev a, a[rel="prev"]');
|
|
357
|
+
if (prevLink && !e.ctrlKey && !e.metaKey) {
|
|
358
|
+
window.location.href = prevLink.href;
|
|
359
|
+
e.preventDefault();
|
|
360
|
+
}
|
|
361
|
+
break;
|
|
362
|
+
|
|
363
|
+
case 'ArrowRight':
|
|
364
|
+
// Next post
|
|
365
|
+
const nextLink = document.querySelector('.post-navigation__next a, a[rel="next"]');
|
|
366
|
+
if (nextLink && !e.ctrlKey && !e.metaKey) {
|
|
367
|
+
window.location.href = nextLink.href;
|
|
368
|
+
e.preventDefault();
|
|
369
|
+
}
|
|
370
|
+
break;
|
|
371
|
+
|
|
372
|
+
case 'r':
|
|
373
|
+
// Toggle reading mode
|
|
374
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
375
|
+
const readingModeToggle = document.querySelector('[data-reading-mode-toggle]');
|
|
376
|
+
if (readingModeToggle) {
|
|
377
|
+
readingModeToggle.click();
|
|
378
|
+
e.preventDefault();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
break;
|
|
382
|
+
|
|
383
|
+
case 't':
|
|
384
|
+
// Toggle theme
|
|
385
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
386
|
+
const currentTheme = document.body.classList.contains('dark-mode') ? 'light' : 'dark';
|
|
387
|
+
const themeButton = document.querySelector(`[data-theme="${currentTheme}"]`);
|
|
388
|
+
if (themeButton) {
|
|
389
|
+
themeButton.click();
|
|
390
|
+
e.preventDefault();
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
break;
|
|
394
|
+
|
|
395
|
+
case '+':
|
|
396
|
+
case '=':
|
|
397
|
+
// Increase font size
|
|
398
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
399
|
+
const increaseBtn = document.querySelector('[data-font-size-control] [data-action="increase"]');
|
|
400
|
+
if (increaseBtn) {
|
|
401
|
+
increaseBtn.click();
|
|
402
|
+
e.preventDefault();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
break;
|
|
406
|
+
|
|
407
|
+
case '-':
|
|
408
|
+
// Decrease font size
|
|
409
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
410
|
+
const decreaseBtn = document.querySelector('[data-font-size-control] [data-action="decrease"]');
|
|
411
|
+
if (decreaseBtn) {
|
|
412
|
+
decreaseBtn.click();
|
|
413
|
+
e.preventDefault();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
break;
|
|
417
|
+
|
|
418
|
+
case '0':
|
|
419
|
+
// Reset font size
|
|
420
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
421
|
+
const resetBtn = document.querySelector('[data-font-size-control] [data-action="reset"]');
|
|
422
|
+
if (resetBtn) {
|
|
423
|
+
resetBtn.click();
|
|
424
|
+
e.preventDefault();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
break;
|
|
428
|
+
|
|
429
|
+
case 's':
|
|
430
|
+
// Share (if native share available)
|
|
431
|
+
if (!e.ctrlKey && !e.metaKey && navigator.share) {
|
|
432
|
+
navigator.share({
|
|
433
|
+
title: document.title,
|
|
434
|
+
url: window.location.href
|
|
435
|
+
}).catch(() => {});
|
|
436
|
+
e.preventDefault();
|
|
437
|
+
}
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// Initialize when DOM is ready
|
|
444
|
+
if (document.readyState === 'loading') {
|
|
445
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
446
|
+
initBackToTop();
|
|
447
|
+
initKeyboardShortcuts();
|
|
448
|
+
});
|
|
449
|
+
} else {
|
|
450
|
+
initBackToTop();
|
|
451
|
+
initKeyboardShortcuts();
|
|
452
|
+
}
|
|
453
|
+
})();
|
|
454
|
+
</script>
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Newsletter Signup Component
|
|
3
|
+
Integrates with popular email services (Mailchimp, ConvertKit, Buttondown, etc.)
|
|
4
|
+
|
|
5
|
+
Parameters:
|
|
6
|
+
- compact: Compact layout (default: false)
|
|
7
|
+
- inline: Inline layout (default: false)
|
|
8
|
+
- heading: Custom heading text
|
|
9
|
+
- description: Custom description text
|
|
10
|
+
- provider: Email provider (mailchimp, convertkit, buttondown, custom)
|
|
11
|
+
{%- endcomment -%}
|
|
12
|
+
|
|
13
|
+
{%- assign compact = include.compact | default: false -%}
|
|
14
|
+
{%- assign inline = include.inline | default: false -%}
|
|
15
|
+
{%- assign heading = include.heading | default: "Stay Updated" -%}
|
|
16
|
+
{%- assign description = include.description | default: "Get the latest posts delivered right to your inbox" -%}
|
|
17
|
+
{%- assign provider = include.provider | default: site.phase2_features.newsletter.provider | default: "custom" -%}
|
|
18
|
+
|
|
19
|
+
{%- comment -%} Get provider settings from config {%- endcomment -%}
|
|
20
|
+
{%- assign newsletter_config = site.phase2_features.newsletter -%}
|
|
21
|
+
{%- assign action_url = newsletter_config.action_url -%}
|
|
22
|
+
{%- assign success_message = newsletter_config.success_message | default: "Thanks for subscribing!" -%}
|
|
23
|
+
|
|
24
|
+
<div class="newsletter-signup{% if compact %} newsletter-signup--compact{% endif %}{% if inline %} newsletter-signup--inline{% endif %}"
|
|
25
|
+
data-newsletter>
|
|
26
|
+
{% unless compact %}
|
|
27
|
+
<div class="newsletter-signup__icon" aria-hidden="true">
|
|
28
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
29
|
+
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
|
|
30
|
+
<polyline points="22,6 12,13 2,6"></polyline>
|
|
31
|
+
</svg>
|
|
32
|
+
</div>
|
|
33
|
+
{% endunless %}
|
|
34
|
+
|
|
35
|
+
<div class="newsletter-signup__content">
|
|
36
|
+
<h3 class="newsletter-signup__heading">{{ heading }}</h3>
|
|
37
|
+
<p class="newsletter-signup__description">{{ description }}</p>
|
|
38
|
+
|
|
39
|
+
<form class="newsletter-signup__form"
|
|
40
|
+
action="{{ action_url }}"
|
|
41
|
+
method="post"
|
|
42
|
+
target="_blank"
|
|
43
|
+
data-newsletter-form
|
|
44
|
+
novalidate>
|
|
45
|
+
|
|
46
|
+
<div class="newsletter-signup__input-group">
|
|
47
|
+
<label for="newsletter-email-{% if compact %}compact{% else %}full{% endif %}"
|
|
48
|
+
class="visually-hidden">
|
|
49
|
+
Email address
|
|
50
|
+
</label>
|
|
51
|
+
<input type="email"
|
|
52
|
+
id="newsletter-email-{% if compact %}compact{% else %}full{% endif %}"
|
|
53
|
+
name="email"
|
|
54
|
+
class="newsletter-signup__input"
|
|
55
|
+
placeholder="your@email.com"
|
|
56
|
+
required
|
|
57
|
+
aria-required="true"
|
|
58
|
+
aria-label="Email address">
|
|
59
|
+
|
|
60
|
+
<button type="submit"
|
|
61
|
+
class="newsletter-signup__button"
|
|
62
|
+
aria-label="Subscribe to newsletter">
|
|
63
|
+
<span class="newsletter-signup__button-text">Subscribe</span>
|
|
64
|
+
<svg class="newsletter-signup__button-icon"
|
|
65
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
66
|
+
width="20"
|
|
67
|
+
height="20"
|
|
68
|
+
viewBox="0 0 24 24"
|
|
69
|
+
fill="none"
|
|
70
|
+
stroke="currentColor"
|
|
71
|
+
stroke-width="2"
|
|
72
|
+
stroke-linecap="round"
|
|
73
|
+
stroke-linejoin="round"
|
|
74
|
+
aria-hidden="true">
|
|
75
|
+
<line x1="5" y1="12" x2="19" y2="12"></line>
|
|
76
|
+
<polyline points="12 5 19 12 12 19"></polyline>
|
|
77
|
+
</svg>
|
|
78
|
+
</button>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
{%- comment -%} Error message {%- endcomment -%}
|
|
82
|
+
<div class="newsletter-signup__message newsletter-signup__message--error"
|
|
83
|
+
data-error-message
|
|
84
|
+
role="alert"
|
|
85
|
+
aria-live="assertive">
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
{%- comment -%} Success message {%- endcomment -%}
|
|
89
|
+
<div class="newsletter-signup__message newsletter-signup__message--success"
|
|
90
|
+
data-success-message
|
|
91
|
+
role="status"
|
|
92
|
+
aria-live="polite">
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
{%- comment -%} Privacy notice {%- endcomment -%}
|
|
96
|
+
<p class="newsletter-signup__privacy">
|
|
97
|
+
{% if newsletter_config.privacy_url %}
|
|
98
|
+
By subscribing, you agree to our
|
|
99
|
+
<a href="{{ newsletter_config.privacy_url }}" target="_blank" rel="noopener">privacy policy</a>.
|
|
100
|
+
{% else %}
|
|
101
|
+
We respect your privacy. Unsubscribe at any time.
|
|
102
|
+
{% endif %}
|
|
103
|
+
</p>
|
|
104
|
+
|
|
105
|
+
{%- comment -%} Honeypot field for spam protection {%- endcomment -%}
|
|
106
|
+
<div style="position: absolute; left: -5000px;" aria-hidden="true">
|
|
107
|
+
<input type="text" name="b_honeypot" tabindex="-1" value="">
|
|
108
|
+
</div>
|
|
109
|
+
</form>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<script>
|
|
114
|
+
(function() {
|
|
115
|
+
'use strict';
|
|
116
|
+
|
|
117
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
118
|
+
const forms = document.querySelectorAll('[data-newsletter-form]');
|
|
119
|
+
|
|
120
|
+
forms.forEach(form => {
|
|
121
|
+
const newsletter = form.closest('[data-newsletter]');
|
|
122
|
+
const emailInput = form.querySelector('input[type="email"]');
|
|
123
|
+
const submitButton = form.querySelector('button[type="submit"]');
|
|
124
|
+
const errorMessage = form.querySelector('[data-error-message]');
|
|
125
|
+
const successMessage = form.querySelector('[data-success-message]');
|
|
126
|
+
|
|
127
|
+
form.addEventListener('submit', function(e) {
|
|
128
|
+
// Clear previous messages
|
|
129
|
+
if (errorMessage) errorMessage.textContent = '';
|
|
130
|
+
if (successMessage) successMessage.textContent = '';
|
|
131
|
+
|
|
132
|
+
// Validate email
|
|
133
|
+
if (!emailInput.validity.valid) {
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
if (errorMessage) {
|
|
136
|
+
errorMessage.textContent = 'Please enter a valid email address.';
|
|
137
|
+
}
|
|
138
|
+
emailInput.focus();
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Show loading state
|
|
143
|
+
if (submitButton) {
|
|
144
|
+
const originalText = submitButton.querySelector('.newsletter-signup__button-text').textContent;
|
|
145
|
+
submitButton.disabled = true;
|
|
146
|
+
submitButton.querySelector('.newsletter-signup__button-text').textContent = 'Subscribing...';
|
|
147
|
+
|
|
148
|
+
// Re-enable after delay (for services that open in new tab)
|
|
149
|
+
setTimeout(() => {
|
|
150
|
+
submitButton.disabled = false;
|
|
151
|
+
submitButton.querySelector('.newsletter-signup__button-text').textContent = originalText;
|
|
152
|
+
|
|
153
|
+
// Show success message
|
|
154
|
+
if (successMessage) {
|
|
155
|
+
successMessage.textContent = '{{ success_message }}';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Reset form
|
|
159
|
+
setTimeout(() => {
|
|
160
|
+
form.reset();
|
|
161
|
+
if (successMessage) successMessage.textContent = '';
|
|
162
|
+
}, 5000);
|
|
163
|
+
}, 2000);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// For custom implementations, you can prevent default and handle via AJAX
|
|
167
|
+
// e.preventDefault();
|
|
168
|
+
// handleAjaxSubmit(emailInput.value);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Clear error on input
|
|
172
|
+
emailInput.addEventListener('input', function() {
|
|
173
|
+
if (errorMessage) errorMessage.textContent = '';
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
})();
|
|
178
|
+
</script>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{% assign badges = include.badges | default: site.data.academic.badges %}
|
|
2
|
+
{% if badges and badges.size > 0 %}
|
|
3
|
+
<section class="open-science-badges" aria-labelledby="open-science-badges-heading">
|
|
4
|
+
<h2 id="open-science-badges-heading">{% t 'open_science_badges.title' %}</h2>
|
|
5
|
+
<p class="open-science-badges__intro">{% t 'open_science_badges.description' %}</p>
|
|
6
|
+
<ul class="open-science-badges__list">
|
|
7
|
+
{% for badge in badges %}
|
|
8
|
+
<li class="open-science-badge" data-badge-label="{{ badge.label | slugify }}">
|
|
9
|
+
<span class="open-science-badge__label">{{ badge.label }}</span>
|
|
10
|
+
{% if badge.description %}<span class="open-science-badge__description">{{ badge.description }}</span>{% endif %}
|
|
11
|
+
{% if badge.criteria %}<span class="open-science-badge__criteria">{{ badge.criteria }}</span>{% endif %}
|
|
12
|
+
</li>
|
|
13
|
+
{% endfor %}
|
|
14
|
+
</ul>
|
|
15
|
+
</section>
|
|
16
|
+
{% endif %}
|