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