Remove toc.js and toc.css files; integrate CSS directly into plugin.php for improved performance and maintainability

This commit is contained in:
spleenftw committed 2026-06-04 11:52:41 +02:00
1 parent a33b949942
commit 0defa8a577
3 files changed
+98 -441

No files matched your search

+98 -43
View File
@@ -1,29 +1,14 @@
<?php
/**
* Bludit ToC Plugin
*
* Injects a sticky Table of Contents sidebar (desktop) + floating drawer
* (mobile) built from the article's h2/h3/h4 headings. Works standalone
* on any Bludit theme that wraps article body in a `.content` element.
*
* Drop this directory into bl-plugins/ and activate via the admin panel.
*/
class PluginBluditToc extends Plugin {
public function init() {
$this->dbFields = array(
// Label shown at the top of the sidebar and in the mobile drawer.
'title' => 'On this page',
// Pixel offset to clear the fixed navbar when jumping to a heading.
'navbarHeight' => 80,
// Viewport width (px) at which the fixed sidebar appears.
// Below this width the mobile floating-button drawer is used instead.
'minWidth' => 1280,
);
}
// ---- Admin settings form ----
public function form() {
global $L;
@@ -34,40 +19,59 @@ class PluginBluditToc extends Plugin {
$h = '';
$h .= '<div class="form-group">';
$h .= '<label for="bltoc-field-title">' . $L->get('Sidebar title') . '</label>';
$h .= '<input class="form-control" type="text" id="bltoc-field-title" name="title" value="' . $title . '">';
$h .= '<label>' . $L->get('Sidebar title') . '</label>';
$h .= '<input class="form-control" type="text" name="title" value="' . $title . '">';
$h .= '</div>';
$h .= '<div class="form-group">';
$h .= '<label for="bltoc-field-navbar">' . $L->get('Navbar height (px)') . '</label>';
$h .= '<input class="form-control" type="number" id="bltoc-field-navbar" name="navbarHeight" min="0" max="300" value="' . $navbarHeight . '">';
$h .= '<small class="form-text text-muted">' . $L->get('Used to offset hash-jump anchors so they clear the fixed navbar.') . '</small>';
$h .= '<label>' . $L->get('Navbar height (px)') . '</label>';
$h .= '<input class="form-control" type="number" name="navbarHeight" min="0" max="300" value="' . $navbarHeight . '">';
$h .= '<small class="form-text text-muted">' . $L->get('Pixel offset applied to heading anchors to clear a fixed navbar.') . '</small>';
$h .= '</div>';
$h .= '<div class="form-group">';
$h .= '<label for="bltoc-field-minwidth">' . $L->get('Fixed sidebar min-width (px)') . '</label>';
$h .= '<input class="form-control" type="number" id="bltoc-field-minwidth" name="minWidth" min="768" max="2560" value="' . $minWidth . '">';
$h .= '<small class="form-text text-muted">' . $L->get('Viewport widths below this value use the mobile floating button instead.') . '</small>';
$h .= '<label>' . $L->get('Sidebar min viewport width (px)') . '</label>';
$h .= '<input class="form-control" type="number" name="minWidth" min="768" max="2560" value="' . $minWidth . '">';
$h .= '<small class="form-text text-muted">' . $L->get('Below this width the mobile floating button is shown instead.') . '</small>';
$h .= '</div>';
return $h;
}
// ---- Front-end hooks ----
/**
* Inject the plugin CSS in <head>.
*/
public function siteHead() {
$url = DOMAIN_PLUGINS . $this->directoryName . '/toc.css';
return '<link rel="stylesheet" type="text/css" href="' . $url . '">' . PHP_EOL;
return '<style>
#bltoc-sidebar{--bltoc-text:#777;--bltoc-hover:#111;--bltoc-active:#0066cc;--bltoc-label:#999;--bltoc-fab-bg:#0066cc;--bltoc-fab-color:#fff}
@media(prefers-color-scheme:dark){#bltoc-sidebar{--bltoc-text:#999;--bltoc-hover:#eee;--bltoc-active:#4d9fff;--bltoc-label:#666}}
@supports(color:var(--accent)){#bltoc-sidebar{--bltoc-active:var(--accent)}}
@supports(color:var(--text-muted)){#bltoc-sidebar{--bltoc-text:var(--text-muted)}}
.bltoc-sidebar{display:none;position:fixed;left:1rem;top:5rem;width:210px;max-height:calc(100vh - 6rem);overflow-y:auto;overflow-x:hidden;z-index:200;font-size:.8125rem;line-height:1.5;scrollbar-width:thin}
.bltoc-header{margin-bottom:.65rem}
.bltoc-title{font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--bltoc-label,#999)}
.bltoc-list{list-style:none;padding:0;margin:0}
.bltoc-item{padding:.12rem 0}
.bltoc-h3{padding-left:.75rem}
.bltoc-h4{padding-left:1.5rem}
.bltoc-link{display:block;color:var(--bltoc-text,#777);text-decoration:none;line-height:1.5;transition:color .15s ease}
.bltoc-link:hover{color:var(--bltoc-hover,#111);text-decoration:none}
.bltoc-link.active{color:var(--bltoc-active,#0066cc);font-weight:600}
.bltoc-fab{display:none;position:fixed;left:1.5rem;bottom:1.5rem;z-index:1500;width:46px;height:46px;border-radius:50%;border:none;background:var(--bltoc-fab-bg,#0066cc);color:var(--bltoc-fab-color,#fff);align-items:center;justify-content:center;cursor:pointer;box-shadow:0 3px 10px rgba(0,0,0,.25);transition:transform .15s ease,box-shadow .15s ease}
.bltoc-fab:hover{transform:scale(1.08);box-shadow:0 4px 14px rgba(0,0,0,.3)}
.bltoc-fab svg{display:block}
.bltoc-drawer{position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.45);opacity:0;visibility:hidden;transition:opacity .22s ease,visibility 0s linear .22s}
.bltoc-drawer.is-open{opacity:1;visibility:visible;transition:opacity .22s ease}
.bltoc-drawer-panel{position:absolute;bottom:0;left:0;right:0;background:#fff;border-radius:1rem 1rem 0 0;padding:1.5rem 1.5rem calc(1.5rem + env(safe-area-inset-bottom,0px));max-height:72vh;overflow-y:auto;transform:translateY(100%);transition:transform .25s ease}
@media(prefers-color-scheme:dark){.bltoc-drawer-panel{background:#1e1e1e}}
@supports(color:var(--bg-primary)){.bltoc-drawer-panel{background:var(--bg-primary)}}
.bltoc-drawer.is-open .bltoc-drawer-panel{transform:translateY(0)}
.bltoc-drawer-title{font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:#999;margin-bottom:.75rem}
.bltoc-drawer .bltoc-list{list-style:none;padding:0;margin:0}
.bltoc-drawer .bltoc-link{display:block;padding:.5rem 0;color:#555;text-decoration:none}
.bltoc-drawer .bltoc-link:hover{color:#0066cc;text-decoration:none}
@media(prefers-color-scheme:dark){.bltoc-drawer .bltoc-link{color:#ccc}.bltoc-drawer .bltoc-link:hover{color:#4d9fff}}
@supports(color:var(--accent)){.bltoc-drawer .bltoc-link:hover{color:var(--accent)}}
</style>' . PHP_EOL;
}
/**
* Inject the ToC sidebar container right after <body>.
* It is position:fixed, so placement in the DOM doesn't affect layout.
* JS will populate <nav id="bltoc-nav"> and mark the container visible.
*/
public function siteBodyBegin() {
$title = htmlspecialchars($this->getValue('title'), ENT_QUOTES, 'UTF-8');
return '<aside class="bltoc-sidebar" id="bltoc-sidebar" aria-label="' . $title . '">'
@@ -76,18 +80,69 @@ class PluginBluditToc extends Plugin {
. '</aside>' . PHP_EOL;
}
/**
* Inject config + script just before </body>.
* Config is passed via window.BLTOC so the JS file stays cache-friendly.
*/
public function siteBodyEnd() {
$title = json_encode($this->getValue('title'));
$navbarHeight = (int) $this->getValue('navbarHeight');
$minWidth = (int) $this->getValue('minWidth');
$url = DOMAIN_PLUGINS . $this->directoryName . '/toc.js';
$out = '<script>window.BLTOC={title:' . $title . ',navbarHeight:' . $navbarHeight . ',minWidth:' . $minWidth . '};</script>' . PHP_EOL;
$out .= '<script defer src="' . $url . '"></script>' . PHP_EOL;
return $out;
return '<script>(function(){' .
'var TITLE=' . $title . ',' .
'NAVBAR_HEIGHT=' . $navbarHeight . ',' .
'MIN_WIDTH=' . $minWidth . ';' .
'var s=document.createElement("style");' .
's.textContent="@media(min-width:"+MIN_WIDTH+"px){.bltoc-sidebar.bltoc-ready{display:block}}"' .
'+"@media(max-width:"+(MIN_WIDTH-1)+"px){.bltoc-fab.bltoc-ready{display:flex}}";' .
'document.head.appendChild(s);' .
'var content=document.querySelector(".content")||document.querySelector("article .entry-content")||document.querySelector(".entry-content")||document.querySelector(".post-content");' .
'if(!content)return;' .
'var headings=content.querySelectorAll("h2,h3,h4");' .
'if(!headings.length)return;' .
'var usedIds={};' .
'Array.prototype.forEach.call(headings,function(h){' .
'if(h.id){usedIds[h.id]=true;}' .
'else{' .
'var base=h.textContent.trim().toLowerCase().replace(/[^a-z0-9\s-]/g,"").replace(/\s+/g,"-").replace(/^-+|-+$/g,"")||"heading";' .
'var id=base,n=2;' .
'while(usedIds[id]){id=base+"-"+(n++);}' .
'usedIds[id]=true;h.id=id;' .
'}' .
'h.style.scrollMarginTop=NAVBAR_HEIGHT+"px";' .
'});' .
'var ul=document.createElement("ul");ul.className="bltoc-list";' .
'Array.prototype.forEach.call(headings,function(h){' .
'var li=document.createElement("li");li.className="bltoc-item bltoc-"+h.tagName.toLowerCase();' .
'var a=document.createElement("a");a.href="#"+h.id;a.textContent=h.textContent;a.className="bltoc-link";' .
'li.appendChild(a);ul.appendChild(li);' .
'});' .
'var nav=document.getElementById("bltoc-nav");' .
'if(nav)nav.appendChild(ul);' .
'var sb=document.getElementById("bltoc-sidebar");' .
'if(sb)sb.classList.add("bltoc-ready");' .
'var arr=Array.prototype.slice.call(headings);' .
'function updateActive(){' .
'var sy=window.scrollY||window.pageYOffset,thresh=sy+NAVBAR_HEIGHT+16,active=null;' .
'arr.forEach(function(h){if(h.getBoundingClientRect().top+sy<=thresh)active=h;});' .
'if(!nav)return;' .
'Array.prototype.forEach.call(nav.querySelectorAll(".bltoc-link"),function(a){a.classList.remove("active");});' .
'if(active){var lnk=nav.querySelector("a[href=\'#"+active.id+"\']");if(lnk)lnk.classList.add("active");}' .
'}' .
'window.addEventListener("scroll",updateActive,{passive:true});updateActive();' .
'var fab=document.createElement("button");fab.type="button";fab.className="bltoc-fab bltoc-ready";' .
'fab.setAttribute("aria-label",TITLE);fab.setAttribute("aria-expanded","false");' .
'fab.innerHTML=\'<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"/></svg>\';' .
'var drawer=document.createElement("div");drawer.className="bltoc-drawer";' .
'drawer.setAttribute("role","dialog");drawer.setAttribute("aria-modal","true");drawer.setAttribute("aria-label",TITLE);' .
'var panel=document.createElement("div");panel.className="bltoc-drawer-panel";' .
'var dt=document.createElement("div");dt.className="bltoc-drawer-title";dt.textContent=TITLE;' .
'var clone=ul.cloneNode(true);' .
'panel.appendChild(dt);panel.appendChild(clone);drawer.appendChild(panel);' .
'document.body.appendChild(fab);document.body.appendChild(drawer);' .
'function openDrawer(){drawer.classList.add("is-open");document.body.style.overflow="hidden";fab.setAttribute("aria-expanded","true");}' .
'function closeDrawer(){drawer.classList.remove("is-open");document.body.style.overflow="";fab.setAttribute("aria-expanded","false");}' .
'fab.addEventListener("click",openDrawer);' .
'drawer.addEventListener("click",function(e){if(e.target===drawer)closeDrawer();});' .
'Array.prototype.forEach.call(clone.querySelectorAll("a"),function(a){a.addEventListener("click",closeDrawer);});' .
'document.addEventListener("keydown",function(e){if((e.key==="Escape"||e.key==="Esc")&&drawer.classList.contains("is-open")){closeDrawer();fab.focus();}});' .
'})();</script>' . PHP_EOL;
}
}
-223
View File
@@ -1,223 +0,0 @@
/* =============================================================
Bludit ToC Plugin — toc.css
Self-contained: no dependency on the host theme's CSS variables.
Dark mode is handled via prefers-color-scheme.
If the theme exposes --accent / --text-muted / --bg-primary,
those are layered on top via @supports for a seamless integration.
============================================================= */
/* ---- Color tokens (light) ---- */
#bltoc-sidebar {
--bltoc-text: #777;
--bltoc-hover: #111;
--bltoc-active: #0066cc;
--bltoc-label: #999;
--bltoc-bg: #fff;
--bltoc-border: rgba(0,0,0,0.08);
--bltoc-fab-bg: #0066cc;
--bltoc-fab-color: #fff;
}
/* ---- Color tokens (dark) ---- */
@media (prefers-color-scheme: dark) {
#bltoc-sidebar {
--bltoc-text: #999;
--bltoc-hover: #eee;
--bltoc-active: #4d9fff;
--bltoc-label: #666;
--bltoc-bg: #1a1a1a;
--bltoc-border: rgba(255,255,255,0.08);
}
}
/* Let blowdit (or any theme) override via its own design tokens */
@supports (color: var(--accent)) {
#bltoc-sidebar { --bltoc-active: var(--accent); }
}
@supports (color: var(--text-muted)) {
#bltoc-sidebar { --bltoc-text: var(--text-muted); }
}
@supports (color: var(--bg-primary)) {
#bltoc-sidebar { --bltoc-bg: var(--bg-primary); }
}
/* ============================================================
Desktop fixed sidebar
Visible only at min-width set by the admin (default 1280px).
JS adds .bltoc-ready when headings are found, making it appear.
============================================================ */
.bltoc-sidebar {
display: none; /* hidden until JS confirms headings exist */
position: fixed;
left: 1rem;
top: 5rem; /* approx below a typical navbar; fine-tuned with navbarHeight in JS */
width: 210px;
max-height: calc(100vh - 6rem);
overflow-y: auto;
overflow-x: hidden;
z-index: 200;
font-size: 0.8125rem;
line-height: 1.5;
scrollbar-width: thin;
scrollbar-color: var(--bltoc-border) transparent;
}
/* JS adds data-bltoc-minwidth on <html>; sidebar shows only above that threshold. */
/* Actual media-query breakpoint is injected by JS via a <style> tag since the
min-width value is dynamic (set in admin). */
/* ---- Sidebar header ("On this page") ---- */
.bltoc-header {
margin-bottom: 0.65rem;
}
.bltoc-title {
font-size: 0.68rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--bltoc-label);
}
/* ---- ToC list ---- */
.bltoc-list {
list-style: none;
padding: 0;
margin: 0;
}
.bltoc-item {
padding: 0.12rem 0;
}
.bltoc-h3 { padding-left: 0.75rem; }
.bltoc-h4 { padding-left: 1.5rem; }
.bltoc-link {
display: block;
color: var(--bltoc-text);
text-decoration: none;
line-height: 1.5;
transition: color 0.15s ease;
}
.bltoc-link:hover {
color: var(--bltoc-hover);
text-decoration: none;
}
.bltoc-link.active {
color: var(--bltoc-active);
font-weight: 600;
}
/* ============================================================
Mobile floating action button + bottom drawer
FAB appears below the sidebar breakpoint.
Both are injected by JS, but their CSS lives here.
============================================================ */
.bltoc-fab {
display: none; /* shown by JS via flex when headings exist */
position: fixed;
left: 1.5rem;
bottom: 1.5rem;
z-index: 1500;
width: 46px;
height: 46px;
border-radius: 50%;
border: none;
background: var(--bltoc-fab-bg, #0066cc);
color: var(--bltoc-fab-color, #fff);
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 3px 10px rgba(0,0,0,0.25);
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.bltoc-fab:hover {
transform: scale(1.08);
box-shadow: 0 4px 14px rgba(0,0,0,0.3);
}
.bltoc-fab svg { display: block; }
/* ---- Bottom drawer backdrop ---- */
.bltoc-drawer {
position: fixed;
inset: 0;
z-index: 2000;
background: rgba(0, 0, 0, 0.45);
opacity: 0;
visibility: hidden;
transition: opacity 0.22s ease, visibility 0s linear 0.22s;
}
.bltoc-drawer.is-open {
opacity: 1;
visibility: visible;
transition: opacity 0.22s ease;
}
/* ---- Drawer panel (slides up from bottom) ---- */
.bltoc-drawer-panel {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: #fff;
border-radius: 1rem 1rem 0 0;
padding: 1.5rem 1.5rem calc(1.5rem + env(safe-area-inset-bottom, 0px));
max-height: 72vh;
overflow-y: auto;
transform: translateY(100%);
transition: transform 0.25s ease;
}
@media (prefers-color-scheme: dark) {
.bltoc-drawer-panel { background: #1e1e1e; }
}
@supports (color: var(--bg-primary)) {
.bltoc-drawer-panel { background: var(--bg-primary); }
}
.bltoc-drawer.is-open .bltoc-drawer-panel {
transform: translateY(0);
}
.bltoc-drawer-title {
font-size: 0.68rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #999;
margin-bottom: 0.75rem;
}
.bltoc-drawer .bltoc-list {
list-style: none;
padding: 0;
margin: 0;
}
.bltoc-drawer .bltoc-link {
display: block;
padding: 0.5rem 0;
color: #555;
text-decoration: none;
}
.bltoc-drawer .bltoc-link:hover {
color: #0066cc;
text-decoration: none;
}
@media (prefers-color-scheme: dark) {
.bltoc-drawer .bltoc-link { color: #ccc; }
.bltoc-drawer .bltoc-link:hover { color: #4d9fff; }
}
@supports (color: var(--accent)) {
.bltoc-drawer .bltoc-link:hover { color: var(--accent); }
}
-175
View File
@@ -1,175 +0,0 @@
/* Bludit ToC Plugin — toc.js
Reads config from window.BLTOC (injected inline by plugin.php).
No dependencies; works with any Bludit theme that wraps the article
body in a .content element (Bludit's default page template does this). */
(function () {
'use strict';
var cfg = window.BLTOC || {};
var TITLE = cfg.title || 'On this page';
var NAVBAR_HEIGHT = cfg.navbarHeight != null ? cfg.navbarHeight : 80;
var MIN_WIDTH = cfg.minWidth != null ? cfg.minWidth : 1280;
/* ---- Inject the dynamic min-width media query for the sidebar ---- */
var styleEl = document.createElement('style');
styleEl.textContent =
'@media (min-width: ' + MIN_WIDTH + 'px) { .bltoc-sidebar.bltoc-ready { display: block; } }' +
'@media (max-width: ' + (MIN_WIDTH - 1) + 'px) { .bltoc-fab.bltoc-ready { display: flex; } }';
document.head.appendChild(styleEl);
/* ---- Find the article content area ---- */
/* Only match unambiguous single-article containers to avoid firing on
homepage post-list cards that also contain <article> and <h2> tags. */
var content = document.querySelector('.content')
|| document.querySelector('article .entry-content')
|| document.querySelector('.entry-content')
|| document.querySelector('.post-content');
if (!content) return;
/* ---- Collect headings ---- */
var headings = content.querySelectorAll('h2, h3, h4');
if (!headings.length) return;
/* ---- Assign stable IDs to any heading that lacks one ---- */
var usedIds = {};
Array.prototype.forEach.call(headings, function (h) {
if (h.id) {
usedIds[h.id] = true;
} else {
var base = h.textContent.trim().toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/^-+|-+$/g, '') || 'heading';
var id = base, n = 2;
while (usedIds[id]) { id = base + '-' + (n++); }
usedIds[id] = true;
h.id = id;
}
/* Offset anchor jumps so the heading clears the fixed navbar */
h.style.scrollMarginTop = NAVBAR_HEIGHT + 'px';
});
/* ---- Build the <ul> list ---- */
var ul = document.createElement('ul');
ul.className = 'bltoc-list';
Array.prototype.forEach.call(headings, function (h) {
var li = document.createElement('li');
li.className = 'bltoc-item bltoc-' + h.tagName.toLowerCase();
var a = document.createElement('a');
a.href = '#' + h.id;
a.textContent = h.textContent;
a.className = 'bltoc-link';
li.appendChild(a);
ul.appendChild(li);
});
/* ---- Populate #bltoc-nav and reveal the sidebar ---- */
var nav = document.getElementById('bltoc-nav');
if (nav) {
nav.appendChild(ul);
}
var sidebar = document.getElementById('bltoc-sidebar');
if (sidebar) {
sidebar.classList.add('bltoc-ready');
}
/* ---- Scroll-spy: highlight the last heading above the viewport fold ---- */
var headingArr = Array.prototype.slice.call(headings);
function updateActive() {
var scrollY = window.scrollY || window.pageYOffset;
var threshold = scrollY + NAVBAR_HEIGHT + 16; /* small extra gap */
var active = null;
headingArr.forEach(function (h) {
if (h.getBoundingClientRect().top + scrollY <= threshold) {
active = h;
}
});
if (!nav) return;
var links = nav.querySelectorAll('.bltoc-link');
Array.prototype.forEach.call(links, function (a) {
a.classList.remove('active');
});
if (active) {
var link = nav.querySelector('a[href="#' + active.id + '"]');
if (link) link.classList.add('active');
}
}
window.addEventListener('scroll', updateActive, { passive: true });
updateActive();
/* ---- Mobile: floating button + bottom drawer ---- */
var fab = document.createElement('button');
fab.type = 'button';
fab.className = 'bltoc-fab bltoc-ready';
fab.setAttribute('aria-label', TITLE);
fab.innerHTML =
'<svg viewBox="0 0 24 24" width="20" height="20" fill="none" ' +
'stroke="currentColor" stroke-width="2" stroke-linecap="round" ' +
'stroke-linejoin="round" aria-hidden="true">' +
'<path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"/>' +
'</svg>';
var drawer = document.createElement('div');
drawer.className = 'bltoc-drawer';
drawer.setAttribute('role', 'dialog');
drawer.setAttribute('aria-modal', 'true');
drawer.setAttribute('aria-label', TITLE);
var panel = document.createElement('div');
panel.className = 'bltoc-drawer-panel';
var drawerTitle = document.createElement('div');
drawerTitle.className = 'bltoc-drawer-title';
drawerTitle.textContent = TITLE;
var clone = ul.cloneNode(true);
panel.appendChild(drawerTitle);
panel.appendChild(clone);
drawer.appendChild(panel);
document.body.appendChild(fab);
document.body.appendChild(drawer);
function openDrawer() {
drawer.classList.add('is-open');
document.body.style.overflow = 'hidden';
fab.setAttribute('aria-expanded', 'true');
}
function closeDrawer() {
drawer.classList.remove('is-open');
document.body.style.overflow = '';
fab.setAttribute('aria-expanded', 'false');
}
fab.setAttribute('aria-expanded', 'false');
fab.addEventListener('click', openDrawer);
/* Close when clicking the backdrop */
drawer.addEventListener('click', function (e) {
if (e.target === drawer) closeDrawer();
});
/* Close when a drawer link is followed */
Array.prototype.forEach.call(clone.querySelectorAll('a'), function (a) {
a.addEventListener('click', closeDrawer);
});
/* Close on Escape */
document.addEventListener('keydown', function (e) {
if ((e.key === 'Escape' || e.key === 'Esc') && drawer.classList.contains('is-open')) {
closeDrawer();
fab.focus();
}
});
})();