NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name US customary to metric autoconvert
// @version 1.0
// @description Converts US customary (or Imperial) units to metric automatically on predefined sites.
// @include http://*food*
// @include https://*food*
// @include http://*recipe*
// @include https://*recipe*
// @run-at document-start
// @grant none
// ==/UserScript==
document.addEventListener('DOMContentLoaded', function(){
var mode_replace = false;
var precision = 3;
var re_matrix = [
{r: /(?:fl\.?\s*)?(?:\bounce|\boz)s?\b(?:\s*fl\.)?/ig, f: function ounce2m(numtxt) { if(/fl\.?\s*(?:\bounce|\boz)s?\b/ig.test(numtxt[0]) || /(?:\bounce|\boz)s?\b\s*fl\.?/ig.test(numtxt[0])) normalize(text2num(numtxt[1]) * 0.0295735, 'l'); return normalize(text2num(numtxt[1]) * 28.3495, 'g');} },
{r: /\b(?:pound|lb)s?\b/ig, f: function pound2m(numtxt) { return normalize(text2num(numtxt[1]) * 453.592, 'g');} },
{r: /\bpints?\b/ig, f: function ounce2m(numtxt) { return normalize(text2num(numtxt[1]) * 0.473176, 'l');} },
{r: /\bcups?\b/ig, f: function cup2m(numtxt) { return normalize(text2num(numtxt[1]) * 0.236588, 'l');} },
{r: /\binch(?:es)?\b/ig, f: function inch2m(numtxt) { return normalize(text2num(numtxt[1]) * 0.0254, 'm');} },
{r: /\bf(?:ee|oo)?t\b/ig, f: function foot2m(numtxt) { return normalize(text2num(numtxt[1]) * 0.3048, 'm');} },
{r: /\byards?\b/ig, f: function yard2m(numtxt) { return normalize(text2num(numtxt[1]) * 0.9144, 'm');} },
{r: /\bmiles?\b/ig, f: function mile2m(numtxt) { return normalize(text2num(numtxt[1]) * 1609.34, 'm');} },
{r: /\bstones?\b/ig, f: function stone2m(numtxt) { return normalize(text2num(numtxt[1]) * 6350.29, 'g');} },
{r: /\bus tons?\b/ig, f: function uston2m(numtxt) { return normalize(text2num(numtxt[1]) * 907185, 'g');} },
{r: /\bgallons?\b/ig, f: function gallon2m(numtxt) { return normalize(text2num(numtxt[1]) * 3.78541, 'l');} },
{r: /\bquarts?\b/ig, f: function quart2m(numtxt) { return normalize(text2num(numtxt[1]) * 0.946353, 'l');} },
{r: /(?:\u2070|\u2218|\u02DA|\u00B0|\u00BA|°|°|deg|degree|degrees)\s*(?:Fahrenheit|F|℃|℉|\u2103|\u2109)/ig, f: function fahr2m(numtxt) { return normalize((5/9)*(text2num(numtxt[1]) - 32), 'c');} }
];
function normalize(num, system) {
if (typeof num !== 'number' || num === 0 || isNaN(num)) return null;
switch (system) {
case 'm':
if(num > 1000) { num /= 1000; system = 'km'; }
else if(num < 0.02) { num *= 1000; system = 'mm'; }
else if(num < 1) { num *= 100; system = 'cm'; }
break;
case 'l':
if(num < 1) { num *= 1000; num = Math.ceil(num); system = 'ml'; }
break;
case 'g':
if (num > 1e6) { num /= 1e6; system = 't'; }
else if(num > 1000) { num /= 1000; system = 'kg'; }
else if(num < 5) { num *= 1000; system = 'mg'; }
break;
case 'c':
return parseFloat(num.toPrecision(precision).toString()) + '\u00B0C';
}
return parseFloat(num.toPrecision(precision).toString()) + (system ? '\u00A0' + system : '');
}
/**
// Damerau-Levenshtein distance between two strings
function dl_dist(source, target) {
if (!source) return target ? target.length : 0;
else if (!target) return source.length;
var m = source.length, n = target.length, INF = m+n, score = new Array(m+2), sd = {};
for (var i = 0; i < m+2; i++) score[i] = new Array(n+2);
score[0][0] = INF;
for (var i = 0; i <= m; i++) {
score[i+1][1] = i;
score[i+1][0] = INF;
sd[source[i]] = 0;
}
for (var j = 0; j <= n; j++) {
score[1][j+1] = j;
score[0][j+1] = INF;
sd[target[j]] = 0;
}
for (var i = 1; i <= m; i++) {
var DB = 0;
for (var j = 1; j <= n; j++) {
var i1 = sd[target[j-1]],
j1 = DB;
if (source[i-1] === target[j-1]) {
score[i+1][j+1] = score[i][j];
DB = j;
}
else {
score[i+1][j+1] = Math.min(score[i][j], Math.min(score[i+1][j], score[i][j+1])) + 1;
}
score[i+1][j+1] = Math.min(score[i+1][j+1], score[i1] ? score[i1][j1] + (i-i1-1) + 1 + (j-j1-1) : Infinity);
}
sd[source[i-1]] = i;
}
return score[m+1][n+1];
}
*/
var smalls = {'zero': 0, 'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6, 'seven': 7, 'eight': 8, 'nine': 9, 'ten': 10, 'eleven': 11, 'twelve': 12, 'thirteen': 13, 'fourteen': 14, 'fifteen': 15, 'sixteen': 16, 'seventeen': 17, 'eighteen': 18, 'nineteen': 19, 'twenty': 20, 'thirty': 30, 'forty': 40, 'fifty': 50, 'sixty': 60, 'seventy': 70, 'eighty': 80, 'ninety': 90 };
var bigs = {
'hundred': 100,
'thousand': 1000,
'million': 1000000,
'billion': 1000000000,
};
/*var textnumbers = 'zero|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety|hundred|thousand|million|billion'.split('|');*/
function text2num(s) {
s = s.toString().trim();
var m, a,
n = 0,
g = 0
sgn = 1;
if (/([-\d\.\/][ ,]?){1,3}/.test(s)) {
if (s[0] === '-') {
sgn = -1;
a[0] = a[0].replace(/^-/, '');
}
a = s.split(/[\s-,]+/);
if (m = a[a.length-1].match(/(\d+)\/(\d+)/)) {
n += parseFloat(m[1]/m[2]) || 0;
a.pop();
}
if (a.length) n += parseInt(a.join(''), 10);
return sgn * n;
} else {
a = s.split(/[\s-]+/);
if (a[0].toLowerCase() === 'minus') {
sgn = -1;
a.shift();
}
for (var x = 0, i = 0; i < a.length; i++) {
/*for (var j = 0; j < textnumbers.length; j++)
if (dl_dist(a[i], textnumbers[j]) === 1) { // max one error per word
a[i] = textnumbers[j];
break;
}*/
if (x = smalls[a[i]]) {
g = g + x || 0;
} else if (x = bigs[a[i]]) {
if (x) {
n = n + (g || 1) * x
g = 0;
} else return 0;
}
}
return sgn * (n + g);
}
}
var allowedParents = [ 'address', 'b', 'bdo', 'big', 'article', 'section', 'aside', 'footer', 'blockquote', 'body', 'caption', 'cite', 'dd', 'del', 'div', 'dfn', 'dt', 'em', 'fieldset', 'font', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'i', 'ins', 'kdb', 'li', 'nobr', 'pre', 'p', 'q', 'samp', 'small', 'span', 'strike', 's', 'strong', 'sub', 'sup', 'td', 'th', 'tt', 'u', 'var' ];
var xpath = '//text()[normalize-space() and (parent::' + allowedParents.join(' or parent::') + ')]';
var re_number = /((?:(?:minus|zero|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety|hundred|thousand|million|billion|[\d.\/]+)[ ,]{0,1}){1,4})-?/;
var candidates = document.evaluate(xpath, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (var cnd = null, i = 0; (cnd = candidates.snapshotItem(i)); i++) {
for (var j = 0; j < re_matrix.length; j++) {
var original = cnd.nodeValue;
re_matrix[j].r.lastIndex = 0;
if (re_matrix[j].r.test(original)) {
var lastLastIndex, converted = '',
re = new RegExp(re_number.source + re_matrix[j].r.source, 'ig'),
match = null;
re.lastIndex = 0;
for (lastLastIndex = 0; (match = re.exec(original)); ) {
var metric = re_matrix[j].f(match);
if (!metric) continue;
converted += original.substring(lastLastIndex, match.index);
converted += mode_replace ? metric : (match[0] + ' /' + metric + '/');
lastLastIndex = re.lastIndex;
}
if (converted !== '') {
if (lastLastIndex) converted += original.substring(lastLastIndex);
cnd.nodeValue = converted;
}
}
}
}
}, false);