Show lyrics LT

Show lyrics on Lyricstraining

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Show lyrics LT
// @namespace    http://tampermonkey.net/
// @version      2025-01-04
// @description  Show lyrics on Lyricstraining
// @author       Valerio Valletta
// @match        https://lyricstraining.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=lyricstraining.com
// @grant        none
// ==/UserScript==

var autoScroll = true;

function setCookie(name, value, days) {
  var expires = "";
  if (days) {
    var date = new Date();
    date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
    expires = "; expires=" + date.toUTCString();
  }
  document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
function getCookie(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(";");
  for (var i = 0; i < ca.length; i++) {
    var c = ca[i];
    while (c.charAt(0) == " ") c = c.substring(1, c.length);
    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
  }
  return null;
}

function dragElement(elmnt) {
  var pos1 = 0,
    pos2 = 0,
    pos3 = 0,
    pos4 = 0;

  const oldBoxPos = getCookie("lyrics-box-pos");
  if (oldBoxPos) {
    const { top, left } = JSON.parse(oldBoxPos);

    elmnt.style.top = top;
    elmnt.style.left = left;
  }

  if (document.getElementById(elmnt.id + "-header")) {
    // if present, the header is where you move the DIV from:
    document.getElementById(elmnt.id + "-header").onmousedown = dragMouseDown;
  }

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    // set the element's new position:
    elmnt.style.top = elmnt.offsetTop - pos2 + "px";
    elmnt.style.left = elmnt.offsetLeft - pos1 + "px";
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    const lyricsBox = document.querySelector("#lyrics-box");
    const lastBoxPos = {
      top: lyricsBox.style.top,
      left: lyricsBox.style.left,
    };

    setCookie("lyrics-box-pos", JSON.stringify(lastBoxPos));
    document.onmouseup = null;
    document.onmousemove = null;
  }
}

function scrollParentToChild(parent, child) {
  // Where is the parent on page
  var parentRect = parent.getBoundingClientRect();
  // What can you see?
  var parentViewableArea = {
    height: parent.clientHeight,
    width: parent.clientWidth,
  };

  // Where is the child
  var childRect = child.getBoundingClientRect();

  // Calculate the child's center relative to the parent's center
  var childCenterY = childRect.top + childRect.height / 2;
  var parentCenterY = parentRect.top + parentViewableArea.height / 2;

  // Calculate the distance to scroll
  var scrollY = childCenterY - parentCenterY;

  // Adjust the parent's scroll position
  parent.scrollTop += scrollY;
}

const createLyricsBox = () => {
  const lyricsBox = document.createElement("div");

  lyricsBox.id = "lyrics-box";
  lyricsBox.style.display = "none";
  lyricsBox.style.zIndex = 50;
  lyricsBox.style.position = "absolute";
  lyricsBox.style.right = 0;
  lyricsBox.style.bottom = 10;
  lyricsBox.style.width = "100%";
  lyricsBox.style.height = "100%";
  lyricsBox.style.maxHeight = "300px";
  lyricsBox.style.maxWidth = "700px";
  lyricsBox.style.overflowY = "scroll";
  lyricsBox.style.backgroundColor = "#FFF";
  lyricsBox.style.boxShadow = "rgba(0, 0, 0, 0.24) 0px 3px 8px";

  lyricsBox.appendChild(createLyricsBoxHeader());
  return lyricsBox;
};
function createCheckbox() {
  // Create the input element
  const input = document.createElement("input");

  // Set attributes
  input.type = "checkbox";
  input.id = "switch-autoscroll";
  input.style.transform = "scale(1.5)";
  input.checked = true;
  input.onclick = toggleAutoScroll;

  return input;
}

const toggleAutoScroll = () => {
  autoScroll = !autoScroll;
  document.querySelector("#switch-autoscroll").checked = autoScroll;
};

const createSwitchAutoScroll = () => {
  const div = document.createElement("div");
  div.style.display = "flex";
  div.style.justifyContent = "center";
  div.style.alignItems = "center";
  div.style.padding = "0.5em";

  const text = document.createElement("span");
  text.innerText = "autoscroll";
  text.style.fontSize = "1.3em";
  text.style.color = "white";
  text.style.paddingRight = "0.3em";

  const checkbox = createCheckbox();

  div.append(text);
  div.appendChild(checkbox);

  return div;
};

const createLyricsBoxHeader = () => {
  const header = document.createElement("div");

  header.id = "lyrics-box-header";
  header.style.top = 0;
  header.style.position = "sticky";
  header.style.width = "100%";
  header.style.height = "100%";
  header.style.maxHeight = "50px";
  header.style.backgroundColor = "royalblue";
  header.style.cursor = "move";
  header.style.display = "flex";
  header.style.alignItems = "center";
  header.style.justifyContent = "space-between";

  const title = document.createElement("span");
  title.innerText = "DRAG ME";
  title.style.fontSize = "1.6em";
  title.style.color = "white";

  title.style.fontWeight = "bold";
  title.style.letterSpacing = "0.3em";
  title.style.padding = "0.5em";

  const switchAutoScroll = createSwitchAutoScroll();
  header.appendChild(title);
  header.appendChild(switchAutoScroll);
  return header;
};

const createShowLyricsBtn = () => {
  const btn = document.createElement("button");
  btn.id = "show-lyrics-btn";
  btn.innerText = "SHOW LYRICS";
  btn.style.fontSize = "1.5em";
  btn.style.fontWeight = "bold";
  btn.style.zIndex = 100;
  btn.style.position = "fixed";
  btn.style.right = 0;
  btn.style.bottom = 10;
  btn.style.border = "2px solid royalblue";
  btn.style.borderRight = "0px";
  btn.style.color = "royalblue";
  btn.style.background = "white";
  btn.style.cursor = "pointer";
  btn.style.transition = "0.5s all";

  return btn;
};

(function () {
  "use strict";

  const addOptions = document.querySelector("#add-options");

  const lyricsBox = createLyricsBox();

  lt.game.page.lyrics.lines
    .map((el) => el.text)
    .forEach((el, i) => {
      const p = document.createElement("p");
      p.className = "lyric-line";
      p.id = `line-${i}`;
      p.style.fontSize = "1.5em";
      p.style.color = "white";
      p.style.paddingLeft = "1em";
      p.innerText = el;
      lyricsBox.appendChild(p);
    });

  const btn = createShowLyricsBtn();

  btn.addEventListener("mouseenter", () => {
    let btn = document.querySelector("#show-lyrics-btn");

    btn.style.fontSize = "1.6em";
  });

  btn.addEventListener("mouseleave", () => {
    let btn = document.querySelector("#show-lyrics-btn");

    btn.style.fontSize = "1.5em";
  });

  btn.addEventListener("click", () => {
    let currStatus = document.querySelector("#lyrics-box").style.display;
    if (currStatus === "none") {
      document.querySelector("#lyrics-box").style.display = "block";
      return;
    }
    document.querySelector("#lyrics-box").style.display = "none";
    return;
  });

  addOptions.appendChild(btn);
  addOptions.appendChild(lyricsBox);

  dragElement(lyricsBox);

  var trackLineVisible = new Set();
  setInterval(() => {
    if (trackLineVisible) [hideVisibleLines()];

    const lineEls = document.querySelectorAll(".lyric-line");
    const currEl = lineEls[lt.game.page.playView.playLine];

    if (!lineEls || !currEl) return;

    currEl.style.color = "green";
    currEl.style.display = "block";
    if (autoScroll) {
      scrollParentToChild(lyricsBox, currEl);
    }
    trackLineVisible.add(currEl);
  }, 500);

  const hideVisibleLines = () => {
    trackLineVisible.forEach((el) => {
      el.style.color = "black";
    });

    trackLineVisible.clear();
  };
})();