Background Network Requests Indicator

Shows an indicator at bottom right/left when there is one or more background network requests in progress.

Verzia zo dňa 08.12.2021. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name        Background Network Requests Indicator
// @namespace   BackgroundNetworkRequestsIndicator
// @version     1.0.12
// @license     AGPL v3
// @author      jcunews
// @description Shows an indicator at bottom right/left when there is one or more background network requests in progress.
// @website     https://greasyfork.org/en/users/85671-jcunews
// @include     *://*/*
// @grant       none
// @run-at      document-start
// ==/UserScript==

/*
The number on the indicator shows the number of background network requests in progress.

When it shows, by default it will be placed at bottom-right. When the mouse cursor is
moved to the right half area of the page, the indicator will move itself to the bottom-left.

If the SHIFT key is held down, the indicator will stay. And when the mouse cursor is on it,
a list of pending network request URLs will be shown.
*/

((eleContainer, eleStyle, eleList, eleIndicator, xhrId, xhrCount, xhrAbort, xhrOpen, xhrSend, ffetch, shiftPressed) => {

  if (!(document instanceof HTMLDocument)) return;

  (eleContainer = document.createElement("DIV")).id = "bnriContainer";
  eleContainer.innerHTML = `<style>
#bnriContainer, #bnriList, #bnriList>.url, #bnriIndicator {
  display:block!important; opacity:1!important; visibility:visible!important;
  position:static!important; float:none!important; margin:0!important;
  box-sizing:content-box!important; border:none!important; padding:0!important;
  width:auto!important; min-width:0!important; max-width:none!important;
  height:auto!important; min-height:0!important; max-height:none!important;
  background:transparent!important; font:10pt/normal sans-serif!important;
}
#bnriContainer {
  position:fixed!important; z-index:9999999999!important; left:auto!important;
  top:auto!important; right:0!important; bottom:.5em!important;
}
#bnriContainer.left, #bnriContainer.left #bnriList {
  left:0!important; right:auto!important;
}
#bnriList {
  display:none!important; position:fixed!important; left:auto!important; top:auto!important;
  right:0!important; bottom:1.7em!important; border:1px solid #555!important;
  max-height:50vw!important; overflow-x:hidden!important; overflow-y:auto!important; background-color:#ddd!important;
}
#bnriContainer:hover>#bnriList {
  display:block!important;
}
#bnriList>.url {
  max-width:90vw!important; padding:0 .2em!important; line-height:1.5em!important;
  white-space: nowrap!important; text-overflow:ellipsis!important; color: #000!important;
}
#bnriList>.url:nth-child(2n) {
  background-color:#ccc!important;
}
#bnriIndicator {
  border:1mm solid #bb0!important; border-radius:2em!important;
  padding:0 1mm!important; background-color:#ff0!important; text-align:center!important;
  color:#000!important; cursor:default!important;
}
</style>
<div id="bnriList"></div>
<div id="bnriIndicator"></div>
`;
  eleList = eleContainer.querySelector("#bnriList");
  eleIndicator = eleContainer.querySelector("#bnriIndicator");

  xhrId = xhrCount = 0;

  function checkCursor(ev) {
    if (!shiftPressed) {
      if (ev.clientX >= Math.floor(innerWidth / 2)) {
        eleContainer.className = "left";
      } else eleContainer.className = "";
    }
  }

  function doneRequest(xhr) {
    if (--xhrCount < 0) xhrCount = 0;
    delete xhr.id_bnri;
    if (xhr.ele_bnri && xhr.ele_bnri.parentNode) xhr.ele_bnri.parentNode.removeChild(xhr.ele_bnri); //ignorant Metodize library broke Element.prototype.remove()
    if (xhrCount) {
      eleIndicator.textContent = xhrCount;
    } else if (eleContainer.parentNode) {
      removeEventListener("mousemove", checkCursor);
      document.body.removeChild(eleContainer);
      setTimeout(() => { //workaround when element isn't removed somehow
        if (!xhrCount && eleContainer.parentNode) document.body.removeChild(eleContainer);
      }, 0);
    }
  }

  function checkState() {
    if ((this.readyState >= XMLHttpRequest.HEADERS_RECEIVED) && !eleContainer.parentNode && document.body) {
      document.body.appendChild(eleContainer);
      addEventListener("mousemove", checkCursor);
    }
    if ((this.readyState !== XMLHttpRequest.DONE) || !this.id_bnri) return;
    doneRequest(this);
  }

  function showList() {
  }

  xhrAbort = XMLHttpRequest.prototype.abort;
  XMLHttpRequest.prototype.abort = function() {
    doneRequest(this);
    return xhrAbort.apply(this, arguments);
  };

  xhrOpen = XMLHttpRequest.prototype.open;
  XMLHttpRequest.prototype.open = function() {
    if (!this.url_bnri) this.addEventListener("readystatechange", checkState);
    this.url_bnri = arguments[1];
    return xhrOpen.apply(this, arguments);
  };

  xhrSend = XMLHttpRequest.prototype.send;
  XMLHttpRequest.prototype.send = function() {
    if (!this.id_bnri) {
      this.id_bnri = ++xhrId;
      (this.ele_bnri = eleList.appendChild(document.createElement("DIV"))).className = "url";
      this.ele_bnri.textContent = this.url_bnri;
    }
    eleIndicator.textContent = ++xhrCount;
    if (!eleContainer.parentNode && document.body) {
      document.body.appendChild(eleContainer);
      addEventListener("mousemove", checkCursor);
    }
    return xhrSend.apply(this, arguments);
  };

  if (window.fetch) {
    ffetch = window.fetch;
    window.fetch = function(urlOpts) {
      var context = {urlOpts: urlOpts, id_bnri: ++xhrId, ele_bnri: eleList.appendChild(document.createElement("DIV"))};
      context.ele_bnri.className = "url";
      context.ele_bnri.textContent = urlOpts.url || urlOpts;
      eleIndicator.textContent = ++xhrCount;
      if (!eleContainer.parentNode && document.body) {
        document.body.appendChild(eleContainer);
        addEventListener("mousemove", checkCursor);
      }
      var res = ffetch.apply(this, arguments).finally(function() {
        doneRequest(context);
      });
      return res;
    };
  }

  addEventListener("keydown", e => {
    if (e.key === "Shift") shiftPressed = true;
  });

  addEventListener("keyup", e => {
    if (e.key === "Shift") shiftPressed = false;
  });

})();