UNPKG

boomerangjs

Version:

boomerang always comes back, except when it hits something

161 lines (130 loc) 4.09 kB
(function(w){ if (!w.XMLHttpRequest || !(new w.XMLHttpRequest()).addEventListener) { return; } var a = document.createElement("A"), xhrNative = w.XMLHttpRequest, resources = [], sendResource, readyStateMap = ["uninitialized", "open", "responseStart", "domInteractive", "responseEnd"]; w.BOOMR = w.BOOMR || {}; // xhr object is what the AutoXHR plugin will interact with once it's loaded BOOMR.xhr = { /** * Stops XHR collection and forwards any additional reporting to Boomerang * * @param {function} sendResourceCallback Callback for any ongoing monitoring * * @returns {object} Array of XHRs */ stop: function(sendResourceCallback) { sendResource = sendResourceCallback; // swap back in the native XHR function w.XMLHttpRequest = xhrNative; delete BOOMR.xhr; // clear our queue after a moment setTimeout(function(){ resources = []; }, 10); return resources; } }; // Gathers a high-resolution timestamp (when available), or falls back to Date.getTime() var now = (function() { try { if ("performance" in w && w.performance.timing) { return function() { return Math.round(w.performance.now() + performance.timing.navigationStart); }; } } catch (ignore) { // NOP } return Date.now || function() { return new Date().getTime(); }; })(); // Overwrite the native XHR with our monitored object w.XMLHttpRequest = function() { var xhr = new xhrNative(), open = xhr.open; xhr.open = function(method, url, async) { // Normalize the URL a.href = url; var resource = { timing: {}, url: a.href, method: method }; // Callback when the XHR is finished function loadFinished() { if (!resource.timing.loadEventEnd) { resource.timing.loadEventEnd = now(); if ("performance" in w && w.performance && typeof w.performance.getEntriesByName === "function") { var entries = w.performance.getEntriesByName(resource.url); var entry = entries && entries.length && entries[entries.length - 1]; if (entry) { var navSt = w.performance.timing.navigationStart; if (entry.responseEnd !== 0) { resource.timing.responseEnd = Math.round(navSt + entry.responseEnd); } if (entry.responseStart !== 0) { resource.timing.responseStart = Math.round(navSt + entry.responseStart); } if (entry.startTime !== 0) { resource.timing.requestStart = Math.round(navSt + entry.startTime); } } } if (sendResource) { sendResource(resource); } else { resources.push(resource); } } } function addListener(ename, stat) { xhr.addEventListener( ename, function() { if (ename === "readystatechange") { resource.timing[readyStateMap[xhr.readyState]] = now(); if (xhr.readyState === 4) { loadFinished(); } } else { resource.status = (stat === undefined ? xhr.status : stat); loadFinished(); } }, false); } if (async === true) { addListener("readystatechange"); } else { resource.synchronous = true; } addListener("load"); addListener("timeout", -1001); addListener("error", -998); addListener("abort", -999); try { open.apply(xhr, arguments); var send = xhr.send; xhr.send = function() { resource.timing.requestStart = now(); send.apply(xhr, arguments); }; } catch (e) { resource.status = -997; loadFinished(); } }; return xhr; }; })(window);