248 lines
13 KiB
Plaintext
Raw Normal View History

2023-09-27 16:20:04 -04:00
<!DOCTYPE html>
<html lang="en">
<%
const { note, subRoot, assetPath, appPath } = locals;
let content = locals.content;
%>
2023-09-27 16:20:04 -04:00
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
2024-06-06 12:20:36 -04:00
<link rel="shortcut icon" href="<% if (note.hasRelation("shareFavicon")) { %>api/notes/<%= note.getRelation("shareFavicon").value %>/download<% } else { %>../favicon.ico<% } %>">
<script src="<%= appPath %>/share.js" type="module"></script>
2025-06-09 14:29:19 +03:00
<% if (!isDev && !note.isLabelTruthy("shareOmitDefaultCss")) { %>
2025-06-09 14:13:35 +03:00
<link href="<%= assetPath %>/stylesheets/share.css" rel="stylesheet">
<% } %>
2024-06-06 12:20:36 -04:00
2023-09-27 16:20:04 -04:00
<% if (note.hasLabel("shareSwagger")) { %>
2024-06-06 12:20:36 -04:00
<!-- TODO: make these note IDs customizable -->
<link href="api/notes/woA8jsLWd4QR/download" rel="stylesheet">
<script src="api/notes/RYOdL9flwQfP/download"></script>
2023-09-27 16:20:04 -04:00
<script>
document.addEventListener("DOMContentLoaded", function() {
const customServerYml = `- url: "{protocol}://{domain}:{port}/etapi"
variables:
protocol:
enum:
- http
- https
default: http
description: Protocol your server is being hosted with
domain:
default: localhost
description: Domain name or localhost or ip
port:
default: 37840
description: Port the app is served over`;
2023-09-27 16:20:04 -04:00
SwaggerUIBundle({
// url: `api/notes/<%= note.noteId %>/download`,
2023-09-27 16:20:04 -04:00
url: `<%= note.getLabelValue("shareSwagger") %>`,
dom_id: "#content",
responseInterceptor: resp => {
if (resp.url !== `<%= note.getLabelValue("shareSwagger") %>`) return resp;
2023-12-05 20:50:29 -05:00
resp.text = resp.text.replace("- url: http://localhost:37740/etapi", "- url: http://localhost:37840/etapi");
resp.text = resp.text.replace(`- url: http://localhost:8080/etapi`, customServerYml);
return resp;
}
2023-09-27 16:20:04 -04:00
});
});
</script>
<% } %>
2024-06-06 12:20:36 -04:00
2023-09-27 16:20:04 -04:00
<% for (const cssRelation of note.getRelations("shareCss")) { %>
<link href="api/notes/<%= cssRelation.value %>/download" rel="stylesheet">
<% } %>
<% for (const jsRelation of note.getRelations("shareJs")) { %>
<script type="module" src="api/notes/<%= jsRelation.value %>/download"></script>
<% } %>
2024-06-06 12:20:36 -04:00
<% if (note.hasLabel("shareDisallowRobotIndexing")) { %>
2023-09-27 16:20:04 -04:00
<meta name="robots" content="noindex,follow" />
<% } %>
2024-06-06 12:20:36 -04:00
<%
const pageTitle = `${note.title}${note.noteId !== subRoot.note.noteId ? ` - ${subRoot.note.title}` : ""}`;
2024-06-06 12:20:36 -04:00
// Setup some key OpenGraph variables
const openGraphColor = subRoot.note.getLabelValue("shareOpenGraphColor");
const openGraphURL = subRoot.note.getLabelValue("shareOpenGraphURL");
const openGraphDomain = subRoot.note.getLabelValue("shareOpenGraphDomain");
let openGraphImage = subRoot.note.getLabelValue("shareOpenGraphImage");
// Relation takes priority and requires some altering
if (subRoot.note.hasRelation("shareOpenGraphImage")) {
openGraphImage = `api/images/${subRoot.note.getRelation("shareOpenGraphImage").value}/image.png`;
2024-06-06 12:20:36 -04:00
}
%>
<title><%= pageTitle %></title>
2023-09-27 22:54:13 -04:00
<!-- HTML Meta Tags -->
2024-06-06 12:20:36 -04:00
<meta name="description" content="<%= note.getLabelValue("shareDescription") %>">
2024-06-06 11:14:43 -04:00
<!-- Facebook Meta Tags -->
2024-06-06 12:20:36 -04:00
<meta property="og:url" content="<%= openGraphURL %>">
2023-09-27 22:54:13 -04:00
<meta property="og:type" content="website">
2024-06-06 12:20:36 -04:00
<meta property="og:title" content="<%= pageTitle %>">
2023-09-27 22:54:13 -04:00
<meta property="og:description" content="<%= note.getLabelValue("shareDescription") %>">
2024-06-06 12:20:36 -04:00
<meta property="og:image" content="<%= openGraphImage %>">
2023-09-27 22:54:13 -04:00
<!-- Twitter Meta Tags -->
<meta name="twitter:card" content="summary_large_image">
2024-06-06 12:20:36 -04:00
<meta property="twitter:domain" content="<%= openGraphDomain %>">
<meta property="twitter:url" content="<%= openGraphURL %>">
<meta name="twitter:title" content="<%= pageTitle %>">
2023-09-27 22:54:13 -04:00
<meta name="twitter:description" content="<%= note.getLabelValue("shareDescription") %>">
2024-06-06 12:20:36 -04:00
<meta name="twitter:image" content="<%= openGraphImage %>">
2023-09-27 22:54:13 -04:00
<!-- Meta Tags Generated via https://opengraph.dev -->
2024-06-06 19:10:56 -04:00
<meta name="theme-color" content="<%= openGraphColor %>">
2023-09-27 16:20:04 -04:00
</head>
<%
const logoWidth = subRoot.note.getLabelValue("shareLogoWidth");
const logoHeight = subRoot.note.getLabelValue("shareLogoHeight");
const mobileLogoHeight = logoHeight && logoWidth ? 32 / (logoWidth / logoHeight) : "";
2024-06-06 12:20:36 -04:00
const shareRootLink = subRoot.note.hasLabel("shareRootLink") ? subRoot.note.getLabelValue("shareRootLink") : `./${subRoot.note.noteId}`;
2023-09-27 16:20:04 -04:00
const currentTheme = note.getLabel("shareTheme") === "light" ? "light" : "dark";
const themeClass = currentTheme === "light" ? " theme-light" : " theme-dark";
2023-09-29 01:43:39 -04:00
const headingRe = /(<h[1-6]>)(.+?)(<\/h[1-6]>)/g;
const headingMatches = [...content.matchAll(headingRe)];
const slugify = (text) => text.toLowerCase().replace(/[^\w]/g, "-");
content = content.replaceAll(headingRe, (...match) => {
match[0] = match[0].replace(match[3], `<a id="${slugify(match[2])}" class="toc-anchor" name="${slugify(match[2])}" href="#${slugify(match[2])}">#</a>${match[3]}`);
return match[0];
});
2023-09-27 16:20:04 -04:00
%>
2024-06-06 11:14:43 -04:00
<body data-note-id="<%= note.noteId %>" class="type-<%= note.type %><%= themeClass %>" data-ancestor-note-id="<%= subRoot.note.noteId %>">
<script>
const preference = localStorage.getItem("theme");
if (preference) {
if (preference === "dark") {
document.body.classList.add("theme-dark");
document.body.classList.remove("theme-light");
}
else {
document.body.classList.remove("theme-dark");
document.body.classList.add("theme-light");
}
}
</script>
2023-09-27 16:20:04 -04:00
<div id="mobile-header">
2024-06-06 19:10:56 -04:00
<a href="<%= shareRootLink %>">
2023-12-05 20:50:29 -05:00
<% if (subRoot.note.hasRelation("shareLogo")) { %>
2024-06-10 02:17:50 -04:00
<img src="api/images/<%= subRoot.note.getRelation("shareLogo").value %>/image.png" width="32" height="<%= mobileLogoHeight %>" alt="Logo" />
2023-09-27 16:20:04 -04:00
<% } %>
2023-12-05 20:50:29 -05:00
<%= subRoot.note.title %>
2023-09-27 16:20:04 -04:00
</a>
2024-06-10 02:17:50 -04:00
<button aria-label="Show Mobile Menu" id="show-menu-button"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M4 6h16v2H4zm0 5h16v2H4zm0 5h16v2H4z"></path></svg></button>
2023-09-27 16:20:04 -04:00
</div>
<div id="split-pane">
<div id="left-pane">
<div id="navigation">
<div id="site-header">
2024-06-06 19:10:56 -04:00
<a href="<%= shareRootLink %>">
2023-12-05 20:50:29 -05:00
<% if (subRoot.note.hasRelation("shareLogo")) { %>
<img src="api/images/<%= subRoot.note.getRelation("shareLogo").value %>/image.png" width="<%= logoWidth %>" height="<%= logoHeight %>" alt="Logo" />
2023-09-27 16:20:04 -04:00
<% } %>
2023-12-05 20:50:29 -05:00
<%= subRoot.note.title %>
2023-09-27 16:20:04 -04:00
</a>
<div class="theme-selection">
2024-06-10 02:17:50 -04:00
<span id="sitetheme">Site Theme</span>
2023-09-27 16:20:04 -04:00
<label class="switch">
2024-06-10 02:17:50 -04:00
<input type="checkbox" checked="<%= currentTheme === "dark" %>" aria-labelledby="sitetheme">
2023-09-27 16:20:04 -04:00
<span class="slider"></span>
<svg class="dark-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20.742 13.045a8.088 8.088 0 0 1-2.077.271c-2.135 0-4.14-.83-5.646-2.336a8.025 8.025 0 0 1-2.064-7.723A1 1 0 0 0 9.73 2.034a10.014 10.014 0 0 0-4.489 2.582c-3.898 3.898-3.898 10.243 0 14.143a9.937 9.937 0 0 0 7.072 2.93 9.93 9.93 0 0 0 7.07-2.929 10.007 10.007 0 0 0 2.583-4.491 1.001 1.001 0 0 0-1.224-1.224zm-2.772 4.301a7.947 7.947 0 0 1-5.656 2.343 7.953 7.953 0 0 1-5.658-2.344c-3.118-3.119-3.118-8.195 0-11.314a7.923 7.923 0 0 1 2.06-1.483 10.027 10.027 0 0 0 2.89 7.848 9.972 9.972 0 0 0 7.848 2.891 8.036 8.036 0 0 1-1.484 2.059z"></path></svg>
<svg class="light-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M6.993 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007S14.761 6.993 12 6.993 6.993 9.239 6.993 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19h2v3h-2zm0-17h2v3h-2zm-9 9h3v2h-3zm17 0h3v2h-3zM4.219 18.363l2.12-2.122 1.415 1.414-2.12 2.122zM16.24 6.344l2.122-2.122 1.414 1.414-2.122 2.122zM6.342 7.759 4.22 5.637l1.415-1.414 2.12 2.122zm13.434 10.605-1.414 1.414-2.122-2.122 1.414-1.414z"></path></svg>
</label>
</div>
<div class="search-item">
<svg class="search-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M10 18a7.952 7.952 0 0 0 4.897-1.688l4.396 4.396 1.414-1.414-4.396-4.396A7.952 7.952 0 0 0 18 10c0-4.411-3.589-8-8-8s-8 3.589-8 8 3.589 8 8 8zm0-14c3.309 0 6 2.691 6 6s-2.691 6-6 6-6-2.691-6-6 2.691-6 6-6z"></path></svg>
<input type="text" class="search-input" placeholder="Search...">
</div>
</div>
2023-12-05 20:50:29 -05:00
<% if (subRoot.note.hasVisibleChildren()) { %>
2023-09-27 16:20:04 -04:00
<nav id="menu">
<%
const ancestors = [];
let notePointer = note;
while (notePointer.parents[0].noteId !== "_share") {
const pointerParent = notePointer.parents[0];
ancestors.push(pointerParent.noteId);
notePointer = pointerParent;
}
%>
<%- include("tree_item", {note: subRoot.note, activeNote: note, subRoot: subRoot, ancestors: ancestors}) %>
2023-09-27 16:20:04 -04:00
</nav>
<% } %>
</div>
</div>
<div id="right-pane">
<div id="main">
2023-09-27 16:20:04 -04:00
2024-06-06 12:20:36 -04:00
<div id="content" class="type-<%= note.type %><% if (note.type === "text") { %> ck-content<% } %><% if (isEmpty) { %> no-content<% } %>">
<h1 id="title"><%= note.title %></h1>
<% if (isEmpty && (!note.hasVisibleChildren() && note.type !== "book")) { %>
<p>This note has no content.</p>
<% } else { %>
2024-06-10 02:17:50 -04:00
<%
content = content.replace(/<img /g, `<img alt="Article Image" loading="lazy" `);
%>
<%- content %>
2023-09-27 16:20:04 -04:00
<% } %>
</div>
2023-09-27 16:20:04 -04:00
<% if (note.hasVisibleChildren() || note.type === "book") { %>
<nav id="childLinks" class="<% if (isEmpty) { %>grid<% } else { %>list<% } %>">
<% if (!isEmpty) { %>
<span>Subpages:</span>
2023-09-27 16:20:04 -04:00
<% } %>
<ul>
<%
const action = note.type === "book" ? "getChildNotes" : "getVisibleChildNotes";
for (const childNote of note[action]()) {
2024-06-06 12:20:36 -04:00
const isExternalLink = childNote.hasLabel("shareExternal") || childNote.hasLabel("shareExternalLink");
const linkHref = isExternalLink ? childNote.getLabelValue("shareExternal") ?? childNote.getLabelValue("shareExternalLink") : `./${childNote.shareId}`;
2023-09-29 01:43:39 -04:00
const target = isExternalLink ? ` target="_blank" rel="noopener noreferrer"` : "";
%>
<li>
2023-09-29 01:43:39 -04:00
<a class="type-<%= childNote.type %>" href="<%= linkHref %>"<%= target %>><%= childNote.title %></a>
</li>
<% } %>
</ul>
</nav>
2023-09-29 01:43:39 -04:00
<% } %>
</div>
2023-09-29 01:43:39 -04:00
<%
if (headingMatches.length > 1) {
const level = (m) => parseInt(m[1].replace(/[<h>]+/g, ""));
const toc = [
{
level: level(headingMatches[0]),
name: headingMatches[0][2],
children: []
}
];
const last = (arr = toc) => arr[arr.length - 1];
const makeEntry = (m) => ({level: level(m), name: m[2], children: []});
const getLevelArr = (lvl, arr = toc) => {
if (arr[0].level === lvl) return arr;
const top = last(arr);
return top.children.length ? getLevelArr(lvl, top.children) : top.children;
};
for (let m = 1; m < headingMatches.length; m++) {
const target = getLevelArr(level(headingMatches[m]));
target.push(makeEntry(headingMatches[m]));
}
%>
<div id="toc-pane">
<h3>On This Page</h3>
<ul id="toc">
<% for (const entry of toc) { %>
2024-06-06 12:20:36 -04:00
<%- include("toc_item", {entry}) %>
2023-09-29 01:43:39 -04:00
<% } %>
</ul>
</div>
<% } %>
2023-09-27 16:20:04 -04:00
</div>
</div>
</body>
</html>