/** Make a node */ function mk (e) { return document.createElement(e) } /** Find one by query */ function qs (s) { return document.querySelector(s) } /** Find all by query */ function qsa (s) { return document.querySelectorAll(s) } /** Convert any to bool safely */ function bool (x) { return (x === 1 || x === '1' || x === true || x === 'true') } /** * Filter 'spacebar' and 'return' from keypress handler, * and when they're pressed, fire the callback. * use $(...).on('keypress', cr(handler)) */ function cr (hdl) { return function (e) { if (e.which == 10 || e.which == 13 || e.which == 32) { hdl() } } } /** Extend an objects with options */ function extend (defaults, options) { var target = {} Object.keys(defaults).forEach(function (k) { target[k] = defaults[k] }) Object.keys(options).forEach(function (k) { target[k] = options[k] }) return target } /** Escape string for use as literal in RegExp */ function rgxe (str) { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') } /** Format number to N decimal places, output as string */ function numfmt (x, places) { var pow = Math.pow(10, places) return Math.round(x * pow) / pow } /** Get millisecond timestamp */ function msNow () { return +(new Date()) } /** Get ms elapsed since msNow() */ function msElapsed (start) { return msNow() - start } /** Shim for log base 10 */ Math.log10 = Math.log10 || function (x) { return Math.log(x) / Math.LN10 } /** * Perform a substitution in the given string. * * Arguments - array or list of replacements. * Arguments numeric keys will replace {0}, {1} etc. * Named keys also work, ie. {foo: "bar"} -> replaces {foo} with bar. * * Braces are added to keys if missing. * * @returns {String} result */ String.prototype.format = function () { let out = this let repl = arguments if (arguments.length == 1 && (Array.isArray(arguments[0]) || typeof arguments[0] == 'object')) { repl = arguments[0] } for (let ph in repl) { if (repl.hasOwnProperty(ph)) { const ph_orig = ph if (!ph.match(/^\{.*\}$/)) { ph = '{' + ph + '}' } // replace all occurrences const pattern = new RegExp(rgxe(ph), 'g') out = out.replace(pattern, repl[ph_orig]) } } return out } /** HTML escape */ function e (str) { return $.htmlEscape(str) } /** Check for undefined */ function undef (x) { return typeof x == 'undefined' } /** Safe json parse */ function jsp (str) { try { return JSON.parse(str) } catch (e) { console.error(e) return null } } /** Create a character from ASCII code */ function Chr (n) { return String.fromCharCode(n) } /** Decode number from 2B encoding */ function parse2B (s, i = 0) { return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127 } /** Decode number from 3B encoding */ function parse3B (s, i = 0) { return (s.charCodeAt(i) - 1) + (s.charCodeAt(i + 1) - 1) * 127 + (s.charCodeAt(i + 2) - 1) * 127 * 127 } /** Encode using 2B encoding, returns string. */ function encode2B (n) { let lsb, msb lsb = (n % 127) n = ((n - lsb) / 127) lsb += 1 msb = (n + 1) return Chr(lsb) + Chr(msb) } /** Encode using 3B encoding, returns string. */ function encode3B (n) { let lsb, msb, xsb lsb = (n % 127) n = (n - lsb) / 127 lsb += 1 msb = (n % 127) n = (n - msb) / 127 msb += 1 xsb = (n + 1) return Chr(lsb) + Chr(msb) + Chr(xsb) }