datalog-theme 0.8.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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -0
  3. data/CITATION.cff +2 -2
  4. data/README.md +4 -2
  5. data/_data/i18n/en.yml +297 -0
  6. data/_data/i18n/es.yml +297 -0
  7. data/_data/i18n/pt.yml +297 -0
  8. data/_data/js_manifest.json +16 -0
  9. data/_includes/components/author-bio.html +21 -11
  10. data/_includes/components/author-list.html +32 -0
  11. data/_includes/components/citation-tools.html +33 -25
  12. data/_includes/components/comments-thread.html +90 -0
  13. data/_includes/components/contact-form.html +120 -0
  14. data/_includes/components/correction-report.html +82 -0
  15. data/_includes/components/enhanced-toc.html +6 -7
  16. data/_includes/components/license-link.html +13 -0
  17. data/_includes/components/license-notice.html +28 -0
  18. data/_includes/components/moderation-inbox.html +139 -0
  19. data/_includes/components/reactions.html +49 -0
  20. data/_includes/components/reading-list.html +31 -0
  21. data/_includes/components/reading-mode-toggle.html +65 -0
  22. data/_includes/components/reading-state-bookmark.html +27 -0
  23. data/_includes/components/reading-state-panel.html +71 -0
  24. data/_includes/components/reproducibility.html +61 -0
  25. data/_includes/components/responsive-image.html +3 -3
  26. data/_includes/components/revision-history.html +39 -0
  27. data/_includes/components/revision-notice.html +35 -0
  28. data/_includes/components/series-nav.html +64 -0
  29. data/_includes/components/subscribe-form.html +88 -0
  30. data/_includes/components/subscription-manage.html +68 -0
  31. data/_includes/components/webmentions.html +48 -0
  32. data/_includes/csp-meta.html +21 -1
  33. data/_includes/footer.html +3 -0
  34. data/_includes/head.html +51 -40
  35. data/_includes/layouts/default/article.html +4 -6
  36. data/_includes/meta/dynamic-services-config.html +14 -0
  37. data/_includes/meta/math-config.html +9 -5
  38. data/_includes/meta/person-json.html +27 -0
  39. data/_includes/meta/publisher.html +45 -0
  40. data/_includes/meta/schema.html +62 -28
  41. data/_includes/meta/scholarly.html +121 -0
  42. data/_includes/meta/scripts-loader.html +2 -0
  43. data/_includes/meta/webmention-discovery.html +14 -0
  44. data/_includes/scripts.html +57 -0
  45. data/_layouts/dataset.html +4 -3
  46. data/_layouts/default.html +1 -1
  47. data/_layouts/package.html +3 -2
  48. data/_layouts/page.html +15 -0
  49. data/_layouts/post.html +82 -18
  50. data/_layouts/project.html +2 -2
  51. data/_layouts/research.html +20 -8
  52. data/_plugins/authors.rb +133 -0
  53. data/_plugins/config_validator.rb +223 -20
  54. data/_plugins/critical_css_check.rb +42 -0
  55. data/_plugins/i18n.rb +6 -4
  56. data/_plugins/image_optimizer.rb +224 -172
  57. data/_plugins/licenses.rb +135 -0
  58. data/_plugins/math_preprocessor.rb +32 -10
  59. data/_plugins/references.rb +238 -0
  60. data/_plugins/reproducibility.rb +150 -0
  61. data/_plugins/revisions.rb +101 -0
  62. data/_plugins/scholarly.rb +50 -0
  63. data/_plugins/series.rb +104 -0
  64. data/_plugins/statements.rb +87 -0
  65. data/_sass/_base.scss +8 -1
  66. data/_sass/_comments-thread.scss +159 -0
  67. data/_sass/_layout.scss +384 -0
  68. data/_sass/_mathematical.scss +27 -0
  69. data/_sass/_moderation.scss +222 -0
  70. data/_sass/_post-components.scss +4 -2
  71. data/_sass/_print.scss +291 -0
  72. data/_sass/_reactions.scss +89 -0
  73. data/_sass/_reading-state.scss +290 -0
  74. data/_sass/_service-forms.scss +204 -0
  75. data/_sass/_subscriptions.scss +140 -0
  76. data/_sass/_syntax-highlighting.scss +2 -2
  77. data/_sass/_theme.scss +11 -0
  78. data/_sass/_typography.scss +116 -0
  79. data/_sass/_utilities.scss +5 -0
  80. data/_sass/_variables.scss +6 -0
  81. data/_sass/_webmentions.scss +125 -0
  82. data/assets/js/dist/academic.js +1 -1
  83. data/assets/js/dist/analytics-dashboard.js +1 -1
  84. data/assets/js/dist/chunks/chunk-2DYDWUFX.js +1 -0
  85. data/assets/js/dist/chunks/chunk-PATLC23F.js +1 -0
  86. data/assets/js/dist/chunks/chunk-V7734B2G.js +1 -0
  87. data/assets/js/dist/comments.js +2 -0
  88. data/assets/js/dist/contact.js +1 -0
  89. data/assets/js/dist/core.js +1 -1
  90. data/assets/js/dist/corrections.js +1 -0
  91. data/assets/js/dist/loader.js +1 -1
  92. data/assets/js/dist/math.js +1 -1
  93. data/assets/js/dist/moderation.js +1 -0
  94. data/assets/js/dist/notebook.js +1 -1
  95. data/assets/js/dist/reactions.js +1 -0
  96. data/assets/js/dist/reading-state.js +1 -0
  97. data/assets/js/dist/search.js +1 -1
  98. data/assets/js/dist/sources.json +52 -0
  99. data/assets/js/dist/subscriptions.js +1 -0
  100. data/assets/js/dist/webmentions.js +1 -0
  101. data/assets/js/loader.js +34 -0
  102. data/datalog-theme.gemspec +1 -2
  103. data/lib/datalog/cli.rb +9 -1
  104. data/lib/datalog/critical_css.rb +168 -0
  105. data/lib/datalog/plugins/comments.rb +33 -3
  106. data/lib/datalog/theme/installed_files.rb +113 -0
  107. data/lib/datalog/theme/repository_checkout.rb +6 -2
  108. data/lib/datalog/theme/version.rb +5 -1
  109. data/lib/datalog-theme.rb +1 -0
  110. metadata +57 -23
  111. data/assets/js/dist/chunks/chunk-225H5YXE.js +0 -1
@@ -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>
@@ -0,0 +1,65 @@
1
+ {%- comment -%}
2
+ ---
3
+ optional: []
4
+ ---
5
+ The reading-mode control of a post: a button that hides the site's chrome
6
+ and the panels around the article (`body.reading-mode`, styled in
7
+ _sass/_print.scss) and leaves the article with its metadata, its table of
8
+ contents and the way back. Nothing is duplicated: the same article is shown
9
+ with less around it. Escape leaves the mode. `theme_options.reading_mode.enabled:
10
+ false` removes the control; `remember: true` keeps the choice in localStorage
11
+ from one page to the next.
12
+ {%- endcomment -%}
13
+ {%- assign reading_mode = site.theme_options.reading_mode -%}
14
+ {%- unless reading_mode.enabled == false -%}
15
+ {%- capture reading_enter %}{% t 'post.reading_mode.enter' %}{% endcapture -%}
16
+ {%- capture reading_exit %}{% t 'post.reading_mode.exit' %}{% endcapture -%}
17
+ <div class="reading-mode-bar">
18
+ <button type="button" class="post-tool reading-mode-toggle" data-reading-mode-toggle aria-pressed="false" aria-describedby="reading-mode-hint"
19
+ data-label-enter="{{ reading_enter | strip | escape }}" data-label-exit="{{ reading_exit | strip | escape }}"{% if reading_mode.remember %} data-reading-mode-remember{% endif %}>
20
+ <svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
21
+ <path d="M2 4h6a4 4 0 0 1 4 4v12a3 3 0 0 0-3-3H2z"></path>
22
+ <path d="M22 4h-6a4 4 0 0 0-4 4v12a3 3 0 0 1 3-3h7z"></path>
23
+ </svg>
24
+ <span data-reading-mode-label>{{ reading_enter | strip }}</span>
25
+ </button>
26
+ <span class="visually-hidden" id="reading-mode-hint">{% t 'post.reading_mode.hint' %}</span>
27
+ </div>
28
+ <script nonce="{{ page.csp_nonce }}">
29
+ // Reading mode: a class on the body, the button's state and label, Escape to
30
+ // leave, and the choice remembered only when the site asks for it.
31
+ (function () {
32
+ var toggle = document.querySelector('[data-reading-mode-toggle]');
33
+ if (!toggle) return;
34
+ var label = toggle.querySelector('[data-reading-mode-label]');
35
+ var remember = toggle.hasAttribute('data-reading-mode-remember');
36
+ var key = 'datalog-reading-mode';
37
+
38
+ function apply(on) {
39
+ document.body.classList.toggle('reading-mode', on);
40
+ toggle.setAttribute('aria-pressed', on ? 'true' : 'false');
41
+ label.textContent = toggle.getAttribute(on ? 'data-label-exit' : 'data-label-enter');
42
+ if (!remember) return;
43
+ try {
44
+ if (on) localStorage.setItem(key, 'on');
45
+ else localStorage.removeItem(key);
46
+ } catch (error) {}
47
+ }
48
+
49
+ toggle.addEventListener('click', function () {
50
+ apply(!document.body.classList.contains('reading-mode'));
51
+ });
52
+ document.addEventListener('keydown', function (event) {
53
+ if (event.key === 'Escape' && document.body.classList.contains('reading-mode')) {
54
+ apply(false);
55
+ toggle.focus();
56
+ }
57
+ });
58
+ if (remember) {
59
+ try {
60
+ if (localStorage.getItem(key) === 'on') apply(true);
61
+ } catch (error) {}
62
+ }
63
+ })();
64
+ </script>
65
+ {%- endunless -%}
@@ -0,0 +1,27 @@
1
+ {%- comment -%}
2
+ ---
3
+ optional: []
4
+ ---
5
+ The "Save for later" button of a post (assets/js/reading-state/ui.js): a
6
+ bookmark kept in this browser, and a link to the saved articles when the
7
+ site has a page for them (theme_options.reading_state.list_url). Renders
8
+ nothing when reading state or bookmarks are off.
9
+ {%- endcomment -%}
10
+ {%- assign reading_state = site.theme_options.reading_state -%}
11
+ {%- unless reading_state.enabled == false or reading_state.bookmarks == false -%}
12
+ {%- capture label_save %}{% t 'post.reading_state.save' %}{% endcapture -%}
13
+ {%- capture label_saved %}{% t 'post.reading_state.saved' %}{% endcapture -%}
14
+ {%- capture label_unavailable %}{% t 'post.reading_state.unavailable' %}{% endcapture -%}
15
+ <button type="button" class="post-tool bookmark-toggle" data-bookmark-toggle aria-pressed="false"
16
+ data-article="{{ page.url | relative_url }}" data-title="{{ page.title | escape }}"
17
+ data-label-save="{{ label_save | strip | escape }}" data-label-saved="{{ label_saved | strip | escape }}"
18
+ data-unavailable="{{ label_unavailable | strip | escape }}">
19
+ <svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
20
+ <path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
21
+ </svg>
22
+ <span data-bookmark-label>{{ label_save | strip }}</span>
23
+ </button>
24
+ {%- if reading_state.list_url %}
25
+ <a class="post-tool post-tool--link" href="{{ reading_state.list_url | relative_url }}">{% t 'post.reading_state.saved_list' %}</a>
26
+ {%- endif %}
27
+ {%- endunless -%}
@@ -0,0 +1,71 @@
1
+ {%- comment -%}
2
+ ---
3
+ optional: []
4
+ ---
5
+ A post's reading state (assets/js/reading-state/ui.js): the "resume where
6
+ you left off" prompt, the toolbar that appears over a selection, the panel
7
+ of private highlights and notes, and the controls to export, import and
8
+ erase the reader's data. Everything stays in this browser. The markup is
9
+ inert until the reading-state bundle runs; a browser without storage sees
10
+ the message and disabled controls. Renders nothing when reading state is
11
+ off; without progress or highlights, only the data controls remain.
12
+ {%- endcomment -%}
13
+ {%- assign reading_state = site.theme_options.reading_state -%}
14
+ {%- unless reading_state.enabled == false -%}
15
+ {%- assign want_progress = true -%}{%- if reading_state.progress == false -%}{%- assign want_progress = false -%}{%- endif -%}
16
+ {%- assign want_highlights = true -%}{%- if reading_state.highlights == false -%}{%- assign want_highlights = false -%}{%- endif -%}
17
+ {%- capture progress_label %}{% t 'post.reading_state.progress' %}{% endcapture -%}
18
+ {%- capture erase_confirm %}{% t 'post.reading_state.erase_confirm' %}{% endcapture -%}
19
+ {%- capture imported %}{% t 'post.reading_state.imported' %}{% endcapture -%}
20
+ {%- capture import_failed %}{% t 'post.reading_state.import_failed' %}{% endcapture -%}
21
+ {%- capture exported %}{% t 'post.reading_state.exported' %}{% endcapture -%}
22
+ {%- capture erased_article %}{% t 'post.reading_state.erased_article' %}{% endcapture -%}
23
+ {%- capture erased_all %}{% t 'post.reading_state.erased_all' %}{% endcapture -%}
24
+ <div class="reading-state" data-reading-state data-article="{{ page.url | relative_url }}" data-title="{{ page.title | escape }}" data-progress="{{ want_progress }}" data-highlights="{{ want_highlights }}">
25
+ {%- if want_progress %}
26
+ <div class="reading-resume" data-reading-resume data-progress-label="{{ progress_label | strip | escape }}" role="region" aria-labelledby="reading-resume-heading" hidden>
27
+ <p class="reading-resume__text"><strong id="reading-resume-heading">{% t 'post.reading_state.resume' %}</strong> <span data-reading-resume-detail></span></p>
28
+ <div class="reading-resume__actions">
29
+ <button type="button" class="post-tool" data-reading-resume-go>{% t 'post.reading_state.resume_button' %}</button>
30
+ <button type="button" class="post-tool" data-reading-resume-dismiss>{% t 'post.reading_state.dismiss' %}</button>
31
+ </div>
32
+ </div>
33
+ {%- endif %}
34
+ {%- if want_highlights %}
35
+ <div class="reading-toolbar" data-reading-toolbar role="toolbar" aria-label="{% t 'post.reading_state.highlight' %}" hidden>
36
+ <button type="button" data-reading-highlight>{% t 'post.reading_state.highlight' %}</button>
37
+ <button type="button" data-reading-note>{% t 'post.reading_state.note' %}</button>
38
+ </div>
39
+ {%- endif %}
40
+ <section class="reading-notes" aria-labelledby="reading-notes-heading" data-reading-panel>
41
+ <h2 id="reading-notes-heading" class="reading-notes__heading">{% if want_highlights %}{% t 'post.reading_state.highlights_heading' %}{% else %}{% t 'post.reading_state.data_heading' %}{% endif %}</h2>
42
+ <p class="reading-notes__intro">{% t 'post.reading_state.intro' %}{% if want_highlights %} {% t 'post.reading_state.shortcuts' %}{% endif %}</p>
43
+ <p class="reading-notes__unavailable" data-reading-unavailable hidden>{% t 'post.reading_state.unavailable' %}</p>
44
+ {%- if want_highlights %}
45
+ <p class="reading-notes__empty" data-reading-empty>{% t 'post.reading_state.empty' %}</p>
46
+ <ul class="reading-notes__list" data-reading-list-items></ul>
47
+ <template data-reading-item-template>
48
+ <li class="reading-note" data-annotation>
49
+ <blockquote class="reading-note__quote" data-annotation-quote></blockquote>
50
+ <p class="reading-note__meta"><span data-annotation-section></span> <span class="reading-note__lost" data-annotation-lost hidden>{% t 'post.reading_state.lost' %}</span></p>
51
+ <label class="reading-note__label"><span>{% t 'post.reading_state.note_label' %}</span><textarea rows="2" data-annotation-note></textarea></label>
52
+ <div class="reading-note__actions">
53
+ <button type="button" class="post-tool" data-annotation-go>{% t 'post.reading_state.go_to' %}</button>
54
+ <button type="button" class="post-tool" data-annotation-remove>{% t 'post.reading_state.remove' %}</button>
55
+ </div>
56
+ </li>
57
+ </template>
58
+ {%- endif %}
59
+ <details class="reading-data">
60
+ <summary>{% t 'post.reading_state.data_heading' %}</summary>
61
+ <div class="reading-data__actions">
62
+ <button type="button" class="post-tool" data-reading-export data-done="{{ exported | strip | escape }}">{% t 'post.reading_state.export' %}</button>
63
+ <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>
64
+ <button type="button" class="post-tool" data-reading-erase-article data-done="{{ erased_article | strip | escape }}">{% t 'post.reading_state.erase_article' %}</button>
65
+ <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>
66
+ </div>
67
+ <p class="reading-data__status" data-reading-status aria-live="polite"></p>
68
+ </details>
69
+ </section>
70
+ </div>
71
+ {%- endunless -%}
@@ -0,0 +1,61 @@
1
+ {%- comment -%}
2
+ ---
3
+ required:
4
+ - page
5
+ ---
6
+ "Reproduce this analysis": the code, data, notebook, environment and results
7
+ behind an article (`reproducibility` in front matter, read by the
8
+ page_reproducibility filter, _plugins/reproducibility.rb). Shows the
9
+ artifacts given, each with its identifiers, and renders nothing without one.
10
+ Site paths already carry the baseurl.
11
+ {%- endcomment -%}
12
+ {%- assign reproduce = include.page | page_reproducibility -%}
13
+ {%- if reproduce -%}
14
+ <section class="reproduce" aria-labelledby="reproduce-heading">
15
+ <h2 id="reproduce-heading" class="reproduce__heading">{% t 'post.reproduce.heading' %}</h2>
16
+ <p class="reproduce__intro">{% t 'post.reproduce.intro' %}</p>
17
+ <dl class="reproduce__list">
18
+ {%- for artifact in reproduce.artifacts %}
19
+ {%- assign kind_key = 'post.reproduce.' | append: artifact.kind %}
20
+ <div class="reproduce__item reproduce__item--{{ artifact.kind }}">
21
+ <dt>{{ kind_key | t }}</dt>
22
+ <dd>
23
+ {%- if artifact.url %}
24
+ <a class="reproduce__link" href="{{ artifact.url | escape }}"{% if artifact.external %} rel="external noopener"{% endif %}>{{ artifact.label | escape }}</a>
25
+ {%- endif %}
26
+ {%- if artifact.ref %}
27
+ {%- capture ref_text -%}
28
+ {%- if artifact.ref_url -%}
29
+ <a href="{{ artifact.ref_url | escape }}" rel="external noopener"><code>{{ artifact.ref | escape }}</code></a>
30
+ {%- else -%}
31
+ <code>{{ artifact.ref | escape }}</code>
32
+ {%- endif -%}
33
+ {%- endcapture %}
34
+ <span class="reproduce__detail">{% t 'post.reproduce.ref' ref: ref_text %}</span>
35
+ {%- endif %}
36
+ {%- if artifact.version %}
37
+ {%- assign version_text = artifact.version | escape %}
38
+ <span class="reproduce__detail">{% t 'post.reproduce.version' version: version_text %}</span>
39
+ {%- endif %}
40
+ {%- if artifact.doi %}
41
+ <span class="reproduce__detail">DOI {{ artifact.doi | escape }}</span>
42
+ {%- endif %}
43
+ {%- if artifact.file %}
44
+ {%- if artifact.file_url %}
45
+ <a class="reproduce__link" href="{{ artifact.file_url | escape }}"{% if artifact.file_url contains '://' %} rel="external noopener"{% endif %}><code>{{ artifact.file | escape }}</code></a>
46
+ {%- else %}
47
+ <code>{{ artifact.file | escape }}</code>
48
+ {%- endif %}
49
+ {%- endif %}
50
+ {%- if artifact.container %}
51
+ <span class="reproduce__detail">{% t 'post.reproduce.container' %} <code>{{ artifact.container | escape }}</code></span>
52
+ {%- endif %}
53
+ {%- if artifact.archive %}
54
+ <a class="reproduce__link" href="{{ artifact.archive | escape }}" rel="external noopener">{% t 'post.reproduce.archive' %}: {{ artifact.archive_label | escape }}</a>
55
+ {%- endif %}
56
+ </dd>
57
+ </div>
58
+ {%- endfor %}
59
+ </dl>
60
+ </section>
61
+ {%- endif -%}
@@ -2,7 +2,7 @@
2
2
  {% assign alt_text = include.alt | to_s %}
3
3
  {% if image_src == '' or alt_text == '' %}
4
4
  {% comment %}Missing required parameters{% endcomment %}
5
- {% assign fallback_loading = include.loading | default: site.config.datalog_image_config.lazy_loading | default: 'lazy' %}
5
+ {% assign fallback_loading = include.loading | default: site.datalog_image_config.lazy_loading | default: 'lazy' %}
6
6
  <img src="{{ image_src }}" alt="{{ alt_text }}" loading="{{ fallback_loading }}" decoding="async" />
7
7
  {% else %}
8
8
  {% assign baseurl = site.baseurl | default: '' %}
@@ -22,8 +22,8 @@
22
22
  {% assign normalized_src = normalized_src | replace: '//', '/' %}
23
23
  {% assign responsive_entry = manifest[normalized_src] %}
24
24
  {% endif %}
25
- {% assign requested_sizes = include.sizes | default: site.config.datalog_image_config.default_sizes | default: '100vw' %}
26
- {% assign loading_mode = include.loading | default: site.config.datalog_image_config.lazy_loading | default: 'lazy' %}
25
+ {% assign requested_sizes = include.sizes | default: site.datalog_image_config.default_sizes | default: '100vw' %}
26
+ {% assign loading_mode = include.loading | default: site.datalog_image_config.lazy_loading | default: 'lazy' %}
27
27
  {% if responsive_entry %}
28
28
  <picture>
29
29
  {% for source in responsive_entry.sources %}
@@ -0,0 +1,39 @@
1
+ {%- comment -%}
2
+ ---
3
+ required:
4
+ - page
5
+ optional:
6
+ - date_format
7
+ ---
8
+ The revision history of an article (`revisions`, sorted newest first by
9
+ _plugins/revisions.rb), after its body, ending with the publication date.
10
+ Renders nothing for a page without revisions.
11
+ {%- endcomment -%}
12
+ {%- assign revisions = include.page.revisions -%}
13
+ {%- if revisions and revisions.size > 0 -%}
14
+ <section class="revision-history" id="revision-history" aria-labelledby="revision-history-heading">
15
+ <h2 id="revision-history-heading">{% t 'post.revisions.heading' %}</h2>
16
+ <ul class="revision-history__list">
17
+ {%- for revision in revisions -%}
18
+ {%- assign type_key = 'post.revisions.types.' | append: revision.type -%}
19
+ <li class="revision-history__item revision-history__item--{{ revision.type }}">
20
+ <time class="revision-history__date" datetime="{{ revision.date | date: '%Y-%m-%d' }}">{% include helpers/date-format.html date=revision.date format=include.date_format %}</time>
21
+ <span class="revision-history__type">{{ type_key | t }}</span>
22
+ <span class="revision-history__summary">{{ revision.summary | escape }}
23
+ {%- if revision.details_url -%}
24
+ {%- assign details_url = revision.details_url -%}
25
+ {%- unless details_url contains '://' -%}{%- assign details_url = details_url | relative_url -%}{%- endunless %}
26
+ <a class="revision-history__details" href="{{ details_url | escape }}">{% t 'post.revisions.details' %}</a>
27
+ {%- endif -%}
28
+ </span>
29
+ </li>
30
+ {%- endfor -%}
31
+ {%- if include.page.date -%}
32
+ <li class="revision-history__item revision-history__item--published">
33
+ <time class="revision-history__date" datetime="{{ include.page.date | date: '%Y-%m-%d' }}">{% include helpers/date-format.html date=include.page.date format=include.date_format %}</time>
34
+ <span class="revision-history__type">{% t 'post.revisions.types.published' %}</span>
35
+ </li>
36
+ {%- endif -%}
37
+ </ul>
38
+ </section>
39
+ {%- endif -%}
@@ -0,0 +1,35 @@
1
+ {%- comment -%}
2
+ ---
3
+ required:
4
+ - page
5
+ optional:
6
+ - date_format
7
+ ---
8
+ The newest correction or update of an article (`revision_notice`, set from
9
+ `revisions` by _plugins/revisions.rb), announced under the metadata with a
10
+ link to the full history. Renders nothing without one, or with
11
+ `revision_notice: false` in front matter.
12
+ {%- endcomment -%}
13
+ {%- assign revision = include.page.revision_notice -%}
14
+ {%- if revision -%}
15
+ {%- capture revision_date -%}{% include helpers/date-format.html date=revision.date format=include.date_format %}{%- endcapture -%}
16
+ {%- assign revision_date = revision_date | strip -%}
17
+ <aside class="revision-notice revision-notice--{{ revision.type }}" aria-labelledby="revision-notice-lead">
18
+ <p class="revision-notice__text">
19
+ <strong id="revision-notice-lead" class="revision-notice__lead">
20
+ {%- if revision.type == 'correction' -%}
21
+ {% t 'post.revisions.corrected_on' date: revision_date %}
22
+ {%- else -%}
23
+ {% t 'post.revisions.updated_on' date: revision_date %}
24
+ {%- endif -%}
25
+ </strong>
26
+ {{ revision.summary | escape }}
27
+ {%- if revision.details_url -%}
28
+ {%- assign details_url = revision.details_url -%}
29
+ {%- unless details_url contains '://' -%}{%- assign details_url = details_url | relative_url -%}{%- endunless %}
30
+ <a class="revision-notice__details" href="{{ details_url | escape }}">{% t 'post.revisions.details' %}</a>
31
+ {%- endif %}
32
+ <a class="revision-notice__history" href="#revision-history">{% t 'post.revisions.history_link' %}</a>
33
+ </p>
34
+ </aside>
35
+ {%- endif -%}
@@ -0,0 +1,64 @@
1
+ {%- comment -%}
2
+ ---
3
+ required:
4
+ - page
5
+ optional:
6
+ - compact
7
+ ---
8
+ Navigation within an article series (`series` in front matter, resolved by
9
+ _plugins/series.rb): the series title, "Part k of n", every part in order
10
+ with the current one marked, and the previous and next parts. With
11
+ `compact: true`, the previous and next parts alone, for the end of the
12
+ article. Renders nothing for a page that is not part of a series.
13
+ {%- endcomment -%}
14
+ {%- assign series = include.page.series -%}
15
+ {%- if series.id -%}
16
+ {%- capture part_of -%}{% t 'post.series.part_of' part: series.position total: series.count %}{%- endcapture -%}
17
+ {%- capture nav_label -%}{% t 'post.series.nav_label' title: series.title %}{%- endcapture -%}
18
+ {%- capture series_links -%}
19
+ {%- if series.previous or series.next %}
20
+ <div class="series-nav__links">
21
+ {%- if series.previous %}
22
+ <a class="series-nav__link series-nav__link--previous" rel="prev" href="{{ series.previous.url | relative_url }}">
23
+ <span class="series-nav__link-label">{% t 'post.series.previous' %}</span>
24
+ <span class="series-nav__link-title">{{ series.previous.title | escape }}</span>
25
+ </a>
26
+ {%- endif %}
27
+ {%- if series.next %}
28
+ <a class="series-nav__link series-nav__link--next" rel="next" href="{{ series.next.url | relative_url }}">
29
+ <span class="series-nav__link-label">{% t 'post.series.next' %}</span>
30
+ <span class="series-nav__link-title">{{ series.next.title | escape }}</span>
31
+ </a>
32
+ {%- endif %}
33
+ </div>
34
+ {%- endif %}
35
+ {%- endcapture -%}
36
+ {%- if include.compact -%}
37
+ {%- if series.previous or series.next -%}
38
+ <nav class="series-nav series-nav--compact" aria-label="{{ nav_label | strip | escape }}">
39
+ <p class="series-nav__context">{{ series.title | escape }} · {{ part_of | strip }}</p>
40
+ {{- series_links }}
41
+ </nav>
42
+ {%- endif -%}
43
+ {%- else -%}
44
+ <nav class="series-nav" aria-labelledby="series-nav-heading">
45
+ <p class="series-nav__heading" id="series-nav-heading">
46
+ <span class="series-nav__label">{% t 'post.series.label' %}</span>
47
+ <span class="series-nav__title">{{ series.title | escape }}</span>
48
+ <span class="series-nav__position">{{ part_of | strip }}</span>
49
+ </p>
50
+ {%- if series.description %}
51
+ <p class="series-nav__description">{{ series.description | escape }}</p>
52
+ {%- endif %}
53
+ <progress class="series-nav__progress" value="{{ series.position }}" max="{{ series.count }}" aria-hidden="true"></progress>
54
+ <ol class="series-nav__list">
55
+ {%- for part in series.parts %}
56
+ <li class="series-nav__part{% if part.current %} series-nav__part--current{% endif %}">
57
+ <a href="{{ part.url | relative_url }}"{% if part.current %} aria-current="page"{% endif %}>{{ part.title | escape }}</a>
58
+ </li>
59
+ {%- endfor %}
60
+ </ol>
61
+ {{- series_links }}
62
+ </nav>
63
+ {%- endif -%}
64
+ {%- endif -%}