(function () { window.DEBUG = document.cookie.indexOf('wmdebug') > -1; window.$ = document.querySelector.bind(document); window.$all = document.querySelectorAll.bind(document); var alphaNum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; // https://github.com/substack/semver-compare/blob/master/index.js function semverCompare(a, b) { var pa = a.split('.'); var pb = b.split('.'); for (var i = 0; i < 3; i++) { var na = Number(pa[i]); var nb = Number(pb[i]); if (na > nb) { return 1; } if (nb > na) { return -1; } if (!isNaN(na) && isNaN(nb)) { return 1; } if (isNaN(na) && !isNaN(nb)) { return -1; } } return 0; } function generateRandomId(len) { var length = len || 10; var id = ''; for (var i = length; i--;) { id += alphaNum[~~(Math.random() * alphaNum.length)]; } return id; } function onButtonClick(btn, listener) { btn.addEventListener('click', function buttonClickListener(e) { listener(e); return false; }); } function log() { if (window.DEBUG) { console.log(...arguments); } } // Generate 2 ASTs for the code to be inserted in loops for infinite run protection. // The `myVar` variable names would be changed later for every insertion. function getLoopProtectorBlocks() { var ast1 = esprima.parse('var myvar = Date.now();'); var ast2 = esprima.parse('while(a){if (Date.now() - a787897 > 1000) { window.top.previewException(new Error("Infinite loop")); break;}}'); return { before: ast1.body[0], inside: ast2.body[0].body.body[0] } } /** * Add timed limit on the loops found in the passed AST body * @param {ASTBody} Body of an AST generated by esprima or any ES compliant AST */ function addInfiniteLoopProtection(astBody) { if (!astBody) { return; } if (!Array.isArray(astBody)) { addInfiniteLoopProtection(astBody.body); return; } var el, randomVariableName, insertionBLocks; for (var i = astBody.length; i--;) { el = astBody[i]; if (el && el.type === 'ForStatement' || el.type === 'WhileStatement' || el.type === 'DoWhileStatement') { randomVariableName = '_' + generateRandomId(3); insertionBLocks = getLoopProtectorBlocks(); insertionBLocks.before.declarations[0].id.name = insertionBLocks.inside.test.left.right.name = randomVariableName; // Insert time variable assignment astBody.splice(i, 0, insertionBLocks.before); // If the loop's body is a single statement, then convert it into a block statement // so that we can insert our conditional break inside it. if (!Array.isArray(el.body)) { el.body = { body: [ el.body ], type: 'BlockStatement' }; } // Insert IfStatement el.body.body.unshift(insertionBLocks.inside); } if (el.body) { addInfiniteLoopProtection(el.body); } } } function getHumanDate(timestamp) { var d = new Date(timestamp); var retVal = d.getDate() + ' ' + [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][d.getMonth()] + ' ' + d.getFullYear(); return retVal; } // create a one-time event function once(node, type, callback) { // create event node.addEventListener(type, function(e) { // remove event e.target.removeEventListener(type, arguments.callee); // call handler return callback(e); }); } window.utils = { semverCompare: semverCompare, generateRandomId: generateRandomId, onButtonClick: onButtonClick, addInfiniteLoopProtection: addInfiniteLoopProtection, getHumanDate: getHumanDate, log: log, once: once }; })();