424 lines
No EOL
15 KiB
Text
424 lines
No EOL
15 KiB
Text
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Nycki's Papermod</title>
|
|
<meta property="og:title" content="Nycki's Papermod">
|
|
<meta property="og:description" content="the analog organizer from 2025">
|
|
<meta property="og:image" content="{{ metadata.url }}/papermod/papermod.png">
|
|
<meta property="og:image:width" content="618">
|
|
<meta property="og:image:height" content="423">
|
|
<style>
|
|
:root {
|
|
--card-width: 74mm;
|
|
--card-height: 105mm;
|
|
--m: 2mm;
|
|
--m2: calc(2 * var(--m));
|
|
--m4: calc(4 * var(--m));
|
|
background-color: #929591; /*xkcd-grey*/
|
|
font-family: Arial, Helvetica, sans-serif;
|
|
}
|
|
@page { size: 74mm 105mm; }
|
|
body {
|
|
margin: 0;
|
|
/* break after fourth page on large monitors */
|
|
max-width: calc(4 * (var(--card-width) + var(--m4)));
|
|
}
|
|
|
|
#settings {
|
|
max-width: fit-content;
|
|
margin: var(--m2);
|
|
padding: var(--m2);
|
|
background-color: white;
|
|
}
|
|
#settings * {
|
|
margin: var(--m) 0;
|
|
}
|
|
#settings :first-child {
|
|
margin: 0;
|
|
}
|
|
#settings .form {
|
|
margin-top: var(--m2);
|
|
max-width: fit-content;
|
|
}
|
|
#settings .form div {
|
|
display: flex;
|
|
flex-direction: row;
|
|
}
|
|
#settings .form div * {
|
|
flex: 1;
|
|
max-width: 50%;
|
|
}
|
|
|
|
#document {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
}
|
|
.page {
|
|
background-color: white;
|
|
box-sizing: border-box;
|
|
width: var(--card-width);
|
|
height: var(--card-height);
|
|
margin: var(--m2);
|
|
padding: var(--m2);
|
|
position: relative;
|
|
text-align: center;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.page :first-child {
|
|
margin-top: 0;
|
|
}
|
|
@media print {
|
|
html {
|
|
background-color: white;
|
|
}
|
|
#settings {
|
|
display: none;
|
|
}
|
|
.page {
|
|
max-height: 100%;
|
|
margin: 0;
|
|
break-inside: avoid;
|
|
break-after: page;
|
|
}
|
|
}
|
|
|
|
/* page 1 */
|
|
.week {
|
|
font-weight: bold;
|
|
font-size: calc(7 * var(--m));
|
|
}
|
|
.date {
|
|
font-size: calc(4 * var(--m));
|
|
}
|
|
.return-addr {
|
|
font-weight: bold;
|
|
margin: var(--m4);
|
|
}
|
|
.footer {
|
|
font-style: italic;
|
|
font-size: smaller;
|
|
position: absolute;
|
|
left: 0;
|
|
bottom: var(--m2);
|
|
width: 100%;
|
|
}
|
|
|
|
/* page 2-3 */
|
|
.month .landscape {
|
|
width: calc(var(--card-height) - var(--m4));
|
|
height: calc(var(--card-width) - var(--m4));
|
|
transform-origin: 0 0;
|
|
transform: rotate(-90deg) translateX(-100%);
|
|
}
|
|
.month table {
|
|
height: 100%;
|
|
width: 100%;
|
|
table-layout: fixed;
|
|
border-collapse: collapse;
|
|
}
|
|
.month thead {
|
|
height: 0.01%;
|
|
}
|
|
.month td {
|
|
border: thin solid black;
|
|
}
|
|
.month tbody td {
|
|
font-size: small;
|
|
text-align: left;
|
|
vertical-align: top;
|
|
}
|
|
|
|
/* page 4-5 */
|
|
.week table {
|
|
height: 100%;
|
|
width: 100%;
|
|
border: thin dashed black;
|
|
border-collapse: collapse;
|
|
}
|
|
.week tr {
|
|
border: thin solid black;
|
|
}
|
|
.week tr:nth-child(odd) td {
|
|
text-align: left;
|
|
padding-left: var(--m);
|
|
font-size: var(--m2);
|
|
height: var(--m2);
|
|
}
|
|
|
|
/* page 6-7 */
|
|
.page:has(img) {
|
|
padding: 0;
|
|
}
|
|
|
|
/* page 8 */
|
|
.sudoku table:has(table) {
|
|
width: fit-content;
|
|
margin: auto;
|
|
padding-top: calc(6 * var(--m));
|
|
border-collapse: separate;
|
|
}
|
|
.sudoku table table {
|
|
border-collapse: collapse;
|
|
}
|
|
.sudoku table table td {
|
|
width: calc(3 * var(--m));
|
|
height: calc(3 * var(--m));
|
|
border: thin solid black;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="settings">
|
|
<h1>PaperMod</h1>
|
|
<h2>the analog organizer</h2>
|
|
<p>I <a href="/blogette/1/#2025-11-14-bullent-journal">used to use</a> this thing called PocketMod to lay out my paper organizer every week. It's <a href="https://pocketmod.com/v2/">defunct</a> unless you can get Silverlight running somehow, so I made my own version with HTML and CSS.</p>
|
|
<p>Just fill out the form, click "generate", and use your browser's "print to pdf" function to get an 8 page document. To convert it into a booklet, I recommend using <a href="https://nashhigh.itch.io/zinearranger">The Zine Arranger</a> by Nash High or <a href="https://pocketverter.pages.dev/">Pocketverter</a> by <a href="https://github.com/Laur401/pocketverter">Laur401</a>.</p>
|
|
<div class="form">
|
|
<div>
|
|
<label for="date">start date:</label>
|
|
<input id="settings-date" type="date" name="date">
|
|
</div>
|
|
<div>
|
|
<label for="settings-dateformat">format:</label>
|
|
<select id="settings-dateformat" name="settings-dateformat">
|
|
<option value="mm-dd" default>mm-dd</option>
|
|
<option value="mm/dd">mm/dd</option>
|
|
<option value="dd/mm">dd/mm</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="settings-has-week">show week number:</label>
|
|
<span><input id="settings-has-week" type="checkbox" name="settings-has-week" checked></span>
|
|
</div>
|
|
<div>
|
|
<label for="settings-start-monday">always start on monday:</label>
|
|
<span><input id="settings-start-monday" type="checkbox" name="settings-start-monday" checked></span>
|
|
</div>
|
|
<div>
|
|
<label for="name">your name:</label>
|
|
<input id="settings-name" type="text" name="name">
|
|
</div>
|
|
<div>
|
|
<label for="contact">contact (email, phone, etc):</label>
|
|
<input id="settings-contact" type="text" name="contact">
|
|
</div>
|
|
</div>
|
|
<button id="settings-update">generate</button>
|
|
<button id="settings-clear">clear all</button>
|
|
<button onclick="window.print()">print</button>
|
|
</div>
|
|
<div id="document">
|
|
<div class="page">
|
|
<div class="week" contenteditable> </div>
|
|
<div class="date" contenteditable> </div>
|
|
<div class="return-addr" contenteditable>
|
|
<p id="user-name"> </p>
|
|
<p id="user-contact"> </p>
|
|
</div>
|
|
<div class="footer">nycki.net/papermod</div>
|
|
</div>
|
|
<div class="page month">
|
|
<div class="landscape">
|
|
<table contenteditable>
|
|
<thead>
|
|
<tr><td>mon</td><td>tue</td><td>wed</td><td>thu</td><td>fri</td><td>sat</td><td>sun</td></tr>
|
|
</thead>
|
|
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="page month">
|
|
<div class="landscape">
|
|
<table contenteditable>
|
|
<thead>
|
|
<tr><td>mon</td><td>tue</td><td>wed</td><td>thu</td><td>fri</td><td>sat</td><td>sun</td></tr>
|
|
</thead>
|
|
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="page week">
|
|
<table>
|
|
<tr><td>monday</td></tr>
|
|
<tr><td></td></tr>
|
|
<tr><td>tuesday</td></tr>
|
|
<tr><td></td></tr>
|
|
<tr><td>wednesday</td></tr>
|
|
<tr><td></td></tr>
|
|
<tr><td>thursday</td></tr>
|
|
<tr><td></td></tr>
|
|
</table>
|
|
</div>
|
|
<div class="page week">
|
|
<table>
|
|
<tr><td>friday</td></tr>
|
|
<tr><td></td></tr>
|
|
<tr><td>saturday</td></tr>
|
|
<tr><td></td></tr>
|
|
<tr><td>sunday</td></tr>
|
|
<tr><td></td></tr>
|
|
</table>
|
|
</div>
|
|
<div class="page">
|
|
<img src="dots.svg">
|
|
</div>
|
|
<div class="page">
|
|
<img src="dots.svg">
|
|
</div>
|
|
<div class="page">
|
|
<div class="sudoku">
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<table>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</td>
|
|
<td>
|
|
<table>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</td>
|
|
<td>
|
|
<table>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<table>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</td>
|
|
<td>
|
|
<table>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</td>
|
|
<td>
|
|
<table>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<table>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</td>
|
|
<td>
|
|
<table>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</td>
|
|
<td>
|
|
<table>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
<tr><td></td><td></td><td></td></tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
const elDate = document.querySelector('#settings-date');
|
|
|
|
const weekday = [
|
|
'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday',
|
|
];
|
|
|
|
function getWeek(date) {
|
|
const januaryFirst = new Date(date.getFullYear(), 0, 1);
|
|
return Math.ceil((((date - januaryFirst) / 86_400_000) + januaryFirst.getDay()+1) / 7);
|
|
}
|
|
|
|
function formatDate(date) {
|
|
const formatStr = document.querySelector('#settings-dateformat').value;
|
|
return (formatStr
|
|
.replace('mm', date.getMonth() + 1)
|
|
.replace('dd', date.getDate())
|
|
);
|
|
}
|
|
|
|
function update() {
|
|
document.querySelector('#user-name').textContent = document.querySelector('#settings-name').value;
|
|
document.querySelector('#user-contact').textContent = document.querySelector('#settings-contact').value;
|
|
|
|
let startDate = new Date(elDate.value);
|
|
if (isNaN(startDate)) startDate = new Date();
|
|
if (document.querySelector('#settings-start-monday').checked) {
|
|
startDate.setDate(startDate.getDate() - startDate.getDay() + 1);
|
|
}
|
|
let d;
|
|
|
|
startDate.setHours(12); // I don't care about the time zone
|
|
document.querySelector('.week').textContent = startDate.getFullYear();
|
|
if (document.querySelector('#settings-has-week').checked) {
|
|
document.querySelector('.week').textContent += `W${getWeek(startDate)}`;
|
|
}
|
|
|
|
d = new Date(startDate);
|
|
d.setDate(d.getDate() + 6);
|
|
document.querySelector('.date').textContent = `${formatDate(startDate)} ⋯ ${formatDate(d)}`
|
|
|
|
/* set up month */
|
|
const startDay = startDate.getDay();
|
|
for (const [i, el] of document.querySelectorAll('.month thead td').entries()) {
|
|
el.textContent = weekday[(startDay + i) % 7].slice(0,3);
|
|
}
|
|
d = new Date(startDate);
|
|
for (const el of document.querySelectorAll('.month tbody td')) {
|
|
el.textContent = d.getDate();
|
|
d.setDate(d.getDate() + 1);
|
|
}
|
|
|
|
/* set up week */
|
|
d = new Date(startDate);
|
|
for (const [i, el] of document.querySelectorAll('.week tr:nth-child(odd) td').entries()) {
|
|
el.textContent = `${formatDate(d)} ${weekday[d.getDay()]}`
|
|
d.setDate(d.getDate() + 1);
|
|
}
|
|
}
|
|
|
|
function clear() {
|
|
document.querySelector('.week').textContent = '';
|
|
document.querySelector('.date').textContent = '';
|
|
document.querySelectorAll('.month td').forEach(el => el.innerHTML = ' ');
|
|
document.querySelectorAll('.week td').forEach(el => el.innerHTML = ' ');
|
|
}
|
|
|
|
document.querySelector('#settings-update').onclick = update;
|
|
document.querySelector('#settings-clear').onclick = clear;
|
|
update();
|
|
</script>
|
|
</body>
|
|
</html> |