#544 Adds newsfoot.js footnote script to project
This commit is contained in:
parent
3decd23c45
commit
a605d9cd1f
|
@ -7,6 +7,8 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||||
|
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||||
510BD15D232D765D002692E4 /* SettingsReaderAPIAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */; };
|
510BD15D232D765D002692E4 /* SettingsReaderAPIAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */; };
|
||||||
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
|
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
|
||||||
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */; };
|
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */; };
|
||||||
|
@ -769,6 +771,7 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
49F40DEF2335B71000552BF4 /* newsfoot.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = newsfoot.js; sourceTree = "<group>"; };
|
||||||
510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAddAccountView.swift; sourceTree = "<group>"; };
|
510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAddAccountView.swift; sourceTree = "<group>"; };
|
||||||
510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLocalAccountView.swift; sourceTree = "<group>"; };
|
510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLocalAccountView.swift; sourceTree = "<group>"; };
|
||||||
510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFeedbinAccountView.swift; sourceTree = "<group>"; };
|
510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFeedbinAccountView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1387,6 +1390,7 @@
|
||||||
51C452A822650DA100C03939 /* Article Rendering */ = {
|
51C452A822650DA100C03939 /* Article Rendering */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
49F40DEF2335B71000552BF4 /* newsfoot.js */,
|
||||||
849A977D1ED9EC42007D329B /* ArticleRenderer.swift */,
|
849A977D1ED9EC42007D329B /* ArticleRenderer.swift */,
|
||||||
848362FE2262A30E00DA1D35 /* template.html */,
|
848362FE2262A30E00DA1D35 /* template.html */,
|
||||||
);
|
);
|
||||||
|
@ -2235,16 +2239,16 @@
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
513C5CE5232571C2003D4054 = {
|
513C5CE5232571C2003D4054 = {
|
||||||
CreatedOnToolsVersion = 11.0;
|
CreatedOnToolsVersion = 11.0;
|
||||||
DevelopmentTeam = SHJK2V3AJG;
|
DevelopmentTeam = DY2XQRVWN9;
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Automatic;
|
||||||
};
|
};
|
||||||
6581C73220CED60000F4AD34 = {
|
6581C73220CED60000F4AD34 = {
|
||||||
DevelopmentTeam = SHJK2V3AJG;
|
DevelopmentTeam = SHJK2V3AJG;
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Manual;
|
||||||
};
|
};
|
||||||
840D617B2029031C009BC708 = {
|
840D617B2029031C009BC708 = {
|
||||||
CreatedOnToolsVersion = 9.3;
|
CreatedOnToolsVersion = 9.3;
|
||||||
DevelopmentTeam = SHJK2V3AJG;
|
DevelopmentTeam = DY2XQRVWN9;
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Automatic;
|
||||||
SystemCapabilities = {
|
SystemCapabilities = {
|
||||||
com.apple.BackgroundModes = {
|
com.apple.BackgroundModes = {
|
||||||
|
@ -2255,7 +2259,7 @@
|
||||||
849C645F1ED37A5D003D8FC0 = {
|
849C645F1ED37A5D003D8FC0 = {
|
||||||
CreatedOnToolsVersion = 8.2.1;
|
CreatedOnToolsVersion = 8.2.1;
|
||||||
DevelopmentTeam = SHJK2V3AJG;
|
DevelopmentTeam = SHJK2V3AJG;
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Manual;
|
||||||
SystemCapabilities = {
|
SystemCapabilities = {
|
||||||
com.apple.HardenedRuntime = {
|
com.apple.HardenedRuntime = {
|
||||||
enabled = 1;
|
enabled = 1;
|
||||||
|
@ -2264,7 +2268,7 @@
|
||||||
};
|
};
|
||||||
849C64701ED37A5D003D8FC0 = {
|
849C64701ED37A5D003D8FC0 = {
|
||||||
CreatedOnToolsVersion = 8.2.1;
|
CreatedOnToolsVersion = 8.2.1;
|
||||||
DevelopmentTeam = SHJK2V3AJG;
|
DevelopmentTeam = 9C84TZ7Q6Z;
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Automatic;
|
||||||
TestTargetID = 849C645F1ED37A5D003D8FC0;
|
TestTargetID = 849C645F1ED37A5D003D8FC0;
|
||||||
};
|
};
|
||||||
|
@ -2513,6 +2517,7 @@
|
||||||
51F85BF12272524100C787DC /* Credits.rtf in Resources */,
|
51F85BF12272524100C787DC /* Credits.rtf in Resources */,
|
||||||
84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */,
|
84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */,
|
||||||
511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */,
|
511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */,
|
||||||
|
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */,
|
||||||
51F85BEF2272520B00C787DC /* Thanks.rtf in Resources */,
|
51F85BEF2272520B00C787DC /* Thanks.rtf in Resources */,
|
||||||
84C9FC9D2262A1A900D921D6 /* Assets.xcassets in Resources */,
|
84C9FC9D2262A1A900D921D6 /* Assets.xcassets in Resources */,
|
||||||
51C452B82265178500C03939 /* styleSheet.css in Resources */,
|
51C452B82265178500C03939 /* styleSheet.css in Resources */,
|
||||||
|
@ -2548,6 +2553,7 @@
|
||||||
B528F81E23333C7E00E735DD /* page.html in Resources */,
|
B528F81E23333C7E00E735DD /* page.html in Resources */,
|
||||||
8483630E2262A3FE00DA1D35 /* MainWindow.storyboard in Resources */,
|
8483630E2262A3FE00DA1D35 /* MainWindow.storyboard in Resources */,
|
||||||
55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */,
|
55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */,
|
||||||
|
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */,
|
||||||
84BAE64921CEDAF20046DB56 /* CrashReporterWindow.xib in Resources */,
|
84BAE64921CEDAF20046DB56 /* CrashReporterWindow.xib in Resources */,
|
||||||
84C9FC8E22629E8F00D921D6 /* Credits.rtf in Resources */,
|
84C9FC8E22629E8F00D921D6 /* Credits.rtf in Resources */,
|
||||||
84BBB12D20142A4700F054F5 /* Inspector.storyboard in Resources */,
|
84BBB12D20142A4700F054F5 /* Inspector.storyboard in Resources */,
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
/** @param {Node | null} el */
|
||||||
|
const remove = (el) => { if (el) el.parentElement.removeChild(el) };
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {<T extends any[]>(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`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
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)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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);
|
||||||
|
});
|
Loading…
Reference in New Issue