Skip to main content

New Mini Web Tool

Submitted by Prateek Kher on

    // ==UserScript==

    // @name     Mini Web Tools
    // @namespace  http://tampermonkey.net/
    // @version   2.0
    // @description Remove readonly/disabled from fields, toggle page edit mode — draggable & aesthetic
    // @author    You
    // @match    *://*/*
    // @grant    none
    // ==/UserScript==

    (function () {

     'use strict';


     // ---- Styles ----

     const style = document.createElement('style');

     style.textContent = `

       #mwt-root {

         position: fixed;

         bottom: 24px;

         left: 24px;

         z-index: 2147483647;

         display: flex;

         flex-direction: column;

         align-items: flex-start;

         gap: 8px;

         font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

       }

       #mwt-root.dragging {

         opacity: .93;

       }


       #mwt-panel {

         background: #ffffff;

         border: 0.5px solid rgba(0,0,0,0.12);

         border-radius: 14px;

         padding: 8px;

         display: flex;

         flex-direction: column;

         gap: 3px;

         min-width: 216px;

         box-shadow: 0 4px 24px rgba(0,0,0,0.10), 0 1px 4px rgba(0,0,0,0.06);

         opacity: 0;

         transform: translateY(8px) scale(0.96);

         pointer-events: none;

         transition: opacity .17s ease, transform .17s ease;

       }

       #mwt-panel.open {

         opacity: 1;

         transform: translateY(0) scale(1);

         pointer-events: all;

       }


       .mwt-panel-header {

         font-size: 11px;

         font-weight: 600;

         color: #999;

         letter-spacing: 0.07em;

         text-transform: uppercase;

         padding: 4px 8px 8px;

         border-bottom: 0.5px solid rgba(0,0,0,0.08);

         margin-bottom: 2px;

         display: flex;

         align-items: center;

         justify-content: space-between;

       }

       .mwt-drag-hint {

         font-size: 11px;

         color: #bbb;

         display: flex;

         align-items: center;

         gap: 3px;

       }

       .mwt-drag-hint svg {

         width: 12px; height: 12px;

         stroke: #bbb;

         stroke-width: 1.8;

         fill: none;

       }


       .mwt-tool-btn {

         display: flex;

         align-items: center;

         gap: 10px;

         background: transparent;

         border: none;

         border-radius: 8px;

         padding: 9px 10px;

         cursor: pointer;

         color: #1a1a1a;

         font-size: 13.5px;

         font-family: inherit;

         font-weight: 400;

         transition: background .12s;

         text-align: left;

         width: 100%;

         line-height: 1;

       }

       .mwt-tool-btn:hover {

         background: #f4f4f5;

       }

       .mwt-tool-btn:active {

         transform: scale(0.98);

       }

       .mwt-tool-btn svg {

         width: 17px; height: 17px;

         stroke: #888;

         stroke-width: 1.8;

         fill: none;

         flex-shrink: 0;

         stroke-linecap: round;

         stroke-linejoin: round;

       }

       .mwt-tool-btn.mwt-active-edit {

         color: #d63031;

       }

       .mwt-tool-btn.mwt-active-edit svg {

         stroke: #d63031;

       }

       .mwt-tool-btn.mwt-flash-success {

         background: #e8f8f0;

         color: #1b8a4a;

       }

       .mwt-tool-btn.mwt-flash-success svg {

         stroke: #1b8a4a;

       }


       #mwt-fab {

         width: 46px;

         height: 46px;

         border-radius: 50%;

         background: #1a1a1a;

         border: none;

         cursor: grab;

         display: flex;

         align-items: center;

         justify-content: center;

         box-shadow: 0 2px 12px rgba(0,0,0,0.18), 0 1px 3px rgba(0,0,0,0.10);

         transition: transform .12s, box-shadow .12s;

         flex-shrink: 0;

         -webkit-tap-highlight-color: transparent;

       }

       #mwt-fab:hover {

         transform: scale(1.06);

         box-shadow: 0 4px 18px rgba(0,0,0,0.22);

       }

       #mwt-root.dragging #mwt-fab {

         cursor: grabbing;

         transform: scale(1.08);

       }

       #mwt-fab svg {

         width: 20px; height: 20px;

         stroke: #ffffff;

         stroke-width: 1.8;

         fill: none;

         stroke-linecap: round;

         stroke-linejoin: round;

         pointer-events: none;

       }


       #mwt-toast {

         position: fixed;

         bottom: 20px;

         right: 20px;

         background: #1a1a1a;

         color: #fff;

         font-size: 13px;

         font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

         padding: 8px 14px;

         border-radius: 8px;

         z-index: 2147483647;

         opacity: 0;

         transform: translateY(4px);

         transition: opacity .15s, transform .15s;

         pointer-events: none;

         white-space: nowrap;

       }

       #mwt-toast.mwt-toast-show {

         opacity: 1;

         transform: translateY(0);

       }


       @media (prefers-color-scheme: dark) {

         #mwt-panel {

           background: #1e1e1e;

           border-color: rgba(255,255,255,0.10);

           box-shadow: 0 4px 24px rgba(0,0,0,0.40);

         }

         .mwt-panel-header { color: #666; border-color: rgba(255,255,255,0.08); }

         .mwt-drag-hint { color: #555; }

         .mwt-drag-hint svg { stroke: #555; }

         .mwt-tool-btn { color: #e8e8e8; }

         .mwt-tool-btn:hover { background: #2a2a2a; }

         .mwt-tool-btn svg { stroke: #666; }

         .mwt-tool-btn.mwt-flash-success { background: #0d2e1a; color: #4ade80; }

         .mwt-tool-btn.mwt-flash-success svg { stroke: #4ade80; }

         #mwt-fab { background: #e8e8e8; box-shadow: 0 2px 12px rgba(0,0,0,0.40); }

         #mwt-fab svg { stroke: #1a1a1a; }

         #mwt-toast { background: #e8e8e8; color: #1a1a1a; }

       }


       @media print {

         #mwt-root, #mwt-toast { display: none !important; }

       }

     `;

     document.head.appendChild(style);


     // ---- SVG Icons ----

     const icons = {

       tools: `<svg viewBox="0 0 24 24"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>`,

       close: `<svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`,

       lockOpen: `<svg viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 9.9-1"/></svg>`,

       bolt: `<svg viewBox="0 0 24 24"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>`,

       pencil: `<svg viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>`,

       move: `<svg viewBox="0 0 24 24"><polyline points="5 9 2 12 5 15"/><polyline points="9 5 12 2 15 5"/><polyline points="15 19 12 22 9 19"/><polyline points="19 9 22 12 19 15"/><line x1="2" y1="12" x2="22" y2="12"/><line x1="12" y1="2" x2="12" y2="22"/></svg>`,

     };


     // ---- Build DOM ----

     const root = document.createElement('div');

     root.id = 'mwt-root';


     const panel = document.createElement('div');

     panel.id = 'mwt-panel';


     // Panel header

     const header = document.createElement('div');

     header.className = 'mwt-panel-header';

     header.innerHTML = `<span>Web tools</span><span class="mwt-drag-hint">${icons.move} drag to move</span>`;

     panel.appendChild(header);


     // Helper: create tool button

     function makeBtn(icon, label, onClick) {

       const btn = document.createElement('button');

       btn.className = 'mwt-tool-btn';

       btn.innerHTML = `${icons[icon]}<span>${label}</span>`;

       btn.addEventListener('click', onClick);

       panel.appendChild(btn);

       return btn;

     }


     const btnReadonly = makeBtn('lockOpen', 'Remove readonly', removeReadonly);

     const btnDisabled = makeBtn('bolt', 'Remove disabled', removeDisabled);

     const btnEdit   = makeBtn('pencil', 'Toggle edit page', toggleEditable);


     // FAB

     const fab = document.createElement('button');

     fab.id = 'mwt-fab';

     fab.setAttribute('aria-label', 'Open web tools');

     fab.innerHTML = icons.tools;


     root.appendChild(panel);

     root.appendChild(fab);

     document.body.appendChild(root);


     // Toast

     const toast = document.createElement('div');

     toast.id = 'mwt-toast';

     document.body.appendChild(toast);


     // ---- Toast ----

     let toastTimer = null;

     function showToast(msg) {

       toast.textContent = msg;

       toast.classList.add('mwt-toast-show');

       clearTimeout(toastTimer);

       toastTimer = setTimeout(() => toast.classList.remove('mwt-toast-show'), 2200);

     }


     // ---- Tools ----

     function removeReadonly() {

       const els = document.querySelectorAll('input[readonly], textarea[readonly]');

       els.forEach(el => el.removeAttribute('readonly'));

       flashBtn(btnReadonly);

       showToast(`Readonly removed from ${els.length} field(s)`);

     }


     function removeDisabled() {

       const els = document.querySelectorAll('[disabled]');

       els.forEach(el => el.removeAttribute('disabled'));

       flashBtn(btnDisabled);

       showToast(`Disabled removed from ${els.length} element(s)`);

     }


     let pageEditable = false;

     function toggleEditable() {

       pageEditable = !pageEditable;

       document.body.contentEditable = pageEditable ? 'true' : 'false';

       document.designMode = pageEditable ? 'on' : 'off';

       btnEdit.querySelector('span').textContent = pageEditable ? 'Editing on' : 'Toggle edit page';

       btnEdit.classList.toggle('mwt-active-edit', pageEditable);

       showToast(pageEditable ? 'Page editing enabled' : 'Page editing disabled');

     }


     function flashBtn(btn) {

       btn.classList.add('mwt-flash-success');

       setTimeout(() => btn.classList.remove('mwt-flash-success'), 800);

     }


     // ---- Panel toggle ----

     let panelOpen = false;


     function openPanel(state) {

       panelOpen = state;

       panel.classList.toggle('open', panelOpen);

       fab.innerHTML = panelOpen ? icons.close : icons.tools;

       fab.setAttribute('aria-label', panelOpen ? 'Close web tools' : 'Open web tools');

     }


     // Close on outside click

     document.addEventListener('mousedown', e => {

       if (panelOpen && !root.contains(e.target)) openPanel(false);

     });


     // ---- Drag logic ----

     let dragging = false;

     let didDrag = false;

     let dragStartX, dragStartY, rootStartX, rootStartY;


     function getEventPos(e) {

       return e.touches

         ? { x: e.touches[0].clientX, y: e.touches[0].clientY }

         : { x: e.clientX, y: e.clientY };

     }


     function applyStoredPos() {

       try {

         const saved = localStorage.getItem('mwt-pos');

         if (!saved) return;

         const { x, y } = JSON.parse(saved);

         root.style.bottom = 'auto';

         root.style.right = 'auto';

         root.style.left = x + 'px';

         root.style.top = y + 'px';

       } catch (e) {}

     }


     function savePos(x, y) {

       try { localStorage.setItem('mwt-pos', JSON.stringify({ x, y })); } catch (e) {}

     }


     function clamp(val, min, max) {

       return Math.max(min, Math.min(val, max));

     }


     fab.addEventListener('mousedown', onDragStart);

     fab.addEventListener('touchstart', onDragStart, { passive: true });


     function onDragStart(e) {

       const pos = getEventPos(e);

       const rootRect = root.getBoundingClientRect();


       dragStartX = pos.x;

       dragStartY = pos.y;

       rootStartX = rootRect.left;

       rootStartY = rootRect.top;

       dragging  = true;

       didDrag   = false;


       root.style.transition = 'none';

       root.style.bottom = 'auto';

       root.style.right = 'auto';

       root.style.left  = rootStartX + 'px';

       root.style.top  = rootStartY + 'px';


       root.classList.add('dragging');


       window.addEventListener('mousemove', onDragMove);

       window.addEventListener('touchmove', onDragMove, { passive: false });

       window.addEventListener('mouseup',  onDragEnd);

       window.addEventListener('touchend', onDragEnd);

     }


     function onDragMove(e) {

       if (!dragging) return;

       if (e.cancelable) e.preventDefault();


       const pos = getEventPos(e);

       const dx = pos.x - dragStartX;

       const dy = pos.y - dragStartY;


       if (Math.abs(dx) > 3 || Math.abs(dy) > 3) didDrag = true;


       const rootRect = root.getBoundingClientRect();

       const newX = clamp(rootStartX + dx, 0, window.innerWidth - rootRect.width);

       const newY = clamp(rootStartY + dy, 0, window.innerHeight - rootRect.height);


       root.style.left = newX + 'px';

       root.style.top = newY + 'px';

     }


     function onDragEnd() {

       if (!dragging) return;

       dragging = false;

       root.classList.remove('dragging');


       const rootRect = root.getBoundingClientRect();

       savePos(rootRect.left, rootRect.top);


       window.removeEventListener('mousemove', onDragMove);

       window.removeEventListener('touchmove', onDragMove);

       window.removeEventListener('mouseup',  onDragEnd);

       window.removeEventListener('touchend', onDragEnd);

     }


     fab.addEventListener('click', () => {

       if (didDrag) { didDrag = false; return; }

       openPanel(!panelOpen);

     });


     // ---- Window resize: keep in bounds ----

     window.addEventListener('resize', () => {

       const rootRect = root.getBoundingClientRect();

       const newX = clamp(rootRect.left, 0, window.innerWidth - rootRect.width);

       const newY = clamp(rootRect.top, 0, window.innerHeight - rootRect.height);

       root.style.left = newX + 'px';

       root.style.top = newY + 'px';

       savePos(newX, newY);

     });


     // ---- Print ----

     window.addEventListener('beforeprint', () => { root.style.display = 'none'; toast.style.display = 'none'; });

     window.addEventListener('afterprint', () => { root.style.display = ''; toast.style.display = ''; });


     // ---- Init position ----

     applyStoredPos();


    })();