4 changed files with 1699 additions and 1286 deletions
File diff suppressed because it is too large
@ -0,0 +1,294 @@ |
|||
(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; |
|||
}); |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
})(); |
File diff suppressed because it is too large
Loading…
Reference in new issue