define([], function () { var Util = {}; // If once is true, after the event has been fired, any further handlers which are // registered will fire immediately, and this type of event cannot be fired twice. Util.mkEvent = function (once) { var handlers = []; var fired = false; return { reg: function (cb) { if (once && fired) { return void setTimeout(cb); } handlers.push(cb); }, unreg: function (cb) { if (handlers.indexOf(cb) === -1) { throw new Error("Not registered"); } handlers.splice(handlers.indexOf(cb), 1); }, fire: function () { if (once && fired) { return; } fired = true; var args = Array.prototype.slice.call(arguments); handlers.forEach(function (h) { h.apply(null, args); }); } }; }; Util.find = function (map, path) { var l = path.length; for (var i = 0; i < l; i++) { if (typeof(map[path[i]]) === 'undefined') { return; } map = map[path[i]]; } return map; }; Util.uid = function () { return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) .toString(32).replace(/\./g, ''); }; Util.fixHTML = function (str) { if (!str) { return ''; } return str.replace(/[<>&"']/g, function (x) { return ({ "<": "<", ">": ">", "&": "&", '"': """, "'": "'" })[x]; }); }; Util.hexToBase64 = function (hex) { var hexArray = hex .replace(/\r|\n/g, "") .replace(/([\da-fA-F]{2}) ?/g, "0x$1 ") .replace(/ +$/, "") .split(" "); var byteString = String.fromCharCode.apply(null, hexArray); return window.btoa(byteString).replace(/\//g, '-').replace(/=+$/, ''); }; Util.base64ToHex = function (b64String) { var hexArray = []; atob(b64String.replace(/-/g, '/')).split("").forEach(function(e){ var h = e.charCodeAt(0).toString(16); if (h.length === 1) { h = "0"+h; } hexArray.push(h); }); return hexArray.join(""); }; Util.uint8ArrayToHex = function (a) { // call slice so Uint8Arrays work as expected return Array.prototype.slice.call(a).map(function (e) { var n = Number(e & 0xff).toString(16); if (n === 'NaN') { throw new Error('invalid input resulted in NaN'); } switch (n.length) { case 0: return '00'; // just being careful, shouldn't happen case 1: return '0' + n; case 2: return n; default: throw new Error('unexpected value'); } }).join(''); }; Util.deduplicateString = function (array) { var a = array.slice(); for(var i=0; i= oneGigabyte) { return 'GB'; } else if (bytes >= oneMegabyte) { return 'MB'; } }; Util.fetch = function (src, cb) { var done = false; var CB = function (err, res) { if (done) { return; } done = true; cb(err, res); }; var xhr = new XMLHttpRequest(); xhr.open("GET", src, true); xhr.responseType = "arraybuffer"; xhr.onload = function () { if (/^4/.test(''+this.status)) { return CB('XHR_ERROR'); } return void CB(void 0, new Uint8Array(xhr.response)); }; xhr.send(null); }; Util.throttle = function (f, ms) { var to; var g = function () { window.clearTimeout(to); to = window.setTimeout(f, ms); }; return g; }; /* takes a function (f) and a time (t) in ms. returns a function wrapper which prevents the internal function from being called more than once every t ms. if the function is prevented, returns time til next valid execution, else null. */ Util.notAgainForAnother = function (f, t) { if (typeof(f) !== 'function' || typeof(t) !== 'number') { throw new Error("invalid inputs"); } var last = null; return function () { var now = +new Date(); if (last && now <= last + t) { return t - (now - last); } last = now; f(); return null; }; }; Util.createRandomInteger = function () { return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); }; /* for wrapping async functions such that they can only be called once */ Util.once = function (f) { var called; return function () { if (called) { return; } called = true; f.apply(this, Array.prototype.slice.call(arguments)); }; }; Util.slice = function (A) { return Array.prototype.slice.call(A); }; Util.blobToImage = function (blob, cb) { var reader = new FileReader(); reader.onloadend = function() { cb(reader.result); }; reader.readAsDataURL(blob); }; Util.blobURLToImage = function (url, cb) { var xhr = new XMLHttpRequest(); xhr.onload = function() { var reader = new FileReader(); reader.onloadend = function() { cb(reader.result); }; reader.readAsDataURL(xhr.response); }; xhr.open('GET', url); xhr.responseType = 'blob'; xhr.send(); }; return Util; });