// ==UserScript==
// @name Show Last Modified Timestamp for Non-HTML Documents
// @namespace https://gf.qytechs.cn/users/1310758
// @description Displays the Last-Modified timestamp for non-HTML documents
// @match *://*/*
// @license MIT License
// @author pachimonta
// @grant GM.setValue
// @grant GM_getValue
// @version 2025.04.26.002
// ==/UserScript==
(function () {
/*
* Note: The 'lastModified' property of this script may not necessarily match the
* 'Last-Modified' header returned by the server. If the server does not provide
* a 'Last-Modified' header, the 'Date' header will be used instead, which reflects
* the time when the server sent the response. Additionally, the format of the
* timestamps may vary depending on the environment, which could lead to
* issues with formatting or errors in the code execution.
*/
// Default overlay display state
const defaultOverlayVisible = true;
// Exit the process if the document is not valid
if (
!document ||
!document.lastModified ||
!document.contentType ||
/^text\/html(?:$|;)/.test(document.contentType) ||
!document.head ||
!document.body
) {
return;
}
// Get the last modified date
const lastModifiedDate = new Date(document.lastModified);
// Set up date formatting options
const dateFormatOptions = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
};
const formattedDate = new Intl.DateTimeFormat('sv-SE', dateFormatOptions).format(lastModifiedDate);
// Set up time formatting options
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 the day of the week
const weekdayOptions = {
weekday: 'short',
};
const dayOfWeek = new Intl.DateTimeFormat('en-US', weekdayOptions).format(lastModifiedDate);
// Create the string to display
const lastModifiedText = `${formattedDate} (${dayOfWeek}) ${formattedTime}`;
// Save and retrieve the visibility state of the overlay
const isOverlayVisible = GM_getValue('lastModifiedOverlayVisible', defaultOverlayVisible);
// Create a div for the overlay
const overlayDiv = document.createElement('div');
overlayDiv.classList.add('last-modified-overlay');
if (!isOverlayVisible) {
overlayDiv.classList.add('hidden'); // Hide the overlay if not visible
}
overlayDiv.setAttribute('tabindex', '0'); // Make the overlay focusable
const overlayText = document.createElement('span');
overlayText.textContent = lastModifiedText; // Set the text content to the last modified date
overlayText.setAttribute('tabindex', '0'); // Make the text focusable
overlayText.addEventListener(
'focus',
function () {
const range = document.createRange();
range.selectNodeContents(overlayText); // Select the text when focused
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
},
true
);
overlayDiv.append(overlayText);
// Create a checkbox to toggle the display
const toggleCheckbox = document.createElement('input');
toggleCheckbox.type = 'checkbox';
toggleCheckbox.checked = isOverlayVisible; // Set the checkbox state based on visibility
toggleCheckbox.title = 'Toggle the display of the Last-Modified timestamp'; // Tooltip for the checkbox
toggleCheckbox.addEventListener(
'change',
function () {
const currentVisible = GM_getValue('lastModifiedOverlayVisible', defaultOverlayVisible);
const checked = toggleCheckbox.checked;
if (currentVisible !== checked) {
GM.setValue('lastModifiedOverlayVisible', checked); // Save the new visibility state
}
if (checked) {
overlayDiv.classList.remove('hidden'); // Show the overlay if checked
} else {
overlayDiv.classList.add('hidden'); // Hide the overlay if unchecked
}
},
true
);
overlayDiv.prepend(toggleCheckbox);
const dragButton = document.createElement('button');
dragButton.classList.add('drag-button');
dragButton.setAttribute('tabindex', '0'); // Make the button focusable
dragButton.textContent = '📌'; // Set the button text to a pin emoji
dragButton.title = 'Drag to move the overlay'; // Tooltip for the drag button
overlayDiv.append(dragButton);
// Synchronize the state when the window regains focus
window.addEventListener(
'focus',
function () {
const currentVisible = GM_getValue('lastModifiedOverlayVisible', defaultOverlayVisible);
toggleCheckbox.checked = currentVisible; // Update checkbox state
if (currentVisible) {
overlayDiv.classList.remove('hidden'); // Show the overlay if visible
} else {
overlayDiv.classList.add('hidden'); // Hide the overlay if not visible
}
},
true
);
// Add styles for the overlay
const style = document.createElement('style');
style.textContent = String.raw`
.last-modified-overlay {
position: fixed;
top: 8px;
right: 32px;
background: rgba(0, 0, 0, 0.6);
color: white;
z-index: 100;
font-size: 14px;
padding: 8px;
font-family: sans-serif;
cursor: pointer;
}
.last-modified-overlay [type="checkbox"] {
opacity: 0.6;
margin-right: 16px;
}
.last-modified-overlay [type="checkbox"]:hover,
.last-modified-overlay [type="checkbox"]:focus {
opacity: 1;
}
.last-modified-overlay .drag-button {
margin-left: 16px;
appearance: none;
background: none;
border: none;
font: inherit;
color: inherit;
cursor: move;
}
.last-modified-overlay.hidden {
opacity: 0;
}
.last-modified-overlay:focus,
.last-modified-overlay:hover {
opacity: 1;
}
`;
document.head.append(style); // Append the styles to the document head
document.body.append(overlayDiv); // Append the overlay to the body
function getEventClientXY(e) {
// If it is a touch event, get from touches[0], otherwise mouse event
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 };
}
// Otherwise null
return null;
}
// Variables for dragging functionality
let isDragging = false; // Flag to indicate if dragging is in progress
let dragOffsetX = 0; // X offset for dragging
let dragOffsetY = 0; // Y offset for dragging
function dragStart(e) {
// Ignored if e.target is not a dragButton
if (e.target !== dragButton) return;
isDragging = true;
const pos = getEventClientXY(e);
if (!pos) return;
const rect = overlayDiv.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;
newLeft = Math.max(0, Math.min(window.innerWidth - overlayDiv.offsetWidth, newLeft));
newTop = Math.max(0, Math.min(window.innerHeight - overlayDiv.offsetHeight, newTop));
overlayDiv.style.left = newLeft + 'px';
overlayDiv.style.top = newTop + 'px';
overlayDiv.style.right = 'auto';
overlayDiv.style.bottom = 'auto';
e.preventDefault();
}
function dragEnd(e) {
isDragging = false;
}
// Event registration (both mouse and touch)
overlayDiv.addEventListener('mousedown', dragStart);
overlayDiv.addEventListener('touchstart', dragStart, {passive: false});
document.addEventListener('mousemove', dragMove);
document.addEventListener('touchmove', dragMove, {passive: false});
document.addEventListener('mouseup', dragEnd);
document.addEventListener('touchend', dragEnd);
// Uncomment the line below to set the document title to the last modified date
// document.body.title = lastModifiedText; // Set the title to the last modified date
})();