Skip to content

Commit

Permalink
Deploy preview for PR 37 🛫
Browse files Browse the repository at this point in the history
  • Loading branch information
d33bs committed Jul 29, 2024
1 parent 69d40ab commit 78338a3
Show file tree
Hide file tree
Showing 184 changed files with 29,033 additions and 0 deletions.
691 changes: 691 additions & 0 deletions preview/pr-37/2022/10/17/Use-Linting-Tools-to-Save-Time.html

Large diffs are not rendered by default.

763 changes: 763 additions & 0 deletions preview/pr-37/2022/11/27/Diagrams-as-Code.html

Large diffs are not rendered by default.

Large diffs are not rendered by default.

750 changes: 750 additions & 0 deletions preview/pr-37/2022/12/12/Remove-Unused-Code-to-Avoid-Decay.html

Large diffs are not rendered by default.

779 changes: 779 additions & 0 deletions preview/pr-37/2023/01/03/Linting-Documentation-as-Code.html

Large diffs are not rendered by default.

742 changes: 742 additions & 0 deletions preview/pr-37/2023/01/17/Timebox-Your-Software-Work.html

Large diffs are not rendered by default.

731 changes: 731 additions & 0 deletions preview/pr-37/2023/01/30/Software-Linting-with-R.html

Large diffs are not rendered by default.

780 changes: 780 additions & 0 deletions preview/pr-37/2023/02/13/Branch-Review-and-Learn.html

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1,113 changes: 1,113 additions & 0 deletions preview/pr-37/2023/09/05/Python-Packaging-as-Publishing.html

Large diffs are not rendered by default.

900 changes: 900 additions & 0 deletions preview/pr-37/2023/10/04/Data-Quality-Validation.html

Large diffs are not rendered by default.

1,049 changes: 1,049 additions & 0 deletions preview/pr-37/2023/11/15/Codesgiving-Open-source-Contribution-Walkthrough.html

Large diffs are not rendered by default.

1,026 changes: 1,026 additions & 0 deletions preview/pr-37/2024/01/22/Python-Memory-Management-and-Troubleshooting.html

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

927 changes: 927 additions & 0 deletions preview/pr-37/2024/07/28/Uncovering-Code-Coverage.html

Large diffs are not rendered by default.

481 changes: 481 additions & 0 deletions preview/pr-37/404.html

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions preview/pr-37/_scripts/anchors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
creates link next to each heading that links to that section.
*/

{
const onLoad = () => {
// for each heading
const headings = document.querySelectorAll(
"h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]"
);
for (const heading of headings) {
// create anchor link
const link = document.createElement("a");
link.classList.add("icon", "fa-solid", "fa-link", "anchor");
link.href = "#" + heading.id;
link.setAttribute("aria-label", "link to this section");
heading.append(link);

// if first heading in the section, move id to parent section
if (heading.matches("section > :first-child")) {
heading.parentElement.id = heading.id;
heading.removeAttribute("id");
}
}
};

// scroll to target of url hash
const scrollToTarget = () => {
const id = window.location.hash.replace("#", "");
const target = document.getElementById(id);

if (!target) return;
const offset = document.querySelector("header").clientHeight || 0;
window.scrollTo({
top: target.getBoundingClientRect().top + window.scrollY - offset,
behavior: "smooth",
});
};

// after page loads
window.addEventListener("load", onLoad);
window.addEventListener("load", scrollToTarget);
window.addEventListener("tagsfetched", scrollToTarget);

// when hash nav happens
window.addEventListener("hashchange", scrollToTarget);
}
25 changes: 25 additions & 0 deletions preview/pr-37/_scripts/dark-mode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
manages light/dark mode.
*/

{
// immediately load saved (or default) mode before page renders
document.documentElement.dataset.dark =
window.localStorage.getItem("dark-mode") ?? "false";

const onLoad = () => {
// update toggle button to match loaded mode
document.querySelector(".dark-toggle").checked =
document.documentElement.dataset.dark === "true";
};

// after page loads
window.addEventListener("load", onLoad);

// when user toggles mode button
window.onDarkToggleChange = (event) => {
const value = event.target.checked;
document.documentElement.dataset.dark = value;
window.localStorage.setItem("dark-mode", value);
};
}
67 changes: 67 additions & 0 deletions preview/pr-37/_scripts/fetch-tags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
fetches tags (aka "topics") from a given GitHub repo and adds them to row of
tag buttons. specify repo in data-repo attribute on row.
*/

{
const onLoad = async () => {
// get tag rows with specified repos
const rows = document.querySelectorAll("[data-repo]");

// for each repo
for (const row of rows) {
// get props from tag row
const repo = row.dataset.repo.trim();
const link = row.dataset.link.trim();

// get tags from github
if (!repo) continue;
let tags = await fetchTags(repo);

// filter out tags already present in row
let existing = [...row.querySelectorAll(".tag")].map((tag) =>
window.normalizeTag(tag.innerText)
);
tags = tags.filter((tag) => !existing.includes(normalizeTag(tag)));

// add tags to row
for (const tag of tags) {
const a = document.createElement("a");
a.classList.add("tag");
a.innerHTML = tag;
a.href = `${link}?search="tag: ${tag}"`;
a.dataset.tooltip = `Show items with the tag "${tag}"`;
row.append(a);
}

// delete tags container if empty
if (!row.innerText.trim()) row.remove();
}

// emit "tags done" event for other scripts to listen for
window.dispatchEvent(new Event("tagsfetched"));
};

// after page loads
window.addEventListener("load", onLoad);

// GitHub topics endpoint
const api = "https://api.github.com/repos/REPO/topics";
const headers = new Headers();
headers.set("Accept", "application/vnd.github+json");

// get tags from GitHub based on repo name
const fetchTags = async (repo) => {
const url = api.replace("REPO", repo);
try {
const response = await (await fetch(url)).json();
if (response.names) return response.names;
else throw new Error(JSON.stringify(response));
} catch (error) {
console.groupCollapsed("GitHub fetch tags error");
console.log(error);
console.groupEnd();
return [];
}
};
}
215 changes: 215 additions & 0 deletions preview/pr-37/_scripts/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
filters elements on page based on url or search box.
syntax: term1 term2 "full phrase 1" "full phrase 2" "tag: tag 1"
match if: all terms AND at least one phrase AND at least one tag
*/
{
// elements to filter
const elementSelector = ".card, .citation, .post-excerpt";
// search box element
const searchBoxSelector = ".search-box";
// results info box element
const infoBoxSelector = ".search-info";
// tags element
const tagSelector = ".tag";

// split search query into terms, phrases, and tags
const splitQuery = (query) => {
// split into parts, preserve quotes
const parts = query.match(/"[^"]*"|\S+/g) || [];

// bins
const terms = [];
const phrases = [];
const tags = [];

// put parts into bins
for (let part of parts) {
if (part.startsWith('"')) {
part = part.replaceAll('"', "").trim();
if (part.startsWith("tag:"))
tags.push(normalizeTag(part.replace(/tag:\s*/, "")));
else phrases.push(part.toLowerCase());
} else terms.push(part.toLowerCase());
}

return { terms, phrases, tags };
};

// normalize tag string for comparison
window.normalizeTag = (tag) =>
tag.trim().toLowerCase().replaceAll(/-|\s+/g, " ");

// get data attribute contents of element and children
const getAttr = (element, attr) =>
[element, ...element.querySelectorAll(`[data-${attr}]`)]
.map((element) => element.dataset[attr])
.join(" ");

// determine if element should show up in results based on query
const elementMatches = (element, { terms, phrases, tags }) => {
// tag elements within element
const tagElements = [...element.querySelectorAll(".tag")];

// check if text content exists in element
const hasText = (string) =>
(
element.innerText +
getAttr(element, "tooltip") +
getAttr(element, "search")
)
.toLowerCase()
.includes(string);
// check if text matches a tag in element
const hasTag = (string) =>
tagElements.some((tag) => normalizeTag(tag.innerText) === string);

// match logic
return (
(terms.every(hasText) || !terms.length) &&
(phrases.some(hasText) || !phrases.length) &&
(tags.some(hasTag) || !tags.length)
);
};

// loop through elements, hide/show based on query, and return results info
const filterElements = (parts) => {
let elements = document.querySelectorAll(elementSelector);

// results info
let x = 0;
let n = elements.length;
let tags = parts.tags;

// filter elements
for (const element of elements) {
if (elementMatches(element, parts)) {
element.style.display = "";
x++;
} else element.style.display = "none";
}

return [x, n, tags];
};

// highlight search terms
const highlightMatches = async ({ terms, phrases }) => {
// make sure Mark library available
if (typeof Mark === "undefined") return;

// reset
new Mark(document.body).unmark();

// limit number of highlights to avoid slowdown
let counter = 0;
const filter = () => counter++ < 100;

// highlight terms and phrases
new Mark(elementSelector)
.mark(terms, { separateWordSearch: true, filter })
.mark(phrases, { separateWordSearch: false, filter });
};

// update search box based on query
const updateSearchBox = (query = "") => {
const boxes = document.querySelectorAll(searchBoxSelector);

for (const box of boxes) {
const input = box.querySelector("input");
const button = box.querySelector("button");
const icon = box.querySelector("button i");
input.value = query;
icon.className = input.value.length
? "icon fa-solid fa-xmark"
: "icon fa-solid fa-magnifying-glass";
button.disabled = input.value.length ? false : true;
}
};

// update info box based on query and results
const updateInfoBox = (query, x, n) => {
const boxes = document.querySelectorAll(infoBoxSelector);

if (query.trim()) {
// show all info boxes
boxes.forEach((info) => (info.style.display = ""));

// info template
let info = "";
info += `Showing ${x.toLocaleString()} of ${n.toLocaleString()} results<br>`;
info += "<a href='./'>Clear search</a>";

// set info HTML string
boxes.forEach((el) => (el.innerHTML = info));
}
// if nothing searched
else {
// hide all info boxes
boxes.forEach((info) => (info.style.display = "none"));
}
};

// update tags based on query
const updateTags = (query) => {
const { tags } = splitQuery(query);
document.querySelectorAll(tagSelector).forEach((tag) => {
// set active if tag is in query
if (tags.includes(normalizeTag(tag.innerText)))
tag.setAttribute("data-active", "");
else tag.removeAttribute("data-active");
});
};

// run search with query
const runSearch = (query = "") => {
const parts = splitQuery(query);
const [x, n] = filterElements(parts);
updateSearchBox(query);
updateInfoBox(query, x, n);
updateTags(query);
highlightMatches(parts);
};

// update url based on query
const updateUrl = (query = "") => {
const url = new URL(window.location);
let params = new URLSearchParams(url.search);
params.set("search", query);
url.search = params.toString();
window.history.replaceState(null, null, url);
};

// search based on url param
const searchFromUrl = () => {
const query =
new URLSearchParams(window.location.search).get("search") || "";
runSearch(query);
};

// return func that runs after delay
const debounce = (callback, delay = 250) => {
let timeout;
return (...args) => {
window.clearTimeout(timeout);
timeout = window.setTimeout(() => callback(...args), delay);
};
};

// when user types into search box
const debouncedRunSearch = debounce(runSearch, 1000);
window.onSearchInput = (target) => {
debouncedRunSearch(target.value);
updateUrl(target.value);
};

// when user clears search box with button
window.onSearchClear = () => {
runSearch();
updateUrl();
};

// after page loads
window.addEventListener("load", searchFromUrl);
// after tags load
window.addEventListener("tagsfetched", searchFromUrl);
}
14 changes: 14 additions & 0 deletions preview/pr-37/_scripts/site-search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
for site search component. searches site/domain via google.
*/

{
// when user submits site search form/box
window.onSiteSearchSubmit = (event) => {
event.preventDefault();
const google = "https://www.google.com/search?q=site:";
const site = window.location.origin;
const query = event.target.elements.query.value;
window.location = google + site + " " + query;
};
}
25 changes: 25 additions & 0 deletions preview/pr-37/_scripts/table-wrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
put a wrapper around each table to allow scrolling.
*/

{
const onLoad = () => {
// for each top-level table
const tables = document.querySelectorAll("table:not(table table)");
for (const table of tables) {
// create wrapper with scroll
const wrapper = document.createElement("div");
wrapper.style.overflowX = "auto";

// undo css force-text-wrap
table.style.overflowWrap = "normal";

// add wrapper around table
table.parentNode.insertBefore(wrapper, table);
wrapper.appendChild(table);
}
};

// after page loads
window.addEventListener("load", onLoad);
}
Loading

0 comments on commit 78338a3

Please sign in to comment.