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);