Default search filters on Cardmarket

Posted on 2021-07-05


Disclaimer: I am not deeply familiar with JavaScript. I have written JavaScript code before, but that was quite a while ago – when node.js was at version 8.x – and JavaScript has changed a lot since then. I do not claim to be proficient at writing JavaScript code

These days, when I buy Magic: the Gathering singles, it’s practically always for my fully foil Legacy Death & Taxes (D&T) deck. Which means my default search on Cardmarket (formerly known as MCM) is for foil cards. They support default search filters, but only if you are logged in. Since they log out users after some time, I am an “anonymous” user most of the time, i. e. I do not have access to my default search filters.

How do search filters work on MCM?

Fortunately, search filters are implemented very transparently in the URL query string. For example, the product page for Aether Vial from Darksteel can be found at:

https://www.cardmarket.com/en/Magic/Products/Singles/Darksteel/Aether-Vial

Search filters for cards that are

  1. in English
  2. at least EX
  3. foil

are just appended to that URL in the query string like this:

https://www.cardmarket.com/en/Magic/Products/Singles/Darksteel/Aether-Vial?language=1&minCondition=3&isFoil=Y

This makes implementing default filters for anonymous users very easy: We just need to set the query string accordingly. However, we do not want to overwrite existing query string parameters, lest our default filters always take precedence over custom filters selected on the product page.

Enter stage left: browser extension

I have been using Vivaldi since its Technical Preview 1, so I fortunately can use all the resources on creating a browser extension for Chrome. I started out with the simplest implementation possible: Default filters are hard-coded into the browser extension and if I wanted to change them, I will have to touch the internals of the browser extension.

This means I only need two files: the manifest.json and a .js file for the actual code itself.

my-extension/
├─ manifest.json
└─ script.js

The manifest.json contains metadata about the browser extension such as its name, description, version, and which permissions it requires.

{
  "name": "MCM redirector",
  "description": "Automagically set search parameters to 'English', 'EX+', and 'Foil' for MCM M:tG singles",
  "version": "0.0.1",
  "manifest_version": 2,
  "background": {
    "scripts": [
      "script.js"
    ]
  },
  "permissions": [
    "webRequest",
    "webRequestBlocking",
    "*://www.cardmarket.com/*/Products/Singles/*"
  ]
}

We want to modify the request by adding the default filters to the request URL (webRequest permission) and we want to block the request processing until our code has run (webRequestBlocking permission). In order to use webRequestBlocking, we need to be on manifest_version 2 or smaller though, which means we cannot use the new manifest version 3 for this approach.

Our actual JavaScript file itself (here: script.js) is pretty simple, as well:

const addDefaultMcmParams = (details) => {
  const url = new URL(details.url)
  const defaultParams = {
    language: "1",
    minCondition: "3",
    isFoil: "Y"
  }
  let urlChanged = false

  for (const [key, value] of Object.entries(defaultParams)) {
    if (url.searchParams.has(key)) { continue }

    urlChanged = true
    url.searchParams.append(key, value)
  }
  if (!urlChanged) { return {} }

  return {
    redirectUrl: url.toString()
  }
}

chrome.webRequest.onBeforeRequest.addListener(
  addDefaultMcmParams,
  {
    urls: ["*://www.cardmarket.com/*/Products/Singles/*"]
  },
  ["blocking"]
)

We want addDefaultMcmParams to execute before a request if the request URL matches

*://www.cardmarket.com/*/Products/Singles/*

That callback function redirects to a URL with a rewritten query string, if necesssary, and the browser continues doing its thing to eventually display the product webpage; with our filters.

Loading the extension

Loading our extension is very straightforward, as well.

  1. Navigate to chrome://extensions.
  2. Enable Developer Mode in the upper-right corner.
  3. Load unpacked and select the root folder of your extension.

I also created an animated GIF (~5 MB) showing how to do this.

Next steps

I should have started out with tests, but knowing absolutely nothing going into this, I opted to skip them and get myself familiar with how browser extensions work first. So the next immediate steps for me is to add tests.

Then, it is arguable that there should be some UI to set the default search filters, which includes having the extension remember those changes, of course.

Once that is done, I guess it is time to post the extension to the Chrome Web Store. There is no roadmap for any of this though, so it might happen next weekend; at the end of the year; or never.

Huy Dinh

Huy Dinh

Senior Software Engineer
at Shopify

  • LINE: _huydinh
  • Signal: +49 152 39511506

    This phone number is not monitored for incoming calls or SMS messages. It only exists, because Signal uses phone numbers to identify their users and I do not want to expose my private phone number. If you do have my actual one, you can use that to contact me on Signal, as well.

  • Threema: MYZF697W