Skip to content

Commit f85cffb

Browse files
committed
Merge branch 'main' of github.com:forwards/forwards.github.io
2 parents 0f94356 + 3e522ba commit f85cffb

File tree

15 files changed

+921
-70
lines changed

15 files changed

+921
-70
lines changed

_freeze/site_libs/clipboard/clipboard.min.js

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

_freeze/site_libs/quarto-listing/list.min.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
const kProgressiveAttr = "data-src";
2+
let categoriesLoaded = false;
3+
4+
window.quartoListingCategory = (category) => {
5+
// category is URI encoded in EJS template for UTF-8 support
6+
category = decodeURIComponent(atob(category));
7+
if (categoriesLoaded) {
8+
activateCategory(category);
9+
setCategoryHash(category);
10+
}
11+
};
12+
13+
window["quarto-listing-loaded"] = () => {
14+
// Process any existing hash
15+
const hash = getHash();
16+
17+
if (hash) {
18+
// If there is a category, switch to that
19+
if (hash.category) {
20+
// category hash are URI encoded so we need to decode it before processing
21+
// so that we can match it with the category element processed in JS
22+
activateCategory(decodeURIComponent(hash.category));
23+
}
24+
// Paginate a specific listing
25+
const listingIds = Object.keys(window["quarto-listings"]);
26+
for (const listingId of listingIds) {
27+
const page = hash[getListingPageKey(listingId)];
28+
if (page) {
29+
showPage(listingId, page);
30+
}
31+
}
32+
}
33+
34+
const listingIds = Object.keys(window["quarto-listings"]);
35+
for (const listingId of listingIds) {
36+
// The actual list
37+
const list = window["quarto-listings"][listingId];
38+
39+
// Update the handlers for pagination events
40+
refreshPaginationHandlers(listingId);
41+
42+
// Render any visible items that need it
43+
renderVisibleProgressiveImages(list);
44+
45+
// Whenever the list is updated, we also need to
46+
// attach handlers to the new pagination elements
47+
// and refresh any newly visible items.
48+
list.on("updated", function () {
49+
renderVisibleProgressiveImages(list);
50+
setTimeout(() => refreshPaginationHandlers(listingId));
51+
52+
// Show or hide the no matching message
53+
toggleNoMatchingMessage(list);
54+
});
55+
}
56+
};
57+
58+
window.document.addEventListener("DOMContentLoaded", function (_event) {
59+
// Attach click handlers to categories
60+
const categoryEls = window.document.querySelectorAll(
61+
".quarto-listing-category .category"
62+
);
63+
64+
for (const categoryEl of categoryEls) {
65+
// category needs to support non ASCII characters
66+
const category = decodeURIComponent(
67+
atob(categoryEl.getAttribute("data-category"))
68+
);
69+
categoryEl.onclick = () => {
70+
activateCategory(category);
71+
setCategoryHash(category);
72+
};
73+
}
74+
75+
// Attach a click handler to the category title
76+
// (there should be only one, but since it is a class name, handle N)
77+
const categoryTitleEls = window.document.querySelectorAll(
78+
".quarto-listing-category-title"
79+
);
80+
for (const categoryTitleEl of categoryTitleEls) {
81+
categoryTitleEl.onclick = () => {
82+
activateCategory("");
83+
setCategoryHash("");
84+
};
85+
}
86+
87+
categoriesLoaded = true;
88+
});
89+
90+
function toggleNoMatchingMessage(list) {
91+
const selector = `#${list.listContainer.id} .listing-no-matching`;
92+
const noMatchingEl = window.document.querySelector(selector);
93+
if (noMatchingEl) {
94+
if (list.visibleItems.length === 0) {
95+
noMatchingEl.classList.remove("d-none");
96+
} else {
97+
if (!noMatchingEl.classList.contains("d-none")) {
98+
noMatchingEl.classList.add("d-none");
99+
}
100+
}
101+
}
102+
}
103+
104+
function setCategoryHash(category) {
105+
setHash({ category });
106+
}
107+
108+
function setPageHash(listingId, page) {
109+
const currentHash = getHash() || {};
110+
currentHash[getListingPageKey(listingId)] = page;
111+
setHash(currentHash);
112+
}
113+
114+
function getListingPageKey(listingId) {
115+
return `${listingId}-page`;
116+
}
117+
118+
function refreshPaginationHandlers(listingId) {
119+
const listingEl = window.document.getElementById(listingId);
120+
const paginationEls = listingEl.querySelectorAll(
121+
".pagination li.page-item:not(.disabled) .page.page-link"
122+
);
123+
for (const paginationEl of paginationEls) {
124+
paginationEl.onclick = (sender) => {
125+
setPageHash(listingId, sender.target.getAttribute("data-i"));
126+
showPage(listingId, sender.target.getAttribute("data-i"));
127+
return false;
128+
};
129+
}
130+
}
131+
132+
function renderVisibleProgressiveImages(list) {
133+
// Run through the visible items and render any progressive images
134+
for (const item of list.visibleItems) {
135+
const itemEl = item.elm;
136+
if (itemEl) {
137+
const progressiveImgs = itemEl.querySelectorAll(
138+
`img[${kProgressiveAttr}]`
139+
);
140+
for (const progressiveImg of progressiveImgs) {
141+
const srcValue = progressiveImg.getAttribute(kProgressiveAttr);
142+
if (srcValue) {
143+
progressiveImg.setAttribute("src", srcValue);
144+
}
145+
progressiveImg.removeAttribute(kProgressiveAttr);
146+
}
147+
}
148+
}
149+
}
150+
151+
function getHash() {
152+
// Hashes are of the form
153+
// #name:value|name1:value1|name2:value2
154+
const currentUrl = new URL(window.location);
155+
const hashRaw = currentUrl.hash ? currentUrl.hash.slice(1) : undefined;
156+
return parseHash(hashRaw);
157+
}
158+
159+
const kAnd = "&";
160+
const kEquals = "=";
161+
162+
function parseHash(hash) {
163+
if (!hash) {
164+
return undefined;
165+
}
166+
const hasValuesStrs = hash.split(kAnd);
167+
const hashValues = hasValuesStrs
168+
.map((hashValueStr) => {
169+
const vals = hashValueStr.split(kEquals);
170+
if (vals.length === 2) {
171+
return { name: vals[0], value: vals[1] };
172+
} else {
173+
return undefined;
174+
}
175+
})
176+
.filter((value) => {
177+
return value !== undefined;
178+
});
179+
180+
const hashObj = {};
181+
hashValues.forEach((hashValue) => {
182+
hashObj[hashValue.name] = decodeURIComponent(hashValue.value);
183+
});
184+
return hashObj;
185+
}
186+
187+
function makeHash(obj) {
188+
return Object.keys(obj)
189+
.map((key) => {
190+
return `${key}${kEquals}${obj[key]}`;
191+
})
192+
.join(kAnd);
193+
}
194+
195+
function setHash(obj) {
196+
const hash = makeHash(obj);
197+
window.history.pushState(null, null, `#${hash}`);
198+
}
199+
200+
function showPage(listingId, page) {
201+
const list = window["quarto-listings"][listingId];
202+
if (list) {
203+
list.show((page - 1) * list.page + 1, list.page);
204+
}
205+
}
206+
207+
function activateCategory(category) {
208+
// Deactivate existing categories
209+
const activeEls = window.document.querySelectorAll(
210+
".quarto-listing-category .category.active"
211+
);
212+
for (const activeEl of activeEls) {
213+
activeEl.classList.remove("active");
214+
}
215+
216+
// Activate this category
217+
const categoryEl = window.document.querySelector(
218+
`.quarto-listing-category .category[data-category='${btoa(
219+
encodeURIComponent(category)
220+
)}']`
221+
);
222+
if (categoryEl) {
223+
categoryEl.classList.add("active");
224+
}
225+
226+
// Filter the listings to this category
227+
filterListingCategory(category);
228+
}
229+
230+
function filterListingCategory(category) {
231+
const listingIds = Object.keys(window["quarto-listings"]);
232+
for (const listingId of listingIds) {
233+
const list = window["quarto-listings"][listingId];
234+
if (list) {
235+
if (category === "") {
236+
// resets the filter
237+
list.filter();
238+
} else {
239+
// filter to this category
240+
list.filter(function (item) {
241+
const itemValues = item.values();
242+
if (itemValues.categories !== null) {
243+
const categories = decodeURIComponent(
244+
atob(itemValues.categories)
245+
).split(",");
246+
return categories.includes(category);
247+
} else {
248+
return false;
249+
}
250+
});
251+
}
252+
}
253+
}
254+
}

_quarto.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ website:
2626
© 2024 Forwards, under [CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/) license.<br>
2727
2828
navbar:
29-
logo: /images/forwards.svg
29+
logo: /images/forwards_hex_solid_border.png
3030
logo-href: /index.html
3131
left:
3232
# - menu:
@@ -79,8 +79,9 @@ website:
7979
format:
8080
html:
8181
theme:
82-
light: cosmo
83-
dark: [darkly, theme-dark.scss] #cosmo
82+
light: forwards-light.scss
83+
dark: forwards-dark.scss
84+
highlight-style: forwards.theme
8485
css: styles.css
8586
toc: true
8687
email-obfuscation: references

blog/2025/package-dev-workshops/index.qmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ draft: false
1212

1313
The [Forwards](https://forwards.github.io/) teaching team is hosting two workshop series beginning next month to teach participants how to build their own R packages. No prior package building experience is required. The course consists of five 90-minute sessions held every two weeks, and will cover core topics such as the structure of R packages, writing and documenting functions, managing dependencies, writing tests, checking packages for errors, and preparing them for distribution on platforms like CRAN or GitHub. Participants will also learn how to package datasets for use within their packages.
1414

15-
Two cohorts are being offered to accommodate different time zones. [Cohort 1]( https://forwards.github.io/package-dev/workshops/summer-2025-cohort-1.html) meets every other Monday starting June 2, from 09:00 to 10:30 UTC and will be taught by Pao Corrales and Ella Kaye. [Cohort 2]( https://forwards.github.io/package-dev/workshops/summer-2025-cohort-1.html) meets every other Tuesday starting June 3, from 14:30 to 16:00 UTC and will be led by Emma Rand, Joyce Robbins, and Heather Turner.
15+
Two cohorts are being offered to accommodate different time zones. [Cohort 1]( https://forwards.github.io/package-dev/workshops/summer-2025-cohort-1.html) meets every other Monday starting June 2, from 09:00 to 10:30 UTC and will be taught by Pao Corrales and Ella Kaye. [Cohort 2]( https://forwards.github.io/package-dev/workshops/summer-2025-cohort-2.html) meets every other Tuesday starting June 3, from 14:30 to 16:00 UTC and will be led by Emma Rand, Joyce Robbins, and Heather Turner.
1616

1717
More information on the content of each session as well as links to register are available on Forwards’ newly revamped [R Package Development Workshop](https://forwards.github.io/package-dev/workshops.html) web site. Note that the modules were designed to be used with or without an accompanying workshop, so learners may choose to work through the material on their own.
1818

0 commit comments

Comments
 (0)