Greasy Fork is available in English.
DevTools cho điện thoại, nút nổi mở panel
// ==UserScript==
// @name Mini DevTools Mobile
// @namespace http://tampermonkey.net/
// @version 1.0
// @description DevTools cho điện thoại, nút nổi mở panel
// @author You
// @match *://*/*
// @include *
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
// Đợi DOM sẵn sàng
function waitForBody(callback) {
if (document.body) {
callback();
} else {
setTimeout(function() { waitForBody(callback); }, 100);
}
}
waitForBody(function() {
// ========== NÚT NỔI ==========
var floatBtn = document.createElement('div');
floatBtn.id = 'dtm-float-btn';
floatBtn.textContent = '🔧';
floatBtn.style.cssText =
'position:fixed;top:15px;right:15px;z-index:9999999;' +
'width:45px;height:45px;background:#007acc;color:white;' +
'border-radius:50%;text-align:center;line-height:45px;' +
'font-size:22px;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,0.5);' +
'user-select:none;-webkit-user-select:none;' +
'-webkit-tap-highlight-color:transparent;';
// Kéo thả nút nổi
var isDragging = false;
var startX, startY, startLeft, startTop;
floatBtn.addEventListener('touchstart', function(e) {
var touch = e.touches[0];
startX = touch.clientX;
startY = touch.clientY;
startLeft = floatBtn.offsetLeft;
startTop = floatBtn.offsetTop;
isDragging = false;
});
floatBtn.addEventListener('touchmove', function(e) {
var touch = e.touches[0];
var dx = touch.clientX - startX;
var dy = touch.clientY - startY;
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
isDragging = true;
floatBtn.style.left = Math.max(0, Math.min(window.innerWidth - 45, startLeft + dx)) + 'px';
floatBtn.style.top = Math.max(0, Math.min(window.innerHeight - 45, startTop + dy)) + 'px';
floatBtn.style.right = 'auto';
}
});
floatBtn.addEventListener('touchend', function(e) {
if (!isDragging) {
togglePanel();
}
});
document.body.appendChild(floatBtn);
// ========== PANEL ==========
var panel = document.createElement('div');
panel.id = 'dtm-panel';
panel.innerHTML =
'<div id="dtm-header">' +
'<span>🔧 Mini DevTools</span>' +
'<div style="display:flex;gap:3px;flex-wrap:wrap;">' +
'<button id="dtm-tab-network" class="dtm-tab-btn">🌐 Net</button>' +
'<button id="dtm-tab-console" class="dtm-tab-btn active">📜 JS</button>' +
'<button id="dtm-tab-sources" class="dtm-tab-btn">📁 Src</button>' +
'<button id="dtm-tab-storage" class="dtm-tab-btn">💾 Stg</button>' +
'<button id="dtm-tab-iframe" class="dtm-tab-btn">🖼️ Iframe</button>' +
'<button id="dtm-btn-pause" style="background:#8b0000;color:#fff;">⏯️</button>' +
'<button id="dtm-btn-close">❌</button>' +
'</div>' +
'</div>' +
'<div id="dtm-log"></div>' +
'<textarea id="dtm-input" placeholder="Gõ lệnh JS..."></textarea>';
panel.style.cssText =
'position:fixed;bottom:0;left:0;right:0;height:45vh;background:#1e1e1e;' +
'color:#d4d4d4;font-family:monospace;font-size:11px;z-index:9999998;' +
'display:none;flex-direction:column;border-top:3px solid #007acc;' +
'border-radius:12px 12px 0 0;overflow:hidden;';
document.body.appendChild(panel);
// Style bổ sung
var style = document.createElement('style');
style.textContent =
'#dtm-header{display:flex;justify-content:space-between;align-items:center;' +
'padding:8px;background:#252526;border-bottom:1px solid #3e3e3e;flex-shrink:0;flex-wrap:wrap;gap:4px;}' +
'#dtm-header span{font-weight:bold;color:#007acc;font-size:13px;}' +
'.dtm-tab-btn{background:#3e3e3e;color:#ccc;border:1px solid #555;' +
'padding:5px 8px;border-radius:3px;font-size:10px;white-space:nowrap;}' +
'.dtm-tab-btn.active{background:#007acc;color:white;}' +
'#dtm-log{flex:1;overflow-y:auto;padding:8px;font-size:10px;' +
'line-height:1.4;white-space:pre-wrap;word-break:break-all;}' +
'#dtm-input{width:100%;background:#2d2d2d;color:#4fc1ff;border:none;' +
'border-top:1px solid #3e3e3e;padding:10px;font-family:monospace;' +
'font-size:13px;outline:none;box-sizing:border-box;flex-shrink:0;}' +
'.dtm-log-info{color:#4fc1ff;}' +
'.dtm-log-warn{color:#ffcc00;}' +
'.dtm-log-error{color:#ff6b6b;}' +
'.dtm-log-success{color:#4caf50;}' +
'.dtm-log-network{color:#ce9178;}';
document.head.appendChild(style);
// ========== REFS ==========
function $(id) { return document.getElementById(id); }
function getLog() { return $('dtm-log'); }
function getInput() { return $('dtm-input'); }
// ========== TOGGLE ==========
function togglePanel() {
var p = $('dtm-panel');
if (p.style.display === 'none' || p.style.display === '') {
p.style.display = 'flex';
floatBtn.textContent = '▼';
floatBtn.style.background = '#e74c3c';
} else {
p.style.display = 'none';
floatBtn.textContent = '🔧';
floatBtn.style.background = '#007acc';
if (isPaused) resumeExecution();
}
}
// ========== LOG ==========
function dtLog(msg, type) {
type = type || 'info';
var log = getLog();
if (!log) return;
var line = document.createElement('div');
line.className = 'dtm-log-' + type;
line.textContent = '[' + new Date().toLocaleTimeString() + '] ' + msg;
log.appendChild(line);
log.scrollTop = log.scrollHeight;
}
// ========== OVERRIDE CONSOLE ==========
var _log = console.log.bind(console);
var _warn = console.warn.bind(console);
var _error = console.error.bind(console);
console.log = function() {
_log.apply(console, arguments);
dtLog([].slice.call(arguments).map(function(a) {
return typeof a === 'object' ? JSON.stringify(a) : String(a);
}).join(' '), 'info');
};
console.warn = function() { _warn.apply(console, arguments); dtLog([].slice.call(arguments).join(' '), 'warn'); };
console.error = function() { _error.apply(console, arguments); dtLog([].slice.call(arguments).join(' '), 'error'); };
// ========== NETWORK ==========
var oldFetch = window.fetch;
window.fetch = function() {
var url = typeof arguments[0] === 'string' ? arguments[0] : arguments[0].url;
dtLog('📡 FETCH → ' + url, 'network');
return oldFetch.apply(this, arguments).then(function(r) {
dtLog('📡 FETCH ← ' + r.status + ' ' + url, 'network');
return r;
});
};
var oldXHRO = XMLHttpRequest.prototype.open;
var oldXHRS = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(m, url) {
this._u = url; this._m = m;
return oldXHRO.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function() {
var s = this;
dtLog('📡 XHR ' + s._m + ' → ' + s._u, 'network');
s.addEventListener('load', function() { dtLog('📡 XHR ← ' + s.status, 'network'); });
return oldXHRS.apply(s, arguments);
};
// ========== IFRAME ==========
function getGameWindow() {
var iframes = document.querySelectorAll('iframe');
for (var i = 0; i < iframes.length; i++) {
try { if (iframes[i].contentWindow) return iframes[i].contentWindow; } catch(e) {}
}
return window;
}
// ========== EXEC ==========
function exec(cmd) {
var ctx = getGameWindow();
try {
var r = (function() { return eval(cmd); }).call(ctx);
if (r !== undefined) dtLog(String(r), 'success');
} catch(e) {
dtLog('❌ ' + e.message, 'error');
}
}
// ========== PAUSE ==========
var isPaused = false;
var pauseTimer = null;
function pauseExecution() {
if (isPaused) return;
isPaused = true;
$('dtm-btn-pause').style.background = '#006400';
$('dtm-btn-pause').textContent = '▶️';
dtLog('⏸️ PAUSE', 'warn');
pauseTimer = setInterval(function() {
var s = Date.now(); while (Date.now() - s < 50) {}
}, 0);
}
function resumeExecution() {
if (!isPaused) return;
isPaused = false;
$('dtm-btn-pause').style.background = '#8b0000';
$('dtm-btn-pause').textContent = '⏯️';
dtLog('▶️ RESUME', 'success');
if (pauseTimer) { clearInterval(pauseTimer); pauseTimer = null; }
}
// ========== SOURCES ==========
function showSources() {
var log = getLog();
log.innerHTML = '';
var doc = document;
var gameWin = getGameWindow();
try { doc = gameWin.document; } catch(e) {}
dtLog('📁 SCRIPTS:', 'success');
var scripts = doc.querySelectorAll('script[src]');
scripts.forEach(function(s, i) {
dtLog(' 📄 ' + (s.src.split('/').pop() || ('script_' + i)), 'info');
});
dtLog('📁 INLINE SCRIPTS: ' + doc.querySelectorAll('script:not([src])').length, 'info');
dtLog('📁 CANVAS: ' + doc.querySelectorAll('canvas').length, 'info');
}
// ========== STORAGE ==========
function showStorage() {
var log = getLog();
log.innerHTML = '';
dtLog('💾 LOCAL STORAGE:', 'success');
try {
var ls = getGameWindow().localStorage || localStorage;
for (var i = 0; i < ls.length; i++) {
var k = ls.key(i);
dtLog(k + ': ' + ls.getItem(k).substring(0, 150), 'info');
}
} catch(e) { dtLog('Lỗi: ' + e.message, 'error'); }
}
// ========== IFRAME INFO ==========
function showIframe() {
var log = getLog();
log.innerHTML = '';
var iframes = document.querySelectorAll('iframe');
dtLog('🖼️ Tổng iframe: ' + iframes.length, 'info');
iframes.forEach(function(f, i) {
dtLog(' #' + i + ': ' + (f.src || '(no src)'), 'info');
try {
dtLog(' Canvas: ' + f.contentDocument.querySelectorAll('canvas').length, 'success');
} catch(e) {
dtLog(' ⚠️ Cross-origin', 'warn');
}
});
}
// ========== TAB SWITCH ==========
function switchTab(tab) {
['network','console','sources','storage','iframe'].forEach(function(t) {
var btn = $('dtm-tab-' + t);
if (btn) btn.classList.remove('active');
});
var active = $('dtm-tab-' + tab);
if (active) active.classList.add('active');
var log = getLog();
if (log) log.innerHTML = '';
switch(tab) {
case 'network': dtLog('🌐 Đang theo dõi...', 'info'); break;
case 'console': dtLog('📜 Sẵn sàng', 'info'); break;
case 'sources': showSources(); break;
case 'storage': showStorage(); break;
case 'iframe': showIframe(); break;
}
}
// ========== EVENTS ==========
$('dtm-btn-close').addEventListener('click', togglePanel);
$('dtm-btn-pause').addEventListener('click', function() {
isPaused ? resumeExecution() : pauseExecution();
});
$('dtm-tab-network').addEventListener('click', function() { switchTab('network'); });
$('dtm-tab-console').addEventListener('click', function() { switchTab('console'); });
$('dtm-tab-sources').addEventListener('click', function() { switchTab('sources'); });
$('dtm-tab-storage').addEventListener('click', function() { switchTab('storage'); });
$('dtm-tab-iframe').addEventListener('click', function() { switchTab('iframe'); });
$('dtm-input').addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
var cmd = this.value.trim();
if (cmd) {
dtLog('> ' + cmd, 'info');
exec(cmd);
this.value = '';
}
}
});
// ========== START ==========
dtLog('✅ DevTools Mobile sẵn sàng', 'success');
dtLog('👆 Bấm nút 🔧 góc phải để mở', 'info');
dtLog('📜 Tab JS để chạy lệnh', 'info');
});
})();