Simple example to inject console panel to any website. It injects CSS and JavaScript to the website.
{
"name": "Athena",
"description": "Athena, a tool for improving communication between developers and testers.",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["https://*/*", "http://*/*"],
"js": ["inject.js", "console-panel.js"]
}
],
"permissions": [
"tabs"
],
"web_accessible_resources": [
{
"resources": [ "panel.js" ],
"matches": [ "https://*/*", "http://*/*" ]
}
],
"host_permissions": ["<all_urls>"],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "/images/get_started16.png",
"32": "/images/get_started32.png",
"48": "/images/get_started48.png",
"128": "/images/get_started128.png"
}
},
"icons": {
"16": "/images/get_started16.png",
"32": "/images/get_started32.png",
"48": "/images/get_started48.png",
"128": "/images/get_started128.png"
}
}
function loadScript() {
console.log("loading");
const _script = document.createElement('script');
_script.setAttribute('src', chrome.runtime.getURL('panel.js'));
(document.head||document.documentElement).appendChild( _script );
_script.parentNode.removeChild( _script);
}
(function() {
loadScript();
})();
// this is the code which will be injected into a given page...
const css = `
/*!
console-panel
A console panel within webpage to help in the following use-cases:
* Get notification on console messages
* Console logging on mobile and tablet devices
* Console logging on Microsoft Edge / Internet Explorer (without opening native Developer Tools)
https://github.com/webextensions/console-panel
by Priyank Parashar (https://webextensions.org/)
MIT License
*/
#console-panel .dev-tools-icon-container,
#console-panel .dev-tools-icon-container *,
#console-panel .dev-tools-console,
#console-panel .dev-tools-console * {
font-family: monospace;
}
#console-panel .dev-tools-header,
#console-panel .dev-tools-header * {
font-family: sans-serif;
}
#console-panel .dev-tools-icon-container {
position: fixed;
z-index: 2000000001;
}
#console-panel .dev-tools-icon-container-left-top,
#console-panel .dev-tools-icon-container-top-left {
top: 20px;
left: 20px;
}
#console-panel .dev-tools-icon-container-top-right,
#console-panel .dev-tools-icon-container-right-top {
top: 20px;
right: 20px;
}
#console-panel .dev-tools-icon-container-bottom-left,
#console-panel .dev-tools-icon-container-left-bottom {
bottom: 20px;
left: 20px;
}
#console-panel .dev-tools-icon-container-bottom-right,
#console-panel .dev-tools-icon-container-right-bottom {
right: 20px;
bottom: 20px;
}
#console-panel .dev-tools-icon {
width: 32px;
height: 32px;
line-height: 35px; /* Keeping height a little more than height, so that it looks better middle-aligned (since we are going to render numbers inside it) */
border-radius: 999px;
cursor: pointer;
text-align: center;
font-size: 14px;
/* This may help in improving CPU usage for some of the animations */
transform: translateZ(0);
}
#console-panel .dev-tools-icon.no-unread-messages {
/* https://github.com/mozilla/gecko-dev/blob/7aef56cc4e682e5c99fcc282f30abbf8212efd50/devtools/client/definitions.js */
/* chrome://devtools/skin/images/tool-webconsole.svg */
background-image: url("data:image/svg+xml;base64,PCEtLSBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljCiAgIC0gTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpcwogICAtIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uIC0tPgo8c3ZnIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9ImNvbnRleHQtZmlsbCAjMGIwYjBiIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogIDxwYXRoIGQ9Ik0xNCA0VjNIMnYxaDEyem0wIDF2OEgyVjVoMTJ6TTEgMy4wMDJDMSAyLjQ1IDEuNDUgMiAyLjAwNyAyaDExLjk4NkExLjAxIDEuMDEgMCAwIDEgMTUgMy4wMDJ2OS45OTZDMTUgMTMuNTUgMTQuNTUgMTQgMTMuOTkzIDE0SDIuMDA3QTEuMDEgMS4wMSAwIDAgMSAxIDEyLjk5OFYzLjAwMnoiLz4KICA8cGF0aCBkPSJNNC4wOSA3Ljg1OWwyLjA2MiAyLS4wMDYtLjcxMy0yLjA2MSAyLjA2MmEuNS41IDAgMCAwIC43MDcuNzA3bDIuMDYyLTIuMDYxYS41LjUgMCAwIDAtLjAwNi0uNzEzbC0yLjA2MS0yYS41LjUgMCAxIDAtLjY5Ny43MTh6Ii8+Cjwvc3ZnPg==");
background-repeat: no-repeat;
background-position: center center;
opacity: 0.5;
}
#console-panel .dev-tools-icon.no-unread-messages:hover {
opacity: 1;
}
#console-panel .dev-tools-icon { background-color: #e7e7e7; box-shadow: inset 0 0 15px 1px #979797; }
#console-panel .dev-tools-icon:hover { background-color: #d0d0d0; }
#console-panel .dev-tools-icon.found-something,
#console-panel .dev-tools-icon.found-log { background-color: #d3d3d3; box-shadow: inset 0 0 15px 1px #777; }
#console-panel .dev-tools-icon.found-something:hover,
#console-panel .dev-tools-icon.found-log:hover { background-color: #b9b9b9; }
#console-panel .dev-tools-icon.found-info { background-color: #dad4dd; box-shadow: inset 0 0 15px 1px #6e61bf; }
#console-panel .dev-tools-icon.found-info:hover { background-color: #cbb6d6; }
#console-panel .dev-tools-icon.found-warn { background-color: #ffea83; box-shadow: inset 0 0 15px 1px #f8981b; }
#console-panel .dev-tools-icon.found-warn:hover { background-color: #f9d626; }
#console-panel .dev-tools-icon.found-error { background-color: #ffc5c5; box-shadow: inset 0 0 15px 1px #ff5858; }
#console-panel .dev-tools-icon.found-error:hover { background-color: #fc9292; box-shadow: inset 0 0 15px 1px #f00; }
#console-panel .dev-tools-icon.found-error {
/* Limiting the animation to 5 times. Otherwise, the CSS animation may cause high CPU usage. */
animation: console-panel-animation-notify-error 3s 5;
}
@keyframes console-panel-animation-notify-error {
50% {
background-color: #ffa500;
box-shadow: inset 0 0 15px 1px #f00;
}
}
#console-panel .dev-tools-icon-container .strong-notification:before,
#console-panel .dev-tools-icon-container .strong-notification:after {
display: block;
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
border-radius: 50%;
z-index: -1;
}
#console-panel .dev-tools-icon-container .strong-notification:before { background-color: rgba(255, 0, 0, 0.5); }
#console-panel .dev-tools-icon-container .strong-notification:after { background-color: rgba(255,177,0, 0.5); }
/* To ensure that the CSS animation does not cause high CPU usage, we remove
the "strong-notification" class via JavaScript, once it is not required
anymore. */
#console-panel .dev-tools-icon-container .strong-notification:before { animation: console-panel-animation-ripple 0.75s ease-in infinite; }
#console-panel .dev-tools-icon-container .strong-notification:after { animation: console-panel-animation-ripple 0.75s ease-out infinite; }
/* https://stackoverflow.com/questions/32955459/rings-with-ripple-animation-css-only/32955876#32955876 */
@keyframes console-panel-animation-ripple {
0% {
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
}
25% {
top: -10vh;
top: -10vmin;
right: -10vh;
right: -10vmin;
bottom: -10vh;
bottom: -10vmin;
left: -10vh;
left: -10vmin;
opacity: 0.5;
}
90% {
opacity: 0.2;
}
100% {
top: -20vh;
top: -20vmin;
right: -20vh;
right: -20vmin;
bottom: -20vh;
bottom: -20vmin;
left: -20vh;
left: -20vmin;
opacity: 0;
}
}
#console-panel .dev-tools {
position: fixed;
z-index: 2000000000;
display: block;
bottom: 0;
right: 0;
box-sizing: border-box;
background-color: #fff;
border-top: 1px solid #d0d0d0;
width: 100%;
/* Values much higher than this may not work so well in different mobile device orientation since the "vh"/"vmin" might be calculated
w.r.t. full-screen size, while the toolbar is also visible (which eats some of that height) */
max-height: 80vh;
max-height: 80vmin;
height: 250px;
min-height: 90px;
padding: 0;
color: #303942;
/* A mix and match of font-family names from Chrome DevTools (ui/inspectorStyle.css) */
font-family: 'Segoe UI', '.SFNSDisplay-Regular', 'Helvetica Neue', 'Lucida Grande', Roboto, Ubuntu, Tahoma, Arial, sans-serif;
font-size: 11px;
}
#console-panel .dev-tools-header {
height: 27px;
line-height: 27px;
background-color: #f3f3f3;
padding: 2px 0px 2px 6px;
border-bottom: 1px solid #d0d0d0;
font-size: 12px;
}
#console-panel .dev-tools-clear-console-icon {
width: 13px;
height: 13px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gsJFi8Kd+JgAwAAAblJREFUSMellj1PQjEUhh8SGU3EuCkjfoVJ/BgciYNxwMmBxN/A4sfgDzAOOgjhRxBNSJwUwYG4sqJxIJEYVsHFqIBLb3Jz0vb2Xpp0OT1vn7anPacxgtsSsA/sAPNAUtm7wAdQA6rAKxHbOlAHxo79GdgOA4gDZWAUAuL1EVBSc1jbLPBkmegb6AH9AGADSNh2ooN8AZfApljptIpdxbD7hmlnZcPqCg7HnQHaGm1ROm5YYvIH5B1gM0BTE7OM36kRcOausDmgI7Q1b3BZE5OCmjwKLKdZ6CLAqTBeKUF+AlhL6I4BHoVxyyeICjsTmgdU6vAMP5orGQWWFf4vAAOfoWcQhoWtCN+BBH1aVnkA/GpghxrftPDry6MbqxfPhDvb0x1dTRhzAYF2gV2I8XuAE2GsOFxfGyyuapV/7MgrbNa0ERJ2q3mwKU8kC1xb5a4osLEpBXnVVCbVpspdk8CGwJoUlDSOHYfLEQduDKBrk8CUxVsqrWTVY1wFdoFz4N2gqdtKesKhZLj0uq2U+3dWjPg5GarjmgrzG0oDdyEgNV3gvRZzAKZ8H8gksOD7QHZVmakCb7ZJ/gEMg1YzMOnqgwAAAABJRU5ErkJggg==");
float: left;
background-size: contain;
opacity: 0.5;
margin-right: 5px;
margin-top: 7px;
cursor: pointer;
}
#console-panel .dev-tools-clear-console-icon:hover {
opacity: 0.85;
}
#console-panel .dev-tools-header-cross-icon,
#console-panel .dev-tools-header-disable-icon {
float: right;
cursor: pointer;
width: 13px;
height: 13px;
opacity: 0.5;
background-repeat: no-repeat;
height: 24px;
}
#console-panel .dev-tools-header-cross-icon {
width: 30px;
/* Source: chrome-devtools://devtools/bundled/Images/largeIcons_2x.png (in Google Chrome browser) */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gsGFTI1ZM2PLQAAAKdJREFUOMutlUsOwjAMRB8cgIqzdGGuHnEANpwFuScoC1LJQq1rW4mUTZR5+c048GsT0IAH+TYDT+COgb2AFViS0Bn4dO17g7Y+sHUFJACTPtdq2/8qa3Cnexq1mgz0FJaBhmERaBrmXbYejEnFDntdK549grqwayEZF4px8o6cStTQR/GskU5UxGdhaMa0p9BKAlzo8PJ1MwVWRxRY+wVIwbPStRPAF611ou20opYrAAAAAElFTkSuQmCC");
background-position: 9px 8px;
background-size: 10px 10px;
}
#console-panel .dev-tools-header-disable-icon {
width: 20px;
/* Source: https://www.iconfinder.com/icons/1608429/off_power_icon */
background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB3aWR0aD0iMTc5MiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTY2NCA4OTZxMCAxNTYtNjEgMjk4dC0xNjQgMjQ1LTI0NSAxNjQtMjk4IDYxLTI5OC02MS0yNDUtMTY0LTE2NC0yNDUtNjEtMjk4cTAtMTgyIDgwLjUtMzQzdDIyNi41LTI3MHE0My0zMiA5NS41LTI1dDgzLjUgNTBxMzIgNDIgMjQuNSA5NC41dC00OS41IDg0LjVxLTk4IDc0LTE1MS41IDE4MXQtNTMuNSAyMjhxMCAxMDQgNDAuNSAxOTguNXQxMDkuNSAxNjMuNSAxNjMuNSAxMDkuNSAxOTguNSA0MC41IDE5OC41LTQwLjUgMTYzLjUtMTA5LjUgMTA5LjUtMTYzLjUgNDAuNS0xOTguNXEwLTEyMS01My41LTIyOHQtMTUxLjUtMTgxcS00Mi0zMi00OS41LTg0LjV0MjQuNS05NC41cTMxLTQzIDg0LTUwdDk1IDI1cTE0NiAxMDkgMjI2LjUgMjcwdDgwLjUgMzQzem0tNjQwLTc2OHY2NDBxMCA1Mi0zOCA5MHQtOTAgMzgtOTAtMzgtMzgtOTB2LTY0MHEwLTUyIDM4LTkwdDkwLTM4IDkwIDM4IDM4IDkweiIvPjwvc3ZnPg==");
background-size: 14px 14px;
background-position: 3px 6px;
}
#console-panel .dev-tools-header-cross-icon:hover,
#console-panel .dev-tools-header-disable-icon:hover {
opacity: 0.75;
}
#console-panel .dev-tools-console {
clear: both;
overflow: auto;
height: calc(100% - 31px);
}
#console-panel .dev-tools-console-body {
overflow: auto;
}
#console-panel .dev-tools-console-message-wrapper {
line-height: 13px;
border-top: 1px solid transparent;
border-bottom: 1px solid #f0f0f0;
line-height: 17px;
padding: 3px 22px 1px 0;
}
/* This helps in ensuring that the texts show proper whitespace (also useful in showing function definitions) */
#console-panel .dev-tools-console-message > span {
white-space: pre-wrap;
}
#console-panel .log-mode-info,
#console-panel .log-mode-warn,
#console-panel .log-mode-error,
#console-panel .log-mode-window-onerror {
background-repeat: no-repeat;
}
#console-panel .log-mode-info {
/* chrome-devtools://devtools/bundled/Images/smallIcons_2x.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gsKDw81VUJejQAAAsxJREFUOMutVFtLVFEU/vbex5lxjjIqlWVXsKQL4UORRb0WJZSKmQVRD0UE9hLRjSDqqRtdXnqpH1BvXpDeoiBIgyYyGcuCUuli6ejczzmzL6uHypzRJNDvae+1v/Wtb7FZi2EaNDVFfCNwG7Q0dZ4yNcqYCqUIWpmvWZdeKCPbgsnRtkhkXzY/l+UHag/2NHpS34prWuYSYPIJ2kA5CpmYO5h15ckfPTtaJz+Lv65IVNXsvTGe1XdGFYUkAAJQFrJQu60UyYxBIq0BzsB9Ar5iXwmImkXRftsZWfkYeEoAYP0RpFDftR9pdSom/3oSguHBldVYssCHeEphZ0vklygAxhjseTaY4Ke12kTxfpwFAA4AB46/bYy66lQsm9ugHeBYPN8HAAgVWaj4fZ6MYGkh7DL7TLDyUT0AiKZLEZ+KU+dgKluST/YkQWrCsoV+dD4bQ+uT6HR/CL9dgPS4s9Ur33OXHTrR3zwQcx+OOiqHJDiDXchzYmnXQGuaVjQxksG3gfg+iwH1cU9PIVQuDaD15pqc2JHLH9Ddm5xW0C7xgzPUcUeZzdJMrfp+0MG6va8wNOzhfyAKBCy/2MIVUTnmCJaPL+IgzBkYMcMtwYbnSlBKPcwDgnUVcDZrMS01lGeec0ZoD/mtWQumYy4MmQ7ul6nWeYXWAJuFSTKExIgzpIudDhEO3zPVm1qGGKE5NWmOly/y4/7FVVi+MAAhflWrripCcVAg/DaVIxgfTlMm6RyW/Q19E74aj0auf0tnT/+Z52CAo7rKnuLm+5jEx8/uxD0z7iL6NXE1+W7X+Zxts37J2nN6sJcEsTNjUiPjGnS9Sc7YZibqUCrqXE++e3Hhnwt298E39Wmpbye0WeESweRRyBB0RsKNuZ88T538/np7+4wbGwA2HHtZEBpDvZSoc43ZrI2p0NJASvqisrpbKt1eporbw+GNMj/3Jz2CZsrrkClGAAAAAElFTkSuQmCC");
background-size: 11px 11px;
background-position: 7px 4px;
background-color: #edebfb;
border-bottom-color: #e5e1ff;
}
#console-panel .log-mode-warn {
/* chrome-devtools://devtools/bundled/Images/smallIcons_2x.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gsKDwIt84C4lgAAAbFJREFUOMudlLtOG0EUhr9dr9esL7uz4MvKSkIRiZKaDvECeYRADSjCBmoeIZKL1IZHSIMrEzp3kVKCEJcgCHgxdizLyNg4RbhkYXfWZLqZc+afb/45ZxRCxu4XPqQtygBui4XZRb7K8hVZsFbCFBnO3mZJAPy8pNOsk5/5xO+gPapMcBCjmBv/KwaQGycxiFH8L8JaCdPKcv4uQ/zf9dM63bNz8nMFmq8i7OusOsIrBpCzMWIGhVcRPtClDOLNtjcmUtDpBlNqfoK3UYqOIH5yAdMfh57Yjy2FSQfjqkkB2Ai9cq2EmbZZ16PBxkc1mBCs73xGhAoGeTeql+pzugnBmowujFKV0UUiMOh3n+qy3yUSkVOqMjqRAvdk93HuHn/DTskpVZl3VhLcw8rjvH5UwUzKvVSD6Ib3Rdpzt5/Kqb6Nch8LolTDXnYqs0enqtCpKkxl90NfXPHr2SGjDcWnx7W7MVYd21/s4NS/U96/8doCkLUx3BYrmpVgSddG/CAlOboGIsmyJvtxbBO+l73htJCepWqNNvPA5piO7ZdhJb1Xvu3Dr8bLvJse1402838AjWeMl7yfz78AAAAASUVORK5CYII=");
background-size: 10px 10px;
background-position: 7px 5px;
background-color: #fffbe5;
border-bottom-color: #fff5c2;
}
#console-panel .log-mode-error,
#console-panel .log-mode-window-onerror {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gsKDxgoM8e2wgAAAxlJREFUOMudlMtPE1EUxr97Z+ZOh0I7pTxKwbZAjCaNxhijkBiNbowx8hD1D1DDgvhCMJq4cYuCGjUuXOAejAKJKxMJmCgmLkyMGjGA5WGRVx9IKcPMHRdg40B56Fmec88v557vu5cgTbQFwZQpVk1Ns1Jmyj6BMi8AGFz7saDNvzNM0pHM0zpOf4K2spesTLwoyKyRRXbH4/T4XBkuMIFZ6pqhITIXQXg2HEouJuuPh+efpwW2AUKmX23y2PMbCh0FoIRgveAmx+hsGBO/Jm+/C0Wu3wQ4AIh/DjhKPU1bsvIb8pRsbCYoofA5CqFIylUiyiYGxq+lJny53V+TIzufFmW48T8xMjeFyWS0+kj/SAdtCwaZLCot3gyX5ZDtRAWoS109mUuF7USFJVdkd0Nhyv22YJAJdd6ckx6b85ydsr9glchsvAxWvhdady/MZDIFcz5oge3YUfBIFPqXr8tCEFAqOnWy8JEKEqtyiDarkq96oA8MQigphvPRPVC3G1RV4bjfDKGkGEZoGFrPa0uPQ7JBFFgl6SsvGyqFElh1NVWF40EzxNISGKOjgM4hBHwwQsOInb8CPj2zah3feGKQSlTKT2uLaBTxC43Qv4cgFBUtwUbH1oQBgCyyAgpBWldBsolMqiIwTrnExtP6bPnKQsAPY2RsedJCOB+2gLrT24uL0jjlTH6rU7r2/kLDiNVdQryufkkovy8tVKcEBpPfUCLKnfNMthTZ4YNLsMEhxOoug0/PLO30YiOMwSEIfh/Ywf2WnoQsg4qsi7yvrZXEn/F+7+REgJimxdhady94JLrK2OzQASSfdaVyJiEIu3OHRRvfSgDgw6kz1fZfsWdqdOa/nl5MzTbjmc6q3e2tXRQAdrW3Pk+oObfnHOo/w+ayVCTU3Kbd7a1dAJBSY+e2guvxvC23YjlemJRsCDIpRTzXa8Y9vqYd2zw31jTV57MNVdD0u/aZ8YA8G4WwaP2UDYlhIUtFIjt/iIusPvikuXNDl76vrZUU5FZRwitJcqFM0DXvss/GDFnpM4nZOW9Od+55/HhxZe9vO6ohHl8Nk3cAAAAASUVORK5CYII=");
background-size: 11px 11px;
background-position: 7px 5px;
background-color: #fff0f0;
border-bottom-color: #ffd6d6;
}
#console-panel .log-mode-unhandled {
background-color: #eff;
}
#console-panel .dev-tools-console-message {
margin-left: 24px;
word-wrap: break-word;
font-family: monospace;
}
#console-panel .dev-tools-console-message-code-line {
float: right;
}
#console-panel .log-call-stack {
white-space: pre-wrap;
}
#console-panel .log-value-window-onerror {
color: #f00;
}
#console-panel .log-value-unknown {
color: #000;
}
#console-panel .log-value-boolean,
#console-panel .log-value-number {
color: #1c00cf;
}
#console-panel .log-value-null,
#console-panel .log-value-undefined,
#console-panel .log-value-console-clear {
color: #808080;
}
#console-panel .log-value-console-clear {
font-style: italic;
}
#console-panel .log-value-string:before,
#console-panel .log-value-string:after {
content: '"';
color: #222;
}
#console-panel .log-value-string {
color: #c41a16;
}
#console-panel .log-value-dom-text:before,
#console-panel .log-value-dom-text:after {
color: #888;
font-style: italic;
}
#console-panel .log-value-dom-text:before {
content: '#text "';
}
#console-panel .log-value-dom-text:after {
content: '"';
}
#console-panel .log-value-dom {
color: #881280;
}
/* This helps in keeping the console-panel-expand-collapse icon together with the highlighted
code (for example when multiple items are logged via single console.log()) */
#console-panel .log-value-dom {
display: inline-block;
}
/* But, the above rule may cause the console-panel-expand-collapse icon to move to the next
line even when it is the first child, but that case may be better to ignore (to avoid
that extra line). For example: when we log an element which contains huge HTML code, which
would need to get wrapped */
#console-panel .dev-tools-console-message .log-value-dom:first-child {
display: inline;
}
#console-panel .jsoneditor-not-available.log-value-array,
#console-panel .jsoneditor-not-available.log-value-object {
color: #808080;
}
#console-panel .jsoneditor-not-available.log-value-array:before,
#console-panel .jsoneditor-not-available.log-value-object:before {
color: rgb(33, 33, 33);
}
#console-panel .jsoneditor-not-available.log-value-array:before {
content: 'Array ';
}
#console-panel .jsoneditor-not-available.log-value-object:before {
content: 'Object ';
}
/* CSS fixes for JSON Editor */
#console-panel div.jsoneditor-menu {
display: none;
}
#console-panel div.jsoneditor-outer {
margin-top: 0;
padding-top: 0;
}
#console-panel div.jsoneditor {
border-width: 0;
}
#console-panel div.jsoneditor-tree div.jsoneditor-tree-inner {
padding-bottom: 0;
}
/* Without this, a scroll seems to come up */
#console-panel div.jsoneditor-tree {
display: inline;
}
#console-panel .jsoneditor,
#console-panel .jsoneditor-outer,
#console-panel .jsoneditor-tree-inner,
#console-panel .jsoneditor-outer > .jsoneditor-tree,
#console-panel .jsoneditor-outer > .jsoneditor-tree > .jsoneditor-tree-inner > .jsoneditor-tree {
display: inline;
}
/* This style may be useful in older browsers */
#console-panel div.jsoneditor-value.jsoneditor-array,
#console-panel div.jsoneditor-value.jsoneditor-object {
min-width: unset;
}
#console-panel div.jsoneditor-value {
width: max-content;
}
#console-panel div.jsoneditor-tree button.jsoneditor-button,
#console-panel div.jsoneditor-tree button.jsoneditor-button.jsoneditor-expanded {
background-position: 0px 2px;
}
#console-panel div.jsoneditor-tree button.jsoneditor-button {
width: 10px;
height: 10px;
background-repeat: no-repeat;
/* chrome-devtools://devtools/bundled/Images/treeoutlineTriangles.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAICAQAAABwz0azAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfiCxITJRj/Z+u0AAAAL0lEQVQI12NgyGBgZICD/wy7GGQRnP8MHxjSEJz/DP8ZtjJIMTEga0BXhmQAktEApfUQ8zThbVYAAAAASUVORK5CYII=");
}
#console-panel div.jsoneditor-tree button.jsoneditor-button.jsoneditor-expanded {
/* chrome-devtools://devtools/bundled/Images/treeoutlineTriangles.png */
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAGCAQAAAC7znxOAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfiCxITJDGkzkKZAAAAP0lEQVQI12Ng4Ga4xfAfDu8x8DIwMDBYMfyBCvxlsGOAgm6oUBcDHLAzXGL4z3CNgYMBCRgyfGUwZUAD2jAGAEP7FIhkSA3EAAAAAElFTkSuQmCC");
}
#console-panel div.jsoneditor-readonly,
#console-panel div.jsoneditor-value {
padding: 0;
margin: 0;
}
#console-panel div.jsoneditor-field,
#console-panel div.jsoneditor-readonly,
#console-panel div.jsoneditor-value {
min-height: 0px;
min-width: 0px; /* Useful for keeping widths for property names as small as possible */
}
#console-panel .jsoneditor-schema-error,
#console-panel div.jsoneditor td,
#console-panel div.jsoneditor textarea,
#console-panel div.jsoneditor th,
#console-panel div.jsoneditor-field,
#console-panel div.jsoneditor-value {
font-size: 11px;
font-family: monospace;
}
#console-panel div.jsoneditor td.jsoneditor-tree {
vertical-align: middle;
}
/* Begin: Styles to make JSON Editor match Chrome DevTools UI */
#console-panel div.jsoneditor-field {
color: #881391;
}
#console-panel div.jsoneditor-value.jsoneditor-string {
color: #c41a16;
}
#console-panel div.jsoneditor-value.jsoneditor-string:before,
#console-panel div.jsoneditor-value.jsoneditor-string:after {
content: '"';
color: #222;
}
#console-panel div.jsoneditor-empty {
border-width: 0;
}
#console-panel .jsoneditor-expandable .jsoneditor-readonly {
text-transform: capitalize;
color: rgb(33, 33, 33);
}
#console-panel div.jsoneditor-tree button.jsoneditor-button:focus {
background-color: transparent;
outline: none;
}
/* End */
/* Begin: Styles to make Prism JS match Chrome DevTools */
#console-panel .only-first-line-of-code code.language-markup:after {
content: '…';
}
#console-panel pre.language-markup {
background-color: transparent;
padding: 0;
margin: 0;
display: inline-block;
}
/* End */
#console-panel .all-lines-of-code {
display: inline-block;
}
#console-panel .console-panel-expand-collapse {
display: inline-block;
width: 10px;
height: 10px;
}
#console-panel .console-panel-expand-collapse.console-panel-collapsed,
#console-panel .console-panel-expand-collapse.console-panel-expanded {
cursor: pointer;
background-repeat: no-repeat;
}
#console-panel .console-panel-expand-collapse.console-panel-expanded {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAGCAQAAAC7znxOAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfiCxITJDGkzkKZAAAAP0lEQVQI12Ng4Ga4xfAfDu8x8DIwMDBYMfyBCvxlsGOAgm6oUBcDHLAzXGL4z3CNgYMBCRgyfGUwZUAD2jAGAEP7FIhkSA3EAAAAAElFTkSuQmCC");
background-position: 0px 2px;
}
#console-panel .console-panel-expand-collapse.console-panel-collapsed {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAICAQAAABwz0azAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfiCxITJRj/Z+u0AAAAL0lEQVQI12NgyGBgZICD/wy7GGQRnP8MHxjSEJz/DP8ZtjJIMTEga0BXhmQAktEApfUQ8zThbVYAAAAASUVORK5CYII=");
}
#console-panel .only-first-line-of-code {
vertical-align: top;
vertical-align: text-top;
}
#console-panel .all-lines-of-code {
vertical-align: top;
}
#console-panel code[class*="language-"],
#console-panel pre[class*="language-"] {
white-space: pre-wrap;
word-break: break-word;
}
/* Begin: Useful styles when Prism JS is not available */
#console-panel .log-value-dom .all-lines-of-code pre,
#console-panel .log-value-dom .only-first-line-of-code pre {
display: inline;
}
/* End */
/* Begin: Match Prism JS with DevTools style */
#console-panel code[class*="language-"],
#console-panel pre[class*="language-"] {
font-family: monospace;
}
#console-panel .token.tag {
color: #881280;
}
#console-panel .token.attr-name {
color: #994500;
}
#console-panel .token.attr-value {
color: #1a1aa6;
}
#console-panel .token.comment {
color: #236e25;
}
/* */
/* Begin: Resize related CSS */
html #console-panel .dev-tools-resize-handle {
top: 0;
height: inherit;
padding-top: inherit;
padding-bottom: inherit;
position: absolute;
width: 100%;
left: 0;
display: block;
}
#console-panel .dev-tools {
top: unset !important;
bottom: 0 !important;
}
/* End */
/* Begin: jQuery UI related fix */
#console-panel .dev-tools {
position: fixed;
}
/* End */
`;
(function() {
// just place a div at top right
var div = document.createElement('div');
div.style.position = 'fixed';
div.style.top = 0;
div.style.right = 0;
div.textContent = 'Injected!';
document.body.appendChild(div);
const style = document.createElement('style');
style.textContent = css;
document.head.append(style);
})();
(function ($) {
if (window.consolePanel) {
return;
}
/* eslint-disable */
// https://raw.githubusercontent.com/WebReflection/circular-json/v0.5.9/build/circular-json.js
/*! (C) WebReflection Mit Style License */
var CircularJSON=function(JSON,RegExp){var specialChar="~",safeSpecialChar="\\x"+("0"+specialChar.charCodeAt(0).toString(16)).slice(-2),escapedSafeSpecialChar="\\"+safeSpecialChar,specialCharRG=new RegExp(safeSpecialChar,"g"),safeSpecialCharRG=new RegExp(escapedSafeSpecialChar,"g"),safeStartWithSpecialCharRG=new RegExp("(?:^|([^\\\\]))"+escapedSafeSpecialChar),indexOf=[].indexOf||function(v){for(var i=this.length;i--&&this[i]!==v;);return i},$String=String;function generateReplacer(value,replacer,resolve){var doNotIgnore=false,inspect=!!replacer,path=[],all=[value],seen=[value],mapp=[resolve?specialChar:"[Circular]"],last=value,lvl=1,i,fn;if(inspect){fn=typeof replacer==="object"?function(key,value){return key!==""&&replacer.indexOf(key)<0?void 0:value}:replacer}return function(key,value){if(inspect)value=fn.call(this,key,value);if(doNotIgnore){if(last!==this){i=lvl-indexOf.call(all,this)-1;lvl-=i;all.splice(lvl,all.length);path.splice(lvl-1,path.length);last=this}if(typeof value==="object"&&value){if(indexOf.call(all,value)<0){all.push(last=value)}lvl=all.length;i=indexOf.call(seen,value);if(i<0){i=seen.push(value)-1;if(resolve){path.push((""+key).replace(specialCharRG,safeSpecialChar));mapp[i]=specialChar+path.join(specialChar)}else{mapp[i]=mapp[0]}}else{value=mapp[i]}}else{if(typeof value==="string"&&resolve){value=value.replace(safeSpecialChar,escapedSafeSpecialChar).replace(specialChar,safeSpecialChar)}}}else{doNotIgnore=true}return value}}function retrieveFromPath(current,keys){for(var i=0,length=keys.length;i<length;current=current[keys[i++].replace(safeSpecialCharRG,specialChar)]);return current}function generateReviver(reviver){return function(key,value){var isString=typeof value==="string";if(isString&&value.charAt(0)===specialChar){return new $String(value.slice(1))}if(key==="")value=regenerate(value,value,{});if(isString)value=value.replace(safeStartWithSpecialCharRG,"$1"+specialChar).replace(escapedSafeSpecialChar,safeSpecialChar);return reviver?reviver.call(this,key,value):value}}function regenerateArray(root,current,retrieve){for(var i=0,length=current.length;i<length;i++){current[i]=regenerate(root,current[i],retrieve)}return current}function regenerateObject(root,current,retrieve){for(var key in current){if(current.hasOwnProperty(key)){current[key]=regenerate(root,current[key],retrieve)}}return current}function regenerate(root,current,retrieve){return current instanceof Array?regenerateArray(root,current,retrieve):current instanceof $String?current.length?retrieve.hasOwnProperty(current)?retrieve[current]:retrieve[current]=retrieveFromPath(root,current.split(specialChar)):root:current instanceof Object?regenerateObject(root,current,retrieve):current}var CircularJSON={stringify:function stringify(value,replacer,space,doNotResolve){return CircularJSON.parser.stringify(value,generateReplacer(value,replacer,!doNotResolve),space)},parse:function parse(text,reviver){return CircularJSON.parser.parse(text,generateReviver(reviver))},parser:JSON};return CircularJSON}(JSON,RegExp);
/* eslint-enable */
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
(function () {
if (typeof window.CustomEvent === "function") return false;
function CustomEvent ( event, params ) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent( 'CustomEvent' );
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
})();
var alertNote = (function () {
var w = window,
d = document,
dE = d.documentElement,
div = d.createElement('div'),
t;
div.id = 'topCenterAlertNote';
// Hide functionality
var h = function (div) {
div.style.display = 'none';
};
var clearTimeout = function () {
w.clearTimeout(t);
};
var alertNote = function (msg, hideDelay, options) {
options = options || {};
var verticalAlignment = options.verticalAlignment || 'top',
horizontalAlignment = options.horizontalAlignment || 'center',
textAlignment = options.textAlignment || horizontalAlignment,
backgroundColor = options.backgroundColor || '#f9edbe',
borderColor = options.borderColor || '#eb7',
opacity = options.opacity || '1',
unobtrusive = options.unobtrusive || false;
// TODO:
// - Apply !important for various inline styles (otherwise, it might get over-ridden by some previously present !important CSS styles)
// - "OK" button functionality
/* eslint-disable indent */
div.innerHTML = [
'<div ' +
'style="' +
'pointer-events:none;' + // To avoid it from stealing hover (the pointer-events will be enabled for a child element)
'position:fixed;width:100%;z-index:2147483600;' +
(verticalAlignment === 'bottom' ? 'bottom:0;' : 'top:0;') +
(function () {
if (horizontalAlignment === 'left') {
return 'left:0;';
} else if (horizontalAlignment === 'right') {
return 'right:0;';
} else {
/* Even for center aligning, we need to set left or right as 0, without that
it would try to center align whithout considering the width taken by vertical scrollbar */
return 'left:0;';
}
}()) +
'text-align:' + horizontalAlignment + ';' + // TODO: Check if we need this
'opacity:' + opacity + ';' +
'"' +
'>',
'<div ' +
'style="' +
'display:flex;width:auto;margin:0;padding:0;border:0;' +
(function () {
if (horizontalAlignment === 'left') {
return 'justify-content:flex-start;';
} else if (horizontalAlignment === 'right') {
return 'justify-content:flex-end;';
} else {
return 'justify-content:center;';
}
}()) +
// margin:0 is useful for some sites (eg: https://developer.chrome.com/home)
'"' +
'>',
'<div ' +
'style="' +
'pointer-events:initial;' + // To gain back the pointer-events which were disabled in one of the parent elements
'border:1px solid ' + borderColor + ';' +
'background-color:' + backgroundColor + ';' + // background-color:#feb;
// TODO: Check if we need "text-align: left". Maybe it helps to set the default style.
'padding:2px 10px;max-width:980px;overflow:hidden;text-align:left;font-family:Arial,sans-serif;font-weight:bold;font-size:12px' +
'"' +
'>',
'<div class="alert-note-text" style="color:#000;text-align:' + textAlignment + ';word-wrap:break-word;">',
msg,
'</div>',
'</div>',
'</div>',
'</div>'
].join('');
/* eslint-enable indent */
if (unobtrusive) {
try {
var firstChild = div.firstChild.firstChild.firstChild;
firstChild.addEventListener('mouseenter', function () {
// Note:
// If we wish to directly apply the opacity changes to the parent "div",
// which is currently a direct child of <html> tag, then, on some sites (eg:
// gmail.com) somehow, as soon as we reduce its opacity to a value less than
// 1 (eg: 0.99), it gets hidden immediately. The fact that it is appended to
// <html> tag and not to <body> is somehow causing this behavior. Since we
// are using that parent div's inner child, the opacity transition works fine.
firstChild.style.transition = 'opacity 0.3s ease-out';
firstChild.style.opacity = '0';
firstChild.style.pointerEvents = 'none';
}, false);
} catch (e) {
// do nothing
}
}
div.style.display = ''; // Required when the same div element is being reused
dE.appendChild(div);
clearTimeout();
t = w.setTimeout(function () { h(div); }, hideDelay || 5000);
};
alertNote.hide = function () {
h(div);
clearTimeout();
};
return alertNote;
}());
var constants = {
DISABLE_FOR_THIS_INSTANCE: 'Disable for this instance'
};
var moduleGlobal = {};
// Customized version of devtools-detect:
// https://github.com/sindresorhus/devtools-detect/blob/gh-pages/index.js
/*!
devtools-detect
Detect if DevTools is open
https://github.com/sindresorhus/devtools-detect
by Sindre Sorhus
MIT License
*/
(function (scope) {
'use strict';
scope = scope || {};
var devtools = {
open: false,
orientation: null
};
var threshold = 160;
var emitEvent = function (state, orientation) {
// https://github.com/sindresorhus/devtools-detect/issues/9
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
window.dispatchEvent(new CustomEvent('console-panel-devtoolschange', {
detail: {
open: state,
orientation: orientation
}
}));
};
// TODO:
// Currently, getDevToolsStatus() and updateDevToolsStatus() have duplicated functionality / code.
// Refactor them to reuse properly.
var getDevToolsStatus = function () {
var devtoolsStatus = {};
var widthThreshold = window.outerWidth - window.innerWidth > threshold;
var heightThreshold = window.outerHeight - window.innerHeight > threshold;
var orientation = widthThreshold ? 'vertical' : 'horizontal';
if (
!(heightThreshold && widthThreshold) &&
(
(
window.Firebug &&
window.Firebug.chrome &&
window.Firebug.chrome.isInitialized
) ||
widthThreshold ||
heightThreshold
)
) {
devtoolsStatus.open = true;
devtoolsStatus.orientation = orientation;
} else {
devtoolsStatus.open = false;
devtoolsStatus.orientation = null;
}
return devtoolsStatus;
};
scope.getDevToolsStatus = getDevToolsStatus;
var updateDevToolsStatus = function () {
var widthThreshold = window.outerWidth - window.innerWidth > threshold;
var heightThreshold = window.outerHeight - window.innerHeight > threshold;
var orientation = widthThreshold ? 'vertical' : 'horizontal';
if (
!(heightThreshold && widthThreshold) &&
(
(
window.Firebug &&
window.Firebug.chrome &&
window.Firebug.chrome.isInitialized
) ||
widthThreshold ||
heightThreshold
)
) {
if (!devtools.open || devtools.orientation !== orientation) {
emitEvent(true, orientation);
}
devtools.open = true;
devtools.orientation = orientation;
} else {
if (devtools.open) {
emitEvent(false, null);
}
devtools.open = false;
devtools.orientation = null;
}
return devtools;
};
scope.updateDevToolsStatus = updateDevToolsStatus;
window.addEventListener('resize', function (e) {
if (e.target !== window) {
return;
}
updateDevToolsStatus();
});
})(moduleGlobal);
var ready = function (cb) {
if (document.readyState !== 'loading') {
cb();
} else {
document.addEventListener('DOMContentLoaded', cb);
}
};
var getFullKey = function (key) {
return 'userPreference-' + key;
};
var defaultUserPreference = {};
defaultUserPreference[getFullKey('consolePanelHeight')] = '250';
var getLocalStorage = function (key) {
try {
return localStorage[key];
} catch (e) {
return undefined;
}
};
var setLocalStorage = function (key, value) {
try {
localStorage[key] = value;
return true;
} catch (e) {
return false;
}
};
var userPreference = function (preference, value) {
var fullKey = getFullKey(preference);
if (typeof value !== 'undefined') {
return setLocalStorage(fullKey, value);
} else {
var retValue = getLocalStorage(fullKey);
if (typeof retValue === 'undefined') {
return defaultUserPreference[fullKey];
}
return retValue;
}
};
var nl2br = function (str) { // eslint-disable-line no-unused-vars
return ('' + str)
.replace(/\r\n/g, '<br>')
.replace(/\n/g, '<br>')
.replace(/\r/g, '<br>');
};
var sanitizeHTML = function (html) {
return ('' + html)
.replace(/&/g,"&")
.replace(/</g,"<")
.replace(/>/g,">")
.replace(/"/g,""")
.replace(/'/g,"'")
.replace(/\//g,"/");
};
var sanitizedInnerHTML = function (el, html) {
el.innerHTML = sanitizeHTML(html);
};
var ellipsis = function (str, length) {
if (!length || length <= 3) {
return str;
} else {
if (str.length > length) {
return str.substr(0, length - 3) + '...';
} else {
return str;
}
}
};
var getResourceUrlFromPath = function (path) {
path = path || '';
path = path.replace(/:[0-9]+$/, ''); // Sometimes, we may get line number and character number both
path = path.replace(/:[0-9]+$/, ''); // Sometimes, we may get only line number
return path;
};
var getResourceLineCharacterFromPath = function (path) {
path = path || '';
path = path.split('/').pop();
return path;
};
var getCurrentExecutionDetails = function (options) {
var skipLevels = options.skipLevels || 1;
var executionDetails = {
stack: null,
resourceLineCharacter: null,
resourceUrlLineCharacter: null,
resourceUrl: null
};
var errStr = '';
if (Object.keys(options).indexOf('stack') >= 0) {
if (options.stack) {
errStr = options.stack;
} else {
errStr = '';
}
} else {
try {
// IMPORTANT:
// If you have enabled "Pause on caught exceptions" feature in your browser's DevTools
// and your debugging session is getting interrupted by this error, please check the
// stack trace of current execution. You may find appropriate instructions to avoid
// that problem.
throw new Error('');
} catch (e) {
errStr = e.stack || '';
}
}
var arrStack;
var isGecko = (
navigator.userAgent.toLowerCase().indexOf('gecko') !== -1 &&
navigator.userAgent.toLowerCase().indexOf('like gecko') === -1
);
if (isGecko) {
arrStack = errStr.split(/\n[\s]*/).map(function (str) {
var split = str.split('@');
return split[split.length-1];
});
arrStack.splice(0, skipLevels - 1);
} else {
arrStack = errStr.split(/\n[\s]+at[\s]/);
arrStack.splice(0, skipLevels);
}
var stackToReport = errStr.split(/\n/);
if (stackToReport[0] === 'Error') {
stackToReport.splice(1, skipLevels - 1);
} else {
stackToReport.splice(0, skipLevels - 1);
}
stackToReport = stackToReport.join('\n');
var relevantString = arrStack[0];
if (relevantString && relevantString.indexOf('(') >= 0) {
relevantString = relevantString.substr(relevantString.indexOf('(') + 1);
relevantString = relevantString.substr(0, relevantString.indexOf(')'));
}
executionDetails = {
stack: stackToReport,
resourceLineCharacter: getResourceLineCharacterFromPath(relevantString),
resourceUrlLineCharacter: relevantString,
resourceUrl: getResourceUrlFromPath(relevantString)
};
return executionDetails;
};
// https://bytes.babbel.com/en/articles/2014-09-09-javascript-function-call-interception.html
// Note that the code from that blog post has been corrected below
var after = function (object, method, fn, context) {
context = context || object;
var originalMethod = object[method];
object[method] = function () {
// originalMethod may be null (eg: If we wish to intercept window.onerror, and window.onerror might be null)
if (typeof originalMethod === 'function') {
// IMPORTANT:
// If you are seeing the following line for log entries in the native console and wish to get rid of it,
// please use the following command to disable consolePanel:
// consolePanel.disable()
originalMethod.apply(context, arguments);
}
// IMPORTANT:
// If you are encountering the following line in stack trace for "Pause on caught exceptions" feature in your
// browser's DevTools and your debugging session is getting interrupted by it, please run the following command to
// disable logging line numbers in consolePanel
// consolePanel.disableReportLogLines()
fn.apply(context, arguments);
};
return originalMethod;
};
// Note: For documentation of before() function, please refer to the documentation of the after() function
var before = function (object, method, fn, context) { // eslint-disable-line no-unused-vars
context = context || object;
var originalMethod = object[method];
object[method] = function () {
if (typeof originalMethod === 'function') {
fn.apply(context, arguments);
}
originalMethod.apply(context, arguments);
};
return originalMethod;
};
var varConsole;
// If "console" has a special implementation (applicable for Microsoft Edge (EdgeHTML) and Internet Explorer)
if ((Object.getPrototypeOf(console)).log) {
varConsole = (Object.getPrototypeOf(console));
} else {
varConsole = console;
}
// Definition of ConsolePanel class
var ConsolePanel = (function () {
var ConsolePanel = function () {
this.originals = {};
this.arrLogs = [];
this.config = {
reportLogLines: true
};
this.domReady = false;
this.enabled = false;
};
ConsolePanel.prototype.setButtonPosition = function (position) {
this.devToolsIconContainer.className = 'dev-tools-icon-container dev-tools-icon-container-' + position;
};
ConsolePanel.prototype.showDevToolsIconContainer = function () {
if (!this.isConsolePanelVisible()) {
this.devToolsIconContainer.style.display = 'block';
}
};
ConsolePanel.prototype.hideDevToolsIconContainer = function () {
this.devToolsIconContainer.style.display = 'none';
};
ConsolePanel.prototype.isDevToolsIconContainerVisible = function () {
return this.devToolsIconContainer.style.display === 'block';
};
ConsolePanel.prototype.isConsolePanelVisible = function () {
return this.devTools.style.display === 'block';
};
ConsolePanel.prototype.hideConsolePanel = function () {
this.devTools.style.display = 'none';
};
ConsolePanel.prototype.showConsolePanel = function () {
this.devTools.style.display = 'block';
this.flushLogsToUIAsync();
};
ConsolePanel.prototype.hideBecauseDevToolsIsOpen = function () {
var that = this;
alertNote('Disabled console-panel', null, {verticalAlignment: 'bottom', horizontalAlignment: 'right'});
that.disable();
that.hideDevToolsIconContainer();
that.hideConsolePanel();
};
ConsolePanel.prototype.showBecauseDevToolsIsClosed = function () {
var that = this;
that.enable(that.config);
if (that.isDevToolsIconContainerVisible()) {
alertNote.hide();
} else {
alertNote('Enabled console-panel', null, {verticalAlignment: 'bottom', horizontalAlignment: 'right'});
}
};
ConsolePanel.prototype.hasStrongNotification = function () {
var strongNotificationFor = this.config.strongNotificationFor;
var skipStrongNotificationIfNoStackTrace = this.config.skipStrongNotificationIfNoStackTrace;
var showStrongNotification = false;
var arrLogs = this.arrLogs;
for (var i = 0; i < arrLogs.length; i++) {
var log = arrLogs[i],
logMode = log.logMode;
if (Date.now() <= (arrLogs[i].time.getTime() + 1500)) {
if (skipStrongNotificationIfNoStackTrace && !log.initiator.stack) {
// do nothing
} else if (
(logMode === 'window.onerror' && strongNotificationFor.indexOf('window.onerror') >= 0) ||
(logMode === 'error' && strongNotificationFor.indexOf('console.error' ) >= 0) ||
(logMode === 'warn' && strongNotificationFor.indexOf('console.warn' ) >= 0) ||
(logMode === 'info' && strongNotificationFor.indexOf('console.info' ) >= 0) ||
(logMode === 'log' && strongNotificationFor.indexOf('console.log' ) >= 0)
) {
showStrongNotification = true;
break;
}
}
}
return showStrongNotification;
};
ConsolePanel.prototype.getRecommendedClassNameForDevToolsIcon = function () {
var recommendedClassName = 'found-something';
var foundError = false,
foundWarn = false,
foundInfo = false,
foundLog = false;
var arrLogs = this.arrLogs;
for (var i = 0; i < arrLogs.length; i++) {
var logMode = arrLogs[i].logMode;
if (logMode === 'error' || logMode === 'window.onerror') { foundError = true; }
else if (logMode === 'warn') { foundWarn = true; }
else if (logMode === 'info') { foundInfo = true; }
else if (logMode === 'log' ) { foundLog = true; }
}
if (foundError) { recommendedClassName = 'found-error'; }
else if (foundWarn ) { recommendedClassName = 'found-warn'; }
else if (foundInfo ) { recommendedClassName = 'found-info'; }
else if (foundLog ) { recommendedClassName = 'found-log'; }
return recommendedClassName;
};
ConsolePanel.prototype.areThereUnreadRelevantMessages = function (relevantMessages) {
var arrLogs = this.arrLogs;
if (arrLogs.length) {
if (relevantMessages === 'all') {
return true;
} else if (Array.isArray(relevantMessages)) {
for (var i = 0; i < arrLogs.length; i++) {
var normalizedLogMode = arrLogs[i].logMode;
if (normalizedLogMode !== 'window.onerror') {
normalizedLogMode = 'console.' + normalizedLogMode;
}
if (relevantMessages.indexOf(normalizedLogMode) >= 0) {
return true;
}
}
}
}
return false;
};
ConsolePanel.prototype.flushCountToIcon = function () {
var devToolsIconStrongNotification = this.devToolsIconStrongNotification,
devToolsIcon = this.devToolsIcon;
if (this.config.showOnlyForTheseRelevantMessages) {
var relevantMessages = this.config.showOnlyForTheseRelevantMessages;
if (this.areThereUnreadRelevantMessages(relevantMessages)) {
this.showDevToolsIconContainer();
}
}
var arrLogs = this.arrLogs;
if (arrLogs.length) {
devToolsIcon.innerHTML = arrLogs.length;
devToolsIcon.title = arrLogs.length + ' unread message' + (arrLogs.length === 1 ? '' : 's');
var recommendedClassName = this.getRecommendedClassNameForDevToolsIcon();
var showStrongNotification = this.hasStrongNotification();
devToolsIcon.className = 'dev-tools-icon ' + recommendedClassName;
devToolsIconStrongNotification.className = (function () {
if (showStrongNotification) {
return 'strong-notification';
} else {
return '';
}
}());
if (showStrongNotification) {
var dataLastStrongNotification = Date.now();
devToolsIconStrongNotification.setAttribute('data-last-strong-notification', dataLastStrongNotification);
var animationDuration = 1500;
setTimeout(function () {
if (dataLastStrongNotification === parseInt(devToolsIconStrongNotification.getAttribute('data-last-strong-notification'), 10)) {
devToolsIconStrongNotification.removeAttribute('data-last-strong-notification');
devToolsIconStrongNotification.classList.remove('strong-notification');
}
}, animationDuration);
}
} else {
devToolsIcon.innerHTML = '';
devToolsIcon.removeAttribute('title');
devToolsIcon.className = 'dev-tools-icon no-unread-messages';
devToolsIconStrongNotification.classList.remove('strong-notification');
}
};
ConsolePanel.prototype.flushLogsToUIAsync = function () {
var that = this;
// Using 2 requestAnimationFrame() to avoid performance issues
requestAnimationFrame(function () {
requestAnimationFrame(function () {
that.flushLogsToUI();
});
});
};
ConsolePanel.prototype.flushLogsToUI = function () {
this.flushCountToIcon();
if (!this.isConsolePanelVisible()) {
return;
}
var shouldScrollToBottom = false;
var logger = this.logger;
if (logger.scrollHeight === logger.scrollTop + logger.offsetHeight) {
shouldScrollToBottom = true;
}
var arrLogs = this.arrLogs;
while (arrLogs.length) {
var item = arrLogs.shift();
var logMode = item.logMode,
logEntry = item.logEntry,
initiator = item.initiator,
time = item.time;
var consoleMessageWrapper = document.createElement('div');
this.loggerBody.appendChild(consoleMessageWrapper);
consoleMessageWrapper.title = 'Logged at ' + time.toTimeString().substring(0, 8);
consoleMessageWrapper.className = 'dev-tools-console-message-wrapper ' + (function () {
if (logMode === 'log' ) { return 'log-mode-log'; } // Note: This CSS class is not being used yet
else if (logMode === 'info' ) { return 'log-mode-info'; }
else if (logMode === 'warn' ) { return 'log-mode-warn'; }
else if (logMode === 'error' ) { return 'log-mode-error'; }
else if (logMode === 'window.onerror') { return 'log-mode-window-onerror'; }
else if (logMode === 'clear' ) { return 'log-mode-clear'; } // Note: This CSS class is not being used yet
else if (logMode === 'unhandled' ) { return 'log-mode-unhandled'; }
else { return 'log-mode-unknown'; }
}());
var divLogExecution = document.createElement('div');
consoleMessageWrapper.appendChild(divLogExecution);
divLogExecution.className = 'dev-tools-console-message-code-line';
divLogExecution.innerHTML = (function (initiator) {
if (initiator.resourceLineCharacter) {
var str = '<a target="_blank" style="color:#545454; font-family:monospace;"' +
' href="' + sanitizeHTML(initiator.resourceUrl) + '"' +
' title="' + sanitizeHTML(initiator.resourceUrlLineCharacter) + '">' +
sanitizeHTML(initiator.resourceLineCharacter) +
'</a>';
return str;
} else {
return '';
}
}(initiator));
var consoleMessage = document.createElement('div');
consoleMessageWrapper.appendChild(consoleMessage);
consoleMessage.className = 'dev-tools-console-message';
var span;
if (logEntry.length === 0) {
span = document.createElement('span');
consoleMessage.appendChild(span);
span.innerHTML = ' ';
} else {
for (var i = 0; i < logEntry.length; i++) {
if (i > 0) {
var spacer = document.createElement('span');
consoleMessage.appendChild(spacer);
spacer.innerHTML = ' ';
}
span = document.createElement('span');
consoleMessage.appendChild(span);
var updateDom = function (options) {
var className = options.className || 'log-value-unknown';
var valueToLog = options.valueToLog;
var container = span;
container.className = className;
if (className === 'log-value-unknown' || className === 'log-value-object' || className === 'log-value-array') {
if (typeof JSONEditor === 'undefined') {
container.classList.add('jsoneditor-not-available');
if (Array.isArray(valueToLog)) {
sanitizedInnerHTML(container, String('[' + valueToLog.length + ']'));
} else if (typeof valueToLog === 'object') {
sanitizedInnerHTML(container, String('{' + Object.keys(valueToLog).length + '}'));
} else {
sanitizedInnerHTML(container, String(typeof valueToLog) + ' (' + String(valueToLog) + ')');
}
} else {
var jsonEditorOptions = {
mode: 'view',
navigationBar: false,
search: false,
sortObjectKeys: true
};
var editor = new JSONEditor(container, jsonEditorOptions);
editor.set(valueToLog);
editor.collapseAll();
}
} else if (className === 'log-value-dom') {
var firstLineOfValueToLog = valueToLog.split('\n')[0];
var hasMultilineHTML = false;
if (firstLineOfValueToLog !== valueToLog) {
hasMultilineHTML = true;
}
var renderFullCode = function () {
var spanFullCode = document.createElement('span');
container.appendChild(spanFullCode);
spanFullCode.className = 'all-lines-of-code';
spanFullCode.innerHTML = '<pre><code class="language-markup">' +
sanitizeHTML(valueToLog) +
'</code></pre>';
if (typeof Prism !== 'undefined') {
Prism.highlightAllUnder(spanFullCode);
}
return spanFullCode;
};
if (hasMultilineHTML) {
var spanExpandCollapse = document.createElement('span');
container.appendChild(spanExpandCollapse);
spanExpandCollapse.className = 'console-panel-expand-collapse console-panel-collapsed';
var spanFullCode;
var spanFirstLine = document.createElement('span');
container.appendChild(spanFirstLine);
spanFirstLine.className = 'only-first-line-of-code';
spanFirstLine.innerHTML = '<pre><code class="language-markup">' +
sanitizeHTML(firstLineOfValueToLog) +
'</code></pre>';
if (typeof Prism !== 'undefined') {
Prism.highlightAllUnder(spanFirstLine);
}
spanExpandCollapse.addEventListener('click', function (evt) { // eslint-disable-line no-unused-vars
var currentlyInCollapsedState = spanExpandCollapse.classList.contains('console-panel-collapsed');
if (currentlyInCollapsedState) {
spanFirstLine.style.display = 'none';
if (spanFullCode) {
spanFullCode.style.display = '';
} else {
spanFullCode = renderFullCode();
}
} else {
spanFirstLine.style.display = '';
spanFullCode.style.display = 'none';
}
spanExpandCollapse.classList.toggle('console-panel-collapsed');
spanExpandCollapse.classList.toggle('console-panel-expanded');
});
} else {
renderFullCode();
}
} else {
container.innerHTML = String(valueToLog); // To ensure that we are setting a string as innerHTML
}
};
updateDom(logEntry[i]);
}
}
// Show call stack along with console.warn and console.error messages
if (['error', 'warn'].indexOf(logMode) >= 0) {
if (initiator.stack) {
var div = document.createElement('div');
consoleMessage.appendChild(div);
div.className = 'log-call-stack';
var initiatorStack = initiator.stack.split('\n');
initiatorStack.shift();
initiatorStack = initiatorStack.join('\n');
div.innerHTML = sanitizeHTML(initiatorStack);
}
}
}
if (shouldScrollToBottom && consoleMessageWrapper) {
consoleMessageWrapper.scrollIntoView(false);
}
this.flushCountToIcon();
};
ConsolePanel.prototype.logArrayEntry = function (options) {
var type = options.type || 'unknown',
initiator = options.initiator || {},
value = options.value;
var className = 'log-value-unknown',
valueToLog = 'not-handled';
if (type === 'boolean') {
className = 'log-value-boolean';
valueToLog = value;
} else if (type === 'number') {
className = 'log-value-number';
valueToLog = value;
} else if (type === 'string') {
className = 'log-value-string';
valueToLog = sanitizeHTML(ellipsis(value.toString(), 5003));
} else if (type === 'document.all') {
className = 'log-value-document-all'; // TODO: Not handled in CSS yet
valueToLog = value;
} else if (type === 'undefined') {
className = 'log-value-undefined';
valueToLog = value;
} else if (type === 'null') {
className = 'log-value-null';
valueToLog = value;
} else if (type === 'function') {
className = 'log-value-function';
valueToLog = value;
} else if (type === 'console.clear') {
className = 'log-value-console-clear';
valueToLog = value;
} else if (type === 'dom') {
className = 'log-value-dom';
valueToLog = value.outerHTML;
} else if (type === 'dom-text') {
className = 'log-value-dom-text';
valueToLog = value.textContent;
} else if (type === 'window.onerror') {
className = 'log-value-window-onerror';
var errorMessageToLog = (function () {
// https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror
var strError = 'An error occurred';
var error = value.error;
try {
strError = error[4].stack;
} catch (e) {
try {
strError = value.error[0] + '\n' +
value.error[1] + ':' + value.error[2] +
(
typeof value.error[3] === 'undefined' ?
'' :
':' + value.error[3]
);
} catch (e) {
// do nothing
}
}
return strError;
}());
valueToLog = sanitizeHTML(errorMessageToLog);
} else if (type === 'array') {
className = 'log-value-array';
valueToLog = JSON.parse(CircularJSON.stringify(value, null, '', "[Circular]"));
} else if (type === 'object') {
className = 'log-value-object';
valueToLog = JSON.parse(CircularJSON.stringify(value, null, '', "[Circular]"));
} else {
className = 'log-value-unknown'; // TODO: Not handled in CSS yet.
valueToLog = JSON.parse(CircularJSON.stringify(value, null, '', "[Circular]"));
}
return {
className: className,
initiator: initiator,
valueToLog: valueToLog
};
};
ConsolePanel.prototype.markLogEntry = function (logMode, args) {
var entryToPush = [];
for (var i = 0; i < args.length; i++) {
var msg = args[i];
// TODO:
// Handle various native objects
// References:
// http://xahlee.info/js/js_Object.prototype.toString.html
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
// https://gist.github.com/pbakondy/f442e91995e9d206c056
// Use:
// var objectType = Object.prototype.toString.call(msg);
var logEntryType = 'unknown';
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#Description
if (typeof msg === 'boolean') { logEntryType = 'boolean'; }
else if (typeof msg === 'function') { logEntryType = 'function'; }
else if (typeof msg === 'number') { logEntryType = 'number'; }
else if (typeof msg === 'string') { logEntryType = 'string'; }
else if (typeof msg === 'symbol') { logEntryType = 'unknown'; } // TODO: Currently using logEntryType as 'unknown' for 'symbol'
else if (typeof msg === 'object') {
if (msg === null) { logEntryType = 'null'; }
else if (msg instanceof HTMLElement) { logEntryType = 'dom'; } // Note: This may not work well for nodes across iframes. See: https://stackoverflow.com/questions/13894644/having-trouble-with-dom-nodes-and-instanceof/13895357#13895357
else if (msg instanceof Text) { logEntryType = 'dom-text'; } // Note: This may not work well for nodes across iframes. See: https://stackoverflow.com/questions/13894644/having-trouble-with-dom-nodes-and-instanceof/13895357#13895357
else {
if (logMode === 'window.onerror') { logEntryType = 'window.onerror'; }
else if (Array.isArray(msg)) { logEntryType = 'array'; }
else { logEntryType = 'object'; }
}
// TODO:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
// Handle various objects, like:
// * "Array" object
// * "Date" object
// * Error objects
// * Error, EvalError, InternalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError
// * "Function" object
// * "Arguments" object
// * various nodeTypes (for example: "Comment" node) // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#Node_type_constants
// * "RegExp" object
// * "global" object
// * "Math" object
// * "JSON" object
// * Indexed_collections
// * Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array
// * Keyed_collections
// * Map, Set, WeakMap, WeakSet
// * Structured_data
// * ArrayBuffer, SharedArrayBuffer, Atomics, DataView
// * Control_abstraction_objects
// * Promise, Generator, GeneratorFunction, AsyncFunction
// * Reflection
// * Reflect, Proxy
// * Internationalization
// * Intl, Intl.Collator, Intl.DateTimeFormat, Intl.NumberFormat
// * WebAssembly
// * WebAssembly, WebAssembly.Module, WebAssembly.Instance, WebAssembly.Memory, WebAssembly.Table, WebAssembly.CompileError, WebAssembly.LinkError, WebAssembly.RuntimeError
}
else if (typeof msg === 'undefined') {
if (msg === document.all) { logEntryType = 'document.all'; } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#Exceptions
else { logEntryType = 'undefined'; }
}
else { logEntryType = 'unknown'; } // Handle any unknown "typeof"
entryToPush.push(
this.logArrayEntry({
type: logEntryType,
value: msg
})
);
}
var report = {
logMode: logMode,
time: new Date(),
logEntry: entryToPush
};
if (this.config.reportLogLines) {
if (logMode === 'window.onerror') {
report.initiator = getCurrentExecutionDetails({skipLevels: 0, stack: (args[0].error[4] || {}).stack});
} else {
report.initiator = getCurrentExecutionDetails({skipLevels: 5});
}
} else {
report.initiator = {};
}
var that = this;
that.arrLogs.push(report);
ready(function () {
that.flushLogsToUIAsync();
});
};
ConsolePanel.prototype.clear = function () {
var that = this;
var reportLogLines = that.config.reportLogLines;
// Since console is being cleared, "arrLogs" can be emptied
that.arrLogs = [];
that.arrLogs.push({
logMode: 'clear',
time: new Date(),
logEntry: [
that.logArrayEntry({
type: 'console.clear',
value: 'Console was cleared'
})
],
initiator: reportLogLines ?
getCurrentExecutionDetails({skipLevels: 4}) :
{}
});
that.loggerBody.innerHTML = '';
that.flushLogsToUIAsync();
};
ConsolePanel.prototype.setupIntercept = function () {
var that = this;
var originals = that.originals;
var functionsToIntercept = that.config.functionsToIntercept;
var interceptIfRequired = function (type, original, cb) {
if (
functionsToIntercept === 'all' ||
functionsToIntercept.indexOf(type) >= 0
) {
return cb();
} else {
return original;
}
};
originals['window.onerror'] = interceptIfRequired('window.onerror', originals['window.onerror'], function () { return after(window, 'onerror', function () { that.markLogEntry('window.onerror', [{ error: arguments }]); }); });
// Note: console.clear() would be intercepted in all cases, therefore, not using interceptIfRequired()
originals['console.clear' ] = after(varConsole, 'clear', function () { that.clear(); }, console);
originals['console.log' ] = interceptIfRequired('console.log', originals['console.log' ], function () { return after(varConsole, 'log', function () { that.markLogEntry('log', arguments); }, console); });
originals['console.info' ] = interceptIfRequired('console.info', originals['console.info' ], function () { return after(varConsole, 'info', function () { that.markLogEntry('info', arguments); }, console); });
originals['console.warn' ] = interceptIfRequired('console.warn', originals['console.warn' ], function () { return after(varConsole, 'warn', function () { that.markLogEntry('warn', arguments); }, console); });
originals['console.error' ] = interceptIfRequired('console.error', originals['console.error' ], function () { return after(varConsole, 'error', function () { that.markLogEntry('error', arguments); }, console); });
Object.keys(varConsole).forEach(function (key) {
if (['log', 'info', 'warn', 'error', 'clear'].indexOf(key) === -1) {
if (typeof varConsole[key] === 'function') {
originals['console.' + key] = interceptIfRequired(key, originals['console.' + key], function () { return after(varConsole, key, function () { that.markLogEntry('unhandled', arguments); }, console); });
}
}
});
};
ConsolePanel.prototype.render = function () {
var that = this;
var consolePanelContainer = document.createElement('div');
consolePanelContainer.id = 'console-panel';
document.body.appendChild(consolePanelContainer);
var devToolsIconContainer = document.createElement('div');
this.devToolsIconContainer = devToolsIconContainer;
consolePanelContainer.appendChild(devToolsIconContainer);
var devToolsIconStrongNotification = document.createElement('div');
this.devToolsIconStrongNotification = devToolsIconStrongNotification;
devToolsIconContainer.appendChild(devToolsIconStrongNotification);
var devToolsIcon = document.createElement('div');
this.devToolsIcon = devToolsIcon;
devToolsIcon.className = 'dev-tools-icon no-unread-messages';
devToolsIcon.addEventListener('click', function (evt) { // eslint-disable-line no-unused-vars
that.showConsolePanel();
that.hideDevToolsIconContainer();
});
this.hideDevToolsIconContainer();
devToolsIconStrongNotification.appendChild(devToolsIcon);
var devTools = document.createElement('div');
this.devTools = devTools;
consolePanelContainer.appendChild(devTools);
devTools.className = 'dev-tools';
this.hideConsolePanel();
devTools.style.height = (function () {
var height = parseInt(userPreference('consolePanelHeight'), 10);
if (height >= 0 && height <= Infinity) {
// do nothing
} else {
height = defaultUserPreference[getFullKey('consolePanelHeight')];
}
return height + 'px';
}());
// Just a block
{
var devToolsHeader = document.createElement('div');
devTools.appendChild(devToolsHeader);
devToolsHeader.className = 'dev-tools-header';
// Just a block
{
var consoleDragHandle = document.createElement('div');
devToolsHeader.appendChild(consoleDragHandle);
consoleDragHandle.className = 'dev-tools-resize-handle';
consoleDragHandle.innerHTML = ' ';
var crossIcon = document.createElement('div');
devToolsHeader.appendChild(crossIcon);
crossIcon.title = 'Close';
crossIcon.className = 'dev-tools-header-cross-icon';
crossIcon.addEventListener('click', function (evt) { // eslint-disable-line no-unused-vars
that.hideConsolePanel();
that.showDevToolsIconContainer();
});
var disableIcon = document.createElement('div');
that.disableIcon = disableIcon;
disableIcon.title = constants.DISABLE_FOR_THIS_INSTANCE;
disableIcon.className = 'dev-tools-header-disable-icon';
disableIcon.addEventListener('click', function (evt) { // eslint-disable-line no-unused-vars
if (that.config && typeof that.config.beforeDisableButtonClick === 'function') {
var doContinue = that.config.beforeDisableButtonClick();
if (doContinue === false) {
return;
}
}
that.hideConsolePanel();
that.disable();
});
devToolsHeader.appendChild(disableIcon);
var clearConsoleIcon = document.createElement('div');
devToolsHeader.appendChild(clearConsoleIcon);
clearConsoleIcon.title = 'Clear';
clearConsoleIcon.className = 'dev-tools-clear-console-icon';
clearConsoleIcon.addEventListener('click', function (evt) { // eslint-disable-line no-unused-vars
// For most of the situations/browsers, the call to that.clear() is redundant here because immediately
// after it, we are calling console.clear() which internally calls that.clear()
// But, in some cases, if console.clear() call is not intercepted (eg: In some situations
// in IE / Edge or for any future changes where console.clear() may be skipped from
// interception), then we shall call it manually.
that.clear();
console.clear();
});
var consoleTitle = document.createElement('div');
devToolsHeader.appendChild(consoleTitle);
consoleTitle.innerHTML = 'Console';
consoleTitle.style.cssFloat = 'left';
}
if ($ && $.fn && $.fn.resizable) {
// Just a preventive try...catch block
try {
var $devTools = $('.dev-tools');
if ($.ui) {
// http://api.jqueryui.com/resizable/#option-handles
consoleDragHandle.classList.add('ui-resizable-handle');
consoleDragHandle.classList.add('ui-resizable-n');
$devTools.resizable({
handles: {
n: $devTools.find('.dev-tools-resize-handle')
},
stop: function (evt, ui) { // eslint-disable-line no-unused-vars
userPreference('consolePanelHeight', ui.size.height);
}
});
} else {
$devTools.resizable({
handleSelector: '.dev-tools-resize-handle',
resizeWidth: false,
resizeHeightFrom: 'top',
onDragEnd: function (e, $el, opt) { // eslint-disable-line no-unused-vars
userPreference('consolePanelHeight', $el.outerHeight());
}
});
}
// Use this styling only when the resizing has been setup
consoleDragHandle.style.cursor = 'n-resize';
} catch (e) {
alertNote(
'Error in setting up "resize" for console-panel' +
' (' +
'<a target="_blank" href="https://github.com/webextensions/console-panel#full-featured-setup">Learn more</a>' +
')',
10000
);
}
}
var logger = document.createElement('div');
this.logger = logger;
devTools.appendChild(logger);
logger.className = 'dev-tools-console';
// Just a block
{
var loggerHeader = document.createElement('div');
logger.appendChild(loggerHeader);
loggerHeader.className = 'dev-tools-console-header';
var loggerBody = document.createElement('div');
this.loggerBody = loggerBody;
logger.appendChild(loggerBody);
loggerBody.className = 'dev-tools-console-body';
}
}
window.addEventListener('console-panel-devtoolschange', function (e) {
if (that.config.doNotUseWhenDevToolsMightBeOpenInTab) {
if (e.detail.open) {
that.hideBecauseDevToolsIsOpen();
} else {
that.showBecauseDevToolsIsClosed();
}
}
});
moduleGlobal.updateDevToolsStatus(); // Ensure that the 'console-panel-devtoolschange' event gets fired once (if required)
};
/*
config {}
position
Summary: Position of console-panel's icon
Type: string
Supported positions: "top-left", "top-right", "bottom-left", "bottom-right"
Default value: "bottom-right",
Example value: "top-right"
functionsToIntercept
Summary: List of console functions which should be intercepted
Type: <falsy-value> OR "all" OR array (of strings)
Supported function names: "window.onerror", "console.error", "console.warn", "console.info", "console.log"
Default value: "all",
Example value: ["window.onerror", "console.error"]
Notes: console.clear() would always get intercepted when console-panel is enabled
showOnlyForTheseRelevantMessages
Summary: List of console function calls for which console-panel icon should be shown
Type: <falsy-value> OR "all" OR array (of strings)
Supported function names: "window.onerror", "console.error", "console.warn", "console.info", "console.log"
Default value: null
Example value: ["window.onerror", "console.error", "console.warn"]
Notes: If it is a <falsy-value>, then console-panel notification icon would be shown all the time
strongNotificationFor
Summary: List of console function calls for which console-panel notification should be shown strongly
Type: <falsy-value> OR array (of strings)
Supported function names: "window.onerror", "console.error", "console.warn", "console.info", "console.log"
Default value: ["window.onerror", "console.error"]
Example value: ["window.onerror", "console.error", "console.warn"]
skipStrongNotificationIfNoStackTrace
Summary: When it is set as true, "strong-notification" effect is not shown for errors for which stack
trace is not available. This can be used to avoid highlighting errors which are occurring due
to a cross-origin / third-party script.
Type: boolean
Allowed values: <falsy-value> OR <truthy-value>
Default value: false
Example value: false
reportLogLines
Summary: When it is set as true, the corresponding code line is mentioned along with each console entry.
When it is set as true, it may interrupt your debugging session if you are using the "Pause on
caught exceptions" feature in browser DevTools
Type: boolean
Allowed values: <falsy-value> OR <truthy-value>
Default value: true
Example value: true
doNotUseWhenDevToolsMightBeOpenInTab
Summary: Disable console-panel if browser DevTools might be open within the tab
Type: boolean
Allowed values: <falsy-value> OR <truthy-value>
Default value: false
Example value: false
Reference: https://github.com/sindresorhus/devtools-detect#support
disableButtonTitle
Summary: Customize the title for the "disable" button in console-panel
Type: string
Allowed values: Any non-empty string
Default value: "Disable for this instance"
Example value: "Disable\n(and keep disabled)"
beforeDisableButtonClick
Summary: Function to be called before performing the default action for "disable" button
Type: function
Example value: function () { localStorage['console-panel-status'] = 'disabled'; }
Notes: If this function returns boolean "false", then the default action would not be performed
*/
ConsolePanel.prototype.enable = function (config) {
config = config || {};
var that = this;
// If consolePanel is already enabled
if (that.enabled) {
// Disable consolePanel
that.disable();
}
var functionsToIntercept = (function () {
if (Array.isArray(config.functionsToIntercept)) {
return config.functionsToIntercept;
} else {
return 'all';
}
}()),
showOnlyForTheseRelevantMessages = config.showOnlyForTheseRelevantMessages || null,
strongNotificationFor = config.strongNotificationFor || ['window.onerror', 'console.error'],
skipStrongNotificationIfNoStackTrace = config.skipStrongNotificationIfNoStackTrace || false,
reportLogLines = typeof config.reportLogLines === 'undefined' ? true : !!config.reportLogLines,
doNotUseWhenDevToolsMightBeOpenInTab = typeof config.doNotUseWhenDevToolsMightBeOpenInTab === 'undefined' ? false : config.doNotUseWhenDevToolsMightBeOpenInTab,
disableButtonTitle = (typeof config.disableButtonTitle === 'string' && config.disableButtonTitle !== '') ? config.disableButtonTitle : constants.DISABLE_FOR_THIS_INSTANCE,
beforeDisableButtonClick = config.beforeDisableButtonClick,
position = (function () {
switch(config.position) {
case 'top-left':
case 'top-right':
case 'bottom-left':
case 'bottom-right':
case 'left-top':
case 'left-bottom':
case 'right-top':
case 'right-bottom':
return config.position;
default:
return 'bottom-right';
}
}());
(function (config) {
config.functionsToIntercept = functionsToIntercept;
config.showOnlyForTheseRelevantMessages = showOnlyForTheseRelevantMessages;
config.strongNotificationFor = strongNotificationFor;
config.skipStrongNotificationIfNoStackTrace = skipStrongNotificationIfNoStackTrace;
config.doNotUseWhenDevToolsMightBeOpenInTab = doNotUseWhenDevToolsMightBeOpenInTab;
config.disableButtonTitle = disableButtonTitle;
config.beforeDisableButtonClick = beforeDisableButtonClick;
config.position = position;
}(that.config));
that.setupIntercept();
if (reportLogLines) {
that.enableReportLogLines();
} else {
that.disableReportLogLines();
}
if (!that.domReady) {
ready(function () {
that.render();
that.domReady = true;
});
}
ready(function () {
that.setButtonPosition(position);
that.disableIcon.title = disableButtonTitle;
if (
showOnlyForTheseRelevantMessages ||
(
that.config.doNotUseWhenDevToolsMightBeOpenInTab &&
(function () {
var devtoolsStatus = moduleGlobal.getDevToolsStatus();
return devtoolsStatus && devtoolsStatus.open;
}())
)
) {
// do nothing
} else {
that.showDevToolsIconContainer();
}
that.flushLogsToUIAsync();
});
that.enabled = true;
};
ConsolePanel.prototype.disable = function () {
var that = this;
// Restore window.onerror
window.onerror = that.originals['window.onerror'];
// Restore console functions
Object.keys(varConsole).forEach(function (key) {
if (that.originals['console.' + key]) { // Ensure that we have over-ridden that console member (function)
varConsole[key] = that.originals['console.' + key];
}
});
that.enabled = false;
};
ConsolePanel.prototype.enableReportLogLines = function () {
this.config.reportLogLines = true;
};
ConsolePanel.prototype.disableReportLogLines = function () {
this.config.reportLogLines = false;
};
return ConsolePanel;
}());
var ready = function (cb) {
if (document.readyState !== 'loading') {
cb();
} else {
document.addEventListener('load', cb);
}
};
ready(function () {
const panel = new ConsolePanel();
window.consolePanel = panel;
panel.enable();
});
}(window.jQuery));