diff --git a/Mac/MainWindow/Detail/page.html b/Mac/MainWindow/Detail/page.html
index 97e7c4541..7482a74b0 100644
--- a/Mac/MainWindow/Detail/page.html
+++ b/Mac/MainWindow/Detail/page.html
@@ -3,6 +3,7 @@
+
diff --git a/Mac/MainWindow/Detail/styleSheet.css b/Mac/MainWindow/Detail/styleSheet.css
index 107a7cb7e..c39d376ce 100644
--- a/Mac/MainWindow/Detail/styleSheet.css
+++ b/Mac/MainWindow/Detail/styleSheet.css
@@ -181,3 +181,51 @@ img[src*="feedblitz"],
img[src*="share-buttons"] {
display: none !important;
}
+
+
+/* Newsfoot specific styles. Structural styles come first, theme styles second */
+.newsfoot-footnote-container {
+ position: relative;
+ display: inline-block;
+}
+.newsfoot-footnote-popover {
+ position: absolute;
+ display: block;
+ padding: 0em 1em;
+ margin: 1em;
+ left: -11em;
+ right: -11em;
+ max-width: none;
+ border-radius: 0.3em;
+ box-sizing: border-box;
+}
+a.footnote {
+ display: inline-block;
+ text-decoration: none;
+ padding: 0.05em 0.75em;
+ border-radius: 1em;
+ min-width: 1em;
+ text-align: center;
+ font-size: 0.8em;
+ line-height: 1em;
+ position:relative;
+ top: -0.1em;
+}
+
+/* light / default */
+.newsfoot-footnote-popover {
+ background: #fafafa;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
+ color: black;
+ border: 1px solid #ccc;
+}
+body a.footnote,
+body a.footnote:visited {
+ background: #aaa;
+ color: white;
+ transition: background-color 200ms ease-out;
+}
+a.footnote:hover {
+ background: #666;
+ transition: background-color 200ms ease-out;
+}
diff --git a/Shared/Article Rendering/newsfoot.js b/Shared/Article Rendering/newsfoot.js
index ca725c19e..7374610e7 100644
--- a/Shared/Article Rendering/newsfoot.js
+++ b/Shared/Article Rendering/newsfoot.js
@@ -1,119 +1,122 @@
+ (function () {
+ // @ts-check
+ /** @param {Node | null} el */
+ const remove = (el) => { if (el) el.parentElement.removeChild(el) };
-// @ts-check
-/** @param {Node | null} el */
-const remove = (el) => { if (el) el.parentElement.removeChild(el) };
+ const stripPx = (s) => +s.slice(0, -2);
-const stripPx = (s) => +s.slice(0, -2);
+ /** @param {string} tag
+ * @param {string} cls
+ * @returns HTMLElement
+ */
+ function newEl(tag, cls) {
+ const el = document.createElement(tag);
+ el.classList.add(cls);
+ return el;
+ }
-/** @param {string} tag
- * @param {string} cls
- * @returns HTMLElement
- */
-function newEl(tag, cls) {
- const el = document.createElement(tag);
- el.classList.add(cls);
- return el;
-}
+ /** @type {(fn: (...args: T) => void, t: number) => ((...args: T) => void)} */
+ function debounce(f, ms) {
+ let t = Date.now();
+ return (...args) => {
+ const now = Date.now();
+ if (now - t < ms) return;
+ t = now;
+ f(...args);
+ };
+ }
-/** @type {(fn: (...args: T) => void, t: number) => ((...args: T) => void)} */
-function debounce(f, ms) {
- let t = Date.now();
- return (...args) => {
- const now = Date.now();
- if (now - t < ms) return;
- t = now;
- f(...args);
- };
-}
+ const clsPrefix = "newsfoot-footnote-";
+ const CONTAINER_CLS = `${clsPrefix}container`;
+ const POPOVER_CLS = `${clsPrefix}popover`;
-const clsPrefix = "newsfoot-footnote-";
-const CONTAINER_CLS = `${clsPrefix}container`;
-const POPOVER_CLS = `${clsPrefix}popover`;
-
-/**
- * @param {Node} content
- * @returns {HTMLElement}
- */
-function footnoteMarkup(content) {
- const popover = newEl("div", POPOVER_CLS);
- popover.appendChild(content);
- return popover;
-}
-
-class Footnote {
/**
* @param {Node} content
- * @param {Element} fnref
+ * @returns {HTMLElement}
*/
- constructor(content, fnref) {
- this.popover = footnoteMarkup(content);
- this.style = window.getComputedStyle(this.popover);
- this.fnref = fnref;
- this.fnref.closest(`.${CONTAINER_CLS}`).appendChild(this.popover);
- this.reposition();
-
- /** @type {(ev:MouseEvent) => void} */
- this.clickoutHandler = (ev) => {
- if (!(ev.target instanceof Element)) return;
- if (ev.target.closest(`.${POPOVER_CLS}`) === this.popover) return;
- this.cleanup();
- }
- document.addEventListener("click", this.clickoutHandler, {capture: true});
-
- this.resizeHandler = debounce(() => this.reposition(), 20);
- window.addEventListener("resize", this.resizeHandler);
+ function footnoteMarkup(content) {
+ const popover = newEl("div", POPOVER_CLS);
+ popover.appendChild(content);
+ return popover;
}
-
- cleanup() {
- remove(this.popover);
- document.removeEventListener("click", this.clickoutHandler, {capture: true});
- window.removeEventListener("resize", this.resizeHandler);
- delete this.popover;
- delete this.clickoutHandler;
- delete this.resizeHandler;
- }
-
- reposition() {
- const refRect = this.fnref.getBoundingClientRect();
- const center = refRect.left + (refRect.width / 2);
- const popoverHalfWidth = this.popover.clientWidth / 2;
- const marginLeft = stripPx(this.style.marginLeft);
- const marginRight = stripPx(this.style.marginRight);
-
- let offset = 0;
- if (center + popoverHalfWidth + marginRight > window.innerWidth) {
- offset = -((center + popoverHalfWidth + marginRight) - window.innerWidth);
- }
- else if (center - (popoverHalfWidth + marginLeft) < 0) {
- offset = (popoverHalfWidth + marginLeft) - center;
- }
- this.popover.style.transform = `translate(${offset}px)`;
- }
-}
-/** @param {Node} n */
-function fragFromContents(n) {
- const frag = document.createDocumentFragment();
- n.childNodes.forEach((ch) => frag.appendChild(ch));
- return frag;
-}
-
-/** @param {HTMLAnchorElement} a */
-function installContainer(a) {
- if (!a.parentElement.matches(`.${CONTAINER_CLS}`)) {
- const container = newEl("div", CONTAINER_CLS);
- a.parentElement.insertBefore(container, a);
- container.appendChild(a);
+ class Footnote {
+ /**
+ * @param {Node} content
+ * @param {Element} fnref
+ */
+ constructor(content, fnref) {
+ this.popover = footnoteMarkup(content);
+ this.style = window.getComputedStyle(this.popover);
+ this.fnref = fnref;
+ this.fnref.closest(`.${CONTAINER_CLS}`).appendChild(this.popover);
+ this.reposition();
+
+ /** @type {(ev:MouseEvent) => void} */
+ this.clickoutHandler = (ev) => {
+ if (!(ev.target instanceof Element)) return;
+ if (ev.target.closest(`.${POPOVER_CLS}`) === this.popover) return;
+ this.cleanup();
+ }
+ document.addEventListener("click", this.clickoutHandler, {capture: true});
+
+ this.resizeHandler = debounce(() => this.reposition(), 20);
+ window.addEventListener("resize", this.resizeHandler);
+ }
+
+ cleanup() {
+ remove(this.popover);
+ document.removeEventListener("click", this.clickoutHandler, {capture: true});
+ window.removeEventListener("resize", this.resizeHandler);
+ delete this.popover;
+ delete this.clickoutHandler;
+ delete this.resizeHandler;
+ }
+
+ reposition() {
+ const refRect = this.fnref.getBoundingClientRect();
+ const center = refRect.left + (refRect.width / 2);
+ const popoverHalfWidth = this.popover.clientWidth / 2;
+ const marginLeft = stripPx(this.style.marginLeft);
+ const marginRight = stripPx(this.style.marginRight);
+
+ let offset = 0;
+ if (center + popoverHalfWidth + marginRight > window.innerWidth) {
+ offset = -((center + popoverHalfWidth + marginRight) - window.innerWidth);
+ }
+ else if (center - (popoverHalfWidth + marginLeft) < 0) {
+ offset = (popoverHalfWidth + marginLeft) - center;
+ }
+ this.popover.style.transform = `translate(${offset}px)`;
+ }
}
-}
-document.addEventListener("click", (ev) => {
- if (!(ev.target && ev.target instanceof HTMLAnchorElement)) return;
- if (!ev.target.matches(".footnote")) return;
- ev.preventDefault();
-
- const content = document.querySelector(`[id='${ev.target.hash.substring(1)}']`).cloneNode(true);
- if (content instanceof HTMLElement) remove(content.querySelector(".reversefootnote"));
- installContainer(ev.target);
- void new Footnote(fragFromContents(content), ev.target);
- });
+ /** @param {Node} n */
+ function fragFromContents(n) {
+ const frag = document.createDocumentFragment();
+ n.childNodes.forEach((ch) => frag.appendChild(ch));
+ return frag;
+ }
+
+ /** @param {HTMLAnchorElement} a */
+ function installContainer(a) {
+ if (!a.parentElement.matches(`.${CONTAINER_CLS}`)) {
+ const container = newEl("div", CONTAINER_CLS);
+ a.parentElement.insertBefore(container, a);
+ container.appendChild(a);
+ }
+ }
+
+ document.addEventListener("click", (ev) => {
+ if (!(ev.target && ev.target instanceof HTMLAnchorElement)) return;
+ if (!ev.target.matches(".footnote")) return;
+ ev.preventDefault();
+
+ const content = document.querySelector(`[id='${ev.target.hash.substring(1)}']`).cloneNode(true);
+ if (content instanceof HTMLElement) {
+ remove(content.querySelector(".reversefootnote"));
+ }
+ installContainer(ev.target);
+ void new Footnote(fragFromContents(content), ev.target);
+ });
+}());