One of my low priority goals for my site is to make my pages work offline. I had looked into "web manifests" but they seem more suited for web apps and not for documents like mine. So I looked at what it would take to make File→Save As work, preserving interactivity. It turns out it does not work in general on my site. Why?

I tried to make the original HTML + JavaScript should work offline in the sense that they only access resources on the current server and not from any CDN, Google Fonts, etc. So if you save the HTML + JavaScript it should work on a local web server. However, File→Save As doesn't work this way!

Diagram showing what happens when using Save As in the browser on an interactive page
Save As for an interactive page (diagram made with excalidraw)

The browser saves the HTML after it has been modified by running Javascript. But that HTML contains the modified HTML without the event handlers or JavaScript variables. When you load the saved page, it re-runs the JavaScript, and that will modify the HTML again. Unless that code is carefully written, you can end up with two of some things, and some errors when the code can't handle two of something.

The property we want is idempotency. When the code runs a second time, it should produce the same results as it did the first time it ran.

I think it's too much work to expect idempotency on most of my pages. The Hexagons guide is the main place I thought it'd be useful for someone to have an offline copy, and fortunately most of that page was ready for idempotency, because I used Vue with server-side prerendering. It turns out my A* Introduction was mostly idempotent, so I made the rest of it idempotent.

Unfortunately it's fragile. Even if they're idempotent now, it would be easy for me to miss something in a future update and lose the idempotency. I could run Save As in a script and do some testing, but it turns out the output HTML is not actually the same the second time. It's functionally the same but I can't test it with a simple string compare. I could either run some kind of in-browser test that simulates clicks and other interactions, or I could run a textural diff on the HTML and make exceptions for all the things the browser changes during Save As.

I think the number of people who use Save As is going to be small enough that it's not setting up a lot of infrastructure to test this. :-( It'll be nice when it works but it's just not worth it.

I also discovered a few other things during this mini project:

  1. Browsers will not load ES6 modules from file:// urls. This is unfortunate. To make the A* page work, I had to compile my ES6 module code into non-module code using esbuild (which I love!). But I really don't like that browser makers are limiting some features to having the correct mime types, or not working on file:// urls, or requiring https. All these limit what locally saved file:// pages can do.
  2. Browsers will only save resources in subfolders, not in parent folders. This is reasonable. But since I share code across pages on my site, that code didn't get saved, and that broke the interactivity. For the hex and A* pages I ended up restructuring some files so that they would get saved.
  3. Links within the page like <a href="#foo"> don't get saved property in Chrome or Safari. Those browsers rewrite the links to point back to the original site. Firefox saves them as local links. So I recommend if you want to save an offline copy, use Firefox.

Unfortunately this was all a distraction. I sometimes fall into this trap of doing something low value but easier in order to procrastinate doing something high value but harder. I think it was far lower value than making my pages printable to PDF, as I think PDF is probably a better solution for offline access than relying on the browser's Save As feature. And even that was pretty low value. Putting the pages on GitHub would be better, but my pages are so tangled together that I will have to solve several issues to make that work.

In good news, as part of this project I shrunk the JavaScript down to 15k, and delayed diagram rendering until the page has loaded. This sped up page loading some more. Try going to my home page and clicking the A* Introduction link. Does it load fast for you?

I want to shift gears and work on something unrelated to my tutorials, but I keep falling into these rabbit holes. It's easier to go improve an existing page than to start something unfamiliar and new.

Labels:

5 comments:

Scott Turner wrote at April 10, 2022 2:35 PM

The original version of Dragons Abound was "offline" but I ran into so many aggravating problems with file: URLs and similar issues that I eventually gave up. I'm not sure why the security devs consider file: URLs to be such a threat, but it makes it very hard to do anything without a web server.

Amit wrote at April 14, 2022 8:29 PM

Thanks Monstrim! I've been wanting to use this blog to show the process, even when I do the wrong thing. :-)

Agreed Scott, it seems like it's getting harder and harder, not only file: urls, but there are now lots of restrictions on what http: can do. You have to use https: for some features.

Thomas Reasoner wrote at May 01, 2022 12:42 PM

Have you looked into turning your site into a Progressive Web Application? PWAs have many interesting/great features including working offline.

Amit wrote at May 02, 2022 4:17 PM

@Unknown: yes, I implemented a PWA, but it seems designed for web apps rather than documents, and it seemed like a lot of work for me to handle all the corner cases correctly. I think the best option is going to be supporting printing to PDF.

Unknown wrote at September 01, 2022 11:45 PM

Hello Mr. Amit, I have been following your work for a couple of years or so, they are very interesting and educational. Although my level of mathematics, physics or programming is quite poor, every time I go back to a concept I have read before in some of your articles, I understand new concepts. your blog with interactivity is simply magical and great. thanks for this magnificent work! a greeting!