<!DOCTYPE html>
<!--
Javascript Bytewise Comparison Thing
by "Pegasus Epsilon" <pegasus@pimpninjas.org>
(C) 2025 Distribute Unmodified
https://pegasus.pimpninjas.org/license
Feedback to address above.
-->
<html lang="en" prefix="og: http://ogp.me/ns#">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="icon" type="image/png" href="/images/favicon.png" />
<title>Bytewise Comparison Thing</title>
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="\@pegasusepsilon" />
<meta name="twitter:creator" content="\@pegasusepsilon" />
<meta property="og:site_name" content="The Almighty Pegasus Epsilon" />
<meta property="og:title" content="Bytewise Comparison Thing" />
<meta property="og:type" content="text/html" />
<meta property="og:description" content="Like diffutils cmp, and its opposite, but optionally iterative, and in the browser" />
<meta property="og:image" content="/images/favicon.png" />
<meta property="og:url" content="https://pegasus.pimpninjas.org/" />
<style type="text/css">
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
font-family: sans-serif;
}
html, body { margin: 0; height: 100%; }
body { text-align: center; display: flex; flex-direction: column; }
body, textarea { background: #000; color: #fff; font-size: 1.5em; }
div { flex: 99; display: flex; }
span { margin: auto; }
button { margin: 0; vertical-align: middle; font-size: 1.5em; }
textarea { resize: none; font-family: monospace; position: relative; flex: 1; }
</style>
<script type="text/javascript">
var undo = [];
function compare (invert, a, b) {
if (!b.value.length)
return b.value = a.value;
var s = a.value.split('');
var d = b.value.split('');
var t = [];
var m = Math.min(s.length, d.length);
if (invert) {
for (var i = 0; i < m; i++)
if (s[i] != d[i]) t.push(d[i]);
else t.push('.');
t.push((s.length > d.length ? s : d).slice(m).join(''));
} else {
for (var i = 0; i < m; i++)
if (s[i] == d[i]) t.push(d[i]);
else t.push('.');
t.push('.'.repeat(Math.max(s.length, d.length) - m));
}
t = t.join('');
if (b.value != t) {
undo.push(b.value);
b.value = t;
} else alert("no changes!");
}
addEventListener("load", async function load (e) {
var a = document.querySelector("#A");
var b = document.querySelector("#B");
function select (e) {
e.stopPropagation();
e.target.focus();
e.target.select();
}
a.addEventListener("click", select);
b.addEventListener("click", select);
a.addEventListener("keydown", function (e) {
if ("Enter" == e.key) e.preventDefault();
});
a.addEventListener("keyup", function (e) {
if ("Enter" == e.key) similarity(a, b);
});
document.querySelector("#similarity")
.addEventListener("click", _ => compare(0, a, b));
document.querySelector("#difference")
.addEventListener("click", _ => compare(1, a, b));
document.querySelector("#undo")
.addEventListener("click", _ => undo.length ? b.value = undo.pop()
: alert("no more undo!"));
document.querySelector("#reset")
.addEventListener("click", _ => b.value = '');
});
</script>
</head>
<body>
<div><textarea id="A"></textarea></div>
<div style="flex: 1"><span>
<button id="similarity">similarities</button>
<button id="difference">differences</button>
<button id="undo">undo</button>
<button id="reset">reset</button>
</span></div>
<div><textarea id="B" readonly="readonly"></textarea></div>
</body>
</html>