(function() { 'use strict'; // update nodes {{{ function describeTimeElementDate(element) { // {{{ if (!(element instanceof HTMLTimeElement)) { return "unknown"; } const startYear = element.getAttribute('startyear'); const startMonth = element.getAttribute('startmonth'); const startDay = element.getAttribute('startday'); if (!startYear || !startMonth || !startDay || isNaN(startYear) || isNaN(startMonth) || isNaN(startDay)) { return 'Invalid date attributes on the <time> element'; } const timeElementDate = new Date(startYear, startMonth - 1, startDay); timeElementDate.setHours(0, 0, 0, 0); const today = new Date(); today.setHours(0, 0, 0, 0); const dayDifference = Math.round((timeElementDate - today) / (1000 * 60 * 60 * 24)); const daysOfWeek = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; const dayOfWeek = daysOfWeek[timeElementDate.getDay()]; const monthsOfYear = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']; const month = monthsOfYear[timeElementDate.getMonth()]; var ret = [dayOfWeek, month]; if (dayDifference === 0) { ret.push('today'); } else if (dayDifference === 1) { ret.push('tomorrow'); } else if (dayDifference === -1) { ret.push('yesterday'); } else { if (dayDifference < 0) { ret.push('late'); // if (dayDifference > -8) { // ret.push('last'); // } else { // ret.push('older'); // } } else { ret.push('future'); // if (dayDifference < 8) { // ret.push('next'); // } else { // ret.push('later'); // } } } return ret; } // }}} function safeGetContent(parent, selector) { // {{{ var element = parent.querySelector(selector); if (element !== null) { var content = element.innerText; if (content !== null) { return content; } } return "" } // }}} function removeClassesStartingWith(element, prefix) { // {{{ const classes = element.classList; for (const className of classes) { if (className.startsWith(prefix)) { element.classList.remove(className); } } } // }}} function createImages(node, imgSrcs) { // {{{ var inject = node.querySelector('.inject'); if (inject === null) { var inject = document.createElement("div"); inject.className = 'inject'; node.appendChild(inject); } var imgs = inject.querySelectorAll('img'); if (imgs.length != imgSrcs.length) { inject.innerHTML = ''; imgSrcs.forEach(imgSrc => { var img = document.createElement("img"); img.src = imgSrc; img.style.display = "none"; img.onload = function () { const maxDimension = 100; const aspectRatio = img.naturalWidth / img.naturalHeight; if (aspectRatio > 1) { img.style.width = `${maxDimension}px`; img.style.height = `${maxDimension / aspectRatio}px`; } else { img.style.height = `${maxDimension}px`; img.style.width = `${maxDimension * aspectRatio}px`; } img.style.display = "block"; }; img.onclick = function () { // open link in new tab window.open(imgSrc, '_blank'); }; inject.appendChild(img); }); } } // }}} function updateProject(projectNode) { // {{{ removeClassesStartingWith(projectNode, "backlinked"); removeClassesStartingWith(projectNode, "link"); removeClassesStartingWith(projectNode, "tag"); removeClassesStartingWith(projectNode, "time"); const names = []; const notes = []; const breadcrumbNodes = []; for (let i = 0; i < projectNode.children.length; i++) { const child = projectNode.children[i]; if (child.classList.contains('name')) { names.push(child); } if (child.classList.contains('notes')) { notes.push(child); } if (child.classList.contains('_1zok2')) { breadcrumbNodes.push(child.querySelector('.breadcrumbs')); } } [notes, names].forEach(function(ns) { if (ns.length > 0) { const n = ns[0]; const text = safeGetContent(n, ".content > .innerContentContainer"); if (text.startsWith("!")) { const links = n.querySelectorAll('a.contentLink'); const imgSrcs = []; for (var i = 0; i < links.length; i++) { var link = links[i]; link.textContent = "link"; var imgSrc = link.href; imgSrcs.push(imgSrc); } if (imgSrcs.length > 0) { createImages(n, imgSrcs); } } const tags = n.querySelectorAll('.contentTag'); tags.forEach(tag => { var tagText = safeGetContent(tag, ".contentTagText").trim(); if (tagText !== "") { projectNode.classList.add("tagged"); projectNode.classList.add("tagged-" + tagText); } }); const links = n.querySelectorAll('.contentLink'); links.forEach(link => { link.spellcheck = false; const nonLetterRegex = /[^a-zA-Z0-9]/g; var linkFull = link.innerText.trim().replace(nonLetterRegex, ''); if (linkFull.startsWith("x") || linkFull.startsWith("y") || linkFull.startsWith("z")) { var className = "linked-" + linkFull; var linkClassName = "link-" + linkFull; projectNode.classList.add("linked"); projectNode.classList.add(className); link.classList.add("link"); link.classList.add(linkClassName); } }); const times = n.querySelectorAll('time'); times.forEach(time => { var relatives = describeTimeElementDate(time); projectNode.classList.add("timed"); relatives.forEach(relative => { projectNode.classList.add("timed-" + relative); }); }); if (isIosSafari()) { const content = n.querySelector('.content'); removeKeyboard(content); } } }); } // }}} function updateAll() { const projectNodes = document.querySelectorAll('.project'); projectNodes.forEach(projectNode => { updateProject(projectNode); }); } const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { var nodes = null; if (mutation.addedNodes.length > 0) { nodes = mutation.addedNodes; } else if (mutation.removedNodes.length > 0) { nodes = mutation.addedNodes; } if ( nodes !== null ) { var node = mutation.target; if ( node !== null ) { var projectNode = node.closest('.project'); if ( projectNode !== null && projectNode !== node ) { updateProject(projectNode); } } } } }); observer.observe(document.body, { childList: true, subtree: true }); updateAll(); // }}} // hide keyboard on iOS Safari {{{ function isIosSafari() { const ua = navigator.userAgent; return ( /iPhone|iPad|iPod/.test(ua) && /Safari/.test(ua) ); } function removeKeyboard(contentElement) { if (!('allowEditing' in contentElement)) { contentElement.allowEditing = false; } contentElement.addEventListener('focus', (e) => { if (!contentElement.allowEditing) { e.preventDefault(); contentElement.blur(); } }); contentElement.addEventListener('dblclick', (e) => { contentElement.allowEditing = true; // Small delay to ensure focus is handled correctly setTimeout(() => { contentElement.focus(); }, 0); }); contentElement.addEventListener('blur', () => { contentElement.allowEditing = false; }); } // }}} })();