Mensaar Show Next Day

A userscript that switches to the meal plans for the next day when the canteen has already closed for today

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name             Mensaar Show Next Day
// @namespace        https://github.com/ikelax/userscripts
// @match            https://mensaar.de/
// @grant            none
// @version          0.4.0
// @author           Alexander Ikonomou
// @description      A userscript that switches to the meal plans for the next day when the canteen has already closed for today
// @license          MIT
// @require          https://update.greasyfork.org/scripts/502635/1422102/waitForKeyElements-CoeJoder-fork.js
// @supportURL       https://github.com/ikelax/userscripts/issues
// @copyright        2025, Alexander Ikonomou (https://github.com/ikelax)
// @homepageURL      https://github.com/ikelax/userscripts
// @homepage         https://github.com/ikelax/userscripts
// @contributionURL  https://github.com/ikelax/userscripts
// @collaborator     ikelax
// @icon             https://mensaar.de/img/icon.png
// ==/UserScript==

waitForKeyElements("div.active", switchToNextDay);

function switchToNextDay(activeTab) {
  const tabDate = activeTab.innerText;

  if (tabDate == undefined) {
    return;
  }

  const canteen = window.location.hash.replace("#/menu/", "");

  if (!["sb", "htwcas", "musiksb"].includes(canteen)) {
    return;
  }

  const closingTime = getClosingTime(canteen, tabDate);

  if (closingTime === undefined) {
    return;
  }

  const now = new Date();

  if (now > closingTime) {
    activeTab.nextSibling?.click();
  }
}

/***********************************************************
 *
 * The code below is copied from `utils/getClosingTime.js`.
 * If you make any changes to the code, please copy them to
 * `utils/getClosingTime.js`.
 *
 * For the reasons, see commit c47a8305a7a5163d638becc2c169.
 *
 **********************************************************/

/**
 * Converts a German date in the format "Weekday, Day. Month
 * Year" to "Day. Month Year", translating the month into
 * English.
 *
 * @param {string} date The date string to convert
 * @returns {string | undefined} The converted date string or undefined if the
 * date string was not in the expected format
 */
function convertDate(date) {
  const germanDate = date.split(", ")[1];

  return germanDate
    ?.replace("Januar", "January")
    ?.replace("Februar", "February")
    ?.replace("März", "March")
    ?.replace("Mai", "May")
    ?.replace("Juni", "June")
    ?.replace("Juli", "July")
    ?.replace("Oktober", "October")
    ?.replace("Dezember", "December");
}

/**
 * Depending on the weekday, the canteens close at different
 * times. For the closing times see also the
 * {@link https://www.stw-saarland.de/gastro/ page of the Studentenwerk}.
 *
 * The closing time of the canteen is the closing time of
 * the latest counter. For example, for the canteen at the
 * Saarland University, this means that it is the closing
 * time of the counter for Menü 1 and Menü 2.
 *
 * `dateString` is expected to be a string in the format
 * "Weekday, Day. Month Year" in German.
 *
 * @param {"sb" | "htwcas" | "musiksb"} canteen The canteen
 * @param {string} dateString The date string of the day in
 * the specified format
 * @returns {Date | undefined} The closing time for the
 * `canteen` and the day or undefined if the `dateString`
 * was not in the expected format
 */
function getClosingTime(canteen, dateString) {
  const convertedDateString = convertDate(dateString);

  if (convertedDateString === undefined) {
    return;
  }

  const convertedDate = new Date(convertedDateString);

  let closingMinutes = getClosingMinutes(canteen, convertedDate.getDay());
  return new Date(
    convertedDate.getFullYear(),
    convertedDate.getMonth(),
    convertedDate.getDate(),
    14,
    closingMinutes,
  );
}

/**
 * The function does not check whether `canteen` is one of
 * the specified strings.
 *
 * @param {"sb" | "htwcas" | "musiksb"} canteen The canteen
 * @param {number} day The day of the week in the same
 * enumeration as the `getDay` property of `Date`
 * @returns {0 | 15 | 30} The minutes of the closing time
 * for the `canteen` and the day
 */
function getClosingMinutes(canteen, day) {
  switch (canteen) {
    case "musiksb":
      return 0;
    case "htwcas":
      return 15;
    case "sb":
      // The canteen closes 15 minutes earlier on Fridays.
      return day === 5 ? 15 : 30;
  }
}