|
| 1 | +include("shared/lib/notify.js"); |
| 2 | + |
| 3 | +function runWithString(string) { |
| 4 | + // Easy access to preferences and bug reporting if Command is pressed. |
| 5 | + if (LaunchBar.options.commandKey) { |
| 6 | + Lib.Notify.force_prompt(); |
| 7 | + return []; |
| 8 | + } |
| 9 | + |
| 10 | + try { |
| 11 | + var data = parse(string); |
| 12 | + |
| 13 | + // Convert it to intermediary radix (base 10) |
| 14 | + var result = from10(to10(data.num, data.from), data.to); |
| 15 | + |
| 16 | + // Formatted result |
| 17 | + var formatted_result = format(result, data.to); |
| 18 | + |
| 19 | + return [ |
| 20 | + { |
| 21 | + title: formatted_result, |
| 22 | + subtitle: "Result", |
| 23 | + label: "Base " + data.to |
| 24 | + }, |
| 25 | + formatted_result != result ? { |
| 26 | + title: result, |
| 27 | + subtitle: "Unformatted result", |
| 28 | + label: "Base " + data.to |
| 29 | + } : {}, |
| 30 | + { |
| 31 | + title: format(data.num, data.from), |
| 32 | + subtitle: "From", |
| 33 | + label: "Base " + data.from + (data.guessed ? " (guessed)" : "") |
| 34 | + } |
| 35 | + ]; |
| 36 | + } catch (err) { |
| 37 | + Lib.Notify.error(err); |
| 38 | + return []; |
| 39 | + } |
| 40 | +} |
| 41 | + |
| 42 | +/** |
| 43 | + * Format a number with commas (or spaces with binary) as appropriate. |
| 44 | + * |
| 45 | + * @param {string} nstr - The number string to be formatted |
| 46 | + * @param {int} base - The base nstr is in |
| 47 | + * @param {int} [from] - The base nstr was in (only relevant when base is 2) |
| 48 | + * @return {string} - The formatted number |
| 49 | + */ |
| 50 | +function format(nstr, base, from) { |
| 51 | + assert(typeof nstr == "string", "format(" + nstr + ", ...): not a string!"); |
| 52 | + assert(isNumeric(base), "format(..., " + base + "): not a number!"); |
| 53 | + |
| 54 | + if (base == 2 && from != 2) { |
| 55 | + var log = Math.log2(from); |
| 56 | + if (log != parseInt(log)) { |
| 57 | + log = 4; |
| 58 | + } |
| 59 | + return nstr.replace(new RegExp("\B(?=(\d{" + log + "})+(?!\d))", "gi"), " "); |
| 60 | + } else { |
| 61 | + return nstr.replace(/\B(?=(\d{3})+(?!\d))/gi, ","); |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +/** |
| 66 | + * Parses the input. |
| 67 | + * |
| 68 | + * @param {string} string - The raw input from Launchbar |
| 69 | + * @return {Object} data - Information about the number |
| 70 | + * @return {string} data.num - The number to be converted |
| 71 | + * @return {int} data.from - The radix to be converted from |
| 72 | + * @return {int} data.to - The radix to be converted to |
| 73 | + * @return {bool} data.guessed - Whether or not data.from was guessed from data.num |
| 74 | + */ |
| 75 | +function parse(string) { |
| 76 | + assert(typeof string == "string", "parse(string): not an string!"); |
| 77 | + assert(string.trim() != "", "parse(string): string is empty!"); |
| 78 | + assert(string.search(/[^0-9a-zA-Z,._ ]+/) == -1, "Invalid input!"); |
| 79 | + |
| 80 | + parts = string.split(" "); |
| 81 | + if (parts.length == 1) { |
| 82 | + parts[1] = 10; |
| 83 | + } |
| 84 | + |
| 85 | + var data = { |
| 86 | + num: parts[0].toLowerCase(), |
| 87 | + to: parseInt(parts[1]), |
| 88 | + from: 10, |
| 89 | + guessed: true |
| 90 | + }; |
| 91 | + data.num = data.num.replace(",", ""); |
| 92 | + |
| 93 | + if (data.num.indexOf("_") != -1) { |
| 94 | + var _num = data.num.split("_"); |
| 95 | + data.num = _num[0]; |
| 96 | + data.from = parseInt(_num[1]); |
| 97 | + data.guessed = false; |
| 98 | + } else { |
| 99 | + data.from = guessBase(data.num); |
| 100 | + } |
| 101 | + |
| 102 | + if (data.from < 2 || data.from > 36 || data.to < 2 || data.to > 36) { |
| 103 | + throw "Invalid radix; this converter only supports 2 <= r <= 36"; |
| 104 | + } |
| 105 | + |
| 106 | + return data; |
| 107 | +} |
| 108 | + |
| 109 | +/** |
| 110 | + * Converts [num] to base 10, from base [from] |
| 111 | + * |
| 112 | + * @param {string} num - The number to convert |
| 113 | + * @param {int} from - The source radix |
| 114 | + * @return {float} - The converted number in decimal |
| 115 | + */ |
| 116 | +function to10(num, from) { |
| 117 | + assert(typeof num == "string", "to10(" + num + ", ...): not a string!"); |
| 118 | + assert(isNumeric(from), "to10(..., " + from + "): not a number!"); |
| 119 | + |
| 120 | + if (from == 10) { |
| 121 | + return parseFloat(num); |
| 122 | + } |
| 123 | + |
| 124 | + var digits = num; |
| 125 | + var n, m; |
| 126 | + |
| 127 | + if ((n = digits.indexOf(".")) == -1) { |
| 128 | + n = digits.length; |
| 129 | + m = 0; |
| 130 | + } else { |
| 131 | + m = -(digits.length - (n + 1)); |
| 132 | + } |
| 133 | + --n; |
| 134 | + |
| 135 | + var result = 0; |
| 136 | + for (var j = 0, i = n; i >= m; --i, j = (n - m) - i) { |
| 137 | + if (digits[j] != "0") { |
| 138 | + result += (parseInt(digits[j], from) * Math.pow(from, i)); |
| 139 | + } |
| 140 | + } |
| 141 | + return result; |
| 142 | +} |
| 143 | + |
| 144 | +/** |
| 145 | + * Converts [num] to N in base [to] |
| 146 | + * |
| 147 | + * @param {Number} num - The number to convert |
| 148 | + * @param {int} to - Radix to convert to |
| 149 | + * @return {Number} - Converted amount in new base |
| 150 | + */ |
| 151 | +function from10(num, to) { |
| 152 | + assert(isNumeric(num), "from10(" + num + ", ...): not a number!"); |
| 153 | + assert(isNumeric(to), "from10(..., " + to + "): not a number!"); |
| 154 | + |
| 155 | + return num.toString(to); |
| 156 | +} |
| 157 | + |
| 158 | +/** |
| 159 | + * Tries to guess the radix of data.num, and removes prefixes if any. Defaults to 10 if it |
| 160 | + * fails to guess. Hold down shift to force the guess to be 10. |
| 161 | + * |
| 162 | + * @param {string} nstr - The number the radix should be deduced from |
| 163 | + * @return {int} - The guessed radix |
| 164 | + */ |
| 165 | +function guessBase(nstr) { |
| 166 | + assert(typeof nstr == "string", "guessBase(nstr): not an string!"); |
| 167 | + |
| 168 | + if (!LaunchBar.options.shiftKey) { |
| 169 | + if (nstr.search(/[g-z]+/) != -1) { |
| 170 | + return 36; |
| 171 | + } |
| 172 | + else if (nstr.search(/[a-f]+/) != -1) { |
| 173 | + return 16; |
| 174 | + } |
| 175 | + else if (nstr.search(/[^2-9a-zA-Z,._]+/) != -1) { |
| 176 | + return 2; |
| 177 | + } |
| 178 | + } |
| 179 | + return 10; |
| 180 | +} |
| 181 | + |
| 182 | +/** |
| 183 | + * Tests whether n is numeric or not (as a string or number). |
| 184 | + * |
| 185 | + * @private |
| 186 | + * @param {string|number} n - The value to test |
| 187 | + * @return {bool} - Whether n is numeric or not |
| 188 | + */ |
| 189 | +function isNumeric(n) { |
| 190 | + return !isNaN(parseFloat(n)) && isFinite(n); |
| 191 | +} |
0 commit comments