Show Last Modified Timestamp for Non-HTML Documents

Displays the Last-Modified timestamp for non-HTML documents

اعتبارا من 26-04-2025. شاهد أحدث إصدار.

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