Skip to content

Instantly share code, notes, and snippets.

@donquixote
Last active June 10, 2025 22:29
Show Gist options
  • Save donquixote/6f12f57970a1ed6f417dec41ae1e3248 to your computer and use it in GitHub Desktop.
Save donquixote/6f12f57970a1ed6f417dec41ae1e3248 to your computer and use it in GitHub Desktop.
Browser search

Usage

Save the search.html in your local filesystem somewhere.

In a chromium-like browser.

Manage search engines. Create search ending with nickname 'eji'.

Add this as search url (remove line breaks): (This example is for Jira search in a specific project)

file:///PATH/TO/search.html
  ?pattern=https://jira.example.com/jira/issues/?jql=project=@0 ORDER BY key DESC
  &pattern=https://jira.example.com/jira/issues/?jql=project=@0 AND text ~ "@@" ORDER BY key DESC
  #%s

In your browser address bar, type "eji MYPROJECT my search terms" -> you get a Jira search for project=MYPROJECT and search text = "my search terms".

Later, type "eji MYPROJECT" -> you get a Jira search in project=MYPROJECT, without the search terms.

Later, type "eji MYPROJECT my search terms#debug" -> you get a debug page, which is useful to fine-tune your search engine.

Explanation

In the example above, "MYPROJECT my search terms" is the search string, which is split into ['MYPROJECT', 'my', 'search', 'terms'].

In the "pattern" urls, the '@0' placeholder is for the first part ("MYPROJECT"), the '@1' placeholder would be for the second part ("my"). The "@@" placeholder is for the remaining parts. In this case, because "@1" does not exist, that is "my search terms".

With "eji MYPROJECT my search terms", the first pattern does not match, because there is no "@1" and no "@@". So it goes to the second pattern. With "eji MYPROJECT", the first pattern matches, so it uses that for the destination url.

<html lang="en">
<head>
<title>Browser search</title>
</head>
<body>
</body>
<script>
function match(pattern, parts) {
let dest = pattern;
for (var i = 0; i < parts.length; ++i) {
if (!dest.includes('@' + i)) {
break;
}
dest = dest.replaceAll('@' + i, encodeURIComponent(parts[i]));
}
if (i === parts.length) {
console.log('match', {parts, pattern, i, dest});
return dest;
}
if (!dest.includes('@@')) {
return null;
}
dest = dest.replaceAll('@@', encodeURIComponent(parts.slice(1).join(' ')));
console.log('match', {parts, pattern, i, dest});
return dest;
}
function matchAll(patterns, parts) {
for (const pattern of patterns) {
const dest = match(pattern, parts);
if (dest !== null) {
console.log('matchAll', {pattern, parts, dest});
return dest;
}
}
return null;
}
(function () {
const params = new URLSearchParams(window.location.search);
const raw_hash = document.location.hash.substring(1);
let hash = decodeURIComponent(raw_hash).replaceAll('+', ' ');
const debug = hash.endsWith('#debug');
if (debug) {
hash = hash.substring(0, hash.length - 6);
}
const parts = hash.split(' ');
const patterns = params.getAll('pattern');
const dest = matchAll(patterns, parts);
console.log('done', {params, patterns, parts, dest, debug});
if (debug) {
const json = JSON.stringify({patterns, raw_hash, hash, parts, dest}, null, 2);
const code = document.createTextNode(json);
const pre = document.createElement('pre');
pre.append(code);
document.body.append(pre);
}
else {
document.location.href = dest;
}
})();
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment