(function() { 'use strict'; // helpers {{{ 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 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; }); } // }}} // }}} function updateProject(projectNode) { removeClassesStartingWith(projectNode, "backlinked"); removeClassesStartingWith(projectNode, "link"); removeClassesStartingWith(projectNode, "tag"); removeClassesStartingWith(projectNode, "time"); const names = []; const notes = []; 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); } } [notes, names].forEach(function(ns) { if (ns.length > 0) { const n = ns[0]; const text = safeGetContent(n, ".content > .innerContentContainer"); 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); tag.classList.add("tag"); tag.classList.add("tag-" + 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")) { 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"); time.classList.add("time"); relatives.forEach(relative => { projectNode.classList.add("timed-" + relative); time.classList.add("time-" + 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(); })();