datalog-theme 0.8.0 → 0.9.1

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -0
  3. data/CITATION.cff +2 -2
  4. data/README.md +4 -2
  5. data/_data/i18n/en.yml +321 -0
  6. data/_data/i18n/es.yml +321 -0
  7. data/_data/i18n/pt.yml +321 -0
  8. data/_data/js_manifest.json +16 -0
  9. data/_includes/components/academic-dashboard.html +4 -4
  10. data/_includes/components/author-bio.html +21 -11
  11. data/_includes/components/author-list.html +32 -0
  12. data/_includes/components/breadcrumbs.html +6 -4
  13. data/_includes/components/citation-tools.html +18 -58
  14. data/_includes/components/comments-thread.html +90 -0
  15. data/_includes/components/contact-form.html +120 -0
  16. data/_includes/components/correction-report.html +82 -0
  17. data/_includes/components/enhanced-toc.html +6 -7
  18. data/_includes/components/hero.html +1 -1
  19. data/_includes/components/license-link.html +13 -0
  20. data/_includes/components/license-notice.html +28 -0
  21. data/_includes/components/moderation-inbox.html +139 -0
  22. data/_includes/components/post-hero.html +2 -2
  23. data/_includes/components/reactions.html +49 -0
  24. data/_includes/components/reading-list.html +34 -0
  25. data/_includes/components/reading-mode-toggle.html +67 -0
  26. data/_includes/components/reading-state-bookmark.html +27 -0
  27. data/_includes/components/reading-state-panel.html +62 -0
  28. data/_includes/components/reading-state-resume.html +17 -0
  29. data/_includes/components/reproducibility.html +61 -0
  30. data/_includes/components/responsive-image.html +3 -3
  31. data/_includes/components/revision-history.html +39 -0
  32. data/_includes/components/revision-notice.html +35 -0
  33. data/_includes/components/series-nav.html +64 -0
  34. data/_includes/components/subscribe-form.html +88 -0
  35. data/_includes/components/subscription-manage.html +69 -0
  36. data/_includes/components/visualization-card.html +1 -1
  37. data/_includes/components/webmentions.html +48 -0
  38. data/_includes/csp-meta.html +21 -1
  39. data/_includes/footer/nav-column.html +1 -1
  40. data/_includes/footer.html +5 -2
  41. data/_includes/head.html +64 -52
  42. data/_includes/header/navigation.html +2 -2
  43. data/_includes/header.html +1 -1
  44. data/_includes/layouts/default/article.html +6 -8
  45. data/_includes/meta/dynamic-services-config.html +14 -0
  46. data/_includes/meta/math-config.html +12 -5
  47. data/_includes/meta/person-json.html +27 -0
  48. data/_includes/meta/publisher.html +45 -0
  49. data/_includes/meta/schema.html +84 -48
  50. data/_includes/meta/scholarly.html +122 -0
  51. data/_includes/meta/scripts-loader.html +2 -0
  52. data/_includes/meta/webmention-discovery.html +14 -0
  53. data/_includes/post/related-posts.html +1 -1
  54. data/_includes/scripts.html +57 -0
  55. data/_layouts/dataset.html +5 -4
  56. data/_layouts/default.html +1 -1
  57. data/_layouts/home.html +2 -2
  58. data/_layouts/notebook.html +1 -1
  59. data/_layouts/package.html +8 -7
  60. data/_layouts/page.html +15 -0
  61. data/_layouts/portfolio.html +1 -1
  62. data/_layouts/post.html +94 -25
  63. data/_layouts/project.html +5 -5
  64. data/_layouts/research.html +27 -75
  65. data/_plugins/authors.rb +151 -0
  66. data/_plugins/citation_exports.rb +93 -0
  67. data/_plugins/config_validator.rb +223 -20
  68. data/_plugins/critical_css_check.rb +42 -0
  69. data/_plugins/i18n.rb +6 -4
  70. data/_plugins/image_optimizer.rb +240 -186
  71. data/_plugins/licenses.rb +147 -0
  72. data/_plugins/math_preprocessor.rb +55 -11
  73. data/_plugins/references.rb +260 -0
  74. data/_plugins/reproducibility.rb +167 -0
  75. data/_plugins/responsive_content.rb +40 -0
  76. data/_plugins/revisions.rb +102 -0
  77. data/_plugins/scholarly.rb +55 -0
  78. data/_plugins/series.rb +104 -0
  79. data/_plugins/statements.rb +87 -0
  80. data/_sass/_base.scss +13 -1
  81. data/_sass/_comments-thread.scss +159 -0
  82. data/_sass/_layout.scss +402 -1
  83. data/_sass/_mathematical.scss +38 -1
  84. data/_sass/_moderation.scss +222 -0
  85. data/_sass/_post-components.scss +4 -2
  86. data/_sass/_print.scss +299 -0
  87. data/_sass/_reactions.scss +89 -0
  88. data/_sass/_reading-state.scss +290 -0
  89. data/_sass/_service-forms.scss +204 -0
  90. data/_sass/_subscriptions.scss +140 -0
  91. data/_sass/_syntax-highlighting.scss +3 -3
  92. data/_sass/_theme.scss +11 -0
  93. data/_sass/_typography.scss +116 -0
  94. data/_sass/_utilities.scss +5 -0
  95. data/_sass/_variables.scss +6 -0
  96. data/_sass/_webmentions.scss +125 -0
  97. data/assets/js/dist/academic.js +1 -1
  98. data/assets/js/dist/analytics-dashboard.js +1 -1
  99. data/assets/js/dist/chunks/chunk-PATLC23F.js +1 -0
  100. data/assets/js/dist/chunks/chunk-VZ5WKQVA.js +1 -0
  101. data/assets/js/dist/chunks/chunk-WIRUK3OZ.js +1 -0
  102. data/assets/js/dist/comments.js +2 -0
  103. data/assets/js/dist/contact.js +1 -0
  104. data/assets/js/dist/core.js +1 -1
  105. data/assets/js/dist/corrections.js +1 -0
  106. data/assets/js/dist/loader.js +1 -1
  107. data/assets/js/dist/math.js +1 -1
  108. data/assets/js/dist/moderation.js +1 -0
  109. data/assets/js/dist/notebook.js +1 -1
  110. data/assets/js/dist/reactions.js +1 -0
  111. data/assets/js/dist/reading-state.js +1 -0
  112. data/assets/js/dist/search.js +1 -1
  113. data/assets/js/dist/sources.json +52 -0
  114. data/assets/js/dist/subscriptions.js +1 -0
  115. data/assets/js/dist/visualizations.js +2 -2
  116. data/assets/js/dist/webmentions.js +1 -0
  117. data/assets/js/loader.js +34 -0
  118. data/datalog-theme.gemspec +1 -2
  119. data/lib/datalog/cli.rb +9 -1
  120. data/lib/datalog/critical_css.rb +168 -0
  121. data/lib/datalog/plugins/comments.rb +33 -3
  122. data/lib/datalog/theme/installed_files.rb +113 -0
  123. data/lib/datalog/theme/repository_checkout.rb +6 -2
  124. data/lib/datalog/theme/version.rb +5 -1
  125. data/lib/datalog-theme.rb +1 -0
  126. metadata +60 -23
  127. data/assets/js/dist/chunks/chunk-225H5YXE.js +0 -1
@@ -2,6 +2,14 @@
2
2
  "core": "/assets/js/dist/core.js",
3
3
  "loader": "/assets/js/dist/loader.js",
4
4
  "features": {
5
+ "reading-state": "/assets/js/dist/reading-state.js",
6
+ "corrections": "/assets/js/dist/corrections.js",
7
+ "contact": "/assets/js/dist/contact.js",
8
+ "comments": "/assets/js/dist/comments.js",
9
+ "reactions": "/assets/js/dist/reactions.js",
10
+ "webmentions": "/assets/js/dist/webmentions.js",
11
+ "subscriptions": "/assets/js/dist/subscriptions.js",
12
+ "moderation": "/assets/js/dist/moderation.js",
5
13
  "search": "/assets/js/dist/search.js",
6
14
  "math": "/assets/js/dist/math.js",
7
15
  "visualizations": "/assets/js/dist/visualizations.js",
@@ -10,6 +18,14 @@
10
18
  "analytics-dashboard": "/assets/js/dist/analytics-dashboard.js"
11
19
  },
12
20
  "entrypoints": {
21
+ "reading-state": "/assets/js/dist/reading-state.js",
22
+ "corrections": "/assets/js/dist/corrections.js",
23
+ "contact": "/assets/js/dist/contact.js",
24
+ "comments": "/assets/js/dist/comments.js",
25
+ "reactions": "/assets/js/dist/reactions.js",
26
+ "webmentions": "/assets/js/dist/webmentions.js",
27
+ "subscriptions": "/assets/js/dist/subscriptions.js",
28
+ "moderation": "/assets/js/dist/moderation.js",
13
29
  "core": "/assets/js/dist/core.js",
14
30
  "loader": "/assets/js/dist/loader.js",
15
31
  "search": "/assets/js/dist/search.js",
@@ -155,7 +155,7 @@
155
155
  <li class="submission-card" data-submission-status="{{ submission.status | slugify }}">
156
156
  <article>
157
157
  <header>
158
- <h3>{{ submission.title }}</h3>
158
+ <h3>{{ submission.title | escape }}</h3>
159
159
  <p class="submission-venue">{{ submission.venue }} · <span class="submission-type">{{ submission.type | capitalize }}</span></p>
160
160
  </header>
161
161
  <dl class="submission-meta">
@@ -182,7 +182,7 @@
182
182
  <ul class="workflow-list">
183
183
  {% for review in academic.workflow.peer_review %}
184
184
  <li>
185
- <strong>{{ review.title }}</strong>
185
+ <strong>{{ review.title | escape }}</strong>
186
186
  <p>{{ review.journal }}</p>
187
187
  <p>{% t 'academic_dashboard.workflow.peer_review.status_label' %} <span class="status-badge">{{ review.status }}</span></p>
188
188
  {% assign assigned_label = review.assigned_date | localize_date: date_format %}
@@ -255,7 +255,7 @@
255
255
  <li>
256
256
  <article>
257
257
  <header>
258
- <h3>{{ grant.title }}</h3>
258
+ <h3>{{ grant.title | escape }}</h3>
259
259
  <p>{{ grant.agency }} · {{ grant.role }}</p>
260
260
  </header>
261
261
  <dl class="funding-meta">
@@ -279,7 +279,7 @@
279
279
  <li>
280
280
  <article>
281
281
  <header>
282
- <h3>{{ opportunity.title }}</h3>
282
+ <h3>{{ opportunity.title | escape }}</h3>
283
283
  {% if opportunity.tags %}<p class="opportunity-tags">{{ opportunity.tags | array_to_sentence_string }}</p>{% endif %}
284
284
  </header>
285
285
  <p>{{ opportunity.description }}</p>
@@ -1,23 +1,30 @@
1
1
  {%- comment -%}
2
2
  Author bio card with social links and credentials
3
3
  Parameters:
4
- - author: Author name (default: page.author or site.author.name)
4
+ - person: An author record from `page | page_authors` (default: the page's first author)
5
+ - author: An author's name or _data/authors.yml key, when no person is given
5
6
  - show_avatar: Show author avatar (default: true)
6
7
  - show_social: Show social links (default: true)
7
- - compact: Compact layout (default: false)
8
+ - compact: Avatar, name, affiliation and links only (default: false)
8
9
  {%- endcomment -%}
9
10
 
10
- {%- assign author_name = include.author | default: page.author | default: site.author.name -%}
11
11
  {%- assign show_avatar = include.show_avatar | default: true -%}
12
12
  {%- assign show_social = include.show_social | default: true -%}
13
13
  {%- assign compact = include.compact | default: false -%}
14
14
 
15
- {%- comment -%} Get author details from site.data.authors or site.author {%- endcomment -%}
16
- {%- if site.data.authors and site.data.authors[author_name] -%}
17
- {%- assign author_data = site.data.authors[author_name] -%}
15
+ {%- if include.person -%}
16
+ {%- assign author_data = include.person -%}
17
+ {%- elsif include.author -%}
18
+ {%- if site.data.authors and site.data.authors[include.author] -%}
19
+ {%- assign author_data = site.data.authors[include.author] -%}
20
+ {%- else -%}
21
+ {%- assign author_data = site.author -%}
22
+ {%- endif -%}
18
23
  {%- else -%}
19
- {%- assign author_data = site.author -%}
24
+ {%- assign bio_authors = page | page_authors -%}
25
+ {%- assign author_data = bio_authors | first -%}
20
26
  {%- endif -%}
27
+ {%- assign author_name = author_data.name | default: include.author | default: site.author.name | escape -%}
21
28
 
22
29
  <aside class="author-bio{% if compact %} author-bio--compact{% endif %}" itemscope itemtype="https://schema.org/Person">
23
30
  <div class="author-bio__inner">
@@ -33,7 +40,10 @@ Parameters:
33
40
  height="80">
34
41
  {%- else -%}
35
42
  {%- comment -%} Fallback avatar with initials {%- endcomment -%}
36
- {%- assign initials = author_name | split: ' ' | map: 'first' | slice: 0, 2 | join: '' | upcase -%}
43
+ {%- comment -%} `map: 'first'` on a list of words returned nothing, so the placeholder showed no initials. {%- endcomment -%}
44
+ {%- assign name_words = author_data.name | default: author_name | split: ' ' -%}
45
+ {%- capture initials -%}{%- for word in name_words limit: 2 -%}{{ word | slice: 0 }}{%- endfor -%}{%- endcapture -%}
46
+ {%- assign initials = initials | upcase | escape -%}
37
47
  <div class="author-bio__avatar-placeholder" role="img" aria-label="{{ author_name }}">
38
48
  <span aria-hidden="true">{{ initials }}</span>
39
49
  </div>
@@ -46,17 +56,17 @@ Parameters:
46
56
 
47
57
  {%- if author_data.affiliation -%}
48
58
  <p class="author-bio__affiliation" itemprop="affiliation">
49
- {{ author_data.affiliation }}
59
+ {{ author_data.affiliation | escape }}
50
60
  </p>
51
61
  {%- endif -%}
52
62
 
53
- {%- if author_data.biography or author_data.bio -%}
63
+ {%- if compact != true and author_data.biography or compact != true and author_data.bio -%}
54
64
  <p class="author-bio__description" itemprop="description">
55
65
  {{ author_data.biography | default: author_data.bio }}
56
66
  </p>
57
67
  {%- endif -%}
58
68
 
59
- {%- if author_data.research_areas and author_data.research_areas.size > 0 -%}
69
+ {%- if compact != true and author_data.research_areas and author_data.research_areas.size > 0 -%}
60
70
  <div class="author-bio__research">
61
71
  <strong class="author-bio__research-label">Research interests:</strong>
62
72
  <span class="author-bio__research-list">{{ author_data.research_areas | join: ', ' }}</span>
@@ -0,0 +1,32 @@
1
+ {%- comment -%}
2
+ ---
3
+ required:
4
+ - people
5
+ optional:
6
+ - property
7
+ - show_affiliation
8
+ ---
9
+ Lists the people who wrote or contributed to a page as schema.org Person
10
+ items: each name, linked to the person's url when there is one, with their
11
+ role and affiliation. `people` comes from `page | page_authors` or
12
+ `page | page_contributors`; `property` is the microdata property, author
13
+ (the default) or contributor.
14
+ {%- endcomment -%}
15
+ {%- assign list_property = include.property | default: 'author' -%}
16
+ <ul class="author-list">
17
+ {%- for person in include.people -%}
18
+ <li class="author-list__person" itemprop="{{ list_property }}" itemscope itemtype="https://schema.org/{% if person.type == 'Organization' %}Organization{% else %}Person{% endif %}">
19
+ {%- if person.url -%}
20
+ {%- assign person_href = person.url -%}
21
+ {%- unless person_href contains '://' -%}{%- assign person_href = person_href | relative_url -%}{%- endunless -%}
22
+ <a href="{{ person_href | escape }}" itemprop="url"><span itemprop="name">{{ person.name | escape }}</span></a>
23
+ {%- else -%}
24
+ <span itemprop="name">{{ person.name | escape }}</span>
25
+ {%- endif -%}
26
+ {%- if person.role %} <span class="author-list__role">({{ person.role | escape }})</span>{%- endif -%}
27
+ {%- if person.affiliation and include.show_affiliation != false -%}
28
+ <span class="post-author-affiliation" itemprop="affiliation">{{ person.affiliation | escape }}</span>
29
+ {%- endif -%}
30
+ </li>
31
+ {%- endfor -%}
32
+ </ul>
@@ -64,11 +64,13 @@ Parameters:
64
64
  {%- if page.categories and page.categories.size > 0 -%}
65
65
  {%- assign main_category = page.categories | first -%}
66
66
  {%- assign category_slug = main_category | slugify -%}
67
+ {%- assign category_archive_path = site.category_archive.path | default: '/categories/' -%}
68
+ {%- assign category_archive_page = site.pages | where: 'url', category_archive_path | first -%}
67
69
  {%- unless position -%}{%- assign position = 2 -%}{%- endunless -%}
68
70
  <li class="breadcrumbs__item" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
69
- <a href="{{ site.category_archive.path | default: '/categories/' | append: '#' | append: category_slug | relative_url }}" class="breadcrumbs__link" itemprop="item">
70
- <span itemprop="name">{{ main_category }}</span>
71
- </a>
71
+ {% if category_archive_page %}<a href="{{ category_archive_path | append: '#' | append: category_slug | relative_url }}" class="breadcrumbs__link" itemprop="item">{% endif %}
72
+ <span itemprop="name">{{ main_category | escape }}</span>
73
+ {% if category_archive_page %}</a>{% endif %}
72
74
  <meta itemprop="position" content="{{ position }}" />
73
75
  <span class="breadcrumbs__separator" aria-hidden="true">{{ separator }}</span>
74
76
  </li>
@@ -80,7 +82,7 @@ Parameters:
80
82
  {%- unless position -%}{%- assign position = 2 -%}{%- endunless -%}
81
83
  <li class="breadcrumbs__item breadcrumbs__item--current" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem" aria-current="page">
82
84
  <span class="breadcrumbs__current" itemprop="item">
83
- <span itemprop="name">{{ page.title | truncate: 50 }}</span>
85
+ <span itemprop="name">{{ page.title | truncate: 50 | escape }}</span>
84
86
  </span>
85
87
  <meta itemprop="position" content="{{ position }}" />
86
88
  </li>
@@ -2,65 +2,25 @@
2
2
  {% assign citation_url = include.url | default: page.url | default: '/' | absolute_url %}
3
3
  {% assign citation_date = include.date | default: page.date %}
4
4
  {% assign citation_year = citation_date | date: '%Y' %}
5
- {% assign authors_source = include.authors | default: page.authors | default: page.author | default: site.author.name %}
6
- {% assign authors_array = authors_source | arrayify %}
7
- {% assign publisher = include.publisher | default: site.author.affiliation | default: site.title %}
5
+ {%- comment -%} `authors`: records from `page | page_authors`, or names. Without it, the page's authors. {%- endcomment -%}
6
+ {% if include.authors %}
7
+ {% assign authors_array = include.authors %}
8
+ {% else %}
9
+ {% assign authors_array = page | page_authors %}
10
+ {% endif %}
11
+ {%- comment -%} A configured publisher, else the site's name. The author's affiliation is not the publisher. {%- endcomment -%}
12
+ {% assign publisher = include.publisher | default: site.publisher.name | default: site.title %}
8
13
  {% assign doi = include.doi | default: page.doi %}
9
14
  {% assign citation_type = include.citation_type | default: include.type | default: 'article' %}
10
15
  {% assign citation_key = include.citation_key | default: page.citation_key | default: page.slug | default: citation_title | slugify %}
11
- {% capture author_line %}
12
- {% for author in authors_array %}
13
- {% if author.name %}
14
- {{ author.name | strip }}{% unless forloop.last %} and {% endunless %}
15
- {% else %}
16
- {{ author | strip }}{% unless forloop.last %} and {% endunless %}
17
- {% endif %}
18
- {% endfor %}
19
- {% endcapture %}
20
- {% capture ris_authors %}
21
- {% for author in authors_array %}
22
- {% assign author_name = author.name | default: author %}
23
- AU - {{ author_name | strip }}
24
- {% endfor %}
25
- {% endcapture %}
26
- {% capture bibtex_entry %}@{{ citation_type }}{ {{ citation_key }},
27
- title = { {{ citation_title | strip }} },
28
- author = { {{ author_line | strip }} },
29
- {% if citation_year %}year = { {{ citation_year }} },{% endif %}
30
- {% if publisher %}publisher = { {{ publisher | strip }} },{% endif %}
31
- {% if doi %}doi = { {{ doi }} },{% endif %}
32
- url = { {{ citation_url }} }
33
- }
34
- {% endcapture %}
35
- {% capture ris_entry %}TY - {{ citation_type | upcase }}
36
- TI - {{ citation_title | strip }}
37
- {% if citation_year %}PY - {{ citation_year }}
38
- {% endif %}{{ ris_authors | strip }}
39
- {% if publisher %}PB - {{ publisher | strip }}
40
- {% endif %}
41
- {% if doi %}DO - {{ doi }}
42
- {% endif %}UR - {{ citation_url }}
43
- ER -
44
- {% endcapture %}
45
- {% capture endnote_entry %}%0 {{ citation_type | capitalize }}
46
- %T {{ citation_title | strip }}
47
- {{ ris_authors | strip | replace: 'AU', '%A' }}
48
- {% if citation_year %}%D {{ citation_year }}
49
- {% endif %}
50
- {% if publisher %}%I {{ publisher | strip }}
51
- {% endif %}
52
- {% if doi %}%R {{ doi }}
53
- {% endif %}
54
- %U {{ citation_url }}
55
- {% endcapture %}
56
- {% capture authors_plain %}
57
- {% for author in authors_array %}
58
- {% assign author_name = author.name | default: author %}
59
- {{ author_name | strip }}{% unless forloop.last %}, {% endunless %}
60
- {% endfor %}
61
- {% endcapture %}
16
+ {% assign exports = page | citation_exports: include %}
17
+ {%- capture authors_plain -%}
18
+ {%- for author in authors_array -%}
19
+ {{- author.name | default: author | strip | escape -}}{%- unless forloop.last -%}, {% endunless -%}
20
+ {%- endfor -%}
21
+ {%- endcapture -%}
62
22
  {% capture citation_text %}
63
- {{ authors_plain | strip }}{% if citation_year %} ({{ citation_year }}){% endif %}. {{ citation_title | strip }}. {{ publisher | strip }}. {{ citation_url }}.
23
+ {{ authors_plain | strip }}{% if citation_year %} ({{ citation_year }}){% endif %}. {{ citation_title | strip | escape }}. {{ publisher | strip | escape }}. {{ citation_url | escape }}.
64
24
  {% endcapture %}
65
25
  <section class="citation-tools" aria-labelledby="citation-tools-heading-{{ citation_key }}">
66
26
  <div class="citation-tools__header">
@@ -74,21 +34,21 @@ ER -
74
34
  <h3>{% t 'citation_tools.formats.bibtex' %}</h3>
75
35
  <button type="button" class="btn btn--ghost" data-copy-citation data-target="bibtex-{{ citation_key }}">{% t 'citation_tools.copy' %}</button>
76
36
  </header>
77
- <textarea id="bibtex-{{ citation_key }}" readonly aria-label="{% t 'citation_tools.aria.bibtex' %}">{{ bibtex_entry | strip }}</textarea>
37
+ <textarea id="bibtex-{{ citation_key }}" readonly aria-label="{% t 'citation_tools.aria.bibtex' %}">{{ exports.bibtex | escape }}</textarea>
78
38
  </article>
79
39
  <article class="citation-tools__format" data-citation-format="ris">
80
40
  <header>
81
41
  <h3>{% t 'citation_tools.formats.ris' %}</h3>
82
42
  <button type="button" class="btn btn--ghost" data-copy-citation data-target="ris-{{ citation_key }}">{% t 'citation_tools.copy' %}</button>
83
43
  </header>
84
- <textarea id="ris-{{ citation_key }}" readonly aria-label="{% t 'citation_tools.aria.ris' %}">{{ ris_entry | strip }}</textarea>
44
+ <textarea id="ris-{{ citation_key }}" readonly aria-label="{% t 'citation_tools.aria.ris' %}">{{ exports.ris | escape }}</textarea>
85
45
  </article>
86
46
  <article class="citation-tools__format" data-citation-format="endnote">
87
47
  <header>
88
48
  <h3>{% t 'citation_tools.formats.endnote' %}</h3>
89
49
  <button type="button" class="btn btn--ghost" data-copy-citation data-target="endnote-{{ citation_key }}">{% t 'citation_tools.copy' %}</button>
90
50
  </header>
91
- <textarea id="endnote-{{ citation_key }}" readonly aria-label="{% t 'citation_tools.aria.endnote' %}">{{ endnote_entry | strip }}</textarea>
51
+ <textarea id="endnote-{{ citation_key }}" readonly aria-label="{% t 'citation_tools.aria.endnote' %}">{{ exports.endnote | escape }}</textarea>
92
52
  </article>
93
53
  </div>
94
54
  </section>
@@ -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,retry_after,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,retry_after,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,retry_after,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 -%}