// #region UserScript Metadata
// ==UserScript==
// #region Info
// @name akkd-all-sites
// @namespace https://openuserjs.org/users/93Akkord
// @version 0.0.4
// @description Akkd All Sites
// @copyright 2022+, Michael Barros (https://openuserjs.org/users/93Akkord)
// @license CC-BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
// @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt
// @author 93Akkord
// @run-at document-start
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNvyMY98AAA5DSURBVGhDtVr5T1zXGeVviPJDq/bHNq2ZN8MyrGEx4C2JY6tqo+SXRGoVKYqqSlXaqFUjNUlTRXWUWE5iXC/YmNWAjdmXGRgDNruNYciwG4aBYR0wY+x4AWbe1/PdN282PwO21HGO333bfed893zfve85Efx7q6zrpd9UDX32er295bDZ2f6GeVbgdZMT4G1Q2+zwbWfbD6F9SGydvu1s+0GcU3HAh5B2fTgc7QeALLSzGmfbM4GsRuwD+8RWOZaB84y06smW/RUTnx7MMb0kyB860/jT1xrvjO+3LFGWZZmymhcps3kB4C3vL9E+XzsT7UzLImU0LVEGjqVb5gFscVy0cV9a0wKlmucFXjUvUopZ2ed2MpCE/cRwmOYpqWGREoGERmwbcaxxnuJxPN60II7FNywIxKFtNK1Qaq19ZG92608iDtYO5WSBRNa1ZcpgQEQGxDCYNO/vBQTJHZAGpEIckw1HcpMiIBmEkkxLfiSCEBPUQrwpgDiIMjYuUYxpmWLNAO5NLx/PicismZrLsLhE9DMF+TABwN4wolpIw4ikgmTKjgIwCir55xKAyPsEGCGAkVA1MRuRWe9wZzSv+CzyNIRdAC3SwUiFfVJgHy3yDFVAcPS1SAcjOPpxIM8CYnkLEXEQEFcz5Y7IUgXARsLjKnGArbOb6DPYOikgqUWeoWUhLdKM4MirApi8H75RSKix7yxAi6wWhIAw0sF4UQFK9BeUyAcJ4DxIqMYI7K+3uzObXX7iDBH5XZIX3od9tEgzBHGAq08S9lXyuxGgkg8XwO0Y3B9fBQH7ICDDJ2C3CasiDYme6kteLfIMvwAQEsCx5xPA20VBWkWMELAMAWyhBghAFRKR1yC5HRQBin20yDMCApg0C9i9hQICAuQDgIAKhxtVaNK9F+S1CD4LPFmlgdTufL/w3LVfbW8nwCgEzLyoALXmaxNXoQgAOPLPIUAAbVWA6vtQAZwDYgSmnluAv2Q28RJh+wROMgeIP5cAgAUYcSycPCewP4l3OwK7tQ1DjbwgzFuI1CKrBZW4gkDV0RYwqQhI36UAts12FUfF/1uACiEgYwcBXGlEtQmxzdOkVTB5tR1sGy2ywQi3DkOLdDB8AqZ2ELCMycoF8iwCiauuebD/qtlFqajHDEGaI++LfrKPeDLO8Qgkm+boVZBKQvVIwHGxkGNhPtJCBKCQ5y28j+MxAFecuIYlHzAL7yRA+B1E0zFJHb3uoqNtynkeAbFo45nX5KTqmfv0w+ojKpx0UXLDrCiZwXVeRB4l9JOBVbLefUgDdx/RG/UOsfYPnH+WAJDEvTF4L/hL3yIN4l6+/6jJIcizMDGRZWAtxETTEWWOdBomNcUu8/Rep5Mey0SbRPRp7xyi5xTR57qeXDdLI2uPCaepY85FUUUDlFg/I84FC0isX6DjtmXy4rot4LXLt8hYOY3VpM8+PgEKEGGQM6LN5IWIeid9YV0Uz9nEX0eqhshYNYXzeNkRAuocbl7PpGEU0ppgB0EeD4fyCxNruE35tcy4yFDSBxs4QRJvULUzNIyICAFOF0m5PaQrGaSEBuQIHpwMCFJ1c/Q1CPDPAxws7SF98QAZq+/4vK++dYE87BYHESwgxjwHu6CMVjvo84EFkmUvbeFhh8ut4DFEUTXTEIAyml7rcLM1uDSqEH6uddD4+gYJhsD61hal5neQvnQIr34zlIQOhlYf+gXoLnSRruA2RV8aJIwq7aufpfRGPKTGTl/1z4lOPMCB4i4I6KOokn5KqR7DtTOUiWuT6+cFcUY8bJTR6KDfWpy0t2qE/tXH92ME8bAjLOCSjfZcGaPYKyPuiFQI4MkoFTf6yaODd6/N0hMZj8RNMv7y4s/HljGKzMPDy2yUUDFOw34By6Q/30VSwS26MOKkex6Z3F6ZTlqn5JjSH+ir27O4DhEUAjrJUNhLHzQN0+qml9bRwa3V+5RZOYJknaFUiM8bWxV98PXr2I6sPRAC0KQ3Lw+QoXgQAoZJX2LVFpCAhDw94hKPnFt/IPc55mXCEJqmFkm62CmISgU3aWjlR3QrU+fsMkWfa6Fsq528uI5RPmz3Rp+7Lu+52EvHeibFdTwC+ws76L2afnI/AT0Exu5+KB8qaJP1Rf1kuDJEZ8dWxHUcuU3gsdcr9jlQqoCoYivpcG2IABF5QCQhhn3kHicoiFinvMeuDcgIKN19vEnJBbBAQQ9F5naSzXWfA0NdM4t0vGcMyc53EJlGZ7zRZ8xe6WIHRV7spv90T4jrtkDoT9W95EI/TMZ1/0f5SH6rR3+xiyIL+yi1tJ/WNjhTvDSwuCof/G/lVkZ29Vbr9Dx3GyRgkKTwEVAFcPK+Y3HAPmwamf5Y2e19M9dCG2wjJtAE5XndpDvfTkOude6X7m8wITwDD+hyLHhjTpm8ugsdZMB1Ul4PHWMBOIf/aH1DifzKoyf0O5CXzt/AaPaQVHSLPjSNKpbFk/9ce1OWTlso8vQ1+mfrkDiuCtAjz0IF8MTE0cfCK7HBSdlDKFu4YxOkznUOyd/esNH9TcQXHVSMz5H+QjtJOTdo2HVPYeX78UAX2ablyJxWIdKQ30sScKxLGQH/D/c43A8oLbcdOYXrim+LxP779SnRHY/B0bJ20uXewEi30+cdd8RtqgAJ1U4qH0YyswCU0RSUUfY/z5BJSCKb+yFGUYzaU7/5xxuUgI6lnOs0vBwQ8ACjwKI9wD/aRklf2ENRhUquHOtWCPCIPsQIsJWYZOvsKkXhfNQlK2zRTx9fGxfHeb54pwLFIh8lN6+XvujkHFIEHLliJQmFgUdACEiBgGSenCCAZ8K3mh2wC5OXaXzprrdzet7TMb3gsc6tIA0Uj79fP0i6nDayCQEyTbrW5H0ny7Ym7q7jTpnWUF3erkC1KLqJfMEIqAJA4PdFTVv5N0fZjejLS9+hQhlAngW8c7WPNvgp6OWbW1MkIS+kgn46dtMu4sTC3kQZVQQgSEJArd0tpn/YJ75+jk7YlElnCx0dPGP26I5XyhKQ8F29d+0Jz8lEpaNOMpxtoUEIYEkd9gV5z7d19HbhNe/DTXgcKifcP1JKaS9s1OXPAa7j+842yTHf18jWxRXWQI/wnA8tmJiK+siIwjDkWhMjeW/TQ6dQ1U72zdLqI8xH6IAFvHEVFajUBgFjsBIEvFrjcCci+rywiqudxprlEXOkkZV78p7sGvr1KQvtQTL9KtuMMqpMKAsPNsh45poQwKW2bXaJ9KfMpMu20Il2m5dtxOSq7asUndtLxzkHcIAtcCDvOhlON9ORglavSGiInQfBQ1d6hNh3r/TK95BvXDp5xGTctIX5iIsp93u4oh8jgAQun8CkOsgCZhQBsM/euikqGJjyFg9MeP5ad9OrQ4XQ53aRgcscqs77Vb1ykXXSkzcw6dmfd0P+un3YW2S94/mybcgbhZzQc0U53SSf7B7xXMJ1BcDh8pvyH6r75IKBO558XJuc3ylK6x4Ugg9rur3FuKbIOuX5pGVYjobn9+R10eGCZk9J/4Snd8blvYrzH1V0eopwfyHa6aWw1eWxYAEOWMj3RRjrm1isM3TnWlEmQQYPYg9HFcLLXFEuYBI710bS2TYyMGG0BXLacR4Jh4SMhGAujXq+5hy2EC/BGhKukc53wNPoB8mtB6IwexvOtWOL80WwGy8xYCUpF8/F9bqTzbLhdIvvOddxXTdyhf0P+6gCkqodbl4RipUh1iOxdQ4ylo+L5NEjicQMiQQzFN0WFUWX302RIKsTwHyALR/n83ydHlsdCwFRHSch9wHouCJha8BCzoA6rkcp5L6lQvQL6EtwHMlpgL/1l/g4RBbwvXwfAghxhhL4//II6Zjf1Tt8vTsisdoOAYFXPv4Gn9CAlSAWayhTYuXI4AdzpeCaLUgIoHpcwnEug6WDAYCcAYgqsQE/iGOiLyZ9GRNhmQ9oS1hX6QDDZZRFkNNzdbk8inYopCsgLcDRHyd9BQSUQUAS1tQJsI8qIBjGOqdIGAkkWL0gBiIKuA1ygmQo9BzFcDBxkNQzySBwOZTKgwmGgqOtQro6IWBA9PUVkwEB7H8tAQLICz1EcESVIeaIMnFt8gYghDiiKyDIKwKkYAgBiKoGeYEwARx5Ju8XkLyTAMCIty8DD7mPlBZxRjh5cY+IPCMo6oDwMrfZElrEVfjJB4iHCAjPAUbgFS8AI146+MHhAjjpGE8LCCUdTpyhLwN5lERN4oAg7o98KPkQAeo/pG0nIN48T3F1sBMeHC5AFaW2BUT0txOAZBUCQkkHI9j3OwrQJB0E9VuNERUqumyEohFhRihpIMwuKoIjL5J2F4mrZRuByiky4MU+uszmjkjAi3GwAIWo8nUgFPx9RkE8EjsKQ294UQHC99rW2ck2AhCgDwjgEQiNsraAoM98EByDnIjiqvLCAkKJq9jJNgLBAuIr7G7lQxIT931Q2iWi6+ZBCGR4rmCUhdb5ENLCNtsTV8kHl8pQgHiFHQKmIWAaAoYDApgQR3i7j6laMNY78YINYiAvITe0BajkdxP57cj7UAkRVXYIGIWFKibmOPoxjfzNMfDdcdeA9aKR2KJec2V5SsAosLvIP5s8A6SFALQrAVgo5pLVGRF9sTc3FgKYPP/jmSbJnYCciMVkJ10FEV+F4TWNUmmeTV7Ab5tgsloICJCqsV/lwOr0xvmIX/zt2M+ji/sn+csv/6OBMgrPCb6PRcBO+gpE0i9gB/IMLIt3K0ACDLBOFPwfe773zi8/Ov4z8X+svPLBNy8bjtd8GZXbbonJ7+18EURf7OrRn2m16k+1WA3Zptv6E3WD0vfm29KJepv03dPQAZHfY3uywSadbNwWOgGTLTK72Rp9utUifVP571c++OzliIiIiP8B4VfIttF+iOkAAAAASUVORK5CYII=
// #endregion Info
// #region Matches/Includes/Excludes
// @include /^.*$/
// #endregion Matches/Includes/Excludes
// #region Grants
// @grant GM_addElement
// @grant GM_addStyle
// @grant GM_addValueChangeListener
// @grant GM_deleteValue
// @grant GM_download
// @grant GM_getResourceText
// @grant GM_getResourceURL
// @grant GM_getTab
// @grant GM_getTabs
// @grant GM_getValue
// @grant GM_listValues
// @grant GM_log
// @grant GM_notification
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// @grant GM_removeValueChangeListener
// @grant GM_saveTab
// @grant GM_setClipboard
// @grant GM_setValue
// @grant GM_unregisterMenuCommand
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @grant window.close
// @grant window.focus
// @grant window.onurlchange
// #endregion Grants
// #region Resources
// #endregion Resources
// #region Requires
// @require https://code.jquery.com/jquery-3.2.1.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js
// @require https://openuserjs.org/src/libs/93Akkord/loglevel.js
// @require https://openuserjs.org/src/libs/93Akkord/akkd-common.js
// @require https://openuserjs.org/src/libs/sizzle/GM_config.min.js
// #endregion Requires
// #region Other
// noframes
// @connect *
// #endregion Other
// ==/UserScript==
// ==OpenUserJS==
// @author 93Akkord
// ==/OpenUserJS==
// #endregion UserScript Metadata
// #region Type References
/// <reference path='./node_modules/@types/tampermonkey/index.d.ts' />
/// <reference path='./node_modules/@types/jquery/index.d.ts' />
/// <reference path='./node_modules/@types/arrive/index.d.ts' />
// #endregion Type References
const logger = getLogger('akkd', { logLevel: log.levels.DEBUG });
function setupConfig(logger) {
// demo: http://sizzlemctwizzle.github.io/GM_config/
GM_config.init({
id: `main-${location.host.replace(/\./g, '_')}`,
title: 'Akkd All Sites Config',
fields: {
// test: https://www.codingwithjesse.com/demo/2007-05-16-detect-browser-window-focus/
always_focus: {
label: 'Always Focus',
type: 'checkbox',
default: false,
},
},
events: {
init: function () {
init('loaded', () => alwaysOnFocus(GM_config.get('always_focus')));
},
open: function () {
alwaysOnFocus(true);
},
save: function () {},
close: function () {
alwaysOnFocus(GM_config.get('always_focus'));
},
reset: function () {},
},
});
}
function registryConfigMenu() {
let menuId = GM_registerMenuCommand(`Config`, () => {
GM_config.open();
});
}
function exposeGlobalVariables() {
let variables = [
// libs
{ name: 'jQuery', value: jQuery },
{ name: '$', value: $ },
// functions/variables
{ name: 'pp', value: pp },
{ name: 'pformat', value: pformat },
{ name: 'getObjProps', value: getObjProps },
{ name: 'getUserDefinedGlobalProps', value: getUserDefinedGlobalProps },
{ name: 'getLocalStorageSize', value: getLocalStorageSize },
{ name: 'unsafeWindow', value: unsafeWindow },
{ name: 'getWindow', value: getWindow },
{ name: 'getTopWindow', value: getTopWindow },
{ name: 'getStyle', value: getStyle },
{ name: 'GM_info', value: GM_info },
{ name: 'alwaysOnFocus', value: alwaysOnFocus },
];
GM_info.script.grant.forEach((grant) => {
if (grant.includes('GM_')) {
variables.push({
name: grant,
value: window[grant],
});
}
});
variables.forEach((variable, index, variables) => {
try {
setupWindowProps(getWindow(), variable.name, variable.value);
} catch (error) {
logger.error(`Unable to expose variable ${variable.name} into the global scope.`);
}
});
}
function startPerformanceMonitor() {
// if (getWindow().top != getWindow().self) {
// setTimeout(() => {
let _window = 'unsafeWindow' in window ? getWindow() : window;
class Stats {
constructor({
//
containerId = 'performance-monitor-container',
includeMem = true,
includeMemOld = true,
includeFps = true,
includeMs = true,
} = {}) {
this.mode = 0;
this.container = document.createElement('div');
this.on = false;
this.changing = false;
this.includeMem = includeMem;
this.includeMemOld = includeMemOld;
this.includeFps = includeFps;
this.includeMs = includeMs;
this.container.id = containerId;
this.container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000';
this.container.style.display = 'none';
this.container.addEventListener('click', (ev) => {
if (!this.me.moving && !this.me.keyPressed) {
ev.preventDefault();
this.showPanel(++this.mode % this.container.children.length);
}
});
this.beginTime = (performance || Date).now();
this.prevTime = this.beginTime;
this.frames = 0;
this.memPanel;
/** @type {Panel} */
this.memPanelOld;
/** @type {Panel} */
this.fpsPanel;
/** @type {Panel} */
this.msPanel;
if (_window.self.performance && _window.self.performance.memory) {
if (this.includeMem) {
this.memPanel = new MemoryStats();
this.container.appendChild(this.memPanel.domElement);
}
if (this.includeMemOld) {
this.memPanelOld = this.addPanel(new Panel('MB', '#f08', '#201'));
}
}
if (this.includeFps) {
this.fpsPanel = this.addPanel(new Panel('FPS', '#0ff', '#002'));
}
if (this.includeMs) {
this.msPanel = this.addPanel(new Panel('MS', '#0f0', '#020'));
}
this.showPanel(0);
this.REVISION = 16;
this.dom = this.container;
this.domElement = this.container;
this.setMode = this.showPanel;
this.me = new MoveableElement(this.container, true);
this.me.init();
}
showPanel(id) {
for (let i = 0; i < this.container.children.length; i++) {
this.container.children[i].style.display = i === id ? 'block' : 'none';
}
this.mode = id;
}
addPanel(panel) {
this.container.appendChild(panel.dom);
return panel;
}
begin() {
this.beginTime = (performance || Date).now();
}
end() {
let time = (performance || Date).now();
this.frames++;
if (this.msPanel) {
this.msPanel.update(time - this.beginTime, 200);
}
if (time >= this.prevTime + 1000) {
if (this.fpsPanel) {
this.fpsPanel.update((this.frames * 1000) / (time - this.prevTime), 100);
}
this.prevTime = time;
this.frames = 0;
if (this.memPanel) {
this.memPanel.update(performance.memory.usedJSHeapSize / 1048576, performance.memory.jsHeapSizeLimit / 1048576);
}
if (this.memPanelOld) {
this.memPanelOld.update(performance.memory.usedJSHeapSize / 1048576, performance.memory.jsHeapSizeLimit / 1048576);
}
}
return time;
}
update() {
this.beginTime = this.end();
}
start(cb) {
if (!this.on) {
this.on = true;
this.showPanel(this.mode);
this.container.style.display = 'block';
this.animate(cb);
}
}
stop() {
this.on = false;
this.container.style.display = 'none';
}
animate(cb) {
let _animate = () => {
this.begin();
if (cb) {
cb();
}
this.end();
if (this.on) {
requestAnimationFrame(_animate);
}
};
requestAnimationFrame(_animate);
}
}
class Panel {
constructor(name, foreground, background) {
this.name = name;
this.foreground = foreground;
this.background = background;
this.min = Infinity;
this.max = 0;
this.PR = Math.round(_window.devicePixelRatio || 1);
this.WIDTH = 80 * this.PR;
this.HEIGHT = 48 * this.PR;
this.TEXT_X = 3 * this.PR;
this.TEXT_Y = 2 * this.PR;
this.GRAPH_X = 3 * this.PR;
this.GRAPH_Y = 15 * this.PR;
this.GRAPH_WIDTH = 74 * this.PR;
this.GRAPH_HEIGHT = 30 * this.PR;
this.canvas = document.createElement('canvas');
this.canvas.width = this.WIDTH;
this.canvas.height = this.HEIGHT;
this.canvas.style.cssText = 'width:80px;height:48px;cursor:pointer';
this.context = this.canvas.getContext('2d');
this.context.font = 'bold ' + 9 * this.PR + 'px Helvetica,Arial,sans-serif';
this.context.textBaseline = 'top';
this.context.fillStyle = this.background;
this.context.fillRect(0, 0, this.WIDTH, this.HEIGHT);
this.context.fillStyle = this.foreground;
this.context.fillText(this.name, this.TEXT_X, this.TEXT_Y);
this.context.fillRect(this.GRAPH_X, this.GRAPH_Y, this.GRAPH_WIDTH, this.GRAPH_HEIGHT);
this.context.fillStyle = this.background;
this.context.globalAlpha = 0.9;
this.context.fillRect(this.GRAPH_X, this.GRAPH_Y, this.GRAPH_WIDTH, this.GRAPH_HEIGHT);
this.dom = this.canvas;
}
update(value, maxValue) {
this.min = Math.min(this.min, value);
this.max = Math.max(this.max, value);
this.context.fillStyle = this.background;
this.context.globalAlpha = 1;
this.context.fillRect(0, 0, this.WIDTH, this.GRAPH_Y);
this.context.fillStyle = this.foreground;
this.context.fillText(Math.round(value) + ' ' + this.name + ' (' + Math.round(this.min) + '-' + Math.round(this.max) + ')', this.TEXT_X, this.TEXT_Y);
this.context.drawImage(this.canvas, this.GRAPH_X + this.PR, this.GRAPH_Y, this.GRAPH_WIDTH - this.PR, this.GRAPH_HEIGHT, this.GRAPH_X, this.GRAPH_Y, this.GRAPH_WIDTH - this.PR, this.GRAPH_HEIGHT);
this.context.fillRect(this.GRAPH_X + this.GRAPH_WIDTH - this.PR, this.GRAPH_Y, this.PR, this.GRAPH_HEIGHT);
this.context.fillStyle = this.background;
this.context.globalAlpha = 0.9;
this.context.fillRect(this.GRAPH_X + this.GRAPH_WIDTH - this.PR, this.GRAPH_Y, this.PR, Math.round((1 - value / maxValue) * this.GRAPH_HEIGHT));
}
}
function MemoryStats() {
let msMin = 100;
let msMax = 0;
let GRAPH_HEIGHT = 30;
let GRAPH_WIDTH = 74;
let redrawMBThreshold = GRAPH_HEIGHT;
let container = document.createElement('div');
container.style.display = 'none';
container.id = 'stats';
container.style.cssText = 'width:80px;height:48px;opacity:0.9;cursor:pointer;overflow:hidden;z-index:10000;will-change:transform;';
let msDiv = document.createElement('div');
msDiv.id = 'ms';
msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;';
container.appendChild(msDiv);
let msText = document.createElement('div');
msText.id = 'msText';
msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
msText.innerHTML = 'Memory';
msDiv.appendChild(msText);
let msGraph = document.createElement('div');
msGraph.id = 'msGraph';
msGraph.style.cssText = 'position:relative;width:74px;height:' + GRAPH_HEIGHT + 'px;background-color:#0f0';
msDiv.appendChild(msGraph);
while (msGraph.children.length < GRAPH_WIDTH) {
let bar = document.createElement('span');
bar.style.cssText = 'width:1px;height:' + GRAPH_HEIGHT + 'px;float:left;background-color:#131';
msGraph.appendChild(bar);
}
let updateGraph = function (dom, height, color) {
let child = dom.appendChild(dom.firstChild);
child.style.height = height + 'px';
if (color) child.style.backgroundColor = color;
};
let redrawGraph = function (dom, oHFactor, hFactor) {
[].forEach.call(dom.children, function (c) {
let cHeight = c.style.height.substring(0, c.style.height.length - 2);
// Convert to MB, change factor
let newVal = GRAPH_HEIGHT - ((GRAPH_HEIGHT - cHeight) / oHFactor) * hFactor;
c.style.height = newVal + 'px';
});
};
// polyfill usedJSHeapSize
if (_window.performance && !performance.memory) {
performance.memory = { usedJSHeapSize: 0, totalJSHeapSize: 0 };
}
// support of the API?
if (performance.memory.totalJSHeapSize === 0) {
logger.warn('totalJSHeapSize === 0... performance.memory is only available in Chrome .');
}
let sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
let precision;
let i;
function bytesToSize(bytes, nFractDigit) {
if (bytes === 0) return 'n/a';
nFractDigit = nFractDigit !== undefined ? nFractDigit : 0;
precision = Math.pow(10, nFractDigit);
i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round((bytes * precision) / Math.pow(1024, i)) / precision + ' ' + sizes[i];
}
// TODO, add a sanity check to see if values are bucketed.
// If so, remind user to adopt the --enable-precise-memory-info flag.
// open -a "/Applications/Google Chrome.app" --args --enable-precise-memory-info
let lastTime = Date.now();
let lastUsedHeap = performance.memory.usedJSHeapSize;
let delta = 0;
let color = '#131';
let ms = 0;
let mbValue = 0;
let factor = 0;
let newThreshold = 0;
return {
domElement: container,
update: function () {
// update at 30fps
if (Date.now() - lastTime < 1000 / 30) return;
lastTime = Date.now();
delta = performance.memory.usedJSHeapSize - lastUsedHeap;
lastUsedHeap = performance.memory.usedJSHeapSize;
// if memory has gone down, consider it a GC and draw a red bar.
color = delta < 0 ? '#830' : '#131';
ms = lastUsedHeap;
msMin = Math.min(msMin, ms);
msMax = Math.max(msMax, ms);
msText.textContent = 'Mem: ' + bytesToSize(ms, 2);
mbValue = ms / (1024 * 1024);
if (mbValue > redrawMBThreshold) {
factor = (mbValue - (mbValue % GRAPH_HEIGHT)) / GRAPH_HEIGHT;
newThreshold = GRAPH_HEIGHT * (factor + 1);
redrawGraph(msGraph, GRAPH_HEIGHT / redrawMBThreshold, GRAPH_HEIGHT / newThreshold);
redrawMBThreshold = newThreshold;
}
updateGraph(msGraph, GRAPH_HEIGHT - mbValue * (GRAPH_HEIGHT / redrawMBThreshold), color);
},
};
}
let stats = new Stats({
includeMemOld: false,
// includeFps: false,
// includeMs: false,
});
function initPerformanceMonitor() {
if (!document.body) {
setTimeout(() => {
initPerformanceMonitor();
}, 250);
} else {
function setupIFrameEvents() {
setTimeout(() => {
let iframes = document.querySelectorAll('iframe');
for (let i = 0; i < iframes.length; i++) {
try {
const iframe = iframes[i];
/** @type {Window} */
let _window = iframe.contentWindow;
_window.document.addEventListener('keydown', function (e) {
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() == 'm') {
e.cancelBubble = true;
e.preventDefault();
e.stopImmediatePropagation();
_window.parent.postMessage('performance-monitor-keybind', '*');
}
});
} catch (error) {}
}
}, 5000);
}
document.body.appendChild(stats.dom);
let changing = false;
function startOrStop() {
if (!changing) {
changing = true;
if (!stats.on) {
stats.start();
} else {
stats.stop();
}
setTimeout(() => {
changing = false;
}, 500);
}
}
document.addEventListener('keydown', function (e) {
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() == 'm') {
e.cancelBubble = true;
e.preventDefault();
e.stopImmediatePropagation();
startOrStop();
}
});
setupIFrameEvents();
/**
*
*
* @author Michael Barros <[email protected]>
* @param {MessageEvent} ev
*/
function messageEvent(ev) {
if (ev.data === 'performance-monitor-keybind' || ev.message === 'performance-monitor-keybind') {
startOrStop();
}
}
_window.removeEventListener('message', messageEvent);
_window.addEventListener('message', messageEvent);
}
}
if (getTopWindow() === getWindow()) {
initPerformanceMonitor();
}
}
function alwaysOnFocusOld() {
let on = GM_getValue('always_focus', false);
let focusMenuCommandID;
/**
*
*
* @author Michael Barros <[email protected]>
* @param {boolean} [init=false]
*/
function registerAlwaysFocusMenuCommand(init = false) {
if (!init) {
on = !on;
GM_setValue('always_focus', on);
}
if (focusMenuCommandID != undefined) {
GM_unregisterMenuCommand(focusMenuCommandID);
}
focusMenuCommandID = GM_registerMenuCommand(`Always Focus: ${on ? 'on' : 'off'}`, () => {
registerAlwaysFocusMenuCommand();
});
_alwaysOnFocus(on);
}
function _alwaysOnFocus(on) {
if (!('originalFocusValues' in getWindow())) {
getWindow().originalFocusValues = {
'unsafeWindow.onblur': unsafeWindow.onblur,
'unsafeWindow.blurred': unsafeWindow.blurred,
'unsafeWindow.document.hasFocus': unsafeWindow.document.hasFocus,
'unsafeWindow.window.onfocus': unsafeWindow.window.onfocus,
'document.hidden': document.hidden,
'document.mozHidden': document.mozHidden,
'document.msHidden': document.msHidden,
'document.webkitHidden': document.webkitHidden,
'document.visibilityState': document.visibilityState,
'unsafeWindow.document.onvisibilitychange': unsafeWindow.document.onvisibilitychange,
};
}
if (!('__eventHandler__' in getWindow())) {
getWindow().__eventHandler__ = function (event) {
event.stopImmediatePropagation();
};
}
function getNestedDot(obj, dotStr) {
let parts = dotStr.split('.');
while (parts.length > 0) {
let part = parts.shift();
obj = obj[part];
}
return obj;
}
if (on) {
unsafeWindow.onblur = null;
unsafeWindow.blurred = false;
unsafeWindow.document.hasFocus = function () {
return true;
};
unsafeWindow.window.onfocus = function () {
return true;
};
Object.defineProperty(document, 'hidden', { value: false, configurable: true });
Object.defineProperty(document, 'mozHidden', { value: false, configurable: true });
Object.defineProperty(document, 'msHidden', { value: false, configurable: true });
Object.defineProperty(document, 'webkitHidden', { value: false, configurable: true });
Object.defineProperty(document, 'visibilityState', {
get: function () {
return 'visible';
},
configurable: true,
});
unsafeWindow.document.onvisibilitychange = undefined;
let events = [
'visibilitychange',
'webkitvisibilitychange',
'blur', // may cause issues on some websites
'mozvisibilitychange',
'msvisibilitychange',
];
for (let i = 0; i < events.length; i++) {
const event = events[i];
window.addEventListener(event, getWindow().__eventHandler__, true);
}
} else {
let orig = getWindow().originalFocusValues;
unsafeWindow.onblur = orig['unsafeWindow.onblur'];
unsafeWindow.blurred = orig['unsafeWindow.blurred'];
unsafeWindow.document.hasFocus = orig['unsafeWindow.document.hasFocus'];
unsafeWindow.window.onfocus = orig['unsafeWindow.window.onfocus'];
// Object.defineProperty(document, 'hidden', { value: orig['document.hidden'] });
// Object.defineProperty(document, 'mozHidden', { value: orig['document.mozHidden'] });
// Object.defineProperty(document, 'msHidden', { value: orig['document.msHidden'] });
// Object.defineProperty(document, 'webkitHidden', { value: orig['document.webkitHidden'] });
document.hidden = orig['document.hidden'];
document.mozHidden = orig['document.mozHidden'];
document.msHidden = orig['document.msHidden'];
document.webkitHidden = orig['document.webkitHidden'];
document.visibilityState = orig['document.visibilityState'];
unsafeWindow.document.onvisibilitychange = orig['unsafeWindow.document.onvisibilitychange'];
let events = [
'visibilitychange',
'webkitvisibilitychange',
'blur', // may cause issues on some websites
'mozvisibilitychange',
'msvisibilitychange',
];
for (let i = 0; i < events.length; i++) {
const event = events[i];
window.removeEventListener(event, getWindow().__eventHandler__, true);
}
}
}
registerAlwaysFocusMenuCommand(true);
}
/**
*
*
* @author Michael Barros <[email protected]>
* @param {boolean} on
*/
function alwaysOnFocus(on) {
if (!('originalFocusValues' in getWindow())) {
getWindow().originalFocusValues = {
'unsafeWindow.onblur': unsafeWindow.onblur,
'unsafeWindow.blurred': unsafeWindow.blurred,
'unsafeWindow.document.hasFocus': unsafeWindow.document.hasFocus,
'unsafeWindow.window.onfocus': unsafeWindow.window.onfocus,
'document.hidden': document.hidden,
'document.mozHidden': document.mozHidden,
'document.msHidden': document.msHidden,
'document.webkitHidden': document.webkitHidden,
'document.visibilityState': document.visibilityState,
'unsafeWindow.document.onvisibilitychange': unsafeWindow.document.onvisibilitychange,
};
}
if (!('__eventHandler__' in getWindow())) {
getWindow().__eventHandler__ = function (event) {
event.stopImmediatePropagation();
};
}
function getNestedDot(obj, dotStr) {
let parts = dotStr.split('.');
while (parts.length > 0) {
let part = parts.shift();
obj = obj[part];
}
return obj;
}
if (on) {
unsafeWindow.onblur = null;
unsafeWindow.blurred = false;
unsafeWindow.document.hasFocus = function () {
return true;
};
unsafeWindow.window.onfocus = function () {
return true;
};
Object.defineProperty(document, 'hidden', { value: false, configurable: true });
Object.defineProperty(document, 'mozHidden', { value: false, configurable: true });
Object.defineProperty(document, 'msHidden', { value: false, configurable: true });
Object.defineProperty(document, 'webkitHidden', { value: false, configurable: true });
Object.defineProperty(document, 'visibilityState', {
get: function () {
return 'visible';
},
configurable: true,
});
unsafeWindow.document.onvisibilitychange = undefined;
let events = [
'visibilitychange',
'webkitvisibilitychange',
'blur', // may cause issues on some websites
'mozvisibilitychange',
'msvisibilitychange',
];
for (let i = 0; i < events.length; i++) {
const event = events[i];
window.addEventListener(event, getWindow().__eventHandler__, true);
}
} else {
let orig = getWindow().originalFocusValues;
unsafeWindow.onblur = orig['unsafeWindow.onblur'];
unsafeWindow.blurred = orig['unsafeWindow.blurred'];
unsafeWindow.document.hasFocus = orig['unsafeWindow.document.hasFocus'];
unsafeWindow.window.onfocus = orig['unsafeWindow.window.onfocus'];
// Object.defineProperty(document, 'hidden', { value: orig['document.hidden'] });
// Object.defineProperty(document, 'mozHidden', { value: orig['document.mozHidden'] });
// Object.defineProperty(document, 'msHidden', { value: orig['document.msHidden'] });
// Object.defineProperty(document, 'webkitHidden', { value: orig['document.webkitHidden'] });
document.hidden = orig['document.hidden'];
document.mozHidden = orig['document.mozHidden'];
document.msHidden = orig['document.msHidden'];
document.webkitHidden = orig['document.webkitHidden'];
document.visibilityState = orig['document.visibilityState'];
unsafeWindow.document.onvisibilitychange = orig['unsafeWindow.document.onvisibilitychange'];
let events = [
'visibilitychange',
'webkitvisibilitychange',
'blur', // may cause issues on some websites
'mozvisibilitychange',
'msvisibilitychange',
];
for (let i = 0; i < events.length; i++) {
const event = events[i];
window.removeEventListener(event, getWindow().__eventHandler__, true);
}
}
}
/**
*
*
* @author Michael Barros <[email protected]>
*/
async function init(when) {
const DEFAULT_OPTIONS = {
use_vanilla: false,
};
let options = typeof arguments[1] == 'object' ? arguments[1] : {};
let func = typeof arguments[1] == 'object' ? arguments[2] : arguments[1];
let args = typeof arguments[1] == 'object' ? arguments[3] : arguments[2];
options = Object.assign(DEFAULT_OPTIONS, options);
async function runCallback() {
if (args && args.length > 0) {
await func(...args);
} else {
await func();
}
}
if (when == 'start') {
await runCallback();
} else if (when == 'ready') {
if (!options.use_vanilla) {
$(document).ready(async (e) => {
await runCallback();
});
} else {
document.addEventListener('DOMContentLoaded', async (e) => {
await runCallback();
});
}
} else if (when == 'loaded') {
if (!options.use_vanilla) {
$(document).on('readystatechange', async (e) => {
if (e.target.readyState == 'complete') {
await runCallback();
}
});
} else {
document.addEventListener('readystatechange', async (e) => {
if (e.target.readyState === 'complete') {
await runCallback();
}
});
}
}
}
(async function () {
setupConfig(logger);
registryConfigMenu();
GM_getTab((tab) => {
tab.title = document.title;
GM_saveTab(tab);
});
exposeGlobalVariables();
startPerformanceMonitor();
})();