Sort empty strings last

You want to sort a list of strings in JavaScript, but some of the strings are empty, and you want those to come last. localeCompare is your friend, but it places empty strings first.

The solution is a bit of boolean sweetness:

["foo", "", "bar"].sort((a, b) =>
  a && b
    ? a.localeCompare(b)
    : !a - !b
// [ "bar", "foo", "" ]

For instance, if a is empty but b is not, the else case in the ternary resolves to true - false1 - 01 which means: put b before a.

Comma, comma & and

I recently had to concatenate author names with commas and an ampersand in the following manner:

commaAnd(['Emir Jong']);
// becomes: "Emir Jong"

commaAnd(['Kristian Josefsen', 'Tetyana Bohuňková']);
// becomes: "Kristian Josefsen & Tetyana Bohuňková"

commaAnd(['Luana Ferreira Carvalho', 'Jian Tu', 'Ambessa Afwerki']);
// becomes: "Luana Ferreira Carvalho, Jian Tu & Ambessa Afwerki"

Here are a few implementations in JavaScript:

function commaAnd(strs, comma = ", ", and = " & ") {
  const glue = (i) => ["", and, comma][Math.min(strs.length - i - 1, 2)];
  return strs.reduce((res, str, i) => res + str + glue(i), "");
function commaAnd(strs, comma = ", ", and = " & ") {
  return (
    ((s) => (s ? s + and : ""))(strs.slice(0, -1).join(comma)) + strs.slice(-1)
function commaAnd(strs, comma = ", ", and = " & ") {
  const init = strs.slice(0, -1).join(comma);
  return [init, strs.slice(-1)].filter((s) => s).join(and);


But then each author name needed a bit of markup as well. In Vue, we could concatenate HTML strings and use v-html, but we should avoid that if we can.

Here’s my method using slot scopes (much like that todo list example):

    <span v-for="(item, i) in items" :key="i"
      ><slot name="item" :item="item">{{ item }}</slot
      >{{ [null, and, comma][Math.min(items.length - i - 1, 2)] }}</span

export default {
  name: "CommaAnd",
  props: {
    items: { type: Array, default: Array },
    comma: { type: String, default: () => ", " },
    and: { type: String, default: () => " & " },

The usage is as follows:

<CommaAnd :items="authors">
  <template v-slot:item="{ item }">
    {{ fullName(item)
      affiliations.indexOf(item.affiliation) + 1

From the CommaAnd component, we call out to the parent component to define how to render each item. Inside the component, we only define what to do in between the items.

Note that much of the whitespace between elements and interpolations ({{ }}) must be eliminated when we are dealing with inline text. Furthermore, forcing HTML/XML into short lines is difficult (Prettier is doings its best here…) and the Vue syntax for slot scopes is a bit entangled, so the result is not super readable.

The resulting output is also pretty loud. Lots of <span>s in <span>s. If you don’t want that you should probably go with v-html anyway.

GU Eduroam trouble solved

Connecting to Eduroam (at the University of Gothenburg) was tricky this time. The main problem was ensuring the CA Certificate was installed. Clicking the certificate link on the instructions page at Medarbetarportalen gave a message saying This certificate is already installed as a certificate authority. Yet, when configuring the WiFi connection, there was no option to choose that certificate (or any other certificate, for that matter). The trick turned out to be first saving the file, and then opening it.

You don’t have to bother with the crazy Eduroam CAT app.


  1. Visit the Installationsguide Eduroam page.
  2. On Google Chrome, long-tap the certificate link at the bottom of the page and select Download link.
  3. Or, if you use another browser, save the file on a computer and transfer it to your phone 🤷‍♀️
  4. Install the certificate by either opening it through the file browser, or using Settings > Wi-Fi > Advanced > View more > Install network certificates.
  5. Finally, configure the network connection:
    EAP method: PEAP
    Phase 2 authentication: MSCHAPV2
    CA certificate: (whatever name you gave it, should be an option between Select certificate and Don’t validate)
    Identity: x… or gus…

This was with Android 8.0.0 on a Samsung Galaxy J3. Your mileage may vary. Good luck 🤞

Push to deploy Vue app

With this workflow, my web space contains a git remote which, whenever pushed to, builds the Vue project and puts it on a public path.

A few years ago I, working with Drupal 8, I was using Pantheon and I really enjoyed the workflow, where deploying comprised of simply git push‘ing to a certain remote. (I think maybe this model was made popular by Heroku?)

Nowadays I’m getting into JS development. A framework that I have been using recently is Vue. One of my current project ideas is using Vue to develop a small web app for crawling some websites and grabbing data on arbitrary movies. I want to do the development in short iterations, and I want it to be easy to publish each step of progress.

So today I set up a push-to-deploy workflow for my Vue app. I don’t want to give the prototype its own domain name, so I’m publishing it in a subdirectory:


With this workflow, my web space contains a git remote which, whenever pushed to, builds the Vue project and puts it on a public path.

The deploy procedure is reduced to a single command: git push deploy

Git setup on server

The web host I am using is, which provides SSH access and git.

SSH into the server and create the remote repository:

cd /home/private
git init --bare /home/private/myproject.deploy

Clone it to get the local git repository:

git clone myproject.deploy myproject.checkout

Then add a post-receive hook script in the remote repository:

touch myproject.deploy/hooks/post-receive
chmod +x $_
# Bonus trick: That $_ expands to the last argument in the previous command, in this case the path of the new file.

Edit the script as follows (making changes where appropriate, depending on your Vue setup):

# -e exits as soon as any command fails
# -x prints each command being executed
set -ex
git --git-dir .git fetch
git --git-dir .git reset --hard origin/master
yarn install --production
yarn build --dest $SITE

Local git configuration

Back on your local machine, all you have to do is add the new remote:

git remote add deploy

Give it a spin:

echo Hello World! > hello.txt
git add hello.txt
git commit -m 'Deploy test'
git push deploy
# Verify that the post-receive script output is showing.
# Clean up after yourself:
git reset --hard HEAD^
git push -f deploy

Configure Vue for a subdirectory

Vue is normally configured to serve the app at the root level of the host. I want to serve it at the root level locally, but in a subdirectory on the web server. I accomplished this using environment variables.

Locally, create .env containing:


Edit vue.config.js like this:

module.exports = {
  publicPath: process.env.PUBLIC_PATH,

If you use vue-router, configure it (in main.ts):

const router = new VueRouter({
  base: process.env.PUBLIC_PATH,
  // ...

Finally, in the local repository on the server (that’s an oxymoron, but you know what I mean), create .env.local, containing:


After that, commit and push the changes you made on your local machine.

A cryptocurrency-mining bot brute-forced into my WordPress site

Someone brute-forced into a client’s WordPress site and added cryptomining js. Good thing they knocked down an HTML div on their way out!

Yesterday, at my parents’ cabin outside Stockholm, me and my father were having an office session after breakfast. Me procrastinating thesis work, him doing whatever he was doing. At one point he needed to check something on the website of FactWise, a company where he is involved.

The website didn’t appear as it should. The carousel in the footer was broken and the subsequent contact details bar had been pushed off to the side.

Broken design

The FactWise website is one of the first I’ve made, at least counting those which are still up and running today. Dad had a friend do the design, and I turned the PSDs into a WordPress theme and set it all up on shared hosting. It’s not beautiful but it works – kind of.

When dad showed me the buggy appearance, I was running out of other things to do instead of studying, so I started looking into it immediately. My first guess was that a stylesheet ref was broken. I looked through the Network tab in Firefox Developer Tools to find any 404s, but what I found was something else. Continue reading “A cryptocurrency-mining bot brute-forced into my WordPress site”

Fix SSH backspace coming out as “^?”

When using SSH at FS Data, pressing backspace in Vim were coming out as ^? so editing files was a hassle. (I am on a Linux workstation btw.) mistyped

I found a solution in the Vim documentation, via a Stack Overflow post. I just opened ~/.vimrc and added the following:

:if &term == "xterm-256color"
:  set t_kb=CTRL-V<BS>

Here, xterm-256color is whatever came out when I ran :echo &term inside Vim. CTRL-V<BS> is not those literal characters, but the key combination Ctrl+V followed by Backspace. It comes out on the screen as ^?, like in the screenshot below.vimbs

Duplicated id="x" in WP Meta Boxes

I am building a very minimalistic calendar plugin for WP (an idea that doesn’t seem to turn out very well so far), and I got a cryptic JS error when adding JQuery datepicker saying “a is undefined”. I debugged this for a good two or three hours, until today I tried replacing the minified datepicker.js file with source code.

The error turned out to be caused by the same HTML id being assigned to a meta box div as well as to a custom field.

function transparentcalendar_add_meta_boxes() {
    add_meta_box( 'transparentcalendar-time', __( 'Calendar', 'transparentcalendar' ), 'transparentcalendar_meta_box_time', 'post' );

function transparentcalendar_meta_box_time( $post ) {
    $time_current = transparentcalendar_post_get_time( $post->ID );

    echo '<div class="form-field">
        <label for="transparentcalendar-time">' . __('Time', 'transparentcalendar') . '</label>
         <input id="transparentcalendar-time" name="transparentcalendar_time" type="text" size="20" value="' . $time_current . '" class="datepicker">';

I have learned these two simple things:

    • Get a HTML validator browser plugin, thus I might have noticed the duplicated id earlier
    • Use a dev version of WP when developing, as it uses non-minified JS

Customer-specified price in Drupal 7 Commerce

Today, I spent several hours messing about with the Rules UI and googling Commerce docs and forums, trying to figure out how to provide a field where the customer can freely choose the price of a donation-style product. In the end, I found pointers to a method which turns out to work pretty well.

My use case

Donation as a Drupal 7 Commerce product. Donation amount can be chosen freely, above a fixed minimum. The customer/donor receives a reward (but that is not actually relevant here).


This is how I did it:

  1. Install Commerce Customizable Products
  2. Add price field
    1. Add a line item type at admin/commerce/config/line-items. It is created with a bunch of default fields, which you cannot change.
    2. Add a field of the Price type (TODO: Figure out how to set a minimum)
  3. Make it visible
    1. At the field display settings of your product display content type (something like admin/structure/types/manage/product-display/display), edit the settings for the product reference field. Change Add to Cart line item type to your new line item type. Click Update and Save.
  4. Make it count
    1. Add a new rule at admin/config/workflow/rules. Choose the event Calculating the sell price of a product.
    2. Add an Entity has field condition with commerce-line-item for Entity and your price field for Field.
    3. Add a Set the unit price to a specific amount action with commerce-line-item for Line item and something like commerce-line-item:field-donation-price:amount for Amount.