Background Network Requests Indicator

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

目前为 2020-05-06 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Background Network Requests Indicator
  3. // @namespace BackgroundNetworkRequestsIndicator
  4. // @version 1.0.9
  5. // @license AGPL v3
  6. // @author jcunews
  7. // @description Shows an indicator at bottom right/left when there is one or more background network requests in progress.
  8. // @website https://greasyfork.org/en/users/85671-jcunews
  9. // @include *://*/*
  10. // @grant none
  11. // @run-at document-start
  12. // ==/UserScript==
  13.  
  14. /*
  15. The number on the indicator shows the number of background network requests in progress.
  16.  
  17. When it shows, by default it will be placed at bottom-right. When the mouse cursor is
  18. moved to the right half area of the page, the indicator will move itself to the bottom-left.
  19.  
  20. If the SHIFT key is held down, the indicator will stay. And when the mouse cursor is on it,
  21. a list of pending network request URLs will be shown.
  22. */
  23.  
  24. ((eleContainer, eleStyle, eleList, eleIndicator, xhrId, xhrCount, xhrAbort, xhrOpen, xhrSend, ffetch, shiftPressed) => {
  25.  
  26. if (!(document instanceof HTMLDocument)) return;
  27.  
  28. (eleContainer = document.createElement("DIV")).id = "bnriContainer";
  29. eleContainer.innerHTML = `<style>
  30. #bnriContainer, #bnriList, #bnriList>.url, #bnriIndicator {
  31. display:block!important; opacity:1!important; visibility:visible!important;
  32. position:static!important; float:none!important; margin:0!important;
  33. box-sizing:content-box!important; border:none!important; padding:0!important;
  34. width:auto!important; min-width:0!important; max-width:none!important;
  35. height:auto!important; min-height:0!important; max-height:none!important;
  36. background:transparent!important; font:10pt/normal sans-serif!important;
  37. }
  38. #bnriContainer {
  39. position:fixed!important; z-index:9999999999!important; left:auto!important;
  40. top:auto!important; right:0!important; bottom:.5em!important;
  41. }
  42. #bnriContainer.left, #bnriContainer.left #bnriList {
  43. left:0!important; right:auto!important;
  44. }
  45. #bnriList {
  46. display:none!important; position:fixed!important; left:auto!important; top:auto!important;
  47. right:0!important; bottom:1.7em!important; border:1px solid #555!important;
  48. background-color:#ddd!important;
  49. }
  50. #bnriContainer:hover>#bnriList {
  51. display:block!important;
  52. }
  53. #bnriList>.url {
  54. max-width:90vw!important; max-height:50vw!important;
  55. overflow-x:hidden!important; overflow-y:auto!important;
  56. padding:0 .2em!important; line-height:1.5em!important;
  57. white-space: nowrap!important; text-overflow:ellipsis!important;
  58. }
  59. #bnriList>.url:nth-child(2n) {
  60. background-color:#ccc!important;
  61. }
  62. #bnriIndicator {
  63. border:1mm solid #bb0!important; border-radius:2em!important;
  64. padding:0 1mm!important; background-color:#ff0!important; text-align:center!important;
  65. cursor:default!important;
  66. }
  67. </style>
  68. <div id="bnriList"></div>
  69. <div id="bnriIndicator"></div>
  70. `;
  71. eleList = eleContainer.querySelector("#bnriList");
  72. eleIndicator = eleContainer.querySelector("#bnriIndicator");
  73.  
  74. xhrId = xhrCount = 0;
  75.  
  76. function checkCursor(ev) {
  77. if (!shiftPressed) {
  78. if (ev.clientX >= Math.floor(innerWidth / 2)) {
  79. eleContainer.className = "left";
  80. } else eleContainer.className = "";
  81. }
  82. }
  83.  
  84. function doneRequest(xhr) {
  85. if (--xhrCount < 0) xhrCount = 0;
  86. delete xhr.id_bnri;
  87. if (xhr.ele_bnri) xhr.ele_bnri.parentNode.removeChild(xhr.ele_bnri); //ignorant Metodize library broke Element.prototype.remove()
  88. if (xhrCount) {
  89. eleIndicator.textContent = xhrCount;
  90. } else if (eleContainer.parentNode) {
  91. removeEventListener("mousemove", checkCursor);
  92. document.body.removeChild(eleContainer);
  93. setTimeout(() => { //workaround when element isn't removed somehow
  94. if (!xhrCount && eleContainer.parentNode) document.body.removeChild(eleContainer);
  95. }, 0);
  96. }
  97. }
  98.  
  99. function checkState() {
  100. if ((this.readyState >= XMLHttpRequest.HEADERS_RECEIVED) && !eleContainer.parentNode && document.body) {
  101. document.body.appendChild(eleContainer);
  102. addEventListener("mousemove", checkCursor);
  103. }
  104. if ((this.readyState !== XMLHttpRequest.DONE) || !this.id_bnri) return;
  105. doneRequest(this);
  106. }
  107.  
  108. function showList() {
  109. }
  110.  
  111. xhrAbort = XMLHttpRequest.prototype.abort;
  112. XMLHttpRequest.prototype.abort = function() {
  113. doneRequest(this);
  114. return xhrAbort.apply(this, arguments);
  115. };
  116.  
  117. xhrOpen = XMLHttpRequest.prototype.open;
  118. XMLHttpRequest.prototype.open = function() {
  119. if (!this.url_bnri) this.addEventListener("readystatechange", checkState);
  120. this.url_bnri = arguments[1];
  121. return xhrOpen.apply(this, arguments);
  122. };
  123.  
  124. xhrSend = XMLHttpRequest.prototype.send;
  125. XMLHttpRequest.prototype.send = function() {
  126. if (!this.id_bnri) {
  127. this.id_bnri = ++xhrId;
  128. (this.ele_bnri = eleList.appendChild(document.createElement("DIV"))).className = "url";
  129. this.ele_bnri.textContent = this.url_bnri;
  130. }
  131. eleIndicator.textContent = ++xhrCount;
  132. if (!eleContainer.parentNode && document.body) {
  133. document.body.appendChild(eleContainer);
  134. addEventListener("mousemove", checkCursor);
  135. }
  136. return xhrSend.apply(this, arguments);
  137. };
  138.  
  139. if (window.fetch) {
  140. ffetch = window.fetch;
  141. window.fetch = function(urlOpts) {
  142. var context = {urlOpts: urlOpts, id_bnri: ++xhrId, ele_bnri: eleList.appendChild(document.createElement("DIV"))};
  143. context.ele_bnri.className = "url";
  144. context.ele_bnri.textContent = urlOpts.url || urlOpts;
  145. eleIndicator.textContent = ++xhrCount;
  146. if (!eleContainer.parentNode && document.body) {
  147. document.body.appendChild(eleContainer);
  148. addEventListener("mousemove", checkCursor);
  149. }
  150. var res = ffetch.apply(this, arguments).finally(function() {
  151. doneRequest(context);
  152. });
  153. return res;
  154. };
  155. }
  156.  
  157. addEventListener("keydown", e => {
  158. if (e.key === "Shift") shiftPressed = true;
  159. });
  160.  
  161. addEventListener("keyup", e => {
  162. if (e.key === "Shift") shiftPressed = false;
  163. });
  164.  
  165. })();