"use strict"; var _MiniProfiler = (function() { var _arguments = arguments; var options, container, controls, fetchedIds = (window.MiniProfiler && window.MiniProfiler.fetchedIds) || [], fetchingIds = (window.MiniProfiler && window.MiniProfiler.fetchingIds) || [], // so we never pull down a profiler twice ajaxStartTime, totalsControl, reqs = 0, expandedResults = false, totalTime = 0, totalSqlCount = 0; var hasLocalStorage = function hasLocalStorage(keyPrefix) { try { // attempt to save to localStorage as Safari private windows will throw an error localStorage[keyPrefix + "-test"] = "1"; localStorage.removeItem(keyPrefix + "-test"); return "localStorage" in window && window["localStorage"] !== null; } catch (e) { return false; } }; var getVersionedKey = function getVersionedKey(keyPrefix) { return keyPrefix + "-" + options.version; }; // polyfills as helper functions to avoid conflicts // needed for IE11 // remove and replace with Element.closest when we drop IE11 support // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest var elementMatches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; var elementClosest = function elementClosest(el, s) { if (typeof el.closest === "function") { return el.closest(s); } do { if (typeof el.matches === "function") { if (el.matches(s)) return el; } else { if (elementMatches.call(el, s)) return el; } el = el.parentElement || el.parentNode; } while (el !== null && el.nodeType === 1); return null; }; var save = function save(keyPrefix, value) { if (!hasLocalStorage(keyPrefix)) { return; } // clear old keys with this prefix, if any for (var i = 0; i < localStorage.length; i++) { if ((localStorage.key(i) || "").indexOf(keyPrefix) > -1) { localStorage.removeItem(localStorage.key(i)); } } // save under this version localStorage[getVersionedKey(keyPrefix)] = value; }; var load = function load(keyPrefix) { if (!hasLocalStorage(keyPrefix)) { return null; } return localStorage[getVersionedKey(keyPrefix)]; }; var getClientPerformance = function getClientPerformance() { return window.performance === null ? null : window.performance; }; var fetchResults = function fetchResults(ids) { var clientPerformance, clientProbes, i, j, p, id, idx; for (i = 0; i < ids.length; i++) { id = ids[i]; clientPerformance = null; clientProbes = null; if (window.mPt) { clientProbes = mPt.results(); for (j = 0; j < clientProbes.length; j++) { clientProbes[j].d = clientProbes[j].d.getTime(); } mPt.flush(); } if (id == options.currentId) { clientPerformance = getClientPerformance(); if (clientPerformance !== null) { // ie is buggy strip out functions var copy = { navigation: {}, timing: clientPerformance.timing.toJSON() }; if (clientPerformance.navigation) { copy.navigation.redirectCount = clientPerformance.navigation.redirectCount; } clientPerformance = copy; } } else if ( ajaxStartTime !== null && clientProbes && clientProbes.length > 0 ) { clientPerformance = { timing: { navigationStart: ajaxStartTime.getTime() } }; ajaxStartTime = null; } if (fetchedIds.indexOf(id) < 0 && fetchingIds.indexOf(id) < 0) { idx = fetchingIds.push(id) - 1; (function() { var request = new XMLHttpRequest(); var url = options.path + "results"; var params = { id: id, clientPerformance: clientPerformance, clientProbes: clientProbes, popup: 1 }; var queryParam = toQueryString(params); request.open("POST", url, true); request.onload = function() { if (request.status >= 200 && request.status < 400) { var json = JSON.parse(request.responseText); fetchedIds.push(id); if (json != "hidden" && MiniProfiler.templates) { buttonShow(json); } } fetchingIds.splice(idx, 1); }; request.setRequestHeader("Accept", "application/json"); request.setRequestHeader("X-Requested-With", "XMLHttpRequest"); request.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" ); request.send(queryParam); })(); } } }; var toQueryString = function toQueryString(data, parentKey) { var result = []; for (var key in data) { var val = data[key]; var newKey = !parentKey ? key : parentKey + "[" + key + "]"; if ( typeof val === "object" && !Array.isArray(val) && val !== null && val !== undefined ) { result[result.length] = toQueryString(val, newKey); } else { if (Array.isArray(val)) { val.forEach(function(v) { result[result.length] = encodeURIComponent(newKey + "[]") + "=" + encodeURIComponent(v); }); } else if (val === null || val === undefined) { result[result.length] = encodeURIComponent(newKey) + "="; } else { result[result.length] = encodeURIComponent(newKey) + "=" + encodeURIComponent(val.toString()); } } } return result .filter(function(element) { return element && element.length > 0; }) .join("&"); }; var renderTemplate = function renderTemplate(json) { var textDom = MiniProfiler.templates.profilerTemplate(json); var tempElement = document.createElement("DIV"); tempElement.innerHTML = textDom; return tempElement.children[0]; }; var buttonShow = function buttonShow(json) { var result = renderTemplate(json); totalTime += parseFloat(json.duration_milliseconds, 10); totalSqlCount += parseInt(json.sql_count); reqs++; if (!controls && reqs > 1 && options.collapseResults && !expandedResults) { if (!totalsControl) { toArray(container.querySelectorAll(".profiler-result")).forEach( function(el) { return (el.style.display = "none"); } ); totalsControl = document.createElement("div"); totalsControl.setAttribute("class", "profiler-result"); totalsControl.innerHTML = "
"; container.appendChild(totalsControl); totalsControl.addEventListener("click", function() { toArray( totalsControl.parentNode.querySelectorAll(".profiler-result") ).forEach(function(el) { return (el.style.display = "block"); }); totalsControl.style.display = "none"; expandedResults = true; }); toArray(totalsControl.querySelectorAll(".profiler-button")).forEach( function(el) { return (el.style.display = "block"); } ); } var reqsHtml = reqs > 1 ? "" + reqs + "" : ""; var sqlHtml = options.showTotalSqlCount && totalSqlCount > 0 ? " / " + totalSqlCount + " sql" : ""; totalsControl.querySelector(".profiler-button").innerHTML = "" + totalTime.toFixed(1) + " ms" + sqlHtml + reqsHtml; result.style.display = "none"; } if (controls) result.insertBefore(controls); else container.appendChild(result); var button = result.querySelector(".profiler-button"), popup = result.querySelector(".profiler-popup"); // button will appear in corner with the total profiling duration - click to show details button.addEventListener("click", function() { buttonClick(button, popup); }); // small duration steps and the column with aggregate durations are hidden by default; allow toggling toggleHidden(popup); // lightbox in the queries toArray(popup.querySelectorAll(".profiler-queries-show")).forEach(function( el ) { el.addEventListener("click", function() { queriesShow(this, result); }); }); // limit count if ( container.querySelectorAll(".profiler-result").length > options.maxTracesToShow ) { var elem = container.querySelector(".profiler-result"); if (elem) { elem.parentElement.removeChild(elem); } } button.style.display = "block"; }; var toggleHidden = function toggleHidden(popup) { var trivial = popup.querySelector(".profiler-toggle-trivial"); var childrenTime = popup.querySelector( ".profiler-toggle-duration-with-children" ); var trivialGaps = popup.parentNode.querySelector( ".profiler-toggle-trivial-gaps" ); var toggleIt = function toggleIt(node) { var link = node, klass = "profiler-" + link.getAttribute("class").substr("profiler-toggle-".length), isHidden = link.textContent.indexOf("show") > -1; var elements = toArray(popup.parentNode.querySelectorAll("." + klass)); if (isHidden) { elements.forEach(function(el) { return (el.style.display = "table-row"); }); } else { elements.forEach(function(el) { return (el.style.display = "none"); }); } var text = link.textContent; link.textContent = text.replace( isHidden ? "show" : "hide", isHidden ? "hide" : "show" ); popupPreventHorizontalScroll(popup); }; [childrenTime, trivial, trivialGaps].forEach(function(el) { if (el) { el.addEventListener("click", function() { toggleIt(this); }); } }); // if option is set or all our timings are trivial, go ahead and show them if ( trivial && (options.showTrivial || trivial.getAttribute("show-on-load")) ) { toggleIt(trivial); } // if option is set, go ahead and show time with children if (childrenTime && options.showChildrenTime) { toggleIt(childrenTime); } }; var toArray = function toArray(list) { var arr = []; if (!list) { return arr; } for (var i = 0; i < list.length; i++) { arr.push(list[i]); } return arr; }; var buttonClick = function buttonClick(button, popup) { // we're toggling this button/popup if (popup.offsetWidth > 0 || popup.offsetHeight > 0) { // if visible popupHide(button, popup); } else { var visiblePopups = toArray( container.querySelectorAll(".profiler-popup") ).filter(function(el) { return el.offsetWidth > 0 || el.offsetHeight > 0; }); // theirButtons = visiblePopups.siblings(".profiler-button"); var theirButtons = []; visiblePopups.forEach(function(el) { theirButtons.push(el.parentNode.querySelector(".profiler-button")); }); // hide any other popups popupHide(theirButtons, visiblePopups); // before showing the one we clicked popupShow(button, popup); } }; var popupShow = function popupShow(button, popup) { button.classList.add("profiler-button-active"); popupSetDimensions(button, popup); popup.style.display = "block"; popupPreventHorizontalScroll(popup); }; var popupSetDimensions = function popupSetDimensions(button, popup) { var px = button.offsetTop - 1, // position next to the button we clicked windowHeight = window.innerHeight, maxHeight = windowHeight - px - 40; // make sure the popup doesn't extend below the fold popup.style[options.renderVerticalPosition] = "".concat(px, "px"); popup.style.maxHeight = "".concat(maxHeight, "px"); popup.style[options.renderHorizontalPosition] = "".concat( button.offsetWidth - 3, "px" ); // move left or right, based on config }; var popupPreventHorizontalScroll = function popupPreventHorizontalScroll( popup ) { var childrenHeight = 0; toArray(popup.children).forEach(function(el) { childrenHeight += el.offsetHeight; }); popup.style.paddingRight = "".concat( childrenHeight > popup.offsetHeight ? 40 : 10, "px" ); }; var popupHide = function popupHide(button, popup) { if (button) { if (Array.isArray(button)) { button.forEach(function(el) { return el.classList.remove("profiler-button-active"); }); } else { button.classList.remove("profiler-button-active"); } } if (popup) { if (Array.isArray(popup)) { popup.forEach(function(el) { return (el.style.display = "none"); }); } else { popup.style.display = "none"; } } }; var queriesShow = function queriesShow(link, result) { result = result; var px = 30, win = window, width = win.innerWidth - 2 * px, height = win.innerHeight - 2 * px, queries = result.querySelector(".profiler-queries"); // opaque background var background = document.createElement("div"); background.classList.add("profiler-queries-bg"); document.body.appendChild(background); background.style.height = "".concat(window.innerHeight, "px"); background.style.display = "block"; // center the queries and ensure long content is scrolled queries.style[options.renderVerticalPosition] = "".concat(px, "px"); queries.style.maxHeight = "".concat(height, "px"); queries.style.width = "".concat(width, "px"); queries.style[options.renderHorizontalPosition] = "".concat(px, "px"); queries.querySelector("table").style.width = "".concat(width, "px"); // have to show everything before we can get a position for the first query queries.style.display = "block"; queriesScrollIntoView(link, queries, queries); // syntax highlighting prettyPrint(); }; var queriesScrollIntoView = function queriesScrollIntoView( link, queries, whatToScroll ) { var id = elementClosest(link, "tr").getAttribute("data-timing-id"), cells = toArray( queries.querySelectorAll('tr[data-timing-id="' + id + '"]') ); // ensure they're in view whatToScroll.scrollTop = (whatToScroll.scrollTop || 0) + cells[0].offsetTop - 100; // highlight and then fade back to original bg color; do it ourselves to prevent any conflicts w/ jquery.UI or other implementations of Resig's color plugin cells.forEach(function(el) { el.classList.add("higlight-animate"); }); setTimeout(function() { cells.forEach(function(el) { return el.classList.remove("higlight-animate"); }); }, 3000); }; var onTurboBeforeVisit = function onTurboBeforeVisit(e) { if(!e.defaultPrevented) { window.MiniProfilerContainer = document.querySelector('body > .profiler-results') window.MiniProfiler.pageTransition() } } var onTurboLoad = function onTurboLoad(e) { if(window.MiniProfilerContainer) { document.body.appendChild(window.MiniProfilerContainer) } } var onClickEvents = function onClickEvents(e) { // this happens on every keystroke, and :visible is crazy expensive in IE <9 // and in this case, the display:none check is sufficient. var popup = toArray(document.querySelectorAll(".profiler-popup")).filter( function(el) { return el.style.display === "block"; } ); if (!popup.length) { return; } popup = popup[0]; var button = popup.parentNode.querySelector(".profiler-button"), queries = elementClosest(popup, ".profiler-result").querySelector( ".profiler-queries" ), bg = document.querySelector(".profiler-queries-bg"), isEscPress = e.type == "keyup" && e.which == 27, hidePopup = false, hideQueries = false; if (bg && bg.style.display === "block") { hideQueries = isEscPress || (e.type == "click" && !(queries !== e.target && queries.contains(e.target)) && !(popup !== e.target && popup.contains(e.target))); } else if (popup.style.display === "block") { hidePopup = isEscPress || (e.type == "click" && !(popup !== e.target && popup.contains(e.target)) && !(button !== e.target && button.contains(e.target)) && button != e.target); } if (hideQueries) { bg.parentElement.removeChild(bg); queries.style.display = "none"; } if (hidePopup) { popupHide(button, popup); } }; var keydownEvent = function keydownEvent() { var results = document.querySelector(".profiler-results"); if (results.style.display === "none") { results.style.display = "block"; } else { results.style.display = "none"; } sessionStorage["rack-mini-profiler-start-hidden"] = results.style.display === "none"; }; var toggleShortcutEvent = function toggleShortcutEvent(e) { // simplified version of https://github.com/jeresig/jquery.hotkeys/blob/master/jquery.hotkeys.js var shortcut = options.toggleShortcut.toLowerCase(); var modifier = ""; ["alt", "ctrl", "shift"].forEach(function(k) { if (e[k + "Key"]) { modifier += "".concat(k, "+"); } }); var specialKeys = { 8: "backspace", 9: "tab", 10: "return", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 27: "esc", 32: "space", 59: ";", 61: "=", 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 173: "-", 186: ";", 187: "=" }; var shiftNums = { "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": '"', ",": "<", ".": ">", "/": "?", "\\": "|" }; var character = String.fromCharCode(e.which).toLowerCase(); var special = specialKeys[e.which]; var keys = []; if (special) { keys.push(special); } else { keys.push(character); keys.push(shiftNums[character]); } for (var i = 0; i < keys.length; i++) { if (modifier + keys[i] === shortcut) { keydownEvent(); break; } } }; var turbolinksSkipResultsFetch = function turbolinksSkipResultsFetch(event) { event.data.xhr.__miniProfilerSkipResultsFetch = true; }; var bindDocumentEvents = function bindDocumentEvents() { document.addEventListener("click", onClickEvents); document.addEventListener("keyup", onClickEvents); document.addEventListener("keyup", toggleShortcutEvent); if (typeof Turbolinks !== "undefined" && Turbolinks.supported) { document.addEventListener("page:change", unbindDocumentEvents); document.addEventListener("turbolinks:load", unbindDocumentEvents); document.addEventListener( "turbolinks:request-start", turbolinksSkipResultsFetch ); } if (options.hotwireTurboDriveSupport) { document.addEventListener("turbo:before-visit", onTurboBeforeVisit) document.addEventListener("turbo:load", onTurboLoad) } }; var unbindDocumentEvents = function unbindDocumentEvents() { document.removeEventListener("click", onClickEvents); document.removeEventListener("keyup", onClickEvents); document.removeEventListener("keyup", toggleShortcutEvent); document.removeEventListener("page:change", unbindDocumentEvents); document.removeEventListener("turbolinks:load", unbindDocumentEvents); document.removeEventListener( "turbolinks:request-start", turbolinksSkipResultsFetch ); document.removeEventListener("turbo:before-visit", onTurboBeforeVisit); document.removeEventListener("turbo:load", onTurboLoad); }; var initFullView = function initFullView() { // profiler will be defined in the full page's head container[0].appendChild(renderTemplate(profiler)); var popup = document.querySelector(".profiler-popup"); toggleHidden(popup); prettyPrint(); // since queries are already shown, just highlight and scroll when clicking a "1 sql" link toArray(popup.querySelectorAll(".profiler-queries-show")).forEach(function( el ) { el.addEventListener("click", function() { queriesScrollIntoView( this, document.querySelector(".profiler-queries"), document.body ); }); }); }; var initSnapshots = function initSnapshots(dataElement) { var data = JSON.parse(dataElement.textContent); var temp = document.createElement("DIV"); if (data.page === "overview") { temp.innerHTML = MiniProfiler.templates.snapshotsGroupsList(data); } else if (data.group_name) { var allCustomFieldsNames = []; data.list.forEach(function(snapshot) { Object.keys(snapshot.custom_fields).forEach(function(k) { if ( allCustomFieldsNames.indexOf(k) === -1 && options.hiddenCustomFields.indexOf(k.toLowerCase()) === -1 ) { allCustomFieldsNames.push(k); } }); }); allCustomFieldsNames.sort(); temp.innerHTML = MiniProfiler.templates.snapshotsList({ data: data, allCustomFieldsNames: allCustomFieldsNames }); } Array.from(temp.children).forEach(function(child) { document.body.appendChild(child); }); }; var initControls = function initControls(container) { if (options.showControls) { var _controls = document.createElement("div"); _controls.classList.add("profiler-controls"); _controls.innerHTML = 'mc'; container.appendChild(_controls); document .querySelector(".profiler-controls .profiler-min-max") .addEventListener("click", function() { return toggleClass(container, "profiler-min"); }); container.addEventListener("mouseenter", function() { if (this.classList.contains("profiler-min")) { this.querySelector(".profiler-min-max").style.display = "block"; } }); container.addEventListener("mouseleave", function() { if (this.classList.contains("profiler-min")) { this.querySelector(".profiler-min-max").style.display = "none"; } }); document .querySelector(".profiler-controls .profiler-clear") .addEventListener("click", function() { toArray(container.querySelectorAll(".profiler-result")).forEach( function(el) { return el.parentElement.removeChild(el); } ); }); } else { container.classList.add("profiler-no-controls"); } }; var toggleClass = function toggleClass(el, className) { if (el.classList) { el.classList.toggle(className); } else { var classes = el.className.split(" "); var existingIndex = classes.indexOf(className); if (existingIndex >= 0) classes.splice(existingIndex, 1); else classes.push(className); el.className = classes.join(" "); } }; var initPopupView = function initPopupView() { if (options.authorized) { // all fetched profilings will go in here container = document.createElement("div"); container.className = "profiler-results"; document.querySelector(options.htmlContainer).appendChild(container); // MiniProfiler.RenderIncludes() sets which corner to render in - default is upper left container.classList.add("profiler-" + options.renderHorizontalPosition); container.classList.add("profiler-" + options.renderVerticalPosition); //initialize the controls initControls(container); fetchResults(options.ids); if (options.startHidden) container.style.display = "none"; } else { fetchResults(options.ids); } if (!window.MiniProfiler || !window.MiniProfiler.patchesApplied) { var send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function(data) { ajaxStartTime = new Date(); this.addEventListener("load", function() { // responseURL isn't available in IE11 if ( this.responseURL && this.responseURL.indexOf(window.location.origin) !== 0 ) { return; } if (this.__miniProfilerSkipResultsFetch) { return; } // getAllResponseHeaders isn't available in Edge. var allHeaders = this.getAllResponseHeaders ? this.getAllResponseHeaders() : null; if ( allHeaders && allHeaders.toLowerCase().indexOf("x-miniprofiler-ids") === -1 ) { return; } // should be a string of comma-separated ids var stringIds = this.getResponseHeader("X-MiniProfiler-Ids"); if (stringIds) { var ids = stringIds.split(","); fetchResults(ids); } }); send.call(this, data); }; // fetch results after ASP Ajax calls if ( typeof Sys != "undefined" && typeof Sys.WebForms != "undefined" && typeof Sys.WebForms.PageRequestManager != "undefined" ) { // Get the instance of PageRequestManager. var PageRequestManager = Sys.WebForms.PageRequestManager.getInstance(); PageRequestManager.add_endRequest(function(sender, args) { if (args) { var response = args.get_response(); if ( response.get_responseAvailable() && response._xmlHttpRequest !== null ) { var stringIds = args .get_response() .getResponseHeader("X-MiniProfiler-Ids"); if (stringIds) { var ids = stringIds.split(","); fetchResults(ids); } } } }); } // more Asp.Net callbacks if (typeof WebForm_ExecuteCallback == "function") { WebForm_ExecuteCallback = (function(callbackObject) { // Store original function var original = WebForm_ExecuteCallback; return function(callbackObject) { original(callbackObject); var stringIds = callbackObject.xmlRequest.getResponseHeader( "X-MiniProfiler-Ids" ); if (stringIds) { var ids = stringIds.split(","); fetchResults(ids); } }; })(); } // also fetch results after ExtJS requests, in case it is being used if ( typeof Ext != "undefined" && typeof Ext.Ajax != "undefined" && typeof Ext.Ajax.on != "undefined" ) { // Ext.Ajax is a singleton, so we just have to attach to its 'requestcomplete' event Ext.Ajax.on("requestcomplete", function(e, xhr, settings) { //iframed file uploads don't have headers if (!xhr || !xhr.getResponseHeader) { return; } var stringIds = xhr.getResponseHeader("X-MiniProfiler-Ids"); if (stringIds) { var ids = stringIds.split(","); fetchResults(ids); } }); } if (typeof MooTools != "undefined" && typeof Request != "undefined") { Request.prototype.addEvents({ onComplete: function onComplete() { var stringIds = this.xhr.getResponseHeader("X-MiniProfiler-Ids"); if (stringIds) { var ids = stringIds.split(","); fetchResults(ids); } } }); } // add support for AngularJS, which use the basic XMLHttpRequest object. if (window.angular && typeof XMLHttpRequest != "undefined") { var _send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function sendReplacement(data) { if (this.onreadystatechange) { if ( typeof this.miniprofiler == "undefined" || typeof this.miniprofiler.prev_onreadystatechange == "undefined" ) { this.miniprofiler = { prev_onreadystatechange: this.onreadystatechange }; this.onreadystatechange = function onReadyStateChangeReplacement() { if (this.readyState == 4) { var stringIds = this.getResponseHeader("X-MiniProfiler-Ids"); if (stringIds) { var ids = stringIds.split(","); fetchResults(ids); } } if (this.miniprofiler.prev_onreadystatechange !== null) return this.miniprofiler.prev_onreadystatechange.apply( this, arguments ); }; } } return _send.apply(this, arguments); }; } // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API if (typeof window.fetch === "function") { var __originalFetch = window.fetch; window.fetch = function(input, init) { var originalFetchRun = __originalFetch(input, init); originalFetchRun.then(function(response) { try { // look for x-mini-profile-ids var entries = response.headers.entries(); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for ( var _iterator = entries[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()) .done); _iteratorNormalCompletion = true ) { var pair = _step.value; if ( pair[0] && pair[0].toLowerCase() == "x-miniprofiler-ids" ) { var ids = pair[1].split(","); fetchResults(ids); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } catch (e) { console.error(e); } }); return originalFetchRun; }; } window.MiniProfiler.patchesApplied = true; } bindDocumentEvents(); }; return { fetchedIds: fetchedIds, fetchingIds: fetchingIds, init: function init() { var script = document.getElementById("mini-profiler"); if (!script || !script.getAttribute) return; this.options = options = (function() { var version = script.getAttribute("data-version"); var path = script.getAttribute("data-path"); var currentId = script.getAttribute("data-current-id"); var ids = script.getAttribute("data-ids"); if (ids) ids = ids.split(","); var horizontal_position = script.getAttribute( "data-horizontal-position" ); var vertical_position = script.getAttribute("data-vertical-position"); var toggleShortcut = script.getAttribute("data-toggle-shortcut"); if (script.getAttribute("data-max-traces")) { var maxTraces = parseInt(script.getAttribute("data-max-traces"), 10); } var collapseResults = script.getAttribute("data-collapse-results") === "true"; var trivial = script.getAttribute("data-trivial") === "true"; var children = script.getAttribute("data-children") === "true"; var controls = script.getAttribute("data-controls") === "true"; var totalSqlCount = script.getAttribute("data-total-sql-count") === "true"; var authorized = script.getAttribute("data-authorized") === "true"; var startHidden = script.getAttribute("data-start-hidden") === "true" || sessionStorage["rack-mini-profiler-start-hidden"] === "true"; var htmlContainer = script.getAttribute("data-html-container"); var cssUrl = script.getAttribute("data-css-url"); var hiddenCustomFields = script .getAttribute("data-hidden-custom-fields") .toLowerCase() .split(","); var hotwireTurboDriveSupport = script .getAttribute('data-turbo-permanent') === "true"; return { ids: ids, path: path, version: version, renderHorizontalPosition: horizontal_position, renderVerticalPosition: vertical_position, showTrivial: trivial, showChildrenTime: children, maxTracesToShow: maxTraces, showControls: controls, showTotalSqlCount: totalSqlCount, currentId: currentId, authorized: authorized, toggleShortcut: toggleShortcut, startHidden: startHidden, collapseResults: collapseResults, htmlContainer: htmlContainer, cssUrl: cssUrl, hiddenCustomFields: hiddenCustomFields, hotwireTurboDriveSupport: hotwireTurboDriveSupport }; })(); var doInit = function doInit() { var snapshotsElement = document.getElementById("snapshots-data"); if (snapshotsElement != null) { initSnapshots(snapshotsElement); return; } // when rendering a shared, full page, this div will exist container = document.querySelectorAll(".profiler-result-full"); if (container.length) { if (window.location.href.indexOf("&trivial=1") > 0) { options.showTrivial = true; } initFullView(); } else { initPopupView(); } }; // this preserves debugging var load = function load(s, f) { var sc = document.createElement("script"); sc.async = "async"; sc.type = "text/javascript"; sc.src = s; var done = false; sc.onload = sc.onreadystatechange = function(_, abort) { if (!sc.readyState || /loaded|complete/.test(sc.readyState)) { if (!abort && !done) { done = true; f(); } } }; document.getElementsByTagName("head")[0].appendChild(sc); }; var wait = 0; var finish = false; var deferInit = function deferInit() { if (finish) return; if ( window.performance && window.performance.timing && window.performance.timing.loadEventEnd === 0 && wait < 10000 ) { setTimeout(deferInit, 100); wait += 100; } else { finish = true; init(); } }; var init = function init() { if (options.authorized) { var url = options.cssUrl; if (document.createStyleSheet) { document.createStyleSheet(url); } else { var head = document.querySelector("head"); var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = url; head.appendChild(link); } if (!MiniProfiler.loadedVendor) { load(options.path + "vendor.js?v=" + options.version, doInit); } else { doInit(); } } else { doInit(); } }; deferInit(); }, cleanUp: function cleanUp() { unbindDocumentEvents(); }, pageTransition: function pageTransition() { if (totalsControl) { if (totalsControl.parentElement) { totalsControl.parentElement.removeChild(totalsControl); } totalsControl = null; } reqs = 0; totalTime = 0; expandedResults = false; toArray( document.querySelectorAll(".profiler-results .profiler-result") ).forEach(function(el) { return el.parentElement.removeChild(el); }); }, getClientTimingByName: function getClientTimingByName(clientTiming, name) { for (var i = 0; i < clientTiming.timings.length; i++) { if (clientTiming.timings[i].name == name) { return clientTiming.timings[i]; } } return { Name: name, Duration: "", Start: "" }; }, renderDate: function renderDate(jsonDate) { // JavaScriptSerializer sends dates as /Date(1308024322065)/ if (jsonDate) { return typeof jsonDate === "string" ? new Date( parseInt(jsonDate.replace("/Date(", "").replace(")/", ""), 10) ).toUTCString() : jsonDate; } }, renderIndent: function renderIndent(depth) { var result = ""; for (var i = 0; i < depth; i++) { result += " "; } return result; }, renderExecuteType: function renderExecuteType(typeId) { // see StackExchange.Profiling.ExecuteType enum switch (typeId) { case 0: return "None"; case 1: return "NonQuery"; case 2: return "Scalar"; case 3: return "Reader"; } }, shareUrl: function shareUrl(id) { return options.path + "results?id=" + id; }, flamegraphUrl: function flamegrapgUrl(id) { return options.path + "flamegraph?id=" + id; }, moreUrl: function moreUrl(requestName) { var requestParts = requestName.split(" "); var linkSrc = requestParts[0] == "GET" ? requestParts[1] : window.location.href; var linkSuffix = linkSrc.indexOf("?") > 0 ? "&pp=help" : "?pp=help"; return linkSrc + linkSuffix; }, getClientTimings: function getClientTimings(clientTimings) { var list = []; var t; if (!clientTimings.timings) return []; for (var i = 0; i < clientTimings.timings.length; i++) { t = clientTimings.timings[i]; var trivial = t.Name != "Dom Complete" && t.Name != "Response"; trivial = t.Duration < 2 ? trivial : false; list.push({ isTrivial: trivial, name: t.Name, duration: t.Duration, start: t.Start }); } // Use the Paint Timing API for render performance. if (window.performance && window.performance.getEntriesByName) { var firstPaint = window.performance.getEntriesByName("first-paint"); if (firstPaint !== undefined && firstPaint.length >= 1) { list.push({ isTrivial: false, name: "First Paint Time", duration: firstPaint[0].duration, start: firstPaint[0].startTime }); } } list.sort(function(a, b) { return a.start - b.start; }); return list; }, getSqlTimings: function getSqlTimings(root) { var result = [], addToResults = function addToResults(timing) { if (timing.sql_timings) { for (var i = 0, sqlTiming; i < timing.sql_timings.length; i++) { sqlTiming = timing.sql_timings[i]; // HACK: add info about the parent Timing to each SqlTiming so UI can render sqlTiming.parent_timing_name = timing.name; if (sqlTiming.duration_milliseconds > 50) { sqlTiming.row_class = "slow"; } if (sqlTiming.duration_milliseconds > 200) { sqlTiming.row_class = "very-slow"; } if (sqlTiming.duration_milliseconds > 400) { sqlTiming.row_class = "very-very-slow"; } result.push(sqlTiming); } } if (timing.children) { for (var i = 0; i < timing.children.length; i++) { addToResults(timing.children[i]); } } }; // start adding at the root and recurse down addToResults(root); var removeDuration = function removeDuration(list, duration) { var newList = []; for (var i = 0; i < list.length; i++) { var item = list[i]; if (duration.start > item.start) { if (duration.start > item.finish) { newList.push(item); continue; } newList.push({ start: item.start, finish: duration.start }); } if (duration.finish < item.finish) { if (duration.finish < item.start) { newList.push(item); continue; } newList.push({ start: duration.finish, finish: item.finish }); } } return newList; }; var processTimes = function processTimes(elem, parent) { var duration = { start: elem.start_milliseconds, finish: elem.start_milliseconds + elem.duration_milliseconds }; elem.richTiming = [duration]; if (parent !== null) { elem.parent = parent; elem.parent.richTiming = removeDuration( elem.parent.richTiming, duration ); } if (elem.children) { for (var i = 0; i < elem.children.length; i++) { processTimes(elem.children[i], elem); } } }; processTimes(root, null); // sort results by time result.sort(function(a, b) { return a.start_milliseconds - b.start_milliseconds; }); var determineOverlap = function determineOverlap(gap, node) { var overlap = 0; for (var i = 0; i < node.richTiming.length; i++) { var current = node.richTiming[i]; if (current.start > gap.finish) { break; } if (current.finish < gap.start) { continue; } overlap += Math.min(gap.finish, current.finish) - Math.max(gap.start, current.start); } return overlap; }; var determineGap = function determineGap(gap, node, match) { var overlap = determineOverlap(gap, node); if (match === null || overlap > match.duration) { match = { name: node.name, duration: overlap }; } else if (match.name == node.name) { match.duration += overlap; } if (node.children) { for (var i = 0; i < node.children.length; i++) { match = determineGap(gap, node.children[i], match); } } return match; }; var time = 0; var prev = null; result.forEach(function(r) { r.prevGap = { duration: (r.start_milliseconds - time).toFixed(2), start: time, finish: r.start_milliseconds }; r.prevGap.topReason = determineGap(r.prevGap, root, null); time = r.start_milliseconds + r.duration_milliseconds; prev = r; }); if (result.length > 0) { var me = result[result.length - 1]; me.nextGap = { duration: (root.duration_milliseconds - time).toFixed(2), start: time, finish: root.duration_milliseconds }; me.nextGap.topReason = determineGap(me.nextGap, root, null); } return result; }, getSqlTimingsCount: function getSqlTimingsCount(root) { var result = 0, countSql = function countSql(timing) { if (timing.sql_timings) { result += timing.sql_timings.length; } if (timing.children) { for (var i = 0; i < timing.children.length; i++) { countSql(timing.children[i]); } } }; countSql(root); return result; }, fetchResultsExposed: function fetchResultsExposed(ids) { return fetchResults(ids); }, formatParameters: function formatParameters(parameters) { if (parameters != null) { return parameters .map(function(item, index) { return "[" + item[0] + ", " + item[1] + "]"; }) .join(", "); } else { return ""; } }, formatDuration: function formatDuration(duration) { return (duration || 0).toFixed(1); }, showTotalSqlCount: function showTotalSqlCount() { return options.showTotalSqlCount; }, timestampToRelative: function timestampToRelative(timestamp) { var now = Math.round(new Date().getTime() / 1000); timestamp = Math.round(timestamp / 1000); var diff = now - timestamp; if (diff < 60) { return "< 1 minute"; } var buildDisplayTime = function buildDisplayTime(num, unit) { var res = num + " " + unit; if (num !== 1) { res += "s"; } return res; }; diff = Math.round(diff / 60); if (diff <= 60) { return buildDisplayTime(diff, "minute"); } diff = Math.round(diff / 60); if (diff <= 24) { return buildDisplayTime(diff, "hour"); } diff = Math.round(diff / 24); return buildDisplayTime(diff, "day"); } }; })(); if (window.MiniProfiler) { _MiniProfiler.patchesApplied = window.MiniProfiler.patchesApplied; } window.MiniProfiler = _MiniProfiler; _MiniProfiler.init();