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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +188 -0
- data/CITATION.cff +2 -2
- data/README.md +25 -17
- data/_data/cdn-integrity.yml +0 -30
- data/_data/i18n/en.yml +297 -0
- data/_data/i18n/es.yml +297 -0
- data/_data/i18n/pt.yml +297 -0
- data/_data/js_manifest.json +16 -0
- data/_includes/analytics/dashboard.html +3 -1
- data/_includes/components/api-function.html +20 -1
- data/_includes/components/author-bio.html +21 -11
- data/_includes/components/author-list.html +32 -0
- data/_includes/components/citation-tools.html +33 -25
- data/_includes/components/comments-thread.html +90 -0
- data/_includes/components/contact-form.html +120 -0
- data/_includes/components/correction-report.html +82 -0
- data/_includes/components/enhanced-code-block.html +1 -1
- data/_includes/components/enhanced-toc.html +6 -7
- data/_includes/components/license-link.html +13 -0
- data/_includes/components/license-notice.html +28 -0
- data/_includes/components/moderation-inbox.html +139 -0
- data/_includes/components/reactions.html +49 -0
- data/_includes/components/reading-list.html +31 -0
- data/_includes/components/reading-mode-toggle.html +65 -0
- data/_includes/components/reading-state-bookmark.html +27 -0
- data/_includes/components/reading-state-panel.html +71 -0
- data/_includes/components/reproducibility.html +61 -0
- data/_includes/components/responsive-image.html +3 -3
- data/_includes/components/revision-history.html +39 -0
- data/_includes/components/revision-notice.html +35 -0
- data/_includes/components/series-nav.html +64 -0
- data/_includes/components/subscribe-form.html +88 -0
- data/_includes/components/subscription-manage.html +68 -0
- data/_includes/components/webmentions.html +48 -0
- data/_includes/csp-meta.html +135 -11
- data/_includes/footer.html +22 -20
- data/_includes/head.html +111 -63
- data/_includes/header/navigation.html +12 -15
- data/_includes/header.html +28 -19
- data/_includes/layouts/default/article.html +9 -7
- data/_includes/meta/dynamic-services-config.html +14 -0
- data/_includes/meta/math-config.html +18 -10
- data/_includes/meta/person-json.html +27 -0
- data/_includes/meta/publisher.html +45 -0
- data/_includes/meta/schema.html +67 -30
- data/_includes/meta/scholarly.html +121 -0
- data/_includes/meta/scripts-loader.html +18 -32
- data/_includes/meta/webmention-discovery.html +14 -0
- data/_includes/post/related-posts.html +4 -7
- data/_includes/scripts.html +57 -0
- data/_includes/search/index-data.json +9 -34
- data/_layouts/dataset.html +5 -3
- data/_layouts/default.html +16 -8
- data/_layouts/notebook.html +1 -0
- data/_layouts/package.html +4 -2
- data/_layouts/page.html +15 -0
- data/_layouts/portfolio.html +1 -0
- data/_layouts/post.html +102 -23
- data/_layouts/project.html +4 -3
- data/_layouts/research.html +20 -8
- data/_plugins/analytics_dashboard.rb +9 -3
- data/_plugins/authors.rb +133 -0
- data/_plugins/config_validator.rb +231 -23
- data/_plugins/critical_css_check.rb +42 -0
- data/_plugins/csp_generator.rb +18 -28
- data/_plugins/datalog_bibliography.rb +9 -7
- data/_plugins/datalog_comments.rb +8 -5
- data/_plugins/datalog_slides.rb +9 -8
- data/_plugins/i18n.rb +13 -12
- data/_plugins/image_optimizer.rb +241 -178
- data/_plugins/licenses.rb +135 -0
- data/_plugins/math_preprocessor.rb +59 -7
- data/_plugins/notebook_converter.rb +23 -4
- data/_plugins/plugin_loader.rb +3 -1
- data/_plugins/publications_generator.rb +8 -2
- data/_plugins/references.rb +238 -0
- data/_plugins/reproducibility.rb +150 -0
- data/_plugins/revisions.rb +101 -0
- data/_plugins/rouge_highlight_filter.rb +42 -0
- data/_plugins/scholarly.rb +50 -0
- data/_plugins/search_code_blocks.rb +30 -0
- data/_plugins/search_normalizer.rb +15 -53
- data/_plugins/search_pages.rb +3 -4
- data/_plugins/series.rb +104 -0
- data/_plugins/statements.rb +87 -0
- data/_sass/_academic-dashboard.scss +262 -0
- data/_sass/_base.scss +20 -1
- data/_sass/_comments-thread.scss +159 -0
- data/_sass/_components.scss +64 -1153
- data/_sass/_features.scss +17 -0
- data/_sass/_layout.scss +385 -1
- data/_sass/_mathematical.scss +27 -0
- data/_sass/_moderation.scss +222 -0
- data/_sass/_notebooks.scss +322 -0
- data/_sass/_open-science-badges.scss +56 -0
- data/_sass/{_phase1-enhancements.scss → _post-components.scss} +5 -3
- data/_sass/_print.scss +291 -0
- data/_sass/_reactions.scss +89 -0
- data/_sass/_reading-state.scss +290 -0
- data/_sass/_search-page.scss +530 -0
- data/_sass/_search.scss +46 -0
- data/_sass/_service-forms.scss +204 -0
- data/_sass/_subscriptions.scss +140 -0
- data/_sass/_syntax-highlighting.scss +212 -97
- data/_sass/_theme.scss +40 -19
- data/_sass/_typography.scss +130 -0
- data/_sass/_utilities.scss +5 -0
- data/_sass/_variables.scss +6 -0
- data/_sass/_webmentions.scss +125 -0
- data/assets/css/main.scss +14 -0
- data/assets/js/dist/academic.js +1 -1
- data/assets/js/dist/analytics-dashboard.js +1 -1
- data/assets/js/dist/chunks/chunk-2DYDWUFX.js +1 -0
- data/assets/js/dist/chunks/chunk-PATLC23F.js +1 -0
- data/assets/js/dist/chunks/chunk-V7734B2G.js +1 -0
- data/assets/js/dist/comments.js +2 -0
- data/assets/js/dist/contact.js +1 -0
- data/assets/js/dist/core.js +1 -1
- data/assets/js/dist/corrections.js +1 -0
- data/assets/js/dist/loader.js +1 -1
- data/assets/js/dist/math.js +1 -1
- data/assets/js/dist/moderation.js +1 -0
- data/assets/js/dist/notebook.js +1 -1
- data/assets/js/dist/reactions.js +1 -0
- data/assets/js/dist/reading-state.js +1 -0
- data/assets/js/dist/search.js +1 -1
- data/assets/js/dist/sources.json +52 -0
- data/assets/js/dist/subscriptions.js +1 -0
- data/assets/js/dist/visualizations.js +11 -2
- data/assets/js/dist/webmentions.js +1 -0
- data/assets/js/loader.js +37 -1
- data/datalog-theme.gemspec +35 -23
- data/lib/datalog/cli.rb +43 -15
- data/lib/datalog/critical_css.rb +168 -0
- data/lib/datalog/plugin_system/dependency_resolver.rb +0 -2
- data/lib/datalog/plugins/comments.rb +33 -3
- data/lib/datalog/theme/installed_files.rb +113 -0
- data/lib/datalog/theme/package.rb +57 -0
- data/lib/datalog/theme/repository_checkout.rb +94 -0
- data/lib/datalog/theme/version.rb +5 -1
- data/lib/datalog/warning_filter.rb +5 -11
- data/lib/datalog-theme.rb +6 -0
- metadata +96 -147
- data/_data/academic.yml +0 -217
- data/_data/config/author.yml +0 -121
- data/_data/datasets.yml +0 -28
- data/_data/js_meta.json +0 -371
- data/_data/navigation.yml +0 -145
- data/_data/projects.yml +0 -41
- data/_data/publications.yml +0 -28
- data/_data/social.yml +0 -73
- data/_data/visualizations.yml +0 -51
- data/_includes/components/advanced-search.html +0 -682
- data/_includes/components/bookmark-system.html +0 -96
- data/_includes/components/comments.html +0 -244
- data/_includes/components/content-recommendations.html +0 -228
- data/_includes/components/email-preferences.html +0 -200
- data/_includes/components/enhanced-metadata.html +0 -228
- data/_includes/components/language-switcher.html +0 -396
- data/_includes/components/navigation-enhancements.html +0 -454
- data/_includes/components/newsletter-signup.html +0 -178
- data/_includes/components/popular-posts.html +0 -233
- data/_includes/components/reading-progress.html +0 -133
- data/_includes/components/reading-time.html +0 -121
- data/_includes/components/series-navigation.html +0 -124
- data/_includes/components/social-proof.html +0 -34
- data/_includes/components/user-preferences.html +0 -566
- data/_includes/meta/syntax-config.html +0 -19
- data/_layouts/archive.html +0 -282
- data/_layouts/post-sidebar.html +0 -183
- data/_sass/_phase3-enhancements.scss +0 -874
- data/_sass/_phase4-enhancements.scss +0 -1214
- data/_sass/_phase5-enhancements.scss +0 -414
- data/assets/js/academic.js +0 -262
- data/assets/js/analytics-dashboard.js +0 -382
- data/assets/js/core/dark-mode.js +0 -79
- data/assets/js/core/github-cards.js +0 -123
- data/assets/js/core/language-filter.js +0 -69
- data/assets/js/core/navigation.js +0 -184
- data/assets/js/core/scroll-progress.js +0 -45
- data/assets/js/core/search-hotkeys.js +0 -62
- data/assets/js/core/skip-links.js +0 -62
- data/assets/js/dist/manifest.json +0 -22
- data/assets/js/dist/meta.json +0 -371
- data/assets/js/main.js +0 -23
- data/assets/js/math.js +0 -818
- data/assets/js/notebook.js +0 -158
- data/assets/js/search/analytics.js +0 -91
- data/assets/js/search/app.js +0 -271
- data/assets/js/search/autocomplete.js +0 -120
- data/assets/js/search/engine.js +0 -260
- data/assets/js/search/filters.js +0 -45
- data/assets/js/search/render.js +0 -217
- data/assets/js/search/utils.js +0 -99
- data/assets/js/search.js +0 -354
- data/assets/js/visualizations.js +0 -816
- data/assets/publications/datalog-publications.bib +0 -8
- data/assets/publications/datalog-publications.ris +0 -9
- data/assets/publications/publications.bib +0 -30
- data/assets/templates/diogo-ribeiro-cv.md +0 -31
- data/assets/templates/diogo-ribeiro-cv.tex +0 -32
- data/lib/datalog/theme/theme.rb +0 -18
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
---
|
|
3
|
+
optional: []
|
|
4
|
+
---
|
|
5
|
+
The comments thread of the `api` provider (lib/datalog/plugins/comments.rb
|
|
6
|
+
renders it through the datalog_comments tag; assets/js/comments/thread.js
|
|
7
|
+
fills it): the discussion under an article, read from and written to the
|
|
8
|
+
site's own backend through the dynamic services (docs/dynamic-services.md).
|
|
9
|
+
Reads `page.datalog_comments` (the plugin's resolved settings: `replies`,
|
|
10
|
+
`moderation`, `endpoint`) and `page.url`, which keys the thread.
|
|
11
|
+
{%- endcomment -%}
|
|
12
|
+
{%- assign settings = page.datalog_comments -%}
|
|
13
|
+
{%- capture labels_json -%}
|
|
14
|
+
{
|
|
15
|
+
"loading": {{ 'comments.loading' | t | jsonify }},
|
|
16
|
+
"empty": {{ 'comments.empty' | t | jsonify }},
|
|
17
|
+
"retry": {{ 'comments.retry' | t | jsonify }},
|
|
18
|
+
"reply": {{ 'comments.reply' | t | jsonify }},
|
|
19
|
+
"replying_to": {{ 'comments.replying_to' | t | jsonify }},
|
|
20
|
+
"anonymous": {{ 'comments.anonymous' | t | jsonify }},
|
|
21
|
+
"pending": {{ 'comments.pending' | t | jsonify }},
|
|
22
|
+
"sending": {{ 'comments.sending' | t | jsonify }},
|
|
23
|
+
"posted": {{ 'comments.posted' | t | jsonify }},
|
|
24
|
+
"posted_pending": {{ 'comments.posted_pending' | t | jsonify }},
|
|
25
|
+
"name_required": {{ 'comments.name_required' | t | jsonify }},
|
|
26
|
+
"email_invalid": {{ 'comments.email_invalid' | t | jsonify }},
|
|
27
|
+
"url_invalid": {{ 'comments.url_invalid' | t | jsonify }},
|
|
28
|
+
"body_short": {{ 'comments.body_short' | t | jsonify }},
|
|
29
|
+
"errors": {
|
|
30
|
+
{%- assign kinds = "disabled,unsupported,version,timeout,network,malformed,unauthorized,forbidden,not_found,conflict,invalid,rate_limited,server,http,aborted,reference" | split: "," -%}
|
|
31
|
+
{%- for kind in kinds -%}
|
|
32
|
+
{%- assign kind_key = "dynamic_services.errors." | append: kind -%}
|
|
33
|
+
{{ kind | jsonify }}: {{ kind_key | t | jsonify }}{% unless forloop.last %},{% endunless %}
|
|
34
|
+
{%- endfor -%}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
{%- endcapture -%}
|
|
38
|
+
<div class="comments-thread" id="comments" data-comments-thread data-comments-path="{{ page.url }}" data-comments-replies="{% if settings.replies == false %}false{% else %}true{% endif %}"{% if settings.endpoint and settings.endpoint != '' %} data-comments-endpoint="{{ settings.endpoint }}"{% endif %} data-state="idle">
|
|
39
|
+
<script type="application/json" nonce="{{ page.csp_nonce }}" data-comments-labels>{{ labels_json | strip }}</script>
|
|
40
|
+
<p class="comments-thread__status" data-comments-status role="status" aria-live="polite">{% t 'comments.loading' %}</p>
|
|
41
|
+
<noscript><p class="comments-thread__noscript">{% t 'comments.noscript' %}</p></noscript>
|
|
42
|
+
<ol class="comments-thread__list" data-comments-list hidden></ol>
|
|
43
|
+
<form class="service-form comments-form" id="comments-form" data-comments-form data-state="idle" aria-busy="false" aria-labelledby="comments-form-heading" novalidate>
|
|
44
|
+
<h3 class="comments-form__heading" id="comments-form-heading">{% t 'comments.form_heading' %}</h3>
|
|
45
|
+
<p class="comments-form__replying" data-comments-replying hidden>
|
|
46
|
+
<span data-comments-replying-to></span>
|
|
47
|
+
<button type="button" class="post-tool" data-comments-cancel-reply>{% t 'comments.cancel_reply' %}</button>
|
|
48
|
+
</p>
|
|
49
|
+
<fieldset class="service-form__fields">
|
|
50
|
+
<legend class="visually-hidden">{% t 'comments.form_heading' %}</legend>
|
|
51
|
+
<div class="service-form__row">
|
|
52
|
+
<div class="service-form__field">
|
|
53
|
+
<label for="comments-name">{% t 'comments.name' %}</label>
|
|
54
|
+
<input id="comments-name" name="name" type="text" required autocomplete="name">
|
|
55
|
+
<p class="service-form__error" data-error-for="name" hidden></p>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="service-form__field">
|
|
58
|
+
<label for="comments-email">{% t 'comments.email' %}</label>
|
|
59
|
+
<input id="comments-email" name="email" type="email" autocomplete="email" aria-describedby="comments-email-hint">
|
|
60
|
+
<p class="service-form__hint" id="comments-email-hint">{% t 'comments.email_hint' %}</p>
|
|
61
|
+
<p class="service-form__error" data-error-for="email" hidden></p>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="service-form__field">
|
|
65
|
+
<label for="comments-url">{% t 'comments.url' %}</label>
|
|
66
|
+
<input id="comments-url" name="url" type="url" autocomplete="url">
|
|
67
|
+
<p class="service-form__error" data-error-for="url" hidden></p>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="service-form__field">
|
|
70
|
+
<label for="comments-body">{% t 'comments.body' %}</label>
|
|
71
|
+
<textarea id="comments-body" name="body" rows="5" required></textarea>
|
|
72
|
+
<p class="service-form__error" data-error-for="body" hidden></p>
|
|
73
|
+
</div>
|
|
74
|
+
<input type="hidden" name="parent_id" value="">
|
|
75
|
+
<div class="service-form__trap" aria-hidden="true">
|
|
76
|
+
<label for="comments-website">Website</label>
|
|
77
|
+
<input id="comments-website" name="website" type="text" tabindex="-1" autocomplete="off">
|
|
78
|
+
</div>
|
|
79
|
+
</fieldset>
|
|
80
|
+
{%- if settings.moderation != false %}
|
|
81
|
+
<p class="service-form__privacy">{% t 'comments.moderated' %}</p>
|
|
82
|
+
{%- else %}
|
|
83
|
+
<p class="service-form__privacy">{% t 'comments.privacy' %}</p>
|
|
84
|
+
{%- endif %}
|
|
85
|
+
<div class="service-form__actions">
|
|
86
|
+
<button type="submit" class="post-tool">{% t 'comments.send' %}</button>
|
|
87
|
+
</div>
|
|
88
|
+
<p class="service-form__status" data-form-status role="status" aria-live="polite" hidden></p>
|
|
89
|
+
</form>
|
|
90
|
+
</div>
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
---
|
|
3
|
+
optional: []
|
|
4
|
+
---
|
|
5
|
+
The contact and collaboration form (assets/js/contact/form.js): a structured
|
|
6
|
+
message to the author, research collaboration, consulting, speaking,
|
|
7
|
+
mentoring, a reproducibility question, a media request or anything else,
|
|
8
|
+
sent to the site's dynamic services (docs/dynamic-services.md). Rendered by
|
|
9
|
+
the page layout when the page says `contact_form: true`, or included
|
|
10
|
+
anywhere. Without a backend offering the feature it falls back to the
|
|
11
|
+
site's email address, so the page is never empty. Categories come from
|
|
12
|
+
`contact.categories`, their labels and prompts from `contact.categories.*`
|
|
13
|
+
and `contact.prompts.*` in _data/i18n; `contact.prompts` in _config.yml
|
|
14
|
+
overrides a prompt.
|
|
15
|
+
{%- endcomment -%}
|
|
16
|
+
{%- assign services = site.dynamic_services -%}
|
|
17
|
+
{%- assign contact = site.contact -%}
|
|
18
|
+
{%- if contact.enabled != false -%}
|
|
19
|
+
{%- assign mail = site.contact_email | default: site.author.email -%}
|
|
20
|
+
{%- if services.base_url and services.base_url != '' and services.features.contact != false -%}
|
|
21
|
+
{%- assign categories = contact.categories -%}
|
|
22
|
+
{%- if categories == nil or categories.size == 0 -%}
|
|
23
|
+
{%- assign categories = "research-collaboration,consulting,speaking,mentoring,reproducibility,media,other" | split: "," -%}
|
|
24
|
+
{%- endif -%}
|
|
25
|
+
{%- capture labels_json -%}
|
|
26
|
+
{
|
|
27
|
+
"pending": {{ 'contact.pending' | t | jsonify }},
|
|
28
|
+
"success": {{ 'contact.success' | t | jsonify }},
|
|
29
|
+
"name_required": {{ 'contact.name_required' | t | jsonify }},
|
|
30
|
+
"email_invalid": {{ 'contact.email_invalid' | t | jsonify }},
|
|
31
|
+
"subject_required": {{ 'contact.subject_required' | t | jsonify }},
|
|
32
|
+
"message_short": {{ 'contact.message_short' | t | jsonify }},
|
|
33
|
+
"errors": {
|
|
34
|
+
{%- assign kinds = "disabled,unsupported,version,timeout,network,malformed,unauthorized,forbidden,not_found,conflict,invalid,rate_limited,server,http,aborted,reference" | split: "," -%}
|
|
35
|
+
{%- for kind in kinds -%}
|
|
36
|
+
{%- assign kind_key = "dynamic_services.errors." | append: kind -%}
|
|
37
|
+
{{ kind | jsonify }}: {{ kind_key | t | jsonify }}{% unless forloop.last %},{% endunless %}
|
|
38
|
+
{%- endfor -%}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
{%- endcapture -%}
|
|
42
|
+
{%- capture prompts_json -%}
|
|
43
|
+
{
|
|
44
|
+
{%- for category in categories -%}
|
|
45
|
+
{%- assign prompt = nil -%}
|
|
46
|
+
{%- if contact.prompts -%}{%- assign prompt = contact.prompts[category] -%}{%- endif -%}
|
|
47
|
+
{%- unless prompt -%}
|
|
48
|
+
{%- assign prompt_key = "contact.prompts." | append: category -%}
|
|
49
|
+
{%- assign prompt = prompt_key | t -%}
|
|
50
|
+
{%- if prompt == prompt_key -%}{%- assign prompt = nil -%}{%- endif -%}
|
|
51
|
+
{%- endunless -%}
|
|
52
|
+
{%- if prompt -%}{{ category | jsonify }}: {{ prompt | jsonify }},{%- endif -%}
|
|
53
|
+
{%- endfor -%}
|
|
54
|
+
"": ""
|
|
55
|
+
}
|
|
56
|
+
{%- endcapture -%}
|
|
57
|
+
{%- assign privacy = contact.privacy_notice | default: nil -%}
|
|
58
|
+
{%- unless privacy -%}{%- assign privacy = 'contact.privacy' | t -%}{%- endunless -%}
|
|
59
|
+
<section class="contact-form" id="contact" data-contact-form data-source-url="{{ page.url | absolute_url }}">
|
|
60
|
+
<script type="application/json" nonce="{{ page.csp_nonce }}" data-contact-labels>{{ labels_json | strip }}</script>
|
|
61
|
+
<script type="application/json" nonce="{{ page.csp_nonce }}" data-contact-prompts>{{ prompts_json | strip }}</script>
|
|
62
|
+
<form class="service-form contact-form__form" id="contact-form" data-state="idle" aria-busy="false" novalidate>
|
|
63
|
+
<p class="service-form__intro">{% t 'contact.intro' %}</p>
|
|
64
|
+
<fieldset class="service-form__fields">
|
|
65
|
+
<legend class="visually-hidden">{% t 'contact.legend' %}</legend>
|
|
66
|
+
<div class="service-form__field">
|
|
67
|
+
<label for="contact-category">{% t 'contact.category' %}</label>
|
|
68
|
+
<select id="contact-category" name="category">
|
|
69
|
+
{%- for category in categories %}
|
|
70
|
+
{%- assign category_key = "contact.categories." | append: category %}
|
|
71
|
+
<option value="{{ category | escape }}">{{ category_key | t }}</option>
|
|
72
|
+
{%- endfor %}
|
|
73
|
+
</select>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="service-form__row">
|
|
76
|
+
<div class="service-form__field">
|
|
77
|
+
<label for="contact-name">{% t 'contact.name' %}</label>
|
|
78
|
+
<input id="contact-name" name="name" type="text" required autocomplete="name">
|
|
79
|
+
<p class="service-form__error" data-error-for="name" hidden></p>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="service-form__field">
|
|
82
|
+
<label for="contact-email">{% t 'contact.email' %}</label>
|
|
83
|
+
<input id="contact-email" name="email" type="email" required autocomplete="email">
|
|
84
|
+
<p class="service-form__error" data-error-for="email" hidden></p>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="service-form__field">
|
|
88
|
+
<label for="contact-affiliation">{% t 'contact.affiliation' %}</label>
|
|
89
|
+
<input id="contact-affiliation" name="affiliation" type="text" autocomplete="organization">
|
|
90
|
+
</div>
|
|
91
|
+
<div class="service-form__field">
|
|
92
|
+
<label for="contact-subject">{% t 'contact.subject' %}</label>
|
|
93
|
+
<input id="contact-subject" name="subject" type="text" required>
|
|
94
|
+
<p class="service-form__error" data-error-for="subject" hidden></p>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="service-form__field">
|
|
97
|
+
<label for="contact-message">{% t 'contact.message' %}</label>
|
|
98
|
+
<textarea id="contact-message" name="message" rows="7" required minlength="20" aria-describedby="contact-message-hint"></textarea>
|
|
99
|
+
<p class="service-form__hint" id="contact-message-hint" data-contact-hint>{% t 'contact.message_hint' %}</p>
|
|
100
|
+
<p class="service-form__error" data-error-for="message" hidden></p>
|
|
101
|
+
</div>
|
|
102
|
+
<div class="service-form__trap" aria-hidden="true">
|
|
103
|
+
<label for="contact-website">Website</label>
|
|
104
|
+
<input id="contact-website" name="website" type="text" tabindex="-1" autocomplete="off">
|
|
105
|
+
</div>
|
|
106
|
+
</fieldset>
|
|
107
|
+
<p class="service-form__privacy">{{ privacy }}{% if contact.retention %} {{ contact.retention }}{% endif %}</p>
|
|
108
|
+
<div class="service-form__actions">
|
|
109
|
+
<button type="submit" class="post-tool">{% t 'contact.send' %}</button>
|
|
110
|
+
</div>
|
|
111
|
+
<p class="service-form__status" data-form-status role="status" aria-live="polite" hidden></p>
|
|
112
|
+
</form>
|
|
113
|
+
</section>
|
|
114
|
+
{%- elsif mail -%}
|
|
115
|
+
{%- capture mail_link -%}<a href="mailto:{{ mail }}">{{ mail }}</a>{%- endcapture -%}
|
|
116
|
+
<section class="contact-form contact-form--fallback" id="contact">
|
|
117
|
+
<p class="contact-form__fallback">{% t 'contact.fallback' email: mail_link %}</p>
|
|
118
|
+
</section>
|
|
119
|
+
{%- endif -%}
|
|
120
|
+
{%- endif -%}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
---
|
|
3
|
+
optional: []
|
|
4
|
+
---
|
|
5
|
+
"Report an error or suggest a correction" (assets/js/corrections/form.js):
|
|
6
|
+
structured feedback about an article, apart from comments, sent to the
|
|
7
|
+
site's dynamic services (docs/dynamic-services.md) and never shown on the
|
|
8
|
+
page. Renders only when the site has a backend with the corrections feature
|
|
9
|
+
on, `corrections.enabled` is not false, and the page does not say
|
|
10
|
+
`corrections: false`. The categories come from `corrections.categories`,
|
|
11
|
+
their labels from `corrections.categories.*` in _data/i18n.
|
|
12
|
+
{%- endcomment -%}
|
|
13
|
+
{%- assign services = site.dynamic_services -%}
|
|
14
|
+
{%- assign corrections = site.corrections -%}
|
|
15
|
+
{%- if services.base_url and services.base_url != '' and services.features.corrections != false and corrections.enabled != false and page.corrections != false -%}
|
|
16
|
+
{%- assign categories = corrections.categories -%}
|
|
17
|
+
{%- if categories == nil or categories.size == 0 -%}
|
|
18
|
+
{%- assign categories = "mathematical-error,factual-error,citation,code,reproducibility,typo,accessibility,other" | split: "," -%}
|
|
19
|
+
{%- endif -%}
|
|
20
|
+
{%- capture labels_json -%}
|
|
21
|
+
{
|
|
22
|
+
"pending": {{ 'corrections.pending' | t | jsonify }},
|
|
23
|
+
"success": {{ 'corrections.success' | t | jsonify }},
|
|
24
|
+
"message_short": {{ 'corrections.message_short' | t | jsonify }},
|
|
25
|
+
"email_invalid": {{ 'corrections.email_invalid' | t | jsonify }},
|
|
26
|
+
"errors": {
|
|
27
|
+
{%- assign kinds = "disabled,unsupported,version,timeout,network,malformed,unauthorized,forbidden,not_found,conflict,invalid,rate_limited,server,http,aborted,reference" | split: "," -%}
|
|
28
|
+
{%- for kind in kinds -%}
|
|
29
|
+
{%- assign kind_key = "dynamic_services.errors." | append: kind -%}
|
|
30
|
+
{{ kind | jsonify }}: {{ kind_key | t | jsonify }}{% unless forloop.last %},{% endunless %}
|
|
31
|
+
{%- endfor -%}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
{%- endcapture -%}
|
|
35
|
+
<details class="correction-report" id="correction-report" data-correction-report data-article-url="{{ page.url | absolute_url }}" data-article-title="{{ page.title | escape }}">
|
|
36
|
+
<summary class="correction-report__summary">{% t 'corrections.summary' %}</summary>
|
|
37
|
+
<script type="application/json" nonce="{{ page.csp_nonce }}" data-correction-labels>{{ labels_json | strip }}</script>
|
|
38
|
+
<form class="service-form correction-report__form" id="correction-report-form" data-state="idle" aria-busy="false" novalidate>
|
|
39
|
+
<p class="service-form__intro">{% t 'corrections.intro' %}</p>
|
|
40
|
+
<p class="service-form__quote" data-correction-quote hidden></p>
|
|
41
|
+
<fieldset class="service-form__fields">
|
|
42
|
+
<legend class="visually-hidden">{% t 'corrections.summary' %}</legend>
|
|
43
|
+
<div class="service-form__field">
|
|
44
|
+
<label for="correction-category">{% t 'corrections.category' %}</label>
|
|
45
|
+
<select id="correction-category" name="category">
|
|
46
|
+
{%- for category in categories %}
|
|
47
|
+
{%- assign category_key = "corrections.categories." | append: category %}
|
|
48
|
+
<option value="{{ category | escape }}">{{ category_key | t }}</option>
|
|
49
|
+
{%- endfor %}
|
|
50
|
+
</select>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="service-form__field">
|
|
53
|
+
<label for="correction-section">{% t 'corrections.section' %}</label>
|
|
54
|
+
<select id="correction-section" name="section">
|
|
55
|
+
<option value="">{% t 'corrections.section_any' %}</option>
|
|
56
|
+
</select>
|
|
57
|
+
</div>
|
|
58
|
+
<div class="service-form__field">
|
|
59
|
+
<label for="correction-message">{% t 'corrections.message' %}</label>
|
|
60
|
+
<textarea id="correction-message" name="message" rows="5" required minlength="20" aria-describedby="correction-message-hint"></textarea>
|
|
61
|
+
<p class="service-form__hint" id="correction-message-hint">{% t 'corrections.message_hint' %}</p>
|
|
62
|
+
<p class="service-form__error" data-error-for="message" hidden></p>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="service-form__field">
|
|
65
|
+
<label for="correction-email">{% t 'corrections.contact_email' %}</label>
|
|
66
|
+
<input id="correction-email" name="contact_email" type="email" autocomplete="email" aria-describedby="correction-email-hint">
|
|
67
|
+
<p class="service-form__hint" id="correction-email-hint">{% t 'corrections.contact_hint' %}</p>
|
|
68
|
+
<p class="service-form__error" data-error-for="contact_email" hidden></p>
|
|
69
|
+
</div>
|
|
70
|
+
<input type="hidden" name="quote" value="">
|
|
71
|
+
<div class="service-form__trap" aria-hidden="true">
|
|
72
|
+
<label for="correction-website">Website</label>
|
|
73
|
+
<input id="correction-website" name="website" type="text" tabindex="-1" autocomplete="off">
|
|
74
|
+
</div>
|
|
75
|
+
</fieldset>
|
|
76
|
+
<div class="service-form__actions">
|
|
77
|
+
<button type="submit" class="post-tool">{% t 'corrections.send' %}</button>
|
|
78
|
+
</div>
|
|
79
|
+
<p class="service-form__status" data-form-status role="status" aria-live="polite" hidden></p>
|
|
80
|
+
</form>
|
|
81
|
+
</details>
|
|
82
|
+
{%- endif -%}
|
|
@@ -104,7 +104,7 @@ Parameters:
|
|
|
104
104
|
</div>
|
|
105
105
|
</div>
|
|
106
106
|
|
|
107
|
-
<pre class="enhanced-code-block__pre{% if show_line_numbers %} enhanced-code-block__pre--numbered{% endif %}"><code id="{{ code_id }}" class="language-{{ language }}" data-code-content>{{ code_content }}</code></pre>
|
|
107
|
+
<pre class="highlight enhanced-code-block__pre{% if show_line_numbers %} enhanced-code-block__pre--numbered{% endif %}" tabindex="0"><code id="{{ code_id }}" class="language-{{ language }}" data-code-content>{{ code_content | rouge_highlight: language }}</code></pre>
|
|
108
108
|
|
|
109
109
|
{%- comment -%} Feedback message {%- endcomment -%}
|
|
110
110
|
<div class="enhanced-code-block__feedback" data-feedback role="status" aria-live="polite"></div>
|
|
@@ -27,7 +27,8 @@ Parameters:
|
|
|
27
27
|
{%- endcapture -%}
|
|
28
28
|
|
|
29
29
|
{%- if toc_markup contains '<li' -%}
|
|
30
|
-
<
|
|
30
|
+
{%- comment -%} A <nav>: doc-toc refines the navigation landmark, and is not a role an <aside> may carry. {%- endcomment -%}
|
|
31
|
+
<nav class="enhanced-toc{% if collapsible %} enhanced-toc--collapsible{% endif %}"
|
|
31
32
|
data-toc-enhanced
|
|
32
33
|
aria-label="{{ toc_heading }}"
|
|
33
34
|
role="doc-toc">
|
|
@@ -87,7 +88,7 @@ Parameters:
|
|
|
87
88
|
<span data-progress-percent>0</span>% complete
|
|
88
89
|
</span>
|
|
89
90
|
</div>
|
|
90
|
-
</
|
|
91
|
+
</nav>
|
|
91
92
|
|
|
92
93
|
<script>
|
|
93
94
|
(function() {
|
|
@@ -154,7 +155,8 @@ Parameters:
|
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
|
|
157
|
-
//
|
|
158
|
+
// Scroll to TOC targets. The stylesheet's scroll-behavior decides whether
|
|
159
|
+
// the scroll is smooth, so reduced-motion preferences are respected.
|
|
158
160
|
tocLinks.forEach(link => {
|
|
159
161
|
link.addEventListener('click', function(e) {
|
|
160
162
|
const targetId = this.getAttribute('href');
|
|
@@ -163,10 +165,7 @@ Parameters:
|
|
|
163
165
|
const targetElement = document.querySelector(targetId);
|
|
164
166
|
if (targetElement) {
|
|
165
167
|
const offsetTop = targetElement.offsetTop - 80; // Account for sticky header
|
|
166
|
-
window.scrollTo({
|
|
167
|
-
top: offsetTop,
|
|
168
|
-
behavior: 'smooth'
|
|
169
|
-
});
|
|
168
|
+
window.scrollTo({ top: offsetTop });
|
|
170
169
|
// Update URL without jumping
|
|
171
170
|
if (history.pushState) {
|
|
172
171
|
history.pushState(null, null, targetId);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
---
|
|
3
|
+
required:
|
|
4
|
+
- license
|
|
5
|
+
---
|
|
6
|
+
A licence's name (from the page_license or page_code_license filter), linked
|
|
7
|
+
with rel="license" when the licence has a URL.
|
|
8
|
+
{%- endcomment -%}
|
|
9
|
+
{%- if include.license.url -%}
|
|
10
|
+
<a class="license-link" rel="license" href="{{ include.license.url | escape }}">{{ include.license.name | escape }}</a>
|
|
11
|
+
{%- else -%}
|
|
12
|
+
<span class="license-link">{{ include.license.name | escape }}</span>
|
|
13
|
+
{%- endif -%}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
---
|
|
3
|
+
required:
|
|
4
|
+
- page
|
|
5
|
+
---
|
|
6
|
+
The reuse terms of an article: its copyright holder and year, the licence of
|
|
7
|
+
its text and figures and, when it differs, of its code samples
|
|
8
|
+
(_plugins/licenses.rb). Renders nothing for a page without a licence.
|
|
9
|
+
{%- endcomment -%}
|
|
10
|
+
{%- assign content_license = include.page | page_license -%}
|
|
11
|
+
{%- assign code_license = include.page | page_code_license -%}
|
|
12
|
+
{%- if content_license or code_license -%}
|
|
13
|
+
{%- capture content_link -%}{% include components/license-link.html license=content_license %}{%- endcapture -%}
|
|
14
|
+
{%- capture code_link -%}{% include components/license-link.html license=code_license %}{%- endcapture -%}
|
|
15
|
+
{%- assign content_link = content_link | strip -%}
|
|
16
|
+
{%- assign code_link = code_link | strip -%}
|
|
17
|
+
<p class="content-license">
|
|
18
|
+
{%- if content_license and content_license.holder %}
|
|
19
|
+
<span class="content-license__holder">©{% if content_license.year %} {{ content_license.year }}{% endif %} {{ content_license.holder | escape }}.</span>
|
|
20
|
+
{%- endif %}
|
|
21
|
+
{%- if content_license %}
|
|
22
|
+
{% if content_license.reserved %}{% t 'post.license.reserved' %}{% else %}{% t 'post.license.content' license: content_link %}{% endif %}
|
|
23
|
+
{%- endif %}
|
|
24
|
+
{%- if code_license %}
|
|
25
|
+
{% t 'post.license.code' license: code_link %}
|
|
26
|
+
{%- endif %}
|
|
27
|
+
</p>
|
|
28
|
+
{%- endif -%}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
---
|
|
3
|
+
optional: []
|
|
4
|
+
---
|
|
5
|
+
The moderation inbox (assets/js/moderation/inbox.js, docs/moderation.md):
|
|
6
|
+
what the self-hosted features received, comments awaiting approval or
|
|
7
|
+
flagged as spam, correction reports, abuse reports, with the actions a
|
|
8
|
+
moderator can take. Rendered by the page layout when the page says
|
|
9
|
+
`moderation_inbox: true`, and only when `moderation.enabled` is true and
|
|
10
|
+
there is a moderation API to talk to (`moderation.endpoint`, or the
|
|
11
|
+
dynamic services' base_url).
|
|
12
|
+
|
|
13
|
+
This markup is a shell and protects nothing: a static site cannot hold an
|
|
14
|
+
admin secret. Every request is authenticated and authorized by the service
|
|
15
|
+
or the identity-aware proxy in front of it; the settings here (an address,
|
|
16
|
+
a fetch credentials mode, a sign-in link) are public.
|
|
17
|
+
{%- endcomment -%}
|
|
18
|
+
{%- assign moderation = site.moderation -%}
|
|
19
|
+
{%- assign services = site.dynamic_services -%}
|
|
20
|
+
{%- assign endpoint = moderation.endpoint | default: '' -%}
|
|
21
|
+
{%- assign has_api = false -%}
|
|
22
|
+
{%- if endpoint != '' -%}{%- assign has_api = true -%}{%- endif -%}
|
|
23
|
+
{%- if services.base_url and services.base_url != '' and services.features.moderation != false -%}{%- assign has_api = true -%}{%- endif -%}
|
|
24
|
+
{%- if moderation.enabled == true and has_api -%}
|
|
25
|
+
{%- assign categories = site.corrections.categories -%}
|
|
26
|
+
{%- if categories == nil or categories.size == 0 -%}
|
|
27
|
+
{%- assign categories = "mathematical-error,factual-error,citation,code,reproducibility,typo,accessibility,other" | split: "," -%}
|
|
28
|
+
{%- endif -%}
|
|
29
|
+
{%- assign statuses = "pending,approved,spam,hidden,deleted,new,reviewed,accepted,rejected,resolved,open,dismissed" | split: "," -%}
|
|
30
|
+
{%- assign actions = "approve,spam,hide,delete,reviewed,accept,reject,resolve,dismiss" | split: "," -%}
|
|
31
|
+
{%- assign types = "comment,correction,abuse" | split: "," -%}
|
|
32
|
+
{%- capture labels_json -%}
|
|
33
|
+
{
|
|
34
|
+
"loading": {{ 'moderation.loading' | t | jsonify }},
|
|
35
|
+
"empty": {{ 'moderation.empty' | t | jsonify }},
|
|
36
|
+
"working": {{ 'moderation.working' | t | jsonify }},
|
|
37
|
+
"done": {{ 'moderation.done' | t | jsonify }},
|
|
38
|
+
"unauthorized": {{ 'moderation.unauthorized' | t | jsonify }},
|
|
39
|
+
"forbidden": {{ 'moderation.forbidden' | t | jsonify }},
|
|
40
|
+
"link_required": {{ 'moderation.link_required' | t | jsonify }},
|
|
41
|
+
"note": {{ 'moderation.note' | t | jsonify }},
|
|
42
|
+
"link": {{ 'moderation.link' | t | jsonify }},
|
|
43
|
+
"in_reply_to": {{ 'moderation.in_reply_to' | t | jsonify }},
|
|
44
|
+
"types": {
|
|
45
|
+
{%- for type in types -%}{%- assign key = "moderation.types." | append: type -%}
|
|
46
|
+
{{ type | jsonify }}: {{ key | t | jsonify }}{% unless forloop.last %},{% endunless %}
|
|
47
|
+
{%- endfor -%}
|
|
48
|
+
},
|
|
49
|
+
"statuses": {
|
|
50
|
+
{%- for status in statuses -%}{%- assign key = "moderation.statuses." | append: status -%}
|
|
51
|
+
{{ status | jsonify }}: {{ key | t | jsonify }}{% unless forloop.last %},{% endunless %}
|
|
52
|
+
{%- endfor -%}
|
|
53
|
+
},
|
|
54
|
+
"actions": {
|
|
55
|
+
{%- for action in actions -%}{%- assign key = "moderation.actions." | append: action -%}
|
|
56
|
+
{{ action | jsonify }}: {{ key | t | jsonify }}{% unless forloop.last %},{% endunless %}
|
|
57
|
+
{%- endfor -%}
|
|
58
|
+
},
|
|
59
|
+
"categories": {
|
|
60
|
+
{%- for category in categories -%}{%- assign key = "corrections.categories." | append: category -%}
|
|
61
|
+
{{ category | jsonify }}: {{ key | t | jsonify }}{% unless forloop.last %},{% endunless %}
|
|
62
|
+
{%- endfor -%}
|
|
63
|
+
},
|
|
64
|
+
"errors": {
|
|
65
|
+
{%- assign kinds = "disabled,unsupported,version,timeout,network,malformed,unauthorized,forbidden,not_found,conflict,invalid,rate_limited,server,http,aborted,reference" | split: "," -%}
|
|
66
|
+
{%- for kind in kinds -%}
|
|
67
|
+
{%- assign kind_key = "dynamic_services.errors." | append: kind -%}
|
|
68
|
+
{{ kind | jsonify }}: {{ kind_key | t | jsonify }}{% unless forloop.last %},{% endunless %}
|
|
69
|
+
{%- endfor -%}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
{%- endcapture -%}
|
|
73
|
+
<section class="moderation" id="moderation" data-moderation-inbox data-moderation-site="{{ '' | absolute_url }}"{% if endpoint != '' %} data-moderation-endpoint="{{ endpoint }}"{% endif %} data-moderation-credentials="{{ moderation.credentials | default: 'include' }}" data-state="idle" aria-busy="false">
|
|
74
|
+
<script type="application/json" nonce="{{ page.csp_nonce }}" data-moderation-labels>{{ labels_json | strip }}</script>
|
|
75
|
+
<p class="moderation__boundary">{% t 'moderation.boundary' %}</p>
|
|
76
|
+
<form class="service-form moderation__filters" id="moderation-filters" data-moderation-filters>
|
|
77
|
+
<fieldset class="service-form__fields moderation__filter-grid">
|
|
78
|
+
<legend class="visually-hidden">{% t 'moderation.filters' %}</legend>
|
|
79
|
+
<div class="service-form__field">
|
|
80
|
+
<label for="moderation-type">{% t 'moderation.type' %}</label>
|
|
81
|
+
<select id="moderation-type" name="type">
|
|
82
|
+
<option value="">{% t 'moderation.any_type' %}</option>
|
|
83
|
+
{%- for type in types %}{%- assign key = "moderation.types." | append: type %}
|
|
84
|
+
<option value="{{ type }}">{{ key | t }}</option>
|
|
85
|
+
{%- endfor %}
|
|
86
|
+
</select>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="service-form__field">
|
|
89
|
+
<label for="moderation-status">{% t 'moderation.status' %}</label>
|
|
90
|
+
<select id="moderation-status" name="status">
|
|
91
|
+
<option value="">{% t 'moderation.queue' %}</option>
|
|
92
|
+
{%- for status in statuses %}{%- assign key = "moderation.statuses." | append: status %}
|
|
93
|
+
<option value="{{ status }}">{{ key | t }}</option>
|
|
94
|
+
{%- endfor %}
|
|
95
|
+
</select>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="service-form__field">
|
|
98
|
+
<label for="moderation-category">{% t 'moderation.category' %}</label>
|
|
99
|
+
<select id="moderation-category" name="category">
|
|
100
|
+
<option value="">{% t 'moderation.any_category' %}</option>
|
|
101
|
+
{%- for category in categories %}{%- assign key = "corrections.categories." | append: category %}
|
|
102
|
+
<option value="{{ category | escape }}">{{ key | t }}</option>
|
|
103
|
+
{%- endfor %}
|
|
104
|
+
</select>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="service-form__field">
|
|
107
|
+
<label for="moderation-path">{% t 'moderation.path' %}</label>
|
|
108
|
+
<input id="moderation-path" name="path" type="text" autocomplete="off">
|
|
109
|
+
</div>
|
|
110
|
+
<div class="service-form__field">
|
|
111
|
+
<label for="moderation-since">{% t 'moderation.since' %}</label>
|
|
112
|
+
<input id="moderation-since" name="since" type="date">
|
|
113
|
+
</div>
|
|
114
|
+
<div class="service-form__field">
|
|
115
|
+
<label for="moderation-search">{% t 'moderation.search' %}</label>
|
|
116
|
+
<input id="moderation-search" name="q" type="search" autocomplete="off">
|
|
117
|
+
</div>
|
|
118
|
+
</fieldset>
|
|
119
|
+
<div class="service-form__actions">
|
|
120
|
+
<button type="submit" class="post-tool">{% t 'moderation.apply' %}</button>
|
|
121
|
+
<button type="reset" class="post-tool">{% t 'moderation.reset' %}</button>
|
|
122
|
+
</div>
|
|
123
|
+
</form>
|
|
124
|
+
<p class="moderation__status" data-moderation-status role="status" aria-live="polite" hidden></p>
|
|
125
|
+
<p class="moderation__sign-in" data-moderation-sign-in hidden>
|
|
126
|
+
{%- if moderation.sign_in_url and moderation.sign_in_url != '' %}
|
|
127
|
+
<a href="{{ moderation.sign_in_url }}" rel="noopener">{% t 'moderation.sign_in' %}</a>
|
|
128
|
+
{%- else %}
|
|
129
|
+
{% t 'moderation.sign_in_hint' %}
|
|
130
|
+
{%- endif %}
|
|
131
|
+
</p>
|
|
132
|
+
<ol class="moderation__list" data-moderation-list></ol>
|
|
133
|
+
<button type="button" class="post-tool moderation__more" data-moderation-more hidden>{% t 'moderation.more' %}</button>
|
|
134
|
+
</section>
|
|
135
|
+
{%- else -%}
|
|
136
|
+
<section class="moderation" id="moderation">
|
|
137
|
+
<p class="moderation__boundary">{% t 'moderation.unavailable' %}</p>
|
|
138
|
+
</section>
|
|
139
|
+
{%- endif -%}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
---
|
|
3
|
+
optional: []
|
|
4
|
+
---
|
|
5
|
+
"Was this useful?" (assets/js/reactions/widget.js): a small set of
|
|
6
|
+
reactions a reader can give an article without writing a comment, read
|
|
7
|
+
from and sent to the site's dynamic services (docs/dynamic-services.md).
|
|
8
|
+
Renders only when the site has a backend with the reactions feature on,
|
|
9
|
+
`reactions.enabled` is not false, and the page does not say
|
|
10
|
+
`reactions: false`. The set comes from `reactions.types`, the labels from
|
|
11
|
+
`reactions.types.*` in _data/i18n. The counts on the buttons are filled by
|
|
12
|
+
the script from the service's answer, never here.
|
|
13
|
+
{%- endcomment -%}
|
|
14
|
+
{%- assign services = site.dynamic_services -%}
|
|
15
|
+
{%- assign reactions = site.reactions -%}
|
|
16
|
+
{%- if services.base_url and services.base_url != '' and services.features.reactions != false and reactions.enabled != false and page.reactions != false -%}
|
|
17
|
+
{%- assign types = reactions.types -%}
|
|
18
|
+
{%- if types == nil or types.size == 0 -%}
|
|
19
|
+
{%- assign types = "useful,clear,interesting,needs-clarification" | split: "," -%}
|
|
20
|
+
{%- endif -%}
|
|
21
|
+
{%- capture labels_json -%}
|
|
22
|
+
{
|
|
23
|
+
"sending": {{ 'reactions.sending' | t | jsonify }},
|
|
24
|
+
"thanks": {{ 'reactions.thanks' | t | jsonify }},
|
|
25
|
+
"already": {{ 'reactions.already' | t | jsonify }},
|
|
26
|
+
"errors": {
|
|
27
|
+
{%- assign kinds = "disabled,unsupported,version,timeout,network,malformed,unauthorized,forbidden,not_found,conflict,invalid,rate_limited,server,http,aborted,reference" | split: "," -%}
|
|
28
|
+
{%- for kind in kinds -%}
|
|
29
|
+
{%- assign kind_key = "dynamic_services.errors." | append: kind -%}
|
|
30
|
+
{{ kind | jsonify }}: {{ kind_key | t | jsonify }}{% unless forloop.last %},{% endunless %}
|
|
31
|
+
{%- endfor -%}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
{%- endcapture -%}
|
|
35
|
+
<section class="reactions" id="reactions" data-reactions data-reactions-path="{{ page.url }}" data-reactions-counts="{% if reactions.counts == false %}false{% else %}true{% endif %}" data-state="idle" aria-busy="false" aria-labelledby="reactions-prompt">
|
|
36
|
+
<script type="application/json" nonce="{{ page.csp_nonce }}" data-reactions-labels>{{ labels_json | strip }}</script>
|
|
37
|
+
<p class="reactions__prompt" id="reactions-prompt">{% t 'reactions.prompt' %}</p>
|
|
38
|
+
<div class="reactions__choices" role="group" aria-labelledby="reactions-prompt">
|
|
39
|
+
{%- for type in types %}
|
|
40
|
+
{%- assign type_key = "reactions.types." | append: type %}
|
|
41
|
+
<button type="button" class="reactions__choice post-tool" data-reaction="{{ type | escape }}" aria-pressed="false">
|
|
42
|
+
<span class="reactions__label">{{ type_key | t }}</span>
|
|
43
|
+
<span class="reactions__count" data-reaction-count hidden></span>
|
|
44
|
+
</button>
|
|
45
|
+
{%- endfor %}
|
|
46
|
+
</div>
|
|
47
|
+
<p class="reactions__status" data-reactions-status role="status" aria-live="polite" hidden></p>
|
|
48
|
+
</section>
|
|
49
|
+
{%- endif -%}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
---
|
|
3
|
+
optional: []
|
|
4
|
+
---
|
|
5
|
+
The articles a reader saved, with their reading progress and highlights, on
|
|
6
|
+
a page of the site (assets/js/reading-state/ui.js): `{% include
|
|
7
|
+
components/reading-list.html %}` on a page, named in
|
|
8
|
+
theme_options.reading_state.list_url so posts link to it. Filled from this
|
|
9
|
+
browser's storage; nothing comes from a server.
|
|
10
|
+
{%- endcomment -%}
|
|
11
|
+
{%- capture progress_label %}{% t 'post.reading_state.progress' %}{% endcapture -%}
|
|
12
|
+
{%- capture highlights_label %}{% t 'post.reading_state.highlights_count' %}{% endcapture -%}
|
|
13
|
+
{%- capture saved_label %}{% t 'post.reading_state.saved' %}{% endcapture -%}
|
|
14
|
+
{%- capture remove_label %}{% t 'post.reading_state.remove' %}{% endcapture -%}
|
|
15
|
+
{%- capture erase_confirm %}{% t 'post.reading_state.erase_confirm' %}{% endcapture -%}
|
|
16
|
+
{%- capture imported %}{% t 'post.reading_state.imported' %}{% endcapture -%}
|
|
17
|
+
{%- capture import_failed %}{% t 'post.reading_state.import_failed' %}{% endcapture -%}
|
|
18
|
+
{%- capture exported %}{% t 'post.reading_state.exported' %}{% endcapture -%}
|
|
19
|
+
{%- capture erased_all %}{% t 'post.reading_state.erased_all' %}{% endcapture -%}
|
|
20
|
+
<div class="reading-list" data-reading-list data-progress-label="{{ progress_label | strip | escape }}" data-highlights-label="{{ highlights_label | strip | escape }}" data-saved-label="{{ saved_label | strip | escape }}" data-remove-label="{{ remove_label | strip | escape }}">
|
|
21
|
+
<p class="reading-notes__intro">{% t 'post.reading_state.intro' %}</p>
|
|
22
|
+
<p class="reading-notes__unavailable" data-reading-unavailable hidden>{% t 'post.reading_state.unavailable' %}</p>
|
|
23
|
+
<p class="reading-list__empty" data-reading-list-empty>{% t 'post.reading_state.list_empty' %}</p>
|
|
24
|
+
<ul class="reading-list__items" data-reading-list-items></ul>
|
|
25
|
+
<div class="reading-data__actions">
|
|
26
|
+
<button type="button" class="post-tool" data-reading-export data-done="{{ exported | strip | escape }}">{% t 'post.reading_state.export' %}</button>
|
|
27
|
+
<label class="post-tool reading-data__import">{% t 'post.reading_state.import' %}<input type="file" accept="application/json,.json" data-reading-import data-done="{{ imported | strip | escape }}" data-failed="{{ import_failed | strip | escape }}"></label>
|
|
28
|
+
<button type="button" class="post-tool" data-reading-erase-all data-confirm="{{ erase_confirm | strip | escape }}" data-done="{{ erased_all | strip | escape }}">{% t 'post.reading_state.erase_all' %}</button>
|
|
29
|
+
</div>
|
|
30
|
+
<p class="reading-data__status" data-reading-status aria-live="polite"></p>
|
|
31
|
+
</div>
|