Skip to content

Instantly share code, notes, and snippets.

@amit08255
Last active June 29, 2022 18:09
Show Gist options
  • Save amit08255/2e17ab244499f976f0fecd3302748c4b to your computer and use it in GitHub Desktop.
Save amit08255/2e17ab244499f976f0fecd3302748c4b to your computer and use it in GitHub Desktop.
Chrome extension inject javascript

Chrome Extension Inject JavaScript to Website

Simple example to inject console panel to any website. It injects CSS and JavaScript to the website.

Example: Manifest.json

{
  "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"
  }
}

Example: console-panel.js to inject javascript

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();
})();

Example: inject.js to inject css

// 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);

})();

Example: panel.js script which will be injected

(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,"&amp;")
            .replace(/</g,"&lt;")
            .replace(/>/g,"&gt;")
            .replace(/"/g,"&quot;")
            .replace(/'/g,"&#x27;")
            .replace(/\//g,"&#x2F;");
    };
    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 = '&nbsp;';

                    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));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment