tipograph
Version:
A little javascript library and command line tool that makes your written content more typographically correct.
104 lines (88 loc) • 2.76 kB
JavaScript
function matrix(n, m) {
var mat = new Array(n);
for (var i = 0; i < n; i++) {
mat[i] = new Array(m);
}
return mat;
}
function argmin(vals) {
var min = 0;
for (var i = 1; i < vals.length; i++) {
if (vals[i] < vals[min]) {
min = i;
}
}
return min;
}
function align(fst, snd) {
var n = fst.length;
var m = snd.length;
// edit distance matrix
var dist = matrix(n + 1, m + 1);
// indices to strings where the chars are equal
var equal = [];
// NOTE: these weights must be set in a way that they build such tables which lead to alignments
// corresponding to actual rules transformations performed by tipograph
var ins = 3;
var del = 1;
var sub = 3;
// set the "trivial" cells
dist[0][0] = 0;
for (var k = 0; k < m; k++) {
dist[0][k + 1] = dist[0][k] + ins;
}
for (var l = 0; l < n; l++) {
dist[l + 1][0] = dist[l][0] + del;
}
// build the table
for (var i = 0; i < n; i++) {
for (var j = 0; j < m; j++) {
if (fst[i] === snd[j]) {
dist[i + 1][j + 1] = dist[i][j];
equal.push([i, j]);
} else {
// d_del, d_sub, d_ins
var dists = [dist[i][j + 1] + del, dist[i][j] + sub, dist[i + 1][j] + ins];
var min = argmin(dists);
dist[i + 1][j + 1] = dists[min];
}
}
}
if (equal.length === 0) {
// no characters are equal
return null;
} else {
// find the indices whose characters have the minimal distance
var out = argmin(equal.map(function (coords) {
return dist[coords[0] + 1][coords[1] + 1];
}));
return equal[out];
}
}
export function find(original, converted) {
// add sentinel chars which always match each other
original += '\0';
converted += '\0';
var changes = [];
var i = 0;
var j = 0;
while (i < original.length && j < converted.length) {
if (original[i] == converted[j]) {
i++;
j++;
} else {
var alignment;
// NOTE: this bound must be large enough to cover the longest rule transformation
var bound = 10;
// NOTE: this loop is guaranteed to terminate because of '\0's at the ends
do {
alignment = align(original.slice(i, i + bound), converted.slice(j, j + bound));
bound *= 2;
} while (alignment === null);
changes.push([[i, i + alignment[0]], [j, j + alignment[1]]]);
i += alignment[0] + 1;
j += alignment[1] + 1;
}
}
return changes;
}