您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Display the Last-Modified timestamp for non-HTML documents
// ==UserScript== // @name Show Last Modified Timestamp for Non-HTML Documents // @namespace https://gf.qytechs.cn/users/1310758 // @description Display the Last-Modified timestamp for non-HTML documents // @match *://*/* // @license MIT License // @author pachimonta // @grant GM.setValue // @grant GM_getValue // @grant GM_setClipboard // @noframes // @version 2025.05.03.001 // ==/UserScript== (function () { // Only proceed for non-HTML documents with required properties if ( !document || !document.lastModified || !document.contentType || /^text\/html(?:$|;)/.test(document.contentType) || !document.head || !document.body ) return; // Information note shown on overlay focus const NOTE = String.raw`Content last modified time. Note: The 'lastModified' value may differ from the server's 'Last-Modified' header. If not available, the 'Date' header is used, showing when the server sent the response. Timestamp formats may also vary by environment, which can cause display errors. 📋: Copy, 📌: Drag overlay, ✅: Toggle display. Press CTRL+C to copy the last modified time. Press CTRL+V to toggle display. When unchecked, it hides on blur.`; const DEFAULT_OVERLAY_VISIBLE = true; // Parse lastModified string into Date const lastModifiedDate = new Date(document.lastModified); // Format date (YYYY-MM-DD) const dateFormatOptions = { year: 'numeric', month: '2-digit', day: '2-digit', }; const formattedDate = new Intl.DateTimeFormat( 'sv-SE', dateFormatOptions ).format(lastModifiedDate); // Format time with timezone const timeFormatOptions = { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', timeZoneName: 'short', }; const formattedTime = new Intl.DateTimeFormat( 'sv-SE', timeFormatOptions ).format(lastModifiedDate); // Get short weekday name (Mon, Tue, ...) const weekdayOptions = { weekday: 'short', }; const dayOfWeek = new Intl.DateTimeFormat('en-US', weekdayOptions).format( lastModifiedDate ); // Final display string const lastModifiedText = `${formattedDate} (${dayOfWeek}) ${formattedTime}`; // Get overlay visibility state from user settings const isOverlayVisible = GM_getValue( 'lastModifiedOverlayVisible', DEFAULT_OVERLAY_VISIBLE ); // Create overlay root element const overlay = document.createElement('span'); overlay.classList.add('last-modified-overlay'); if (!isOverlayVisible) overlay.classList.add('opacity-zero'); overlay.setAttribute('tabindex', '0'); overlay.dataset.content = NOTE; // Last-modified info button const infoButton = document.createElement('button'); infoButton.textContent = lastModifiedText; infoButton.classList.add('overlay-info-button'); infoButton.setAttribute('tabindex', '-1'); overlay.append(infoButton); // When info button receives focus, move focus to overlay for accessibility/tooltips infoButton.addEventListener( 'focus', function () { overlay.focus(); }, true ); // Copy button const copyButton = document.createElement('button'); copyButton.classList.add('copy-button'); copyButton.textContent = '📋'; copyButton.dataset.tooltip = 'Copy'; overlay.append(copyButton); // Copy to clipboard with double confirmation let copyTooltipTimeout; copyButton.addEventListener('click', () => { if (copyButton.dataset.tooltip === 'Copied!') return; function resetTooltip() { copyButton.dataset.tooltip = 'Copy'; copyButton.blur(); } if (copyTooltipTimeout) clearTimeout(copyTooltipTimeout); if (copyButton.dataset.tooltip === 'Copy') { copyButton.dataset.tooltip = 'Copy?'; copyTooltipTimeout = setTimeout(resetTooltip, 3000); return; } GM_setClipboard(lastModifiedText, 'text'); copyButton.dataset.tooltip = 'Copied!'; copyTooltipTimeout = setTimeout(resetTooltip, 1000); }); // Toggle label & checkbox for overlay visibility const toggleLabel = document.createElement('label'); toggleLabel.setAttribute('tabindex', '0'); toggleLabel.classList.add('toggle-label'); toggleLabel.dataset.tooltip = 'Toggle display'; const toggleCheckbox = document.createElement('input'); toggleCheckbox.type = 'checkbox'; toggleCheckbox.classList.add('toggle-checkbox'); toggleCheckbox.checked = isOverlayVisible; toggleCheckbox.addEventListener( 'change', function () { const checked = toggleCheckbox.checked; const prevValue = GM_getValue( 'lastModifiedOverlayVisible', DEFAULT_OVERLAY_VISIBLE ); if (prevValue !== checked) { GM.setValue('lastModifiedOverlayVisible', checked); } overlay.classList.toggle('opacity-zero', !checked); }, true ); toggleLabel.append(toggleCheckbox); overlay.prepend(toggleLabel); // Drag button const dragButton = document.createElement('button'); dragButton.classList.add('drag-button'); dragButton.textContent = '📌'; dragButton.dataset.tooltip = 'Drag overlay'; overlay.prepend(dragButton); // Reset overlay position on double click of drag button dragButton.addEventListener('dblclick', function() { overlay.style.top = defaultTop; overlay.style.right = defaultRight; overlay.style.bottom = 'auto'; overlay.style.left = 'auto'; }); // Keyboard shortcuts: Ctrl+C to copy, Ctrl+V to toggle overlay.addEventListener('keydown', function(event) { if (event.ctrlKey && event.key === 'c') { GM_setClipboard(lastModifiedText, 'text'); } if (event.ctrlKey && event.key === 'v') { toggleCheckbox.click(); } }); // Sync overlay state when window/tab gets focus (cross-tab) window.addEventListener( 'focus', function () { const currentVisible = GM_getValue( 'lastModifiedOverlayVisible', DEFAULT_OVERLAY_VISIBLE ); toggleCheckbox.checked = currentVisible; overlay.classList.toggle('opacity-zero', !currentVisible); }, true ); // Overlay styles const style = document.createElement('style'); style.textContent = String.raw` .last-modified-overlay { display: inline-block; position: fixed; top: 8px; right: 32px; background-color: rgba(0, 0, 0, 0.6); color: white; z-index: 10; font-size: 13px; padding: 8px; font-family: sans-serif; cursor: pointer; border: 1px solid transparent; border-radius: 6px; } .last-modified-overlay:focus::after { content: attr(data-content); position: absolute; top: 100%; left: 0; background-color: #ffe; color: #000; padding: 4px 8px; margin-top: 4px; border: 1px solid #000; border-radius: 6px; z-index: 10; white-space: break-spaces; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.19); opacity: 0.9; } .last-modified-overlay:focus, .last-modified-overlay:hover { opacity: 1; } .overlay-info-button { appearance: none; color: white; margin: 0 6px; background-color: transparent; border: 2px solid transparent; cursor: pointer; } .drag-button { cursor: move; } .copy-button { cursor: copy; } [data-tooltip] { font-family: inherit; position: relative; opacity: 0.6; z-index: 100; } [data-tooltip]:hover::after, [data-tooltip]:focus::after { content: attr(data-tooltip); display: inline-block; position: absolute; top: 100%; left: -26px; background-color: #ffe; color: #000; padding: 4px 8px; margin-top: 4px; border: 1px solid #000; border-radius: 6px; white-space: break-spaces; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.19); } [data-tooltip]:hover, [data-tooltip]:focus { opacity: 0.9; } .opacity-zero { opacity: 0; transition: opacity 0.25s; } `; document.head.append(style); // Insert overlay after <body> for layout consistency document.body.after(overlay); // ------- Drag and Drop Functionality ------- // Utility: Get clientX and clientY from mouse or touch event function getEventClientXY(e) { if (e.touches && e.touches.length > 0) { return { x: e.touches[0].clientX, y: e.touches[0].clientY }; } else if (typeof e.clientX === 'number' && typeof e.clientY === 'number') { return { x: e.clientX, y: e.clientY }; } return null; } let isDragging = false; let dragOffsetX = 0; let dragOffsetY = 0; // Store default position for reset const defaultTop = overlay.style.top; const defaultRight = overlay.style.right; function dragStart(e) { // Only allow dragging from the drag button if (e.target !== dragButton) return; isDragging = true; const pos = getEventClientXY(e); if (!pos) return; const rect = overlay.getBoundingClientRect(); dragOffsetX = pos.x - rect.left; dragOffsetY = pos.y - rect.top; e.preventDefault(); } function dragMove(e) { if (!isDragging) return; const pos = getEventClientXY(e); if (!pos) return; let newLeft = pos.x - dragOffsetX; let newTop = pos.y - dragOffsetY; // Keep overlay within viewport newLeft = Math.max(0, Math.min(window.innerWidth - overlay.offsetWidth, newLeft)); newTop = Math.max(0, Math.min(window.innerHeight - overlay.offsetHeight, newTop)); overlay.style.left = newLeft + 'px'; overlay.style.top = newTop + 'px'; overlay.style.right = 'auto'; overlay.style.bottom = 'auto'; e.preventDefault(); } function dragEnd() { isDragging = false; } // Register drag event listeners for mouse and touch overlay.addEventListener('mousedown', dragStart); overlay.addEventListener('touchstart', dragStart, { passive: false }); document.addEventListener('mousemove', dragMove); document.addEventListener('touchmove', dragMove, { passive: false }); document.addEventListener('mouseup', dragEnd); document.addEventListener('touchend', dragEnd); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址