Hello! I studied Computer Science, and live in Bath. I write code, design games, and occasionally tweet.
Hello! I studied Computer Science, and live in Bath. I write code, design games, and occasionally tweet.

Posts about Tutorial Other Categories

Jekyll Category and Year Pages Without Plugins Mar. 20, 2019 in Jekyll, Text, Tutorial

Jekyll is fantastic, easy to use, and extensible. However, some Jekyll hosts such as GitHub Pages do not allow people to use custom plugins - and I want category and year pages. But rightly so. Allowing custom plugins would require hosts to allow users to run arbitrary Ruby code on their build servers and possibly create a mess. This problem is solvable, but it’s ultimately simpler to just ban them entirely.

This constrained environment forces you to get creative. On this blog I use the fantastic jekyll-compress-html template which minifies the very post you are reading. I have written by own template to automatically replace standard markdown or HTML images with lazily loaded ones. And finally, I have written a small (it’s only a hundred or so lines ;) ) Makefile to automatically, among other-things, generate year and category pages:

# Find categories and years
CATTARGETS  = $(shell grep -h '^categories:' -r _posts/ | awk -F ':' \
              '{print $$2}' | tr ' ' '\n' | sed '/^$$/d' | sort | uniq | \
              awk '{print "categories/"$$1".html"}')
YEARTARGETS = $(shell find _posts/ -regextype egrep -regex '.*/[0-9]{4}.[^/]*' \
              -printf '%P\n' | cut -c-4 | sort | uniq | \
              awk '{print "years/"$$1".html"}')

# Generate category pages
cat: cat-clean $(CATTARGETS)
categories/%.html: _templates/category.html
	cp $< $@
	sed -i 's/#####CATEGORY#####/$*/' $@

# Generate year pages
year: year-clean $(YEARTARGETS)
years/%.html: _templates/year.html
	cp $< $@
	sed -i 's/#####YEAR#####/$*/' $@

To use, simply create _templates/category.html and a _templates/year.html templates and run make cat year. The code assumes your posts start with the date and they are stored in the default _posts/. When run #####CATEGORY##### and #####YEAR##### will be replaced with the templated value.

Lazy Loading Images, the Jekyll Way Sep. 10, 2018 in Jekyll, Text, Tutorial

Intersection Observer is a relatively new API with decent support that can have a huge impact on performance. It has many uses and can trigger all sorts of code but this article is simply looking into performance. With it, image lazy loading can be quickly and easily added to any site including static Jekyll sites.

To start, define a variable to store the Interaction Observer. For simpler deployment, the code below avoids using modern JavaScript features such as let and arrow functions.

var observer

Secondly, a function to load a given image. Here, a temporary image is created to load the image and if successful the source of the actual image is replaced.

function loadImage(image) {
	var i = new Image();
	i.onload = function() {
		image.src = image.dataset.lazySrc
	i.onerror = function() {image.classList.add('lazy-error')}
	i.src = image.dataset.lazySrc

Thirdly, a function to process intersections. It checks which images are currently intersecting.

function onIntersection(entries) {
	for (var e in entries) {
		if(entries[e].intersectionRatio <= 0) continue
		observer.unobserve(entries[e].target) // Stop watching

Fourthly, lazily loaded images are located and the code is run.

var images = document.querySelectorAll('img[data-lazy-src]')
if ('IntersectionObserver' in window) {
	observer = new IntersectionObserver(onIntersection, {rootMargin: '250px'})
	for(var i in images) {
		if(typeof images[i] === 'object' && 'classList' in images[i] &&
			 !images[i].classList.contains('lazy-loaded') &&
			 !images[i].classList.contains('lazy-error')) {
} else {
	for(var image in images) loadImage(image)

But, the code above can only lazy-load images with very specific markup. This is where this nasty piece of Liquid code comes in. Liquid is fundamentally a very limited language with no direct way to initialise arrays, weird syntax, and no regular expressions. So, instead of using regular expressions, we can instead create a nasty piece of splitting code which works just well enough for the job.

The HTML Jekyll generates from Markdown is simple and regular enough for the following code to work. I would not expect it to work for more complicated HTML. But it’s worth a shot!

{%- assign excerpt = content | split: '<img src="' -%}
{%- for e in excerpt -%}
	{%- if forloop.first == true -%}
		{{ e }}
	{%- else -%}
		{%- if e contains '" alt="' -%}
			{%- assign f = e | split: '" alt="' -%}
			{%- assign url = f | first -%}
			{%- assign g = f | shift | join: '" alt="' | split: '"' -%}
			{%- assign alt = g | first -%}
			{%- assign rest = g | shift | join: '"' -%}
			<noscript><img src="{{ url }}" alt="{{ alt }}" /></noscript><img class="script-required" src="#" data-lazy-src="{{ url }}" alt="{{ alt }}"{{ rest }}
		{%- else -%}
			{%- assign f = e | split: '"' -%}
			{%- assign url = f | first -%}
			{%- assign rest = f | shift | join: '"' -%}
			<noscript><img src="{{ url }}" /></noscript><img class="script-required" src="#" data-lazy-src="{{ url }}"{{ rest }}
		{%- endif -%}
	{%- endif -%}
{%- endfor -%}

Now I recommend styling the unloaded images with width and height. If known ahead of time it can be set explicitly. Failing that, I recommend setting a generic default using CSS.

img {
	min-width: 100px;
	min-height: 100px;

And finally with this in place, my site often enjoys a perfect score in Google Chrome Inspector Audit. It cleanly beats google.com which has a top score of 91 and even beats motherfuckingwebsite.com in every category but performance where it draws at 100.

(100) Performance, (100) Progressive Web App, (100) Accessibility, (100) Best Practices, (100) SEO

Colourful Consoles with Bash Aug. 14, 2017 in Bash, Text, Tutorial

With bash it is trivially easy to produce nice, colourful console output with the code below. Simply paste it into the top of your script, and then you can colour your text by just printing the variables.

For example, if you want bold yellow text with a red background use echo "${BOLD}${YELLOW}${BRED}Critical Warning!${CLEAR}". Additionally, you can ${ITALIC}, ${UNDERLINE}, ${INVERT}, or ${STRIKE} text as you see fit. Once you are done with formatted text, use ${CLEAR} to clear all formatting.

Lastly, ${RESET} and ${RULE} to reset the screen and create a horizontal rule. Vertical rules are left as an exercise for the reader.


CLEAR="\033[0m"; BOLD="\033[1m"; ITALIC="\033[3m"; UNDERLINE="\033[4m"; INVERT="\033[7m"; STRIKE="\033[9m
RED="\033[31m"; GREEN="\033[32m"; YELLOW="\033[33m"; BLUE="\033[34m"; PINK="\033[35m"; CYAN="\033[36m"; WHITE="\033[37m"
BRED="\033[41m"; BGREEN="\033[42m"; BYELLOW="\033[43m"; BBLUE="\033[44m"; BPINK="\033[45m"; BCYAN="\033[46m"; BWHITE="\033[47m"

alias RESET='printf "\033c"'
alias RULE='printf %"$COLUMNS"s | tr " " "-"'

# Use $LINES and $COLUMNS to query console dimensions