(function() { // スタイルの動的追加 const style = document.createElement('style'); style.textContent = ` .devtools-container { position: fixed; bottom: 0; left: 0; right: 0; height: 300px; background: #1a1e24; color: #e0e0e0; font-family: 'Consolas', 'Courier New', monospace; border-top: 2px solid #4fc3f7; display: flex; flex-direction: column; z-index: 9999; box-shadow: 0 -5px 15px rgba(0, 0, 0, 0.5); } .devtools-header { background: #252a33; padding: 8px 15px; display: flex; justify-content: space-between; border-bottom: 1px solid #4fc3f7; } .devtools-tabs { display: flex; gap: 10px; } .devtools-tab { padding: 5px 10px; background: #2c313a; border: 1px solid #4fc3f7; border-radius: 3px 3px 0 0; cursor: pointer; transition: all 0.2s; } .devtools-tab.active { background: #4fc3f7; color: #000; } .devtools-close { background: transparent; border: none; color: #e0e0e0; cursor: pointer; } .devtools-content { flex: 1; display: flex; overflow: hidden; } .devtools-panel { flex: 1; padding: 10px; overflow: auto; display: none; } .devtools-panel.active { display: block; } /* Console スタイル */ #console-log { white-space: pre-wrap; margin: 0; line-height: 1.4; } .console-input { width: 100%; background: #252a33; border: 1px solid #4fc3f7; color: #e0e0e0; padding: 8px; margin-top: 10px; } /* Elements スタイル */ .dom-tree { font-family: monospace; } .dom-node { margin-left: 15px; } .dom-tag { color: #4fc3f7; } .dom-attr { color: #ff7043; } /* Storage スタイル */ .storage-table { width: 100%; border-collapse: collapse; } .storage-table th, .storage-table td { border: 1px solid #4fc3f7; padding: 5px; text-align: left; } .storage-table th { background: #252a33; } .storage-actions { display: flex; gap: 5px; } .storage-btn { background: #4fc3f7; border: none; padding: 2px 5px; cursor: pointer; } `; document.head.appendChild(style); // 開発者ツールのUI構築 function createDevTools() { const container = document.createElement('div'); container.className = 'devtools-container'; container.id = 'devtools-container'; container.style.display = 'none'; // ヘッダー部分 const header = document.createElement('div'); header.className = 'devtools-header'; const tabs = document.createElement('div'); tabs.className = 'devtools-tabs'; const consoleTab = createTab('Console', 'console'); const elementsTab = createTab('Elements', 'elements'); const storageTab = createTab('Storage', 'storage'); tabs.appendChild(consoleTab); tabs.appendChild(elementsTab); tabs.appendChild(storageTab); const closeBtn = document.createElement('button'); closeBtn.className = 'devtools-close'; closeBtn.textContent = '×'; closeBtn.onclick = toggleDevTools; header.appendChild(tabs); header.appendChild(closeBtn); // コンテンツ部分 const content = document.createElement('div'); content.className = 'devtools-content'; const consolePanel = createConsolePanel(); const elementsPanel = createElementsPanel(); const storagePanel = createStoragePanel(); content.appendChild(consolePanel); content.appendChild(elementsPanel); content.appendChild(storagePanel); container.appendChild(header); container.appendChild(content); document.body.appendChild(container); // タブ切り替え機能 function createTab(name, panelId) { const tab = document.createElement('div'); tab.className = 'devtools-tab'; tab.textContent = name; tab.onclick = () => { document.querySelectorAll('.devtools-tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.devtools-panel').forEach(p => p.classList.remove('active')); tab.classList.add('active'); document.getElementById(panelId + '-panel').classList.add('active'); }; return tab; } // 初期タブをアクティブに consoleTab.click(); } // Consoleパネルの作成 function createConsolePanel() { const panel = document.createElement('div'); panel.className = 'devtools-panel'; panel.id = 'console-panel'; const log = document.createElement('div'); log.id = 'console-log'; const input = document.createElement('input'); input.className = 'console-input'; input.placeholder = 'ここにJavaScriptを入力... (Enterで実行)'; input.onkeypress = (e) => { if (e.key === 'Enter') { try { const result = eval(e.target.value); if (result !== undefined) { logMessage('> ' + e.target.value, '#4fc3f7'); logMessage('← ' + result, '#69f0ae'); } } catch (err) { logMessage(err.message, '#ff5252'); } e.target.value = ''; } }; panel.appendChild(log); panel.appendChild(input); // コンソールログをキャプチャ ['log', 'error', 'warn'].forEach(method => { const original = console[method]; console[method] = (...args) => { original.apply(console, args); const color = method === 'error' ? '#ff5252' : method === 'warn' ? '#ffab40' : '#e0e0e0'; logMessage(args.join(' '), color); }; }); function logMessage(message, color) { const line = document.createElement('div'); line.style.color = color; line.textContent = message; log.appendChild(line); log.scrollTop = log.scrollHeight; } return panel; } // Elementsパネルの作成 function createElementsPanel() { const panel = document.createElement('div'); panel.className = 'devtools-panel'; panel.id = 'elements-panel'; const tree = document.createElement('div'); tree.className = 'dom-tree'; tree.id = 'dom-tree'; panel.appendChild(tree); // DOMツリーを構築 function buildDOMTree(node, parentElement, depth = 0) { if (node.nodeType === Node.ELEMENT_NODE) { const element = document.createElement('div'); element.className = 'dom-node'; element.style.marginLeft = `${depth * 15}px`; // タグ名 const tag = document.createElement('span'); tag.className = 'dom-tag'; tag.textContent = `<${node.tagName.toLowerCase()}`; element.appendChild(tag); // 属性 Array.from(node.attributes).forEach(attr => { const attrSpan = document.createElement('span'); attrSpan.className = 'dom-attr'; attrSpan.textContent = ` ${attr.name}="${attr.value}"`; element.appendChild(attrSpan); }); element.appendChild(document.createTextNode('>')); // 子要素 if (node.childNodes.length > 0) { node.childNodes.forEach(child => { buildDOMTree(child, element, depth + 1); }); } // 閉じタグ if (node.childNodes.length > 0 || node.tagName.toLowerCase() !== 'br') { const closeTag = document.createElement('div'); closeTag.style.marginLeft = `${depth * 15}px`; closeTag.innerHTML = `</${node.tagName.toLowerCase()}>`; element.appendChild(closeTag); } parentElement.appendChild(element); } else if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) { const text = document.createElement('div'); text.style.marginLeft = `${depth * 15}px`; text.style.color = '#e0e0e0'; text.textContent = `"${node.textContent.trim()}"`; parentElement.appendChild(text); } } // 初期DOMツリー構築 buildDOMTree(document.documentElement, tree); return panel; } // Storageパネルの作成 function createStoragePanel() { const panel = document.createElement('div'); panel.className = 'devtools-panel'; panel.id = 'storage-panel'; // LocalStorage表示 const localStorageTitle = document.createElement('h3'); localStorageTitle.textContent = 'Local Storage'; panel.appendChild(localStorageTitle); const localStorageTable = document.createElement('table'); localStorageTable.className = 'storage-table'; panel.appendChild(localStorageTable); // SessionStorage表示 const sessionStorageTitle = document.createElement('h3'); sessionStorageTitle.style.marginTop = '20px'; sessionStorageTitle.textContent = 'Session Storage'; panel.appendChild(sessionStorageTitle); const sessionStorageTable = document.createElement('table'); sessionStorageTable.className = 'storage-table'; panel.appendChild(sessionStorageTable); // Cookie表示 const cookiesTitle = document.createElement('h3'); cookiesTitle.style.marginTop = '20px'; cookiesTitle.textContent = 'Cookies'; panel.appendChild(cookiesTitle); const cookiesTable = document.createElement('table'); cookiesTable.className = 'storage-table'; panel.appendChild(cookiesTable); // ストレージを表示する関数 function renderStorage() { renderTable(localStorageTable, localStorage); renderTable(sessionStorageTable, sessionStorage); renderCookiesTable(cookiesTable); } function renderTable(tableElement, storage) { tableElement.innerHTML = ` Key Value Actions `; const tbody = tableElement.querySelector('tbody'); for (let i = 0; i < storage.length; i++) { const key = storage.key(i); const value = storage.getItem(key); const row = document.createElement('tr'); const keyCell = document.createElement('td'); keyCell.textContent = key; const valueCell = document.createElement('td'); valueCell.textContent = value; const actionsCell = document.createElement('td'); actionsCell.className = 'storage-actions'; const editBtn = document.createElement('button'); editBtn.className = 'storage-btn'; editBtn.textContent = 'Edit'; editBtn.onclick = () => { const newValue = prompt('Enter new value:', value); if (newValue !== null) { storage.setItem(key, newValue); renderStorage(); } }; const deleteBtn = document.createElement('button'); deleteBtn.className = 'storage-btn'; deleteBtn.textContent = 'Delete'; deleteBtn.onclick = () => { storage.removeItem(key); renderStorage(); }; actionsCell.appendChild(editBtn); actionsCell.appendChild(deleteBtn); row.appendChild(keyCell); row.appendChild(valueCell); row.appendChild(actionsCell); tbody.appendChild(row); } } function renderCookiesTable(tableElement) { tableElement.innerHTML = ` Name Value Actions `; const tbody = tableElement.querySelector('tbody'); document.cookie.split(';').forEach(cookie => { if (!cookie.trim()) return; const [name, ...valueParts] = cookie.split('='); const value = valueParts.join('=').trim(); const row = document.createElement('tr'); const nameCell = document.createElement('td'); nameCell.textContent = name.trim(); const valueCell = document.createElement('td'); valueCell.textContent = decodeURIComponent(value); const actionsCell = document.createElement('td'); actionsCell.className = 'storage-actions'; const deleteBtn = document.createElement('button'); deleteBtn.className = 'storage-btn'; deleteBtn.textContent = 'Delete'; deleteBtn.onclick = () => { document.cookie = `${name.trim()}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`; renderStorage(); }; actionsCell.appendChild(deleteBtn); row.appendChild(nameCell); row.appendChild(valueCell); row.appendChild(actionsCell); tbody.appendChild(row); }); } // 初期表示 renderStorage(); return panel; } // 開発者ツールの表示/非表示切り替え function toggleDevTools() { const container = document.getElementById('devtools-container'); if (container.style.display === 'none') { container.style.display = 'flex'; } else { container.style.display = 'none'; } } // 開発者ツールを開くボタンの作成 function createOpenButton() { const button = document.createElement('button'); button.id = 'open-devtools-btn'; button.textContent = '開発者ツールを開く'; button.style.position = 'fixed'; button.style.bottom = '10px'; button.style.right = '10px'; button.style.padding = '8px 16px'; button.style.background = '#4fc3f7'; button.style.color = '#000'; button.style.border = 'none'; button.style.borderRadius = '4px'; button.style.cursor = 'pointer'; button.style.zIndex = '9998'; button.onclick = toggleDevTools; document.body.appendChild(button); } document.addEventListener('DOMContentLoaded', function() { // 初期化 createDevTools(); createOpenButton(); // 初期メッセージ console.log('開発者ツールが初期化されました'); console.log('このコンソールでJavaScriptを実行できます'); }) })();