LiquidBase60

This article, part of the writing collection, was published on and last updated on .

It’s JS Naked Day!

I’m participating in JS Naked Day with the hope of helping to promote the rule of least power. This means that your browsing experience on this website during the 50 hours that make up JS Naked Day should be identical to one where you have disabled JavaScript in your browser.

This is an excellent exercise in making sure there is a clear separation of concerns between HTML for markup, CSS for styling, and JavaScript for interactivity. I highly recommend trying it out and participating yourself!

I have started auto-generating ShortURLs for each of my posts, old and new, and opted to implement Tantek Çelik’s NewBase60 for representing the unique URLs, but I've done so with Liquid so it plays nicely with my Jekyll-powered website.

Since moving to Eleventy I have recreated this functionality using JavaScript in a similar way I did using Liquid. For all intents and purposes, everything in this article still stands, but if you'd like to see how I’m achieving this now, check out Generating Automatic ShortURLs with NewBase60.

Off the back of the Tantek Çelik’s enlighting and superb work on Whistle and NewBase60, I yearned for a way to generate ShortURLs for each of my post categories, and figured out a way, albeit a bit over-complicated, using vanilla Jekyll/Liquid.

For example, there is a Note that I published on 08 September 2018, and is the 1st (and only) Note of that day. Let’s look at the page’s ShortURL and break down what the code at the end means:

repc.co/n4wN1

This code is comprised of three parts:

  1. nCategory Code (required, 1 character)
  2. 4wNSexagesimal Epoch Days (required, 3 characters)
  3. 1Post Number for the Day (optional, 1 character, default = 1)

Category Code Permalink

Firstly, we need to figure out the category for a particular page. I’m assigning a letter to each category for two reasons: to limit the loop to posts of that type, and to make it such that one can discern the category from the ShortURL’s slug.

Category Prefix
Article a
Bookmark h
Note n
{%- if page.category == 'article' -%}
	a
{%- elsif page.category == 'bookmark' -%}
	h
{%- elsif page.category == 'note' -%}
	n
{%- endif -%}

Sexagesimal Epoch Days Permalink

How do we calculate Epoch days from a given post’s date?

The full RFC 3339 datetime for our example post is 2018-09-08T23:58:42+0100, and the first step is to convert this to a Unix timestamp (seconds since Epoch), and from there we can divide by 86400 (the number of seconds in a day) to get the number of days since Epoch. Fortunately, this is quite trivial with Jekyll:

2018-09-08T23:58:42+0100153644752217782 (rounded down)

{% assign n = page.date | date: '%s' | divided_by: 86400 | floor %}

Sexagesimal is a number system with 60 as its base and is actually over 5000 years old[^3]! For our ShortURLs, the 60 characters are represented by the following:

0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz

In order to complete this step, we need to loop through our Epoch days tallying and converting into base 60 until we hit 0. One limitation that we’ll experience with using Liquid (as opposed to NewBase60’s original languages, PHP or JavaScript) is its lack of a while loop, so the following won’t work:

{% while condition != satisfied %}
	...
{% endwhile %}

However, we can mimic one using a for loop that counts to a (theoretically) unreachable number, and we can break out of the loop when our condition is met (as with a standard while loop):

{% for i in (1..9999) %}
	...
	{% if condition == satisfied %}
		{% break %}
	{% endif %}
{% endfor %}

Much like a while loop, we are not concerned with the iteration variable, i, we want to perform an action until a condition is met—it doesn’t matter how many iterations are required.

Putting it all together, our code now looks like the following, where we are storing our output in the s variable:

{% assign n = page.date | date: '%s' | divided_by: 86400 | floor %}
{% assign s = '' %}
{% assign m = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz' | split: '' %}
{% for i in (1..9999) %}
	{% assign d = n | modulo: 60 %}
	{% assign s = s | prepend: m[d] %}
	{% assign n = n | minus: d | divided_by: 60 %}
	{% if n <= 0 %}
		{% break %}
	{% endif %}
{% endfor %}
{{ s }}

Post Index for the Day Permalink

The remaining portion of work is to determine how many posts of the given category have been published on the given day, and which of those posts, chronologically, this one is. To do so, we need to add an additional action to our category checks which instructs which category of posts to trawl:

{%- if page.category == 'article' -%}
	a
	{%- assign posts_to_check = site.categories.article -%}
{%- elsif page.category == 'bookmark' -%}
	h
	{%- assign posts_to_check = site.categories.bookmark -%}
{%- elsif page.category == 'note' -%}
	n
	{%- assign posts_to_check = site.categories.note -%}
{%- endif -%}

We can use this categorised list of posts to match dates against and build an array of posts under the specific category on the specific day. Looping through this array of posts until we reach the post for which we’re building and pulling out the array index gives us the number we’re looking for:

{%- assign page_date = page.date | date: '%Y-%m-%d' -%}
{%- assign filtered_posts = site.emptyArray -%}
{%- for check in posts_to_check -%}
	{%- assign check_date = check.date | date: '%Y-%m-%d' -%}
	{%- if check_date == page_date -%}
		{%- assign filtered_posts = filtered_posts | push: check -%}
	{%- endif -%}
{%- endfor -%}
{%- for check in filtered_posts -%}
	{%- if check.title == page.title -%}
		{%- assign shorturl = shorturl | append: forloop.index -%}
		{%- break -%}
	{%- endif -%}
{%- endfor -%}

Presenting… Permalink

{%- capture shorturl -%}
	{%- if page.category == 'article' -%}
		a
		{%- assign posts_to_check = site.categories.article -%}
	{%- elsif page.category == 'bookmark' -%}
		h
		{%- assign posts_to_check = site.categories.bookmark -%}
	{%- elsif page.category == 'note' -%}
		n
		{%- assign posts_to_check = site.categories.note -%}
	{%- endif -%}
	{%- assign n = page.date | date: '%s' | divided_by: 86400 -%}
	{%- assign s = '' -%}
	{%- assign m = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz' | split: '' -%}
	{%- if n == empty or n == 0 -%}0{%- endif -%}
	{%- for i in (1..9999) -%}
		{%- assign d = n | modulo: 60 -%}
		{%- assign s = m[d] | append: s -%}
		{%- assign n = n | minus: d | divided_by: 60 -%}
		{%- if n <= 0 -%}
			{%- break -%}
		{%- endif -%}
	{%- endfor -%}{{ s }}
{%- endcapture -%}
{%- assign page_date = page.date | date: '%Y-%m-%d' -%}
{%- assign filtered_posts = site.emptyArray -%}
{%- for check in posts_to_check -%}
	{%- assign check_date = check.date | date: '%Y-%m-%d' -%}
	{%- if check_date == page_date -%}
		{%- assign filtered_posts = filtered_posts | push: check -%}
	{%- endif -%}
{%- endfor -%}
{%- for check in filtered_posts -%}
	{%- if check.title == page.title -%}
		{%- assign shorturl = shorturl | append: forloop.index -%}
		{%- break -%}
	{%- endif -%}
{%- endfor -%}

At last, the following variable becomes available for use:

{{ shorturl }}

You can also send an anonymous reply (using Quill and Comment Parade).