Website Analyser

Outputs capturable elements to console.log.

  1. // ==UserScript==
  2. // @name Website Analyser
  3. // @name:en Website Analyser
  4. // @name:ja サイト分析
  5. // @namespace http://tampermonkey.net/
  6. // @version 2025-05-09
  7. // @description  Outputs capturable elements to console.log.
  8. // @description:en Outputs capturable elements to console.log
  9. // @description:ja キャプチャ可能な要素をconsole.logに出力する
  10. // @author ぐらんぴ
  11. // @match http*://*/*
  12. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  13. // @grant GM_registerMenuCommand
  14. // @grant GM_getValue
  15. // @grant GM_setValue
  16. // @run-at document-start
  17. // @license MIT
  18. // ==/UserScript==
  19.  
  20. //const log = console.log;
  21. let settings = {
  22. querySelector: GM_getValue('querySelector', false),
  23. querySelectorAll: GM_getValue('querySelectorAll', false),
  24. append: GM_getValue('append', false),
  25. appendChild: GM_getValue('appendChild', false),
  26. defineProperty: GM_getValue('defineProperty', false),
  27. event: GM_getValue('event', false),
  28. fetch: GM_getValue('fetch', false),
  29. webSocket: GM_getValue('webSocket', false),
  30. XMLHttpRequest: GM_getValue('XMLHttpRequest', false),
  31. ShadowDOM: GM_getValue('ShadowDOM', false),
  32. before: GM_getValue('before', false),
  33. after: GM_getValue('after', false),
  34. insertBefore: GM_getValue('insertBefore', false),
  35. dispatchEvent: GM_getValue('dispatchEvent', false),
  36. setInterval: GM_getValue('setInterval', false),
  37. setTimeout: GM_getValue('setTimeout', false),
  38. requestIdleCallback: GM_getValue('requestIdleCallback', false),
  39. };
  40.  
  41. const toggleSetting = (key) => {
  42. settings[key] = !settings[key];
  43. GM_setValue(key, settings[key]);
  44. document.getElementById(key).checked = settings[key]; // チェックボックスの状態を同期
  45. console.log(`${key} is now ${settings[key]}`);
  46. };
  47.  
  48. const analyzer = () => {
  49. if(settings.defineProperty){
  50. const origObjDefineProperty = Object.defineProperty;
  51. Object.defineProperty = (...args)=>{
  52. console.log("defineProperty:", args);
  53. return origObjDefineProperty(...args);
  54. };
  55. }
  56. if(settings.fetch){
  57. const origFetch = unsafeWindow.fetch;
  58. unsafeWindow.fetch = async (...args) => {
  59. console.log("fetch:", args);
  60. const res = await origFetch(...args);
  61. return res;
  62. };
  63.  
  64. }
  65. if(settings.webSocket){
  66. const origWb = unsafeWindow.WebSocket;
  67. unsafeWindow.WebSocket = function (...args) {
  68. const ws = new origWb(...args);
  69. ws.addEventListener("message", e => {
  70. const data = JSON.parse(e.data);
  71. console.log("webSocket:", data);
  72. });
  73. return ws;
  74. };
  75. }
  76. if(settings.event){
  77. const origAddEventListener = EventTarget.prototype.addEventListener;
  78. EventTarget.prototype.addEventListener = function(type, listener, options) {
  79. console.log("event:", type);
  80. return origAddEventListener.call(this, type, listener, options);
  81. };
  82. }
  83. if(settings.appendChild){
  84. const origAppendChild = Element.prototype.appendChild;
  85. Element.prototype.appendChild = function(...args) {
  86. console.log("appendChild:", args);
  87. return origAppendChild.apply(this, args);
  88. };
  89. }
  90. if(settings.querySelector){
  91. const originalQuerySelector = Document.prototype.querySelector;
  92. Object.defineProperty(Document.prototype, 'querySelector', {
  93. value: function(...args) {
  94. console.log("selector:", ...args);
  95. return originalQuerySelector.apply(this, args);
  96. },
  97. writable: true,
  98. configurable: true
  99. });
  100. }
  101. if(settings.querySelectorAll){
  102. const originalQuerySelectorAll = Document.prototype.querySelectorAll;
  103. Object.defineProperty(Document.prototype, 'querySelectorAll', {
  104. value: function(...args) {
  105. console.log("selectorAll:", ...args);
  106. return originalQuerySelectorAll.apply(this, args);
  107. },
  108. writable: true,
  109. configurable: true
  110. });
  111. }
  112. if(settings.XMLHttpRequest){
  113. const originalOpen = XMLHttpRequest.prototype.open;
  114. const originalSend = XMLHttpRequest.prototype.send;
  115.  
  116. XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
  117. this._requestInfo = { method, url, async, user, password };
  118. console.log('XHR Request:', { method, url, async, user });
  119. return originalOpen.apply(this, arguments);
  120. };
  121.  
  122. XMLHttpRequest.prototype.send = function(body) {
  123. this.addEventListener('load', function() {
  124. console.log('XHR Response:', {
  125. url: this._requestInfo.url,
  126. status: this.status,
  127. statusText: this.statusText,
  128. response: this.responseText.substring(0, 200) // untill 200
  129. });
  130. });
  131. console.log('XHR Send:', {
  132. url: this._requestInfo.url,
  133. body: body ? body.substring(0, 200) : null
  134. });
  135. return originalSend.apply(this, arguments);
  136. };
  137. }
  138. if(settings.ShadowDOM){
  139. const originalAttachShadow = Element.prototype.attachShadow;
  140. Element.prototype.attachShadow = function(...args) {
  141. console.log('Shadow DOM:', this,/* 'Args:', args*/);
  142.  
  143. const shadowRoot = originalAttachShadow.apply(this, args);
  144.  
  145. console.log('ShadowRoot:', shadowRoot);
  146.  
  147. return shadowRoot;
  148. };
  149. }
  150. if(settings.before){
  151. const origBefore = Element.prototype.before;
  152. Element.prototype.before = function(...args) {
  153. setTimeout(() => {
  154. console.log('Before:', args)
  155. },0)
  156. return origBefore.apply(this, args);
  157. };
  158. }
  159. if(settings.after){
  160. const origAfter = Element.prototype.after;
  161. Element.prototype.after = function(...args) {
  162. setTimeout(() => {
  163. console.log('After:', args)
  164. },0)
  165. return origAfter.apply(this, args);
  166. };
  167. }
  168. if(settings.insertBefore){
  169. const origInsertBefore = Node.prototype.insertBefore;
  170. Node.prototype.insertBefore = function(newNode, referenceNode) {
  171. console.log('newNode:', newNode);
  172. console.log('referenceNode:', referenceNode);
  173. return origInsertBefore.apply(this, [newNode, referenceNode]);
  174. };
  175. }
  176. if(settings.dispatchEvent){
  177. const originalDispatchEvent = EventTarget.prototype.dispatchEvent;
  178. EventTarget.prototype.dispatchEvent = function(event) {
  179. console.log('DispatchEvent:', {
  180. target: this,
  181. event: {
  182. type: event.type,
  183. eventObject: event,
  184. }
  185. });
  186. return originalDispatchEvent.apply(this, arguments);
  187. };
  188. }
  189. if(settings.remove){
  190. const originalRemove = Element.prototype.remove;
  191. Element.prototype.remove = function() {
  192. console.log('Removed:', this);
  193. return originalRemove.apply(this);
  194. };
  195. }
  196. if(settings.setInterval){
  197. const originalSetInterval = unsafeWindow.setInterval;
  198. unsafeWindow.setInterval = function(callback, delay, ...args) {
  199. if (callback.toString() !== "function () { [native code] }") {
  200. console.log('setInterval:', {
  201. function: callback.toString(),
  202. callback: callback,
  203. delay: delay,
  204. args: args
  205. });
  206. }
  207. return originalSetInterval(callback, delay, ...args);
  208. };
  209. }
  210. if(settings.setTimeout){
  211. const originalSetTimeout = unsafeWindow.setTimeout;
  212. unsafeWindow.setTimeout = function(callback, delay, ...args) {
  213. if (callback.toString() !== "function () { [native code] }") {
  214. console.log('setTimeout:', {
  215. function: callback.toString(),
  216. callback: callback,
  217. delay: delay,
  218. args: args
  219. });
  220. }
  221. return originalSetTimeout(callback, delay, ...args);
  222. }
  223. }
  224. if(settings.requestIdleCallback){
  225. const originalRequestIdleCallback = unsafeWindow.requestIdleCallback;
  226. unsafeWindow.requestIdleCallback = function(callback, options) {
  227. if (callback.toString() !== "function () { [native code] }") {
  228. console.log('requestIdleCallback', {
  229. function: callback.toString(),
  230. callback: callback,
  231. options,
  232. });
  233. }
  234. return originalRequestIdleCallback(callback, options);
  235. };
  236. }
  237. };
  238. analyzer()
  239.  
  240. const createSettingsUI = () => {
  241. // Remove existing settings menu if it exists
  242. const existingMenu = document.getElementById("settingsMenu");
  243. if (existingMenu) {
  244. document.body.removeChild(existingMenu);
  245. }
  246.  
  247. // 画面サイズを取得し、メニューの大きさを調整
  248. const screenWidth = window.innerWidth * 0.8;
  249. const screenHeight = window.innerHeight * 0.8;
  250.  
  251. const div = document.createElement('div');
  252. div.style.position = 'fixed';
  253. div.style.top = '50%';
  254. div.style.left = '50%';
  255. div.style.transform = 'translate(-50%, -50%)';
  256. div.style.background = 'white';
  257. div.style.padding = '20px';
  258. div.style.zIndex = '10000';
  259. div.style.width = `${screenWidth}px`;
  260. div.style.height = `${screenHeight}px`;
  261. div.style.display = 'flex';
  262. div.style.flexDirection = 'column';
  263. div.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.2)';
  264. div.style.overflowY = "scroll";
  265. div.style.overflow = 'auto'; // はみ出し防止
  266. div.style.flexDirection = 'row';
  267. div.style.flexWrap = 'wrap';
  268. div.id = "settingsMenuGRMP";
  269.  
  270. // 閉じるボタン(右上)
  271. const closeBtn = document.createElement('button');
  272. closeBtn.innerText = '✖';
  273. closeBtn.style.position = 'absolute';
  274. closeBtn.style.top = '10px';
  275. closeBtn.style.right = '10px';
  276. closeBtn.style.border = 'none';
  277. closeBtn.style.background = 'none';
  278. closeBtn.style.fontSize = '18px';
  279. closeBtn.style.cursor = 'pointer';
  280. closeBtn.onclick = () => document.body.removeChild(div);
  281. div.appendChild(closeBtn);
  282.  
  283. // 設定項目を追加
  284. const categories = {
  285. "DOM": ["append", "appendChild", "before", "after", "insertBefore", "ShadowDOM", "remove", "querySelector", "querySelectorAll"],
  286. "Object": ["defineProperty"],
  287. "Event": ["event", "dispatchEvent"],
  288. "Network": ["fetch", "XMLHttpRequest", "webSocket"],
  289. "Timer": ["setInterval", "setTimeout", "requestIdleCallback"],
  290. };
  291.  
  292. for (let category in categories) {
  293. const categoryDiv = document.createElement('div');
  294. categoryDiv.style.padding = '10px';
  295. categoryDiv.style.overflow = 'hidden';
  296.  
  297. // 親カテゴリーのラベル
  298. const categoryLabel = document.createElement('label');
  299. categoryLabel.style.display = 'flex';
  300. categoryLabel.style.alignItems = 'center';
  301. categoryLabel.style.fontSize = '18px';
  302. categoryLabel.style.marginBottom = '10px';
  303. categoryLabel.style.fontWeight = 'bold';
  304.  
  305. const categoryCheckbox = document.createElement('input');
  306. categoryCheckbox.type = 'checkbox';
  307. categoryCheckbox.id = category
  308. categoryCheckbox.checked = settings[category] || false;
  309. categoryCheckbox.style.marginRight = '10px';
  310. categoryCheckbox.onclick = e => {
  311. let targetCategory = e.target.id;
  312. let checks;
  313.  
  314. if (targetCategory == "DOM") checks = document.querySelectorAll(".DOM");
  315. if (targetCategory == "Object") checks = document.querySelectorAll(".Object");
  316. if (targetCategory == "Event") checks = document.querySelectorAll(".Event");
  317. if (targetCategory == "Network") checks = document.querySelectorAll(".Network");
  318. if (targetCategory == "Timer") checks = document.querySelectorAll(".Timer");
  319.  
  320. let isChecked = categoryCheckbox.checked;
  321.  
  322. checks.forEach(val => {
  323. val.checked = isChecked;
  324. GM_setValue(val.id, isChecked);
  325. console.log(`${val.id} is now ${isChecked}`);
  326. });
  327. };
  328.  
  329. categoryLabel.appendChild(categoryCheckbox);
  330. categoryLabel.appendChild(document.createTextNode(category));
  331. categoryDiv.appendChild(categoryLabel);
  332.  
  333. // 子要素のチェックボックス
  334. categories[category].forEach(key => {
  335. const label = document.createElement('label');
  336. label.style.display = 'flex';
  337. label.style.alignItems = 'center';
  338. label.style.fontSize = '16px';
  339. label.style.marginBottom = '5px';
  340.  
  341. const checkbox = document.createElement('input');
  342. checkbox.type = 'checkbox';
  343. checkbox.id = key;
  344. checkbox.className = [category];
  345. checkbox.checked = settings[key] || false;
  346. checkbox.onclick = e => {
  347. let targetCategory = e.target.className;
  348. let checks;
  349.  
  350. if(targetCategory == "DOM") checks = document.querySelectorAll(".DOM");
  351. if(targetCategory == "Object") checks = document.querySelectorAll(".Object");
  352. if(targetCategory == "Event") checks = document.querySelectorAll(".Event");
  353. if(targetCategory == "Network") checks = document.querySelectorAll(".Network");
  354. if(targetCategory == "Timer") checks = document.querySelectorAll(".Timer");
  355.  
  356. checks.forEach(elm => {
  357. if (elm.checked == false) {
  358. document.querySelector("#" + elm.className).checked = false;
  359. }
  360.  
  361. if (document.querySelectorAll('.' + elm.className + ':checked').length == checks.length){
  362. document.querySelector("#" + elm.className).checked = true;
  363. }
  364. });
  365. toggleSetting(key);
  366. }
  367. checkbox.style.marginRight = '10px';
  368.  
  369. label.appendChild(checkbox);
  370. label.appendChild(document.createTextNode(key));
  371. categoryDiv.appendChild(label);
  372. });
  373.  
  374. div.appendChild(categoryDiv);
  375. }
  376.  
  377. document.body.appendChild(div);
  378.  
  379. // 設定メニュー以外の要素をクリックすると閉じる
  380. document.addEventListener('click', e => {
  381. if (!div.contains(e.target)) {
  382. document.body.removeChild(div);
  383. }
  384. }, { once: true });
  385. };
  386.  
  387. // メニューコマンドを登録
  388. GM_registerMenuCommand("Open Settings", createSettingsUI);