// ==UserScript==
// @name Website Analyser
// @name:en Website Analyser
// @name:ja サイト分析
// @namespace http://tampermonkey.net/
// @version 2025-05-09
// @description Outputs capturable elements to console.log.
// @description:en Outputs capturable elements to console.log
// @description:ja キャプチャ可能な要素をconsole.logに出力する
// @author ぐらんぴ
// @match http*://*/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-start
// @license MIT
// ==/UserScript==
//const log = console.log;
let settings = {
querySelector: GM_getValue('querySelector', false),
querySelectorAll: GM_getValue('querySelectorAll', false),
append: GM_getValue('append', false),
appendChild: GM_getValue('appendChild', false),
defineProperty: GM_getValue('defineProperty', false),
event: GM_getValue('event', false),
fetch: GM_getValue('fetch', false),
webSocket: GM_getValue('webSocket', false),
XMLHttpRequest: GM_getValue('XMLHttpRequest', false),
ShadowDOM: GM_getValue('ShadowDOM', false),
before: GM_getValue('before', false),
after: GM_getValue('after', false),
insertBefore: GM_getValue('insertBefore', false),
dispatchEvent: GM_getValue('dispatchEvent', false),
setInterval: GM_getValue('setInterval', false),
setTimeout: GM_getValue('setTimeout', false),
requestIdleCallback: GM_getValue('requestIdleCallback', false),
};
const toggleSetting = (key) => {
settings[key] = !settings[key];
GM_setValue(key, settings[key]);
document.getElementById(key).checked = settings[key]; // チェックボックスの状態を同期
console.log(`${key} is now ${settings[key]}`);
};
const analyzer = () => {
if(settings.defineProperty){
const origObjDefineProperty = Object.defineProperty;
Object.defineProperty = (...args)=>{
console.log("defineProperty:", args);
return origObjDefineProperty(...args);
};
}
if(settings.fetch){
const origFetch = unsafeWindow.fetch;
unsafeWindow.fetch = async (...args) => {
console.log("fetch:", args);
const res = await origFetch(...args);
return res;
};
}
if(settings.webSocket){
const origWb = unsafeWindow.WebSocket;
unsafeWindow.WebSocket = function (...args) {
const ws = new origWb(...args);
ws.addEventListener("message", e => {
const data = JSON.parse(e.data);
console.log("webSocket:", data);
});
return ws;
};
}
if(settings.event){
const origAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
console.log("event:", type);
return origAddEventListener.call(this, type, listener, options);
};
}
if(settings.appendChild){
const origAppendChild = Element.prototype.appendChild;
Element.prototype.appendChild = function(...args) {
console.log("appendChild:", args);
return origAppendChild.apply(this, args);
};
}
if(settings.querySelector){
const originalQuerySelector = Document.prototype.querySelector;
Object.defineProperty(Document.prototype, 'querySelector', {
value: function(...args) {
console.log("selector:", ...args);
return originalQuerySelector.apply(this, args);
},
writable: true,
configurable: true
});
}
if(settings.querySelectorAll){
const originalQuerySelectorAll = Document.prototype.querySelectorAll;
Object.defineProperty(Document.prototype, 'querySelectorAll', {
value: function(...args) {
console.log("selectorAll:", ...args);
return originalQuerySelectorAll.apply(this, args);
},
writable: true,
configurable: true
});
}
if(settings.XMLHttpRequest){
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
this._requestInfo = { method, url, async, user, password };
console.log('XHR Request:', { method, url, async, user });
return originalOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function(body) {
this.addEventListener('load', function() {
console.log('XHR Response:', {
url: this._requestInfo.url,
status: this.status,
statusText: this.statusText,
response: this.responseText.substring(0, 200) // untill 200
});
});
console.log('XHR Send:', {
url: this._requestInfo.url,
body: body ? body.substring(0, 200) : null
});
return originalSend.apply(this, arguments);
};
}
if(settings.ShadowDOM){
const originalAttachShadow = Element.prototype.attachShadow;
Element.prototype.attachShadow = function(...args) {
console.log('Shadow DOM:', this,/* 'Args:', args*/);
const shadowRoot = originalAttachShadow.apply(this, args);
console.log('ShadowRoot:', shadowRoot);
return shadowRoot;
};
}
if(settings.before){
const origBefore = Element.prototype.before;
Element.prototype.before = function(...args) {
setTimeout(() => {
console.log('Before:', args)
},0)
return origBefore.apply(this, args);
};
}
if(settings.after){
const origAfter = Element.prototype.after;
Element.prototype.after = function(...args) {
setTimeout(() => {
console.log('After:', args)
},0)
return origAfter.apply(this, args);
};
}
if(settings.insertBefore){
const origInsertBefore = Node.prototype.insertBefore;
Node.prototype.insertBefore = function(newNode, referenceNode) {
console.log('newNode:', newNode);
console.log('referenceNode:', referenceNode);
return origInsertBefore.apply(this, [newNode, referenceNode]);
};
}
if(settings.dispatchEvent){
const originalDispatchEvent = EventTarget.prototype.dispatchEvent;
EventTarget.prototype.dispatchEvent = function(event) {
console.log('DispatchEvent:', {
target: this,
event: {
type: event.type,
eventObject: event,
}
});
return originalDispatchEvent.apply(this, arguments);
};
}
if(settings.remove){
const originalRemove = Element.prototype.remove;
Element.prototype.remove = function() {
console.log('Removed:', this);
return originalRemove.apply(this);
};
}
if(settings.setInterval){
const originalSetInterval = unsafeWindow.setInterval;
unsafeWindow.setInterval = function(callback, delay, ...args) {
if (callback.toString() !== "function () { [native code] }") {
console.log('setInterval:', {
function: callback.toString(),
callback: callback,
delay: delay,
args: args
});
}
return originalSetInterval(callback, delay, ...args);
};
}
if(settings.setTimeout){
const originalSetTimeout = unsafeWindow.setTimeout;
unsafeWindow.setTimeout = function(callback, delay, ...args) {
if (callback.toString() !== "function () { [native code] }") {
console.log('setTimeout:', {
function: callback.toString(),
callback: callback,
delay: delay,
args: args
});
}
return originalSetTimeout(callback, delay, ...args);
}
}
if(settings.requestIdleCallback){
const originalRequestIdleCallback = unsafeWindow.requestIdleCallback;
unsafeWindow.requestIdleCallback = function(callback, options) {
if (callback.toString() !== "function () { [native code] }") {
console.log('requestIdleCallback', {
function: callback.toString(),
callback: callback,
options,
});
}
return originalRequestIdleCallback(callback, options);
};
}
};
analyzer()
const createSettingsUI = () => {
// Remove existing settings menu if it exists
const existingMenu = document.getElementById("settingsMenu");
if (existingMenu) {
document.body.removeChild(existingMenu);
}
// 画面サイズを取得し、メニューの大きさを調整
const screenWidth = window.innerWidth * 0.8;
const screenHeight = window.innerHeight * 0.8;
const div = document.createElement('div');
div.style.position = 'fixed';
div.style.top = '50%';
div.style.left = '50%';
div.style.transform = 'translate(-50%, -50%)';
div.style.background = 'white';
div.style.padding = '20px';
div.style.zIndex = '10000';
div.style.width = `${screenWidth}px`;
div.style.height = `${screenHeight}px`;
div.style.display = 'flex';
div.style.flexDirection = 'column';
div.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.2)';
div.style.overflowY = "scroll";
div.style.overflow = 'auto'; // はみ出し防止
div.style.flexDirection = 'row';
div.style.flexWrap = 'wrap';
div.id = "settingsMenuGRMP";
// 閉じるボタン(右上)
const closeBtn = document.createElement('button');
closeBtn.innerText = '✖';
closeBtn.style.position = 'absolute';
closeBtn.style.top = '10px';
closeBtn.style.right = '10px';
closeBtn.style.border = 'none';
closeBtn.style.background = 'none';
closeBtn.style.fontSize = '18px';
closeBtn.style.cursor = 'pointer';
closeBtn.onclick = () => document.body.removeChild(div);
div.appendChild(closeBtn);
// 設定項目を追加
const categories = {
"DOM": ["append", "appendChild", "before", "after", "insertBefore", "ShadowDOM", "remove", "querySelector", "querySelectorAll"],
"Object": ["defineProperty"],
"Event": ["event", "dispatchEvent"],
"Network": ["fetch", "XMLHttpRequest", "webSocket"],
"Timer": ["setInterval", "setTimeout", "requestIdleCallback"],
};
for (let category in categories) {
const categoryDiv = document.createElement('div');
categoryDiv.style.padding = '10px';
categoryDiv.style.overflow = 'hidden';
// 親カテゴリーのラベル
const categoryLabel = document.createElement('label');
categoryLabel.style.display = 'flex';
categoryLabel.style.alignItems = 'center';
categoryLabel.style.fontSize = '18px';
categoryLabel.style.marginBottom = '10px';
categoryLabel.style.fontWeight = 'bold';
const categoryCheckbox = document.createElement('input');
categoryCheckbox.type = 'checkbox';
categoryCheckbox.id = category
categoryCheckbox.checked = settings[category] || false;
categoryCheckbox.style.marginRight = '10px';
categoryCheckbox.onclick = e => {
let targetCategory = e.target.id;
let checks;
if (targetCategory == "DOM") checks = document.querySelectorAll(".DOM");
if (targetCategory == "Object") checks = document.querySelectorAll(".Object");
if (targetCategory == "Event") checks = document.querySelectorAll(".Event");
if (targetCategory == "Network") checks = document.querySelectorAll(".Network");
if (targetCategory == "Timer") checks = document.querySelectorAll(".Timer");
let isChecked = categoryCheckbox.checked;
checks.forEach(val => {
val.checked = isChecked;
GM_setValue(val.id, isChecked);
console.log(`${val.id} is now ${isChecked}`);
});
};
categoryLabel.appendChild(categoryCheckbox);
categoryLabel.appendChild(document.createTextNode(category));
categoryDiv.appendChild(categoryLabel);
// 子要素のチェックボックス
categories[category].forEach(key => {
const label = document.createElement('label');
label.style.display = 'flex';
label.style.alignItems = 'center';
label.style.fontSize = '16px';
label.style.marginBottom = '5px';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = key;
checkbox.className = [category];
checkbox.checked = settings[key] || false;
checkbox.onclick = e => {
let targetCategory = e.target.className;
let checks;
if(targetCategory == "DOM") checks = document.querySelectorAll(".DOM");
if(targetCategory == "Object") checks = document.querySelectorAll(".Object");
if(targetCategory == "Event") checks = document.querySelectorAll(".Event");
if(targetCategory == "Network") checks = document.querySelectorAll(".Network");
if(targetCategory == "Timer") checks = document.querySelectorAll(".Timer");
checks.forEach(elm => {
if (elm.checked == false) {
document.querySelector("#" + elm.className).checked = false;
}
if (document.querySelectorAll('.' + elm.className + ':checked').length == checks.length){
document.querySelector("#" + elm.className).checked = true;
}
});
toggleSetting(key);
}
checkbox.style.marginRight = '10px';
label.appendChild(checkbox);
label.appendChild(document.createTextNode(key));
categoryDiv.appendChild(label);
});
div.appendChild(categoryDiv);
}
document.body.appendChild(div);
// 設定メニュー以外の要素をクリックすると閉じる
document.addEventListener('click', e => {
if (!div.contains(e.target)) {
document.body.removeChild(div);
}
}, { once: true });
};
// メニューコマンドを登録
GM_registerMenuCommand("Open Settings", createSettingsUI);