{
	"version": "https://jsonfeed.org/version/1",
	"title": "Bloggus Doekmanni",
	"home_page_url": "https://blog.zanstra.com/",
	"feed_url": "https://blog.zanstra.com/feed.json",
	"description": "This is Bloggus Doekmanni, or Doekman's blog if you like. A bit of drivel about computer programming; some dead serious, some not so much...\n",
	"icon": "https://blog.zanstra.com/images/doekman-square.png",
	"author": { "name": "Doeke Zanstra"},
	"items": [  {
			"id": "/ict/2026/05/20/i-spoke-on-pygrunn",
			"url": "https://blog.zanstra.com/ict/2026/05/20/i-spoke-on-pygrunn.html",
			"title": "I Spoke on PyGrunn",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>I spoke on <a href=\"https://pygrunn.org\" title=\"pyGrunn is the “Python and friends” developer conference with a local footprint and global mindset.\">PyGrunn</a> this year (May 8<sup>th</sup> 2026). See the details of this talk below for future reference.</p>\n\n<hr />\n\n<table>\n  <tbody>\n    <tr>\n      <td><strong>Title</strong></td>\n      <td>List-Man: Pragmatic System Integration</td>\n    </tr>\n    <tr>\n      <td><strong>Timeslot</strong></td>\n      <td>15:00 - 15:30</td>\n    </tr>\n    <tr>\n      <td><strong>Location</strong></td>\n      <td>Camera 4 (84 people)</td>\n    </tr>\n  </tbody>\n</table>\n\n<h3 id=\"summary\">Summary</h3>\n\n<p>Time is always limited, but when you have big plans, it’s time to get smart and pragmatic. At the time, the company was rapidly growing and processes were getting more mature every day. It was a dynamic environment with a heterogenous landscape of different information systems. That mix also puts a demand on the data-quality of all those systems; an error quickly goes unnoticed.</p>\n\n<p>Big companies often deploy some kind of enterprise service bus for these kinds of problems, but that was clearly overkill. So I made a plan how to tackle this problem, and presented the vision within ArchiLab, our companies internal vehicle for innovation.</p>\n\n<p>In this presentation I will present a number of examples of what I built, which address actual use-cases within the company. Examples include publishing data on the intranet, visually comparing lists for inconsistencies and recording data on ever changing lists. With these examples I will address things like bootstrapping your project, having an information architecture, software reusability and just building cool stuff. My tooling of choice were Debian Linux (development on Mac OS X), Python, Flask and Postgresql.</p>\n\n<h3 id=\"about-doeke-zanstra\">About Doeke Zanstra</h3>\n\n<p>Doeke is a versatile software developer living in the city of Groningen.\nToday he’s programming in Python, but in the past decades he also wrote other languages like Java, C# and Objective-C for Atos, KPN and AkzoNobel amongst others.<br />\nHe gets energy from roles where structure and creativity come together.</p>\n\n<h3 id=\"video\">Video</h3>\n\n<p><a href=\"https://youtu.be/gMO9ozFaFjk\">https://youtu.be/gMO9ozFaFjk</a></p>\n\n",
			"summary": "I spoke on PyGrunn this year (May 8th 2026). See the details of this talk below for future reference.",
			"date_published": "2026-05-20T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/ict/2026/01/06/open-tab-s-on-my-devices",
			"url": "https://blog.zanstra.com/ict/2026/01/06/open-tab-s-on-my-devices.html",
			"title": "Open Tab's on My Devices",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Below are the links on the internet-tab’s I found on my devices. I did a virtual cleanup of my browsers. I’ll keep the links for reference, but I doubt I’ll need them again. The list is obviously slightly redacted.</p>\n\n<p>I used <a href=\"https://www.idownloadblog.com/2025/10/17/copy-all-open-tab-links-safari/#On-iPhone\">these instructions</a> (I’m using Safari on my Apple devices) to save the links. With the use of an editor and macro’s, I converted the list to MarkDown. However, the iPhone-list didn’t come with titles, so I wrote a <a href=\"https://gist.github.com/doekman/121cc8e71368aaf2396f97b232b58f2a\">simple Python script</a> to fetch the titles. I did get around 10 <code class=\"language-plaintext highlighter-rouge\">403 Forbidden</code> responses, they probably thought I was an AI crawler. I fixed the titles by hand. Then I did a visual inspection, and fixed some other problems (no <code class=\"language-plaintext highlighter-rouge\">|</code> allowed in MarkDown link-text? Some WFT-8 problems with my script).</p>\n\n<html>\n<p>\n    To add some actual use to this page, I've added the following button (style by <a href=\"https://codepen.io/zastrow/pen/eYxvQZW\">this CodePen</a>):\n    <br />\n    <button onclick=\"window.open(getRandomLink());\">I'm feeling adventurous and nostalgic</button>\n</p>\n<script>\n    function getRandomLink() {\n        const nr_of_links_above = 3;\n        let container = document.getElementsByClassName('post-content')[0];\n        let links = container.getElementsByTagName('A');\n        let nr = Math.floor(Math.random() * (links.length - nr_of_links_above) + nr_of_links_above);\n        let link = links[nr];\n        console.info(\"Opening link nr %s.\", nr, links);\n        return link.href;\n    }\n</script>\n<style>\n    header {\n        \n    }\n    body {\n    \tfont-family: \"Lucida Grande\", system-ui, sans-serif;\n    \tmin-height: 100vh;\n        /*\n    \tdisplay: flex;\n    \tjustify-content: center;\n    \talign-items: center;\n        */\n    \tbackground-image: repeating-linear-gradient(\n    \t\t#fff 0%,\n    \t\t#fff 50%,\n    \t\t#f0f0f0 50%,\n    \t\t#f0f0f0 100%\n    \t);\n    \tbackground-size: 0.5ch 0.5ch;\n    \t/* Try changing these values to change the color\t */\n    \t--light-adjust: 0%;\n    \t--chroma-base: 41; /* Default: 41 */\n    \t--chroma-range: 13; /* Default: 13 */\n    \t--hue-base: 267deg; /* Default: 267deg */\n    }\n\n    button {\n    \tappearance: none;\n    \tfont-family: inherit;\n    \tfont-size: 1rem;\n    \ttext-shadow: 0 0.25ch 0.25ch\n    \t\tlch(calc(30% + var(--light-adjust)) var(--chroma-base) var(--hue-base) / 50%);\n    \tposition: relative;\n    \tborder: none;\n    \tcolor: lch(0% 0 0deg);\n    }\n\n    button,\n    button::after {\n    \tborder-radius: 1.5ch;\n    \tpadding: 0.5ch 1.5ch;\n    \tbackground-color: lch(\n    \t\tcalc(98% + var(--light-adjust))\n    \t\t\tcalc(var(--chroma-base) + (var(--chroma-range) * 2)) var(--hue-base) / 50%\n    \t);\n    \tbox-shadow: inset 0 0 0.05ch 0.025ch\n    \t\t\tlch(\n    \t\t\t\tcalc(100% + var(--light-adjust))\n    \t\t\t\t\tcalc(var(--chroma-base) + var(--chroma-range)) var(--hue-base) / 10%\n    \t\t\t),\n    \t\tinset 0 0 0.1ch 0.05ch\n    \t\t\tlch(\n    \t\t\t\tcalc(100% + var(--light-adjust))\n    \t\t\t\t\tcalc(var(--chroma-base) + var(--chroma-range)) var(--hue-base) / 50%\n    \t\t\t),\n    \t\tinset 0 0.2ch 0.4ch 0.1ch\n    \t\t\tlch(calc(60% + var(--light-adjust)) var(--chroma-base) var(--hue-base) / 50%),\n    \t\tinset 0 -0.25ch 0.25ch 0 lch(calc(20% + var(--light-adjust)) 41\n    \t\t\t\t\tvar(--hue-base) / 10%),\n    \t\tinset -0.25ch 0 0.25ch 0 lch(calc(20% + var(--light-adjust))\n    \t\t\t\t\tcalc(var(--chroma-base) + var(--chroma-range)) var(--hue-base) / 40%),\n    \t\tinset 0.25ch 0 0.25ch 0\n    \t\t\tlch(\n    \t\t\t\tcalc(20% + var(--light-adjust))\n    \t\t\t\t\tcalc(var(--chroma-base) + var(--chroma-range)) var(--hue-base) / 40%\n    \t\t\t),\n    \t\tinset 0 0.5ch 1ch 0.5ch\n    \t\t\tlch(\n    \t\t\t\tcalc(30% + var(--light-adjust))\n    \t\t\t\t\tcalc(var(--chroma-base) + var(--chroma-range)) var(--hue-base) / 60%\n    \t\t\t);\n    \ttransition: all ease-in-out 250ms;\n    }\n\n    button:hover {\n    \t--light-adjust: 10%;\n    }\n\n    button::before,\n    button::after {\n    \tcontent: \"\";\n    \tdisplay: block;\n    \tposition: absolute;\n    }\n\n    button::before {\n    \tinset: 0.25ch 0.75ch auto;\n    \tbackground: linear-gradient(\n    \t\tlch(calc(100% + var(--light-adjust)) 0 var(--hue-base) / 50%),\n    \t\tlch(\n    \t\t\tcalc(100% + var(--light-adjust)) var(--chroma-base) var(--hue-base) / 30%\n    \t\t),\n    \t\tlch(calc(98% + var(--light-adjust)) var(--chroma-base) var(--hue-base) / 5%)\n    \t);\n    \theight: 0.875ch;\n    \tborder-radius: 2ch 2ch 0.5ch 0.5ch;\n    \tz-index: 1;\n    }\n\n    button::after {\n    \tinset: 0;\n    \topacity: 80%;\n    \tfilter: blur(0.25ch);\n    \ttranslate: 0 0.875ch;\n    \tz-index: -1;\n    }\n</style>\n</html>\n\n<h2 id=\"tabs-on-my-imac\">Tab’s on my iMac</h2>\n\n<ul>\n  <li>Window 1\n    <ul>\n      <li><a href=\"https://hackaday.com/2023/11/12/the-ibm-mda-should-have-been-the-cda/\">The IBM MDA Should Have Been The CDA | Hackaday</a></li>\n      <li><a href=\"https://ports.macports.org/\">Ports | MacPorts</a></li>\n      <li><a href=\"https://docs.brew.sh/Support-Tiers#tier-3\">Support Tiers — Homebrew Documentation</a></li>\n      <li><a href=\"https://en.wikipedia.org/wiki/Dyson_sphere\">Dyson sphere - Wikipedia</a></li>\n      <li><a href=\"https://garagehq.deuxfleurs.fr/documentation/quick-start/\">Quick Start | Garage HQ</a></li>\n      <li><a href=\"https://jyn.dev/the-terminal-of-the-future/\">the terminal of the future</a></li>\n      <li><a href=\"https://asahilinux.org/2025/12/progress-report-6-18/\">Progress Report: Linux 6.18 - Asahi Linux</a></li>\n      <li><a href=\"https://pivot-to-ai.com/\">Pivot to AI – It can’t be that stupid, you must be prompting it wrong</a></li>\n      <li><a href=\"https://course.ysap.sh/\">The Complete Bash Scripting Course - ysap.sh</a></li>\n      <li><a href=\"https://www.martinnobel.com/techresearch/ios-6-screenshots\">iOS 6 Revisited — Martin Nobel</a></li>\n      <li><a href=\"https://www.qwant.com/?client=ext-safari-macos-sb&amp;t=web&amp;q=shortcut+to+export+all+opened+tabs+in+safari\">shortcut to export all opened tabs in safari – Qwant</a></li>\n      <li><a href=\"https://www.idownloadblog.com/2025/10/17/copy-all-open-tab-links-safari/\">How to copy website links of all open tabs in Safari</a></li>\n      <li><a href=\"https://www.howtogeek.com/758131/how-to-copy-urls-of-all-open-tabs-in-safari/\">How to Copy URLs of All Open Tabs in Safari</a></li>\n    </ul>\n  </li>\n  <li>Window 2\n    <ul>\n      <li><a href=\"https://zed.dev/\">Zed — Love your editor again</a></li>\n      <li><a href=\"https://jpmens.net/2024/02/23/totp-codes-in-the-terminal/\">Jan-Piet Mens :: Using 2FAS TOTP codes in the terminal</a></li>\n      <li><a href=\"https://github.com/phuocng/html-dom\">phuocng/html-dom: Common tasks of managing HTML DOM with vanilla JavaScript. Give me 1 ⭐if it’s useful.</a></li>\n      <li><a href=\"https://www.bikebaze.nl/user-account\">Mijn account - Beheer Je BIKEBAZE® Fietsregistraties</a></li>\n      <li><a href=\"https://www.sfgate.com/tech/article/time-to-declare-war-ai-21221535.php\">The time has come to declare war on AI</a></li>\n      <li><a href=\"https://codeberg.org/doekman/PySQL\">doekman/PySQL: Use raw SQL directly from python. Inspired by PugSQL, but with more Pythonesque. - Codeberg.org</a></li>\n      <li><a href=\"https://pugsql.org/\">PugSQL :: SQL is Extremely Good, Actually</a></li>\n    </ul>\n  </li>\n  <li>Window 3 (minimized to dock)\n    <ul>\n      <li><a href=\"https://fastpen.lecaro.me/#H4sIAAAAAAAAE31TQZKbMBC8%2BxUqckmqQjDxHlIy5pCfCGmA2ZUllSRssym%2FIB%2FYWz6SPyVPiAQ2YHsrOlCt6ZY008wUzHrkEgiKXcJb5pNyRcIq2rw8MCUYa4os4DFoCJfMuV2ifQs2KQtnmCr%2F%2Fnp7K7IBFpm5lzqQ9X%2BVtbb7EQ7bqvNeq%2FLP759FdsEzicp0nvjewC7xcPIJMZJxaLUUYHfJd7DI26mGbLy6yC5FBsiZOjA3VjvAkFo2onK1KpzvoyyerrTof0wv6wPYWuojbVEIUFsyMS1g03qar9eHdjtFBbqQWU9rCac5GnepQAvco1aUa9nt1UwziY1K0cPeUQ7Kg5255855rPuU6xBX%2FoGvQzyt2R5lT4ljyqUumFHPgvCYtvTDZrNZJG%2FAOhOzOQAl39Zrc0n2PHwvts0uDEe0wyF7C5LFg9sb%2BjVFJeBE89twxfhLY3WnBD22ocJb9ojCt%2FTr%2FP51GSYEqiZQ90ylbfjlqWUCO0ef3qdpbk7EaYmCVKFPXpbFtflcV2yldHD%2Fwdc9sw0qul4YKYFZyqRc3mZI7GoyXzn8D4evQJ%2FuXP0yDM9SKjXzlEio%2FY0uTs6DzMZuu08vHaL0xqS7fOK6NiUqiQrSSuqrJdflbeicODWhhziT8DHNP5P80yw6LxKMusULoy9VqO7SYOcweZeBCpPFLRofUTbBKJqOvw9mzT%2FmWRMPqgQAAA%3D%3D\">Fastpen : a client side only codepen alternative</a></li>\n      <li><a href=\"https://aaadaaam.com/notes/tools-that-build-you/\">Tools that build you | Adam Stoddard</a></li>\n      <li><a href=\"https://maclabs.jazzace.ca/2025/06/07/blog-or-wiki.html\">Anthony’s Mac Labs Blog | Blog Post or Wiki Edit? (📦)</a></li>\n      <li><a href=\"https://alexwlchan.net/2025/swift-bird-animation/?ref=mastodon\">Recreating the bird animation from Swift.org – alexwlchan</a></li>\n    </ul>\n  </li>\n  <li>Window 4 (minimized to dock)\n    <ul>\n      <li><a href=\"https://www.hotsoup.nl/nl/herkomst/nepalese-thee.html\">Nepalese thee | Hotsoup.nl online thee voor fijnproevers</a></li>\n      <li><a href=\"https://mikrotik.com/\">MikroTik · Routers and Wireless</a></li>\n      <li><a href=\"https://www.ebay.nl/mye/myebay/summary\">Overzicht | Mijn eBay | eBay</a></li>\n    </ul>\n  </li>\n  <li>Window 5 (minimized to dock)\n    <ul>\n      <li><a href=\"https://www.kleine-klussen.nl/#klusplanner\">Klusjesman in Groningen, Haren, Meerstad, Eelderwolde</a></li>\n      <li><a href=\"https://www.monumenten.nl/ontzorgingsprogramma\">Ontzorgingsprogramma Verduurzaming Monumenten | Monumenten.nl</a></li>\n      <li><a href=\"https://www.bitsoffreedom.nl/2025/10/16/weg-van-big-tech-stap-4-navigatie-en-kaarten/\">Weg van Big Tech stap 4: navigatie en kaarten – Bits of Freedom</a></li>\n      <li><a href=\"https://www.bitrecover.com/blog/export-gmail-to-maildir/\">Export Gmail/GSuite to Maildir with All Components</a></li>\n      <li><a href=\"https://soverin.nl/help/migratie-van-google-naar-soverin\">Soverin: Migratie van Google naar Soverin</a></li>\n      <li><a href=\"https://www.lastdodo.nl/nl/my/shop_orders/12093119\">LastDodo - ’s Werelds grootste catalogus &amp; community voor verzamelaars</a></li>\n    </ul>\n  </li>\n  <li>Window 6 (minimized to dock)\n    <ul>\n      <li><a href=\"https://noorderlink.nl/vacature/74268-01422-079603050116-senior-devops-engineer-idp-ministerie-van-economische-zaken-en-klimaat-ezk?utm_medium=email&amp;utm_source=transactional&amp;utm_campaign=vacatures@noorderlink.nl\">Senior DevOps Engineer IDP bij Ministerie van Economische Zaken en Klimaat (EZK) | Noorderlink</a></li>\n      <li><a href=\"https://www.mijnmovum.nl/folders/27/blocks\">Mijn Movum</a></li>\n      <li><a href=\"https://runwaylanderspot.com/?d=durdraw.org&amp;pkAId=2143526812\">Parking Page</a></li>\n    </ul>\n  </li>\n</ul>\n\n<h2 id=\"tabs-on-my-macbook-pro\">Tab’s on my MacBook Pro</h2>\n\n<ul>\n  <li>Window 1\n    <ul>\n      <li><a href=\"https://www.joanwestenberg.com/the-case-for-blogging-in-the-ruins/\">he Case for Blogging in the Ruins</a></li>\n      <li><a href=\"https://shantellsans.com/process\">Shantell Sans → ArrowType</a></li>\n      <li><a href=\"https://www.felienne.nl/2026-01/\">Special Schrijven | Writing</a></li>\n      <li><a href=\"https://www.ns.nl/reisplanner/#/?vertrek=Zwolle&amp;vertrektype=treinstation&amp;aankomst=Groningen&amp;aankomsttype=treinstation&amp;type=vertrek&amp;tijd=2026-01-04T19:30&amp;firstMileModality=PUBLIC_TRANSPORT&amp;lastMileModality=PUBLIC_TRANSPORT&amp;context=arnu%7CfromStation%3D8400747%7CrequestedFromStation%3D8400747%7CtoStation%3D8400263%7CrequestedToStation%3D8400263%7CplannedFromTime%3D2026-01-04T19:16:00%2B01:00%7CplannedArrivalTime%3D2026-01-04T20:12:00%2B01:00%7CexcludeHighSpeedTrains%3Dfalse%7CsearchForAccessibleTrip%3Dfalse%7ClocalTrainsOnly%3Dfalse%7CtravelAssistance%3Dfalse%7CtripSummaryHash%3D3604313121&amp;ritnummers=765\">Reisplanner | Plan je reis | NS</a></li>\n      <li><a href=\"https://nos.nl/artikel/2596943-topdrukte-voor-strooiers-dit-is-onze-wintersport\">Topdrukte voor strooiers: ‘Dit is onze wintersport’</a></li>\n      <li><a href=\"https://nos.nl/artikel/2596914-vk-en-frankrijk-bombarderen-is-complex-in-syrie\">VK en Frankrijk bombarderen IS-complex in Syrië</a></li>\n      <li><a href=\"https://www.unilin.com/nl\">Home</a></li>\n      <li><a href=\"https://evabits.com/\">This Connection Is Not Private</a></li>\n      <li><a href=\"https://www.wadcon.nl/\">Wadcon | Speciaalmachinebouw | Automatisering | Transfersystemen</a></li>\n    </ul>\n  </li>\n  <li>Window 2\n    <ul>\n      <li><a href=\"https://voorbereidboek.nl/\">Voorbereid – Zo zien de grootste Nederlandse rampscenario’s eruit</a></li>\n      <li><a href=\"https://lab.alexanderobenauer.com/updates/the-jasper-report\">Recreating the Canon Cat document interface - Little Lab</a></li>\n      <li><a href=\"https://www.w3tutorials.net/python-tutorial/how-to-use-linting-for-python-code-quality/\">How to Use Linting for Python Code Quality — w3tutorials.net</a></li>\n      <li><a href=\"https://realpython.com/ruff-python/\">Ruff: A Modern Python Linter for Error-Free and Maintainable Code – Real Python</a></li>\n    </ul>\n  </li>\n  <li>Window 3\n    <ul>\n      <li><a href=\"https://www.linusakesson.net/programming/tty/\">The TTY demystified</a></li>\n      <li><a href=\"https://typographyforlawyers.com/\">Typography for Lawyers</a></li>\n      <li><a href=\"http://scripting.com/\">Scripting News</a></li>\n      <li><a href=\"https://blog.codinghorror.com/the-trap-you-set-for-yourself/\">The Trap You Set For Yourself</a></li>\n      <li><a href=\"https://aspect.bildhuus.com/#file-system\">Bildhuus Aspect</a></li>\n      <li><a href=\"https://www.bitsoffreedom.nl/2025/09/16/stap-over-op-privacyvriendelijke-e-mail-agenda/\">Weg van Big Tech stap 1: e-mail &amp; agenda – Bits of Freedom</a></li>\n      <li><a href=\"https://tonsky.me/blog/syntax-highlighting/\">I am sorry, but everyone is getting syntax highlighting wrong @ tonsky.me</a></li>\n      <li><a href=\"https://tangiblemediacollection.com/\">Tangible Media: A Historical Collection of Information Storage Technology</a></li>\n    </ul>\n  </li>\n  <li>Window 4\n    <ul>\n      <li><a href=\"https://www.photosync-app.com/home\">PhotoSync – Photo Transfer and Backup App | For iOS &amp; Android - PhotoSync</a></li>\n      <li><a href=\"https://github.com/git/git/blob/master/Documentation/gitdatamodel.adoc\">git/Documentation/gitdatamodel.adoc at master · git/git</a></li>\n      <li><a href=\"https://community.signalusers.org/c/ux/ios-ux-ui/29\">(4) Latest UI/UX Feedback/iOS UI/UX topics - Signal Community</a></li>\n      <li><a href=\"https://bitbucket.org/doekman/workspace/repositories/\">doekman / Repositories — Bitbucket</a></li>\n    </ul>\n  </li>\n  <li>Window 5\n    <ul>\n      <li><a href=\"https://jekyllrb.com/docs/collections/#output\">Collections | Jekyll • Simple, blog-aware, static sites</a></li>\n      <li><a href=\"http://127.0.0.1:4000/like/\">Pagina openen mislukt</a></li>\n      <li><a href=\"https://nora.codes/post/infrastructure-photography-manifesto-and-decker-in-html/\">Infrastructure photography manifesto</a></li>\n      <li><a href=\"https://beyondloom.com/decker/\">https://beyondloom.com/decker/</a></li>\n    </ul>\n  </li>\n  <li>Window 6\n    <ul>\n      <li><a href=\"https://calpaterson.com/bank-python.html\">n oral history of Bank Python</a></li>\n      <li><a href=\"https://togetherlondon.com/insights/active-listening-swiss-army-knife\">Active Listening: Swiss Army Knife of Communication (with Examples)</a></li>\n    </ul>\n  </li>\n  <li>Window 7 (hidden in a virtual desktop)\n    <ul>\n      <li><a href=\"https://buttondown.com/whatever_jamie/archive/the-many-many-many-javascript-runtimes-of-the-last-decade/\">The many, many, many JavaScript runtimes of the last decade • Buttondown</a></li>\n      <li><a href=\"https://github.com/mcfunley/pugsql\">mcfunley/pugsql: A HugSQL-inspired database library for Python</a></li>\n      <li><a href=\"https://www.youtube.com/watch?v=GfH4QL4VqJ0&amp;list=WL&amp;index=11\">(35) Python: The Documentary | An origin story - YouTube</a></li>\n      <li><a href=\"https://www.devalkzalk.nl/nl/molenwinkel\">molenwinkel</a></li>\n      <li><a href=\"https://anno.nl/contact/\">Contact museum ANNO Zwolle</a></li>\n      <li><a href=\"https://hetanderenepal.nl/familiereizen-nepal/\">Familiereizen Nepal | Het Andere Nepal</a></li>\n      <li><a href=\"https://www.anwb.nl/vakantie/nepal/beste-reistijd\">Beste reistijd Nepal | Weer en klimaat Nepal » ANWB</a></li>\n      <li><a href=\"https://www.google.com/travel/flights/search?tfs=CBwQAhonEgoyMDI2LTAzLTAyagsIAhIHL20vMGszcHIMCAMSCC9tLzA0Y3g1GicSCjIwMjYtMDMtMjNqDAgDEggvbS8wNGN4NXILCAISBy9tLzBrM3BAAUgBcAGCAQsI____________AZgBAQ\">Amsterdam to Kathmandu | Google Flights</a></li>\n      <li><a href=\"https://en.wikipedia.org/wiki/Nepal\">Nepal - Wikipedia</a></li>\n      <li><a href=\"https://www.bestereistijd.nl/nepal/#weerervaringen\">Nepal temperatuur, klimaat en weer per maand</a></li>\n    </ul>\n  </li>\n</ul>\n\n<p>Update: to add insult to injury, I found a document with a list of links I saved because I had to re-install my MacBook Pro:</p>\n\n<ul>\n  <li>Mac:\n    <ul>\n      <li><a href=\"https://blog.persistent.info/2025/07/infinite-mac-embedding.html\">Infinite Mac Construction Set</a></li>\n      <li><a href=\"https://mavericksforever.com\">Mavericks Forever</a></li>\n    </ul>\n  </li>\n  <li>Programmeren:\n    <ul>\n      <li><a href=\"https://browser.engineering\">Web Browser Engineering</a></li>\n      <li><a href=\"https://unplannedobsolescence.com/blog/hard-page-load/\">Who’s Afraid of a Hard Page Load</a></li>\n      <li><a href=\"https://tablib.readthedocs.io/en/latest/\">Tablib: Pythonic Tabular Datasets</a></li>\n      <li><a href=\"https://docs.python-guide.org/scenarios/db/\">The Hitchhiker’s Guide to Python; DB’s</a></li>\n    </ul>\n  </li>\n  <li>Ontwerp/CSS/HTML:\n    <ul>\n      <li><a href=\"https://aaadaaam.com/notes/useful-defaults/\">There’s no such thing as a CSS reset</a></li>\n      <li><a href=\"https://mavo.io\">A new, approachable way to create Web applications</a></li>\n      <li><a href=\"https://alexwlchan.net/2025/learning-how-to-make-websites/#thoughtful_html\">What I learnt about making websites by reading two thousand web pages</a></li>\n      <li><a href=\"https://www.smashingmagazine.com/2016/11/css-inheritance-cascade-global-scope-new-old-worst-best-friends/\">CSS Inheritance, The Cascade And Global Scope: Your New Old Worst Best Friends</a></li>\n      <li><a href=\"https://heydonworks.com/article/the-col-element/\">Dingen over HTML enzo (hier over <code class=\"language-plaintext highlighter-rouge\">&lt;col&gt;</code>)</a></li>\n      <li><a href=\"https://fossil-scm.org/home/doc/trunk/www/javascript.md\">Use of JavaScript in Fossil</a></li>\n      <li><a href=\"https://linkingmanifesto.org\">Manifesto for Ubiquitous Linking</a></li>\n    </ul>\n  </li>\n  <li>Tech Ops:\n    <ul>\n      <li><a href=\"https://www.geeksforgeeks.org/devops/podman-vs-docker/\">Podman vs Docker: What Are the Key Differences Explained in Detail</a></li>\n      <li><a href=\"https://stevens.netmeister.org/615/\">CS615 – System Administration</a></li>\n      <li><a href=\"https://hogelandlinux.nl/#diensten\">Geef uw computer een tweede leven</a></li>\n    </ul>\n  </li>\n  <li>Lezen:\n    <ul>\n      <li><a href=\"https://www.nrc.nl/nieuws/2025/07/02/de-maan-is-een-springplank-een-proeftuin-en-nu-nog-een-helder-venster-op-het-heelal-a4899037\">De maan is een springplank, een proeftuin en (nu nog) een helder venster op het heelal</a></li>\n      <li><a href=\"https://thefutureishumane.com/beyond-men-and-masculinity-the-film/\">BEYOND MEN AND MASCULINITY (film)</a></li>\n      <li><a href=\"https://borretti.me/article/notes-on-managing-adhd\">Notes on Managing ADHD</a></li>\n    </ul>\n  </li>\n  <li>Opinion:\n    <ul>\n      <li><a href=\"https://malwaretech.com/2025/08/every-reason-why-i-hate-ai.html?a=1\">Every Reason Why I Hate AI and You Should Too</a></li>\n    </ul>\n  </li>\n  <li>Cmptr (retro):\n    <ul>\n      <li><a href=\"https://tansanrao.com/blog/2025/04/xnu-kernel-and-darwin-evolution-and-architecture/#user-content-fnref-2\">Apple’s Darwin OS and XNU Kernel Deep Dive</a></li>\n      <li><a href=\"https://blog.decryption.net.au/posts/magic-cap.html\">General Magic’s Adorable Magic Cap Operating System</a></li>\n    </ul>\n  </li>\n  <li>Tooling:\n    <ul>\n      <li><a href=\"https://sqlitebrowser.org\">https://sqlitebrowser.org</a></li>\n      <li><a href=\"https://cooked.wiki/landing\">https://cooked.wiki/landing</a></li>\n      <li><a href=\"https://fossil-scm.org/home/doc/trunk/www/fossil-v-git.wiki\">Fossil Versus Git</a></li>\n      <li><a href=\"https://vdt-labs.com/json-editor/\">JSON Editor</a></li>\n      <li>Editors\n        <ul>\n          <li><a href=\"https://zed.dev/blog/debugger\">https://zed.dev/blog/debugger</a></li>\n          <li><a href=\"https://helix-editor.com\">https://helix-editor.com</a></li>\n        </ul>\n      </li>\n      <li><a href=\"https://f-droid.org\">F-Droid is an installable catalogue of FOSS (Free and Open Source Software) applications for the Android platform</a></li>\n    </ul>\n  </li>\n  <li>Verstrooing:\n    <ul>\n      <li><a href=\"https://www.thebiglebow.ski/random/favorite\">The Big Lebowski Quotes</a></li>\n    </ul>\n  </li>\n</ul>\n\n<p>And a list of links from email messages, also around the same date:</p>\n\n<ul>\n  <li><a href=\"https://retool.com/visual-basic/\">https://retool.com/visual-basic/</a></li>\n  <li><a href=\"https://smallstep.com/blog/ssh-agent-explained/\">https://smallstep.com/blog/ssh-agent-explained/</a></li>\n  <li><a href=\"https://hachyderm.io/@leftpaddotpy/110764798227834050\">http debug</a></li>\n  <li><a href=\"https://social.jvns.ca/@b0rk/110775162006555035\">twitter archive</a></li>\n  <li><a href=\"https://learnui.design/blog/7-rules-for-creating-gorgeous-ui-part-2.html\">7 Rules for Creating Gorgeous UI – Part 2</a></li>\n  <li><a href=\"https://nolanlawson.com/2023/12/02/lets-learn-how-modern-javascript-frameworks-work-by-building-one/\">Let’s learn how modern JavaScript frameworks work by building one</a></li>\n  <li><a href=\"https://cacm.acm.org/magazines/2024/1/278891-10-things-software-developers-should-learn-about-learning/fulltext\">10 Things Software Developers Should Learn about Learning</a></li>\n  <li><a href=\"https://www.reddit.com/r/MacOS/comments/1dkklgs/how_to_remove_caps_lock_key_delay/\">How to remove Caps Lock key delay: MacOS</a></li>\n  <li><a href=\"https://pythonspeed.com/docker/\">Articles: Production-ready Docker packaging for Python developers</a></li>\n  <li><a href=\"https://mjtsai.com/blog/2025/04/11/appexindexer-1-0/\">AppexIndexer 1.0</a></li>\n  <li><a href=\"https://mastodon.social/@jpmens/114352679361368952\">Sync bookmarks</a></li>\n  <li><a href=\"https://front-end.social/@mia/114635882877016925\">Css Function</a></li>\n</ul>\n\n<h2 id=\"tabs-on-my-iphone-mini\">Tab’s on my iPhone mini</h2>\n\n<ul>\n  <li><a href=\"https://datasette.io/\">Datasette: An open source multi-tool for exploring and publishing data</a></li>\n  <li><a href=\"https://lekkerder.nl/groentekwekerij-noordlaren\">Groentekwekerij Noordlaren  - Boerderijwinkel, Kwekerij en Anders in Noordlaren</a></li>\n  <li><a href=\"https://zuriga.com/de/Produkte/ZURIGA-E2/ZEEU401\">https://zuriga.com/de/Produkte/ZURIGA-E2/ZEEU401 (no title)</a></li>\n  <li><a href=\"https://pine64.com/product/pinecil-smart-mini-portable-soldering-iron/\">PINECIL – Smart Mini Portable Soldering Iron (Version 2) - PINE STORE</a></li>\n  <li><a href=\"https://bluescsi.com/v2#new\">BlueSCSI v2 Pico Announcement - BlueSCSI</a></li>\n  <li><a href=\"https://en.m.wikipedia.org/wiki/The_Ozark_Mountain_Daredevils\">The Ozark Mountain Daredevils</a></li>\n  <li><a href=\"https://www.npr.org/sections/health-shots/2016/04/13/474071268/how-lsd-makes-your-brain-one-with-the-universe\">Brain Connections May Cause Blurring Sense Of Self With LSD : Shots - Health News : NPR</a></li>\n  <li><a href=\"https://duckduckgo.com/?q=cat+lee+king+and+his+cooks&amp;t=iphone&amp;ia=web\">cat lee king and his cooks at DuckDuckGo</a></li>\n  <li><a href=\"https://weblogs.vpro.nl/plots/\">https://weblogs.vpro.nl/plots/</a> – 404 Not Found</li>\n  <li><a href=\"https://www.vpro.nl/programmas/tegenlicht/kijk/afleveringen/2019-2020/houtbouwers.html\">Houtbouwers | VPRO Tegenlicht</a></li>\n  <li><a href=\"https://ralphammer.com/make-me-think/\">Make me think! – Ralph Ammer</a></li>\n  <li><a href=\"https://unsplash.com/\">Beautiful Free Images &amp; Pictures | Unsplash</a></li>\n  <li><a href=\"https://www.o42interieur.nl/\">De mooiste interieurstudio en showroom van Groningen - O’42 interieur</a></li>\n  <li><a href=\"https://www.mantherapy.org/\">https://www.mantherapy.org/ (no title)</a></li>\n  <li><a href=\"https://vimeo.com/showcase/9459437/video/838408969/embed\">Vimeo: Vera Weekly</a></li>\n  <li><a href=\"https://www.templatemaker.nl/en/\">✂Templatemaker</a></li>\n  <li><a href=\"https://soundtracker.com/\">Official Website: Gordon Hempton – The Sound Tracker®</a></li>\n  <li><a href=\"https://decorrespondent.nl/11931/jij-dacht-dat-je-goed-kon-luisteren-luister-dan-eens-naar-deze-man-met-microfoon/3ae02ea8-67fb-0e9a-27cb-2eef1bd1ee06\">Jij dacht dat je goed kon luisteren? Luister dan eens naar deze man met microfoon  - De Correspondent</a></li>\n  <li><a href=\"https://hackaday.com/2023/01/09/all-about-usb-c-power-delivery/\">All About USB-C: Power Delivery | Hackaday</a></li>\n  <li><a href=\"https://www.npr.org/2017/07/12/533862948/lets-get-graphic-100-favorite-comics-and-graphic-novels\">100 Best Comics And Graphic Novels : NPR</a></li>\n  <li><a href=\"https://lithub.com/tag/climate-change-library/\">Literary Hub » Climate Change Library</a></li>\n  <li><a href=\"https://yewtu.be/\">Invidious - search</a></li>\n  <li><a href=\"https://www.npr.org/sections/altlatino/2013/09/30/227834004/cumbia-the-musical-backbone-of-latin-america\">Cumbia: The Musical Backbone Of Latin America : Alt.Latino : NPR</a></li>\n  <li><a href=\"https://www.c-support.nu/\">C-support | Wij ondersteunen mensen met langdurige corona klachten</a></li>\n  <li><a href=\"https://mtlynch.io/aardvarkd/\">Aardvark’d: The Fog Creek Documentary, 18 Years Later ·mtlynch.io</a></li>\n  <li><a href=\"https://www.abortretry.fail/p/an-introduction-to-linux-part-1\">https://www.abortretry.fail/p/an-introduction-to-linux-part-1</a> – 404 Not Found</li>\n  <li><a href=\"https://www.thomann.de/nl/shure_sm57lc_bundle_ii.htm?msclkid=fcfac665a0f915896003a3681845bcf5&amp;utm_source=bing&amp;utm_medium=cpc&amp;utm_campaign=Shopping%20NL&amp;utm_term=4576717170826333&amp;utm_content=MI\">Shure SM57LC Bundle II – Thomann Nederland</a></li>\n  <li><a href=\"https://www.lovemysalad.com/nl/recepten/lasagne-met-pompoen-ricotta-spinazie-en-salie\">LASAGNE MET POMPOEN, RICOTTA, SPINAZIE EN SALIE</a></li>\n  <li><a href=\"https://duckduckgo.com/?q=jon+spencer+blues+explosion&amp;t=iphone&amp;ia=web\">jon spencer blues explosion at DuckDuckGo</a></li>\n  <li><a href=\"https://jennyodell.com/writing.html\">jenny odell - writing</a></li>\n  <li><a href=\"https://chrisfitzgeraldmusic.com/watch/\">Watch – Chris Fitzgerald</a></li>\n  <li><a href=\"https://chrisfitzgeraldmusic.com/download/walking-bass-line-theory-basics/?wpdmdl=1258&amp;refresh=652eb84518c8f1697560645\">https://chrisfitzgeraldmusic.com/download/walking-bass-line-theory-basics/?wpdmdl=1258&amp;refresh=652eb84518c8f1697560645 (no title)</a></li>\n  <li><a href=\"https://neal.fun/internet-artifacts/amazon-order/\">First Amazon Order</a></li>\n  <li><a href=\"https://plantbaseddennis.nl/nl/recepten/diner/de-lekkerste-tempeh-marinades/\">De lekkerste tempeh-marinade - Plantbased Dennis</a></li>\n  <li><a href=\"https://tome.one/slides/amiet-pelissier-security-keys-workshop-ph0wn-2023-slides.pdf\">https://tome.one/slides/amiet-pelissier-security-keys-workshop-ph0wn-2023-slides.pdf (no title)</a></li>\n  <li><a href=\"https://en.m.wikipedia.org/wiki/Primer_(film)\">Primer (film)</a></li>\n  <li><a href=\"https://standardebooks.org/ebooks\">Browse Standard Ebooks - Standard Ebooks: Free and liberated ebooks, carefully produced for the true book lover</a></li>\n  <li><a href=\"https://www.airdancer.nl/airdancer/natuurlijke-luchtverfrisser/luchtverfrisser-buiten/13823\">Luchtverfrisser Buiten: Natuurlijke luchtverfrisser: Airdancer.nl</a></li>\n  <li><a href=\"https://www.hawalili.com/\">Shop Hawaiian Shirts-Hawalili | hawalili</a></li>\n  <li><a href=\"https://www.macintoshrepository.org/48943-shuriken\">Shuriken - Macintosh Repository</a></li>\n  <li><a href=\"https://rvessen.wordpress.com/2023/07/30/korte-cursus-korte-verhalen-schrijven/\">korte cursus korte  verhalen schrijven | reddend zwemmen</a></li>\n  <li><a href=\"https://duckduckgo.com/?q=data+model+web+shop&amp;t=iphone&amp;ia=web\">data model web shop at DuckDuckGo</a></li>\n  <li><a href=\"https://shkspr.mobi/blog/2024/02/activitypub-server-in-a-single-file/\">ActivityPub Server in a Single PHP File – Terence Eden’s Blog</a></li>\n  <li><a href=\"https://www.nytimes.com/interactive/2023/03/03/well/mind/breathing-exercises.html\">3 Breathing Exercises to Relieve Stress and Improve Health - The New York Times</a></li>\n  <li><a href=\"https://www.tammo80.nl/\">Tammo80.nl - Tammo Jan Dijkema</a></li>\n  <li><a href=\"https://kerkour.com/sqlite-for-servers\">Optimizing SQLite for servers</a></li>\n  <li><a href=\"https://goedmaken.org/index.php/2024/04/15/wie-bepaalt-het-narratief-rond-ai/\">Wie bepaalt het narratief rond AI? – Goed Maken ❤️</a></li>\n  <li><a href=\"https://axleos.com/building-a-gps-receiver-part-1-hearing-whispers/\">Building a GPS Receiver, Part 1: Hearing Whispers | Phillip Tennen</a></li>\n  <li><a href=\"https://anderetijden.nl/aflevering/707/Op-vakantie-naar-Afghanistan\">Op vakantie naar Afghanistan - Andere Tijden</a></li>\n  <li><a href=\"https://alinpanaitiu.com/blog/woodworking-escape-from-software-absurdity/#other-small-wood-things\">Woodworking as an escape from the absurdity of software</a></li>\n  <li><a href=\"https://chriscoyier.net/2024/07/22/practical-svg-is-now-free-to-read-online/\">Practical SVG is Now Free to Read Online – Chris Coyier</a></li>\n  <li><a href=\"https://nl.jvc.com/compare/?cat=93&amp;products=4228,1584,591,585\">Compare • JVC Nederland</a></li>\n  <li><a href=\"https://www.bol.com/nl/nl/p/breaker-s-revenge-original-b-boy-and-b-girl-breakdance-classics/9300000178253704/?s2a=#productTitle\">Arthur Baker - Arthur Baker presents Breaker’s Revenge And Original B-Boy and B-Girl… | bol</a></li>\n  <li><a href=\"https://pagedout.institute/download/PagedOut_001_beta1.pdf\">https://pagedout.institute/download/PagedOut_001_beta1.pdf (no title)</a></li>\n  <li><a href=\"https://endler.dev/2024/the-dying-web/\">The Dying Web | Matthias Endler</a></li>\n  <li><a href=\"https://www.cdfinder.de/index.html\">NeoFinder – The Digital Asset Manager for macOS and iOS</a></li>\n  <li><a href=\"https://sourcefiles.app/\">Source Files for iPhone, iPad &amp; Mac</a></li>\n  <li><a href=\"https://freron.com/\">MailMate</a></li>\n  <li><a href=\"https://landsat.gsfc.nasa.gov/apps/YourNameInLandsat-main/index.html\">Landsat - NASA Science</a></li>\n  <li><a href=\"https://sunshowers.io/posts/beyond-ctrl-c-signals/\">Beyond Ctrl-C: The dark corners of Unix signal handling · sunshowers</a></li>\n  <li><a href=\"https://shop.tropicfeel.com/\">Tropicfeel | The Ultimate Travel Gear</a></li>\n  <li><a href=\"https://www.musictheory.net/exercises/fretboard\">Fretboard Note Identification</a></li>\n  <li><a href=\"https://www.amazon.com/Body-Keeps-Score-Healing-Trauma/dp/0143127748\">The Body Keeps the Score: Brain, Mind, and Body in the Healing of Trauma</a></li>\n  <li><a href=\"https://tonsky.me/sign-in/\">Sign In @ tonsky.me</a></li>\n  <li><a href=\"https://www.schiermonnikoog.nl/trouwambtenaren\">Trouwambtenaren | Gemeente Schiermonnikoog</a></li>\n  <li><a href=\"https://duckduckgo.com/?q=velux+dakraam+monteren&amp;t=iphone&amp;ia=web\">velux dakraam monteren at DuckDuckGo</a></li>\n  <li><a href=\"https://foxilicious.nl/harira-marokkaanse-tomatensoep/\">Harira: Marokkaanse tomatensoep met linzen en kikkererwten</a></li>\n  <li><a href=\"https://speakerdeck.com/pycon2016/sumana-harihareswara-http-can-do-that?slide=9\">Sumana Harihareswara - HTTP Can Do That?! - Speaker Deck</a></li>\n  <li><a href=\"https://pipe.pico.sh/\">pipe: authenticated pubsub over ssh</a></li>\n  <li><a href=\"https://hi-lo-art.com/links\">Links | Hi-Lo</a></li>\n  <li><a href=\"https://www.groene.nl/artikel/werk-is-politiek?utm_campaign=website&amp;utm_medium=owned_social&amp;utm_source=mastodon\">‘Werk is politiek’ –  De Groene Amsterdammer</a></li>\n  <li><a href=\"https://moviesjoy.kim/\">https://moviesjoy.kim/</a> – (exception when retrieving: HTTPSConnectionPool(host=’moviesjoy.kim’, port=443): Max retries exceeded with url: / (Caused by NameResolutionError(“HTTPSConnection(host=’moviesjoy.kim’, port=443): Failed to resolve ‘moviesjoy.kim’ ([Errno 8] nodename nor servname provided, or not known)”)))</li>\n  <li><a href=\"https://ztoz.blog/posts/postscript-code/\">PostScript® 1.0 - A Code Study | ℤ→ℤ</a></li>\n  <li><a href=\"https://www.mollywhite.net/micro/entry/202403091817\">POSSE</a></li>\n  <li><a href=\"https://theartofbeingaman.nl/#contact\">At Home - TheartofbeingaMAN</a></li>\n  <li><a href=\"https://european-alternatives.eu/category/search-engines\">European search engines | European Alternatives</a></li>\n  <li><a href=\"https://archive.org/details/TheInternetsOwnBoyTheStoryOfAaronSwartz\">The Internet’s Own Boy: The Story of Aaron Swartz : Brian Knappenberger : Free Download, Borrow, and Streaming : Internet Archive</a></li>\n  <li><a href=\"https://www.mijnpensioenoverzicht.nl/\">Mijn pensioenoverzicht</a></li>\n  <li><a href=\"https://www.nemokennislink.nl/publicaties/waarom-leerstijlen-niet-bestaan/\">Waarom leerstijlen niet bestaan - NEMO Kennislink</a></li>\n  <li><a href=\"https://klevgrand.com/products/speldosa\">Klevgrand - Dream Louder</a></li>\n  <li><a href=\"https://www.odoo.com/nl_NL/app/planning\">Odoo Planning - Gratis Software voor Planningbeheer</a></li>\n  <li><a href=\"https://daylightcomputer.com/\">Daylight | A More Caring Computer</a></li>\n  <li><a href=\"https://github.com/oils-for-unix/oils/wiki/How-Interactive-Shells-Work#1-processing-user-inputs\">How Interactive Shells Work · oils-for-unix/oils Wiki · GitHub</a></li>\n  <li><a href=\"https://bit-101.com/blog/posts/2024-12-28/journal/\">BIT-101 | Journal</a></li>\n  <li><a href=\"https://www.etsy.com/nl/listing/1322634709/handgemaakte-14k-edel-champagnegoud-voor?ga_order=most_relevant&amp;ga_search_type=all&amp;ga_view_type=gallery&amp;ga_search_query=trouwringen&amp;ref=sc_gallery-4-2&amp;frs=1&amp;cns=1&amp;sts=1&amp;search_preloaded_img=1&amp;plkey=7a0ef5e3202ca95ec326a37978c1df8f23023c95%3A1322634709&amp;variation1=2931233890&amp;variation0=2947772965\">Handgemaakte 14k edel champagnegoud voor hem en haar Mobius-trouwringset</a></li>\n  <li><a href=\"https://100r.co/site/small_space.html\">100R — small space</a></li>\n  <li><a href=\"https://muffinman.io/blog/the-tiny-book-of-great-joys/\">The Tiny Book of Great Joys · Muffin Man</a></li>\n  <li><a href=\"https://www.zanglesvoormannen.nl/\">Zangles voor Mannen | Zangles voor Mannen in Zwolle</a></li>\n  <li><a href=\"https://duckduckgo.com/?q=offerte+velux+dakkspel+groningen&amp;t=iphone&amp;ia=web\">offerte velux dakkspel groningen at DuckDuckGo</a></li>\n  <li><a href=\"https://it-notes.dragas.net/2025/02/26/fedimeteo-how-a-tiny-freebsd-vps-became-a-global-weather-service-for-thousands/\">FediMeteo: How a Tiny €4 FreeBSD VPS Became a Global Weather Service for Thousands - IT Notes</a></li>\n  <li><a href=\"https://www.bol.com/nl/nl/p/woodoro-led-leeslampje-bevordert-slaap-3-lichtstanden-oplaadbaar/9300000001817264/\">Led Leeslampje voor boek – 3 Standen oogvriendelijk licht – Dimbare oplaadbare… | bol</a></li>\n  <li><a href=\"https://localghost.dev/blog/this-page-is-under-construction/\">This page is under construction - localghost</a></li>\n  <li><a href=\"https://nl.m.wikipedia.org/wiki/Tjeerd_Bottema\">Tjeerd Bottema</a></li>\n  <li><a href=\"http://www.ecohovenier.nl/wp/artikelen/onkruid-bestrijden-op-verhardingen-2/\">Ecologisch onkruid bestrijden op verhardingen – ecohovenier.nl</a></li>\n  <li><a href=\"https://www.hornbach.nl/p/altrex-huishoudtrap-double-decker-5-treden/4100185/\">ALTREX Huishoudtrap Double Decker, 5 treden kopen! | HORNBACH</a></li>\n  <li><a href=\"https://retool.com/visual-basic\">Something Pretty Right: A History of Visual Basic | Retool</a></li>\n  <li><a href=\"https://alexwlchan.net/2025/bookmarks-static-site/?utm_source=mastodon\">Creating a static website for all my bookmarks – alexwlchan</a></li>\n  <li><a href=\"https://veltkampenstam.nl/woning/zalk-van-dijksweg-31/\">Zalk – van Dijksweg 31 | Veltkamp &amp; Stam Admono Makelaars</a></li>\n  <li><a href=\"https://speld.nl/2025/05/13/knmi-meet-langste-periode-ooit-dat-nederlanders-niet-zeiken-over-het-weer/\">KNMI meet langste periode ooit dat Nederlanders niet zeiken over het weer</a></li>\n  <li><a href=\"https://www.kleinanzeigen.de/s-anzeige/dell-6k-32-monitor-u3224kba/3084829569-225-3490\">PC &amp; Zubehör &amp; Software gebraucht kaufen in Prenzlauer Berg - Pankow | kleinanzeigen.de</a></li>\n  <li><a href=\"https://decorrespondent.nl/16100/de-zwakke-plek-van-de-verenigde-staten-vind-je-op-zee/ebd85e6c-351c-00c5-10c2-b4db9c79ee42\">De zwakke plek van de Verenigde Staten vind je op zee</a></li>\n  <li><a href=\"https://youmightnotneedjquery.com/\">You Might Not Need jQuery</a></li>\n  <li><a href=\"https://www.hoelangmoetkoken.nl/knolselderij/\">Knolselderij koken, bakken of grillen</a></li>\n  <li><a href=\"https://www.joanwestenberg.com/how-convenience-kills-curiosity/\">How Convenience Kills Curiosity</a></li>\n  <li><a href=\"https://direnv.net/\">direnv – unclutter your .profile | direnv</a></li>\n  <li><a href=\"https://efkobeton.nl/positieve-ervaringen-met-open-hiring-solliciteren-zonder-sollicitatieprocedure/\">Positieve ervaringen met open hiring: solliciteren zonder sollicitatieprocedure - EFKO Beton</a></li>\n  <li><a href=\"https://justfuckingusehtml.com/\">Just fucking use HTML</a></li>\n  <li><a href=\"https://guide.elm-lang.org/core_language.html\">Core Language · An Introduction to Elm</a></li>\n  <li><a href=\"https://straun.nl/\">Straun – Natuureducatie Schiermonnikoog</a></li>\n  <li><a href=\"https://www.netmeister.org/blog/http-123.html\">Bootstrapping HTTP/1.1, HTTP/2, and HTTP/3</a></li>\n  <li><a href=\"https://appdocumentary.com/2015/01/08/neven-mrgan-on-why-skeuomorphism-is-like-a-classic-car/\">Neven Mrgan on Why Skeuomorphism Is Like a Classic Car | App: The Human Story - Available Now</a></li>\n  <li><a href=\"https://m.soundcloud.com/thepowersharingseries/ps165-clip-sculley-atkinson-hypercard\">Stream episode PS165 - Clip - Sculley - Atkinson - Hypercard by The Powersharing Series podcast | Listen online for free on SoundCloud</a></li>\n  <li><a href=\"https://berthub.eu/articles/posts/dutch-postcode-and-building-database/\">Welcome to the BAG: The Dutch Building and Addresses database - Bert Hubert’s writings</a></li>\n  <li><a href=\"https://transeurotrail.org/netherlands/#5.84/52.131/5.659\">TET Netherlands - Trans Euro Trail - Europe’s Dirt Road Adventure</a></li>\n  <li><a href=\"https://crackedmirrorinshalott.wordpress.com/2013/04/12/autistic-allistic-neurodiverse-and-neurotypical-say-what/\">Autistic, Allistic, Neurodiverse, and Neurotypical: Say what? | Cracked Mirror in Shalott</a></li>\n  <li><a href=\"https://cablespaghetti.dev/hosting-a-fediverse-instance-on-an-original-raspberry-pi.html\">Hosting a Fediverse instance on an original Raspberry Pi</a></li>\n  <li><a href=\"https://crescentro.se/posts/writing-drivers/\">Writing a basic Linux device driver when you know nothing about Linux drivers or USB // crescentro.se</a></li>\n  <li><a href=\"https://hetdorpzalk.nl/?s=Zalkerbos&amp;doing_wp_cron=1751886056.3051149845123291015625\">Je zocht naar Zalkerbos - Het dorp Zalk</a></li>\n  <li><a href=\"https://www.crowdsupply.com/excamera/termdriver-2\">TermDriver 2 | Crowd Supply</a></li>\n  <li><a href=\"https://www.feynmanlectures.caltech.edu/\">Feynman Lecures on Physics</a></li>\n  <li><a href=\"https://www.homecomputermuseum.nl/en/collectie/aster/\">HomeComputerMuseum - Aster Computers</a></li>\n  <li><a href=\"https://borretti.me/article/notes-on-managing-adhd#strategies\">Notes on Managing ADHD</a></li>\n  <li><a href=\"https://isolatie.nijbegun.nl/over-de-isolatieaanpak/wat-is-een-isolatieplan/\">Nij Begun</a></li>\n  <li><a href=\"https://copy.sh/v86/\">v86</a></li>\n  <li><a href=\"https://descargasalsa.nl/handig/\">Handige bronnen – Dansschool Descarga Salsa</a></li>\n  <li><a href=\"https://aaadaaam.com/notes/useful-defaults/\">There’s no such thing as a CSS reset | Adam Stoddard</a></li>\n  <li><a href=\"https://web-platform-dx.github.io/web-features-explorer/\">Web platform features explorer - Home</a></li>\n  <li><a href=\"https://web-platform-dx.github.io/web-features-explorer/widely-available/\">Web platform features explorer - Widely available</a></li>\n  <li><a href=\"https://www.bostonmagazine.com/news/lost-at-sea/\">Shipwrecked: A Shocking Tale of Love, Loss, and Survival in the Deep Blue Sea</a></li>\n  <li><a href=\"https://nl.ifixit.com/Guide/How+to+Clean+your+iPhone's+Lightning+Port/164631\">How to Clean your iPhone’s Lightning Port - iFixit reparatiehandleiding</a></li>\n  <li><a href=\"https://www.klompen.frl/webshop/\">Online houten scherjon klompen kopen</a></li>\n  <li><a href=\"https://usetrmnl.com/\">TRMNL | E-ink dashboard to stay focused</a></li>\n  <li><a href=\"https://github.com/schlagmichdoch/pairdrop/blob/master/docs/faq.md\">PairDrop/docs/faq.md at master · schlagmichdoch/PairDrop · GitHub</a></li>\n  <li><a href=\"https://www.ursulakleguin.com/blog\">Ursula K. Le Guin — Blog (2010–2017)</a></li>\n  <li><a href=\"https://www.newmasculinity.org/\">Ibiza Retreat for Men | New Masculinity | Ibiza Men’s Group</a></li>\n  <li><a href=\"https://simoneskitchen.nl/socca-glutenvrije-kikkererwten-pannenkoek/\">Socca: glutenvrije kikkererwten pannenkoek - Simone’s Kitchen</a></li>\n  <li><a href=\"https://jpmens.net/2019/04/04/i-clone-all-repositories-i-ve-starred/\">Jan-Piet Mens :: I clone all GitHub repositories I’ve starred</a></li>\n  <li><a href=\"https://jacobian.org/2025/aug/8/two-scenario-threat-modeling/\">Two Scenario Threat Modeling - Jacob Kaplan-Moss</a></li>\n  <li><a href=\"https://www.emclient.com/\">eM Client | The Best Email Client for Windows and Mac</a></li>\n  <li><a href=\"https://overtype.dev/\">OverType - The Markdown Editor That’s a Textarea</a></li>\n  <li><a href=\"https://kiwix.org/en/\">Explore Offline Wikipedia and Educational Content with Kiwix- Kiwix</a></li>\n  <li><a href=\"https://djot.net/\">djot</a></li>\n  <li><a href=\"https://cpldcpu.com/2025/08/13/candle-flame-oscillations-as-a-clock/\">Candle Flame Oscillations as a Clock – Tim’s Blog</a></li>\n  <li><a href=\"https://www.noorderzon.nl/programma/hanggai\">Hanggai › Noorderzon</a></li>\n  <li><a href=\"https://www.linusakesson.net/programming/tty/\">The TTY demystified</a></li>\n  <li><a href=\"https://aeon.co/essays/why-might-the-big-bang-theory-be-in-crisis-very-soon?ref=readtangle.com\">Why might the Big Bang theory be in crisis very soon? | Aeon Essays</a></li>\n  <li><a href=\"https://m.soundcloud.com/sudhir-ibiza/mindful-magic-mushrum?in=sudhir-ibiza%2Fsets%2Fmedittions\">Listen to Mindful Magic Mushrum Mircordosing Meditation for Men 2.wav by Sudhir, Zorah &amp; friends in Meditations playlist online for free on SoundCloud</a></li>\n  <li><a href=\"https://www.macstories.net/stories/ios-and-ipados-26-the-macstories-review/\">iOS and iPadOS 26: The MacStories Review</a></li>\n  <li><a href=\"https://www.flickr.com/photos/mwichary/albums/72177720329068272/\">Houweling Telecomm Museum, Rotterdam | Flickr</a></li>\n  <li><a href=\"https://iamvishnu.com/posts/utf8-is-brilliant-design\">UTF-8 is a Brilliant Design — Vishnu’s Pages</a></li>\n  <li><a href=\"https://www.reflectionframe.com/\">Reflection Frame</a></li>\n  <li><a href=\"https://www.filfre.net/sitemap/\">» Table of Contents The Digital Antiquarian</a></li>\n  <li><a href=\"https://harpers.org/archive/1941/08/who-goes-nazi/\">Who Goes Nazi?, by Dorothy Thompson</a></li>\n  <li><a href=\"https://sunpig.com/martin/2025/09/28/roman-mars-on-the-curse-of-efficiency/\">Roman Mars on the curse of efficiency – Legends of the Sun Pig</a></li>\n  <li><a href=\"https://the-web-we-want.com/\">The web you want</a></li>\n  <li><a href=\"https://mysamasati.com/?mid=223\">mySamasati - Today’s Meditation</a></li>\n  <li><a href=\"https://pyfound.blogspot.com/2025/10/NSF-funding-statement.html?m=1\">Python Software Foundation News: The PSF has withdrawn a $1.5 million proposal to US government grant program</a></li>\n  <li><a href=\"https://github.com/non-ai-licenses/non-ai-licenses/blob/main/README.md\">non-ai-licenses/README.md at main · non-ai-licenses/non-ai-licenses · GitHub</a></li>\n  <li><a href=\"https://www.avrotros.nl/program/ondertussen-aan-de-hofvijver~75/\">Ondertussen aan de Hofvijver | AVROTROS</a></li>\n  <li><a href=\"https://en.wikipedia.org/wiki/Malicious_compliance\">Malicious compliance</a></li>\n  <li><a href=\"https://visualrambling.space/dithering-part-1/\">Dithering - Part 1</a></li>\n  <li><a href=\"https://aaadaaam.com/notes/pumped-up/\">Pumped up | Adam Stoddard</a></li>\n  <li><a href=\"https://createadaptablelife.com/2025/10/how-can-we-see-and-steer-through-our-life-crises-to-find-a-new-path.html\">How Can We See and Steer Through Our Life “Crises” to Find a New Path? - Create an Adaptable Life</a></li>\n  <li><a href=\"https://smallthingsconsidered.blog/\">Small Things Considered</a></li>\n  <li><a href=\"https://www.tbray.org/ongoing/When/202x/2025/11/01/Blog-Search-Pagefind\">ongoing by Tim Bray · Bye, Google Search</a></li>\n  <li><a href=\"https://www.asnbank.nl/betalen/nieuw-in-de-asn-app-en-asn-online-bankieren.html\">Nieuw in de ASN-app en ASN Online Bankieren - ASN Bank</a></li>\n  <li><a href=\"https://the-web-you-want.org/?css=vasilis2\">The web you want</a></li>\n  <li><a href=\"https://scripting.com/2022/07/19/152235.html?title=devNotesForMarkdownInRss\">https://scripting.com/2022/07/19/152235.html?title=devNotesForMarkdownInRss</a> – (exception when retrieving: (‘Connection aborted.’, ConnectionResetError(54, ‘Connection reset by peer’)))</li>\n  <li><a href=\"https://www.stayokay.com/nl/tips/overnachten-in-een-wikkelhouse\">Op vakantie midden in de natuur, overnacht in een Wikkelhouse bij Stayokay | Stayokay</a></li>\n  <li><a href=\"https://tickets.spotgroningen.nl/mtTicket/performance?id=108512&amp;lang=nl#seats\">Kies je stoelen | Kaartverkoop SPOT Groningen</a></li>\n  <li><a href=\"https://www.ah.nl/allerhande/recept/R-R1189426/pannenkoeken-basisrecept?as_templateId=23793&amp;msclkid=8bcf0be5da3519893b6462f062e3fbcd&amp;utm_source=bing&amp;utm_medium=cpc&amp;utm_campaign=NL%2FNL%20-%20AO%20-%20Search%20-%20API%20-%20NB%20-%20Gerechten%20-%20Top%20100&amp;utm_term=pannenkoeken&amp;utm_content=Gerecht%20-%C2%A0Pannenkoeken%20-%C2%A0HV\">Pannenkoeken basisrecept</a></li>\n  <li><a href=\"https://byte.tsundoku.io/#198610b-266\">Byte - a visual archive</a></li>\n  <li><a href=\"https://jimmyhmiller.com/overly-humble-programmer\">The Overly Humble Programmer</a></li>\n  <li><a href=\"https://www.webdesignmuseum.org/\">Web Design Museum - Discover old websites, apps and software</a></li>\n  <li><a href=\"https://stemvan.groningen.nl/toekomst-haddingestraatgarage/?utm_campaign=Nieuwsbrief+Centrum+&amp;utm_medium=email&amp;utm_source=Mailer#reageren\">Informatie - Toekomst Haddingestraatgarage</a></li>\n  <li><a href=\"https://www.nevel.org/\">Nevel Wild Ales bier</a></li>\n  <li><a href=\"https://nl.wikipedia.org/wiki/Zonnesteen_(kompas)\">Zonnesteen (kompas)</a></li>\n  <li><a href=\"https://eemstuin.nl/\">Biologisch-dynamische groente met smaak - Eemstuin</a></li>\n  <li><a href=\"https://eikemaheert.nl/\">Eikemaheert | Biologisch landbouwbedrijf in Loppersum dat natuur en cultuur verbindt</a></li>\n  <li><a href=\"https://filiph.github.io/unsure/\">Unsure Calculator</a></li>\n  <li><a href=\"https://hpsv-vissen.nl/Lusknopen.htm\">Lusknopen</a></li>\n  <li><a href=\"https://www.laptoprevive.nl/\">Laptop Revive – Sociaal, duurzaam en open</a></li>\n  <li><a href=\"https://simsalabimwebshop.com/alternatieve-herenkleding/\">Alternatieve herenkleding kopen? Simsalabim</a></li>\n  <li><a href=\"https://nkbv.nl/kenniscentrum/inbinden-bij-sportklimmen.html\">Inbinden bij sportklimmen | NKBV</a></li>\n  <li><a href=\"https://www.provinciegroningen.nl/actueel/nieuws/nieuwsartikel/herinrichtingsplannen-pol-gebied-zes-weken-ter-inzage/\">Herinrichtingsplannen POL-gebied zes weken ter inzage - Provincie Groningen</a></li>\n  <li><a href=\"https://app.tasteventura.nl/nl/quiz/intro/oNMd\">Taste Ventura</a></li>\n  <li><a href=\"https://www.theatlantic.com/technology/2025/12/people-outsourcing-their-thinking-ai/685093/?gift=iWa_iB9lkw4UuiWbIbrWGTqj_UFH3shy9qJgLM4CAq4&amp;utm_source=copy-link&amp;utm_medium=social&amp;utm_campaign=share\">The People Outsourcing Their Thinking to AI - The Atlantic</a></li>\n  <li><a href=\"https://www.danmcquillan.org/resisting_genai_highered_cjuu.html\">Resisting GenAI &amp; Big Tech in Higher Education</a></li>\n  <li><a href=\"https://floorp.app/\">Floorp Browser</a></li>\n  <li><a href=\"https://librewolf.net/\">LibreWolf</a></li>\n  <li><a href=\"https://gwij.nl/\">Deen Design Zandbak is under construction</a> – The Woodsmen</li>\n  <li><a href=\"https://marikebol.com/2023/04/26/glutenvrije-soezen/\">Glutenvrije soezen, basisrecept</a></li>\n  <li><a href=\"https://www.ninakalinina.com/\">Nina Kalinina’s home page</a></li>\n  <li><a href=\"https://nl.wikipedia.org/wiki/Escapisme\">Escapisme</a></li>\n  <li><a href=\"https://en.wikipedia.org/wiki/Ambient_1:_Music_for_Airports\">Ambient 1: Music for Airports</a></li>\n  <li><a href=\"https://berthub.eu/articles/posts/on-being-useful/\">On Being Useful - Bert Hubert’s writings</a></li>\n  <li><a href=\"https://en.wikipedia.org/wiki/HDMI#ARC\">HDMI</a></li>\n</ul>\n\n",
			"summary": "Below are the links on the internet-tab’s I found on my devices. I did a virtual cleanup of my browsers. I’ll keep the links for reference, but I doubt I’ll need them again. The list is obviously slightly redacted.",
			"date_published": "2026-01-06T00:00:00+01:00",
			"tags": []
		},
		 {
			"id": "/ict/2026/01/04/installing-bash-via-brew-on-macos",
			"url": "https://blog.zanstra.com/ict/2026/01/04/installing-bash-via-brew-on-macos.html",
			"title": "Installing Bash via Brew on MacOS",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>MacOS comes with an ancient version of bash, due to licensing issues. You can install a modern version of bash with brew, but I broke my shell after updating bash.</p>\n\n<p>What happened? I changed my default shell via de GUI (<code class=\"language-plaintext highlighter-rouge\">System Settings &gt; Users &amp; Groups &gt; Advanced options... (ctrl+option-click on your own name or avatar) &gt; Login shell &gt; Choose</code>).</p>\n\n<p>If you do that, the path to specific version of bash will be stored. It even does that if you choose the path <code class=\"language-plaintext highlighter-rouge\">/opt/homebrew/bin/bash</code> (which is a symbolic link to the latest version). So when you update, and start a new shell, you get the old version of the shell. And when brew cleaned it’s cellar, the old version would have been deleted. This is when I noticed my shell was broken.</p>\n\n<p>You can solve this problem in two ways. What I did: I added the path to the symbolic link to the approved list of shells. First I ran <code class=\"language-plaintext highlighter-rouge\">sudo mate /etc/shells</code> (I still use <code class=\"language-plaintext highlighter-rouge\">mate</code>, but you can use your favorite editor). After that I added the line <code class=\"language-plaintext highlighter-rouge\">/opt/homebrew/bin/bash</code> to the end of the file. And finally I saved it.</p>\n\n<p>The new entry didn’t show in System Settings, so I restarted it. After that, it showed up, so I could choose the symbolic linked version of bash from homebrew.</p>\n\n<p>Another way might be <code class=\"language-plaintext highlighter-rouge\">sudo chsh -s /opt/homebrew/bin/bash</code>, but I didn’t try this one.</p>\n",
			"summary": "MacOS comes with an ancient version of bash, due to licensing issues. You can install a modern version of bash with brew, but I broke my shell after updating bash.",
			"date_published": "2026-01-04T00:00:00+01:00",
			"tags": []
		},
		 {
			"id": "/ict/2025/05/26/hosting-my-pocket-archive",
			"url": "https://blog.zanstra.com/ict/2025/05/26/hosting-my-pocket-archive.html",
			"title": "Hosting My Pocket Archive",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>I published <a href=\"/archive/pocket/\">the export of my Pocket archive</a> on this site. Below a short description how I made this with Jekyll (this site is generated by it). The basic gist of this post is to show how publishing this is (and also CSV is awesome).</p>\n\n<p>The <a href=\"https://getpocket.com/export.php?\">export of Pocket</a> is in CSV format, which Jekyll handles well. Below an example:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>title,url,time_added,tags,status\nhttp://www.linfo.org/unix_philosophy.html,http://www.linfo.org/unix_philosophy.html,1484426802,,archive\nField Of Dreams,http://www.imdb.com/title/tt0097351/,1424351431,,archive\nhttps://en.wikipedia.org/wiki/Community_of_practice,https://en.wikipedia.org/wiki/Community_of_practice,1450194942,,unread\n</code></pre></div></div>\n\n<p>The first row contains column names, which can be used in the Jekyll-template:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>\n{% assign pocket_export = site.data.pocket_export | sort:'time_added' | reverse %}\n&lt;ul&gt;\n{% for row in pocket_export %}\n    &lt;li&gt;\n        &lt;a href=\"{{row.url}}\"&gt;{{row.title}}&lt;/a&gt; \n        saved on &lt;time data-timestamp=\"{{row.time_added}}\"&gt;calculating...&lt;/time&gt;\n        {% if row.tags %}&lt;span class=\"tags\"&gt;[{{row.tags}}]&lt;/span&gt;{% endif %}\n    &lt;/li&gt;\n{% endfor %}\n&lt;/ul&gt;\n\n</code></pre></div></div>\n\n<p>The template is straight forward. It could be much more fancy, but I’m probably not using it much after this.\nThere was one snag though. The format of the <code class=\"language-plaintext highlighter-rouge\">time_added</code> in the export in seconds after the start of 1970.\nI’ve used JavaScript for this to format the date.</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>&lt;script&gt;\n    document.addEventListener('DOMContentLoaded', function() {\n        let date = new Date();\n        for(let ts of document.querySelectorAll('[data-timestamp]')) {\n            date.setTime(ts.dataset.timestamp * 1000);\n            ts.setAttribute('datetime', date.toISOString());\n            ts.innerText = date.toLocaleString();\n        }\n    });\n&lt;/script&gt;\n</code></pre></div></div>\n\n<p>I could have pre-processed the export to include an ISO-8601 formatted date. That would give me the option to group the items per month, but for now I’m done (I mean, this topic is a sinkhole. I could stylize the tags. I could add filter options. Heck, I could even rebuild Pocket).</p>\n\n<p>Sometimes it’s better to ship.</p>\n",
			"summary": "I published the export of my Pocket archive on this site. Below a short description how I made this with Jekyll (this site is generated by it). The basic gist of this post is to show how publishing this is (and also CSV is awesome).",
			"date_published": "2025-05-26T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/ict/2024/08/01/transfer-git-changes-with-a-pasteboard-workflow",
			"url": "https://blog.zanstra.com/ict/2024/08/01/transfer-git-changes-with-a-pasteboard-workflow.html",
			"title": "Transfer git Changes With a Pasteboard-Workflow",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Sometimes you want to transfer changes you made to a source tree from one computer to the other. The source tree on both computers are managed by <em>git</em>. One reason to do this, is because you tried something on your laptop. But you changed your mind, and want to work on a desktop computer with a big screen and real keyboard. And you don’t want to re-type everything.</p>\n\n<p>Another reason might be, because you debugged a problem on a production machine (I never do this, of course). The production machine only has readonly access to git. You want to secure these changes exactly, because they work.</p>\n\n<p>The commands are pretty simple, but I keep forgetting them. The commands use the diff format. If possible, I try not to create temporary files, because often they leave a mess. If the <strong>source machine</strong> is on MacOS, I use this command to secure patch:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ git --no-pager diff | pbcopy\n</code></pre></div></div>\n\n<p>When on <em>Linux</em> via <em>ssh</em> in a <em>Terminal.app</em> window, I copy by hand:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ git --no-pager diff\n# Press cmd+A to select output from last command, and press cmd+C to copy\n</code></pre></div></div>\n\n<p>With help of the universal clipboard, I can paste this on the <strong>target machine</strong>. But before I do that, I first check the status of the target repository:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code># Checking if the index is clean, and we are on the right branch\n$ git status\n# Now we can apply the patch\n$ pbpaste | git apply -\n</code></pre></div></div>\n\n<p>Now I can review and commit the changes, and push the changes to our origin. Normally I would use GitHub Desktop for this, because I like working with GUI’s for this kind of work.</p>\n\n<p>After pushing, we have to cleanup things on the <strong>source machine</strong>. With older git-versions, you didn’t need to reset the working folder. But since I don’t know when this is necessary:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code># Undo all the changes we secured in previous steps\n$ git reset --hard\n# Now get the changes \n$ git pull\n</code></pre></div></div>\n\n<p>Finally you might want to restart any processes that need restarting.</p>\n\n<p>You can of course use temporary files. <a href=\"https://appleinsider.com/inside/macos/tips/how-to-fix-universal-clipboard-problems-on-macos\">Files</a> <a href=\"https://discussions.apple.com/thread/7913125\">are</a> <a href=\"https://beebom.com/fix-universal-clipboard-not-working-iphone-mac/\">more</a> <a href=\"https://www.idownloadblog.com/2022/06/30/how-to-fix-universal-clipboard-not-working-on-iphone-ipad-mac/\">reliable</a> <a href=\"https://www.igeeksblog.com/universal-clipboard-not-working-between-mac-and-iphone/\">anyways</a>.</p>\n\n",
			"summary": "Sometimes you want to transfer changes you made to a source tree from one computer to the other. The source tree on both computers are managed by git. One reason to do this, is because you tried something on your laptop. But you changed your mind, and want to work on a desktop computer with a big screen and real keyboard. And you don’t want to re-type everything.",
			"date_published": "2024-08-01T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/ict/2024/05/14/google-oauth-invalid-grant-nightmare-and-how-i-fixed-it",
			"url": "https://blog.zanstra.com/ict/2024/05/14/google-oauth-invalid-grant-nightmare-and-how-i-fixed-it.html",
			"title": "Google OAuth \"Invalid-Grant\" Nightmare — and How I Fixed It",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>I’m maintaining a couple of web applications, which use <em>login-with-google</em> for authentication. It also uses <em>OAuth</em> to access some of Google API’s. They are private applications, with less than 100 users.</p>\n\n<p>A while ago, one user reported an error with an application. Said user could log in, but when accessing a Google API, it would return the error “invalid-grant”.</p>\n\n<p>I tried a lot of things, but <a href=\"https://blog.timekit.io/google-oauth-invalid-grant-nightmare-and-how-to-fix-it-9f4efaf1da35\">this blog article</a> was most helpful (see also this <a href=\"https://stackoverflow.com/a/38433986/56\">Stack Overflow</a> entry). But solving the problem was not possible at the time. Nothing from the article worked. The user didn’t need to call the Google API since there was a workaround, so I left it at that.</p>\n\n<p>Months later, one direct collegue reported the same thing. Again, nothing I could think of or found on the internet seemed to work. This was more of a problem, since the collegue actually needed the functionality. And also, this not understood behaviour of OAuth was a ticking time bomb. And then, without an plain reason, I had an epiphany. Why not generate new credentials within the same project?</p>\n\n<p>It was worth a try. So I generated a <a href=\"https://support.google.com/cloud/answer/6158849?hl=en-GB\">new “OAuth 2.0 Client IDs” entry</a> within the Google Cloud console. In the database, I emptied the oauth-session table. I made a backup, so I could revert the change if necessary. I also emptied the web application’s session table in the database. Via this way, all users are forced to re-authenticate. The web session table doesn’t have important data in it, so in my case this was possible.</p>\n\n<p>And the fix worked well. Users did need to re-authenticate, but they didn’t need to grant permissions to use the app. I guess permissions are stored on project level, so this was not neccssary. It still is a bit unsatisfactory the underlying problem isn’t known, but I’m pretty happy the error is gone now.</p>\n\n",
			"summary": "I’m maintaining a couple of web applications, which use login-with-google for authentication. It also uses OAuth to access some of Google API’s. They are private applications, with less than 100 users.",
			"date_published": "2024-05-14T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/ict/2024/04/18/stylize-text-files-on-the-terminal-with-awkcss",
			"url": "https://blog.zanstra.com/ict/2024/04/18/stylize-text-files-on-the-terminal-with-awkcss.html",
			"title": "Stylize Text Files on the Terminal With awkcss",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Since I’ve been introduced to UNIX, I’ve been intrigued by the <em>awk</em> program. Most of the time when I use it, it’s to extract a value from a text file. Yet it can do so much more than that: it’s a full blown programming language. It’s a text processing language, which runs one or more pattern/action combinations:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>pattern { action }\n...\n</code></pre></div></div>\n\n<p>When the pattern matches a line, the action is executed. But at a certain moment I realized the pattern/action-combo looked a lot like CSS. In CSS you have rules, which are made up by selectors and property assignments. <em>awk</em> also calls them rules. Selectors and properties. Patterns and actions. A light bulb 💡 moment: <strong>awkcss</strong> was born.</p>\n\n<p>The pattern in awk can be seen as an expression which selects a line or not. In the actions part we can use function calls as property setters. So, what properties would be easy to set? I would like to show the capabilities of awkcss by providing some examples.</p>\n\n<p>Take for example this <code class=\"language-plaintext highlighter-rouge\">zebra.awkcss</code> stylesheet:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>NR % 2 == 1 { \n    color(black);\n    background_color(gray);\n}\nNR % 2 == 0 { \n    color(white);\n    background_color(black);\n}\n</code></pre></div></div>\n\n<p>To show the file <code class=\"language-plaintext highlighter-rouge\">README.md</code> with zebra stripes, run the following command:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> $ awkcss -f zebra.awkcss README.md\n</code></pre></div></div>\n\n<p>The first pattern/action combo (“rule”) selects odd lines (in awk, the <code class=\"language-plaintext highlighter-rouge\">NR</code> variable contains the 1-based line number). For properties I’ve created the <code class=\"language-plaintext highlighter-rouge\">color</code>- and <code class=\"language-plaintext highlighter-rouge\">background_color</code>-functions. As property values I’ve used variables, although you can also use strings.</p>\n\n<p>About the naming convention: CSS claimed the <em>dash-separator</em>, and JavaScript is doing <em>camelCasing</em>. To differentiate awkcss from the CSS and JavaScript, I was forced to use <em>snake_casing</em> as a naming convention. Having the best readable CSS-dialect is considered a bonus.</p>\n\n<p>To create a simple MarkDown stylizer, checkout this <code class=\"language-plaintext highlighter-rouge\">markdown.awkcss</code> stylesheet:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>BEGIN {\n    width(72);\n}\n/^#+ / {\n    color(yellow);\n    font_weight(bold);\n}\n/^\\t| {4,}/ {\n    color(\"gray\");\n}\n</code></pre></div></div>\n\n<p>The <code class=\"language-plaintext highlighter-rouge\">BEGIN</code>-rule sets the default width. It’s like the CSS <code class=\"language-plaintext highlighter-rouge\">*</code>-selector. The second rule matches any lines starting with a <code class=\"language-plaintext highlighter-rouge\">#</code>-sign to match MarkDown headings. And the third rule matches code-blocks, starting with a tab character, or 4 spaces.</p>\n\n<p>Suppose you don’t want to render code-blocks, you can use the <code class=\"language-plaintext highlighter-rouge\">display</code>-property with the <code class=\"language-plaintext highlighter-rouge\">none</code>-variable:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>/^\\t| {4,}/ {\n    display(none); \n}\n</code></pre></div></div>\n\n<p>There is also a box-model:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>/^&gt;/ {\n    block_name(\"block_quote\");\n    border(ascii);\n    border_style_left(none);\n}\n</code></pre></div></div>\n\n<p>This rule matches block quotes, and draws a border around it. Since a block quote already starts with a border-like character, the left border is set to <code class=\"language-plaintext highlighter-rouge\">none</code>.</p>\n\n<p>An issue with awk is that a pattern selects one line only. For inline-properties like <code class=\"language-plaintext highlighter-rouge\">color</code> this is not a problem. But we don’t want to draw a border around every line. We need a mechanism to draw a border around the whole block quote instead. For this the <code class=\"language-plaintext highlighter-rouge\">block_name</code> property can be used. All consecutive lines with the same block name are considered to be part of the same block.</p>\n\n<p>There is also support for media-queries:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>/^\\t| {4,}/ {\n    color(\"gray\");\n}\n/^\\t| {4,}/ &amp;&amp; LINES &lt;= 25 {\n    white_space(pre);\n    text_overflow(ellipsis);\n}\n</code></pre></div></div>\n\n<p>This stylesheet renders MarkDown-code gray. For terminals with limited height, lines are not wrapped (default <code class=\"language-plaintext highlighter-rouge\">white_space</code> is <code class=\"language-plaintext highlighter-rouge\">pre_wrap</code>). And when the line overflows, it is truncated and an ellipsis (…) is shown at the end. Besides the <em>number of lines</em>, there are also variables for <em>number of columns</em> and <em>colors</em>.</p>\n\n<p>Finally, you can use some pseudo-elements as well:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>/^#+ / {\n    color(bright_blue);\n}\n/^#+ / &amp;&amp; select(\"::after\") {\n    content(\"------------------------------------------------\");\n    select();\n}\n</code></pre></div></div>\n\n<p>This will display a line under every heading. Because of the way <code class=\"language-plaintext highlighter-rouge\">awk</code> works, the selector will need to be reset at the end of the rule by calling <code class=\"language-plaintext highlighter-rouge\">select</code> with no arguments. The inserted pseudo-element is block level, meaning it will insert a line below the heading. Of course, the <code class=\"language-plaintext highlighter-rouge\">::before</code> pseudo-element is also supported.</p>\n\n<p>Although I used MarkDown in this blog as an example, the use cases don’t stop there. It can be used to better inspect <code class=\"language-plaintext highlighter-rouge\">/etc/passwd</code> by hiding system accounts, printing comments gray and high lighting accounts.</p>\n\n<p>And I didn’t even touch everything. Check out the properties <code class=\"language-plaintext highlighter-rouge\">margin</code> (including margin-collapse), <code class=\"language-plaintext highlighter-rouge\">tab_size</code>, <code class=\"language-plaintext highlighter-rouge\">text_decoration</code> (both <code class=\"language-plaintext highlighter-rouge\">underline</code> and/or <code class=\"language-plaintext highlighter-rouge\">blink</code>) and <code class=\"language-plaintext highlighter-rouge\">width</code>: they are all documented in the <a href=\"https://github.com/doekman/awkcss/blob/main/reference.md\">AWKCSS Reference</a>.</p>\n\n<p>Getting started: Check out the <a href=\"https://github.com/doekman/awkcss/tree/main\">repository on GitHub</a>; the <code class=\"language-plaintext highlighter-rouge\">README.md</code> contains all information to get you started using awkcss!</p>\n\n<h2 id=\"how-does-this-all-work\">How Does This All Work</h2>\n\n<p>If you want to know exactly how <code class=\"language-plaintext highlighter-rouge\">awk</code> is called, just add the <code class=\"language-plaintext highlighter-rouge\">--debug</code> argument, and the command that would have been ran is displayed (<code class=\"language-plaintext highlighter-rouge\">§</code> is the repository-home):</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> $ awkcss --debug -f zebra.awkcss README.md\n awk -v COLS=180 -v LINES=50 -v COLORS=256 -f §/library.awk -f §/awkcss/defaults.awkcss \n     -f §/awkcss/examples/zebra.awkcss -f §/awkcss/render.awk README.md\n</code></pre></div></div>\n\n<p>The <a href=\"https://github.com/doekman/awkcss/blob/main/reference.md#awk-specific-information\">reference</a> also contains some technical information about this <code class=\"language-plaintext highlighter-rouge\">awk</code>-implementation. Finally, if you are interested in the source code, I suggest to check out the <a href=\"https://github.com/doekman/awkcss/tree/version-0.1\">version 0.1-branch</a> first. This contains the initial implementation without buffering and other more advanced stuff.</p>\n\n<p>There is still a lot left to be desired. There are bugs, more test coverage will be very helpful. But the biggest missing feature is table-support. After all, working with columns/cells (like <code class=\"language-plaintext highlighter-rouge\">$3</code>) is awk’s biggest feature. However, to implement support for that, the render engine needs to be re-architected. We’ll see what the future holds.</p>\n\n",
			"summary": "Since I’ve been introduced to UNIX, I’ve been intrigued by the awk program. Most of the time when I use it, it’s to extract a value from a text file. Yet it can do so much more than that: it’s a full blown programming language. It’s a text processing language, which runs one or more pattern/action combinations:",
			"date_published": "2024-04-18T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/ict/2023/05/03/integer-exposed-rewrite",
			"url": "https://blog.zanstra.com/ict/2023/05/03/integer-exposed-rewrite.html",
			"title": "Integer Exposed Rewrite",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>For a company-internal web application I’ve used <a href=\"https://github.com/stefanhaustein/notemplate\">NoTemplate.js</a> (disclosure: I’m a contributor to the project). It’s a minimal library for dynamically generating DOM nodes. And really like it. It hardly introduces abstractions. It wraps the W3C DOM API with a simple interface. And with “simple” I mean more JavaScriptesque, if that’s a word.</p>\n\n<p>The biggest reason to use <em>NoTemplate</em> was its simplicity. I probably won’t work much on this internal application, but when I do I don’t want to spend a lot of time getting up to speed. I won’t be working on it within 6 months, but it also can be 2 years. So I was happily surprised I’m not alone in wanting to keep things simple. See for example this piece about <a href=\"https://jvns.ca/blog/2023/02/16/writing-javascript-without-a-build-system/\" title=\"Writing Javascript without a build system, by Julia Evans\">Writing Javascript without a build system</a>. So when Julia Evans wrote about her <a href=\"https://jvns.ca/blog/2023/04/19/new-playground-integer-exposed/\" title=\"New playground: integer.exposed, by Julia Evans\">new playground</a>, I got the idea to rewrite this playground with NoTemplate.</p>\n\n<p>With this blog, I try to show the process of this rewrite. Not sure if this is interesting. I do get technical, but I try only to mention the interesting and relevant bits. Well, let’s get on with it.</p>\n\n<h1 id=\"the-rewrite\">The Rewrite</h1>\n\n<p>In this part I’m going to explain how I approached the rewrite. The code is <a href=\"https://github.com/doekman/integer.exposed\">published on GitHub</a>, and every step is a commit in the git repository. I primary written this section as a way to prevent myself from writing the code, and end up not blogging about it. So if you don’t think this is interesting, you can skip to the next section. And when I say “rewrite”, I probably should say “rewire”. I reused most of the code from <code class=\"language-plaintext highlighter-rouge\">script.js</code> and only made minor changes.</p>\n\n<p>First step in the rewrite is to copy the source files and use these as a base for the rewrite. If you want, you can follow these steps on GitHub as I’ve created a commit for every step <code class=\"language-plaintext highlighter-rouge\">(commit 11c34fa Initial)</code>.</p>\n\n<p>The first step is to replace the Vue library include with the NoTemplate one. I also emptied the script.js file. Now I look at the Vue template. It’s the HTML contained by the <code class=\"language-plaintext highlighter-rouge\">&lt;div id=\"app\"&gt;</code> tag/element. With NoTemplate, we write the view-template with JavaScript. So let’s first translate the HTML without the behavior (events).</p>\n\n<p>To keep things simple at this point, the UI will display 16 bit by default. Also, I only show the 16-bit pattern for now. And as I mentioned, the app is not interactive. The <code class=\"language-plaintext highlighter-rouge\">form</code>-method in <code class=\"language-plaintext highlighter-rouge\">script.js</code> generates the HTML view <code class=\"language-plaintext highlighter-rouge\">(commit 9551612 Static version)</code>.</p>\n\n<p>We want to render the view from values. For that, I introduced the <code class=\"language-plaintext highlighter-rouge\">state</code> property, and some  property getters. One thing to note is how the <em>bit pattern</em>-section is generated. With NoTemplate, everything is an expression. So first an array is created for every byte. Then this array is mapped to <code class=\"language-plaintext highlighter-rouge\">span</code>-elements.</p>\n\n<p>This version of the code doesn’t respond to dynamic input. But when you change the state-properties in an editor, the form reflects these new values. I tested some different values, and compared them to the output of the original version. Later I realized there are also <code class=\"language-plaintext highlighter-rouge\">span</code>-elements with the <code class=\"language-plaintext highlighter-rouge\">doublebyte</code>-class. I haven’t actually observed any different rendering without this class usage. So I skipped implementing them <code class=\"language-plaintext highlighter-rouge\">(commit d12e82f Dynamic state)</code>.</p>\n\n<p>And now the step to make it all interesting. Attaching event handlers, and all that is necessary. First I added the property setters and operator methods (not-, shift left-operator etcetera). When working on the event handlers for the text-inputs, I discovered an important disadvantage of using NoTemplate. Later in this blog I will elaborate on this. So with all the click, keyup and blur handlers in place, the rewrite is almost complete <code class=\"language-plaintext highlighter-rouge\">(commit 6d8b93f Event handlers)</code>.</p>\n\n<p>Until so far I didn’t notice the original script used an indent of two spaces, while I was using four. Since I copied most code, I now have mixed code. Let’s fix that now <code class=\"language-plaintext highlighter-rouge\">(commit fee5e6c Fix leading whitespace)</code>.</p>\n\n<p>The last part to write is to save the form state into the hash part of the URL. I had a little trouble understanding Vue, and how it handles changes of the hash. My initial code seemed to call the render-method twice. So I added the <code class=\"language-plaintext highlighter-rouge\">settingHash</code> property to prevent this. And I’ve used the same code to set the state from the hash before the initial render <code class=\"language-plaintext highlighter-rouge\">(commit b7df8f1 Handling hash)</code>.</p>\n\n<p>While playing with the playground I missed a button to reset the display to zero. The playground resembles a calculator, so that must have been why I wanted to add it <code class=\"language-plaintext highlighter-rouge\">(commit 49a20c5 Clear operator)</code>.</p>\n\n<p><a href=\"https://blog.zanstra.com/integer.exposed/\">▶️</a> You can <a href=\"https://blog.zanstra.com/integer.exposed/\">play with the rewrite here</a>.</p>\n\n<p>About tooling, and especially the simple kind: I’ve used <a href=\"https://blog.iconfactory.com/2022/06/worldwideweb-part-2/\">WorldWideWeb</a> for this project. It’s a simple, personal web server for macOS. It’s like <code class=\"language-plaintext highlighter-rouge\">python3 -m http.server</code>, but it has a GUI and it adds niceness. For example, it automatically reloads the webpage if one of the source files have changed.</p>\n\n<h1 id=\"comparison\">Comparison</h1>\n\n<p>How do the two implementations compare? For me, two things stand out. First, with Vue the view is rendered before JavaScript has ran because it’s HTML. With NoTemplate you can get a render flash, because JavaScript has to run before the form is displayed. But if you watch close, you might see a glimpse of several <code class=\"language-plaintext highlighter-rouge\">{{ bit }}</code> items at the bit pattern part in the Vue-app. You can see this better when you turn off JavaScript. With NoTemplate you can overcome this by rendering the form at compile time with some kind of build-system. But yeah, you’ll need a build system then…</p>\n\n<p>But there is one problem with NoTemplate I don’t directly have a solution for. Let me explain. When a value in the application changes, NoTemplate renders the complete form and replaces the original form with the new one. While this might sound wasteful, in practice with small applications this is not a problem. Until I realized after a blur-event, the focus on the input element was lost (for example changing the signed value of the integer and pressing enter). And that is pretty annoying when you are using the keyboard.</p>\n\n<p>My first impulse is to solve this problem. I’m a programmer after all. But this is beyond the scope of this project. However, I will explore three solution directions. The first is to store what element has the focus into the application state. Now the focus can be set <em>after rendering</em> with a calls to <code class=\"language-plaintext highlighter-rouge\">setfocus()</code>. Or <em>during rendering</em> with the  <code class=\"language-plaintext highlighter-rouge\">autofocus</code>-attribute on the appropriate element.</p>\n\n<p>We can determine the current focussed element by calling <code class=\"language-plaintext highlighter-rouge\">document.activeElement</code>. But how do you serialize this into some string? Some kind of DOM-index would be problematic. In a templating system, the number of nodes can differ from render to render. When the element has an <code class=\"language-plaintext highlighter-rouge\">id</code>-attribute, this is not a problem. But this is not always the case. This could be solved by the NoTemplate library by generating <code class=\"language-plaintext highlighter-rouge\">id</code>-values for the DOM elements that can have the focus.</p>\n\n<p>Another problem to tackle is how to determine which element has the focus in all situations. When pressing tab from an input-element, the active element in the blur-event is <code class=\"language-plaintext highlighter-rouge\">document.body</code>. The same is true for the change-event. Not 100% sure why this is. It might be set at a later stage in the event pipeline.</p>\n\n<p>The second direction is not to replace the DOM-node, but to apply changes. Make some DOM diff thing. Not sure how this exactly would work, because event-handlers change too (there’s probably an NPM module for this). Not too fond of this idea.</p>\n\n<p>I guess the best direction would be just to encode the form in plain HTML, like it was 2002. Only the bit-pattern part is dynamic after all. This part could be generated by NoTemplate.js. And perhaps for attaching the events, I could create something like <a href=\"http://dean.edwards.name/jsb/\" title=\"JavaScript Bindings\">Dean Edwards JSB</a>. I always liked that idea too.</p>\n\n<h1 id=\"conclusion\">Conclusion</h1>\n\n<p>It has been a fun project. The outcome is not completely what I wanted, but I learned a lot. The goal was not to create a working app, but to explore after all.</p>\n\n<p>I discovered that writing the blog while doing the project has a lot of advantages. It keeps me focussed, and it let me do a bit of work while being on the train. I hope this blog makes sense, and like to hear your thoughts on this topic.</p>\n\n<h1 id=\"update\">Update</h1>\n\n<p>Via <a href=\"https://mastodon.nl/@dukoid@mastodon.social/110329152286258188\">Mastodon</a> I got the tip to defer the blur-event handler, to find out the actual active element. I directly used <code class=\"language-plaintext highlighter-rouge\">setTimeout</code>, and I got the result I wanted: the <code class=\"language-plaintext highlighter-rouge\">activeElement</code> was the element I tabbed to.</p>\n\n<p>But since it’s not 2002 anymore, and <code class=\"language-plaintext highlighter-rouge\">setTimeout</code> feels like a hack I looked into promises. So I replaced <code class=\"language-plaintext highlighter-rouge\">setTimeout</code> with <code class=\"language-plaintext highlighter-rouge\">Promise.resolve(null).then</code>. But this didn’t work. The activeElement was document.body again…. What’s going on?</p>\n\n<p>After reading about <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop\">the event loop</a> on MDN, I read about using promises, and found <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#task_queues_vs._microtasks\">this quote</a>:</p>\n\n<blockquote>\n  <p>Promise callbacks are handled as a microtask whereas setTimeout() callbacks are handled as task queues.</p>\n</blockquote>\n\n<p>Long story short: defering code with <code class=\"language-plaintext highlighter-rouge\">setTimeout</code> works because it’s a task, and not a <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide\">microtask</a>. My understanding is that a microtask would be ran right after the event-handler, while a task would be scheduled in the next iteration of the event loop. Apparently after the blur-handler, there is a task or microtask setting the activeElement. But now I know setTimeout is not as much as a hack I thought it was. It’s just wat I needed.</p>\n\n<p>So now we can make the render-method preserve the activeElement. To do so, I first gave every element that can be active an <code class=\"language-plaintext highlighter-rouge\">id</code>-attribute. And after that, I altered the render-method to remember and set the active element around the render. The render is wrapped within a setTimeout, so it is executed as a seperate task. I also used the optional chaining operator (<code class=\"language-plaintext highlighter-rouge\">?.</code>), because the activeElement can be <code class=\"language-plaintext highlighter-rouge\">null</code> and focusable elements may be disappear between renders.</p>\n\n<p>Also, I changed the <code class=\"language-plaintext highlighter-rouge\">blur</code>-events to <code class=\"language-plaintext highlighter-rouge\">change</code>, because we don’t need a render when a text-value hasn’t been changed. Also, I wanted to make all the controls accessible by the keyboard. So I changed the <code class=\"language-plaintext highlighter-rouge\">span</code>-items for the bit selector and the bit pattern to <code class=\"language-plaintext highlighter-rouge\">button</code> elements, and gave them an <code class=\"language-plaintext highlighter-rouge\">id</code>- and <code class=\"language-plaintext highlighter-rouge\">tabindex</code>-attribute. To fix their appearance, I only needed to set a couple of CSS properties. As value I used <code class=\"language-plaintext highlighter-rouge\">unset</code>, and it works like a charm. Now we can tab through all controls, and use the spacebar (or enter) to select or toggle them <code class=\"language-plaintext highlighter-rouge\">(commit e98e782 Retaining active element)</code>.</p>\n\n<p>On thing that still bugged me a bit were the bit buttons (pun not intended, but still enjoyed). In the user interface, they differ from the other controls (tab-bar, buttons, text inputs). On macOS, when pressing tab links are skipped. Only “proper” controls get the focus. If you want the next link to get the focus, one can press option+tab (also known as alt+tab). So <code class=\"language-plaintext highlighter-rouge\">a</code>-elements for the bits seemed a better fit.</p>\n\n<p>But how should this work? A click handler would not suffice, I would need a keyboard-handler as well. And also <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#onclick_events\">MDN suggested to use buttons instead</a>. But then I realized, I just can use a hash-link, like <code class=\"language-plaintext highlighter-rouge\">&lt;a href=\"#0x01\"…</code>. When I supply the right value for the right bit, no additional scripting is needed. The <code class=\"language-plaintext highlighter-rouge\">hashchange</code>-handler will handle it all. And tabbing (or option-tabbing) to the elements, and pressing enter toggles the bit <code class=\"language-plaintext highlighter-rouge\">(commit xxx A@href bits)</code>.</p>\n\n<p>That’s it. With this addition I learned even more about JavaScript and the microtasks. Also, the playground is now a bit more accessible, and that’s nice.</p>\n\n",
			"summary": "For a company-internal web application I’ve used NoTemplate.js (disclosure: I’m a contributor to the project). It’s a minimal library for dynamically generating DOM nodes. And really like it. It hardly introduces abstractions. It wraps the W3C DOM API with a simple interface. And with “simple” I mean more JavaScriptesque, if that’s a word.",
			"date_published": "2023-05-03T00:00:00+02:00",
			"date_modified": "2023-05-13T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/ict/2021/09/08/wat-heeft-archipunt-te-maken-met-de-ingenuity-mars-helicopter-",
			"url": "https://blog.zanstra.com/ict/2021/09/08/wat-heeft-archipunt-te-maken-met-de-ingenuity-mars-helicopter.html",
			"title": "Wat heeft Archipunt te maken met de Ingenuity Mars Helicopter?",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Op 30 april 2021 keek ik op <a href=\"https://github.com/doekman/\">m’n GitHub pagina</a>, en zag ik dat ik de volgende “achievement” erbij had gekregen:</p>\n\n<blockquote>\n  <p><strong>Mars 2020 Helicopter Contributor</strong><br />\n<strong><a href=\"https://github.com/doekman/\">Doeke Zanstra</a></strong> contributed code to 1 repository used in the <a href=\"https://github.com/readme/nasa-ingenuity-helicopter\">Mars 2020 Helicopter Mission</a><br />\n<a href=\"https://github.com/pallets/werkzeug\">pallets/werkzeug</a></p>\n</blockquote>\n\n<p>Ik was echt <em>totaal</em> verbaasd. Hoe kan dit? Wat heb ik hieraan gedaan? Maar ik las de tekst hierboven nog een keer, en toen viel het kwartje bij me. Tijdens het werken bij <a href=\"https://archipunt.nl/\">Archipunt</a> heb ik aan software gewerkt wat ook gebruikt is bij het laten vliegen van een helicopter op Mars.</p>\n\n<p>OK. Dit behoeft enige toelichting.</p>\n\n<h2 id=\"apawa\">APAWA</h2>\n\n<p>Aan het eind van 2017 was ik bij Archipunt aan het werk aan <a href=\"https://apawa.archipunt.nl/\" title=\"Archipunt Piekgrond Acceleratie Web Applicatie.\">APAWA</a>. Dit is een <em>web-applicatie</em> voor het berekenen van het lokale effect (voor een specifiek adres dus) van aardbevingen. Ik kan hier verder uitleggen wat dat precies is, maar het is misschien gemakkelijker om voorgenoemde link te klikken en zelf een kijkje te nemen.</p>\n\n<p>Terloops noemde ik het woord “web-applicatie”: dit is de software van het internet. In feite een gewone applicatie, zoals bijvoorbeeld Microsoft Word, alleen dan voor het <em>web</em>.</p>\n\n<p>En die software was ik dus aan het schrijven. Maar om het wiel niet iedere keer opnieuw uit te vinden maak ik gebruik van software componenten. En voor het web-applicatie aspect van deze software maak ik gebruik van het <em>open source</em> component met de naam <a href=\"https://flask.palletsprojects.com/en/2.0.x/\">Flask</a>.</p>\n\n<p>Open source is erg handig. De broncode is beschikbaar. Zodat als je een fout tegenkomt, kun je deze analyseren. Meestal heb je zelf een fout gemaakt, en is de broncode handig om daar achter te komen. Heel, heel soms kom je iets tegen wat verbetering behoeft. Ook dan is open source handig: niet alleen de broncode is open, maar het hele proces is open. Ook verbeter trajecten. Voordat ik hier op in ga nog even het volgende.</p>\n\n<p>Flask zelf bestaat ook weer uit componenten. Ik liep tegen een onvolkomenheid aan in het component <em>Werkzeug</em> (overigens: zowel Flask als Werkzeug is niet in Amerika, maar bij onze Oosterburen bedacht).</p>\n\n<p>Eén van de functionaliteiten van Werkzeug is het bepalen welke <em>browser</em> (zoals Chrome, Safari of Firefox) een eind-gebruiker gebruikt. In een moderne applicatie wil je niet dat er een gedateerde browser gebruikt: de belangrijkste reden hiervoor is om de test-inspanning niet te groot te laten worden.</p>\n\n<p>Nu was er een gebruiker die <em>Microsoft Edge</em> (de opvolger van Internet Explorer) gebruikte. En deze werd niet herkend door <em>Werkzeug</em>, en dus ook niet door Apawa. Edge maakt onder water gebruik van hetzelfde component als Chrome, en is dus modern. Reden om Edge toe te voegen. Als je wilt zien hoe dit er exact uit ziet, <a href=\"https://github.com/pallets/werkzeug/pull/1216\" title=\"Pull Request om edge browser detectie aan Werkzeug toe te voegen\">kijk dan hier</a> voor de details.</p>\n\n<p>Tot hier heb ik uitgelegd hoe software ontwikkeling kan leiden tot een toevoeging van een open source component. Maar wat heeft dit met Helicopters, Mars en NASA te maken?</p>\n\n<h2 id=\"helicopters-mars-en-nasa\">Helicopters, Mars en NASA</h2>\n\n<p>Laat ik om te beginnen verwijzen naar het Engelstalige artikel <a href=\"https://github.com/readme/nasa-ingenuity-helicopter\">Open Source on Mars: Community powers NASA’s Ingenuity Helicopter</a>. Erg interessant om te lezen. NASA maakt dus gebruik van open source code. Waarschijnlijk worden <em>Flask</em> en dus ook <em>Werkzeug</em> gebruikt bij <em>ground control</em> om de helicopter aan te sturen.</p>\n\n<p>In het stuk valt ook te lezen hoe men bepaald heeft welke software gebruikt werd voor het project. NASA heeft een lijst van componenten en hun versienummers aangeleverd aan GitHub, de centrale plek waar open source opgeslagen wordt.</p>\n\n<p>Omdat open source ontwikkeling zich in de openbaarheid voltrekt, kan elke wijziging in open source code gekoppeld worden aan een identiteit. De lijst van componenten en hun versienummers levert dan ook een éénduidige lijst op van namen. Mijn wijziging is opgenomen in <em>Werkzeug versie 0.14</em> en voor Ingenuity wordt versie 0.16 gebruikt. Inmiddels is versie 2.0.0 net uit. Degene die aan versies na 0.16 hebben bijgedragen worden derhalve niet genoemd bij de “achievement”. Maar wellicht wordt deze code in toekomstige missies gebruikt.</p>\n\n<p>Ik vond het leuk om achter dit feit te komen, en uit te zoeken hoe het precies zit. En ik hoop in de toekomst nog aan andere projecten te kunnen bijdragen.</p>\n\n<div class=\"postscript\"><span>Eerder gepubliceert</span> als <a href=\"https://www.archipunt.nl/archipunt-op-mars/\">Archipunt op Mars</a>.</div>\n\n",
			"summary": "Op 30 april 2021 keek ik op m’n GitHub pagina, en zag ik dat ik de volgende “achievement” erbij had gekregen:",
			"date_published": "2021-09-08T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/ict/2021/04/12/a-closure-explanation",
			"url": "https://blog.zanstra.com/ict/2021/04/12/a-closure-explanation.html",
			"title": "One Closure Explanation",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Closures in programming languages are pretty intuitive to use. Understand what they are is actually a bit more difficult.</p>\n\n<p>A closure is a function, but not a regular function. And to better understand closures, it helps me to understand regular functions. So first I explain how regular functions work. After that I’ll make the connection to functional closures.</p>\n\n<h2 id=\"regular-functions\">Regular functions</h2>\n\n<p>When you call a regular function, a lot of things are going on. Under the hood a stack-structure is used.\nThings the function needs, like arguments and the return address, get pushed up this stack. This is a bit of a simplification. There are optimising compilers, but I want explain the principle here. If you want to delve into the nitty-gritty details, please checkout <a href=\"https://en.wikipedia.org/wiki/Calling_convention\">calling conventions</a> on Wikipedia.</p>\n\n<p>So how does this work, what gets pushed up our stack-for-algorithmic-purposes? Let’s explain this with a JavaScript example.</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>1: function test(a, b) {\n2: \tvar c = a + b;\n3: \tconsole.info('The sum of %s + %s is %s', a, b, c);\n4: }\n5: test(1, 2);\n</code></pre></div></div>\n\n<p>The program runs the following steps.</p>\n\n<ul>\n  <li>Before calling <code class=\"language-plaintext highlighter-rouge\">test</code>, the return address is pushed onto the stack. With this info, the function knows where to “return” to. So let’s push the address of <code class=\"language-plaintext highlighter-rouge\">line 5</code> onto the stack.</li>\n  <li>After that, the arguments <code class=\"language-plaintext highlighter-rouge\">1</code> and <code class=\"language-plaintext highlighter-rouge\">2</code> are pushed onto the stack. Now there are 3 items on the stack.</li>\n  <li>Now we actually can start running the function. This is done via a jump to the entry point of the function, like <code class=\"language-plaintext highlighter-rouge\">goto line 1</code>. I will indent steps that are within the function call:\n    <ul>\n      <li>At line 1 there is nothing to do.</li>\n      <li>At line 2, there is some arithmetic, and the result is stored in the <em>local variable</em> <code class=\"language-plaintext highlighter-rouge\">c</code>. Locals are stored on the stack too, so <code class=\"language-plaintext highlighter-rouge\">3</code> will get pushed onto the stack, and we have 4 items on the stack.</li>\n      <li>Line 3 will print a line to the console, while referencing three items on the stack.</li>\n      <li>At line 4, the function returns. All local variables, arguments and return address are popped from the stack. The return address is used to jump back to (the end of) line 5.</li>\n    </ul>\n  </li>\n  <li>And at that point there is no more code to execute, and our program has finished running.</li>\n</ul>\n\n<p>At first glance this may seem a bit tedious and boring. Yet, some smart stuff is going on. When the function returned at line 4, all local variables, arguments and return address have been cleaned up. No need for <em>garbage collection</em> or <em>reference counting</em>. Very efficient.</p>\n\n<p>And it works pretty well when calling other functions or even with recursive calls. The stack-size is the limit! Only a stack-overflow comes after that, but that is about it.</p>\n\n<p>And yes, I left out handling return values, stack pointers and reference types. We do not need to worry about those for this explanation.</p>\n\n<p>To summarise: arguments and local variables live on the stack. They only exist between function entry and exit. In regular functions, that is.</p>\n\n<h2 id=\"first-class-functions\">First class functions</h2>\n\n<p>Before we go to closures, it is important to know what <em>first class functions</em> are. JavaScript has them, but what defines them? At first you might think <em>nesting functions</em> is a property of first class functions, but it is not. Turbo Pascal <a href=\"https://twitter.com/doekezanstra/status/1380426215757070336\">supports inner functions</a>, yet it doesn’t have support for first class functions.</p>\n\n<p>When you can assign a function to a variable, the language supports functions as a first class citizen. It should be no different as assigning an integer or a string to a variable.</p>\n\n<p>And when you can assign a function to a variable, you also can pass them around to other functions. Or let functions <em>return</em> functions or assign functions to data structures. This also seems reasonable, but Turbo Pascal can’t do this. For that one needs something extra.</p>\n\n<h2 id=\"functional-closures\">Functional closures</h2>\n\n<p>So let us explore functional closures. By the way: <em>closure</em>, <em>lexical closure</em> or <em>function closure</em>; all the same thing. Take the following example:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function test_two(a, b) {\n\tvar c = a + b;\n\treturn function inner() {\n\t\tconsole.info('The sum of %s + %s is %s', a, b, c);\n\t}\n}\nvar result = test_two(1, 2);\n</code></pre></div></div>\n\n<p>The function <code class=\"language-plaintext highlighter-rouge\">test_two</code> returns a nested function, which is assigned to the variable <code class=\"language-plaintext highlighter-rouge\">result</code>. So you would expect to execute the inner function by running <code class=\"language-plaintext highlighter-rouge\">result()</code>. And this indeed works in JavaScript.</p>\n\n<p>But, when you think of the stack-based system of regular functions, as described above, something is off. The stack-algorithm says when the function <code class=\"language-plaintext highlighter-rouge\">test_two</code> returns, the arguments <code class=\"language-plaintext highlighter-rouge\">a</code> and <code class=\"language-plaintext highlighter-rouge\">b</code> and variable <code class=\"language-plaintext highlighter-rouge\">c</code> are popped from the stack, and would not be accessible anymore. A call to the inner function would refer to variables that don’t exist anymore!</p>\n\n<p>Functional closures solve this. I like to think of a closure as function with a shopping bag that contains all the variables (state) which are necessary to perform its function. So the variable <code class=\"language-plaintext highlighter-rouge\">result</code> contains a reference to the function <code class=\"language-plaintext highlighter-rouge\">test_two</code> together with a bag with values for <code class=\"language-plaintext highlighter-rouge\">a</code>, <code class=\"language-plaintext highlighter-rouge\">b</code> and <code class=\"language-plaintext highlighter-rouge\">c</code>.</p>\n\n<p>When you would run <code class=\"language-plaintext highlighter-rouge\">var result2 = test_two(3, 4)</code>, the variable <code class=\"language-plaintext highlighter-rouge\">result2</code> will not contain the same thing as <code class=\"language-plaintext highlighter-rouge\">result</code>. The reference to the function is the same, but this closure has its own bag of state.</p>\n\n<p>How exactly this is implemented is not important for my explanation. Different languages do implement this behaviour differently, but the principle is the same. I found some more <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#closure_scope_chain\">technical info about JavaScript closures on MDN</a>, and also a blog about <a href=\"https://vkontech.com/the-intuitive-guide-to-understanding-closures-in-c/\">C# closures</a>.</p>\n\n<p>The Wikipedia article <a href=\"https://en.wikipedia.org/wiki/Closure_(computer_programming)\">on closures</a> has the line <em>“Operationally, a closure is a record storing a function together with an environment.”</em> in the introduction. That summarises the point of my explanation pretty well. I hope you now have a better understanding of closures. Let me know what you think.</p>\n\n",
			"summary": "Closures in programming languages are pretty intuitive to use. Understand what they are is actually a bit more difficult.",
			"date_published": "2021-04-12T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/2021/01/31/Building-Another-Blog-Engine",
			"url": "https://blog.zanstra.com/2021/01/31/Building-Another-Blog-Engine.html",
			"title": "Building Another Blog Engine",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>I have some ideas for a blog engine I want to explore. Instead of creating a new website, I’m planning to make incremental changes/improvements to this website. Baby steps. That’s why I’ve named this project BABE: Building Another Blog Engine.</p>\n\n<p>At the moment I’m using GitHub Pages for this blog. When I push a commit, GitHub schedules a task of transforming the source via Jekyll. And the resulting files live somewhere on a webserver. It’s convenient, but it doesn’t give me the control I need.</p>\n\n<p>Step one is to build the website local, and push the resulting files to GitHub. I don’t want to create another repository for the build result, as this adds confusion. Fortunate, <em>git</em> has a nice solution for this. Create a branch for Jekyll’s build result, and tell GitHub Pages to serve the website from there.</p>\n\n<p>I’m using the branch feature in an unusual way: it is not intended for merging into the main branch. And this way, the published source doesn’t show up in my blog’s history.</p>\n\n<p>This branch should contain no files other than the build result. The obvious choice to do this would be to create a branch and remove all files. But why not use the powers of <em>git</em> and create a branch from the initial commit, way in the past!</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>initial_hash=$(git rev-list HEAD | tail -n 1)\ngit branch web-stek $initial_hash\n</code></pre></div></div>\n\n<p>We now have an (almost) empty branch. How do we get the output from Jekyll’s build command to this branch?</p>\n\n<p>One way might be by having the ignored folder <code class=\"language-plaintext highlighter-rouge\">_site</code> (which contains the Jekyll build result) on the <em>master</em> branch. Somehow  bring the changes to the <em>web-stek</em> branch by switching branches. That would work with new files, but I don’t know how to transfer <em>modified</em> files this way.</p>\n\n<p>Two alternatives come to mind. Either use two <em>git</em> repositories on the same machine in different folders. Or use an intermediate folder for Jekyll’s build result. For simplicity and less confusion, I’ve chosen to work with an intermediate folder.</p>\n\n<p>In the example below, <em>rsync</em> transfers the build result to the <code class=\"language-plaintext highlighter-rouge\">/docs</code> folder instead of <code class=\"language-plaintext highlighter-rouge\">/_site</code>. I’ve changed the name, because <code class=\"language-plaintext highlighter-rouge\">/_site</code> is not listed with the settings for the <a href=\"https://docs.github.com/en/github/working-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\">publication source</a> on GitHub Pages. So <code class=\"language-plaintext highlighter-rouge\">/docs</code> it is. The build process works like this:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>jekyll build --destination \"$jekyll_build\"\ngit checkout web-stek\nrsync -av \"$jekyll_build\" ./docs/\n</code></pre></div></div>\n\n<p>To finish up, I need to add the folders <code class=\"language-plaintext highlighter-rouge\">.jekyll-cache</code> and <code class=\"language-plaintext highlighter-rouge\">.sass-cache</code> to the <code class=\"language-plaintext highlighter-rouge\">.gitignore</code> file on the <em>web-stek</em> branch. Also, some empty folders might be there, because git only works with actual files. Finally, you might want to remove the files from the initial commit in the branch.</p>\n\n<p>I compared the result in <code class=\"language-plaintext highlighter-rouge\">/docs</code> with GitHub’s copy by fetching the pages via <code class=\"language-plaintext highlighter-rouge\">wget -r https\\://blog.zanstra.com</code>. I only found one major difference. GitHub Pages would normalizes time-zone offsets to zero. That might be an issue for RSS readers, but I can live with it.</p>\n\n<p>So after pushing all this, and setting up the publication source you are good to go. The next step is to automate publishing, but that has to wait for an other time.</p>\n\n<p><strong>UPDATED 2021-04-06</strong>: A bit mind-bending, but the <em>publication source</em> is considered to be in Jekyll format. How I learned this? I wanted the folder <code class=\"language-plaintext highlighter-rouge\">.well-known</code> to be copied to my website, but Jekyll ignored this file. Hidden files are on the ignore list. Luckily there is an easy fix. Add a file at the root of your site called <code class=\"language-plaintext highlighter-rouge\">.nojekyll</code>. The file can be empty.</p>\n\n",
			"summary": "I have some ideas for a blog engine I want to explore. Instead of creating a new website, I’m planning to make incremental changes/improvements to this website. Baby steps. That’s why I’ve named this project BABE: Building Another Blog Engine.",
			"date_published": "2021-01-31T00:00:00+01:00",
			"tags": []
		},
		 {
			"id": "/2021/01/11/go-jamf-pro",
			"url": "https://blog.zanstra.com/2021/01/11/go-jamf-pro.html",
			"title": "Moving away from Server.app to Jamf Pro",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>At <a href=\"https://archipunt.nl\">Archipunt</a> we manage around 100 <em>macOS</em> computers. We used to manage them via <em>Server.app</em>. Because of several reasons, we now are moving to an all-MDM solution with Jamf Pro.</p>\n\n<p>To migrate the computers, we identified the following things to be fixed on these computers:</p>\n\n<ul>\n  <li>The registration to Server.app’s <em>Network Account</em> server needs to be removed</li>\n  <li>After this, some cleanup needs to be done in the computers local directory</li>\n  <li>Since password authentication was done via Server.app, the <em>mobile account</em> need to be migrated to a <em>local account</em></li>\n  <li>Some computers used Server.app’s MDM solution. When still present, old profiles need to be removed</li>\n  <li>Most computers are registered via DEP, so enrolment needs to be renewed in order to configure the to be managed via Jamf Pro.</li>\n</ul>\n\n<p>Because of COVID-19, most computers need to be migrated by the users themselves at home. Luckily, all users have elevated privileges. Otherwise, this migration would take a lot more time on our side.</p>\n\n<p>To migration will be done by a custom application. The GUI part will be written in AppleScript because it is so well suited for this. The actual migration is done via a shell-script. I couldn’t have written this without the <a href=\"https://github.com/rtrouton/rtrouton_scripts/blob/master/rtrouton_scripts/migrate_ad_mobile_account_to_local_account/MigrateADMobileAccounttoLocalAccount.command\">this script by Rich Trouton</a>: I made it work with Open Directory (which Server.app users), instead of Active Directory. Another post that helped enormously was this <a href=\"https://www.reddit.com/r/macsysadmin/comments/c97zgf/removing_apsdkeychain_to_enroll_devices_in_dep/\">check and fix for an expired Apple Push Service Delivery certificate</a>.</p>\n\n<p>I designed the shell-script via a “check-fix” strategy: I first check if something needs to be fixed, and if so, fix it. For example, first I check if the machine is linked to a network account server. If so, the link is disconnected. This way, the script can be run as often as one wants without breaking stuff. Most items about are coded via a check/fix pair. An added advantage is separate parts can easily be tested.</p>\n\n<p>It’s also handy in the case the machine has to be rebooted. After that, just run the script again, and it factually starts where it was before the computer was rebooted.</p>\n\n<p>The AppleScript front-end handles the start screen and the request for the elevated privileges. When an error occurs within the shell-script, the user is advised about this and the log file is shown in the finder. I reserved exit-code 199 via which the shell-script can tell AppleScript the computer needs to be rebooted.</p>\n\n<p>To test the AppleScript without running the actual migration shell-script, I used the user defaults system. By writing a key/value pair to the application bundle domain (<code class=\"language-plaintext highlighter-rouge\">defaults write  nl.archipunt.public.go-jamf-pro test -string \"PASS\"</code>) the test-application’s behaviour can be influenced. When the key is not present, the actual migration code will be used.</p>\n\n<p>For source-code, please check out <a href=\"https://bitbucket.org/archipunt/go-jamf-pro/src/master/\">the repository</a> on bitbucket.</p>\n\n<p>About implementation: we first asked a small group of users to test the script, so we could detect common problems early and fix them. This worked very well. After errors from this initial group were fixed, most errors we experienced where plain user errors and could be handled via the phone very quickly.</p>\n\n<p>The hardest error to fix was a user who just didn’t get the enrolment notification. Turned out, the user accidentally had switched on <em>do not distrub</em>. And also had no idea that option existed…</p>\n\n",
			"summary": "At Archipunt we manage around 100 macOS computers. We used to manage them via Server.app. Because of several reasons, we now are moving to an all-MDM solution with Jamf Pro.",
			"date_published": "2021-01-11T00:00:00+01:00",
			"tags": []
		},
		 {
			"id": "/2020/10/05/Pretty-printing-JSON-with-a-twist",
			"url": "https://blog.zanstra.com/2020/10/05/Pretty-printing-JSON-with-a-twist.html",
			"title": "Pretty Print JSON with a Twist",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p><strong>TLDR;</strong> You can pretty print JSON in a <em>different manner</em>. <a href=\"https://zanstra.com/my/Json-pp.html\">Try it here</a>.</p>\n\n<p>My second programming language was Turbo Pascal (first one GW-Basic). From a BBS-pal I learned a notation in Pascal that has the <em>semicolon</em> at the <em>start</em> of the line, instead of at the end of the line, as most people did. Sometimes I still apply the same formatting to JSON.</p>\n\n<p>In this blog, I want to explore how such formatting can be done using Python and JavaScript. Just for the record: Pascal uses semicolons as statement <em>separator</em>, where as languages like C use them as statement <em>terminator</em>. JSON shares this behaviour with Pascal.</p>\n\n<p>To make clear what I want, here’s an example JSON file in the desired layout:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{ \"type\": \"pedant\"\n, \"enabled\": true\n, \"tags\":\n  [ \"nag\"\n  , \"prick\"\n  , \"pin\"\n  ]\n, \"data\":\n  { \"test\": 123\n  , \"cooperate\": null\n  }\n}\n</code></pre></div></div>\n\n<p>At first it looks pretty weird. However, there is an obvious advantage. You can visually follow the nesting-level of the data-structure in any editor. And you never place a comma too many.</p>\n\n<p>But <strong><em>why</em></strong> would one with a sane mind want to do this? Well, for starters because it is interesting. And also fight the orthodoxy. And it’s an ideal project to learn something new along the way.</p>\n\n<p>Anyways. Python’s json module provides a <a href=\"https://docs.python.org/3.8/library/json.html#json.dump\">dump</a>-method to convert a native data-structure to JSON:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>import json\ndata = {'type': 'pedant', 'enabled': True, 'tags': ['nag', 'prick', 'pin'], 'data': {'test': 123, 'cooperate': None}}\njson = json.dumps(data, indent=2)\nprint(json)\n</code></pre></div></div>\n\n<p>When you run this, it will print out the formatted JSON below.</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{\n  \"type\": \"pedant\",\n  \"enabled\": true,\n  \"tags\": [\n    \"nag\",\n    \"prick\",\n    \"pin\"\n  ],\n  \"data\": {\n    \"test\": 123,\n    \"cooperate\": null\n  }\n}\n</code></pre></div></div>\n\n<p>To better understand what’s happening, I visualized this formatting by adding colored borders.</p>\n\n<html><style>\n\t.JSON { font: 15px/1.5 monospace; border:1px solid #e8e8e8; background:#f8f8f8; padding: 8px 12px; margin-bottom:15px; }\n\t.indent-color { border:1px solid blue;}\n\t.indent { border:1px solid blue; display:inline-block;width:3ex;height:1em;}\n\t.sep1 { border:1px solid green; white-space: pre}\n\t.sep2 { border:1px solid green; white-space: pre}\n\t.NL1-color { border:1px solid red;}\n\t.NL1 span:last-of-type { border-right:2px solid red;}\n\t/*.NL2 { border-left:2px solid orange;}*/\n</style><div class=\"JSON\">\n\t<div class=\"NL1\"><span>{</span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span>\"type\"<span class=\"sep2\">: </span>\"pedant\"<span class=\"sep1\">, </span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span>\"enabled\"<span class=\"sep2\">: </span>true<span class=\"sep1\">, </span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span>\"tags\"<span class=\"sep2\">: </span><span>[</span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span><span class=\"indent\"> </span>\"nag\"<span class=\"sep1\">, </span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span><span class=\"indent\"> </span>\"prick\"<span class=\"sep1\">, </span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span><span class=\"indent\"> </span><span>\"pin\"</span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span>]<span class=\"sep1\">, </span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span>\"data\"<span class=\"sep2\">: </span><span>{</span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span><span class=\"indent\"> </span>\"test\"<span class=\"sep2\">: </span>123<span class=\"sep1\">, </span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span><span class=\"indent\"> </span>\"cooperate\"<span class=\"sep2\">: </span><span>null</span></div>\n\t<div class=\"NL1\"><span class=\"NL2 indent\"> </span><span>}</span></div>\n\t<div class=\"NL1\"><span>}</span></div>\n</div></html>\n\n<p><span class=\"indent-color\">blue</span> Providing the <code class=\"language-plaintext highlighter-rouge\">indent</code> argument will kick-off the pretty printing. A positive integer will indent that many spaces per level. If you provide a string, that value is being used per indent-level. In this example I specified two spaces.</p>\n\n<p><span class=\"NL1-color\">red</span> If <code class=\"language-plaintext highlighter-rouge\">indent</code> is specified, a newline will be inserted just before the indentation.</p>\n\n<p><span class=\"sep1\">green</span> You can also change the separator behavior. From the documentation: <em>“If specified, separators should be an <code class=\"language-plaintext highlighter-rouge\">(item_separator, key_separator)</code> tuple”</em>. So by providing the tuple <code class=\"language-plaintext highlighter-rouge\">(';', ': ')</code> for <code class=\"language-plaintext highlighter-rouge\">separators</code>, one could create <em>European JSON</em>. Here I provide a semi-colon instead of a comma, which is obviously not valid JSON. So just be aware, you could produce invalid JSON with this option. However, when <code class=\"language-plaintext highlighter-rouge\">indent</code> is specified, the tuple <code class=\"language-plaintext highlighter-rouge\">(', ', ': ')</code> is automatically used, adding spaces after both separators.</p>\n\n<p>So, these arguments were a bit of a disappointment. It didn’t seem possible to format the JSON the way I wanted it.</p>\n\n<p>However, the API provides another option. We could specify a custom <a href=\"https://github.com/python/cpython/blob/3.8/Lib/json/encoder.py\">JSONEncoder</a> subclass via the <code class=\"language-plaintext highlighter-rouge\">cls</code> argument (you can inspect the code I linked to). This class is heavily optimized and caters for all sorts of requirements. I made a copy of the code, and made some modifications. It just seemed like a lot of work. The code is not really designed for reuse (for example: encoding value types, like boolean, are duplicated multiple times), so I decided to take a different route to a solution at this time.</p>\n\n<p>Look at the color-coded JSON above. What if we replaced <code><span class=\"sep1\">comma</span> <span class=\"NL1-color\">newline</span> <span class=\"indent-color\">indent</span></code> by <code><span class=\"NL1-color\">newline</span> <span class=\"indent-color\">indent</span> <span class=\"sep1\">comma</span></code>? Yes, I’m talking string-replacement. Any newline in JSON can safely be recognized as whitespace, since newlines in strings are always encoded like <code class=\"language-plaintext highlighter-rouge\">\\n</code> (or when reading code, the newline <code class=\"language-plaintext highlighter-rouge\">\\n</code> is encoded as <code class=\"language-plaintext highlighter-rouge\">\\\\n</code>).</p>\n\n<p>The only tweak we need is there should be a little bit indentation <em>after</em> the comma (group <code class=\"language-plaintext highlighter-rouge\">\\2</code> in the code below). The remaining indentation should be placed <em>before</em> the comma (group <code class=\"language-plaintext highlighter-rouge\">\\1</code>). For now, I assumed the indent to be two spaces. The <code class=\"language-plaintext highlighter-rouge\">((?:  )*)</code> construct is to see the two spaces as one “thing”, so it could be matched multiple times. The <code class=\"language-plaintext highlighter-rouge\">?:</code> makes sure it is not remembered as a match. I do want to match <em>multiples</em> of two spaces here, hence the double parentheses.</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>import re\ndef my_json_pretty_print(json):\n    return re.sub(r', *\\n((?:  )*)(  )', r'\\n\\1,\\2', json)\n</code></pre></div></div>\n\n<p><em>As a side-note</em>: since this is a learning exercise, I tried to make the regular expression more readable by using <a href=\"https://docs.python.org/3.8/library/re.html#re.X\">verbose regular expressions</a>. The idea about it whitespace (not used in special ways) is ignored, so you can use multi-line strings. You also can add comments to parts of the regular expression. However, I intend to match whitespace, but I couldn’t get it working. And because I also didn’t think the regular expression was more readable this way, I abandoned it.</p>\n\n<p>If we combine the two code-snippets from above, we get the following output printed:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{\n  \"type\": \"pedant\"\n,  \"enabled\": true\n,  \"tags\": [\n    \"nag\"\n  ,  \"prick\"\n  ,  \"pin\"\n  ]\n,  \"data\": {\n    \"test\": 123\n  ,  \"cooperate\": null\n  }\n}\n</code></pre></div></div>\n\n<p>Not bad. Not bad at all! When we compare this to the desired layout from above, we can note the following:</p>\n\n<ul>\n  <li>There are two spaces between the comma and the object-properties, instead of one. I think this is not wrong, but one space would be nicer.</li>\n  <li>The opening object- and list-characters (<code class=\"language-plaintext highlighter-rouge\">{</code> and <code class=\"language-plaintext highlighter-rouge\">[</code>) are followed by a newline. This needs to be addressed.</li>\n  <li>The closing characters (<code class=\"language-plaintext highlighter-rouge\">}</code> and <code class=\"language-plaintext highlighter-rouge\">]</code>) seem to be at the right place, so we are fine there.</li>\n</ul>\n\n<p>First thing I want to fix is to support all sorts of indentations, and not only hard coded two spaces. I figured it is best to define a new function, which will call <code class=\"language-plaintext highlighter-rouge\">json.dumps</code>, so there is only need to pass <code class=\"language-plaintext highlighter-rouge\">indent</code> once. Addressing the issue with the extra space in the indentation: this is caused by the comma that is also functioning as an indentation character. Basically I need to delete the first character of the last indent, marked visually by the <code class=\"language-plaintext highlighter-rouge\">X</code>: <code><span class=\"indent-color\">&nbsp;&nbsp;</span><span class=\"indent-color\">&nbsp;&nbsp;</span><span class=\"indent-color\">x&nbsp;</span></code>.</p>\n\n<p>I played a bit with parameterizing the indentation into the regular expression. String concatenation looked terrible. Formatting with <code class=\"language-plaintext highlighter-rouge\">{}</code> and <code class=\"language-plaintext highlighter-rouge\">.format()</code> wasn’t a good match either, because curly braces are also special characters in regular expressions. <code class=\"language-plaintext highlighter-rouge\">printf</code>-style formatting (<code class=\"language-plaintext highlighter-rouge\">%s</code>) is a good alternative, while the regular expression stays readable.</p>\n\n<p>While playing with it, I discovered that the handling of the <code class=\"language-plaintext highlighter-rouge\">[</code>- and <code class=\"language-plaintext highlighter-rouge\">{</code>-characters are the same the <code class=\"language-plaintext highlighter-rouge\">,</code>-character, so I generalized the regex by changing <code class=\"language-plaintext highlighter-rouge\">,</code> to a capturing group that matches all three characters, and add a back-reference to the replacement. The code now looks like this:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>def json_stringify(obj, indent=2):\n    if not isinstance(indent, str): indent = ' ' * indent\n    result = json.dumps(obj, indent=indent)\n    rx_indent = r'([,{[]) *\\n((?:%s)*)%s(%s)' % (indent, indent[0], indent[1:])\n    result = re.sub(rx_indent, r'\\n\\2\\1\\3', result)\n    # Special case: remove inserted newline with top-level array or object\n    return result[1:] if result[0]=='\\n' else result\n</code></pre></div></div>\n\n<p>So, if we run the code now, we get this:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{ \"type\": \"pedant\"\n, \"enabled\": true\n, \"tags\": \n  [ \"nag\"\n  , \"prick\"\n  , \"pin\"\n  ]\n, \"data\": \n  { \"test\": 123\n  , \"cooperate\": null\n  }\n}\n</code></pre></div></div>\n\n<p>This is exactly how we want it. As always, there are some things to be desired:</p>\n\n<ul>\n  <li>Indentations with TAB-characters are not handled well. The code should only remove the first character of the last indent when it’s not a TAB-character.</li>\n  <li>Using regex special characters as indentation (like <code class=\"language-plaintext highlighter-rouge\">*</code>) make the program fail: the indent-string should be converted to a valid regular expression string first.</li>\n  <li>The code fails when <code class=\"language-plaintext highlighter-rouge\">indent=0</code> with an <code class=\"language-plaintext highlighter-rouge\">IndexError: string index out of range</code> on the code <code class=\"language-plaintext highlighter-rouge\">indent[0]</code>. This is easy fixable by using slices, like <code class=\"language-plaintext highlighter-rouge\">indent[:1]</code></li>\n</ul>\n\n<p>So that brings us to the following code:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>def json_stringify(obj, indent=2):\n    if not isinstance(indent, str):\n        indent = ' ' * indent\n    result = json.dumps(obj, indent=indent)\n    r_indent=re.escape(indent)\n    if indent[:1]=='\\t':\n        r_indent0  = ''\n        r_indent1N = re.escape(indent)\n    else:\n        r_indent0 = re.escape(indent[:1])\n        r_indent1N = re.escape(indent[1:])\n    rx_indent = r'([,{[]) *\\n((?:%s)*)%s(%s)' % (r_indent, r_indent0, r_indent1N)\n    result = re.sub(rx_indent, r'\\n\\2\\1\\3', result)\n    return result[1:] if result[:1]=='\\n' else result\n</code></pre></div></div>\n\n<p>So, that’s it. I made this code into a <a href=\"https://gist.github.com/doekman/e6e942d03f9dee3e98c080b7a47cd04b\">shell script</a>, so it can be run from the command line (don’t forget to <code class=\"language-plaintext highlighter-rouge\">chmod +x</code> it before you start it). So how does it perform?</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ ls -lh\n-rw-r--r--   1 doekman  staff   4,9M 22 sep 17:08 big_trello_export.json\n-rwxr-xr-x   1 doekman  staff   705B 22 sep 17:10 json-pp.py\n$ time ./json-pp.py &lt; big_trello_export.json &gt; big_test.json\nreal\t0m0.849s\nuser\t0m0.734s\nsys\t0m0.069s\n$ ls -lh\n-rw-r--r--  1 doekman  staff   7,3M 22 sep 17:11 big_test.json\n-rw-r--r--  1 doekman  staff   4,9M 22 sep 17:08 big_trello_export.json\n-rwxr-xr-x  1 doekman  staff   705B 22 sep 17:10 json-pp.py\n$ \n</code></pre></div></div>\n\n<p>Not bad. Five megabytes within the second on my 2017 iMac. I expected worse. But to state the obvious: don’t use this in production.</p>\n\n<p>As I mentioned at the start: I couldn’t have ended this quest without back-porting this code to the <a href=\"https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Object/toSource\">origins of JSON</a>. The advantage of string substitution: it can easily be converted to JavaScript.</p>\n\n<p>Converting the code to JavaScript was pretty straight-forward. Things worth mentioning:</p>\n\n<ul>\n  <li>JavaScript now comes with build-in <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify\">JSON-methods</a> (grandpa speaking)</li>\n  <li>I couldn’t find a native <a href=\"https://docs.python.org/3.8/library/re.html#re.escape\">re_escape</a> for JavaScript, so I used <a href=\"https://github.com/doekman/base2/blob/1e2eb7fb10565db7e71d2b25780783627e0773dd/src/base2/js/String2.js\">Dean Edward’s rescape</a> from the (now ancient) <a href=\"https://code.google.com/archive/p/base2/\">base2-library</a>. Substitutions in JavaScript are <code class=\"language-plaintext highlighter-rouge\">$1</code> instead of <code class=\"language-plaintext highlighter-rouge\">\\1</code> in Python</li>\n  <li>To generate 4 spaces, in Python you would write <code class=\"language-plaintext highlighter-rouge\">4*' '</code>. I couldn’t find the JavaScript equivalent first, so I used <code class=\"language-plaintext highlighter-rouge\">new Array(1+4).join(' ')</code> but then I found out you can use <code class=\"language-plaintext highlighter-rouge\">' '.repeat(4)</code> in <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat#Browser_compatibility\">modern browsers</a></li>\n  <li><a href=\"http://es6-features.org/#StringInterpolation\">Template Literals</a> are still <a href=\"https://caniuse.com/mdn-javascript_grammar_template_literals_template_literal_revision\">a bit to new</a> to use everywhere, so I implemented the regular expression with string concatenation</li>\n  <li>Python slices <code class=\"language-plaintext highlighter-rouge\">[1:2]</code> are calls to <code class=\"language-plaintext highlighter-rouge\">.substring(1,2)</code> in JavaScript. Why can’t I remember this?</li>\n</ul>\n\n<p>You can try the <a href=\"https://zanstra.com/my/Json-pp.html\">JavaScript version here</a>. Thanks for reading!</p>\n\n",
			"summary": "TLDR; You can pretty print JSON in a different manner. Try it here.",
			"date_published": "2020-10-05T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/2020/04/03/query-trello-json-with-postgres",
			"url": "https://blog.zanstra.com/2020/04/03/query-trello-json-with-postgres.html",
			"title": "Query Trello data with Postgres (JSON)",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Recently, I was working on a project to automatically create Trello-cards via their API. To do that, I needed some <em>id</em>-values from the Trello-board which are not shown in the user-interface. But where does one get these values?</p>\n\n<p>Glad you asked. Every Trello-board can be <a href=\"https://help.trello.com/article/747-exporting-data-from-trello-1\">exported to JSON</a>, and the values I was looking for are available in this export. However, <em>JSON</em> gives you the data in one big piece of text. Wouldn’t it be handier to have it available as <strong>tabular data</strong> in a database?</p>\n\n<p>So I created <a href=\"https://github.com/doekman/postgres-with-trello\">this git-repository</a> to do just that (you will need the <a href=\"https://www.postgresql.org\">Postgres</a>-database for this).</p>\n\n<p>The table in which the JSON is stored, is modelled after an <a href=\"https://rob.conery.io/2015/02/28/document-storage-gymnastics-in-postgres/\" title=\"Don't forget to click on the POSTGRES-tag on his site, listing a lot of interesting posts...\">idea of Rob Conery</a> which basically is: store the JSON in a column, along with another column that uniquely identifies the document. This unique value is also available within the JSON itself.</p>\n\n<p>For example:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>id, json\n12, '{\"id\":12, \"other\":\"data\"}\n13, '{\"id\":13, \"other\":\"info\"}\n</code></pre></div></div>\n\n<p>The actual table definition is different, but you get the idea. And don’t worry about storing the id-value twice: this small abstraction will make your life a bit easier.</p>\n\n<p>On top of this document table, I created views for most used objects, like <em>cards</em> and <em>lists</em>. The pretty simple idea of “views on top of json” is pretty powerful. You get a lot of <strong>bang for your buck</strong>!</p>\n\n<p>If you’re on a Mac, <a href=\"https://postgresapp.com\">Postgres.app</a> is the easiest way to setup a database, but <a href=\"https://brew.sh\">Homebrew</a> works fine too. As client, I use <a href=\"https://eggerapps.at/postico/\">Postico</a> which I really like a lot.</p>\n\n<p>So if you have a postgres database setup, and have cloned the <a href=\"https://github.com/doekman/postgres-with-trello\">git project</a>, it’s easy to create the database object via the terminal by typing:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>make trello\n</code></pre></div></div>\n\n<p>This will create the <code class=\"language-plaintext highlighter-rouge\">trello</code> schema and within the schema the <code class=\"language-plaintext highlighter-rouge\">document</code> table in which the json will be loaded and a bunch of views. I’ve included a Trello export file, so you can load it by running the following command:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>tool/loaddoc.sh data/simple_board_v1.json\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">loaddoc</code> inserts the JSON-document in the database, unless it already exists. In that case it will update the row. To aid this <em>upsert</em> behaviour, I’ve added a <em>trigger</em> to the database schema that updates the id-column. Ideally, this would be handled by the tool itself. However: this is a minimum-imlementation. I couldn’t find anything to load json into the database from the command line.</p>\n\n<p>A note about the id-column. Trello uses hexadecimal identifiers of 24 positions. That’s easy for computers, but a little harder for humans. So to ease filtering the views, I’ve added an integer-column with auto-numbering.</p>\n\n<p>Below the screenshot showing the result in Postico with <a href=\"https://trello.com/b/ZWvFVK9Z/postgres-with-trello\">this Trello-board</a> loaded; the same one as we just loaded. On the left you see the table and views. Above I’ve applied a filter, so I only see the lists from the first JSON document. All columns are type-cast. When you hover the image, the right pane is shown which also show type information.</p>\n\n<html>\n\t<figure tabindex=\"0\">\n\t\t<img src=\"/images/2020-03/Query-trello-json-with-postgres_A.png\" alt=\"Screen-shot of the Postico application\" class=\"non_hoover\" />\n\t\t<img src=\"/images/2020-03/Query-trello-json-with-postgres_B.png\" alt=\"Screen-shot of the Postico application, with right pane\" class=\"hoover\" />\n\t\t<figcaption>\n\t\t\tThe trello_list-view, with a filter applied <span class=\"non_print comment\">(mouseover to display right pane)</span>\n\t\t</figcaption>\n\t</figure>\n</html>\n\n<p>Since I talked a lot about tooling: when you run <code class=\"language-plaintext highlighter-rouge\">cat .ok</code> on the command line, you will find some often used commands on your screen. It’s called an <em>ok-profile</em>. The <a href=\"https://github.com/secretGeek/ok-bash\">ok-bash</a>-tool will make it easier to work with this. It also makes you smarter and more efficient.</p>\n\n<p>So if you develop for Trello, you should really <a href=\"https://github.com/doekman/postgres-with-trello\">give this a try</a>. And if you are working with JSON documents from other origins, I’m very much interested in your ideas.</p>\n\n<hr />\n\n<p>Originally published <a href=\"https://gist.github.com/doekman/e9b1530f0150822b464c4f7b0262a4f3\">at this gist</a> on GitHub.</p>\n",
			"summary": "Recently, I was working on a project to automatically create Trello-cards via their API. To do that, I needed some id-values from the Trello-board which are not shown in the user-interface. But where does one get these values?",
			"date_published": "2020-04-03T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/2020/02/17/ok-bash-and-python",
			"url": "https://blog.zanstra.com/2020/02/17/ok-bash-and-python.html",
			"title": "ok-bash and Python on macOS",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Development on <a href=\"https://www.python.org/doc/sunset-python-2/\">Python 2 has been stopped</a>, and the default availability of Python on macOS <a href=\"https://developer.apple.com/documentation/macos_release_notes/macos_catalina_10_15_release_notes#3318257\">will be deprecated</a> anywhere in the nearby future. Last week, <a href=\"https://scriptingosx.com/2020/02/wrangling-pythons/\">Scripting OS X</a> wrote about how to deal with this from an admin-perspective.</p>\n\n<p>Here I would like to explain how we deal with this from the perspective of a small but very handy tool called <a href=\"https://github.com/secretGeek/ok-bash\">ok-bash</a>. This nifty tool helps you free brainspace by creating <code class=\"language-plaintext highlighter-rouge\">.ok</code>-folder profiles for bash. You should check it out: it really makes you smarter and more efficient.</p>\n\n<p>Since the default availability of Python on macOS is threatened, this article is written from a macOS point of view. But the tool itself works on all systems that run <em>bash</em>.</p>\n\n<p>The tool is exposed via a bash-function, with a Python-backend helping with more complex tasks like syntax highlighting and other formatting. We need a bash-frontend, so the tool can work in the current shell environment.</p>\n\n<p>We want to keep installation of <em>ok-bash</em> simple, so users can just clone the git-repository and initialize it via the shell startup file.</p>\n\n<p>Since there are so many ways to manage Python, we decided to resolve the used python-binary that’s within the current <code class=\"language-plaintext highlighter-rouge\">PATH</code>. This is termined via the command <code class=\"language-plaintext highlighter-rouge\">which python3 || which python</code>, so Python 3 is used when available (the Python-backend works both in versions 2 and 3). Also, when only Python 2 is available (or if Python 3 is symlinked to <code class=\"language-plaintext highlighter-rouge\">python</code>), ok-bash still works.</p>\n\n<p>It’s also possible to manually override the path to Python, by setting an environment variable.</p>\n\n<p>In the future, ensuring Python’s availability can be done via a <a href=\"https://docs.brew.sh/Homebrew-and-Python\">homebrew recipe</a>. An extra advantage of homebrew is it supports the use of virtual environments.</p>\n\n",
			"summary": "Development on Python 2 has been stopped, and the default availability of Python on macOS will be deprecated anywhere in the nearby future. Last week, Scripting OS X wrote about how to deal with this from an admin-perspective.",
			"date_published": "2020-02-17T00:00:00+01:00",
			"tags": []
		},
		 {
			"id": "/2019/10/02/To-the-point",
			"url": "https://blog.zanstra.com/2019/10/02/To-the-point.html",
			"title": "To the Point",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Already two internet aeons ago, <em>secretGeek</em> came up with some <a href=\"http://secretgeek.net/up_up_up\">handy bash-aliases</a> to ease navigating the folder hierarchy:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>alias ..='cd ..'\nalias ...='cd ../..'\n#and so on\n</code></pre></div></div>\n\n<p>Two dots plus an enter navigate you one directory down¹, three dots do that twice, and so on. However, a single dot doesn’t go anywhere and gives you rudely some error message. No help there:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ .\n-bash: .: filename argument required\n.: usage: . filename [arguments]\n</code></pre></div></div>\n\n<p>Turns out the <code class=\"language-plaintext highlighter-rouge\">.</code> is a <a href=\"https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#Bourne-Shell-Builtins\">shell buildin command</a> that requires at least a filename as an argument. How rude.</p>\n\n<p>Wouldn’t it be handy and appropriate to print the current path when no argument is given? In that case, you could try adding the following to your <code class=\"language-plaintext highlighter-rouge\">.profile</code> or equivalent startup file:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function . { if [[ $# -eq 0 ]]; then pwd; else source \"$@\"; fi }\n</code></pre></div></div>\n\n<p>Now a single dot will be more polite and will show the current path. Used with arguments, it works like before. So you still can use it to source scripts, like <a href=\"https://github.com/secretGeek/ok-bash/\">ok-bash</a>.</p>\n\n<p style=\"margin-top:4em\">\n\t<small>\n\t\t¹) I've been told a hierarchy of folders/directories is a tree structure. When navigating into a folder, like <code>cd some_folder</code>, to my earnest knowledge one moves up. Branches mostly grow to the light where the sun is, which is up in the sky. So if you go back from said folder with <code>cd ..</code> one would move down. So secretGeek's aliases better be called <code>down_down_down</code> but maybe that has a less engaging ring to it.  Also: the base of the hierarchy —one could go there with <code>cd /</code>— is called the root, which is a kind of odd, because the root is as branched as the part of the tree that grows in the air. And at the file system, it's only one place! Anyways, the proposed function neither goes up nor down, so at least we have no confusion there.\n\t</small>\n</p>\n",
			"summary": "Already two internet aeons ago, secretGeek came up with some handy bash-aliases to ease navigating the folder hierarchy:",
			"date_published": "2019-10-02T00:00:00+02:00",
			"tags": ["cli"]
		},
		 {
			"id": "/2019/08/26/NetNewsWire",
			"url": "https://blog.zanstra.com/2019/08/26/NetNewsWire.html",
			"title": "NetNewsWire 5.0 released",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>From the <a href=\"https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/CodingGuidelines.md#version-control\">Coding Guidelines</a> of NetNewsWire:</p>\n\n<blockquote>\n  <p><strong>Version Control</strong><br />\nEvery commit message should begin with a present-tense verb</p>\n</blockquote>\n\n<h2 id=\"2017\">2017</h2>\n<p>Initial Add Add Add Add Remove Add Add Add Add Add Add Add Add Fix Make Add Unbreak Add Add Add Add Add Add Add Add Add Add Add Delete Add Remove Drop Add Add Add Add Add Add Comment Allow Put Make Update Make Show Wire Avoid Replace Add Add Ignore Add Write Update Remove Merge Generate Merge I Merge.</p>\n\n<p>Provide If Add Merge Fix Merge Fix Merge Merge Update Merge Use Use Start Create Start Extract Start Make Make Get Start Add Turn Add Add Move Fix Add Add.</p>\n\n<p>Commit Continue Move Add Work Continue Continue Delete Convert Convert Convert Convert Convert Remove Converrt Add Replace Set Begin Move Remove Set Continue Add Back Make Start Make Make I Rename Give Start Continue Create Add Make Do.</p>\n\n<p>Get Make Get Attach Progress Make More Continue Make LookupTable. Using Yet More Merge Remove Start Checking Added Start Progress Make Finish Make Turn Make Decide Progress Make Fix Make Create Make Delete Progress. Implement Start.</p>\n\n<p>Implement Create Make Make Make Make Continue Continue Make Continue Deal Get Make Use Call Add Delete Fix Make Marked Use Make Make Update Fix Make Rename Remove Add Prune Switch Save Update Move Merge Fix Update Merge Remove Update Move Update Fix Create Slightly Cache Use Delete Implement Update Update Make Update Move Create Delete Fix Get Move Delete Replace Continue Fix Continue Continue Continue Continue Continue Make Get Make Continue Continue Start Use Create Switch Simplify Update Start Remove Start Rename Do Move Make Fix Start Start Make Init Make Fix Make Init Fix Perform Edit Add.</p>\n\n<p>Make Fix Fix Fix Fix Fix Fix Create Move Remove Make Create Make Rewrite Use Fix Fix Remove Require Add Use Use Send Move Update Keep Update Create Make Call Save Fix Avoid Avoid Set Set Simplify Save Comment-out Add Remove Update Update Include Fix Update Refactor Avoid Handle Handle Rename Update Use Show Make Add Implement Show Update Make Fix Create Update Update Define Handle Make Update Differentiate Add Use Removed Update Change Make Make Post Rejigger Send Rewrite Use Update Move Make Fix Update Maintain Update Update Start Create Switch Begin Clear Rename Update Move Fix Edit Put.</p>\n\n<p>Flesh Craete Fix Create Start Do Start Fix Move Move Read Show Use Use Make Make Make Upgrade Upgrade Upgrade Upgrade Fix Make Switch Make Add Display When Mark Use Add Add Add Add Create Write Allow Write Check Add Remove Calculate Draw Close Start Show Draw Create Add Show Change Add Add Merge Rename Add Post Make Make Run Move Update Add Show Add Remove Add Skip Add Bump Add Add Change Add Change Bump Use Lighten Switch Set Decode Add Expand Set Bump Create Fix Use Create Add Add Add Add Fix Create Treat Update Make Use Add Delete Create Move Implement Remove Bump Switch Disallow Save Start Save Save Create Create Make Create Make Make Bump Make Update Set Darken Create Create Switch Create Make Make Replace Fix Make Create Make Use Fix Implement Use Update Show Return Add Bump Update Check Add Fix Fix Put Note Edit Avoid Log Normalize Print Log Simplify Log Add Start Adjust Pull Add Use Rebuild Add Fix Keep Add Make Parse Get Add Start Add Add Prefer Bump Add Add Add Fix Do Don’t Add Hide Add.</p>\n\n<p>Avoid Update Update Bump Fix Save Use Make Start Add Fix Get Add Add Use Bind When Create Delete Fix Do Increase Fetch Set Show Create Refresh Make Use Update Merge Update Add Start Add Fix Continue Make Define Make Create Make Create Create Parse Fix Base Cache Update Clean Fix Remove Remove Get Increase Add Create Create Remove Remove Remove Add Add Add Make Use Add Draw Skip Add Delete Reuse Parse Remove Bump Update Merge Add Save Refresh Parse Make Add Prefer Test Add Support Add Parse Add Add Make Add Add Fix Add Add Add Add Make Implement Add Style Display Normalized Continue Progress Make Edit Implement Position Bump Update Add Add Update Fix Add Add Link Add Add Add Fix Add Add Fix Add Continue Add Fix Update Use Merge Set Add Merge Update Update Move Implement Merge Clean Move Validate Add Use Use Remove Merge Simplify Return Validate Popup Add Remove Create Improve Set Start Don’t Set Use Use Bump Remove Update Implement Go Add Use Make Bump Update Remove Remove Create Merge Make Continue Continue Show Update Add Update Fix Show Use Put Add Fix Use Update Add Add Show Make Fix Comment Bump Update Freeze Fix Make Show.</p>\n\n<h2 id=\"2018\">2018</h2>\n<p>Move Move Add Show Fix Check Use Update Make Make Turn Create Remove Draw Add move Merge Merge, Bump Increase Make Start Check Remove Merge Make Decrease Make Update Continue basic Merge revert Merge Make Add Use Add Switch Make User Use Fix Increase Add Bump Change Update Update Add Add Create Add Make Use Use Bump Update Make Unbreak Create Scripting Merge Add revert Merge Add Remove Layout Create Create Make Continue Keep Adjust Remove Reopen Open Restore Hide Display Continue Increase Continue Set Share Create Send Extend Make Add Revise Refer Move Add Handle Use Update Merge Scripting revert Add Merge Remove Download Switch Remove Remove Back Use Remove Add Add Set Make Add Add Add Add Validate Set Merge Consider Register Merge Re-sort Create Build Add Add Create Change It Accept Remove Sender Merge Merge Merge.</p>\n\n<p>Merge convert Merge Merge Start Merge Create Layout Change Add Wire Try Disable Make Delete Remake Make Remove Continue Get Add Add Move Make Remove Bump Update Reorder Remove Create Merge Merge Remove Show Make Show Change Update Restore implement Merge Merge Start Add Merge Add Pass Rearrange Add Use Add Create Add Make Add Add Remove Fetch support Merge Merge Fetch Rename Bump Update Remove Skip Make Remember Fix Make Skip Disallow Punt Make Create Implement Support Add Start Make Support Make Remove Update Bump Update When Skip Make Add Do Merge Hide When Make Update Make Create Delete Remove Add Add Use Use Use Remove Remove Use Create Use Create Use Don’t Use Add Remove Create Move Remove Move Move Fix Remove Move Darken Do Start Show Skip Add Mark Release Make Remove Rewrite Define Tweak Bump Update Give Tweak Draw Show Make Update Merge Switch Remove Create Add Turn Reenable Make Add Use Make Don’t Don’t Remove Replace Make support Remove Start Continue Use Fix Turn Reduce Lower Lighten Tweak Add Skip Make Turn Fix Match Adjust Use Make Turn Revert Hide Fix Make Make Deal Rebuild Bump Update Force Draw Reduce Save Bump Update Start Clean.</p>\n\n<p>Support Merge revert Reset Merge Merge.</p>\n\n<p>Add Merge add Merge better.</p>\n\n<p>Merge Add Merge.</p>\n\n<p>Add Remove Break Break Break Break Break First Merge use Break Break Rename Rename Add Add Add Add Build Update.</p>\n\n<p>Add Get Get Make Add Use Nuke Fix Remove Remove Remove Use Update Merge Really Use Use Make Submodule Remove Submodule Start RSDatabase Remove Rename Move Delete Remove Include.</p>\n\n<p>Update Add Unbreak Merge Oops Merge Add I Change Merge Fix Changes Merge Simplify Update Fix Use Unignore Use Use Use Use Update Use Update Use Use Use Use Use Use Use Use Use Update Update Update Add Update Update Use Update Merge Finish Use <strong>🎉<a href=\"https://inessential.com/2018/08/31/netnewswire_comes_home\" title=\"Celebrate\">Start</a> <a href=\"https://inessential.com/2018/08/31/netnewswire_comes_home\" title=\"Celebrate\">Continue</a>🏡</strong> Update Update Revise Merge.</p>\n\n<p>Removed Rename Update Change Use Use Merge Switch Merge Merge Hide Allow Normalize Normalize Fix Make Update Stop Merge Don’t Rename Add Update Add Update Start Keep Use Merge Merge Manually Add Bump Bump Hide Add Add Fixed Fix Merge Merge Don’t Merge Removed Fixed Merge Merge Update Update Change Changed fixed Merge Added Merge Remove Update Bump Update Enabled Merge Enabled Merge Bump Fix Print Fix Give Update Implemented Start Update Rolledback Enabled Changed Enabled Stop Update Update Stop Give Store Stop Merge Don’t Write Merge Merge Changed Added Merge Merge Save Merge Store Store Make Store Store Store Store Skip Write Added On Remove Stop Delete Remove Made Update Skip Merge Modified Updated Disallow Include Register Create Made Made Remove Start Make Refactor Merge Normalize Register Validate Add Accept Make Get Validate Make Added Added Sorted Redo Start Improve Merge Merge Merge 5.0d7. Update Temporarily Fix Include Merge.</p>\n\n<p>Update Continue Complete Fix Similar Merge Merge.</p>\n\n<p>Remove Update Re-enable Make Allow Update Make Update Increment Install Clean Update Update Update.</p>\n\n<p>Clean Update Merge Add Add Make Override Use Enable Add Add Update Update Get Do Disable Update Move Add Remove Add Remove Start Continue Make Remove Add Remove Add Use Rename Remove Make Move Fix Remove Remove Remove Make Add Add Add Add Make Make Add Remove.</p>\n\n<h2 id=\"2019\">2019</h2>\n<p>changed Merge added Merge Start Merge Send Add Fix Update Run Don’t Start Add Add Add Update Update Add Fix Update Bind Add Switch Keep Update Update Fix Set Bump Close Add Bind Refresh Check Add Fix Switch Update Remove Enable Update Make Bump Tighten Reduce Further Update Bump.</p>\n\n<p>Moving Remove Updates Update Re-sort Restore Make Set Start Update Refresh Bump Drop Update Use Make Add Implement Add Implement Bump added Merge Don’t Don’t Update Make Allow Queue Do Remove Remove Remove Skip Fix Fix Discard Bump Update Skip Stop Update Remove Remove Remove Remove Remove Fix Merge Move Merge Remove Get Delete Delete Simplify Simplify Simplify Rationalize Import Start Make Update Quiet Merge Create Update Remove Move Make Remove Make Place Move Create Add Start Start Differentiate Merge Change Fix Merge Work Merge Make Continue Start Continue Remove Make Make Continue Rewire Fix Merge. Update Make Remove Remove Make Update Make Update Update Implement Make Add Index Update Revise Create Show Add Update Remove Revert Bump.</p>\n\n<p>Implemented added Use Remove Get Remove Remove Create Made Recalculate Merge Merge Remove Comment-out Rolled Merge Updated Merge Switch Merge Made Merge updated Merge made Merge add Merge Change Document Delete Create Update Continue Add Import Pull Change Remove Start Continue Update Make Make Skip Remove Remove Perform Do Give Pay Start Update Continue Make Make Move Finish Continue Continue Update Merge Store Update Convert Convert Post Convert Convert.</p>\n\n<p>Remove Set Drop Merge Remove Removed Create Merge Updated Remove Remove Remove Remove Merge Made updated Changed Updated Use Remove Remove Remove Remove Remove Remove Update Move Fixed Merge Remove Merge Delete Fix Move Move Remove Remove Major Move set Added updated Remove Work Start Update Save Update Take Made updated Added added Added adding Merge cleared Attempted Design Removed Merge Added Fixed Combined Combined Fixed Implemented Implemented Updated Group Merge Update Update Update Update Merge Updated Established Adjusted Fixed Fixed Changed Cleaned Fixed Added Added Made Added Reduced Fixed Implemented Refactored Made Added Changed Renamed Added Made Implemented Add Remove Add Update Start Create Switch Change Fix Implement Make Fix updated Update Fix Remove Tweak Fixed Change Revise Merge Removed Restored Fix Change Prevent Create Change Update implement Move Change Refactor Encapsulate Implement Add Finish Hide Move Prevent Fix Removed Hide Change Add Implement Fix Make Make Skip Remove Change Add Rename Make Change Add Refactor Enable Comment Implement Change updated Fix Add Implement Updated Add Add Prevent Add Rework Update Change Remove Add Rollback Enable Add Added Made Remove Change Add Enable Tweak Add Add Change Tweak Hack Provide Delete Rename implement Fix Fix Force Fix Made Deselect Deleted Align Make Respond Fix Align Change Slightly Increase Changed Use Add Merge Update Add Merge Add Add Merge Add Change Merge Change Create Change Fix Merge Refer Implement Make Made Merge Set Add Fix Add Fix Reuse Merge Write Add Make Merge.</p>\n\n<p>Rename Add Add Make Rename Rename Add Load Enable Delete Refine Make Make Prevent Implement Make Make Add Implement Implement Tweak Expand Prevent Show Update Updated validate Make Update Made change Add Add refactor Upgraded Pass Modify Rename Add Add Update Fix update make Add Added Fix Add Change Add Save added Add Keep Add Add Add added Update Add Fix Add Update Add Change Use Add Capture Refactor Add Restrict Move add store make Make Place Make Added Change Delete Set Change Add Put Stub Make Speed Use Set Update Merge Add Save Add Fix Delegate Make Save Change Scale Add Add Fix Upgraded Fix Fix Removed Fix Restore Update Add Start Update Add Add Add Rename Add Align Switch Prevent Retrieve Add Fix Scale Merge Update Show Fixed Disable Merge Move Make Remove Remove Add Disable Merge Remove Rename Dismiss Don’t Removed Disable Don’t Dismiss Rename Updated Improved Further Made Add Made Updated Set Add Moved Always Made Merge Allow Update Decrement Simplify Increase Merge Add Add Merge Make Make Updated Implement Make Make Remember Fix Update Remember Add Remember Make Update Update Send Fix Enhance Use Change Merge Remove Correct Use Cleaned Left Improved Removed UI Show Centered Merge Made Fixed Use Update Add Update Remove Update Merge Reformat Remove Add Set Merge Update Merge Correct Update Add Fixed Change Add Wrap Update Update Tweak Revise Add Fix Fix Add Tweak Change Make Update Change Merge Allow Enable Updated Updated Merge Refactor Correct Remove Google Use Point Enable Prevent Remove Handle Enforce Update Authentication Update Merge Rework Enable Corrected Remove Correct Refactor Rename Rename Add Ensure Modify Enable Handle Decoding Refactor Update Validate Fix Clear Remove Make Merge Merge Update Update Remove Provide Made Bump Update Fix.</p>\n\n<p>Cleanup Cleanup Add Make Pop Add Prevent Fix Fix Fix Ensure Fix Remove Fix Move Revert Fixed Merge Resolved Fix Merge Fix escape Fix Merge NetNewsWire upgraded Fix Changed Add Update Make Updated Updated Change Request Show add Merge Update Update Unread Mark Convert Make Add Update Add Fix Dismiss Merge Added Merge Updated Revert Add Added Updated Updated Marking moved Add Use fixed Refactor adding trying fix Merge Add Merge adjusted Made Made Refactor Add Correct Add Update center add Add Merge Revert Add Added Merge Fixes Merge Need Updated Add Merge Merge submodules Merge Move Subscribing Rename Port Add Merge Opens Update Update Update Merge Move Add Add Disbale removed Merge Merge FIrst Change use Fixed Wiring Code Begin Update Update Only Update Merge Update Remove Skip Use Merge Merge Change Move Update Implement Cleanup Merge Merge Adds Merge Renamed Merge Updated some Merge Merge Merge Merge Renamed restores restores Restrict Corrects Merge Merge Changed Switches Adds Switches Rebrand Merge Update Ensure Reduce Update Make Support Low Set SF Merge SF Rolls Deletes Merge Implement Merge Retrieve Temporarily Move Force Navigate Correct Fix Fix Removed Move Temporally Add Change Rename Fix.</p>\n\n<p>Move Run Merge Update Add Encapsulate Move Move Move Remove Change Merge Write Update Update Make Update Add Note Remove Update Merge Update Make Merge Use Update Update Update Mark Make Merge Remove Make Delete Make Make Make Make Make Make Merge Update Use Update Merge Create Merge Merge Add Merge Add fiddle Merge Merge Use Merge Create Merge Updated Use Remove Merge Merge Create Merge Create Create Make Merge Create merge Merge Add Prevent Added Add Fix Hook Change Show Fix Initial Fix Merge Make Merge Help Remove Merge Remove Automatically Rename Change Merge Show Merge In Restore Merge Remove Merge Merge Make Merge Remove Trying Get Merge make Merge rename CLean Skip Merge Integrate Check Merge Merge Merge revert Revert Try Exempt Merge Helpbook: Merge Add Merge Prevent Merge fix Fix fix Merge Merge.</p>\n\n<p>Merge Refactor Make Add Remove Implemented Correctly Adjust Correct The Merge Merge Replace Merge Fix Fix Fix Remove Merge Made Keep Fix Wire Merge Fix Merge Enable Add Merge Fix Deleted Made Remove Fix Fix Merge Reformat Merge Refactor Fix Change Reenable Correct Removed Fix Fix Fix Helpbook: Merge Remove Merge Help Merge Change Merge Merge fix undo Bump Merge Add, Merge Merge Update Fix Merge Bump Merge Use Merge Break Merge Update Add Add Get Update Merge Bump Merge Update Bump Merge Now Merge Update Fix Rearrange Add Add Add Add Center Add Add Add Add Change Do Implement Add Merge Add Remove Move Add Merge Add Add Add Add Move Merge Commenting Fix Work Change fix Replace, Merge Start Port Change Optimize Remove Make Give Refetch Bump Merge Update Add Merge Hack Remove Add Fix Reload Center Center Center Bump Update Merge Bump Merge Add Leverage Merge add Change Add Update.</p>\n\n<p>Congratulations to Brent and others. <a href=\"https://ranchero.com/netnewswire/\">Enjoy NetNewsWire 5.0</a>…</p>\n\n",
			"summary": "From the Coding Guidelines of NetNewsWire:",
			"date_published": "2019-08-26T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/2019/08/17/Office-for-OS-X-icons",
			"url": "https://blog.zanstra.com/2019/08/17/Office-for-OS-X-icons.html",
			"title": "Office for OS X icons",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p><strong>Updated 2021-05-19</strong>: <em>added Big Sur icons.</em><br />\n<strong>Updated 2023-06-21</strong>: <em>Added 2023 “Show your pride”-variation (<a href=\"https://blogs.microsoft.com/blog/2022/06/01/microsoft-celebrates-pride-around-the-world-even-in-the-metaverse-as-we-donate-to-lgbtqia-nonprofits-release-xbox-pride-controller-and-more/\">2022 update apparently</a>)</em></p>\n\n<p>OS X, now called macOS, was based on the <a href=\"https://en.wikipedia.org/wiki/NeXTSTEP\">NeXTSTEP</a> operating system. And one innovation of this operating system was large full-color icons.</p>\n\n<p>With the introduction of new icons this year with Office 365 (including the 2019 retail version), I felt the need to create this:</p>\n\n<html>\n\t<figure tabindex=\"0\">\n\t\t<img src=\"/images/office-for-os-x-icons-2023.png\" alt=\"Office for OS X icons (2022 Pride update)\" class=\"non_hoover\" />\n\t\t<img src=\"/images/office-for-os-x-icons.png\" alt=\"Office for OS X icons (original)\" class=\"hoover\" />\n\t\t<figcaption>\n\t\t\tOffice for OS X icons 2022 Pride update <span class=\"non_print comment\">(focus or hover to display original)</span>\n\t\t</figcaption>\n\t</figure>\n</html>\n\n<p>In 2001, Office v. X was released. It’s design was very distinct from Windows, and also very <a href=\"https://en.wikipedia.org/wiki/Aqua_(user_interface)\">aqua-y</a>. The maximum size was 256×256 pixels. The 2004 icons I found were oddly sized 128×128 pixels. The resolution of the 2008 and 2011 icons went up to 512×512 pixels. These were also the last OS X-specific icons for Office.</p>\n\n<p>Starting with the 2016 version, the icons design of the Windows version was also used with macOS. And because of higher resolutions of displays, the dpi-count of the icons went up from 72dpi to 144dpi, making the 2016 and 2019 icons sized 1024×1024 pixels.</p>\n\n<p>Haven’t had enough? At the excellent Version Museum they have <a href=\"https://www.versionmuseum.com/history-of/microsoft-word\">all Windows icons of Word</a> including screenshots. They also have a page on <a href=\"https://www.versionmuseum.com/history-of/microsoft-excel\">Excel</a> .</p>\n\n",
			"summary": "Updated 2021-05-19: added Big Sur icons.Updated 2023-06-21: Added 2023 “Show your pride”-variation (2022 update apparently)",
			"date_published": "2019-08-17T00:00:00+02:00",
			"date_modified": "2023-06-21T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/2019/06/12/Rename-github-repo",
			"url": "https://blog.zanstra.com/2019/06/12/Rename-github-repo.html",
			"title": "On renaming a GitHub repository",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Did you know you can rename GitHub-repositories? There are some quirks though.</p>\n\n<p>Check it out: <a href=\"https://github.com/doekman/You-are-so-lame\">https://github.com/doekman/You-are-so-lame</a></p>\n",
			"summary": "Did you know you can rename GitHub-repositories? There are some quirks though.",
			"date_published": "2019-06-12T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/2019/05/06/Introducing-osagitfilter",
			"url": "https://blog.zanstra.com/2019/05/06/Introducing-osagitfilter.html",
			"title": "A third way of putting AppleScript into git",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>There are currently two ways of putting AppleScript into git. First is just by adding the binary <code class=\"language-plaintext highlighter-rouge\">.scpt</code>-files. You don’t get much benefit from using git, but it works.</p>\n\n<p>The <em>second way</em> is to save your AppleScript source as text-files, and put these into git. You get advantages from git, but it is not as seamless as the first way.</p>\n\n<h1 id=\"introducing-osagitfilter\">Introducing osagitfilter</h1>\n<p>Now you can combine those two ways by using the <a href=\"https://github.com/doekman/osagitfilter/\">osagitfilter utility</a>. Technically, it’s a <a href=\"https://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes#filters_a\">git filter</a> that translates AppleScript’s binary format into the text-representation which then will be used by git’s internal workings.</p>\n\n<p>See the <a href=\"https://github.com/doekman/osagitfilter/blob/master/README.md\">repository’s readme</a> for installation instructions. After that, you can put compiled script files, AppleScript applications and script bundles in git as if they where regular text-files. When you then clone this repository, the files are re-assembled bit-perfect.</p>\n\n<p>Let me demonstrate this by an example. First create a git-repository:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>git init osagitfilter-demo\ncd osagitfilter-demo\n</code></pre></div></div>\n\n<p>Now create the AppleScript-file <code class=\"language-plaintext highlighter-rouge\">my_script.scpt</code> in this folder with the following contents:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>display dialog \"What's your name\" default answer \"\"\nsay \"Hi there, \" &amp; text returned of result\n</code></pre></div></div>\n\n<p>Since we don’t want to add a binary file to git, we first need to associate the <code class=\"language-plaintext highlighter-rouge\">.scpt</code>-extension with the osagitfilter (you need to explicitly <em>opt-in</em> every repository). This can be done by adding a line to the <code class=\"language-plaintext highlighter-rouge\">.gitattributes</code>-file, connecting the <code class=\"language-plaintext highlighter-rouge\">.scpt</code>-extension to the <code class=\"language-plaintext highlighter-rouge\">osa</code> filter:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>echo \"*.scpt filter=osa\" &gt;&gt; .gitattributes\n</code></pre></div></div>\n\n<p>Now the files can be added to git:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>git add .gitattributes my_script.scpt\ngit commit -m Initial\n</code></pre></div></div>\n\n<p>Now let’s change the script by appending <code class=\"language-plaintext highlighter-rouge\">&amp; \", I like your name\"</code> to the say command in the <code class=\"language-plaintext highlighter-rouge\">my-script.scpt</code> file. Don’t forget to save.</p>\n\n<p>Now when you run <code class=\"language-plaintext highlighter-rouge\">git diff</code>, you can see the changes you made as you would with text-files. You don’t need to stick with the command-line: I can confirm it works with GitHub Desktop. It should also work with other GUI’s, but I haven’t tested this.</p>\n\n<h1 id=\"not-only-applescript\">Not only AppleScript</h1>\n<p>The program is called <a href=\"https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptX/Concepts/osa.html\" title=\"The Open Scripting Architecture (OSA) provides a standard and extensible mechanism for interapplication communication in OS X.\">osa</a>gitfilter, so you can also add JavaScript <code class=\"language-plaintext highlighter-rouge\">.scpt</code>-files. There is also a feature that prevents you from accidently adding AppleScript-debugger files to git (a special file format used by the indispensible <a href=\"scriptdebugger\">Script Debugger</a>).</p>\n\n<p>I hope osagitfilter will be useful tool. It will not completely replace the first two methods, but it’s nice to have an alternative to them.</p>\n\n<p>Let me know what you think on <a href=\"https://twitter.com/doekezanstra/status/1125696216573140993\">twitter</a> or <a href=\"https://forum.latenightsw.com/t/a-third-way-of-putting-applescript-into-git/1932?u=doekman\">at this thread</a> on Late Night Software’s AppleScript forum.</p>\n\n",
			"summary": "There are currently two ways of putting AppleScript into git. First is just by adding the binary .scpt-files. You don’t get much benefit from using git, but it works.",
			"date_published": "2019-05-06T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/2019/03/08/Git-Url",
			"url": "https://blog.zanstra.com/2019/03/08/Git-Url.html",
			"title": "git-url",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>A while ago, <a href=\"https://gist.github.com/doekman/45acdb0ceedd9dc9dc6105d0b058b06a\">I created a shell script</a> called <code class=\"language-plaintext highlighter-rouge\">git-url</code>. It prints the remote origin URL of your current git-repository (or exit 1 if no git-repo is found). If the URL is in <code class=\"language-plaintext highlighter-rouge\">git:</code> format, it is converted to <code class=\"language-plaintext highlighter-rouge\">http:</code> format.</p>\n\n<p>Why would one write this? Because of <code>open `git-url`</code>.</p>\n\n<p>This will open the GitHub- or BitBucket-repository in your favorite browser. I use it all the time!</p>\n\n<p>Above works on macOS. Windows uses <code class=\"language-plaintext highlighter-rouge\">start</code> instead of <code class=\"language-plaintext highlighter-rouge\">open</code>, but I don’t know how it works with Windows Subsystem for Linux.</p>\n\n<p><strong><em>Updated 2019-12-04</em></strong>: you now can add a SHA1 hash to go directly to that commit in the browser (GitHub and BitBucket) like\n<code>open `git-url 7593e1a`</code>, which <a href=\"https://github.com/doekman/doekman.github.io/commit/7593e1a631df4769853948b31b610a13a4525665\">opens this link</a>).</p>\n\n",
			"summary": "A while ago, I created a shell script called git-url. It prints the remote origin URL of your current git-repository (or exit 1 if no git-repo is found). If the URL is in git: format, it is converted to http: format.",
			"date_published": "2019-03-08T00:00:00+01:00",
			"tags": ["cli"]
		},
		 {
			"id": "/2018/12/31/iOS_ethernet",
			"url": "https://blog.zanstra.com/2018/12/31/iOS_ethernet.html",
			"title": "Networking with iOS via Ethernet",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>This summer I went to <a href=\"https://www.ecolonie.eu/en\">Ecolonie</a>, a very nice place in <strong>France</strong>. It is an idealistic place that doesn’t want to exclude anyone.\nSo they don’t allow digital wireless connections like Wifi and Bluetooth, because this can make some people sick.</p>\n\n<p>So I thought of connecting my iPad via Ethernet to internet.\n<a href=\"https://www.lifewire.com/connect-ipad-to-wired-ethernet-port-1994242\">I’m not</a> <a href=\"https://sixcolors.com/post/2016/07/ios-10-a-place-for-ethernet/\">the first one</a> <a href=\"https://9to5mac.com/2017/03/01/ios-10-2-ethernet-adapter-ui-settings-app/\">to try this</a>, but I wanted to share my experiences.\nI first bought the <a href=\"https://www.apple.com/nl/shop/product/MK0W2ZM/A/lightning-naar-usb-3-camera-adapter?fnode=5a77c06a31d0e5022a688a2defec8127910a5c967ad27547e77a7424219ab7f0a55463cd85fcdd333d6606c4fa120dfcc8c4531e057b6b3771cf23ba049aaa7807f3e771f4c29345bec09a21c843c02ccb64fe33ce5ec4bf8ae31ebbf3f1d902\">Apple Lightning to USB 3 camera adapter</a> (you can get cheaper clones, but non-Apple periferals can suffer from some kind of compatiblity issue).\nNext was a <a href=\"https://www.amazon.de/gp/product/B00QWYJSUG/ref=oh_aui_detailpage_o00_s01?ie=UTF8&amp;psc=1\">powered USB hub</a> from the Aukey, which already has an ethernet LAN adapter build in!\nSo there’s no need to buy an external USB Ethernet Adapter.</p>\n\n<p>This setup works (almost) perfectly with both iPhone 5 (iOS 10.3.3), iPhone SE and iPad Air 2 (both iOS 11.4.1).\nI didn’t test my original iPad, since I don’t have a camera adapter with a <a href=\"https://en.wikipedia.org/wiki/Dock_connector\">dock connector</a>.</p>\n\n<p>When connecting the setup, you’ll get an entry between Wifi and Bluetooth in the settings menu, where you can check your TCP/IP settings.\nWhen I plugged in my Apple USB Ethernet Adapter, the Ethernet menu shows two adapters (also one for the build-in Aukey ethernet).\nLike in macOS, it will prioritize the adapters automatically and choose the “best” one.\nI performed some speed tests, and I didn’t find significant differences between Wifi and Ethernet.</p>\n\n<p>I found one downside which stops it from working perfectly. I switch on Airplane Mode to make sure no digital wireless signals were emitted.\nHowever, in that case for almost every app you open, you’ll get a warning to switch off Airplane Mode.\nLuckily these warnings can be dismissed without any problems, and bytes will happily flow between your device and the internet.</p>\n\n<p>Other findings: I charged by iPad via a cable between the lightning connector in the Camera Adapter and the fast-charge port in the USB hub.\nThis caused noise on the speaker I had connected to the iPad via the 3½mm audio jack.\nThe noise disappeared when removing the charging cable from the USB hub and did not return when connecting to iPad’s charging adapter.</p>\n\n<p>One final note on USB Ethernet adapters: naturally they have built-in MAC-addresses. But you can expect some complications on your network when you use a MAC-addresses to identify all allowed devices.\nDevices, like in Lisa’s tablet and Bart’s iPhone… D’oh!</p>\n\n",
			"summary": "This summer I went to Ecolonie, a very nice place in France. It is an idealistic place that doesn’t want to exclude anyone.So they don’t allow digital wireless connections like Wifi and Bluetooth, because this can make some people sick.",
			"date_published": "2018-12-31T00:00:00+01:00",
			"tags": ["does-it-work"]
		},
		 {
			"id": "/2018/09/13/Jump-around",
			"url": "https://blog.zanstra.com/2018/09/13/Jump-around.html",
			"title": "Jump around",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><style>code {white-space:nowrap;}</style>\n\n<p>There are more ways <a href=\"https://scriptingosx.com/2017/08/navigating-the-file-system/\">to navigate the file system</a> in <em>bash</em> than I imagined when I still was using <em>cmd.exe</em>. Let me name some:</p>\n\n<ul>\n  <li><code class=\"language-plaintext highlighter-rouge\">cd</code> navigates to the home folder. Like <code class=\"language-plaintext highlighter-rouge\">cd /users/$(whoami)</code> or <code class=\"language-plaintext highlighter-rouge\">cd \"$HOME\"</code> but shorter.</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">cd -</code> navigates to the previous directory. It’s like the <em>zap-button</em> on your television remote control!</li>\n  <li><code>cd ~/Doc<span title=\"HORIZONTAL TABULATION\">␉</span></code> <em>tab-completion</em> works like Visual Studio’s intelli-sense, but without the dropdown and it’s case sensitive. If there is one choice, it will complete it. If there is more, you need to press <abbr title=\"␉ or Horizontal Tab\">tab</abbr> twice, and it will show a list.</li>\n  <li><code class=\"language-plaintext highlighter-rouge\">pushd</code>/<code class=\"language-plaintext highlighter-rouge\">popd</code>/<code class=\"language-plaintext highlighter-rouge\">dirs</code> save/load/show a directory to/from the stack. Especially handy in complex scripts.</li>\n</ul>\n\n<p>However, I noticed I was always navigating to not that many different number of folders. I thought, wouldn’t it be handy if one could save/load folders to some kind of <a href=\"https://en.wikipedia.org/wiki/Associative_array\" title=\"In computer science, an associative array, map, symbol table, or dictionary is an abstract data type composed of a collection of (key, value) pairs, such that each possible key appears at most once in the collection\">dictionary</a>. I did extensive research and couldn’t find anything, so I created <a href=\"https://github.com/doekman/go\">go</a>. It was great. Typing <code class=\"language-plaintext highlighter-rouge\">go gh</code> performed <code class=\"language-plaintext highlighter-rouge\">cd ~/prj/GitHub</code>. To save the current folder under the name <em>“here”</em> would be performed by <code class=\"language-plaintext highlighter-rouge\">go here .</code>. And <code class=\"language-plaintext highlighter-rouge\">go</code> would show all the current stored definitions.</p>\n\n<p>As I mentioned before, I performed thorough extensive research. I didn’t take it lightly. However. Apperently, the subject sometimes needs time to sink in. As today I learned from <a href=\"http://www.secretgeek.net/pp2018\">a guy I have known some time</a>, that he created a tool called <a href=\"https://github.com/secretGeek/markjump\">markjump</a>. This is basically what I made, only for <em>powershell</em>. But his work was inspired by <a href=\"http://jeroenjanssens.com/2013/08/16/quickly-navigate-your-filesystem-from-the-command-line.html\">Jeroen Janssens’</a> <em>shell script</em>, from five years ago. And he’s a Dutchie too! How could I have missed that? And his code is 1500% better!</p>\n\n<p>To complete my story line: Half a year ago I ran into the <a href=\"https://github.com/rupa/z/\">z-utility</a> (can’t remember how), which has the description <a href=\"https://en.wikipedia.org/wiki/Jump_Around\">jump</a> <a href=\"https://www.youtube.com/watch?v=XhzpxjuwZy0\">around</a>. I was intrigued! I learnt descriptions of utilities are pretty important. And it turns out to be a pretty clever and helpful utility. It automatically remembers where you navigate to; no need to keep a list manually. And it will also keep <em>statistics</em>.</p>\n\n<p>It uses <em>regular expressions</em> to find which folder you want to navigate to. And it’s case-insensitive, so <code class=\"language-plaintext highlighter-rouge\">z dow</code> will navigate to <code class=\"language-plaintext highlighter-rouge\">~/Downloads</code>. No need to type a capital-D. When the regular expression matches multiple folders, it uses the statistics (how recent and how frequent) to decide where to navigate to.</p>\n\n<p>I must say I was a bit hesitant to use it, because it seemed so… technical. In the beginning of the man-page it says something like <em>“Tracks your most used directories, based on ‘frecency’..”</em> But the <em>“jump around”</em> reeled me in. I never used my own <code class=\"language-plaintext highlighter-rouge\">go</code>-utility, and only just now looked back. Thanks <a href=\"https://github.com/rupa\">rupa</a>!</p>\n\n",
			"summary": "",
			"date_published": "2018-09-13T00:00:00+02:00",
			"tags": ["cli"]
		},
		 {
			"id": "/2018/08/28/Missing-emoji",
			"url": "https://blog.zanstra.com/2018/08/28/Missing-emoji.html",
			"title": "Emoji's missing from emojis.wiki",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>The following entries are supposedly missing from <a href=\"https://emojis.wiki\">emojis.wiki</a>. However, you could also read this article as a proposal for alternate descriptions of a part of the <a href=\"https://en.wikipedia.org/wiki/Miscellaneous_Technical#(2340–237F)\">Miscellaneous Technical range</a> of the <a href=\"https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane\">Basic Multilingual Plane</a> of the <a href=\"https://en.wikipedia.org/wiki/Unicode\">Unicode</a> standard.</p>\n\n<style>\n\tbig  { font-size:150%; line-height:100%; cursor:help;}\n</style>\n\n<ul style=\"list-style-type:none\">\n\t<li title=\"U+2361: APL FUNCTIONAL SYMBOL UP TACK DIAERESIS\">     <big>⍡</big> Chin on fist on arm</li>\n\t<li title=\"U+2362: APL FUNCTIONAL SYMBOL DEL DIAERESIS\">         <big>⍢</big> Super happy</li>\n\t<li title=\"U+2363: APL FUNCTIONAL SYMBOL STAR DIAERESIS\">        <big>⍣</big> Moustache concealing mouth</li>\n\t<li title=\"U+2364: APL FUNCTIONAL SYMBOL JOT DIAERESIS\">         <big>⍤</big> Disbelieve</li>\n\t<li title=\"U+2365: APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS\">      <big>⍥</big> Big surprise</li>\n\t<li title=\"U+2368: APL FUNCTIONAL SYMBOL TILDE DIAERESIS\">       <big>⍨</big> Unsure</li>\n\t<li title=\"U+2369: APL FUNCTIONAL SYMBOL GREATER-THAN DIAERESIS\"><big>⍩</big> Talking frolicsome</li>\n</ul>\n\n<p>If you have any corrections or addendums, please let me know on Twitter.</p>\n\n",
			"summary": "The following entries are supposedly missing from emojis.wiki. However, you could also read this article as a proposal for alternate descriptions of a part of the Miscellaneous Technical range of the Basic Multilingual Plane of the Unicode standard.",
			"date_published": "2018-08-28T00:00:00+02:00",
			"tags": []
		},
		 {
			"id": "/2018/07/08/Vowel-case",
			"url": "https://blog.zanstra.com/2018/07/08/Vowel-case.html",
			"title": "To vowel case",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>There already was <em>Sentence case</em>, <em>lower case</em>, <em>UPPER CASE</em>, <em>Capitalized Case</em>, <em>AlTeRnAtInG CaSe</em> and <em>Title Case</em>.</p>\n\n<p><strong>And nOw YOU cAn dO vOwEr cAsE tOO</strong>!</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function toVowelCase(text) {\n  return text.toLowerCase().replace(/[aeiouy]+/g, match =&gt; match.toUpperCase());\n}\n</code></pre></div></div>\n\n<p><strong>Updated</strong>: <em>a correct implementation of <code class=\"language-plaintext highlighter-rouge\">toVowelCase</code> is locale-dependend. In English, the letter <code class=\"language-plaintext highlighter-rouge\">y</code> is sometimes a vowel, and at other times a consonant.  The above works for the Dutch language and some other languages. Your mileage may vary.</em></p>\n\n<p><strong>Updated</strong>: bookmarking <a href=\"https://headlinecapitalization.com\">https://headlinecapitalization.com</a> and <a href=\"https://titlecaseconverter.com\">https://titlecaseconverter.com</a>.</p>\n",
			"summary": "There already was Sentence case, lower case, UPPER CASE, Capitalized Case, AlTeRnAtInG CaSe and Title Case.",
			"date_published": "2018-07-08T00:00:00+02:00",
			"tags": ["one-liner"]
		},
		 {
			"id": "/2018/06/29/Refactoring",
			"url": "https://blog.zanstra.com/2018/06/29/Refactoring.html",
			"title": "To refactor, or not to refactor",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>The code iteration below is a contemporary comment on the use of refactoring.<br />\nWe start with this code:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function isLeapYear(year) {\n  if (year % 4) {\n    return false;\n  } else {\n    if (year % 100) {\n      return true;\n    } else {\n      if (year % 400)\n        return false;\n      else return true;\n    }\n  }\n}\n</code></pre></div></div>\n\n<p>All code has to be <a href=\"https://github.com/jasmine/jasmine\">unit tested</a>, so we write this (<em>normally I start with this before writing actual code, but you know…</em>):</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function unitTests() {\n  describe(\"leapYear tests\", function() {\n    expect(isLeapYear(1900)).toBe(false);\n    expect(isLeapYear(1971)).toBe(false);\n    expect(isLeapYear(1972)).toBe(true);\n    expect(isLeapYear(2000)).toBe(true);\n    expect(isLeapYear(2003)).toBe(false);\n  });\n}\n</code></pre></div></div>\n\n<p>And to stay very agile, I wrote some minimal unit testing “library” (<a href=\"http://agilemanifesto.org\">individuals over tools</a>, anyone?):</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function describe(testName, tests) {\n    var groupTitle = 'Name: '+testName+' - Start: '+(new Date).toISOString();\n    console.group(groupTitle);\n    expect.nr = 0;\n    expect.errors = 0;\n    tests();\n    if (expect.errors == 0) console.info('All %s tests OK', expect.nr);\n    else console.error('%s of %s tests failed', expect.errors, expect.nr);\n    console.groupEnd(groupTitle);\n}\n\nfunction expect(x) {\n    return {\n        toBe: function(y) {\n            if (x === y) console.info(\"%s: OK 👍\", expect.nr++);\n            else console.error(\"%s: Error 👹\", expect.nr++), expect.errors++;\n        }\n    };\n}\n</code></pre></div></div>\n\n<p>The unit tests all passed. Can’t wait to start with refactor step 1: removing unnecessary nesting. One step at a time.</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function isLeapYear(year) {\n    if (year % 4) {\n        return false;\n    }\n    if (year % 100) {\n        return true;\n    }\n    if (year % 400) {\n        return false;\n    }\n    return true;\n}\n</code></pre></div></div>\n\n<p>Yes…, much better! All tests pass. Refactor step 2, fold down into one if:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function isLeapYear(year) {\n    if (year % 4 || (!(year % 100) &amp;&amp; year % 400)) {\n        return false;\n    }\n    return true;\n}\n</code></pre></div></div>\n\n<p>And for the heck of it, remove the unnecessary if as well:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function isLeapYear(year) {\n    return !(year % 4 || (!(year % 100) &amp;&amp; year % 4000));\n}\n</code></pre></div></div>\n\n<p>Oopsy. The fourth test failed. Just a typo, no worries. And since there’s already too much negativity around, I’ll fix that in the next iteration, and while we are at it, move the negation to another function.</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function isLeapYear(year) {\n    return !isCommonYear(year);\n}\nfunction isCommonYear(year) {\n    return year % 4 || (!(year % 100) &amp;&amp; year % 400);\n}\n</code></pre></div></div>\n\n<p>The committee unanimously decided: don’t rely on javascript’s truthiness, we want explicit code! So fixing that:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function isLeapYear(year) {\n    return !isCommonYear(year);\n}\nfunction isCommonYear(year) {\n    return year % 4 != 0 || (!(year % 100 != 0) &amp;&amp; year % 400 != 0);\n}\n</code></pre></div></div>\n\n<p>Refactor step 6, the overload of negativity was back. Fixing:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function isLeapYear(year) {\n    return !isCommonYear(year);\n}\nfunction isCommonYear(year) {\n    return year % 4 != 0 || (year % 100 == 0 &amp;&amp; year % 400 != 0);\n}\n</code></pre></div></div>\n\n<p>Refactor step 7 should be a charm. I still don’t like the code at all, so f$*# it, just use the native function:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>function isLeapYear(year) {\n    return new Date(year, 2 - 1, 29).getDate() == 29;\n}\n</code></pre></div></div>\n\n<p>Done!</p>\n\n<p>Code can also be found <a href=\"https://github.com/doekman/to-refactor...\">here on GitHub</a>.</p>\n\n",
			"summary": "The code iteration below is a contemporary comment on the use of refactoring.We start with this code:",
			"date_published": "2018-06-29T00:00:00+02:00",
			"tags": ["refactor"]
		},
		 {
			"id": "/2017/05/19/JSON-feed",
			"url": "https://blog.zanstra.com/2017/05/19/JSON-feed.html",
			"title": "Jekyll template for JSON Feed",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>When I learned about <a href=\"https://jsonfeed.org/2017/05/17/announcing_json_feed\">JSON Feed</a>, I immediately liked the idea. It’s like RSS/Atom, with the good parts (it’s de-central) and without the bad parts (no more XML, well thought of standard attributes).</p>\n\n<p>I’m not to sure how it will be used in practise, but it was easy enough to create a Jekyll template (<a href=\"https://github.com/doekman/doekman.github.io/blob/master/feed.json\" title=\"Source code on GitHub\">source on GitHub</a>). I learned it’s handy to add a <code class=\"language-plaintext highlighter-rouge\">base</code>-tag to the <code class=\"language-plaintext highlighter-rouge\">content_html</code>-attribute, after trying <a href=\"http://json-feed-viewer.herokuapp.com\">this app</a>.</p>\n\n<p>And I made myself a <a href=\"http://json-feed-viewer.herokuapp.com/?feed_url=http%3A%2F%2Fblog.zanstra.com%2Ffeed.json\">nice square logo</a> too.</p>\n\n",
			"summary": "When I learned about JSON Feed, I immediately liked the idea. It’s like RSS/Atom, with the good parts (it’s de-central) and without the bad parts (no more XML, well thought of standard attributes).",
			"date_published": "2017-05-19T00:00:00+02:00",
			"tags": ["meta"]
		},
		 {
			"id": "/2017/05/14/JSON-the-game-nr2",
			"url": "https://blog.zanstra.com/2017/05/14/JSON-the-game-nr2.html",
			"title": "JSON, the game #2: The parsings",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>For some reason, I imagined the next step in the development of the game was to make the ‘block’ move. Like up and down, and sliding to the left. For <a href=\"http://agilemanifesto.org\">agile</a> reasons, I want to make progress that is reflected and visible in the software. So no spending time at writing libraries first. You probably end up writing stuff you don’t even need. But when starting, I realized the partial-JSON part needed to be worked on first.</p>\n\n<p>To display the partial-JSON, I need a <em>model</em> of that JSON. That model should be capable of annotating the partial-JSON with grammar issues (like missing bracket/comma, incomplete strings or just plain errors). Indentation should be addressed too. Also some dimension-data is needed, so we can determine whether the block can move up/down and when it has reached the partial-JSON surface (sorry, I can’t stop thinking in planet metaphors).</p>\n\n<p>When pondering on these requirements, I realized a tokenizer/parser was necessary. A library! Not very agile, right? Perhaps skipping the grammar issues first and add it later would have been a wiser choice. We will never know. Writing a parser does takes some time, but it <a href=\"http://zanstra.com/my/RegexMate.html\">sure</a> <a href=\"https://cdn.rawgit.com/doekman/base2/7466e0d79d4f049dfb07e44b78f9e4c23a2ec9f0/src/utils/Parsers/samples/Uri.html\">is</a> <a href=\"https://cdn.rawgit.com/doekman/base2/7466e0d79d4f049dfb07e44b78f9e4c23a2ec9f0/src/utils/Parsers/samples/GWParser.html\">fun</a>.</p>\n\n<p>First I started writing a <a href=\"https://github.com/doekman/Json-the-Game/blob/61fcc2afed7bc6647ebd07510dd8af5f59d5960a/json_parser.js\" title=\"Source-code at GitHub, first version\">tokenizer</a>. And since it’s not your average code, I used the most annoying coding convention I could think of. I learned this style with Turbo Pascal in my BBS days. Years later I discovered you can use it with languages like JavaScript, CSS, C# and even PL/SQL. But it’s too annoying for everyday use. Not only do you get <em>The Eye</em> from your colleagues; every editor I’ve tried actively works against you by messing up the indentation. Again, and again. With. Every. Single. Edit.</p>\n\n<p>The tokenizer splits the JSON into tokens like a string, boolean value or comma. With the parser we then can reason about tokens instead of characters, which makes life much simpler. Because we work with partial JSON, the tokenizer is made to recognizes unfinished strings and other values. Every token has an <code class=\"language-plaintext highlighter-rouge\">is_complete</code>-attribute, so when rendering we can show if a brace is missing, or a string is unfinished.</p>\n\n<html><center style=\"margin-bottom:1em\"><img src=\"/images/json-state-machine.jpg\" alt=\"HTML/CSS mock\" title=\"HTML/CSS mock: the 'ue' token will go from the right to the left\" /></center></html>\n\n<p>The parser was a dragon to me. I made a lot of diagrams to create a <a href=\"https://en.wikipedia.org/wiki/Finite-state_machine\">state machine</a>, see above, and played with <a href=\"http://webgraphviz.com\">GraphViz</a> (which is pretty cool, and a little weird). Finally I thought the state machine was right, and started coding. After writing some unit tests and identifying some failures, I did realize I went in the wrong direction. A state machine is apparently not the best solution to parse JSON and the answer was right in front of me. While I was focussing on the  nice flow diagram at <a href=\"http://json.org\">json.org</a> (which I coincidently also knew from the Turbo Pascal manual; I know, grandpa speaking), the program I needed to write was listed as an inset on the right of that same web page! <code class=\"language-plaintext highlighter-rouge\">object := {} | { members }</code>… Elementary, dear Watson…</p>\n\n<p>A well, <a href=\"https://www.youtube.com/watch?v=AWwQsdHMT7Y\" title=\"Adventures in Failure, by MC 900ft Jesus\">adventures in failure</a>. Start all over, and everything will be fine. At the moment, I have a validating parser (<a href=\"https://github.com/doekman/Json-the-Game/tree/c6f9cdfd302df48813c9a1f86a85d53b45f68303\" title=\"Tokenizer &amp; validating parser\">source here</a>). It says whether the JSON is correct or not. There are still loose ends, which I will fix when needed. For example, I ignored Unicode for a big part and focussed on ASCII for now.</p>\n\n<p>On a side note: I also wanted to check out those new <a href=\"http://es6-features.org/\">EcmaScript 2016 features</a>, since every browser seems to be <a href=\"http://kangax.github.io/compat-table/es6/\">supporting them</a>. It’s nice to see the language is evolving, after multiple timespans of stagnation. The best feature IMHO is string interpolation.</p>\n\n<p>Next step is adding code to the parser for annotating tokens, and insert <code class=\"language-plaintext highlighter-rouge\">missing</code>-tokens. And render the thing in HTML naturally.</p>\n\n",
			"summary": "For some reason, I imagined the next step in the development of the game was to make the ‘block’ move. Like up and down, and sliding to the left. For agile reasons, I want to make progress that is reflected and visible in the software. So no spending time at writing libraries first. You probably end up writing stuff you don’t even need. But when starting, I realized the partial-JSON part needed to be worked on first.",
			"date_published": "2017-05-14T00:00:00+02:00",
			"tags": ["game"]
		},
		 {
			"id": "/2017/04/30/JSON-the-game-nr1",
			"url": "https://blog.zanstra.com/2017/04/30/JSON-the-game-nr1.html",
			"title": "JSON, the game #1: Bootstrapping it",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Around a year ago, I had the daft idea to create some nerdy JSON-game. I had good fun thinking about some concepts, made some notes, and that was about it. Until now that is. The game: It’s coming… It has already started. To keep me focussed, I will keep a journal of the creation, and you’re reading the first part.</p>\n\n<p>I got the ideas from series like the Brent Simmons’s <a href=\"http://inessential.com/vespersyncdiary\">Vesper Sync Diary</a> and Jon Skeet’s <a href=\"https://codeblog.jonskeet.uk/2011/02/23/reimplementing-linq-to-objects-part-45-conclusion-and-list-of-posts/\">Reimplementing Linq to Objects</a>. I found them enjoyable; a bit of a nerdy soap opera. And now is the time for me to get of my comfy sofa and produce some soap of my own.</p>\n\n<p>The idea of the game: <a href=\"http://json.org\">JSON</a> tokens are coming in, and you need to place them correctly. The initial form of the game will be Tetris-like with your head on your right shoulder: the tokens come in from the right and go to the left (alternative gravity you can think). However, this form might change from level to level (I’m thinking of the partial JSON document as a starship, crashing into JSON stars/tokens as it cruises through the JSON universe).</p>\n\n<p>The first step is to create some user interface mockup. A guy gotta start somewhere. My weapon of choice is HTML/CSS. At the top we have a dashboard providing some game details. Unformatted JSON will do for now. The playing field (where the partial JSON is) will have a fixed width, while the height might grow during gameplay when tokens are inserted between lines.</p>\n\n<html><center style=\"margin-bottom:1em\"><img src=\"/images/json-html-mock.png\" alt=\"HTML/CSS mock\" title=\"HTML/CSS mock: the 'ue' token will go from the right to the left\" /></center></html>\n\n<p>Positioning is done with the <code class=\"language-plaintext highlighter-rouge\">em</code> and <code class=\"language-plaintext highlighter-rouge\">ch</code> CSS units with a position <code class=\"language-plaintext highlighter-rouge\">relative</code>/<code class=\"language-plaintext highlighter-rouge\">absolute</code> container-combo, so I can  position things absolute within the relative container. Every character is one <code class=\"language-plaintext highlighter-rouge\">ch</code> and every line is one <code class=\"language-plaintext highlighter-rouge\">em</code>. One little problem though: <code class=\"language-plaintext highlighter-rouge\">em</code> is font-height, not line-height. And a line-height of 100% just doesn’t look right. But there is no such thing as a line-height unit. (<a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/length#Font-relative_lengths\" title=\"Line-height unit ln; unimplemented with an unknown specification\">Or is there?</a>) This is easy fixable with a calculation, but line-height will appear in multiple places in CSS.</p>\n\n<p>Also some color coding needs to be added, so we can identify unbalanced grouping tokens, missing separators, incomplete tokens and downright errors. That’s why I’m not using a <code class=\"language-plaintext highlighter-rouge\">pre</code>-element. I don’t have a definite idea about gameplay, but some ideas come to mind. An obvious solution for an error would be “Game over”. This can also be postponed by counting “lives”. The error will vanish and you loose a life. Or an error could be cleared with the incoming JSON token <code class=\"language-plaintext highlighter-rouge\">\\b</code>. That choice will depend on the level. But I digress.</p>\n\n<p>This is not all markup we need (we will need a dialog-box, for the start and end of a level), but it will do for now. I put the <a href=\"https://github.com/doekman/Json-the-Game/tree/8f14c49ec871a35cbb85d2d0b0e751ea054489c5\">source-code on GitHub</a>, and every instalment will point to a commit so you can see how this game comes to shape. You can also <a href=\"https://cdn.rawgit.com/doekman/Json-the-Game/8f14c49ec871a35cbb85d2d0b0e751ea054489c5/index.html\">“run” the game here</a> (nothing to play for now).</p>\n\n<p>I hope to find a good balance between enjoyability, a bit of fun and some technical remarks. And I like to hear your thoughts on <a href=\"https://twitter.com/doekezanstra\">Twitter</a>.</p>\n\n<p>The next instalment will be about moving those incoming tokens.</p>\n\n",
			"summary": "Around a year ago, I had the daft idea to create some nerdy JSON-game. I had good fun thinking about some concepts, made some notes, and that was about it. Until now that is. The game: It’s coming… It has already started. To keep me focussed, I will keep a journal of the creation, and you’re reading the first part.",
			"date_published": "2017-04-30T00:00:00+02:00",
			"tags": ["game"]
		},
		 {
			"id": "/2016/09/08/Only-my-warnings",
			"url": "https://blog.zanstra.com/2016/09/08/Only-my-warnings.html",
			"title": "Only my warnings",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Don’t you hate to be put in a situation like the following?</p>\n\n<blockquote>\n  <p>You are assigned by your company to add some functionality to some software at\nsome client. The client has standards and practices in place stating you can’t checkin code that produces warnings, only to find\nout that a clean checkout produces loads of warnings when building!</p>\n\n  <p>But you can’t fix <span style=\"font-style:normal\">😢</span> those warnings. You, the super star programmer™ (whose boss is getting\npaid big dollars for high quality, virtually bug-free software), cannot get rid of those pesky lines of inability…</p>\n\n  <p>For starters, why judge those warnings in the first place. You’re just the new guy, you don’t want to get into any trouble. Also, \nby solving warnings, you may introduce new bugs (even though you’re super competent, after all creating bugs is just a matter of probability). \nNobody wants that! And finally, you’re a professional. The client is only paying for the new functionality, not for you to clean\nup the place. And about your collegues, you don’t want to hurt anyones feelings. \nSo in short: the man says no. No way fixing those warnings, José!</p>\n</blockquote>\n\n<p>Situations like these inspired us to envision a new kind of solution to those issues at hand. Let me introduce to you, without further ado, the Visual Studio extension <strong>Only My Warnings</strong>.</p>\n\n<p>When your solution is under source control a new list button is added to the Error List pane, with the options <em>All</em>, <em>Only Mine</em> and <em>Local Only</em>. \nWith the option <em>Local Only</em> selected, the pane will only show errors, warnings and messages caused by local changes, i.e. by you!\nAll other messages are hidden, so you don’t have to deal with them. Victory!</p>\n\n<p><img src=\"/images/Only-my-warnings_error-list.png\" alt=\"screen shot\" title=\"Error List window from Visual Studio 2015 Community Edition\" /></p>\n\n<p>The extension doesn’t get in the way; it only adds this one list button.\nWhen selecting <em>Only Mine</em>-option, the tool will gather all messages from your checkins, and show only the messages caused by you. \nAnd it follows branches, so you can focus at the feature that is imporant at any given moment.</p>\n\n<p>Now you can be productive, and still have a chat with your collegues without having an argument! While using this extension, you\nwill contribute to your team’s productivity. You will be a team productivity enabler! That’s how 10× developers are bred!\nIn a way, <em>only my warnings</em> contributes to world peace.</p>\n\n<p>For now, the extension is still in its early alpha phase. If you want to participate in the beta-program, or want to get notified\nwhen the extention will be released to the Visual Studio Gallery, please \n<a href=\"https://docs.google.com/forms/d/e/1FAIpQLScMYh6JqrQn-dEA8hzIGJdJsV5-9ufCuTkfczPeXlBjNoB7KA/viewform\">fill out this form</a>.</p>\n\n<p>(this is a blog post in the series: I’m perfect, communicate better with tools)</p>\n",
			"summary": "Don’t you hate to be put in a situation like the following?",
			"date_published": "2016-09-08T00:00:00+02:00",
			"tags": ["tools"]
		},
		 {
			"id": "/2016/03/06/Console-log-adventure",
			"url": "https://blog.zanstra.com/2016/03/06/Console-log-adventure.html",
			"title": "A console.log() adventure; pimped",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>Over a year ago, my interwebfriend <a href=\"https://twitter.com/secretGeek\">secretGeek</a> created a thing. I created a fork, and pimped it <a href=\"https://rawgit.com/doekman/console-adventure/master/console.html\">into this</a> (<em>and copied some of his blog lines too</em>). I urge you to go and try it out before you read on. Unfortunately you’ll need to be on a desktop computer, not a mobile phone.</p>\n\n<p><a href=\"https://rawgit.com/doekman/console-adventure/master/console.html\"><img src=\"/images/console_best.png\" style=\"border:8px solid #333;box-shadow:3px 3px 7px #888\" title=\"a console.log() adventure.\" /></a></p>\n\n<p><a href=\"https://rawgit.com/doekman/console-adventure/master/console.html\">Go there now</a>.</p>\n\n<p>In case you don’t have a desktop computer anymore (woah, futuristic!), or have already tried it out, I’ll give some spoilers and discussion now.</p>\n\n<h1 id=\"spoilers\">Spoilers</h1>\n\n<p>Actually, I’ll give some space before the spoilers. Scroll now for spoilers.</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>↓ spoilers ahead ↓</p>\n\n<p>Here is <a href=\"http://www.secretgeek.net/console_log\">secretGeek’s blog-entry</a>. When playing the game I had loads of fun. \nKeeping a map on graph paper, just to make sure the map is deterministic and doesn’t use some random numbers. \nAfter some more playing, I started looking at the code and wanted to tinker with it. So I forked it. I had some wild and interesting ideas.</p>\n\n<p>But somehow, nothing happened.</p>\n\n<p>I guess I was overthinking it. A year later, I remembered one time at work while being bored, I created the so called <a href=\"https://zanstra.com/arc/xs4all/dds.arc/miniwebventure/index.htm\" title=\"Originally hosted at http://www.worldaccess.nl/~mobitdz around 1997 I think\">mini-webventure</a>. I didn’t think too much back then, so why do it now?</p>\n\n<p>So I just started. After running around in the forest (in the game, that is), I got tired of entering parenthesis. And with \n<a href=\"http://ejohn.org/blog/ecmascript-5-objects-and-properties/\">properties</a> around in all browsers nowadays, there’s no need for it. So instead of typing <code class=\"language-plaintext highlighter-rouge\">n()</code>, you now can enter <code class=\"language-plaintext highlighter-rouge\">n</code> to go north.</p>\n\n<p>An other thing: in a JavaScript game, I don’t consider inspecting and modifying variables cheating.\nSo I prevented that with an <a href=\"http://benalman.com/news/2010/11/immediately-invoked-function-expression/\">iife</a> and some help of <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode\">strict mode</a> (to eliminate global variables).</p>\n\n<p>Multiple lines are now returned after entering a command, so they are displayed in the same font and color. And deaths \nare a bit more dramatic now. You should really try it, if you haven’t already…</p>\n\n<p>After dying too often, implementing load/save was next. First I just saved the game state, but then I realized\nthis was another way to cheat, so now only the entered commands are stored. Then I realized, a demo-mode\nis very easy to implement. Couldn’t resist it, but it’s hidden somewhere in the game.</p>\n\n<p>I didn’t want to change the map or story. Just wanted to share the fun I had, both playing and pimping the game.</p>\n\n",
			"summary": "Over a year ago, my interwebfriend secretGeek created a thing. I created a fork, and pimped it into this (and copied some of his blog lines too). I urge you to go and try it out before you read on. Unfortunately you’ll need to be on a desktop computer, not a mobile phone.",
			"date_published": "2016-03-06T13:45:00+01:00",
			"tags": ["refactor","game"]
		},
		 {
			"id": "/2016/01/09/First-post",
			"url": "https://blog.zanstra.com/2016/01/09/First-post.html",
			"title": "First post!",
			"content_html": "<base href=\"https://blog.zanstra.com/\"><p>OK, I started yet another blog.</p>\n\n<p>Here are the predecessors (and thank my <a href=\"https://archive.org/donate/\" title=\"Internet Archive\">public backup service</a> for most of this):</p>\n\n<ul>\n  <li>2006-2011:\n<a href=\"https://web.archive.org/web/20120918224636/http://zanstra.com/base/blog\">http://zanstra.com/base/blog</a>, \n<a href=\"https://web.archive.org/web/20071101024331/http://zanstra.com/base/tag\">archive 1</a> and \n<a href=\"https://web.archive.org/web/20120802022851/http://zanstra.com/base/tag\">archive 2</a> (DokuWiki with plugin)</li>\n  <li>2004-2006: \n<a href=\"https://web.archive.org/web/20080206012725/http://doekman.web-log.nl/\">http://doekman.web-log.nl</a> (hosted)</li>\n  <li>2004-2006: \n<a href=\"https://web.archive.org/web/20061116004043/http://blogger.xs4all.nl/zanstra/\">http://blogger.xs4all.nl/zanstra/</a> (hosted)</li>\n  <li>2002-2006: \n<a href=\"https://zanstra.com/arc/xs4all/logs/jsLog-2004.htm\" title=\"Later: http://zanstra.home.xs4all.nl\">http://www.xs4all.nl/~zanstra/logs/</a>\n<a href=\"https://zanstra.com/arc/xs4all/logs/jsLog.htm\">more entries</a> and \n<a href=\"https://zanstra.com/arc/xs4all/logs/ideaLog.htm\">some idea’s</a> (<a href=\"https://zanstra.com/arc/xs4all/logs/gen.bat.txt\">batch-file</a>)</li>\n</ul>\n\n<p>I also have a Tumblr micro-blogging thing called <a href=\"https://www.tumblr.com/blog/dd3v\" title=\"Doekman Dev\">dd3v</a>, and I used to have a micro blogging service on <em>doekman.micro.blog</em> backed via Kickstarter (<a href=\"/images/archive/doekman.micro.blog.png\">png archive</a> or <a href=\"/images/archive/doekman.micro.blog.pdf\">PDF print</a>).</p>\n",
			"summary": "OK, I started yet another blog.",
			"date_published": "2016-01-09T20:30:00+01:00",
			"tags": ["meta"]
		}
		
	]
}
