Show Last Modified Timestamp for Non-HTML Documents

Displays the Last-Modified timestamp for non-HTML documents

当前为 2025-04-26 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Show Last Modified Timestamp for Non-HTML Documents
// @namespace   https://greasyfork.org/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.001
// ==/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

  // 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

  overlayDiv.addEventListener('mousedown', function (e) {
    // Do not initiate dragging if the target is not the drag button
    if (e.target !== dragButton) return;
    isDragging = true; // Set dragging flag to true
    // Calculate the difference between the mouse position and the top-left corner of the overlay
    const rect = overlayDiv.getBoundingClientRect();
    dragOffsetX = e.clientX - rect.left; // Calculate X offset
    dragOffsetY = e.clientY - rect.top; // Calculate Y offset
    e.preventDefault(); // Prevent default behavior
  });

  document.addEventListener('mousemove', function (e) {
    if (!isDragging) return; // Exit if not dragging
    // Calculate new position for the overlay
    let newLeft = e.clientX - dragOffsetX; // New left position
    let newTop = e.clientY - dragOffsetY; // New top position
    // Constrain the overlay within the window (optional)
    newLeft = Math.max(0, Math.min(window.innerWidth - overlayDiv.offsetWidth, newLeft)); // Constrain left position
    newTop = Math.max(0, Math.min(window.innerHeight - overlayDiv.offsetHeight, newTop)); // Constrain top position
    overlayDiv.style.left = newLeft + 'px'; // Set new left position
    overlayDiv.style.top = newTop + 'px'; // Set new top position
    overlayDiv.style.right = 'auto'; // Remove right constraint
    overlayDiv.style.bottom = 'auto'; // Remove bottom constraint (if necessary)
    e.preventDefault(); // Prevent default behavior
  });

  document.addEventListener('mouseup', function () {
    isDragging = false; // Reset dragging flag when mouse is released
  });

  // 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
})();