datalog-theme 0.10.0 → 0.11.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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +133 -0
  3. data/CITATION.cff +2 -2
  4. data/README.md +4 -2
  5. data/_data/i18n/en.yml +55 -3
  6. data/_data/i18n/es.yml +55 -3
  7. data/_data/i18n/pt.yml +55 -3
  8. data/_includes/analytics/dashboard.html +24 -9
  9. data/_includes/components/academic-dashboard.html +168 -133
  10. data/_includes/components/author-bio.html +7 -5
  11. data/_includes/components/author-list.html +6 -5
  12. data/_includes/components/citation-tools.html +2 -1
  13. data/_includes/components/content-provenance.html +19 -1
  14. data/_includes/components/correction-report.html +15 -8
  15. data/_includes/components/hero.html +22 -15
  16. data/_includes/components/package-index.html +121 -0
  17. data/_includes/components/package-install.html +27 -50
  18. data/_includes/components/post-list.html +14 -0
  19. data/_includes/components/responsive-image.html +15 -21
  20. data/_includes/footer.html +1 -1
  21. data/_includes/head.html +70 -11
  22. data/_includes/layouts/default/article.html +76 -46
  23. data/_includes/meta/dataset-json.html +171 -0
  24. data/_includes/meta/math-config.html +15 -1
  25. data/_includes/meta/package-json.html +69 -0
  26. data/_includes/meta/person-json.html +36 -13
  27. data/_includes/meta/publisher.html +15 -0
  28. data/_includes/meta/schema.html +108 -23
  29. data/_includes/meta/scholarly.html +10 -5
  30. data/_includes/post/related-posts.html +17 -43
  31. data/_includes/scripts.html +2 -2
  32. data/_includes/search/index-data.json +10 -1
  33. data/_includes/search/page.html +11 -6
  34. data/_layouts/archive.html +67 -0
  35. data/_layouts/dataset.html +2 -1
  36. data/_layouts/default.html +13 -11
  37. data/_layouts/docs.html +53 -0
  38. data/_layouts/home.html +30 -1
  39. data/_layouts/notebook.html +5 -1
  40. data/_layouts/package.html +38 -21
  41. data/_layouts/portfolio.html +1 -0
  42. data/_layouts/post.html +21 -62
  43. data/_layouts/project.html +2 -1
  44. data/_layouts/research.html +14 -2
  45. data/_plugins/analytics_dashboard.rb +4 -1
  46. data/_plugins/archive.rb +114 -0
  47. data/_plugins/authors.rb +50 -1
  48. data/_plugins/citation_exports.rb +12 -1
  49. data/_plugins/config_validator.rb +77 -2
  50. data/_plugins/correction_fallback.rb +110 -0
  51. data/_plugins/image_optimizer.rb +129 -22
  52. data/_plugins/math_preprocessor.rb +7 -48
  53. data/_plugins/notebook_converter.rb +44 -5
  54. data/_plugins/packages.rb +84 -0
  55. data/_plugins/page_dates.rb +37 -0
  56. data/_plugins/references.rb +16 -2
  57. data/_plugins/related_posts.rb +149 -0
  58. data/_plugins/search_sections.rb +110 -0
  59. data/_plugins/site_identity.rb +42 -0
  60. data/_plugins/social_cards.rb +17 -0
  61. data/_sass/_academic-dashboard.scss +18 -30
  62. data/_sass/_base.scss +2 -0
  63. data/_sass/_components.scss +21 -23
  64. data/_sass/_docs.scss +143 -0
  65. data/_sass/_features.scss +6 -0
  66. data/_sass/_layout.scss +65 -6
  67. data/_sass/_mathematical.scss +61 -19
  68. data/_sass/_notebooks.scss +1 -32
  69. data/_sass/_package-docs.scss +6 -9
  70. data/_sass/_post-components.scss +23 -23
  71. data/_sass/_print.scss +1 -1
  72. data/_sass/_search-page.scss +75 -0
  73. data/_sass/_search.scss +17 -26
  74. data/_sass/_syntax-highlighting.scss +23 -1
  75. data/_sass/_theme.scss +1 -0
  76. data/_sass/_utilities.scss +87 -40
  77. data/_sass/_variables.scss +31 -29
  78. data/assets/css/main.scss +2 -1
  79. data/assets/js/dist/academic.js +1 -1
  80. data/assets/js/dist/analytics-dashboard.js +1 -1
  81. data/assets/js/dist/chunks/chunk-TNVD6UAM.js +1 -0
  82. data/assets/js/dist/comments.js +1 -1
  83. data/assets/js/dist/contact.js +1 -1
  84. data/assets/js/dist/core.js +1 -1
  85. data/assets/js/dist/corrections.js +5 -1
  86. data/assets/js/dist/loader.js +1 -1
  87. data/assets/js/dist/math.js +2 -1
  88. data/assets/js/dist/moderation.js +1 -1
  89. data/assets/js/dist/reactions.js +1 -1
  90. data/assets/js/dist/search.js +1 -1
  91. data/assets/js/dist/sources.json +20 -16
  92. data/assets/js/dist/subscriptions.js +1 -1
  93. data/assets/js/loader.js +6 -2
  94. data/datalog-theme.gemspec +1 -1
  95. data/lib/datalog/audit/checks.rb +269 -0
  96. data/lib/datalog/audit/known_keys.rb +61 -0
  97. data/lib/datalog/audit/site_reader.rb +118 -0
  98. data/lib/datalog/audit/source_file.rb +85 -0
  99. data/lib/datalog/audit.rb +145 -0
  100. data/lib/datalog/citations/bibtex.rb +200 -0
  101. data/lib/datalog/citations/entry.rb +333 -0
  102. data/lib/datalog/citations/markup.rb +79 -0
  103. data/lib/datalog/cli.rb +164 -89
  104. data/lib/datalog/critical_css.rb +126 -21
  105. data/lib/datalog/latex_speech/words.json +88 -0
  106. data/lib/datalog/latex_speech.rb +355 -0
  107. data/lib/datalog/packages/command.rb +51 -0
  108. data/lib/datalog/packages/refresh.rb +187 -0
  109. data/lib/datalog/packages.rb +177 -0
  110. data/lib/datalog/plugin_system.rb +5 -0
  111. data/lib/datalog/plugins/citations.rb +325 -109
  112. data/lib/datalog/site_config.rb +26 -0
  113. data/lib/datalog/social_cards/font.rb +286 -0
  114. data/lib/datalog/social_cards/fonts/IBMPlexSans-Regular.ttf +0 -0
  115. data/lib/datalog/social_cards/fonts/IBMPlexSerif-SemiBold.ttf +0 -0
  116. data/lib/datalog/social_cards/fonts/OFL.txt +92 -0
  117. data/lib/datalog/social_cards/geometry.rb +51 -0
  118. data/lib/datalog/social_cards/outline.rb +54 -0
  119. data/lib/datalog/social_cards/plain_text.rb +258 -0
  120. data/lib/datalog/social_cards/template.rb +322 -0
  121. data/lib/datalog/social_cards/template.svg +17 -0
  122. data/lib/datalog/social_cards/typesetter.rb +178 -0
  123. data/lib/datalog/social_cards.rb +369 -0
  124. data/lib/datalog/theme/updater.rb +307 -0
  125. data/lib/datalog/theme/version.rb +1 -1
  126. metadata +46 -9
  127. data/_includes/components/enhanced-code-block.html +0 -212
  128. data/_includes/components/performance-monitor.html +0 -170
  129. data/_includes/components/viz-table-fallback.html +0 -19
  130. data/_plugins/datalog_bibliography.rb +0 -21
  131. data/assets/js/dist/chunks/chunk-WIRUK3OZ.js +0 -1
data/_includes/head.html CHANGED
@@ -36,7 +36,7 @@
36
36
  <meta name="robots" content="{{ page.robots | default: 'index,follow' }}" />
37
37
  <meta name="format-detection" content="telephone=no" />
38
38
  <meta name="application-name" content="{{ site.title | escape }}" />
39
- <meta property="og:site_name" content="{{ site.title | escape }}" />
39
+ {% assign head_identity = site | site_identity %}<meta property="og:site_name" content="{{ head_identity.name | default: site.title | escape }}" />
40
40
  {% if page.layout == 'post' or page.collection == 'posts' %}
41
41
  <meta property="og:type" content="article" />
42
42
  {% else %}
@@ -45,8 +45,21 @@
45
45
  <meta property="og:title" content="{{ page_title | normalize_whitespace | escape }}" />
46
46
  <meta property="og:description" content="{{ page_description | normalize_whitespace | escape }}" />
47
47
  <meta property="og:url" content="{{ canonical_url }}" />
48
- {% if page.og_image or page.image %}
49
- <meta property="og:image" content="{{ page.og_image | default: page.image | absolute_url }}" />
48
+ {%- comment -%}
49
+ The share image: the page's own, else the card the build drew for it
50
+ (theme_options.social_cards), else the site's default. A blank `image:`
51
+ is no image, as the card generator reads it.
52
+ {%- endcomment %}
53
+ {%- assign share_image = page.og_image | strip | default: page.image | strip -%}
54
+ {%- assign twitter_share_image = page.twitter_image | strip | default: share_image -%}
55
+ {% if share_image != "" %}
56
+ <meta property="og:image" content="{{ share_image | absolute_url }}" />
57
+ {% elsif page.datalog_social_card %}
58
+ <meta property="og:image" content="{{ page.datalog_social_card.url | absolute_url }}" />
59
+ <meta property="og:image:type" content="image/png" />
60
+ <meta property="og:image:width" content="{{ page.datalog_social_card.width }}" />
61
+ <meta property="og:image:height" content="{{ page.datalog_social_card.height }}" />
62
+ <meta property="og:image:alt" content="{{ page.datalog_social_card.alt | escape }}" />
50
63
  {% elsif site.social.default_image %}
51
64
  <meta property="og:image" content="{{ site.social.default_image | absolute_url }}" />
52
65
  {% endif %}
@@ -56,8 +69,11 @@
56
69
  <meta name="twitter:site" content="{{ site.social.twitter | default: site.twitter_username }}" />
57
70
  <meta name="twitter:title" content="{{ page_title | normalize_whitespace | escape }}" />
58
71
  <meta name="twitter:description" content="{{ page_description | normalize_whitespace | escape }}" />
59
- {% if page.twitter_image or page.og_image or page.image %}
60
- <meta name="twitter:image" content="{{ page.twitter_image | default: page.og_image | default: page.image | absolute_url }}" />
72
+ {% if twitter_share_image != "" %}
73
+ <meta name="twitter:image" content="{{ twitter_share_image | absolute_url }}" />
74
+ {% elsif page.datalog_social_card %}
75
+ <meta name="twitter:image" content="{{ page.datalog_social_card.url | absolute_url }}" />
76
+ <meta name="twitter:image:alt" content="{{ page.datalog_social_card.alt | escape }}" />
61
77
  {% elsif site.social.default_image %}
62
78
  <meta name="twitter:image" content="{{ site.social.default_image | absolute_url }}" />
63
79
  {% endif %}
@@ -154,10 +170,17 @@
154
170
  <link rel="shortcut icon" href="{{ '/assets/img/favicons/favicon.ico' | relative_url }}" />
155
171
  <meta name="msapplication-TileColor" content="#0b1d3d" />
156
172
 
173
+ {%- comment -%}
174
+ ` | <site title>` follows a page's own title. `seo_title_suffix: false` leaves
175
+ it out, for a title that already names the site; a string replaces the site
176
+ title after the bar.
177
+ {%- endcomment %}
178
+ {% assign title_suffix = site.title %}
179
+ {% if page.seo_title_suffix == false or page.seo_title_suffix == '' %}{% assign title_suffix = nil %}{% elsif page.seo_title_suffix %}{% assign title_suffix = page.seo_title_suffix %}{% endif %}
157
180
  {% if page.seo_title %}
158
- <title>{{ page.seo_title | normalize_whitespace | escape }} | {{ site.title | escape }}</title>
181
+ <title>{{ page.seo_title | normalize_whitespace | escape }}{% if title_suffix %} | {{ title_suffix | escape }}{% endif %}</title>
159
182
  {% elsif page.title and page.title != site.title %}
160
- <title>{{ page.title | normalize_whitespace | escape }} | {{ site.title | escape }}</title>
183
+ <title>{{ page.title | normalize_whitespace | escape }}{% if title_suffix %} | {{ title_suffix | escape }}{% endif %}</title>
161
184
  {% else %}
162
185
  <title>{{ site.title | escape }}</title>
163
186
  {% endif %}
@@ -261,6 +284,9 @@
261
284
  {% else %}
262
285
  <script nonce="{{ page.csp_nonce }}">
263
286
  window.MathJax = {
287
+ // The maths toolkit's options. MathJax keeps them in MathJax.config, where
288
+ // ready() below and math.js, whichever runs second, both read them (#421).
289
+ datalog: { numbering: '{{ math_numbering }}' },
264
290
  tex: {
265
291
  inlineMath: [['$', '$'], ['\\(', '\\)']],
266
292
  displayMath: [['$$', '$$'], ['\\[', '\\]']],
@@ -270,7 +296,8 @@
270
296
  // used in \require prefix", and nothing was typeset. Every extension the
271
297
  // pages use is loaded below, so authors never need \require.
272
298
  packages: { '[+]': ['ams', 'mathtools', 'physics', 'mhchem', 'boldsymbol', 'braket', 'cancel'] },
273
- tags: 'all',
299
+ // theme_options.math.numbering: `ams` (the default), `all` or `none`.
300
+ tags: '{{ math_numbering }}',
274
301
  tagSide: 'right',
275
302
  tagIndent: '1.25em',
276
303
  macros: {
@@ -303,11 +330,23 @@
303
330
  enableAssistiveMml: true,
304
331
  enableMenu: true,
305
332
  skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'code', 'pre'],
306
- renderActions: { addMenu: [0, '', ''] },
333
+ renderActions: {
334
+ addMenu: [0, '', ''],
335
+ // After MathJax inserts an expression (STATE.INSERTED is 200), on every
336
+ // render: MathJax draws one again into a new container when a reader
337
+ // changes a menu setting, and that container needs what the toolkit
338
+ // gave the first (#412). It is configuration rather than added at
339
+ // start, because the menu builds a new document for some settings.
340
+ datalogRestore: [210,
341
+ (doc) => { if (window.DatalogMath) { window.DatalogMath.restoreAll(doc); } return false; },
342
+ (math) => { if (window.DatalogMath) { window.DatalogMath.restore(math); } return false; }]
343
+ },
307
344
  menuOptions: {
308
345
  settings: {
309
346
  assistiveMml: true,
310
- collapsible: true,
347
+ // Collapsible math wraps parts of an expression in toggles: a click
348
+ // meant to select a formula collapsed part of it to ◂…▸ (#412).
349
+ collapsible: false,
311
350
  // MathJax's explorer gives each equation tabindex="1" when it
312
351
  // attaches, which takes the equations out of the page's tab order.
313
352
  // The assistive MathML above is what screen readers read.
@@ -320,9 +359,29 @@
320
359
  startup: {
321
360
  ready() {
322
361
  const MJ = MathJax;
362
+ {%- if math_numbering == 'ams' %}
363
+ // `ams` numbers what amsmath numbers (equation, align, gather and the
364
+ // rest, not their starred forms) and also a $$...$$ display with a
365
+ // \label, as the theme's guides write them: MathJax's own `ams` leaves
366
+ // that display unnumbered, and its \eqref reads (???).
367
+ const tex = MJ._ && MJ._.input && MJ._.input.tex;
368
+ const AmsTags = tex && tex.ams && tex.ams.AmsConfiguration && tex.ams.AmsConfiguration.AmsTags;
369
+ if (AmsTags) {
370
+ const finalize = AmsTags.prototype.finalize;
371
+ AmsTags.prototype.finalize = function (node, env) {
372
+ if (env.display && this.label && this.currentTag.tag == null && !this.currentTag.env &&
373
+ !this.history.some((tag) => tag.taggable)) {
374
+ return this.enTag(node, this.getTag(true));
375
+ }
376
+ return finalize.call(this, node, env);
377
+ };
378
+ }
379
+ {%- endif %}
323
380
  MJ.startup.defaultReady();
381
+ // math.js may not have arrived yet; if so, it starts the toolkit itself
382
+ // when it does (#421).
324
383
  if (window.DatalogMath && typeof window.DatalogMath.init === 'function') {
325
- window.DatalogMath.init(MJ);
384
+ window.DatalogMath.init(MJ, MJ.config.datalog);
326
385
  }
327
386
  MJ.startup.promise.then(() => {
328
387
  document.querySelectorAll('[data-math-alt]').forEach((el) => {
@@ -6,64 +6,94 @@ required:
6
6
  optional:
7
7
  - date_format
8
8
  ---
9
- Renders the standard article scaffold used by the default layout.
9
+ The page's heading and the content of every layout built on the default one.
10
+
11
+ A layout that renders its own heading sets `show_title: false`, and one that
12
+ renders its own date and authors sets `article_meta: false`; a page may set
13
+ either for itself. The heading carries the page's `summary`, or a document's
14
+ `description`, never the excerpt, which is the content's own first paragraph.
15
+ The date is the one front matter gives, never the time of the build, which
16
+ Jekyll gives an undated document, and the date and authors are labelled.
17
+
18
+ Structured data comes from the JSON-LD meta/schema.html writes and from the
19
+ microdata of a layout's own item (a post, a notebook, a project, a research
20
+ article). This element is no item of its own: as a ScholarlyArticle around
21
+ every page it contradicted the JSON-LD of most of them (#411).
10
22
  {%- endcomment -%}
11
23
  {%- assign article_page = include.page -%}
12
24
  {%- assign hero_overlay_title = false -%}
13
25
  {%- if article_page.post_hero.image and article_page.post_hero.overlay -%}
14
26
  {%- assign hero_overlay_title = true -%}
15
27
  {%- endif -%}
16
- <article class="content-block" itemscope itemtype="https://schema.org/ScholarlyArticle">
28
+ {%- assign article_meta = true -%}
29
+ {%- if article_page.article_meta == false or layout.article_meta == false -%}
30
+ {%- assign article_meta = false -%}
31
+ {%- endif -%}
32
+ {%- assign article_heading = false -%}
33
+ {%- if article_page.title and article_page.show_title != false and layout.show_title != false -%}
34
+ {%- assign article_heading = true -%}
35
+ {%- endif -%}
36
+ {%- capture article_header -%}
37
+ {%- unless hero_overlay_title -%}
38
+ {%- if article_heading -%}
39
+ <h1 class="article-title">{{ article_page.title | escape }}</h1>
40
+ {%- endif -%}
41
+ {%- if article_page.subtitle -%}
42
+ <p class="article-subtitle">{{ article_page.subtitle | escape }}</p>
43
+ {%- endif -%}
44
+ {%- endunless -%}
45
+ {%- comment -%} A layout with a heading of its own writes its own summary under it {%- endcomment -%}
46
+ {%- assign article_summary = article_page.summary -%}
47
+ {%- if article_page.collection -%}{%- assign article_summary = article_summary | default: article_page.description -%}{%- endif -%}
48
+ {%- if article_heading and article_summary -%}
49
+ <p class="article-summary">{{ article_summary | strip_html | escape }}</p>
50
+ {%- endif -%}
51
+ {%- if article_meta -%}
52
+ {%- assign published = article_page | published_date -%}
53
+ {%- assign modified = article_page.last_modified_at | default: article_page.updated -%}
54
+ {%- if modified and published -%}
55
+ {%- assign modified_day = modified | date: '%F' -%}
56
+ {%- assign published_day = published | date: '%F' -%}
57
+ {%- if modified_day == published_day -%}{%- assign modified = nil -%}{%- endif -%}
58
+ {%- endif -%}
59
+ {%- assign article_authors = article_page | page_authors -%}
60
+ {%- if article_page.show_author == false -%}{%- assign article_authors = '' | split: '' -%}{%- endif -%}
61
+ {%- if published or modified or article_authors.size > 0 -%}
62
+ <dl class="post-meta-list article-meta">
63
+ {%- if published -%}
64
+ <div>
65
+ <dt class="post-meta-term">{% t 'article.meta.published' %}</dt>
66
+ <dd class="post-meta-definition"><time datetime="{{ published | date_to_xmlschema }}">{% include helpers/date-format.html date=published format=include.date_format %}</time></dd>
67
+ </div>
68
+ {%- endif -%}
69
+ {%- if modified -%}
70
+ <div>
71
+ <dt class="post-meta-term">{% t 'article.meta.updated' %}</dt>
72
+ <dd class="post-meta-definition"><time datetime="{{ modified | date_to_xmlschema }}">{% include helpers/date-format.html date=modified format=include.date_format %}</time></dd>
73
+ </div>
74
+ {%- endif -%}
75
+ {%- if article_authors.size > 0 -%}
76
+ <div>
77
+ <dt class="post-meta-term">{% if article_authors.size > 1 %}{% t 'article.meta.authors' %}{% else %}{% t 'article.meta.author' %}{% endif %}</dt>
78
+ <dd class="post-meta-definition">{% include components/author-list.html people=article_authors show_affiliation=false property=false %}</dd>
79
+ </div>
80
+ {%- endif -%}
81
+ </dl>
82
+ {%- endif -%}
83
+ {%- endif -%}
84
+ {%- endcapture -%}
85
+ {%- assign article_header = article_header | strip -%}
86
+ <article class="content-block">
17
87
  {%- if article_page.post_hero.image -%}
18
88
  {% include components/post-hero.html page=article_page %}
19
89
  {%- endif -%}
90
+ {%- if article_header != "" %}
20
91
  <header class="article-header">
21
- {%- unless hero_overlay_title -%}
22
- {%- comment -%}
23
- `show_title: false`, in a page's front matter or a layout's, leaves the <h1> to a
24
- page or layout that renders its own heading, so the page has one.
25
- {%- endcomment -%}
26
- {%- if article_page.title and article_page.show_title != false and layout.show_title != false -%}
27
- <h1 class="article-title" itemprop="headline">{{ article_page.title | escape }}</h1>
28
- {%- endif -%}
29
- {%- if article_page.subtitle -%}
30
- <p class="article-subtitle">{{ article_page.subtitle | escape }}</p>
31
- {%- endif -%}
32
- {%- endunless -%}
33
- {%- if article_page.excerpt -%}
34
- <p class="article-summary" itemprop="abstract">{{ article_page.excerpt | strip_html }}</p>
35
- {%- endif -%}
36
-
37
- <dl class="article-meta">
38
- {%- if article_page.date -%}
39
- <div>
40
- <dt class="visually-hidden">{% t 'article.meta.published' %}</dt>
41
- <dd itemprop="datePublished" content="{{ article_page.date | date_to_xmlschema }}">
42
- {% include helpers/date-format.html date=article_page.date format=include.date_format %}
43
- </dd>
44
- </div>
45
- {%- endif -%}
46
- {%- assign modified = article_page.last_modified_at | default: article_page.updated -%}
47
- {%- if modified -%}
48
- <div>
49
- <dt class="visually-hidden">{% t 'article.meta.updated' %}</dt>
50
- <dd itemprop="dateModified" content="{{ modified | date_to_xmlschema }}">
51
- {% include helpers/date-format.html date=modified format=include.date_format %}
52
- </dd>
53
- </div>
54
- {%- endif -%}
55
- {%- assign article_authors = article_page | page_authors -%}
56
- {%- comment -%} `show_author: false` hides the byline, for pages that are site chrome rather than articles. {%- endcomment -%}
57
- {%- if article_authors.size > 0 and article_page.show_author != false -%}
58
- <div>
59
- <dt class="visually-hidden">{% if article_authors.size > 1 %}{% t 'article.meta.authors' %}{% else %}{% t 'article.meta.author' %}{% endif %}</dt>
60
- <dd>{% include components/author-list.html people=article_authors show_affiliation=false %}</dd>
61
- </div>
62
- {%- endif -%}
63
- </dl>
92
+ {{ article_header }}
64
93
  </header>
94
+ {%- endif %}
65
95
 
66
- <div class="article-body" itemprop="articleBody">
96
+ <div class="article-body">
67
97
  {{ include.content }}
68
98
  </div>
69
99
  </article>
@@ -0,0 +1,171 @@
1
+ {%- comment -%}
2
+ ---
3
+ required:
4
+ - page
5
+ ---
6
+ schema.org/Dataset for a page in the `datasets` collection.
7
+
8
+ Google Dataset Search indexes `Dataset` and nothing else, so a dataset page
9
+ described only as a `WebPage` cannot be found there. Everything this needs is
10
+ already in the front matter the dataset layout renders: the summary, the
11
+ licence, the download and the field schema.
12
+
13
+ Front matter it reads, all optional beyond the title:
14
+
15
+ summary: the description; the excerpt when there is none
16
+ updated: dateModified
17
+ license: resolved through `page_license`, so the URL is the licence's
18
+ download_url: one distribution; `distributions:` takes a list
19
+ schema: name/description per field, as variableMeasured
20
+ doi: identifier, as a PropertyValue and a DOI URL
21
+ keywords: falls back to `tags`
22
+ temporal_coverage, spatial_coverage, measurement_technique, citation
23
+ is_accessible_for_free: false marks a dataset behind a wall
24
+
25
+ Every field is left out when it has no value: `"license": ""` is not valid
26
+ structured data, and an empty one is worse than none.
27
+ {%- endcomment -%}
28
+ {%- assign ds = include.page -%}
29
+ {%- assign ds_url = ds.url | absolute_url -%}
30
+ {%- assign ds_excerpt = ds.excerpt | strip_html | strip_newlines -%}
31
+ {%- assign ds_description = ds.summary | default: ds.description | default: ds_excerpt | strip_newlines -%}
32
+ {%- assign ds_license = ds | page_license -%}
33
+ {%- assign ds_authors = ds | page_authors -%}
34
+ {%- assign ds_keywords = ds.keywords | default: ds.tags -%}
35
+ {%- comment -%}
36
+ Jekyll gives a collection document without a `date:` the build time, so
37
+ `page.date` cannot be trusted to mean anything. Comparing it with the build
38
+ stamp tells a real publication date from that fallback, and keeps the output
39
+ the same from one build to the next.
40
+ {%- endcomment -%}
41
+ {%- assign ds_published = ds | published_date -%}
42
+ {%- assign ds_modified = ds.updated | default: ds.last_modified_at | default: ds_published -%}
43
+ {%- comment -%} One download, or a list of them under `distributions`. {%- endcomment -%}
44
+ {%- assign ds_downloads = ds.distributions -%}
45
+ {%- unless ds_downloads -%}
46
+ {%- if ds.download_url -%}
47
+ {%- assign ds_downloads = '' | split: '' | push: ds.download_url -%}
48
+ {%- endif -%}
49
+ {%- endunless -%}
50
+ <script type="application/ld+json" nonce="{{ schema_nonce }}">
51
+ {
52
+ "@context": "https://schema.org",
53
+ "@type": "Dataset",
54
+ "name": {{ ds.title | jsonify_script }},
55
+ "description": {{ ds_description | jsonify_script }},
56
+ "url": {{ ds_url | jsonify_script }}
57
+ {%- if ds_license.url -%},
58
+ "license": {{ ds_license.url | jsonify_script }}
59
+ {%- elsif ds_license.name -%},
60
+ "license": {{ ds_license.name | jsonify_script }}
61
+ {%- endif -%}
62
+ {%- if ds.doi -%},
63
+ "identifier": {
64
+ "@type": "PropertyValue",
65
+ "propertyID": "doi",
66
+ "value": {{ ds.doi | jsonify_script }},
67
+ "url": {{ ds.doi | prepend: 'https://doi.org/' | replace: 'https://doi.org/https://doi.org/', 'https://doi.org/' | jsonify_script }}
68
+ }
69
+ {%- endif -%}
70
+ {%- if ds_published -%},
71
+ "datePublished": "{{ ds_published | date_to_xmlschema }}"
72
+ {%- endif -%}
73
+ {%- if ds_modified -%},
74
+ "dateModified": "{{ ds_modified | date_to_xmlschema }}"
75
+ {%- endif -%}
76
+ {%- if ds_keywords and ds_keywords.size > 0 -%},
77
+ "keywords": {{ ds_keywords | jsonify_script }}
78
+ {%- endif -%}
79
+ {%- if ds.temporal_coverage -%},
80
+ "temporalCoverage": {{ ds.temporal_coverage | jsonify_script }}
81
+ {%- endif -%}
82
+ {%- if ds.spatial_coverage -%},
83
+ "spatialCoverage": {{ ds.spatial_coverage | jsonify_script }}
84
+ {%- endif -%}
85
+ {%- if ds.measurement_technique -%},
86
+ "measurementTechnique": {{ ds.measurement_technique | jsonify_script }}
87
+ {%- endif -%}
88
+ {%- if ds.citation -%},
89
+ "citation": {{ ds.citation | jsonify_script }}
90
+ {%- endif -%}
91
+ {%- if ds.is_accessible_for_free != nil -%},
92
+ "isAccessibleForFree": {% if ds.is_accessible_for_free == false %}false{% else %}true{% endif %}
93
+ {%- endif -%}
94
+ {%- if ds_authors.size > 0 -%},
95
+ "creator": {% if ds_authors.size > 1 %}[{% endif %}
96
+ {%- for author in ds_authors -%}
97
+ {% include meta/person-json.html person=author %}{% unless forloop.last %},{% endunless %}
98
+ {%- endfor -%}
99
+ {% if ds_authors.size > 1 %}]{% endif %}
100
+ {%- endif -%}
101
+ {%- if ds.schema and ds.schema.size > 0 -%},
102
+ "variableMeasured": [
103
+ {%- for field in ds.schema -%}
104
+ {
105
+ "@type": "PropertyValue",
106
+ "name": {{ field.name | jsonify_script }}
107
+ {%- if field.description -%},
108
+ "description": {{ field.description | jsonify_script }}
109
+ {%- endif -%}
110
+ }{% unless forloop.last %},{% endunless %}
111
+ {%- endfor -%}
112
+ ]
113
+ {%- endif -%}
114
+ {%- if ds_downloads and ds_downloads.size > 0 -%},
115
+ "distribution": [
116
+ {%- for download in ds_downloads -%}
117
+ {%- comment -%} A plain URL, or a hash with `url` and optionally `format` and `name`. {%- endcomment -%}
118
+ {%- if download.url -%}
119
+ {%- assign download_url = download.url -%}
120
+ {%- assign download_format = download.format -%}
121
+ {%- assign download_name = download.name -%}
122
+ {%- else -%}
123
+ {%- assign download_url = download -%}
124
+ {%- assign download_format = nil -%}
125
+ {%- assign download_name = nil -%}
126
+ {%- endif -%}
127
+ {%- unless download_format -%}
128
+ {%- assign extension = download_url | split: '?' | first | split: '.' | last | downcase -%}
129
+ {%- case extension -%}
130
+ {%- when 'csv' -%}{%- assign download_format = 'text/csv' -%}
131
+ {%- when 'tsv' -%}{%- assign download_format = 'text/tab-separated-values' -%}
132
+ {%- when 'json' -%}{%- assign download_format = 'application/json' -%}
133
+ {%- when 'jsonl', 'ndjson' -%}{%- assign download_format = 'application/x-ndjson' -%}
134
+ {%- when 'zip' -%}{%- assign download_format = 'application/zip' -%}
135
+ {%- when 'gz', 'gzip' -%}{%- assign download_format = 'application/gzip' -%}
136
+ {%- when 'parquet' -%}{%- assign download_format = 'application/vnd.apache.parquet' -%}
137
+ {%- when 'xlsx' -%}{%- assign download_format = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' -%}
138
+ {%- when 'nc' -%}{%- assign download_format = 'application/x-netcdf' -%}
139
+ {%- when 'h5', 'hdf5' -%}{%- assign download_format = 'application/x-hdf5' -%}
140
+ {%- else -%}{%- assign download_format = nil -%}
141
+ {%- endcase -%}
142
+ {%- endunless -%}
143
+ {
144
+ "@type": "DataDownload",
145
+ "contentUrl": {{ download_url | absolute_url | jsonify_script }}
146
+ {%- if download_format -%},
147
+ "encodingFormat": {{ download_format | jsonify_script }}
148
+ {%- endif -%}
149
+ {%- if download_name -%},
150
+ "name": {{ download_name | jsonify_script }}
151
+ {%- endif -%}
152
+ }{% unless forloop.last %},{% endunless %}
153
+ {%- endfor -%}
154
+ ]
155
+ {%- endif -%}
156
+ {%- if publisher_name -%},
157
+ "publisher": {
158
+ "@type": {{ publisher_type | jsonify_script }},
159
+ "name": {{ publisher_name | jsonify_script }},
160
+ "url": {{ publisher_url | jsonify_script }}
161
+ }
162
+ {%- endif -%}
163
+ {%- comment -%} The site's own dataset index, so each dataset names the catalogue it belongs to. {%- endcomment -%}
164
+ {%- assign datasets_index = '/datasets/' | absolute_url -%},
165
+ "includedInDataCatalog": {
166
+ "@type": "DataCatalog",
167
+ "name": {{ site.title | default: site.name | jsonify_script }},
168
+ "url": {{ datasets_index | jsonify_script }}
169
+ }
170
+ }
171
+ </script>
@@ -6,7 +6,13 @@ optional:
6
6
  - status_id
7
7
  ---
8
8
  Computes math rendering settings shared by layouts.
9
- Exposes math_enabled, math_engine, math_status_id, and math_has_expressions variables.
9
+ Exposes math_enabled, math_engine, math_status_id, math_has_expressions,
10
+ math_numbering and math_display_style variables.
11
+
12
+ math_numbering is theme_options.math.numbering, `ams` unless it is `all` or
13
+ `none`, and becomes MathJax's `tex.tags` in head.html. math_display_style is
14
+ `card` when theme_options.math.display_style says so and `plain` otherwise; the
15
+ layout marks a card site's <body> with `math-card`.
10
16
 
11
17
  math_enabled resolves, in order: the page's `math` front matter, then its
12
18
  `mathjax` front matter, then `theme_options.math.render_on_load` (falling back
@@ -26,6 +32,14 @@ only on pages that opt in.
26
32
  {%- assign math_settings = site.theme_options.math | default: {} -%}
27
33
  {%- assign math_engine = include.page.math_engine | default: math_settings.engine | default: 'mathjax' -%}
28
34
  {%- assign math_status_id = include.status_id | default: 'math-status' -%}
35
+ {%- assign math_numbering = math_settings.numbering | default: 'ams' -%}
36
+ {%- unless math_numbering == 'all' or math_numbering == 'none' -%}
37
+ {%- assign math_numbering = 'ams' -%}
38
+ {%- endunless -%}
39
+ {%- assign math_display_style = 'plain' -%}
40
+ {%- if math_settings.display_style == 'card' -%}
41
+ {%- assign math_display_style = 'card' -%}
42
+ {%- endif -%}
29
43
  {%- assign math_expressions = include.page.math_expressions | default: '' -%}
30
44
  {%- assign math_has_expressions = false -%}
31
45
  {%- if math_expressions != blank and math_expressions != empty -%}
@@ -0,0 +1,69 @@
1
+ {%- comment -%}
2
+ ---
3
+ required:
4
+ - page
5
+ ---
6
+ schema.org/SoftwareSourceCode for a page in the `packages` collection, beside
7
+ the WebPage that describes the page itself.
8
+
9
+ The release is the one the package layout shows: from
10
+ _data/package_releases.yml when `datalog packages refresh` has written it,
11
+ else from front matter. Front matter it reads, all optional beyond the title:
12
+
13
+ tagline, description: the description
14
+ github_url: codeRepository
15
+ language: programmingLanguage
16
+ registry, pypi_url, cran_url, docs_url: sameAs, one URL per page
17
+ license, version: when the data file has no release; the licence is its
18
+ URL when front matter resolves the same licence
19
+
20
+ Every field is left out when it has no value.
21
+ {%- endcomment -%}
22
+ {%- assign pkg = include.page -%}
23
+ {%- assign pkg_url = pkg.url | absolute_url -%}
24
+ {%- assign pkg_release = pkg | package_release -%}
25
+ {%- assign pkg_links = pkg | package_links | map: 'url' -%}
26
+ {%- if pkg.docs_url -%}{%- assign pkg_links = pkg_links | push: pkg.docs_url -%}{%- endif -%}
27
+ {%- assign pkg_excerpt = pkg.excerpt | strip_html | normalize_whitespace -%}
28
+ {%- assign pkg_description = pkg.description | default: pkg.tagline | default: pkg_excerpt | normalize_whitespace -%}
29
+ {%- assign pkg_authors = pkg | page_authors -%}
30
+ {%- comment -%} The registry's own words (`MIT OR Apache-2.0`) when no URL is known for them. {%- endcomment -%}
31
+ {%- assign pkg_license_value = pkg_release.license_url | default: pkg_release.license -%}
32
+ <script type="application/ld+json" nonce="{{ schema_nonce }}">
33
+ {
34
+ "@context": "https://schema.org",
35
+ "@type": "SoftwareSourceCode",
36
+ "@id": {{ pkg_url | append: '#software' | jsonify_script }},
37
+ "name": {{ pkg.title | jsonify_script }},
38
+ "url": {{ pkg_url | jsonify_script }},
39
+ "mainEntityOfPage": {{ pkg_url | jsonify_script }}
40
+ {%- if pkg_description != '' -%},
41
+ "description": {{ pkg_description | jsonify_script }}
42
+ {%- endif -%}
43
+ {%- if pkg.github_url -%},
44
+ "codeRepository": {{ pkg.github_url | jsonify_script }}
45
+ {%- endif -%}
46
+ {%- if pkg.language -%},
47
+ "programmingLanguage": {{ pkg.language | jsonify_script }}
48
+ {%- endif -%}
49
+ {%- if pkg_release.requirement -%},
50
+ "runtimePlatform": {{ pkg_release.requirement | jsonify_script }}
51
+ {%- endif -%}
52
+ {%- if pkg_release.version -%},
53
+ "version": {{ pkg_release.version | jsonify_script }}
54
+ {%- endif -%}
55
+ {%- if pkg_license_value -%},
56
+ "license": {{ pkg_license_value | jsonify_script }}
57
+ {%- endif -%}
58
+ {%- if pkg_authors.size > 0 -%},
59
+ "author": {% if pkg_authors.size > 1 %}[{% endif %}
60
+ {%- for author in pkg_authors -%}
61
+ {% include meta/person-json.html person=author %}{% unless forloop.last %},{% endunless %}
62
+ {%- endfor -%}
63
+ {% if pkg_authors.size > 1 %}]{% endif %}
64
+ {%- endif -%}
65
+ {%- if pkg_links.size > 0 -%},
66
+ "sameAs": {{ pkg_links | jsonify_script }}
67
+ {%- endif %}
68
+ }
69
+ </script>
@@ -3,25 +3,48 @@
3
3
  required:
4
4
  - person
5
5
  ---
6
- One schema.org Person, as JSON, for a record from `page | page_authors` or
7
- `page | page_contributors`. The site's own author links to the site when the
8
- record names no url of its own.
6
+ One schema.org Person, as JSON, for a record from `page | page_authors`,
7
+ `page | page_contributors` or `site | site_author_record`. The site's own
8
+ author links to the site when the record names no url of its own, and carries
9
+ the `@id` that the publisher, the homepage's identity graph and a ProfilePage's
10
+ `mainEntity` give it too, so every page names the same person (#322). Each
11
+ other field is written when the record has it.
9
12
  {%- endcomment -%}
10
- {%- assign person_url = include.person.url -%}
11
- {%- if person_url == nil and include.person.site_author -%}{%- assign person_url = '/' -%}{%- endif -%}
13
+ {%- assign json_person = include.person -%}
14
+ {%- assign json_person_url = json_person.url -%}
15
+ {%- if json_person_url == nil and json_person.site_author -%}{%- assign json_person_url = '/' -%}{%- endif -%}
16
+ {%- assign json_person_description = json_person.bio | strip_html | normalize_whitespace -%}
12
17
  {
13
- "@type": {% if include.person.type == 'Organization' %}"Organization"{% else %}"Person"{% endif %},
14
- "name": {{ include.person.name | jsonify_script }}
15
- {%- if person_url -%},
16
- "url": {{ person_url | absolute_url | jsonify_script }}
18
+ "@type": {% if json_person.type == 'Organization' %}"Organization"{% else %}"Person"{% endif %},
19
+ {%- if json_person.site_author and json_person.type != 'Organization' %}
20
+ "@id": {{ '/' | absolute_url | append: '#person' | jsonify_script }},
21
+ {%- endif %}
22
+ "name": {{ json_person.name | jsonify_script }}
23
+ {%- if json_person.alternate_names.size > 0 -%},
24
+ "alternateName": {% if json_person.alternate_names.size == 1 %}{{ json_person.alternate_names.first | jsonify_script }}{% else %}{{ json_person.alternate_names | jsonify_script }}{% endif %}
17
25
  {%- endif -%}
18
- {%- if include.person.affiliation -%},
26
+ {%- if json_person_url -%},
27
+ "url": {{ json_person_url | absolute_url | jsonify_script }}
28
+ {%- endif -%}
29
+ {%- if json_person.image -%},
30
+ "image": {{ json_person.image | absolute_url | jsonify_script }}
31
+ {%- endif -%}
32
+ {%- if json_person.job_titles.size > 0 -%},
33
+ "jobTitle": {% if json_person.job_titles.size == 1 %}{{ json_person.job_titles.first | jsonify_script }}{% else %}{{ json_person.job_titles | jsonify_script }}{% endif %}
34
+ {%- endif -%}
35
+ {%- if json_person_description != '' -%},
36
+ "description": {{ json_person_description | jsonify_script }}
37
+ {%- endif -%}
38
+ {%- if json_person.affiliation -%},
19
39
  "affiliation": {
20
40
  "@type": "Organization",
21
- "name": {{ include.person.affiliation | jsonify_script }}
41
+ "name": {{ json_person.affiliation | jsonify_script }}
22
42
  }
23
43
  {%- endif -%}
24
- {%- if include.person.same_as.size > 0 -%},
25
- "sameAs": {{ include.person.same_as | jsonify_script }}
44
+ {%- if json_person.knows_about.size > 0 -%},
45
+ "knowsAbout": {{ json_person.knows_about | jsonify_script }}
46
+ {%- endif -%}
47
+ {%- if json_person.same_as.size > 0 -%},
48
+ "sameAs": {{ json_person.same_as | jsonify_script }}
26
49
  {%- endif -%}
27
50
  }
@@ -7,6 +7,11 @@ Resolves who publishes the site, for structured data. Sets publisher_type
7
7
  Organization, publisher_logo and publisher_logo_default (true when the logo is
8
8
  the theme's 512px icon, whose size is known).
9
9
 
10
+ It also sets publisher_id, the publisher's stable JSON-LD `@id` under the
11
+ site's root: `#person` when the site's author publishes, which is the id the
12
+ author's own Person carries, `#organization` for an Organization and
13
+ `#publisher` for another Person; and publisher_is_site_author.
14
+
10
15
  `publisher` in _config.yml names the publisher. Without it, the site's author
11
16
  publishes as a Person, or the site title as an Organization when there is no
12
17
  author name. The author's affiliation is not the publisher: schema.html puts it
@@ -43,3 +48,13 @@ on the author.
43
48
  {%- assign publisher_logo_default = true -%}
44
49
  {%- endif -%}
45
50
  {%- endif -%}
51
+ {%- assign identity_root = '/' | absolute_url -%}
52
+ {%- assign publisher_is_site_author = false -%}
53
+ {%- if publisher_type == 'Organization' -%}
54
+ {%- assign publisher_id = identity_root | append: '#organization' -%}
55
+ {%- elsif publisher_name == site_author_name -%}
56
+ {%- assign publisher_id = identity_root | append: '#person' -%}
57
+ {%- assign publisher_is_site_author = true -%}
58
+ {%- else -%}
59
+ {%- assign publisher_id = identity_root | append: '#publisher' -%}
60
+ {%- endif -%}