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