From 47efeabc74abd77366d3527f6eb97e5b2cc331b3 Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Sun, 14 Dec 2025 13:02:40 +0100 Subject: [PATCH 1/9] abstract for Psychoco 2026 (https://www.psychoco.org/2026/) --- vignettes/Psychoco2026/abstract.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 vignettes/Psychoco2026/abstract.md diff --git a/vignettes/Psychoco2026/abstract.md b/vignettes/Psychoco2026/abstract.md new file mode 100644 index 00000000..482507d0 --- /dev/null +++ b/vignettes/Psychoco2026/abstract.md @@ -0,0 +1,28 @@ +tinyplot: Lightweight Extension of the Base R Graphics System + +Grant McDermott, Vincent Arel-Bundock, Achim Zeileis + +The base R graphics system provides a lot of powerful infrastructure for drawing +data visualizations. At the core is the `plot()` generic function with its +default and formula methods. The default method can handle many basic plotting +elements (points, lines, etc.) and the formula method flexibly handles various +`y ~ x` setups including scatterplots (numeric `y` vs. numeric `x`), boxplots +(numeric `y` vs. categorical `x`), and spineplots/spinograms (categorical `y`). +Moreover, there are many elements that can be added like legends, axes, +annotation, grids of displays, etc. + +However, based on this powerful infrastructure base R provides only rather +limited convenience features pioneered by newer (`grid`-based) visualization +packages like `ggplot2` and `lattice`, e.g., grouped plots with automatic +legends and/or facets, advanced visualization types, and easy customization via +ready-made themes. + +The `tinyplot` package fills this gap by providing a lightweight extension of +the base R graphics system. It aims to preserve the strengths of the base R +infrastructure (including the `formula`-based interface) while adding the +convenience features above without requiring (strong) non-base dependencies. +The presentation provides an introduction to {tinyplot} using various +visualization examples, highlighting strengths and weaknesses compared to other +visualization packages. The package is available from CRAN +() and has many more galleries +and tutorials at . From 41fb2b2629b1b3c05204efad45fcebb3e7265a54 Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Sun, 14 Dec 2025 17:07:31 +0100 Subject: [PATCH 2/9] small improvements --- vignettes/Psychoco2026/abstract.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/vignettes/Psychoco2026/abstract.md b/vignettes/Psychoco2026/abstract.md index 482507d0..19d5128b 100644 --- a/vignettes/Psychoco2026/abstract.md +++ b/vignettes/Psychoco2026/abstract.md @@ -12,17 +12,17 @@ Moreover, there are many elements that can be added like legends, axes, annotation, grids of displays, etc. However, based on this powerful infrastructure base R provides only rather -limited convenience features pioneered by newer (`grid`-based) visualization -packages like `ggplot2` and `lattice`, e.g., grouped plots with automatic -legends and/or facets, advanced visualization types, and easy customization via -ready-made themes. +limited convenience features such as those pioneered by newer (`grid`-based) +visualization packages like `ggplot2` and `lattice`, e.g., grouped plots with +automatic legends and/or facets, advanced visualization types, and easy +customization via ready-made themes. The `tinyplot` package fills this gap by providing a lightweight extension of the base R graphics system. It aims to preserve the strengths of the base R -infrastructure (including the `formula`-based interface) while adding the +infrastructure (including the formula-based interface) while adding the convenience features above without requiring (strong) non-base dependencies. -The presentation provides an introduction to {tinyplot} using various +The presentation provides an introduction to `tinyplot` using various visualization examples, highlighting strengths and weaknesses compared to other -visualization packages. The package is available from CRAN +packages. The package is available from CRAN () and has many more galleries and tutorials at . From 2f3f1da0a0231394d377f75abf5c1bedc7b29d93 Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Sat, 7 Feb 2026 01:45:25 +0100 Subject: [PATCH 3/9] revealjs version of slides (originally prepared with quarto/beamer/uibk) --- vignettes/Psychoco2026/Psychoco2026.qmd | 338 ++++++++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 vignettes/Psychoco2026/Psychoco2026.qmd diff --git a/vignettes/Psychoco2026/Psychoco2026.qmd b/vignettes/Psychoco2026/Psychoco2026.qmd new file mode 100644 index 00000000..86a8f754 --- /dev/null +++ b/vignettes/Psychoco2026/Psychoco2026.qmd @@ -0,0 +1,338 @@ +--- +# title: tinyplot +title: "


" +subtitle: Convenient and Customizable Base R Plots +format: + clean-revealjs: + title-slide-attributes: + data-background-image: "../useR2025/img/background.png" + data-background-size: contain +execute: + echo: true + fig-height: 4.8 + fig-width: 6.4 + out-width: 60% +author: + - name: Grant McDermott + orcid: 0000-0001-7883-8573 + - name: Vincent Arel-Bundock + orcid: 0000-0003-1995-6531 + - name: Achim Zeileis + orcid: 0000-0003-0918-3766 +institute: "Psychoco 2026" +date: 2026-02-06 +extensions: iconify +--- + +## Motivation + +::: {.callout-tip} +# Ross Ihaka & Robert Gentleman (1996) +R: A Language for Data Analysis _and Graphics_ +::: + + +**Engines:** Base `graphics` vs. newer flexible `grid` (enabling `ggplot2` and `lattice`). + +**Core of base graphics:** `plot()` generic function and corresponding methods. + +**Default method:** Handles many basic plotting elements like points, lines, etc. + +**Formula method:** Handles various `y ~ x` setups. + +- Scatterplots (numeric `y` vs. numeric `x`). +- Boxplots (numeric `y` vs. categorical `x`). +- Spineplots/spinograms (categorical `y`). + + + +## Motivation + +**Illustration:** Determinants of student performance in end-term exam of an +introductory mathematics course for business and economics students at Universität Innsbruck. + +. . . + +```{r} +data("MathExam14W", package = "psychotools") +math <- MathExam14W |> + transform( + attempt = factor(attempt, ordered = FALSE), + points = rowSums(as.matrix(credits)^2 * (-1)^as.matrix(credits)) + ) |> + transform( + pass = factor(points >= 26, labels = c("fail", "pass")), + score = points/52 + ) |> + subset(select = c("score", "pass", "tests", "attempt", "gender")) +``` + + +## Motivation + +**Dependent variables:** + +- Numeric `score` (proportion of points). +- Binary indicator for `pass`-ing the exam (`score >= 0.5`). + +**Explanatory variables:** Points from previous online `tests`, `attempt`, `gender`. + +```{r} +summary(math) +``` + + +## Motivation: numeric ~ numeric + +```{r} +plot(score ~ tests, data = math) +``` + + +## Motivation: numeric ~ categorical + +```{r} +plot(score ~ attempt, data = math) +``` + + +## Motivation: categorical ~ numeric + +```{r} +plot(pass ~ tests, data = math) +``` + + +## Motivation: categorical ~ categorical + +```{r} +plot(pass ~ attempt, data = math) +``` + + +## Motivation: Limitations + +**So far:** + +- Nifty data visualizations. +- Intuitive, concise syntax. + +**Possible customizations:** + +- Groups via shading, symbols, line types, etc. +- Legends, axes, annotation. +- Grid of faceted displays. +- Layers with additional elements. + +**But:** + +- Requires low-level drawing of such elements. +- Tedious without intuitive, concise syntax. + + +## tinyplot + +:::: {.columns} + +::: {.column width="73%"} + +Install: + +```{r} +#| eval: false +install.packages("tinyplot") #or# +install.packages("tinyplot", + repos = "https://grantmcdermott.R-universe.dev") +``` + +Load: + +```{r} +library("tinyplot") +``` + +::: + +::: {.column width="20%"} + +![](../../altdoc/logo.png) + +::: + +:::: + + +::: {.callout-tip} +# Starting point +`tinyplot()` or its shorthand `plt()` as drop-in replacement for `plot()`. +::: + + +## tinyplot + +```{r} +tinyplot(score ~ tests, data = math) +``` + + +## tinyplot: Features + +**Core ideas:** + +- Preservation of strengths of base R graphics. +- Lightweight extension with convenience features. +- No strong dependencies on non-base packages. +- Improved feature parity vs. `grid`-based `ggplot2` and `lattice`. +- Grouped plots with automatic legends and/or facets. +- Advanced visualization types. +- Customization via themes. + +**Here:** + +```{r} +tinytheme("clean2") +``` + + +## tinyplot: Themes + +```{r} +tinyplot(score ~ tests, data = math) +``` + + +## tinyplot: More plot types + +```{r} +tinyplot(score ~ tests, data = math, type = "jitter") +``` + +## tinyplot: Alpha transparency + +```{r} +tinyplot(score ~ tests, data = math, alpha = 0.4) +``` + +## tinyplot: Axis labels + +```{r} +tinyplot(score ~ tests, data = math, alpha = 0.4, yaxl = "%") +``` + +## tinyplot: Add layers + +```{r} +tinyplot(score ~ tests, data = math, alpha = 0.4, yaxl = "%") +tinyplot_add(type = "loess") +``` + +## tinyplot: Grouped plots with legends + +```{r} +tinyplot(score ~ tests | attempt, data = math, pch = "by", type = "jitter") +``` + +## tinyplot: Facets + +```{r} +#| fig-height: 5.4 +#| fig-width: 8.1 +tinyplot(score ~ tests | attempt, data = math, pch = "by", facet = "by") +``` + +## tinyplot: Facets + +```{r} +#| fig-height: 4 +#| fig-width: 12 +#| out-width: 100% +tinyplot(pass ~ tests | attempt, data = math, facet = "by", + breaks = c(9, 17, 20, 23, 26), facet.args = list(nrow = 1)) +``` + +## tinyplot: Facets + +```{r} +#| fig-height: 5.4 +#| fig-width: 11 +#| out-width: 94% +tinyplot(score ~ tests | attempt, data = math, facet = gender ~ attempt) +``` + +## tinyplot: More plot types + +```{r} +#| fig-height: 6 +#| fig-width: 8.1 +tinyplot(score ~ attempt | attempt, data = math, + type = "violin", flip = TRUE, alpha = 0.4) +``` + +## tinyplot: More plot types + +```{r} +#| fig-height: 6 +#| fig-width: 8.1 +tinyplot(attempt ~ score, data = math, bg = "white", + type = type_ridge(gradient = TRUE, col = "white")) +``` + +## tinyplot: More plot types + +**Interface:** Types can be passed as either a _string_ or _function_. + +| | | | | | +|------------|:----------------|:---------------|:---------------|:-----------| +| _String_ | `"p"` | `"ridge"` | `"loess"` | ... | +| _Function_ | `type_points()` | `type_ridge()` | `type_loess()` | ... | + + +**Arguments:** Can always be passed through the `type` function. + +```{r} +#| eval: false +tinyplot(..., type = type_ridge(gradient = TRUE)) +``` + +**Alternatively:** Through `tinyplot()` if there is no clash with top-level arguments. + +```{r} +#| eval: false +tinyplot(..., type = "ridge", gradient = TRUE) +``` + + +## Use cases + +**Teaching and interactive usage:** + +- Start with simple visualizations and fundamental principles in base graphics. +- Proceed to more complex display using the same type of interface. + +**Package development:** + +- Create flexible visualizations without introducing numerous dependencies. + +**Web R:** + +- Engine for appealing graphics without adding much overhead. + +--- + +## References + +McDermott G, Arel-Bundock V, Zeileis A (2025). + _tinyplot: Lightweight Extension of the Base R Graphics System_. + R package version 0.6.0. + [`doi:10.32614/CRAN.package.tinyplot`](https://doi.org/10.32614/CRAN.package.tinyplot) + +Zeileis A (2025). + "Examining Exams Using Rasch Models and Assessment of Measurement Invariance." + _Austrian Journal of Statistics_, **54**(3), 9-26. + [`doi:10.17713/ajs.v54i3.2055`](https://doi.org/10.17713/ajs.v54i3.2055) + +{{< iconify mdi mastodon >}} [`@zeileis@fosstodon.org`](https://fosstodon.org/@zeileis) + +{{< iconify fa6-brands bluesky >}} [`@zeileis.org`](https://bsky.app/profile/zeileis.org) + +{{< iconify mdi link-variant >}} [`https://www.zeileis.org/`](https://www.zeileis.org/) From 093882876a9691b47582e2540eaa52b8bd1a19ea Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Sat, 14 Feb 2026 12:49:06 +0100 Subject: [PATCH 4/9] teak layout by adding a few non-breaking spaces --- vignettes/Psychoco2026/Psychoco2026.qmd | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vignettes/Psychoco2026/Psychoco2026.qmd b/vignettes/Psychoco2026/Psychoco2026.qmd index 86a8f754..1a1a9cbf 100644 --- a/vignettes/Psychoco2026/Psychoco2026.qmd +++ b/vignettes/Psychoco2026/Psychoco2026.qmd @@ -162,6 +162,9 @@ library("tinyplot") :::: +  + + ::: {.callout-tip} # Starting point `tinyplot()` or its shorthand `plt()` as drop-in replacement for `plot()`. @@ -331,8 +334,8 @@ Zeileis A (2025). _Austrian Journal of Statistics_, **54**(3), 9-26. [`doi:10.17713/ajs.v54i3.2055`](https://doi.org/10.17713/ajs.v54i3.2055) -{{< iconify mdi mastodon >}} [`@zeileis@fosstodon.org`](https://fosstodon.org/@zeileis) +{{< iconify mdi mastodon >}}   [`@zeileis@fosstodon.org`](https://fosstodon.org/@zeileis) -{{< iconify fa6-brands bluesky >}} [`@zeileis.org`](https://bsky.app/profile/zeileis.org) +{{< iconify fa6-brands bluesky >}}   [`@zeileis.org`](https://bsky.app/profile/zeileis.org) -{{< iconify mdi link-variant >}} [`https://www.zeileis.org/`](https://www.zeileis.org/) +{{< iconify mdi link-variant >}}   [`https://www.zeileis.org/`](https://www.zeileis.org/) From 4c42ef1b5bbae74be00d6ada2bcd4b5be42a94f4 Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Sat, 14 Feb 2026 12:56:49 +0100 Subject: [PATCH 5/9] moved Psychoco2026 materials to inst/ folder and include in .Rbuildignore --- .Rbuildignore | 1 + {vignettes => inst}/Psychoco2026/Psychoco2026.qmd | 0 {vignettes => inst}/Psychoco2026/abstract.md | 0 3 files changed, 1 insertion(+) rename {vignettes => inst}/Psychoco2026/Psychoco2026.qmd (100%) rename {vignettes => inst}/Psychoco2026/abstract.md (100%) diff --git a/.Rbuildignore b/.Rbuildignore index a80e032c..10f345fc 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -21,6 +21,7 @@ vignettes/ ^cran-comments\.md$ ^CRAN-SUBMISSION$ inst/user2025 +inst/Psychoco2026 #inst/tinytest/ Makefile ^.devcontainer diff --git a/vignettes/Psychoco2026/Psychoco2026.qmd b/inst/Psychoco2026/Psychoco2026.qmd similarity index 100% rename from vignettes/Psychoco2026/Psychoco2026.qmd rename to inst/Psychoco2026/Psychoco2026.qmd diff --git a/vignettes/Psychoco2026/abstract.md b/inst/Psychoco2026/abstract.md similarity index 100% rename from vignettes/Psychoco2026/abstract.md rename to inst/Psychoco2026/abstract.md From 87a38376fd9683e5bc6ece53af52c23374e1a493 Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Sat, 14 Feb 2026 13:01:07 +0100 Subject: [PATCH 6/9] embed resources to facilitate standalone usage --- inst/Psychoco2026/Psychoco2026.qmd | 3 +- .../grantmcdermott/clean/_extension.yml | 13 + .../grantmcdermott/clean/clean.scss | 366 ++++++++++++++++++ .../_extensions/mcanouil/iconify/LICENSE | 21 + .../mcanouil/iconify/_extension.yml | 7 + .../mcanouil/iconify/iconify-icon.min.js | 13 + .../_extensions/mcanouil/iconify/iconify.lua | 300 ++++++++++++++ 7 files changed, 722 insertions(+), 1 deletion(-) create mode 100644 inst/Psychoco2026/_extensions/grantmcdermott/clean/_extension.yml create mode 100644 inst/Psychoco2026/_extensions/grantmcdermott/clean/clean.scss create mode 100644 inst/Psychoco2026/_extensions/mcanouil/iconify/LICENSE create mode 100644 inst/Psychoco2026/_extensions/mcanouil/iconify/_extension.yml create mode 100644 inst/Psychoco2026/_extensions/mcanouil/iconify/iconify-icon.min.js create mode 100644 inst/Psychoco2026/_extensions/mcanouil/iconify/iconify.lua diff --git a/inst/Psychoco2026/Psychoco2026.qmd b/inst/Psychoco2026/Psychoco2026.qmd index 1a1a9cbf..caa34ac4 100644 --- a/inst/Psychoco2026/Psychoco2026.qmd +++ b/inst/Psychoco2026/Psychoco2026.qmd @@ -4,8 +4,9 @@ title: "


" subtitle: Convenient and Customizable Base R Plots format: clean-revealjs: + embed-resources: true title-slide-attributes: - data-background-image: "../useR2025/img/background.png" + data-background-image: "../../vignettes/useR2025/img/background.png" data-background-size: contain execute: echo: true diff --git a/inst/Psychoco2026/_extensions/grantmcdermott/clean/_extension.yml b/inst/Psychoco2026/_extensions/grantmcdermott/clean/_extension.yml new file mode 100644 index 00000000..d45b0831 --- /dev/null +++ b/inst/Psychoco2026/_extensions/grantmcdermott/clean/_extension.yml @@ -0,0 +1,13 @@ +title: clean +author: Grant McDermott +version: 1.3.0 +quarto-required: ">=1.3.0" +contributes: + formats: + revealjs: + theme: [default, clean.scss] + menu: + side: left + slide-number: true + date-format: long + diff --git a/inst/Psychoco2026/_extensions/grantmcdermott/clean/clean.scss b/inst/Psychoco2026/_extensions/grantmcdermott/clean/clean.scss new file mode 100644 index 00000000..910d3021 --- /dev/null +++ b/inst/Psychoco2026/_extensions/grantmcdermott/clean/clean.scss @@ -0,0 +1,366 @@ +/*-- scss:defaults --*/ + +// Custom colours and variables + +$jet: #131516; +$accent: #5195a3; +$accent2: #f51459; +// $accent2: #e64173; +$right-arrow: "\2192"; // Unicode character for right arrow + +// fonts + +/* +Note: This theme uses the Roboto font family, which it imports from Google + Fonts to ensure consistent weighting in addition to availability. While + you can use a local installation of Roboto, this is generally not + recommended since the weighting will likely be wrong (probably too + light). OTOH, importing from Google Fonts can cause some issues in + certain secure environments due the external CDN (see: + https://github.com/grantmcdermott/quarto-revealjs-clean/issues/7). If + that's the case for you, simply comment out the `@import url(...)` line + below and it will default for the default Sans Serif font on your system + (e.g., Helvetica on a Mac). Circling back to the earlier point about + preserving consistent font weights, you may also wish to remove "Roboto" + from the choice set if the family is installed locally. +*/ +@import url('https://fonts.googleapis.com/css?family=Roboto:200,200i,300,300i,350,350i,400,400i&display=swap'); + +$font-family-sans-serif: "Roboto", sans-serif !default; +$presentation-heading-font: "Roboto", sans-serif !default; + +$presentation-heading-color: $jet !default; +$presentation-heading-font-weight: lighter; +//$presentation-heading-line-height: 2; +//$presentation-block-margin: 28px; +$presentation-font-size-root: 32px; + +// colors +//$body-bg: #f0f1eb !default; +$body-color: $jet !default; +$link-color: $accent !default; +$selection-bg: #26351c !default; + + +/*-- scss:rules --*/ + +.reveal a { + line-height: 1.5em; +} + +.reveal p { + // font-weight: 300; + font-weight: lighter; + margin-top: 1.25em; +} + +// title and headings + +#title-slide { + text-align: left; + + .title { + color: $body-color; + font-size: 1.4em; + // font-weight: 350; + font-weight: lighter; + } + + .subtitle { + color: $accent; + font-style: italic; + margin-top: 0em; + font-weight: lighter; + } + + .institute, + .quarto-title-affiliation, + .quarto-title-author-email { + font-style: italic; + // font-size: 80%; + // color: #7F7F7F; + } + + .author, + .quarto-title-author-name { + color: $body-color; + } + + .quarto-title-authors { + display: flex; + justify-content: left; + + .quarto-title-author { + padding-left: 0em; + padding-right: 0em; + width: 100%; + } + } + +} + + +.reveal h2 { + // font-weight: 350; + font-weight: lighter; + font-size: 1.4em; +} + +.reveal h3 { + color: $accent; + font-style: italic; + // font-weight: 350; + font-weight: lighter; + font-size: 0.95em; +} + +.reveal h4 { + color: $accent2; + // font-weight: 350; + font-weight: normal; + margin-top: 1.25em; +} + +// alerts etc. + +.alert { + color: $accent2; +} + +.fg { + color: var(--col, $jet); +} + +.bg { + background-color: var(--col, #fff); + padding: 0.1em; + border-radius: 5px; + display: inline-block; +} + +// lists + +// Unordered lists + +.reveal ul { + // font-weight: 300; + font-weight: lighter; + padding-left: 16px; + + li::marker { + color: mix($accent, white, 70%); + } +} + +.reveal ul ul { + list-style: none; + + li:before { + content: $right-arrow; + color: mix($accent, white, 60%); + display: inline-block; + width: 1em; + margin-left: -1em; + margin-right: 0.5em; + } +} + +// Ordered lists + +.reveal ol { + // font-weight: 300; + font-weight: lighter; + padding-left: 16px; + + li::marker { + color: $accent; + } +} + +// Move "hamburger" menu button to top right + +.reveal .slide-menu-button { + position: fixed; + top: 6px; + right: 0; + display: flex; + justify-content: flex-end; + align-items: flex-start; + pointer-events: none; +} + +.reveal .slide-menu-button > * { + pointer-events: auto; +} + +// Same for chalkboard buttons (with an offset) + +.reveal .slide-chalkboard-buttons { + position: fixed; + top: 12px; + right: 24px; + display: flex; + justify-content: flex-end; + align-items: flex-start; + pointer-events: none; +} + +.reveal .slide-chalkboard-buttons > * { + pointer-events: auto; +} + +// Logo to the bottom-left +.slide-logo { + display: block !important; + position: fixed !important; + bottom: 0 !important; + left: 10px !important; + // max-width: 150px; // Adjust if necessary + // max-height: 50px; + // width: auto !important; + height: 150px !important; + // width: 150px !important; + max-width: unset !important; + max-height: unset !important; + color: $body-color !important; +} + +// Also need to enforce slide numbers at bottom-right (if logo is present) +.slide-number, .reveal.has-logo .slide-number { + bottom: 6px !important; + right: 10px !important; + top: unset !important; + color: #777777 !important; +} + +// Beamer-style button link environment + +.button { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: 400; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + background-color: $accent; + border: 1px solid $accent; + color: #fff !important; + text-decoration: none; + border-radius: 4px; + transition: all 0.2s ease-in-out; +} + +.button:hover { + background-color: #0056b3; + border-color: #0056b3; +} + +.button::before { + content: "▶"; + margin-right: 5px; +} + +// tables + +.reveal table { + // height: auto; /* Adjust table width to fit content up to the available slide space */ + margin: auto; + border-collapse: collapse; + border-spacing: 0; + font-size: 0.8em; +} + +.reveal table th, +.reveal table td { + border: none; /* Remove internal row lines */ + padding: .23em .46em; /* Adjust padding as needed */ + text-align: left; /* Adjust text alignment as needed */ + font-weight: lighter; /* Lighter font weight for main table text */ +} + +/* Add top border to first row and bottom border to last row only */ +.reveal table thead th, +.reveal .slides table tr:first-child td { + border-top: 2px solid #D3D3D3; +} + +.reveal table thead th, +.reveal .slides table tr:last-child td, +.reveal .slides table { + border-bottom: 2px solid #D3D3D3; +} + +/* Make column headers bold */ +.reveal table thead th { + font-weight: bold; +} + +/* Styling table captions */ +.reveal table caption { + color: #666666; /* Dark grey color for the caption */ + font-variant: small-caps; /* Use small caps for the caption text */ +} + +// Special catch for etable environment to ensure these table images +// don't overflow the slide. +// See: https://lrberge.github.io/fixest/articles/etable_new_features.html + +.etable { + width: 100%; + height: calc(100% - 3em); /* Adjust 3em based on the height of your header, if necessary */ + display: flex; + align-items: center; + justify-content: center; +} + +.etable img { + max-width: 100%; + max-height: 100%; + width: auto; + height: auto; + object-fit: contain; +} + +// Change the relative widths of `output-location: column`. +// See: https://github.com/grantmcdermott/quarto-revealjs-clean/pull/16 +// Example usage: +// ```{python} +// #| echo: true +// #| output-location: column +// #| classes: columns3070 +// +// ``` +.reveal .columns3070 > div.column:first-child { + width: 30%; +} +.reveal .columns3070 div.column:not(:first-child) { + width: 70%; +} +.reveal .columns7030 > div.column:first-child { + width: 70%; +} +.reveal .columns7030 div.column:not(:first-child) { + width: 30%; +} +.reveal .columns4060 > div.column:first-child { + width: 40%; +} +.reveal .columns4060 div.column:not(:first-child) { + width: 60%; +} +.reveal .columns6040 > div.column:first-child { + width: 60%; +} +.reveal .columns6040 div.column:not(:first-child) { + width: 40%; +} + +// // inline title fig (https://stackoverflow.com/a/74100740/4115816) +// .center{ +// margin: 0!important; +// height: 1.4em; +// } \ No newline at end of file diff --git a/inst/Psychoco2026/_extensions/mcanouil/iconify/LICENSE b/inst/Psychoco2026/_extensions/mcanouil/iconify/LICENSE new file mode 100644 index 00000000..43fb0c20 --- /dev/null +++ b/inst/Psychoco2026/_extensions/mcanouil/iconify/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Mickaël Canouil + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/inst/Psychoco2026/_extensions/mcanouil/iconify/_extension.yml b/inst/Psychoco2026/_extensions/mcanouil/iconify/_extension.yml new file mode 100644 index 00000000..4222542e --- /dev/null +++ b/inst/Psychoco2026/_extensions/mcanouil/iconify/_extension.yml @@ -0,0 +1,7 @@ +title: Iconify +author: Mickaël Canouil +version: 3.0.1 +quarto-required: ">=1.5.57" +contributes: + shortcodes: + - iconify.lua diff --git a/inst/Psychoco2026/_extensions/mcanouil/iconify/iconify-icon.min.js b/inst/Psychoco2026/_extensions/mcanouil/iconify/iconify-icon.min.js new file mode 100644 index 00000000..00cee269 --- /dev/null +++ b/inst/Psychoco2026/_extensions/mcanouil/iconify/iconify-icon.min.js @@ -0,0 +1,13 @@ +/** +* (c) Iconify +* +* For the full copyright and license information, please view the license.txt +* files at https://github.com/iconify/iconify +* +* Licensed under MIT. +* Source: https://github.com/iconify/code/tree/gh-pages/iconify-icon +* +* @license MIT +* @version 3.0.0 +*/ +!function(){"use strict";const t=Object.freeze({left:0,top:0,width:16,height:16}),e=Object.freeze({rotate:0,vFlip:!1,hFlip:!1}),n=Object.freeze({...t,...e}),i=Object.freeze({...n,body:"",hidden:!1}),r=Object.freeze({width:null,height:null}),o=Object.freeze({...r,...e});const s=/[\s,]+/;const c={...o,preserveAspectRatio:""};function a(t){const e={...c},n=(e,n)=>t.getAttribute(e)||n;var i;return e.width=n("width",null),e.height=n("height",null),e.rotate=function(t,e=0){const n=t.replace(/^-?[0-9.]*/,"");function i(t){for(;t<0;)t+=4;return t%4}if(""===n){const e=parseInt(t);return isNaN(e)?0:i(e)}if(n!==t){let e=0;switch(n){case"%":e=25;break;case"deg":e=90}if(e){let r=parseFloat(t.slice(0,t.length-n.length));return isNaN(r)?0:(r/=e,r%1==0?i(r):0)}}return e}(n("rotate","")),i=e,n("flip","").split(s).forEach((t=>{switch(t.trim()){case"horizontal":i.hFlip=!0;break;case"vertical":i.vFlip=!0}})),e.preserveAspectRatio=n("preserveAspectRatio",n("preserveaspectratio","")),e}const u=/^[a-z0-9]+(-[a-z0-9]+)*$/,l=(t,e,n,i="")=>{const r=t.split(":");if("@"===t.slice(0,1)){if(r.length<2||r.length>3)return null;i=r.shift().slice(1)}if(r.length>3||!r.length)return null;if(r.length>1){const t=r.pop(),n=r.pop(),o={provider:r.length>0?r[0]:i,prefix:n,name:t};return e&&!f(o)?null:o}const o=r[0],s=o.split("-");if(s.length>1){const t={provider:i,prefix:s.shift(),name:s.join("-")};return e&&!f(t)?null:t}if(n&&""===i){const t={provider:i,prefix:"",name:o};return e&&!f(t,n)?null:t}return null},f=(t,e)=>!!t&&!(!(e&&""===t.prefix||t.prefix)||!t.name);function d(t,n){const r=function(t,e){const n={};!t.hFlip!=!e.hFlip&&(n.hFlip=!0),!t.vFlip!=!e.vFlip&&(n.vFlip=!0);const i=((t.rotate||0)+(e.rotate||0))%4;return i&&(n.rotate=i),n}(t,n);for(const o in i)o in e?o in t&&!(o in r)&&(r[o]=e[o]):o in n?r[o]=n[o]:o in t&&(r[o]=t[o]);return r}function h(t,e,n){const i=t.icons,r=t.aliases||Object.create(null);let o={};function s(t){o=d(i[t]||r[t],o)}return s(e),n.forEach(s),d(t,o)}function p(t,e){const n=[];if("object"!=typeof t||"object"!=typeof t.icons)return n;t.not_found instanceof Array&&t.not_found.forEach((t=>{e(t,null),n.push(t)}));const i=function(t,e){const n=t.icons,i=t.aliases||Object.create(null),r=Object.create(null);return Object.keys(n).concat(Object.keys(i)).forEach((function t(e){if(n[e])return r[e]=[];if(!(e in r)){r[e]=null;const n=i[e]&&i[e].parent,o=n&&t(n);o&&(r[e]=[n].concat(o))}return r[e]})),r}(t);for(const r in i){const o=i[r];o&&(e(r,h(t,r,o)),n.push(r))}return n}const g={provider:"",aliases:{},not_found:{},...t};function b(t,e){for(const n in e)if(n in t&&typeof t[n]!=typeof e[n])return!1;return!0}function v(t){if("object"!=typeof t||null===t)return null;const e=t;if("string"!=typeof e.prefix||!t.icons||"object"!=typeof t.icons)return null;if(!b(t,g))return null;const n=e.icons;for(const t in n){const e=n[t];if(!t||"string"!=typeof e.body||!b(e,i))return null}const r=e.aliases||Object.create(null);for(const t in r){const e=r[t],o=e.parent;if(!t||"string"!=typeof o||!n[o]&&!r[o]||!b(e,i))return null}return e}const m=Object.create(null);function y(t,e){const n=m[t]||(m[t]=Object.create(null));return n[e]||(n[e]=function(t,e){return{provider:t,prefix:e,icons:Object.create(null),missing:new Set}}(t,e))}function x(t,e){return v(e)?p(e,((e,n)=>{n?t.icons[e]=n:t.missing.add(e)})):[]}function _(t,e){let n=[];return("string"==typeof t?[t]:Object.keys(m)).forEach((t=>{("string"==typeof t&&"string"==typeof e?[e]:Object.keys(m[t]||{})).forEach((e=>{const i=y(t,e);n=n.concat(Object.keys(i.icons).map((n=>(""!==t?"@"+t+":":"")+e+":"+n)))}))})),n}let w=!1;function k(t){return"boolean"==typeof t&&(w=t),w}function A(t){const e="string"==typeof t?l(t,!0,w):t;if(e){const t=y(e.provider,e.prefix),n=e.name;return t.icons[n]||(t.missing.has(n)?null:void 0)}}function j(t,e){const n=l(t,!0,w);if(!n)return!1;const i=y(n.provider,n.prefix);return e?function(t,e,n){try{if("string"==typeof n.body)return t.icons[e]={...n},!0}catch(t){}return!1}(i,n.name,e):(i.missing.add(n.name),!0)}function O(t,e){if("object"!=typeof t)return!1;if("string"!=typeof e&&(e=t.provider||""),w&&!e&&!t.prefix){let e=!1;return v(t)&&(t.prefix="",p(t,((t,n)=>{j(t,n)&&(e=!0)}))),e}const n=t.prefix;if(!f({prefix:n,name:"a"}))return!1;return!!x(y(e,n),t)}function C(t){return!!A(t)}function I(t){const e=A(t);return e?{...n,...e}:e}function E(t,e){t.forEach((t=>{const n=t.loaderCallbacks;n&&(t.loaderCallbacks=n.filter((t=>t.id!==e)))}))}let T=0;const F=Object.create(null);function R(t,e){F[t]=e}function S(t){return F[t]||F[""]}var L={resources:[],index:0,timeout:2e3,rotate:750,random:!1,dataAfterTimeout:!1};function P(t,e,n,i){const r=t.resources.length,o=t.random?Math.floor(Math.random()*r):t.index;let s;if(t.random){let e=t.resources.slice(0);for(s=[];e.length>1;){const t=Math.floor(Math.random()*e.length);s.push(e[t]),e=e.slice(0,t).concat(e.slice(t+1))}s=s.concat(e)}else s=t.resources.slice(o).concat(t.resources.slice(0,o));const c=Date.now();let a,u="pending",l=0,f=null,d=[],h=[];function p(){f&&(clearTimeout(f),f=null)}function g(){"pending"===u&&(u="aborted"),p(),d.forEach((t=>{"pending"===t.status&&(t.status="aborted")})),d=[]}function b(t,e){e&&(h=[]),"function"==typeof t&&h.push(t)}function v(){u="failed",h.forEach((t=>{t(void 0,a)}))}function m(){d.forEach((t=>{"pending"===t.status&&(t.status="aborted")})),d=[]}function y(){if("pending"!==u)return;p();const i=s.shift();if(void 0===i)return d.length?void(f=setTimeout((()=>{p(),"pending"===u&&(m(),v())}),t.timeout)):void v();const r={status:"pending",resource:i,callback:(e,n)=>{!function(e,n,i){const r="success"!==n;switch(d=d.filter((t=>t!==e)),u){case"pending":break;case"failed":if(r||!t.dataAfterTimeout)return;break;default:return}if("abort"===n)return a=i,void v();if(r)return a=i,void(d.length||(s.length?y():v()));if(p(),m(),!t.random){const n=t.resources.indexOf(e.resource);-1!==n&&n!==t.index&&(t.index=n)}u="completed",h.forEach((t=>{t(i)}))}(r,e,n)}};d.push(r),l++,f=setTimeout(y,t.rotate),n(i,e,r.callback)}return"function"==typeof i&&h.push(i),setTimeout(y),function(){return{startTime:c,payload:e,status:u,queriesSent:l,queriesPending:d.length,subscribe:b,abort:g}}}function M(t){const e={...L,...t};let n=[];function i(){n=n.filter((t=>"pending"===t().status))}return{query:function(t,r,o){const s=P(e,t,r,((t,e)=>{i(),o&&o(t,e)}));return n.push(s),s},find:function(t){return n.find((e=>t(e)))||null},setIndex:t=>{e.index=t},getIndex:()=>e.index,cleanup:i}}function N(t){let e;if("string"==typeof t.resources)e=[t.resources];else if(e=t.resources,!(e instanceof Array&&e.length))return null;return{resources:e,path:t.path||"/",maxURL:t.maxURL||500,rotate:t.rotate||750,timeout:t.timeout||5e3,random:!0===t.random,index:t.index||0,dataAfterTimeout:!1!==t.dataAfterTimeout}}const z=Object.create(null),Q=["https://api.simplesvg.com","https://api.unisvg.com"],q=[];for(;Q.length>0;)1===Q.length||Math.random()>.5?q.push(Q.shift()):q.push(Q.pop());function U(t,e){const n=N(e);return null!==n&&(z[t]=n,!0)}function D(t){return z[t]}function H(){return Object.keys(z)}function J(){}z[""]=N({resources:["https://api.iconify.design"].concat(q)});const $=Object.create(null);function B(t,e,n){let i,r;if("string"==typeof t){const e=S(t);if(!e)return n(void 0,424),J;r=e.send;const o=function(t){if(!$[t]){const e=D(t);if(!e)return;const n={config:e,redundancy:M(e)};$[t]=n}return $[t]}(t);o&&(i=o.redundancy)}else{const e=N(t);if(e){i=M(e);const n=S(t.resources?t.resources[0]:"");n&&(r=n.send)}}return i&&r?i.query(e,r,n)().abort:(n(void 0,424),J)}function G(){}function V(t){t.iconsLoaderFlag||(t.iconsLoaderFlag=!0,setTimeout((()=>{t.iconsLoaderFlag=!1,function(t){t.pendingCallbacksFlag||(t.pendingCallbacksFlag=!0,setTimeout((()=>{t.pendingCallbacksFlag=!1;const e=t.loaderCallbacks?t.loaderCallbacks.slice(0):[];if(!e.length)return;let n=!1;const i=t.provider,r=t.prefix;e.forEach((e=>{const o=e.icons,s=o.pending.length;o.pending=o.pending.filter((e=>{if(e.prefix!==r)return!0;const s=e.name;if(t.icons[s])o.loaded.push({provider:i,prefix:r,name:s});else{if(!t.missing.has(s))return n=!0,!0;o.missing.push({provider:i,prefix:r,name:s})}return!1})),o.pending.length!==s&&(n||E([t],e.id),e.callback(o.loaded.slice(0),o.missing.slice(0),o.pending.slice(0),e.abort))}))})))}(t)})))}function K(t,e,n){function i(){const n=t.pendingIcons;e.forEach((e=>{n&&n.delete(e),t.icons[e]||t.missing.add(e)}))}if(n&&"object"==typeof n)try{if(!x(t,n).length)return void i()}catch(t){console.error(t)}i(),V(t)}function W(t,e){t instanceof Promise?t.then((t=>{e(t)})).catch((()=>{e(null)})):e(t)}function X(t,e){t.iconsToLoad?t.iconsToLoad=t.iconsToLoad.concat(e).sort():t.iconsToLoad=e,t.iconsQueueFlag||(t.iconsQueueFlag=!0,setTimeout((()=>{t.iconsQueueFlag=!1;const{provider:e,prefix:n}=t,i=t.iconsToLoad;if(delete t.iconsToLoad,!i||!i.length)return;const r=t.loadIcon;if(t.loadIcons&&(i.length>1||!r))return void W(t.loadIcons(i,n,e),(e=>{K(t,i,e)}));if(r)return void i.forEach((i=>{W(r(i,n,e),(e=>{K(t,[i],e?{prefix:n,icons:{[i]:e}}:null)}))}));const{valid:o,invalid:s}=function(t){const e=[],n=[];return t.forEach((t=>{(t.match(u)?e:n).push(t)})),{valid:e,invalid:n}}(i);if(s.length&&K(t,s,null),!o.length)return;const c=n.match(u)?S(e):null;if(!c)return void K(t,o,null);c.prepare(e,n,o).forEach((n=>{B(e,n,(e=>{K(t,n.icons,e)}))}))})))}const Y=(t,e)=>{const n=function(t,e=!0,n=!1){const i=[];return t.forEach((t=>{const r="string"==typeof t?l(t,e,n):t;r&&i.push(r)})),i}(t,!0,k()),i=function(t){const e={loaded:[],missing:[],pending:[]},n=Object.create(null);t.sort(((t,e)=>t.provider!==e.provider?t.provider.localeCompare(e.provider):t.prefix!==e.prefix?t.prefix.localeCompare(e.prefix):t.name.localeCompare(e.name)));let i={provider:"",prefix:"",name:""};return t.forEach((t=>{if(i.name===t.name&&i.prefix===t.prefix&&i.provider===t.provider)return;i=t;const r=t.provider,o=t.prefix,s=t.name,c=n[r]||(n[r]=Object.create(null)),a=c[o]||(c[o]=y(r,o));let u;u=s in a.icons?e.loaded:""===o||a.missing.has(s)?e.missing:e.pending;const l={provider:r,prefix:o,name:s};u.push(l)})),e}(n);if(!i.pending.length){let t=!0;return e&&setTimeout((()=>{t&&e(i.loaded,i.missing,i.pending,G)})),()=>{t=!1}}const r=Object.create(null),o=[];let s,c;return i.pending.forEach((t=>{const{provider:e,prefix:n}=t;if(n===c&&e===s)return;s=e,c=n,o.push(y(e,n));const i=r[e]||(r[e]=Object.create(null));i[n]||(i[n]=[])})),i.pending.forEach((t=>{const{provider:e,prefix:n,name:i}=t,o=y(e,n),s=o.pendingIcons||(o.pendingIcons=new Set);s.has(i)||(s.add(i),r[e][n].push(i))})),o.forEach((t=>{const e=r[t.provider][t.prefix];e.length&&X(t,e)})),e?function(t,e,n){const i=T++,r=E.bind(null,n,i);if(!e.pending.length)return r;const o={id:i,icons:e,callback:t,abort:r};return n.forEach((t=>{(t.loaderCallbacks||(t.loaderCallbacks=[])).push(o)})),r}(e,i,o):G},Z=t=>new Promise(((e,i)=>{const r="string"==typeof t?l(t,!0):t;r?Y([r||t],(o=>{if(o.length&&r){const t=A(r);if(t)return void e({...n,...t})}i(t)})):i(t)}));function tt(t){try{const e="string"==typeof t?JSON.parse(t):t;if("string"==typeof e.body)return{...e}}catch(t){}}let et=!1;try{et=0===navigator.vendor.indexOf("Apple")}catch(t){}const nt=/(-?[0-9.]*[0-9]+[0-9.]*)/g,it=/^-?[0-9.]*[0-9]+[0-9.]*$/g;function rt(t,e,n){if(1===e)return t;if(n=n||100,"number"==typeof t)return Math.ceil(t*e*n)/n;if("string"!=typeof t)return t;const i=t.split(nt);if(null===i||!i.length)return t;const r=[];let o=i.shift(),s=it.test(o);for(;;){if(s){const t=parseFloat(o);isNaN(t)?r.push(o):r.push(Math.ceil(t*e*n)/n)}else r.push(o);if(o=i.shift(),void 0===o)return r.join("");s=!s}}const ot=t=>"unset"===t||"undefined"===t||"none"===t;function st(t,e){const i={...n,...t},r={...o,...e},s={left:i.left,top:i.top,width:i.width,height:i.height};let c=i.body;[i,r].forEach((t=>{const e=[],n=t.hFlip,i=t.vFlip;let r,o=t.rotate;switch(n?i?o+=2:(e.push("translate("+(s.width+s.left).toString()+" "+(0-s.top).toString()+")"),e.push("scale(-1 1)"),s.top=s.left=0):i&&(e.push("translate("+(0-s.left).toString()+" "+(s.height+s.top).toString()+")"),e.push("scale(1 -1)"),s.top=s.left=0),o<0&&(o-=4*Math.floor(o/4)),o%=4,o){case 1:r=s.height/2+s.top,e.unshift("rotate(90 "+r.toString()+" "+r.toString()+")");break;case 2:e.unshift("rotate(180 "+(s.width/2+s.left).toString()+" "+(s.height/2+s.top).toString()+")");break;case 3:r=s.width/2+s.left,e.unshift("rotate(-90 "+r.toString()+" "+r.toString()+")")}o%2==1&&(s.left!==s.top&&(r=s.left,s.left=s.top,s.top=r),s.width!==s.height&&(r=s.width,s.width=s.height,s.height=r)),e.length&&(c=function(t,e,n){const i=function(t,e="defs"){let n="";const i=t.indexOf("<"+e);for(;i>=0;){const r=t.indexOf(">",i),o=t.indexOf("",o);if(-1===s)break;n+=t.slice(r+1,o).trim(),t=t.slice(0,i).trim()+t.slice(s+1)}return{defs:n,content:t}}(t);return r=i.defs,o=e+i.content+n,r?""+r+""+o:o;var r,o}(c,'',""))}));const a=r.width,u=r.height,l=s.width,f=s.height;let d,h;null===a?(h=null===u?"1em":"auto"===u?f:u,d=rt(h,l/f)):(d="auto"===a?l:a,h=null===u?rt(d,f/l):"auto"===u?f:u);const p={},g=(t,e)=>{ot(e)||(p[t]=e.toString())};g("width",d),g("height",h);const b=[s.left,s.top,l,f];return p.viewBox=b.join(" "),{attributes:p,viewBox:b,body:c}}function ct(t,e){let n=-1===t.indexOf("xlink:")?"":' xmlns:xlink="http://www.w3.org/1999/xlink"';for(const t in e)n+=" "+t+'="'+e[t]+'"';return'"+t+""}function at(t){return'url("'+function(t){return"data:image/svg+xml,"+function(t){return t.replace(/"/g,"'").replace(/%/g,"%25").replace(/#/g,"%23").replace(//g,"%3E").replace(/\s+/g," ")}(t)}(t)+'")'}let ut=(()=>{let t;try{if(t=fetch,"function"==typeof t)return t}catch(t){}})();function lt(t){ut=t}function ft(){return ut}const dt={prepare:(t,e,n)=>{const i=[],r=function(t,e){const n=D(t);if(!n)return 0;let i;if(n.maxURL){let t=0;n.resources.forEach((e=>{const n=e;t=Math.max(t,n.length)}));const r=e+".json?icons=";i=n.maxURL-t-n.path.length-r.length}else i=0;return i}(t,e),o="icons";let s={type:o,provider:t,prefix:e,icons:[]},c=0;return n.forEach(((n,a)=>{c+=n.length+1,c>=r&&a>0&&(i.push(s),s={type:o,provider:t,prefix:e,icons:[]},c=n.length),s.icons.push(n)})),i.push(s),i},send:(t,e,n)=>{if(!ut)return void n("abort",424);let i=function(t){if("string"==typeof t){const e=D(t);if(e)return e.path}return"/"}(e.provider);switch(e.type){case"icons":{const t=e.prefix,n=e.icons.join(",");i+=t+".json?"+new URLSearchParams({icons:n}).toString();break}case"custom":{const t=e.uri;i+="/"===t.slice(0,1)?t.slice(1):t;break}default:return void n("abort",400)}let r=503;ut(t+i).then((t=>{const e=t.status;if(200===e)return r=501,t.json();setTimeout((()=>{n(function(t){return 404===t}(e)?"abort":"next",e)}))})).then((t=>{"object"==typeof t&&null!==t?setTimeout((()=>{n("success",t)})):setTimeout((()=>{404===t?n("abort",t):n("next",r)}))})).catch((()=>{n("next",r)}))}};function ht(t,e,n){y(n||"",e).loadIcons=t}function pt(t,e,n){y(n||"",e).loadIcon=t}const gt="data-style";let bt="";function vt(t){bt=t}function mt(t,e){let n=Array.from(t.childNodes).find((t=>t.hasAttribute&&t.hasAttribute(gt)));n||(n=document.createElement("style"),n.setAttribute(gt,gt),t.appendChild(n)),n.textContent=":host{display:inline-block;vertical-align:"+(e?"-0.125em":"0")+"}span,svg{display:block;margin:auto}"+bt}const yt={"background-color":"currentColor"},xt={"background-color":"transparent"},_t={image:"var(--svg)",repeat:"no-repeat",size:"100% 100%"},wt={"-webkit-mask":yt,mask:yt,background:xt};for(const t in wt){const e=wt[t];for(const n in _t)e[t+"-"+n]=_t[n]}function kt(t){return t?t+(t.match(/^[-0-9.]+$/)?"px":""):"inherit"}let At;function jt(t){return void 0===At&&function(){try{At=window.trustedTypes.createPolicy("iconify",{createHTML:t=>t})}catch(t){At=null}}(),At?At.createHTML(t):t}function Ot(t){return Array.from(t.childNodes).find((t=>{const e=t.tagName&&t.tagName.toUpperCase();return"SPAN"===e||"SVG"===e}))}function Ct(t,e){const i=e.icon.data,r=e.customisations,o=st(i,r);r.preserveAspectRatio&&(o.attributes.preserveAspectRatio=r.preserveAspectRatio);const s=e.renderedMode;let c;if("svg"===s)c=function(t){const e=document.createElement("span"),n=t.attributes;let i="";n.width||(i="width: inherit;"),n.height||(i+="height: inherit;"),i&&(n.style=i);const r=ct(t.body,n);return e.innerHTML=jt(r),e.firstChild}(o);else c=function(t,e,n){const i=document.createElement("span");let r=t.body;-1!==r.indexOf("{this._check()})))}_check(){if(!this._checkQueued)return;this._checkQueued=!1;const t=this._state,e=this.getAttribute("icon");if(e!==t.icon.value)return void this._iconChanged(e);if(!t.rendered||!this._visible)return;const n=this.getAttribute("mode"),i=a(this);t.attrMode===n&&!function(t,e){for(const n in c)if(t[n]!==e[n])return!0;return!1}(t.customisations,i)&&Ot(this._shadowRoot)||this._renderIcon(t.icon,i,n)}_iconChanged(t){const e=function(t,e){if("object"==typeof t)return{data:tt(t),value:t};if("string"!=typeof t)return{value:t};if(t.includes("{")){const e=tt(t);if(e)return{data:e,value:t}}const n=l(t,!0,!0);if(!n)return{value:t};const i=A(n);if(void 0!==i||!n.prefix)return{value:t,name:n,data:i};const r=Y([n],(()=>e(t,n,A(n))));return{value:t,name:n,loading:r}}(t,((t,e,n)=>{const i=this._state;if(i.rendered||this.getAttribute("icon")!==t)return;const r={value:t,name:e,data:n};r.data?this._gotIconData(r):i.icon=r}));e.data?this._gotIconData(e):this._state=It(e,this._state.inline,this._state)}_forceRender(){if(this._visible)this._queueCheck();else{const t=Ot(this._shadowRoot);t&&this._shadowRoot.removeChild(t)}}_gotIconData(t){this._checkQueued=!1,this._renderIcon(t,a(this),this.getAttribute("mode"))}_renderIcon(t,e,n){const i=function(t,e){switch(e){case"svg":case"bg":case"mask":return e}return"style"===e||!et&&-1!==t.indexOf("{const e=t.some((t=>t.isIntersecting));e!==this._visible&&(this._visible=e,this._forceRender())})),this._observer.observe(this)}catch(t){if(this._observer){try{this._observer.disconnect()}catch(t){}this._observer=null}}}stopObserver(){this._observer&&(this._observer.disconnect(),this._observer=null,this._visible=!0,this._connected&&this._forceRender())}};r.forEach((t=>{t in o.prototype||Object.defineProperty(o.prototype,t,{get:function(){return this.getAttribute(t)},set:function(e){null!==e?this.setAttribute(t,e):this.removeAttribute(t)}})}));const s=function(){let t;R("",dt),k(!0);try{t=window}catch(t){}if(t){if(void 0!==t.IconifyPreload){const e=t.IconifyPreload,n="Invalid IconifyPreload syntax.";"object"==typeof e&&null!==e&&(e instanceof Array?e:[e]).forEach((t=>{try{("object"!=typeof t||null===t||t instanceof Array||"object"!=typeof t.icons||"string"!=typeof t.prefix||!O(t))&&console.error(n)}catch(t){console.error(n)}}))}if(void 0!==t.IconifyProviders){const e=t.IconifyProviders;if("object"==typeof e&&null!==e)for(const t in e){const n="IconifyProviders["+t+"] is invalid.";try{const i=e[t];if("object"!=typeof i||!i||void 0===i.resources)continue;U(t,i)||console.error(n)}catch(t){console.error(n)}}}}return{iconLoaded:C,getIcon:I,listIcons:_,addIcon:j,addCollection:O,calculateSize:rt,buildIcon:st,iconToHTML:ct,svgToURL:at,loadIcons:Y,loadIcon:Z,addAPIProvider:U,setCustomIconLoader:pt,setCustomIconsLoader:ht,appendCustomStyle:vt,_api:{getAPIConfig:D,setAPIModule:R,sendAPIQuery:B,setFetch:lt,getFetch:ft,listAPIProviders:H}}}();for(const t in s)o[t]=o.prototype[t]=s[t];e.define(t,o)}()}(); diff --git a/inst/Psychoco2026/_extensions/mcanouil/iconify/iconify.lua b/inst/Psychoco2026/_extensions/mcanouil/iconify/iconify.lua new file mode 100644 index 00000000..24bcc873 --- /dev/null +++ b/inst/Psychoco2026/_extensions/mcanouil/iconify/iconify.lua @@ -0,0 +1,300 @@ +--[[ +# MIT License +# +# Copyright (c) 2025 Mickaël Canouil +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +]] + +--- @type function +local stringify = pandoc.utils.stringify + +--- Flag to track if deprecation warning has been shown +--- @type boolean +local deprecation_warning_shown = false + +--- Ensure Iconify HTML dependencies are included. +--- @return nil +local function ensure_html_deps() + quarto.doc.add_html_dependency({ + name = 'iconify', + version = '3.0.0', + scripts = { 'iconify-icon.min.js' } + }) +end + +--- Check if a string is empty or nil. +--- @param s string|nil +--- @return boolean +local function is_empty(s) + return s == nil or s == '' +end + +--- Check for deprecated top-level iconify configuration and emit warning. +--- @param meta table Document metadata table +--- @param key string The configuration key being accessed +--- @return string|nil The value from deprecated config, or nil if not found +local function check_deprecated_config(meta, key) + if not is_empty(meta['iconify']) and not is_empty(meta['iconify'][key]) then + if not deprecation_warning_shown then + quarto.log.warning( + 'Top-level "iconify" configuration is deprecated. ' .. + 'Please use:\n' .. + 'extensions:\n' .. + ' iconify:\n' .. + ' ' .. key .. ': value' + ) + deprecation_warning_shown = true + end + return stringify(meta['iconify'][key]) + end + return nil +end + +--- Validate and convert size keyword to CSS font-size. +--- @param size string|nil +--- @return string +local function is_valid_size(size) + if is_empty(size) then + return '' + end + --- @type table + local size_table = { + ['tiny'] = '0.5em', + ['scriptsize'] = '0.7em', + ['footnotesize'] = '0.8em', + ['small'] = '0.9em', + ['normalsize'] = '1em', + ['large'] = '1.2em', + ['Large'] = '1.5em', + ['LARGE'] = '1.75em', + ['huge'] = '2em', + ['Huge'] = '2.5em', + ['1x'] = '1em', + ['2x'] = '2em', + ['3x'] = '3em', + ['4x'] = '4em', + ['5x'] = '5em', + ['6x'] = '6em', + ['7x'] = '7em', + ['8x'] = '8em', + ['9x'] = '9em', + ['10x'] = '10em', + ['2xs'] = '0.625em', + ['xs'] = '0.75em', + ['sm'] = '0.875em', + ['lg'] = '1.25em', + ['xl'] = '1.5em', + ['2xl'] = '2em' + } + for key, value in pairs(size_table) do + if key == size then + return 'font-size: ' .. value .. ';' + end + end + return 'font-size: ' .. size .. ';' +end + +--- Get iconify option from arguments or metadata. +--- @param x string The option name to retrieve +--- @param arg table Arguments table containing options +--- @param meta table Document metadata table +--- @return string The option value as a string +local function get_iconify_options(x, arg, meta) + --- @type string + local arg_value = stringify(arg[x]) + + -- Return argument value if provided + if not is_empty(arg_value) then + return arg_value + end + + -- Check new nested structure: extensions.iconify.x + if not is_empty(meta['extensions']) and + not is_empty(meta['extensions']['iconify']) and + not is_empty(meta['extensions']['iconify'][x]) then + return stringify(meta['extensions']['iconify'][x]) + end + + -- Check deprecated top-level structure: iconify.x (with warning) + local deprecated_value = check_deprecated_config(meta, x) + if deprecated_value then + return deprecated_value + end + + return arg_value +end + +--- Render an Iconify icon as a Pandoc RawInline for HTML output. +--- @param args table Icon arguments (icon set and name) +--- @param kwargs table Key-value options for the icon +--- @param meta table Document metadata +--- @return any Pandoc RawInline for HTML or Pandoc Null for other formats +function iconify(args, kwargs, meta) + -- detect html (excluding epub which won't handle fa) + if quarto.doc.is_format('html:js') then + ensure_html_deps() + --- @type string + local icon = stringify(args[1]) + --- @type string + local set = 'octicon' + + -- Check new nested structure for default set + if not is_empty(meta['extensions']) and + not is_empty(meta['extensions']['iconify']) and + not is_empty(meta['extensions']['iconify']['set']) then + set = stringify(meta['extensions']['iconify']['set']) + else + -- Check deprecated top-level structure for default set (with warning) + local deprecated_set = check_deprecated_config(meta, 'set') + if deprecated_set then + set = deprecated_set + end + end + + if #args > 1 and string.find(stringify(args[2]), ':') then + quarto.log.warning( + 'Use "set:icon" or "set icon" syntax, not both! ' .. + 'Using "set:icon" syntax and discarding first argument!' + ) + icon = stringify(args[2]) + end + + if string.find(icon, ':') then + set = string.sub(icon, 1, string.find(icon, ':') - 1) + icon = string.sub(icon, string.find(icon, ':') + 1) + elseif #args > 1 then + set = icon + icon = stringify(args[2]) + end + + --- @type string + local attributes = ' icon="' .. set .. ':' .. icon .. '"' + --- @type string + local default_label = 'Icon ' .. icon .. ' from ' .. set .. ' Iconify.design set.' + + --- @type string + local size = is_valid_size(get_iconify_options('size', kwargs, meta)) + --- @type string + local style = get_iconify_options('style', kwargs, meta) + + if is_empty(style) and not is_empty(size) then + attributes = attributes .. ' style="' .. size .. '"' + elseif not is_empty(style) and not is_empty(size) then + attributes = attributes .. ' style="' .. style .. ';' .. size .. '"' + elseif not is_empty(style) then + attributes = attributes .. ' style="' .. style .. '"' + end + + --- @type string + local aria_label = stringify(kwargs['label']) + if is_empty(aria_label) then + aria_label = ' aria-label="' .. default_label .. '"' + else + aria_label = ' aria-label="' .. aria_label .. '"' + end + + --- @type string + local title = stringify(kwargs['title']) + if is_empty(title) then + title = ' title="' .. default_label .. '"' + else + title = ' title="' .. title .. '"' + end + + attributes = attributes .. aria_label .. title + + --- @type string + local width = get_iconify_options('width', kwargs, meta) + if not is_empty(width) and is_empty(size) then + attributes = attributes .. ' width="' .. width .. '"' + end + --- @type string + local height = get_iconify_options('height', kwargs, meta) + if not is_empty(height) and is_empty(size) then + attributes = attributes .. ' height="' .. height .. '"' + end + --- @type string + local flip = get_iconify_options('flip', kwargs, meta) + if not is_empty(flip) then + attributes = attributes .. ' flip="' .. flip.. '"' + end + --- @type string + local rotate = get_iconify_options('rotate', kwargs, meta) + if not is_empty(rotate) then + attributes = attributes .. ' rotate="' .. rotate .. '"' + end + + --- @type string + local inline = get_iconify_options('inline', kwargs, meta) + if is_empty(inline) or inline ~= 'false' then + attributes = ' inline ' .. attributes + end + + --- @type string + local mode = get_iconify_options('mode', kwargs, meta) + --- @type table + local valid_modes = { svg = true, style = true, bg = true, mask = true } + if not is_empty(mode) and valid_modes[mode] then + attributes = attributes .. ' mode="' .. mode .. '"' + end + + return pandoc.RawInline( + 'html', + '' + ) + else + return pandoc.Null() + end +end + +--- Render Quarto icon using the iconify function with preset styling. +--- @param args table Icon arguments (ignored as we're using a preset icon) +--- @param kwargs table|nil Key-value options that might override default styling +--- @param meta table Document metadata +--- @return any Pandoc RawInline for HTML or Pandoc Null for other formats +function iconify_quarto(args, kwargs, meta) + --- @type table + local quarto_args = { 'simple-icons:quarto' } + --- @type table + local quarto_kwargs = kwargs or {} + quarto_kwargs['label'] = 'Quarto icon' + quarto_kwargs['title'] = 'Quarto icon' + --- @type string + local quarto_colour = 'color:#74aadb;' + + if not is_empty(quarto_kwargs['style']) then + --- @type string + local style = stringify(quarto_kwargs['style']) + if string.match(style, 'color:[^;]+;') then + quarto_kwargs['style'] = string.gsub(style, 'color:[^;]+;', quarto_colour) + else + quarto_kwargs['style'] = quarto_colour .. style + end + else + quarto_kwargs['style'] = quarto_colour + end + return iconify(quarto_args, quarto_kwargs, meta) +end + +--- @type table +return { + ['iconify'] = iconify, + ['quarto'] = iconify_quarto +} From b02b98408a9967ceb2ad94a0a2b89b5bd50ccd47 Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Sun, 15 Feb 2026 14:28:40 +0100 Subject: [PATCH 7/9] add original beamer slides (with banner image) --- inst/Psychoco2026/banner.png | Bin 0 -> 40092 bytes inst/Psychoco2026/slides-beamer.qmd | 388 ++++++++++++++++++++++++++++ 2 files changed, 388 insertions(+) create mode 100644 inst/Psychoco2026/banner.png create mode 100644 inst/Psychoco2026/slides-beamer.qmd diff --git a/inst/Psychoco2026/banner.png b/inst/Psychoco2026/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..ce35df80482238540a609f0ca4993423e50f936b GIT binary patch literal 40092 zcmYIv1z1z>`~FZ7K@kYkfOLlBVy`5QriS1R{HU=_2sW zy(*|Z@Qc#zg@GpsbhYdBLxylIvjV=n?Ir)pOWW1P%h%k)8szKi%VY22=xJ&0X3gX3 zVVk)w$p8Y~0VzFys^gcnI_sNd;v0-1?d@fG_Uq79ioC3`?)6Bz?DE=j^=ApirMIdt zY93^&)|6)=p7|t^YxyQQolj)#{mK*mi|xTDP)T#x2WR7Vk1Hp!sT2LNC369NdwYl! ztCZUVGtHP?)b{4^`A=edfgJ*#w(E;+LzrzoJn_eQJ-f@yAKu#dl;GOqQd72rsjasO z=!>8{wb&x4P7S9c=2+JJrmnR!Fq+c~(p|oHQ9}Z1iEV$Ex+HciP9`f+TY3WA&`%6F zy9HDd`K=;OG!oR5ab?a7WEVI=oBid$f+_+%Ms0AY+rD(CH+c=oS;EfEyWS zN}4(7)VL;3_4UPd^C!bMA~Q~ykj*-Jr@lW zyYCd;1KU}2-$~B&x`}Ys@4oY==idKX`p~F&mw{nhp|U?3JEvz8t0_LO%XTc4>4}x3 zY;3`hX-Fm1bM8Ow#_fBN|9j>M^@T6a-)^Yu(UAq>JeUjP-|CMEV<}O`u^{!DH<|2#=E&yJchds4^i&PTi7w~IX(q@SVN~k*#5qGSr$Hx zaQK>yF5_vhuhdd zbYJ}ZcWYksC$bp@z&5xp9h@{oD^vheXBm6L#d4kc-?%}77f<`4Q1_|oA!n75+*j|# zPn))0!VzzAf;%VwyGb_qI;ios%oQA9qgHI>L9UrOnvY&Pd-Yy~Xq;}@zZU_uuS8*? zd*#xj%6#zKf`*m@f7MabNj?Rr9Nm-u-PClA%q{cLHITkM-z982xlWluMX0(Z{r~+R ztiD~u{9gWA-ZJ_(qKmso|}SOR);||9=G|FkY&H7ycDtTsI6}f@bbzh&?Yp2masVS#hAIxHwI6 z;{=s_j{e7ssl(rx^D@QT{%=+{x3TSH;~!ZgIWOdEi##KfG^B21rmRt(aSLep!h zhpFYCnSekgF`%Y_1M@Pc5q(>JO`wsuJJHGc!NOrmMUv{wO0Ip*EmDXCjHoiZXc*g@7Rqx^{`i`EqH->)_@_f%AKhV0Tm+WRjDAM*&J?O+A zFjQ~P%@p$p1-U`TEy2|%L z=Kpo`HC0natUy&gL*HTe0!B4%t!sgZ-eYX@6ik2wD?mPOs2#0xnt$ezM zKftJP$myRuYodZ3g#E(vp-BMEc2dYfbmQvv?v zwTj(?v@|{iX6K(Cg75E9+tjK~(u2n=b*YH7E}mXJdtGi@Z#29KWen;~(AaOsw*Ok7 zaID|#r?Dq_@~Q@u<@V#cDLV3JhTrSK34;tySM5{=61?(ws*3WR)Y1#g@R0Fj)<|)& z)Z%UjMyiYlI&oEt2a_O4M0xM(+%^!SrX?q^XD^mz^N~4OP5wGp+rE{}iv7N{raN_g zc>huuUwMMDY2m@M_C`(GcX!5nRpR^Rgss`=-t^FfUsnB$@cA`SZ8+oYz$*FA&D86G z2ka1C>d7Qo$9jU}N8@202D@7qzR*2MWU~At9?vv3b!!1ZB~Ojn;-2*oG};mR)XT45 zKR~WIJ}d&-H~;2Y_jG(4-+|lMIy2BPWFvlsnrf@tUP@TvNzwgG(+~cNvYvMEc_^J5 zv!Y3Pk@$_AL}=hsg1!~x_=y&pCYkH7?ZGpg9C;s&^Ipw~ET~&eOV^3u!Z6sw%By8Hx*ROw=6N8-j)R zA%j-G3lL&G9E;6BXtD7sy7^qM@Ssh><6R__GY z>e}CzHn-Bf^sHF25p9(4VDU{D#^FslX6TLZud|lyype_n2hZdkZz>4ztzER^x$uQ{ zS>>T%V<}fEeJ<1z_~T`EZ_!~7Key+6$F&Qb@#I()wux+I;Q^*UZLD?=a`nOWJws#@ zvyb7GC;kYlTs)qxBDm_*_w1UTpqW)9XP*{TxU73Z%*^0ietF;AN+_}4@v)hPEq^}r zV#TGwqwpT83-C`JPip$;f{SmM4&@BzaGJJ!q5uRLnUt=-2vF%=!fSfQ(gn+VWKRt~l?gU|-4go%;r>UQ zc5e7L!+m`PWUBbx4H3$Ji^Ou_OQ~p&or}8fz|<|@hqPGLQbDbG(6)j#M7Lp?%V=j& zd`ab6)=pbR@JHekox7J(_Re#%YYLg)e54=yrx_}V&@?9ROsEI&Dp{wg_M^yraR(;Q zt%$r5jGQB1PcosgAKdH7{O-C4%fF!xM1b~_)3cekc(f?BscT!rlOG*&hdvdGt>S-I z!2eEbA_3C^*!&GB7s~xlv)W|78-RujUpxhu`*H3UtFk$CS9o=WvqlzUHXfvS(LybY zhf{M9Iw+yi2v&~838+w$u04-nNy#|6X=e_?J)1Q?2)B>t+nQOK{rpQ@A>O^Y^OX63 zK;yBw{rYW@)N+#?iRJFsDKuO!d`UD$0NYA;dt1E6PC?P|e7e)XkO{9Xg{`fpI=Nju zA3r~T$qw<+0PEv-ETz*Lk(%SkTN@*<)MVI;M?H~z3WMdCt%ItG^z@9owL(S_DaqTx z-fT%iVrEeA#-8g9sHHOOk?aT5!LBYt&NtP*=Pq}41xpMQq<6@L%zwf*#_k2rreO2i{Mo^ZTe_pX}{-qge zd8Fef%Cyb}0wqoQa!pQLKbn#)%wICm@&(Y2x~Gag%M(zZzcrQoJKZba0MMYv0c-^u zjJSgR5LYE#tL2Y$_Y06gzK0DP`W0=7XV~Pmq@u7hN;~(swc;(jm|!-NL|PzmjLE!8 zA%rW8?&@!7<>+ru!ZA4Ii4t!PP@Z^IQFTy0r*1;=Mx$*124!-DvFj9>?N@I@=?n1^ zpM^?v+|Qi!wX_9ZJo0cTRM&h2hV}1J%No}v;qb|fOi-+USipUd+IZ*{kG@J0U~nlP zswQ4^T*C#8KZzDhJxcqL2XgM%QF)rt5y7pLZ6hdnJ;;mxf%tZ!@>ou$#33}=;qrsK{Nl6Cq#Zvik z`=__J=8UI{a20fBA1ZarUxLnX>hG=5G&0Eb70x zsWuZ{W>Kw?kl07<>>FL53%vgh0rbQ>Si($~l!L;0tebq4f~5F%JatLr%eIqpW(A2T z6RA=JLBobs^#FETJpT0~%K+G8V9%v`M8&wcc zXmtM~SZ%I?z;RuC3O%1vspgHAU!J{!d-+>@ySG&6+@L;X*hu1(G{_~HK9jnJ8YD}- z!47v9Q=l?hrqF@#7N>~@g&__ofgPkU1@DtxeN641vxD1$9KcC&^ zDepZP2?1KadI~82dth_*9BBAjtl^`AMr7aAzR}aM=D)x`Y>ECb+%$NLeC?!DU?KAJ z3fcwIf>$;4M+|sKHgX1T)Hc_!E;PkMgD_GptK5x!s%)->X~unHSL z2xP}SQHG4Whi?2k3&T|&a)pN@q{Fs`;Du^RF1PLbmQ#oIiaUks!A}nosw-9tXI|3x zfZONOLJak1_DVdznw8%yLT|Rh%MAL{lAEKjCJW1R#0(Y^EVM9K-4!fyoTA)G@C-e& zc0TNDqrn(q*ymB?HH*PfZDTf-g$!-U> z;u_fR_jL0Mz!d36G@~pUR zGnDNRQ4y3;Xy3ONhky_HD@gO^k4if*_WE#-^N2mp(Qqx5Zptl65Dy>0bp*< zA6=bzt+;SG*o^mh4yN{sAdzr>$aQpPf~$UY30F}kP?yvI1Em6fj&QEM_ici1Lv;AP zhpB3Qhiic@s8D}=aEo>KC}o$KQpVV9IZemBAu&|u$Yr+0!=UQu38yaWX?#{=OHuPv zZgQFQeFO*;;_<+I4_avf07WZ@y_9U-=UkZ0wFWn9N3gtgqi%*;eiy_C;UBWo$0pv66P?sX7HCcX5<^W! zr2pztCn2+x_;dBkXSH>I?v8;x=j))C(Nz-~!e-ms7ZWDjpeMoG(xAi&} z$Jh}Vd{NIt8lPyNOIrQaNWXna5~XQdQefF<*ZT`9pj17km%z z9QxubH#TA2wF&m(&9#4AT{4}3Vos!dS0iXOnF^y?JnpnV}~Vb4K9Jui3we2?78 zVn^_Ei#fWrsOf+6d7upWFL3k`A?Fa4tz03Ht}*J#Feh*yg%P^m>r1b zfc>a2LGb}x983T0p|QS6@7o*RS^m_ZxH2T34Ered-oeu{yTm*DJ8fn%SN!3B`zL$( zJ9V7y&i?W63l)js1T4(-Ne@+qvmWe0S^y64ar;q$}KqkbLf!*H_1;3Zo(K@x=!z+=tG$f&f<;@`}o>zdgL) zupAu8jXInJ-yd=xOeco4bQAp6a);iwx7{3{S;_pWMk=a$UN7|5rQUM4>(Yy3k;>;< zhiiNCn3X(NG%Q3Mdb<(n+Ybu~NqU#guK|!%(qCqHGeL6zzXnxib8A?}eX}cZhE)UW zDIh+o9PjBn=+zuwh>ovo8G97Bc)9OpG;K(zYnzw56DFmoTOhFBYgdJt3Cys!t)QjI zf4A|6-blTLN;#YN;9h?YB_$~Ct1{<)xFjkCBbw3I?+Z4dGx}U#jT2RFivQu?&SPx( zrzQR#q5^!u`AE9xI@t10)}ctkDTip7*`_@mNSNpqR%s|JY4!~ubPNwO2&7chwy`dM zA1;eF5AuzErSxX9E&gL{{bIM|eR}}gdIq*wP=^jJfloWVwBx1v$~mz6SgbnNmk_au za@yJ06SP!zZek411-bF*6)XQbE*}AzTX~=KJO3lUI+r3+1EqYJO9j>D@4dOoWC>VW zb?h_Q1LIS%DhX}1oK^nYXW!t=0p&UAfYiUx$T8n$nD`N2XX91E)v~X5e zpSvg(NyNx1RL$ST`CQLioP(Ngse+pB`CR9xuIj58BT>s^q0GC-QjnYH0(H)vw+kq> zoxW&W?=9>=T<=lPvWSM&Dk$XO`aW(dh!|%j{86>M`PMR)38i~2lAFYI#^z*$E`{-a zHLUD(QoDZ;0YDpoZRQB5yYd5T>&63fVx(DB3xM*hU?l|>pn9=<86yd)7*U47#i5`! zf%Nx8W9AQ+Q7Y_hC6uA+5+;v?0FL)fjcrZ4 zj#qs`m=zpc2dO^=%QJkp0(1+`>=%GC08DVH;dTm7gF=IsG2bQKOXn7&PsAn^*G^S~ z%G$!EdHiIHmeOrY!Q=rYE!|y@5JtpXoZ8QbmXU1e1s6;vJt*(CnDtEF!<33JX3LxI zUFs9v-f}>+b|#^3oOaj^+A(|N_3J=YNW$#q+kmx|TaG>*h8vy-kw>TCsV}*@v{}FE z0jP)kQds|Mw#6Vbv0OJNeo7Q1a6izn9E)<`$kcCmm9?|)kc{!`!r6W z8o2w(@@}VS69bp{)`l5d2bnA_0(q^jb&SO7{YYoVO%W|&jb1O<+aeEJO1V~=sS5t{{{kBR`u)0@?E{lq zDH6$Bx zxN~F{Di~>m>rtE+K5OIQn7BP1!!v!(@*>oyJvy-Epw6Ff)*P8*IUQuT%U6tKa)-Q` z{JC%1otNd}@)dTvCja)mt{x=*&f;L;KT|!j|50>Rwf6;9sTA8D7b7mrUvd7toS|@_ z&10NYJx<8LF~w~*)s7g{q4QN+<9(6MaP*zk+7t(*ELWF+Vzx?v z`o#hp*;+r94CF8*Lx(kee4(L+^Wxo2b6Uq^U&GU}h6Nl&@t%~UgT{0Lw_~yEda_VT z@Xb9w3UEEI8gT-BuX=xw$UM|=A3->-q~J8gTpn^7)(~b#xo6zl&w*OSL{5W-qmQ5H z@Ljo?Ei#9y(79{8!F$Y(7slku|bC0fm)ZQ_wTNnanA#KAZP#ZX@7xg<3n0%*(SCF=nFsV)yc?6(Q}TWH~!v z(n#y^XV;c554Dx?N^chwjar)wITOta%YP(a0147pYHhsSH)5SQY}j5n11w*x%0LQ? z0*94|+zH=?)!|lXs}?SmXFN83hg{J>N%5lM&P*l6PCGtj)s87l(E_~?$H}_;^FmtABZx5-N)o>1! z#N5>E3={WYR)j8a#1;s!O7Mx7&=y)+Lq@K~@_8^7KKboS4+<-fE_e~oXvXa3g$$%W zo1@qFs>#1lqIAD{1H#VCBR%m~2IS@sWMXx$oa1EB@TAk$OS5u#M`jCR0| z?Ng>IvRu}1bCN$WJI02q37Xng!EW)(Vp1Ue(7wKx=-vuP7l0`6P3q-kT}4n zAhnXLu>JPSdwn*tC-1}t20WRHX>$^DM2hu(Esa}BDk}RI!=s*B>0UXvNDNC~JbMcW zly_H!0fdqOUh%z5LYFF>((qc^f0)tadS$M@w>V(cG)BESP5V7#0Y=T-`<5ys&uIjM z{|h&OR|tLzX1}=eHRlarYmKlD9RN-u17FJOnh1i5@4W}4&9hp7^%oBq?)~j0z)}>p z$r+aJ(mxd?(sw{^yl=RS00YCYt?*s2(#c53QTFSj4_XY)g=_xnqK0j=Mc&+Tt1C8~ z)mhwAb?b@Zi-og~Rc^oCH1v(wdxW<=9{tmO*4*C{aA=1B0tI79e64 z*Ervon6*>xo4?X5yIpV4gsL*P0Wx~qaNC>(Ku?TP*nSrcIO?-2Ou^Uf^{YMNB&Hto zvsZvM&}*9~c$b2A*@%@$K3n5pV6t29Pa3%lWJ-vH`$O0{_=2jTU~Oq+>p2I@nW4<)VNE>1d=XEMU|PxD0ZU})Z%nds1X2{E$dWLHTI~cT+)V#h{Hja!I&YVofoF4opQ)I13NdS#X{y*XqS4s+Wcns z=!pSm@BrYsynC2SXZcIEC6Fn~4k50M0b`H6A0`3Pw=rr2NOS`7qUFKwbP|icb=Bzx z*Lm#N4ea#4d+1$&E4lY@x{BR=EDHgEvFR^30P&>-N@dO=b18)oycp8~1$wgXyI)EW>Ufy=xNx_1a^qrJZGbo=E_#a^j@ZURU3jjn;!gL z!>NvnNJRlNFc8sf*!~)de4JHTTx(-zV~a>;Wi_Y_S5Z4szv?jbA!%ybS8Mv?Za1E3X2{UMDRT+1>e<5?8 z8bf$#4isO@b{}1f&18fAEiX9r+NDES;M}KtHUoT;#D4f z-eW3#=oyOH^KtY+{c%=(c*CG_RaJ;nF8Crb<}1;wLR?P(r<7f*V51kbs7&cE{$h&ORUz?e|BA& zt^Quhc(hop*Iq{dgA~*IB7l?{cSM8hCPoSU*W2w@f^FSNG{_jyoD8H9>1mA;*H$uV zwKDI?Q8PZ`ev%ARp?bB z+6Z6emko)EUkR1mEsSBYgy_1N3jO>sSho(xQndpTKcMb$p%H#SD8Z$1j}rRnSC%)* zSkNunAKPn;x?lh9t1}!Rnuz%}glxu%bhj+A1}$i=m3{S;c7eN16I#3%Ykne+rrC3N zxj?&2g>NyB=0!bB6rui7R>bc_U%lg?<8N|vsD>^?e}Iasc(Jc>tAcB*jrT+dYmt_y zZpG(ZSZ=F#wyC(E&PSK&V(KFr0sR*Ffz{PvBR$x7us&q;7`^Y(*7V8i>limzDmInw zr8Q&%*@f8o*&kRJGS?Xyl1ms9G4WiwSfKFP3wh;~BbEDQlaAvMb;RWlV4uHOT9(l6 zzeXi>R7z5_JSd2MS~fYW{n#{f_P#&VE5Q+N6C#P)4)^iU1I<)zx_aW7>&+n(uS)_v zg)Tqk7;<;eBWH^GbF;!a#@w$Yu|^_gdyF=RzmWF=U{7{V*n zsHwghM2N67MwFMGYi2T{Vb->SWPwAGsK=(m8)Tmo>U>6yU!j6lzU+HS4a&HRr)x}k zE{abOZ>NnlNG|#ZkmB@l%qVEN5>U<%%hzH)d2jZ!Q^Ia8)A;$J_M$k7_;D|qvK+Qr zn=DE}oS5ksaG_(`&(z!A0Fm6}}@=hFmhS z4)o7@?h5sqLh1pRpfK9*sJ~+CYEp{kKUdQL)Y5$M{(MZQ3Tq*v4KgvWJs#sRFTuJ+{97U3$CL2_|r%s4caSiy~#qp zO61cw0Q4BjgiaGqWq1N1ju)5U-1>>`EE#5zbX-fr3HOJuZq8qp#lVYJL-+FvZtb?6biU=nv(M#yV@IBVqE0puh?jBA^wYwIBuajsOvZsFuZ z=*Kf`RMPi29kSC!QaUk{EZR6SvW}|p;K}60clq`mZJ4OizJ90PR`8Yg#I@Yblx04O zs$;k9E5`Q`aahrsesq*!!uO$&eVs$nmf$v8lN)IW6t(spOwfd~^;X=>@ENzGcP{3k z2Lb4s)XvQ2*-KR9T)rHd`!n&>KidXO%83^?~bc!_ADYGEW3T%^0TxC{Yu1 z+nD9W`-=m)km5@Eff(=hxFcRCgEMBqj@_2MqW8pDMnmpf-})|*wKvKqv2nl)og02 zp_AV`EoQbu0clwxK3ih@C%Eu5Vg3$2`i*QOYh8_aaJ25aoI5gkn#l@1IBB%stU?F4 zE*4}QcGQAr8huhuaKIfc8+%TTRmK(S!5hpiE-POF;Pn?Itj*tE%Gk3 zb++C>*V`6_O?0DPyG-X>RzrOUev?;2ZCBFe$f$$% z*~8jBbm=>R3`b$aS74rb@$F{)HX<%fKQEY6R2B%N4=du~+HfBf_g2;toCXGLijVMZ z7veazzNg$-5r&=7$aZUKkkkGulym(8hrbla(-w3s$k8dgNRgC^S>tQxpvCo`uGYr$jvmm#L>|TOq za6Vn1Cjx{wQT?LypGpMm5-T1+>`=yVvGK_v7_=pa*?UI`NyKb_M`@bt&y)b)l z2#nc}5V4`3*l?A4xlvfMa8Hu(NsWF>XgVzrU#~4O%;$l(hw^0Eso9c$8DK0B_VkOp zOl{=>SmpAcS9<5uN8u;cv1+lgD?c~N)MDf9Mbq@rGz{X#Dav=FaqTIuW&NU zLAZ=6-*Ku+s~n z^g9l1Sap@$dZ5_6;@XA2qvFu+J380oRmJ#54J)1A)R##+14QyLMPg0^@BFRDlPP!^ ziPNq8Zr-0?SS`)2Kag1%fAO-sWiF|e^#1g8gJp2GmOaZR5U0nFT-#cJiqhy)fQRBS zc!hVaOM&WW<@%wdGN#97Am2(CP912!?~D%K`D|y=SI=AJo><{xtfg#F%b!oWdN7rB zh1(CLiaYil`CmR<=UhhD>-EYISEg1eobg#FL2+OW)rU2yRIddkxw0v18Ot3G^w--A z9t~w4e|hKuq>@@fCJQpM904GdA3pyR>B(F;ahVaf4y&|uB4u$OPWiW)tymS9FP-Gz z)9`+8Mn51AvqcA998QSLF-^}n7-!+YL_}kULBCBSUL~#3q}>qT8CcNFE^c=*s#tjmAe8e+nYZ?DrF6Px^-ZCt!0wOnX{-b|*+Ut(XsCgt zS*AnXUN2ZlpC{wibU@}+7R{UTSd|h>BLNqPX?ZgMJf+>ngbug1GqLR{se9XvXpP2( zb)ZD3VL7+C8pf3(Irh5^DIIh~J%thhs92ALz**f_-N!eRd{T2V?T(lAMrGFLDj98t zKKN?WKB;X<-v4=Y`YCeK+=@+pup_^gowR#VvuN0`SyiwW0yY)>!)RxPGz7}RLteGa zfAwalz2Q;um17j$LAE9%Rs`&o$Xb6TpkjF@Is*h1W9D4@CGES^XXL{ zA$Mf_q~79Q0QH=$+JLkGymYlihY@}RAim{>SuAV+`0>oz?^E(=f`eA|HXlD32Y5&Q z+J^g@I2tDRrVB``@rv>Ql_kT|ke8Srqf_s;y=F(>38km6i^ofQkeCm@znL48eaK$! z<@q5tp~|^$dKpNsIC6-dBd+ zeVSkfKjP)@Szb*OcP##{K-ZpogkzPiga;SY_)9|TioMLuxTM4hfkkqP~0+J z95gHfS^-Cc0p!KPiRaW9NzYA4!LR)Misga=>-urGA*7+)tp zolw%M0%Xqj^Ka~={?SDsK^Vp|^H~?)90EZH&!*_MQknyH+v2;n7!ZU&16(}z4})TS zLd26SBDl9`!gDfm{D~9_{>x|_A!D=zOgX(FyiPtp+ zs>0>0Te*f){#EMmBlWovOfFi(XFm|ALY}K`&**bwAKI0@4vOrN5$P_n?8L zqKdrt0BRp5JnTO7cdy@26}AkyM?n#}^C7AOMmky69=jTf@bq|GNs$w%1V34f+L`5+ zs5oqOi5-!E#lXD9l-JuV-XXb-bW-XeMNz`-1)(IM^~-#3;kJwew|2gG@~+&c2~iIO z%FT@rjAeXrgp4t%6V*FC$u1FRMew3?iO{|lC7$)wjGa#%39%uIo@Til88as*PlNx2 z<8|w3@=|-+-M`Yv9hkquwX3A3I3Hon>Nl z%gg}YT(R8%uL-FI%vyOB2hETveQ5p+O;g9-qdZLKX;aSLv*9iNJBuWt$Kin5lx}v} zzWBx5im(D?Ep2m+&1xBo8d#%oj*h zBRUR(iTm|()i5a*nJa|QoWpAAz460}HWW~!rUfy5;`?<7WpVW^-)+Zpd6c=ucK)}$ z6jDfmgdOdLg$D;N#4CLS>Tef}_Ea2MWg)75DFcR;fJbzU=@k7#%*(G#3fI%TbdEUb z8}h1lc>d*(%XrTx47!9iCv_;D4wpeY=^@%7Jlq3@n1IcW7FXVTp5T|>DALL6LG1@$ zv&Y^ePSE6xBuSV-4w@CW$cgifA$i<R zTd+FcCBhtwzt)j@4{0f&x9YYbpRLa-Fcz8_uQ)Bfp{7ClVB(7Ji98}w(bA-mX)e_I zW5?kFcQvflrqkHOtk|IUuhYI`O0zl*!FB8nwcw-X#?)nqP3y-ejo#CRG%FN}3*1ZT zKT-s2ut_2_hI$A@CQo{$OzUom(#QUrL5roIq^MO#A2Qh8OiHx`#-dVva@URT3L|Ii zMZ=Vo9sQ3om8yIc8QOfG23B&-X6SJlfEWJY-Tq-G&c@5>K}X&amSKRGMLlaf6yk+# z9?v4%K$*>y5RR_)jr)^;@&b6!pWcd@3xpEgw4M$diFAPY<-THcWvj2(BP~?3P84tL z1(^{y>7{CILsxMfxE3x1QIuBt@4)PsLO1(th*5DvxaZQogNVKOxMfVv(Jd*zyawiN zG~B7U!wr`?k@-R;U^OuHF$#s=q1CQnVEJxU+7_jG?zP~DM}&pP7xR0tKd*}8Dc-_m zalwncorjiexlkfC$>tC4+NtdP1_Ake|S2=n^)+Z#oZntg-R>Ov**Q_ zS(rX}&(>f5E8rXZN#A(=cTfypd%{D4J&wL*ciwX(jHHze>jcE zLCs0;wweA6{#AgYKsVTxBj-q_L?pJduCDedYpB>*e~-twAhct_GVDme8%p5m}` zS1JHc(C@A>{pu|QZ=96%pIxbE=N!x3g7NaT?_Et77uCo;;2wB9-_jzogq;ImB!Pz4Dc*ZP z8NjW%0mrwAi|Xz5Rl+Zm{1HNqJAkti!&sUZ9@qRu`ol{db*_6wsGXXpc`)Xg(zzy! z0F*}~t8{)SQG^Ct9E?F<3hj#SKKu=Xo9XkgkQjch>L|0q4*~a$W%Rn2t8I1{rZzP8 zJ6??~dkb>`zm^#Q3i8W4M5Tv|Ca3&~Oqc-jC_sq-(V_!5NY%SY85+)TyZ6>kIjbb3 zjEtv)WVh#74Hbcx25!Bv_sjqN`7aDm{V@4pcE-IiIO(Lb+A| zl=(hb1jTCt-i}_I2dI`MWV|s^`sh!rTo|&)Zzd%{1$bxkD{X#^MniM# z-@y~>8UZMYU|BAGe{LXuRm+s?5$34aO*mF2i@M;2<-39&s_w{31z|p$?*Lu$P7*-S z82lii8i>e+%u@~zSsU#BG|DtD^Y{&cUyN8J9| z5WWkCg~~FTO5DU{;4X!gE)G=ce``V0N|;T=TLxD{Z%TM=A~qX1*h?r*#yDSAT#h(!?(Aw3;2R@6hv(t@EGzHDdf! z?ly1?WmB~^&C?13h-s0Y{)@!Cg3m~0&d7YAjv#L7@>b=w$LBx&j4uoha6okgxGpeZ zDBMy$$rK8ceZGlj`qlk6BdF+vQ$HV=4hb+#i7$7jo#E)HUABiY;bqW$g z5|BI@tVc}KdZ_6H(1dq1oQO z$?--a-3x<0L4GP9ozYS&?jK#IifOwntzl;Ia?OBhGKus@jr6pKvx-7~SGFerqErVk zlDI#Yfj^F)v@Kr&PEi9;1~~Bs%($CyDInp5#e~WZ7$}93W_luHmVA0%k?U8|4=2_U zHmZNN+yYN@P>CWB;1q+-b2kJ#2c+JlXT@L1K8hUb6P__1Wkwvv1r)W%ms8a)P`xU;1M8VG-#k*s8h{h~wj z(r=Ws&BLU_a_dFk7t?{Of0Cj33P^{&>fUKt{olE!)0NfLTbu!AVqOKxtz^Gx9h=qt z^vWcF6LA21(BOg1Ym*|?f}KRuP|F79?v^Hc6jS|&`ZPTZ4Lyq!;4X+^eOT(LUKLE# zcD>D?Zg}y_sl0}C80+*r|I6ZMmdL?AwRp@)l*DdN3!3io(zh1V%2wi*bv1;}pc3zd zzCIl=@h2)D$c_mB+sL3<5c#kBi_k1S?a-h#)eMevYU0q7!x9k(y_#cp59Z!)$sgAq zFZ}c{P$D&j!VW3=D*4eq936aNvh_OkbA`H(F1%MG);8Q`2s^jdrF>XQeXF6>`GR7k zf`uccc%gvFA>++{hg-K4^kmS!qQGu3XuH!;|Bx1QJO>a1LEwPEU~}HnH{-qg)Kl5N z6AXCPPdPBL4(}ob9?2S5`r$KWtAP!=`*?CXj?p`^9t?Hp^ER66yFK;0l&dRXf z55bH(6hcFfjOto2gmn{z(SfudV{)z?`g^>_{19)Lp}3hq>u3^-NG%tnGF1;IyY#_Q z(JSb2VWC{1wNyNy$DD_u89^E{f$@8|FQg%Ka1_Ej*S7#T>O{x<{O*@~J_&bp9~rbi zueqnsHfV`JKNyS?HkebbuXU8b%{paSCCykRISE@YNM>fnWs6obPXj-w=qiT~I(Iv% zgs(jS6;KDgH7bfx)Ctk~Gw4P(wReu=BaonO_l^bCrW^RK>IwSREm?8fd)We;hXJL`7lw*g1%i0^C8 zs~}SdG+r2-@lX$c#BSZ%9K)SHFi-G&YP-U!c@olP*{rF64MgKeu&#LHY!GbbWLY+G*TJzR}8lwQ!98 zNrr8b{N90i6EtwL-mAcawyJXD`GY<$rsr3f%}wN%f6GLcbP&{Epspc>fUidW3xD-p z_S>+z9fP2*f%|b(@}_@teA9dLFFrFd3o8QO4fq!@%(q@Fs{D9dlF*m#Ms`JI;Jweb z(#2ZT{T7n%uPKvZF@aYmUrL-)W>4l`0z~#kWqpZo{QJ(MuX+nef@8M>Tdf#V^Ak=z zn2xEqr)gDc0Y_7?T+}P6duLZ-BM$=t5;u_3HD?Xq9l!7A@c)?l>aeP|?&|{xNP~0> zDBayiqlD6V0O{`TPAQR=RJyxMKtPa`ZVr9u?(=Q0`u@K2+(+*pK6|gd){HU79CPoZ z5q$sq;&!2v-9ic+&vNUVy;T9Sv@{8@9~2nXQAK0;Ou^DwA0?Gy1X7UlRK`aPE1)i8 z3;l*88SeKQx%%sFU!7L@&dcuPP5#tT6OQ&&8O{vJ5w}y+VDcI1?{)0igGOc?p=u*- zR(SSTT~p*$q4VG%jmqAvq5ZQE)!+)5}k^P>%hjq~46nSCccs?ukj?s)rcC#5%Uo9~13Xqel5 z-{jjhrtE8!9ST~3ShEQaKbt3ogl3hu)ihkH*BiMraG>VmZx!N4LLyqna}jR>%SEL& zB1`BA`yDK*$dHo{A`V_=$`6I2jmJAq`8%^AH4n8i^Qebbl)S@7jH)?zZgoj%K4@~# zLvC<}WUmAUz87xxZXK9&cPHpX&;qP9w>ts96Uk;qdYT)DqvG*_p0B}}#yrKn|Fn6~ z@~a}Qa<0MX$alfd*`EoD)r*a&5TGQ`zCCiiT-}aEfr@BA%;Dj)t&!lRtCJzU_wQL9 zilTzzL>@7H_D|fOo3nt!6Nzr_ueL|mRm1heH(8fl{Jo92{LF?hZ7PcUNE30dDGTGM zOj*FV6vPbsI__9OO+y2JP$&Yd^%%;CDt@?F! ztXgE|LXw@eb?xj-#X%FlAYR+a=n&>5Ry1N49}i#G7?mOf*N)%TFe+Bbw%YFri?xQ zti_!yC0WWGNn<}aU97Zj#r!yTidn?Q!|r-Pktk7K92q@TqNb~}A%mYyoPk%m8UDfY zy@Spn)E<&9Q?2tiCT)PE ze{L$hy`K5|>=F5~i$>MiexV`hQHAoZLxe7KIGCL=s);@(N5cH}Ec8c-_t@^Y?lCn( zAMPD@#I7Vuz1p1J(6=fdX!d>0lOKZ%B)r!NLi~<;-swQmM3Kke%)%*1y{uluC@U`Y zU#JE*gS0N#qgvigMq8f0C1{Xbe`9TBWKmg(U+ns__+<2zA6(TzORBu-J0;~(NK0UA zt)q>dc-eG%x>j1>$)>b2UftA>m_boKs+IzmSdxsEr1BwlXVF$P;4i zd>G1L;=d8MjOh>8-3OfUlTi6Od+ zX1K!AjRJxB8G0Raf44-7kAz{PjZ%$Q@#n}Jg*Av3tEmNv)fb!uouncG3Te3%PuLF~ zg71U`(pEQ?9%lMw1PF$I132t+YDS;`jvvM}s-gg)%Io`mC>voLS7T~Ht+r3am6ZNy zGefawI((C#D4G`UzP`9qr<-b@;X&>`B?Ei0-jKgvK{eD*Y>Nb!j=Q#|<}0 z)jo+&PVVe+v9qLesv-YHXDp0Zyz^WJ^MzFTd<5c-uM7aR<4qoq;*#Nw9-xQJ;d z!h3Sk=63*V{|Mw6utJZMO+u|&261jB2dD@oyw^%Sy+N?~WR#}XDYMp3XTYTT`LDo= zV#dAMK&3w}wGll-*ROfeG*h5))ubX&%N`DMJ{*pr^oXS0;h*PIIkv9gk$9S^3ygd% z3nLe@Ri6giA|I|_RAx4vk-;gR0tSQ_pppCDk|(6A0-EPtb&Q3ZV)a3Ke4Jj0THOj( z32osQYO*H8Gv-CU`ofDkZ85@Ym$$UmEP zAFpp}Ec0WeaZHrsIxM*aBX}3h@L?{po^$;$Z0)eF-*68ko>TG#L^%c#KBc(@=5plAi zjk4^`u;~W;DoJdBeu4?Vs50E0e~Kc@kEQ&1?C7YezZRA^wV+blGNq|&y%+8Caq4b) z2U}lZW5tKd;hS#HNMhPhrTK!v95P?CQ%dFIISmt`>L)0SUpWxipd=1yW$cQ8%uYTd z3r6mGrT=d@zgckC^4+^H?7?D{3e5uIRZ_l9Y|mUzmmgLPh_M^)w-)VKtlW9Fi27 zG}GqmHkt{b%9C5#A=mf;lXvz+MdQ(vHph{N4@5sV#F7-CR}IO~lXNd4-|foVqpfTa zgo71fQ2rLlj4nQ6E^M1-Y}p%m-<~2MBQ>}tm-sgPFw{%l^U0o?=kI-Iketk6oWges zb;TgA!{rMZ%-1RD=I|)(uqWSZmlz*e}DT953wch z)s!0jJI2%Rfj(cIKf*a|x;k=RRG{qlJ~0lTA;@f%x`D9 z93uV4t4hBxF+M(a$e!%>R6??mMthn!jv``m*+spFGOq`|v&@u_r+n_GJSXdRmtlSG z?pSzi8}suzpB(IAuq{(v>$m2^)*<9Xb+qj&h@>y(_kGgZeviZBg%$6jOv=9Ho*(H9Rq@i%XiKr?f0A7E*Y$!lYvDZl0k4Vy=TV zdOLjG$PI+Hu!9L5KIi+h?Utrcx`9%{a(_I`e|;k9-TAT>IJaEJbe}b3Y2_RPg}tshRkwEtx{kWM9JzQ0u%D+^mbP0 zqy1rcjhXA+r^u+{+h??SprS3bRiIRuN>F?K?CdrA=y3F}`N_G%=CGZ|73vuOf~i`v z=BIj<-!k563}O4&S9~P}E9kPamxZ-o`6&ljhjpfin1u$U{)s3hL*G3x{H!K<@9!_c z9`@T|q+y-eV(v(=#~95DRDZUMq;BWyU%4|PnNSJF$n4pxZ>|RBFQJ*(mz1L0yBA`* zGpMdUo7QcIHmFA%Vy`>kui~hH=O^Ik(`f&qHw2D%uQk;5ETi)zT z2nz_tn()%8SU2`_i~*j9<#_KrNUw!z%B>#icR~p`Ybhh%Kq$`#UkzFTlEz=&wb7A$ z&p&ng6w0WrGa~&h)h^x!Zy%oo-<@@^uEJkmcl%yLYYXjWdD;xqugf7z*VUB5(I*Nm zInFYh&JkVajCHgm6#1tu%x$znB6!#h=1|#%% z7%nJgL0w$y#MM6qiJH7VfMv>?JJ>mWcP4L1FJK%~$FTWELyZ!8&y&U%{HH=1PtpT* zx|WgpI~K@(T_#n9co=B&tCOQ&VnHUJYjFVX<@xxmVX3U+AR7`WKhVe}GuoOu4CVf}m$q2e%WD!5v%pj!t zUY_9(bZtKGw%!~e8veqh$^QbA-KXYW=&#f3M--a7%vNV9PtjXxwuJ5co@s> zB46W{W+X@Ik}`c&*`?fHR}(DJy?Z!v*_KUH51VlW13bZAGbW2~Dmtm4Jv>!jUJbds zy1R%_Mb9qgp9gya^00Lxd)t1F?gzvUj*Ouj-_PpBp=W+kHKzIl`?j zE;n;8iiD$%GUQmXuw*TSY4iB<(|Rmx9=!m%fes>S+Oya8J5ClyOV!ZUM| zb6J|8HeCyV;0iR{zoXYoFJZrbNxjn4HIZr_*74^w)DH%R@kCpW5I>kr_)+E?mgKEo z5|fL0+&rl%P4@OS!E}j&jSW1z-gP-!vF3F$Et#5j=0F_ zWRELhl>f0=kbFO79--~_m8LS@KHe-$DC5l+|A__67^?zCRP>mCPahF6U;xBwnVg=L zpKe6fSR?CqqvU6JkB>eNBxC{!;tP+?L4`xv-p9h9M@67e;09F)sRQO@FulxJ47zv@ z7AbUl`yReZ+rV5)cH6tKxc1^^WfPeF)#}q(E)62Y)e(cgz>}I0&3DkVGwxkHh7u9a zcH*r2#`#1@Df|+7Bn0+?M(e74xhCyn{yC!gHfIu_8wmGC0S;l+%4D857Bj}H0(aWg z#@beY%J^)Tpd-sJ)}J+%B4lLc^tw#<1aJA?S<&>=o#oH1@=6@sYp*N=^Pf55FQ!Ks zo}J*u`a$URMHV&RU6(SVW>r{A#7%uL&nE3|(fmONye@XJe(nhOU8o^4dqNx@Xc$y7 z(;fJJvE8zLTd-AV!NKVKyL*_cs|W1Wc2k9%12{%anqhvxT`{3Gd&rHvTBnXP7S`D? zrNH(4@3Rtj`rj>S)hV=z3ShQ=ka&l{NRs=*6BF67<&7&Qx;;4~6U9F%%c4Z$#QPiIT9`0v!t2>2>iQa=Kd=+qXXw$EW zN!EI3{Phx+MUwG~GviFJkYrcqB+QN|sJ{Kf&tjL4YhM|O=I<1plHmT8mU$D}gfS3O zERn!6TN*H}Kb_|4Hx5%0yger+Lk)IDp7#Nz5IJn%UsEDN;wTuCcl)?U-YOV7%O@5J z3eC?v|7ty;&EzkaI1lrVfMlcPF}_h2vw0Jn?6#PHCWszX`LTWEN~^=A-U)tHYXG}6 zi7Bzu=GmXXrk+?;K&7WtIwT?Cx-Q`w0bk@)A^HsQ-77oMJZeF9_WkCtT+7+zhOL&k z1mN-xV|SjV2^t$!Z#fm}z=b>87$s$C(C1N=D^rRfnF;p0-} zFZXpMYvdC8*wp*DlBPz2kjRw^d0 zRS5h9AJa`Y#0D$H4vFagUF0+tjvXfhM zK9JU$YzWiOd@i6&%)_bH0YJ;G?tn?(Kb8syN1aK3>E&s{klUMYD(2;yEhR>>-a|b8 z3P!NX-`tzq#hi>2Q2_w@jZe1P*`bwy2m8U;k8 zxK!9P5LpL1{A7d3j5fng(k#r|%7!eO?59q=%0`jmNs(6hr{?p!;rlDAXOQ_6brp~v z+w9NJ!^gg1e_$sNC`zL+0`R2)M~@&9%nJ*QuMmfbN-}b>>IR^^`d;T?WU}a-DUx}h z`M?Kk+!7EXu=AxDtRxj!LHZPM7@rRB#W`q*K#em~QB$W2^ImD$(P)O8&GahK|L#zf z4)%U;@@lsvN{c_FD3|7|<&4hb6Pcqh2>{N zDEe1clw-gb+^{%7tWw1t^)H12*vOsB0uoVKd77USD!K7aM^2rpYR!xj#YO%~m(Ne& zU`hB##fkIs35Q9&Wh0<7Gc?oCf6Y_-&8j*3p>IW!nfC0;m67rbVTEr@!%^rAyVG?q z>Zl5Kar75EThjR}8(FCx89zpUd>oKrhOC**%*?sp zicx!lB$^y@KQYthIbE2+u(SLOD+@`|OIEiolZU?PL_Tz?4&osdnhDPo%j7pTOKPff zqaZpoKnz9CE-^Ji?e|iMvogcc(hNIANyHWTLrl74OB-iqq8?Cq1sk8M{jEvqQ_YZM z9rmqPdVbBpy>%UNtHeW6d6_Qi56ABh>1Qy|s6JU1s)9bmQR(%NK4Ao}%D(FFp(-n{ z1gl?rzXYp~lf}JPR3x)XsyAc=<%LlZkbnY8iY!VZsR%gvdAYF~dR#&SdVQWraye%~ z_dATx>i}0f?8Y$*FfGg1I2^5-a|!#hTS;TAllrEhchA^ApQG?&Tsc}-;N;f5)cJJb`jEJc+p;m!jU9`^Lth~ zrGbx|TJ;gt+!Y)Yn32dUsCc~+RNQ@E@E^gmVv^&?;cEuGhj{K*Gb z%0;MO3(QaUvSH*CwQU5dgYX$1yuH*#7~U(^%?g_8b<;D{R_tFvRN+s^8xDo=`_8GC(J zuiY9oO3)^q;Eo%nSCPA*B%2+Nx3mSl|3U#`XxhkX*LEjn%1mT&2&Xd||LLavoi93L z-YZ(?>Ub)WN7hL^L~E;ce1Q)BGALn>R{7V;(qcvuBOED=FJCK3U$D+1D#$Cv#(FO! zmoTEbf%k8JJHC@kkoSZLeINg3jAD#Yy^I;#O7{=PF<=HR1&zVI!WQG+JhlS!=!mOp zB0JO~t|wYp@A9H6Z`0G3)(ND`9f5LE1wn8Ejvgp`(@$ z^)P|F!>Em8C9T4ZmV-f~l!~CcfX4~WuNYv~bIX;?kR6Qd;Ly^_4OrEa1jPrfm6uRIt}7Nk`MXRg1+9 z_9v++^VDz(Fn>_I_C+r-;@X}AsTIQ~Iqj81c@$m0FMwB*dPj^#Y|7^)@D;lMqyuTj zmAC;LXogm=aqv?JeY3Z+>&hB&8(pym9G_KT)T>1N`2NmQ#9MHo&o0Udd{u)p)+g*9 z-waYhbAtn3G!(2=u9BF_S@ogy`yX#FA~D?}w7Y+vN4-8{xQVI;B4c__XU%MK&C9MW z(L092B(9|&QB{$msOcn7ZQ}-KP&_Z;h}dPs8{UCpFC>F6-5s}5-+$(biHYg_q$hgwr)Q$3nDRw}$h&02HvAO-Mw}oCtQ1WI zLUl?%42_Cch+WE(Y;)GdQ9<$gEi;KHJT8a{xWC4H7rV;0F6|!+kkX+} z|7PB2VR@fm0`h}(wg3R-wdoJlHGL=?>V$2Mp_mKA%3&+Y?^EyJERQb-k#r~AnzQW+ zvwinudTk^ciN@t@bdj|)kEw_;EXxV%!UZq%0-me(x8HymnO9=5(A)?30lxJLi+fD? z4|&51-aS!0qN)0>`_s0UB%JWhINE_h9uW{k*1oY#+bNj{xLD2sl`Ci=c^l3i`GO-_RUw9H)_iJU@=GQpYi9+ zo$iSEQ3jT%HPMEREVKgU7XEAYj}oACom(Yk9>b;LlDy{{uX6TKV{VvVu*VUFv90+v zJHc&~?V*WQng}SV$7Z|2y{Hvva&SBk>Rf6BvNrRN(EY5Vh+Ih4Pp825g62jRW&~Xm z0;NZ-Z2py5n}IC1qNXtBEl;b(Ta&6n(h8S6cA#$x)D(fP-GFfLsQZ;c+rLxtF+n~rsr(&^(f z6*K(sRUrFO0p0hU{n&9ISF|I3jHvHgcr5iNdB2kPOME@<%*J^T>16s>=9_;Wu1pE= zaj*y?wbbTfV%e`SYgB3hzLwb97(KNx!>2`Xw+x=azpLeoV3PV1$p#GE{mx~TXT|}; zEQ+%6I%c(LJHiAODvg&4nst-+7Z;SH0tgjE1BXv7vj#iRaPAmzmuArx>*{AyP2a2B zgC>t!|Jg>I;l#mD7FGB!!V1>R4?An=>%jvbcxTk{kD52lj*we=osbJG&+gbL*|J61 zbdG4;S9C7d)_{e5`I4(tw#?N2U{v4s@m3gb(+lknhWwF8fda`j@FlSht-QeoznH2Et=Mw&;7|m)o$nib-5OHoi0|_VUa)#a^VVnFxJqkeyX4Q`B>B27*2jvRNa_R?oDGZn>}lKJ)yzCkAB z&u-qEOVKoK8&h^abY81kth6Jfcawg2t@O15DT87*y5IjHrRpIP8BV{o$RqIe(tfnD zqkMA;AKh57I%8<80a7xn2QP|(usW!2YRb^VO!R0w41+v+j!bBj_5D1rnl#F=l3b-8 z;Xr!wNmHiUE#+h&9VJ}lV0>795i-zit@S_fVo*-+|4(I(kEfy5u z#z6iYb#+mf1{&nww+Nmn{S7z3D~z`_>JArC`)d-?3|_IOF*Z$CSBeF{2Hy1*6@=`! zTlii|zLASJ@KK~r2c?q_HL3ATTuBi*7tx|A?qN8L8I#^Fpu%+%=B|T%^uR} zQUybJLl6LP*K;Qhq7}FAi96HT6xDM*iE`WMi5$wT%h>2a8aZ8K^(7R(J}d%5620st z>t>(JOiz#kn8@}i!=fd;@6e0|Fk`Xk-dsm*7B|y|#fjWw-j8=a=Fnd_Cx^yo`9Hl+ zyjqNHI$l249L0QYeX0bd_s*>WP?&Qd4G){?)B-8b|rv0?lUZn|O zt#ds|EMg1#iMq}yE$@Y&PHXbK0_g8^I4D2*HaL+TDmJs*KOp(oytCUw~H%+;U z>&9LO{A5u?6q_02u69;KC45zY8kD9N(6REyk~ZmkbL4~@To%#m>jv+p#l=onnyeOE zO=YWAt#r71ut>zaH@bAEi9TH|jCQiMIZ&H9|~+I^`3ywGQ*o_NVRXV54$sy*(d z)*OTn%Ag&7c7U9;56{pL-@mHH30htn*En`21{q{PTL#pQEleieny#({G0#)nDB1eO z3=RRoeXKXHJ^dS7K)z60roMDv)ozO+dU-6=JL=%(gW6Mj^VT+J=%I%+ywCcuy%@Li zc&GhkCdh%sHG%zc$lL#^D^Bx?lJhQlhwJrMEfEG)3K=VKv6gr5-yE!Ay)RX1(azgF zXgKw?pq|(fE5=##&F39yeeg@QdU$*gLYI%A>iNuoj$4l4YTE~YqyUsa4%8tXE6D8p zyxy4e1?R2{qiifxV62LU1TwWl_-+65+DdYJ7FN1{yQoAg}6AA^gkS4dE zDq;f++etAUrG!>W=j$!+{?Uk;GMWBxTo1@S;z>)~{_F<>zkShB{}Dn)0?uRWBflrs zP-|(Z-^J=l&HZN{UN*iOPh;oc!eaWrg}naD*dfF&8Rn=0MQHDydWKu~u`pL-K=T?$ zHL&sUgrGtv+6n3XFc+!?99>j>St(5Xs@f~BW$&S%h@z?t8ICEN18$6^xT$MLd*?B4 z-+NVaAH9a44FFzWCXZ1N9iYsQ6in1Y2K&%7z5nP0SC5o+yy zyBD|b^jNaGxba<*PBS|`j{SK(+)61Pb9}f>I=46xB6h#p>ge3MPYp;G+Q4 zTT#<@tIHyJ7k}}w2Gj3FCxmFVH)QJOT)PB&y|h?(xga7kO|!71n8XmjQmqL?p2CWHJH(Kd)7UD`vKzpm1<^*~E6 z_kL_%ZznMT9g~hD%zGW2;HFI_|4FwTkcZQio6l)DB>?-PPrX)z{K8hkgJiY|j@e|R zxTddxfPdYC)lRywFpZc0n3T2HPTqMHNGa3muw99^>de-z-(UQS zecoc}mJ3bnB6JSvjSuI(?m49azFqKQnNi!%o6a7@7Pk11nHWgk(prGTBq~%zj=ZT9dDzo?g8Y zJ?^=7$65l{Wni}m&L7WRiN*R~jV-Zr-y}drv-f+(b5F^YKmAA}N>6wYK0k3^j$6vS z`M<}K;C`-#SLbS*RH_-u-P1+Vjqg0U9UiZ%5IObb2i^dPNQ)#TE!E(aQjYf^B75eW z6qC*>?&9lQd!IK&6CdL%)>0^12`Lv_kzEsxOR`i+Q6S4dNvq1c$iJyAC(CGMI{N1rBHN-2M<$%!+7{~ocoT&2o zRkuBvU|=)oF-`NZCOLGp{aj|;Zc-T!@$KPqZ0GGx+&U0V0=Lpr<(3xsTvh=HgK~cv z*M{3zW=&Y^nGdjD*}woBef`T!vJC8ULLT({?{^gaZVa=l=zZj<% zB(WOTH;9f66Ql<{ZroMoY4XR@!kP|MnzSlt#Dbq2>+BE{_uE2e2o89>WWF<5^}8_G z*w}TAig{r@R^A=;20l^&%vIDEN#j)g%_VnRSDx94i4P0BWqm@t<+2BiJyewigp<;w z>Iz-sxyr)cd4zBA9Jy6~u^-bk9(@DczISjOrOnL`yHJMC!Ow{VQ1x9m*H&YKfsf@Z zjfYi?@N$38lNMsC;$FV@_k*dwKwvlB1WNVmqs#)v>n@;~z1NTl5FlZI^ea_g;XOx2 zFr+0ST+$+{zpA5}0-C;84g3W*b1Ip#fToL-6VeeF#%DfKqYCN**yW+zzM zQWfvA$BV}uwHgq>iT0?P_=rHe#b{=JT3sXy637&8DO6=6)FoLu)ZZPrmWR{@5N}Od z16`5}d~3M}86f7E1oF(x{$EC@#FG-=bx^0jgasD8mPO`hk^3tL={^YMA_?=Bto zfC@BRb4yV1=pFP3W1(e?*h!bL$5l{6ndP@+YIi4~umY2p&rVgJZv!LtM47r>vy1Q_ z=g1Tm!YWFD5+4xWeqkjMmtU)d?N;3L*^SYpd>VPWo!)Mnd`37 zZZ|R$me#LFvIVvftSnDr*hBD<{>7{T3P8vDLKeusZmy24KJD;2Zd&QrsyE>c2VNPt zSX1G_w^&9XhV_T{R;GF;V%}#Jx~|M^w~tqv!Y0z@(%wJhN-m<)V$H=jv0P7X-hUPS z>jx{d(d9RK5>3x90_xdahoFwEGSm00ncZrfp^>4|FSZg>6QHcRw5IQT`P#r!Ulx*j}_CDQiCsu3=sGJlTeQGRsz zz26HejY``YUiuP$j*kSW?|VcV@$Y@4)vI>{Tc5n$P}EJa;d9L^)U2aWPmbCJRhTtE zBnXMrJxna{0K4A%f)l8UKjJ2E%1*E6zI!#~c28WZV+))JetzxIDyUzKO@32BD^_)) z;-`uOn9%=rQFORq&__x@W_4}9;s+NE zH+$!u|FDg8!j;zE(TT+mXu1BZ-1Fo9BjOL^`!jN}6TImaS0)0wf-|qIJO+#TItl=p zOABg0PTMRzVZjT^Ol;QwxU<{$^TB$!Bq=a4g#+6%T`@Hkn?Ba0I z^c<@!mba#QBZ9c`^O5R)*ZJ=Fr2nWgJ(C$Y8ox8$(f6FqJ6Fo6A8T!do$t{_o803n zw^z5R{+5F?TBDZLI#GE!u7xl-c05EDA>Q9WyYuaOT8XvATs4YuzlmSA8B$n+t^y5S zT-2{GKUBa6^!PvOHU*{hg?%vru1u?4LTw`A&}o~W6|fT^3QX6;*V~w|kTyrdGU}O4 z6woXuCH6k{YhzyB-LC!G3Aw&1v|QLBP9{ZDMFE;`YtfbDmv=-)04{_uA;;eMeyCa1 zBs|3Ox;qi;fH9Ua?(BS4U|ko3opDK%Y8x=FhytJSJT&$DizD)U_>*HIxwiZEcx?8i zns`|?;*#s!ZE;IC1u>t|@gt>I#K1Wq4$SdBuS4cH*15h+P^GR%HP!4!Jat+(+NG{N zXd;vq5UTpz*e@uwf6hV*C{m6?mB-To#5tgoPaaMB1m;tOydvl`ab)b_29PQK=apis zSI5$RysI4(3vnxtDcgYF{*gRyAk1v*I1Q-T*3wpw@C*h_PU;UK9a(|d;eQYuJ*BEI zLKUYIAD28*(s@He#X;W1Gdh!gKh;e`kRF`gjI>e}+I2qH_PCe;jRu2cN;Kck>?$WOXoFrFFw-?YLYNP zWIGnBh#ozH%8bOrvRuZ4VrpN3$3>$Gbkeb*|7@Pov-Y~Bdp40}h>~J!m_xfJ| zQ>OUtH4-s}I@3E=l`C0B0j>a7q4B3EyicC-*d=l4#CpZ)l#JNVlf%fF_ISK6{J52b za6Q}OF3E5*tPm^wLPwi3qi47GiCektg&q?SnRXU~x{4W7AT8HW%C^XaIZxq-F8w0E z5o~=#!Y)lLvYwP>e(E}+94G!*Ca<#)LbNpI#|3x&uDGS~P^`lx1j)&JQ5A8ZmcOqg zyo`=3V58fnEjTG0J?N5B*j0BKj$>xlfNFZh2RGW9!H1%&oK@#?rKvruHr z>W!?Oy~zR^z(`5Nu>lp&wI`oZ+TYw4i~8*T{PME>>0m5j9#rT#-lEgJvtKMm+!{cF zVCsvxcOQMxGc{NNi9Y7rYua90&oRN~8p$4$ty6(cQNYUW<=HjF+@Sk-zip+zITzA{ z^4ki5BA`Pbf9es@18s=&;=zIz9JSdQ#jxi8D2r>u)FVSzaZZ)3pa?R=2+1~aJ$4J4?r1c*h< zelTygwPaW8lW}kR{;gzMgHdN%j!599Peo`@3`W=Z7F^>;(*7lQ{=fu{sucGiRs1>&69&es`7*?C8> zsJ@|n%FE{QZ0X%fZ(Ski`dTh;UuGR?5Wtu|r=`YC_CrW*zz{6Zw=OWLJZNEUL6$u} z_)_TbHORekYseA2!Mp&3qxBY6{oR9pfawE3Ylyp>ZI9#vFO|{tnIlUQ*U67LKcAy( z;`6s7*G^$hXY@6l3_TXD@eWaG6_LenW>J+ zlM0xQN{pLRL(29(aZS*fEdC^yX(YfSNOkFaKn~FBs;q8x-e_cibVg{#^<-}j?)Sz6 z<>M-V@f~LrGc6*dfkHEQvKHV#pZ>15b`~X+^?`VkP1-x#HarSB-wBb+beiDfi0L3S z=wCSy3-RWy>?bD`bX*9lk06mzHgM-n62DqyZ$XxEFf^n;&h_F@U@JJeTk%E#^cKh6 z?cPd$&U}oIBh*;Zm|utkqw&d7 z=iMDr^dm`MUFy#@gSY3mz8^jkWo2UZDh;bF%q~Xo@HoEZ!BpR^m^BheOzlnUeIZ&% z4;(rY(5tD+uP4^kO&{089cs;jv=F7IN!lBswnx4uBPNkH64ee#yxZQ`;a7^)IO1QM z(iz@5UK|t^cCW!|DOumDXZd}V4Bhmq{Cr}ds6m`*AV&t?#I}=>`mH(A@|>2|(ax#h zg+GC25a6d|lu9`OyZyUh{9XApO!Ij9QaI@m8at!yWT$t}oiLD*Dy`~8ivGi%rbJKy z2+$Hi-yl#;-KCUKO`uCwb^wg#9l)%!CIK)V9*^n?Qou>5wD9C)w7Hj8E+gtR&-9n_ z3M8PbX_Sl2eQ_}JQ=4~-OMAFrqKTa=Jg}J94Ic-8XC#I^D@%-1$N&d`!2_<~&lDM5 zyVZ?CDv&kod8pNxhGsNRJ%>e9R79@iO!DD(c~eDz&Zx6A@)3>fm3Rg@i)Od&ke%3M z?)PMJ(Ic4P;qEuK0HpS6xRS%{*jV8?jHpIy`F3k2w2kLH$z0X(gGN>yp@SRLW-7!; zG4NsL+rQKHwa%5}?+Kr2ZX;V?mUJWK-k#jXju|+hpiEWF2PW0N_kN=WXprmnzas*O zx}QIbJNf(oS~Y3F=JbG4mp{6L$R>CI)n7{qpbF!!KpSdEsJGGYV&qEx-MyCvbL)Ll zT9pTewz_w<(UnH^x0P9eiieZ-D)yPDV}F>^+tDMlEBn_hX3p{uAG(4EQB?Ju&!)yM zc7c4ZpHLPY$M{4;MISda{sf{ow4S|7qGrFvTG^LTb|F&}yw4N=Sc6wc3$X6`6( zi>15WdqA^e+(1QI6dk5aiefN5#(VOI~G|C>IDh=8{*leJUN$t#(?ob`R$cV=KN6G|%RK}Hk)HsV)0 zIfyH5}k=0R{S3<##Pk$uYPzk)&ere`&FZh#$$e>8bk% zgrzfeo+3R4lmProWZG&W5}TvbjDtUDVF=2#q_4A7QxEv=f!8Xi^ZQ0rV5TzcNT479 zY6t?LLq$K;(V3r_TwBa-;}(n?*<}2s@np0=tlxtR=})J*$|fU z`JFQB@owN~R3m6b6)-kgD$=a4u>+D0BGlUfM=;P~`#l%f^J;dC3fH?CnB!6rNF?u6 zYfzI0M3ROUBrfW}Angp9{ZSJu(45@lud*Q(7`pVWyh%=`N{j-ZfW0wDx5;1@^c*4M}q!2xoNFmA0kfE@)7+pzK8$W#4 zi~$vL;M0@01c69zUQ3CqP5QdOQkLgLZ#1+8FF4$$rS$E^WbT-P6!NhG;BrI>x&b@@ zu(urS%m}OjWu{ct+Mwi?dUfgFlbKUP-oaqz1Kb8O^17WtdU9k_d`Z_?M$AuEbbXqJxGS7bt*sP?@#WF?4fR5I7sl1)y64 z*y9Vf8*`2fNk(&O^Eq_?VjF;%1q26x7{h4oC<$FrQj$5Szyr?<wl^=>O7TNX90!GKmLdC!9uDm>qUQ!$czJ`)hCGz6T$|l(S3+b3O{1pQSc}qFW)Ps zvm2=(%8t@7fc{IWgDn{b6wo1%icKN+y8#5{@iheq;g!6t zo-jLA-X4%ONFW>`=cgW6%6K^p6w+EVeO4XDlU{&A_2OUi3WyYlD^06P3l@^(d(#b3 zQ;B9$H1dRc?Y|=WLzGl~Y{5>(IpqojliNsrzzsf0gUT3RS%VjkjIouwm|Y)b3@uDg zG~Ckiod-iE;ttN;2$#o($$+7bgxbG@yf`=sVg%o5tN4KevC{U6cNET=qqrqZ4jfZR zQvKZvI9`K{WB$Wxh`AJ0x$2Xm)dX4G7oQcgh$6D7{yb9=ksJq#3k*%HaT4FPJ(JendvfXIJfzxqbn4?gZ0$>SA^v|M?PFlpZ#JPryG*~@rp4dB zdo+gk7ptoPa>#E}eCfV?lkh}k5)eYrYyAMenjFw#(R)OLtqdYcCrBlg0a~TDfk(E0 z{My6fj`nROFz>XsfBmVD6}wA!~=NONU>z(}kow=QkhP3;a)+{~i@Vo+{Gv%hSv;hCh?pA=rUOt10_}9ci z{z~G3;0|x+_ag9o_%pDhzXdNzpYn3fE?HfcmqQngPzO&ewbIbPxz-n1+M+IYtR&SP z%ts}zc#4-TYrSPdSY=dmb5@&rUMM7`#*LZ`zxsSq4gdYa?bXt6S%Gjzlrk-!kr1ms z|I&6d=D*zidw)HEM&{)b97X^ePE`L`oh7-ByPdSJxo#iu;h(XpYTUK?Card`0;+%m924W%Hee&-J0MwElL?86kUnD#1Uo5MV2huRq75 z9jk$R(rq((_Y#DqmB(nuUewvVw}FpNl<%zM#EDNW%|V}Fg+Qqh_(C@NKUzcpM0d%bN6C51Xb z!!U)btx0vBxC=1fos!D-S^w2x0|06FrI(2ipFBAzV_N0VScsKuq2qeR24v5N(DKeO z{W6M2Zzj++6MHcfZBwuopGPsYq)ZrlW=vhr??yY&v6|}j6t_i*(}`n#^e>4MA`(6~ z!`u6Kx%U4eKS3R8THR|t4UNvpL51o6Hj%UL^ytxduK_%u&1L5mRaA<+$7&m@PLHC%8MLH@lYzF<_2kW?x!@O9B8DOR_ zxF2Do1sv#M0?A;!#<*5agwY=e8f?J8Ri5=&eT}ZBU(#Kpos{j1eJC*?`6j${zw`P# z-31)u#{|WK_^eFAQgZ-2x3YD@={kIM;i+LmX>u;m{xmbDhM)dYh{qiu{sD(k;Vq7` z6asMjPC9qJ&2q_)dFR4gKpy}UEB}{QkO453`SZP6(VcVJ(!}sF-*cOOaaIb1CmFE? zvd`qqYaR5izGD@1F=b$`?wX+9f27H?R%SAPupbBTr_V`!4?uwY54UMhW&Wf}8ZqR^ z6z5EmVa$|fOqAqJk)$<;uRwFR(l|uq!FVH|s3*dRQtylL`SE>%j&ULv(Te-icX+^v z&6>m0YB{263nHHJr|Z@9M&nQO*5U`C37tQvL2h6}pZDXy!tnvEED>Q!cWH?iN0puu z3z#41*SMx!()07$gf|LpNGhj-Rt5+!U5!~YPwt5R=Tj~ygs(W$z70z}b4FueW-egQ z)(f$mZ(jG1Zezmb9OOG(^bELvZ?;=l)faJ_OOIIRM^)uq#4kBdfHs`)bM$LyEcD@&AKA5xDh`l zI%ya=7N;Ahn=ny-&2|Bfbfk&<%%)zAh04d5AFXQG)TJN&(UhrMN-^j5?MkxFI_won z+BCJj8CSNuNQZX)ZZobfGToPLz5ae#4fG;Oy*%!4X{KKYtM1OhK;5_FzQ-o^s~>Ap z^k^DSqrToXk(SumM2vRy@Jqa;G^plw3-}<|jbh(&JMw1IGkN#+GTE-9s$7@P;GNhg zp+1&I=?Q4I|Lc`IC2u>rq1`l5j90VQ+>QJHE9$zVnp&QJY@kw)N>>q;E+AE!GzASP zJb?hA1T@4@q=wLonx|M2d168b5rU8yh!O%42=&CtEkbM z!j$BI9LOA(T!r7UX65`=B7xQ*lT&09EhHyn~e_kH<{FI zZjciT(}O+xrN*K`a)sl!X8&#d@3mAYoO3VUcgm1<^B*|tq^RWKq?f)Jy%{!5=eP<5 zV}8OT5L|!GOAmQ)GdpQRY)%5!y9*``(4_i(O^lL!O4?HowDHD? z4*w3aMj>k@qAx(a+=YH%Q_VJgUB|W-JN3p{5rO(yh6&~7-P}cdiy{D_-RMUd>n%yw z+JwZNgJa5sw`9Lzq}WxCX90;esVnVCJI}Y*C*rPa@j(Cp?^nV(E{-DpWp`Q=w{PB~E(PDN=6CBJKMuIcH~E?$V&vua8Gu^>k%Lv04N-QO1B9tHRrvhS;%mDi)l) zjqYaVlfSQ6*4t^%u*)0$95u{($4@UR5FHG*L#Au$>vSG29zMy(> z*nB&Z>$!#*@U8#Rm^r!a9$2A$+%FJXqMj|sVo6W8PF^jvOagiXnEpYX+Zn%2a%O^# zLgpTEYc5GwN3>&FPEJCRA6|?Zm!aR1NiZ>%Q&Nm~1#00a} zWo}~nnLY;a_$VD%8NWd5hb)Xh0`H_pOt1<&6@k44eZKB~rW$&xwm9n|e-J?>Py(j7 z;~a*rPH>uEyIz#MMTz_nw?+9!i{V=q#Hfv)yY@<+)>IqoG2cIAE4GH6pR?_?I&3K` z>RVam8(ep45Ka++mp(+*32;3dAP(H^@9=PF-+-TCS7_JEt=KcH5RE-QMl1Sqx5P?r zUxph7%2RIzKhf^7sV}DcD-_5QRxBHMLH^uGjmhRv61UFG)c=Zt#A0dGu4QW)?h-3n#6@NLhM{6k;NE*;MSL2 zk4{*fcO^G_jk#zsu~sAw@=RpC-2NBlwfiO;e^ghkDwGgWwPa8~1T+U4u*8vhH<%YAjx3Iv zF>Y=u5ih6}JFL*9U$&=F8rGrOnM0z6cM#G&jApSX1ZZ?qCoKk!W6yhTe;PZ43`=$D z#l{IjPhgA6LeLSg{|0Qx#l6OF8_!}GwsE~yCKHdxAk;=CB;0(!$k1K>-6436Xouy)RL9V3 zMo1vz>LGl?$hkf_eDhRv-QMDiRI7taX3fbZ;`am0x2B+f3Ex2eunm(mP*(h$ags_9 zRZuJXIc1pYyj6n5OHUM@mRdmdINhYIi+Jt3V6)+*K-6|jYyYYXFC|fRY+7~Wei|tU zV$f($gPM@ZlS!Hg`$pSphHozDc5V)$l>+{dZS>|GBIVgW(*2Fu`Yq;grz#ZvFd6nO zlN!!c`80^`DOgiWiRszvMbV#94+&jc2vY)Gmw0;{H<+zK=tyLF(>JW?8`B=KF>$lQ zP48=x@)x*E1DjP*qy5TT6N+V`QXfy3zDzml*hl9s7L9rGt5e^N6w+49>r&7z0!Qb- z1|J_lGu^A9x4-U{8qD15^QC~&I5p#EBoWE@2JIQG@Qanf=%xnW2|8EQg8)?uBqU5W z>eCm$BI)rkJF@2xliK|y)BC&?nih4wduQ(Z%z}$Wmwg5nS*_s} zseBX{G0WFE;I0rmT%_;Qzi4e)-^`^uOr=5TLhS9EE+_6kVMWE-TWAHs>wmS$f}zG- zy4OQI_{|v1#Zq%9@y5*DSOUH(X}!C!QwGRwh^$dil0cRMLEf89N0$*y&5OTq_{6lM zfPKp6k4W&g<@`RT&hGVTD}4!LO>uM_K;E*2;LcdMelz0w8~5I49k@34VS&?8_C>`a zD-}fj@9feSbNePf#DI+hQ*3Bud&RCXS%pZ$dWcFDJwU%DS) zi~>j_WLCpP;x-T=aJ1eGC!9HzPh!3;XScc>D?f*VO4s7S8MF%;$OPzrD~^)}gn)m& z&-sEJk{GjxC4JAfNM{ZvLyIEZfZ2|3)B_NlX9;9#V@bNvgN$em!9OBcD2oFosyD#~ zu0OZp)?0KxWAz9$r$cwpU~qG}urMKsBx)%LD{4<9PZyb{r`!;<9$eAs3Zp~^Ni-ek z2$o#Sf*aq@iH=(~R7t}}NbfI}1oh<|+cs+f7h*~A=s@Ky4?AZ_ov6y4I=>zlY^ z-+RBvhW#vm$?dW__VThDrEX?|Qt2vIE6*1?pMCId7||MT|GF<1;FfCTxs7gzEowkK zvYpNRI6kDsEaE+N`G^Id0#tbcFTb77CprI&eFeST&cZG~xnF!yG->E#)~X&{G;CYM zhcdJjAohpbyODvNAhU{|0>rghabt-m{jccHuVxQ04S<*7;V!5qSqO z^=C16-kOOif3CjtC8Hp+$tecn5Q+v5aU$OZW*zcu@1~h6w)B3L=d0nwKl*e)g~yFJ zk)>b5l)Fz^T2YqjF3I^fghHynF3dx; z=7J_kIn?mR?NOyrNH}(;`dW2dB;t~@Bb(j`8>t?E zl2V>C4MNuA(P%LD>SO$%&3XA}#8((RUi#=qEj}y84*=4g`h)%kKk8V3-V5dr0fv~m5dIpOU^Gyoy+=5i+7Fpe)E+34e?P1mK85x8>be2;L zh8|-&)AR5RBIdRKm-l36>_Jz5mtqeq__9%JudU%)*=f`e(9=G(zgrzdLAnom^; zqL$WkpiPUPPpdN|s}d94Yu;e_LBZX%{Jqi$xpW*)<B^5<~_x|fz`v9Sq$o!w{&yg;BcCFCM27uN*q&<^Q@@)k(uAV83*u$9snU!=D zgb&<#{tHheY##uS{*L+DS$4jW6fe1ppWB73xZ7BLyuE2zTg~(BkM(9tu)b`ai&@D^ z+wb>nq&(E32)B0gOuxf>&WGN|rmj7;eRogzzvPlIaOut`z+JmS)CQU6@{SKy1<<<( zkT$e_#Pgp7q~zYMj0$fTsdAz$pi9*2&Q3f&`wid!0GP=D>1a{;!nG&i8gu!x;#6~u zjF5u>x67!E`_BPC$)(%ORJ0^@0|pKN4s%YMh6Vh; z(-e?AcB@a%=KAblJx06SMn@+(z4*ngYo0`bJRi)B`Qc3UDa>;nC=RTus4QO?f6%m^ zKaBwZ!^;5Sb6{Jb0?=7`Pp3}mQkyCzauF({8s(BnC0ga5mNrCTX`Tn#w$yixP?oo2 z=!3gqU>{20NKPo}hpj+M_Yl@DxdfH1%f9u8*A7-Ul2X%5HdM}x=KO8~w6?!o IX61ALe^W9Y`Tzg` literal 0 HcmV?d00001 diff --git a/inst/Psychoco2026/slides-beamer.qmd b/inst/Psychoco2026/slides-beamer.qmd new file mode 100644 index 00000000..1be2cd1f --- /dev/null +++ b/inst/Psychoco2026/slides-beamer.qmd @@ -0,0 +1,388 @@ +--- +title: "Convenient and Customizable Base R Plots" +author: "Grant McDermott, Vincent Arel-Bundock, Achim Zeileis" +format: + beamer: + slide_level: 2 + aspectratio: 169 + highlight-style: monochrome + code-block-border-left: "#999999" + theme: uibk + themeoptions: quarto,foot,nofootrule,license + header-includes: + - \URL{https://grantmcdermott.com/tinyplot/} + - \headerimage{banner.png} + - \AddToHook{env/Highlighting/begin}{\small} + - \AddToHook{env/verbatim/begin}{\small} + +pdf-engine: pdflatex + +# social media +author-mastodon: zeileis +author-mastodon-server: fosstodon.org +author-bluesky: zeileis.org +author-website: https://www.zeileis.org/ +author-email: Achim.Zeileis@R-project.org +--- + +```{r} +#| include: false +knitr::opts_chunk$set( + engine = "R", + echo = TRUE, + fig.height = 4.8, + fig.width = 6.4, + out.width = "60%" +) +set.seed(403) +``` + + +## Motivation + +::: {.callout-tip} +# Ross Ihaka & Robert Gentleman (1996) +R: A Language for Data Analysis _and Graphics_ +::: + +\medskip + +**Engines:** Base `graphics` vs. newer flexible `grid` (enabling `ggplot2` and `lattice`). + +\medskip + +**Core of base graphics:** `plot()` generic function and corresponding methods. + +\medskip + +**Default method:** Handles many basic plotting elements like points, lines, etc. + +\medskip + +**Formula method:** Handles various `y ~ x` setups. + +- Scatterplots (numeric `y` vs. numeric `x`). +- Boxplots (numeric `y` vs. categorical `x`). +- Spineplots/spinograms (categorical `y`). + + + +## Motivation + +**Illustration:** Determinants of student performance in end-term exam of an +introductory mathematics course for business and economics students at Universität Innsbruck. + +. . . + +\medskip + +```{r} +data("MathExam14W", package = "psychotools") +math <- MathExam14W |> + transform( + attempt = factor(attempt, ordered = FALSE), + points = rowSums(as.matrix(credits)^2 * (-1)^as.matrix(credits)) + ) |> + transform( + pass = factor(points >= 26, labels = c("fail", "pass")), + score = points/52 + ) |> + subset(select = c("score", "pass", "tests", "attempt", "gender")) +``` + + +## Motivation + +**Dependent variables:** + +- Numeric `score` (proportion of points). +- Binary indicator for `pass`-ing the exam (`score >= 0.5`). + +\medskip + +**Explanatory variables:** Points from previous online `tests`, `attempt`, `gender`. + +\medskip + +```{r} +summary(math) +``` + + +## Motivation: numeric ~ numeric + +```{r} +plot(score ~ tests, data = math) +``` + + +## Motivation: numeric ~ categorical + +```{r} +plot(score ~ attempt, data = math) +``` + + +## Motivation: categorical ~ numeric + +```{r} +plot(pass ~ tests, data = math) +``` + + +## Motivation: categorical ~ categorical + +```{r} +plot(pass ~ attempt, data = math) +``` + + +## Motivation: Limitations + +**So far:** + +- Nifty data visualizations. +- Intuitive, concise syntax. + +\medskip + +**Possible customizations:** + +- Groups via shading, symbols, line types, etc. +- Legends, axes, annotation. +- Grid of faceted displays. +- Layers with additional elements. + +\medskip + +**But:** + +- Requires low-level drawing of such elements. +- Tedious without intuitive, concise syntax. + + +## tinyplot + +:::: {.columns} + +::: {.column width="73%"} + +**Install:** + +```{r} +#| eval: false +install.packages("tinyplot") #or# +install.packages("tinyplot", + repos = "https://grantmcdermott.R-universe.dev") +``` + +**Load:** + +```{r} +library("tinyplot") +``` + +::: + +::: {.column width="20%"} + +![](../../altdoc/logo.png) + +::: + +:::: + +\medskip + +::: {.callout-tip} +# Starting point +`tinyplot()` or its shorthand `plt()` as drop-in replacement for `plot()`. +::: + + +## tinyplot + +```{r} +tinyplot(score ~ tests, data = math) +``` + + +## tinyplot: Features + +**Core ideas:** + +- Preservation of strengths of base R graphics. +- Lightweight extension with convenience features. +- No strong dependencies on non-base packages. +- Improved feature parity vs. `grid`-based `ggplot2` and `lattice`. +- Grouped plots with automatic legends and/or facets. +- Advanced visualization types. +- Customization via themes. + +\medskip + +**Here:** + +```{r} +tinytheme("clean2") +``` + + +## tinyplot: Themes + +```{r} +tinyplot(score ~ tests, data = math) +``` + + +## tinyplot: More plot types + +```{r} +tinyplot(score ~ tests, data = math, type = "jitter") +``` + +## tinyplot: Alpha transparency + +```{r} +tinyplot(score ~ tests, data = math, alpha = 0.4) +``` + +## tinyplot: Axis labels + +```{r} +tinyplot(score ~ tests, data = math, alpha = 0.4, yaxl = "%") +``` + +## tinyplot: Add layers + +```{r} +tinyplot(score ~ tests, data = math, alpha = 0.4, yaxl = "%") +tinyplot_add(type = "loess") +``` + +## tinyplot: Grouped plots with legends + +```{r} +tinyplot(score ~ tests | attempt, data = math, pch = "by", type = "jitter") +``` + +## tinyplot: Facets + +```{r} +#| fig-height: 5.4 +#| fig-width: 8.1 +tinyplot(score ~ tests | attempt, data = math, pch = "by", facet = "by") +``` + +## tinyplot: Facets + +```{r} +#| fig-height: 4 +#| fig-width: 12 +#| out-width: 100% +tinyplot(pass ~ tests | attempt, data = math, facet = "by", + breaks = c(9, 17, 20, 23, 26), facet.args = list(nrow = 1)) +``` + +## tinyplot: Facets + +```{r} +#| fig-height: 5.4 +#| fig-width: 11 +#| out-width: 94% +tinyplot(score ~ tests | attempt, data = math, facet = gender ~ attempt) +``` + +## tinyplot: More plot types + +```{r} +#| fig-height: 6 +#| fig-width: 8.1 +tinyplot(score ~ attempt | attempt, data = math, + type = "violin", flip = TRUE, alpha = 0.4) +``` + +## tinyplot: More plot types + +```{r} +#| fig-height: 6 +#| fig-width: 8.1 +tinyplot(attempt ~ score, data = math, bg = "white", + type = type_ridge(gradient = TRUE, col = "white")) +``` + +## tinyplot: More plot types + +**Interface:** Types can be passed as either a _string_ or _function_. + +| | | | | | +|------------|:----------------|:---------------|:---------------|:-----------| +| _String_ | `"p"` | `"ridge"` | `"loess"` | ... | +| _Function_ | `type_points()` | `type_ridge()` | `type_loess()` | ... | + + +\medskip + +**Arguments:** Can always be passed through the `type` function. + +```{r} +#| eval: false +tinyplot(..., type = type_ridge(gradient = TRUE)) +``` + +\medskip + +**Alternatively:** Through `tinyplot()` if there is no clash with top-level arguments. + +```{r} +#| eval: false +tinyplot(..., type = "ridge", gradient = TRUE) +``` + + +## Use cases + +**Teaching and interactive usage:** + +- Start with simple visualizations and fundamental principles in base graphics. +- Proceed to more complex display using the same type of interface. + +\medskip + +**Package development:** + +- Create flexible visualizations without introducing numerous dependencies. + +\medskip + +**Web R:** + +- Engine for appealing graphics without adding much overhead. + +--- + +## References + +McDermott G, Arel-Bundock V, Zeileis A (2025). + _tinyplot: Lightweight Extension of the Base R Graphics System_. + R package version 0.6.0. + [`doi:10.32614/CRAN.package.tinyplot`](https://doi.org/10.32614/CRAN.package.tinyplot) + +\medskip + +Zeileis A (2025). + "Examining Exams Using Rasch Models and Assessment of Measurement Invariance." + _Austrian Journal of Statistics_, **54**(3), 9-26. + [`doi:10.17713/ajs.v54i3.2055`](https://doi.org/10.17713/ajs.v54i3.2055) + +\medskip + +\medskip + +\medskip + +**Mastodon:** [`@{{< meta author-mastodon >}}@{{< meta author-mastodon-server >}}`](https://{{< meta author-mastodon-server >}}/@{{< meta author-mastodon >}}) + +**Bluesky:** [`@{{< meta author-bluesky >}}`](https://bsky.app/profile/{{< meta author-bluesky >}}) + +**Web:** [`{{< meta author-website >}}`]({{< meta author-website >}}) From cdff8bf217d8f4207ce07a4182a7d6db170475ff Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Sun, 15 Feb 2026 14:29:31 +0100 Subject: [PATCH 8/9] rename revealjs file (for consistency), tweak margins/padding/etc. --- .../{Psychoco2026.qmd => slides-revealjs.qmd} | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) rename inst/Psychoco2026/{Psychoco2026.qmd => slides-revealjs.qmd} (86%) diff --git a/inst/Psychoco2026/Psychoco2026.qmd b/inst/Psychoco2026/slides-revealjs.qmd similarity index 86% rename from inst/Psychoco2026/Psychoco2026.qmd rename to inst/Psychoco2026/slides-revealjs.qmd index caa34ac4..ae755a85 100644 --- a/inst/Psychoco2026/Psychoco2026.qmd +++ b/inst/Psychoco2026/slides-revealjs.qmd @@ -3,11 +3,32 @@ title: "


" subtitle: Convenient and Customizable Base R Plots format: - clean-revealjs: - embed-resources: true - title-slide-attributes: - data-background-image: "../../vignettes/useR2025/img/background.png" - data-background-size: contain + clean-revealjs: + width: 1150 + embed-resources: true + code-line-numbers: false + highlight-style: monochrome + title-slide-attributes: + data-background-image: "../../vignettes/useR2025/img/background.png" + data-background-size: contain + include-in-header: + - text: | + execute: echo: true fig-height: 4.8 @@ -52,9 +73,9 @@ R: A Language for Data Analysis _and Graphics_ **Illustration:** Determinants of student performance in end-term exam of an introductory mathematics course for business and economics students at Universität Innsbruck. -. . . - -```{r} +```{r Prepr} +#| code-fold: true +#| code-summary: "Data preparation" data("MathExam14W", package = "psychotools") math <- MathExam14W |> transform( @@ -118,6 +139,8 @@ plot(pass ~ attempt, data = math) - Nifty data visualizations. - Intuitive, concise syntax. + + **Possible customizations:** - Groups via shading, symbols, line types, etc. @@ -125,6 +148,8 @@ plot(pass ~ attempt, data = math) - Grid of faceted displays. - Layers with additional elements. + + **But:** - Requires low-level drawing of such elements. @@ -137,7 +162,7 @@ plot(pass ~ attempt, data = math) ::: {.column width="73%"} -Install: +**Install:** ```{r} #| eval: false @@ -146,7 +171,7 @@ install.packages("tinyplot", repos = "https://grantmcdermott.R-universe.dev") ``` -Load: +**Load:** ```{r} library("tinyplot") @@ -154,6 +179,10 @@ library("tinyplot") ::: +::: {.column width="7%"} + +::: + ::: {.column width="20%"} ![](../../altdoc/logo.png) @@ -291,6 +320,8 @@ tinyplot(attempt ~ score, data = math, bg = "white", | _Function_ | `type_points()` | `type_ridge()` | `type_loess()` | ... | + + **Arguments:** Can always be passed through the `type` function. ```{r} @@ -298,6 +329,8 @@ tinyplot(attempt ~ score, data = math, bg = "white", tinyplot(..., type = type_ridge(gradient = TRUE)) ``` + + **Alternatively:** Through `tinyplot()` if there is no clash with top-level arguments. ```{r} @@ -335,6 +368,7 @@ Zeileis A (2025). _Austrian Journal of Statistics_, **54**(3), 9-26. [`doi:10.17713/ajs.v54i3.2055`](https://doi.org/10.17713/ajs.v54i3.2055) + {{< iconify mdi mastodon >}}   [`@zeileis@fosstodon.org`](https://fosstodon.org/@zeileis) {{< iconify fa6-brands bluesky >}}   [`@zeileis.org`](https://bsky.app/profile/zeileis.org) From b10e870646194e1ce07921900d708c5a1ea579c4 Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Sun, 15 Feb 2026 14:30:12 +0100 Subject: [PATCH 9/9] rename abstract to README and add notes/links/etc --- inst/Psychoco2026/{abstract.md => README.md} | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) rename inst/Psychoco2026/{abstract.md => README.md} (71%) diff --git a/inst/Psychoco2026/abstract.md b/inst/Psychoco2026/README.md similarity index 71% rename from inst/Psychoco2026/abstract.md rename to inst/Psychoco2026/README.md index 19d5128b..96b9b84e 100644 --- a/inst/Psychoco2026/abstract.md +++ b/inst/Psychoco2026/README.md @@ -1,6 +1,12 @@ -tinyplot: Lightweight Extension of the Base R Graphics System +## tinyplot: Lightweight Extension of the Base R Graphics System -Grant McDermott, Vincent Arel-Bundock, Achim Zeileis +_Grant McDermott, Vincent Arel-Bundock, Achim Zeileis_ + +**Presented at:** Psychoco 2026 - International Workshop on Psychometric Computing, Università di Padova, Italy, February 5-6. + +**PDF slides:** + +**Abstract:** The base R graphics system provides a lot of powerful infrastructure for drawing data visualizations. At the core is the `plot()` generic function with its @@ -26,3 +32,10 @@ visualization examples, highlighting strengths and weaknesses compared to other packages. The package is available from CRAN () and has many more galleries and tutorials at . + +**Notes:** + +- The source file for the PDF slides is `slides-beamer.qmd`. This requires that + the [UIBK beamer class](https://git.uibk.ac.at/uibklatex/beamer_letter) is installed. +- As an alternative a similar HTML version of the slides can be created from + `slides-revealjs.qmd`.