- // ==UserScript==
- // @name 91 Plus M
- // @namespace https://github.com/DonkeyBear
- // @version 0.96.2
- // @description 打造行動裝置看91譜的最好體驗。
- // @author DonkeyBear
- // @match https://www.91pu.com.tw/m/*
- // @match https://www.91pu.com.tw/song/*
- // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
- // @grant none
- // ==/UserScript==
-
- let currentUrl = window.location.href;
- if (currentUrl.match(/\/song\//)) {
- let sheetId = currentUrl.match(/\/\d*\./)[0].slice(1, -1);
- let newUrl = `https://www.91pu.com.tw/m/tone.shtml?id=${sheetId}`;
- window.location.replace(newUrl);
- }
-
- document.querySelector("html").style.backgroundColor = "#f7f7f7";
-
- let observerCheckList = {
- modifyTitle: false,
- modifyHeaderBackground: false,
- modifyHeaderFlex: false,
- modifyFunctionBarMargin: false,
- modifyTransposeButton: false
- }
-
- const observer = new MutationObserver(() => {
- /* 隱藏網頁元素 */
- let elementShouldBlock = {
- // 需要倒數才能關閉的蓋版廣告
- modalAd: document.querySelector("#viptoneWindow.window"),
- // 在頁面最底部的廣告
- bottomAd: document.querySelector("#bottomad"),
- // 最上方提醒升級VIP的廣告
- updateVipBar: document.querySelector(".update_vip_bar"),
- // 譜上的LOGO和浮水印
- overlayLogo: document.querySelector(".wmask"),
- // 彈出式頁尾
- footer: document.querySelector("footer"),
- // 自動滾動頁面捲軸
- autoScroll: document.querySelector(".autoscroll"),
- // 頁首的返回列
- headerBackplace: document.querySelector(".backplace"),
- // 頁首的Key選項
- keys: document.querySelector(".set .keys"),
- // 其餘的Google廣告
- adsByGoogle: document.querySelectorAll(".adsbygoogle")
- }
- for (let selected in elementShouldBlock) {
- // 將上述元素隱藏
- if (elementShouldBlock[selected]) {
- if (elementShouldBlock[selected].length === undefined) {
- // Node
- elementShouldBlock[selected].style.display = "none";
- } else {
- // NodeList
- for (let elem of elementShouldBlock[selected]) {
- elem.style.display = "none";
- }
- }
- }
- }
- if (document.querySelectorAll(".setint .hr")) {
- // 隱藏頁首部分功能鈕
- for (let i = 3; i < 6; i++) {
- if (document.querySelectorAll(".setint .hr")[i]) {
- document.querySelectorAll(".setint .hr")[i].style.display = "none";
- }
- }
- }
-
- /* 更改網頁標題 */
- if (!observerCheckList.modifyTitle) {
- if (document.querySelector("#mtitle")) {
- document.title = `${document.querySelector("#mtitle").innerText} | 91+ M`;
- observerCheckList.modify = true;
- }
- }
-
- /* 更改頁首背景樣式 */
- if (!observerCheckList.modifyHeaderBackground) {
- if (document.querySelector("header")) {
- document.querySelector("header").style.backdropFilter = "blur(5px) saturate(80%)";
- document.querySelector("header").style['-webkit-backdrop-filter'] = "blur(5px) saturate(80%)";
- document.querySelector("header").style.backgroundColor = "rgba(25, 20, 90, 0.5)";
- observerCheckList.modifyHeaderBackground = true;
- }
- }
-
- /* 更改頁首內容物排列方式 */
- if (!observerCheckList.modifyHeaderFlex) {
- for (let elem of [
- document.querySelector(".setint"),
- document.querySelector(".plays .capo")
- ]) {
- if (elem) {
- elem.style.display = "flex";
- elem.style.justifyContent = "space-between";
- if (elem.classList.contains("setint")) {
- elem.style.borderTop = "1px solid rgba(255, 255, 255, 0.2)";
- }
- observerCheckList.modifyHeaderFlex = true;
- }
- }
- }
-
- /* 更改六線譜前奏功能列邊界留白 */
- if (!observerCheckList.modifyFunctionBarMargin) {
- if (document.querySelector(".tfunc2")) {
- observerCheckList.modifyFunctionBarMargin = true;
- document.querySelector(".tfunc2").style.margin = "10px";
- }
- }
-
- /* 刪除內建的移調鈕,建立自製的 */
- if (!observerCheckList.modifyTransposeButton) {
- if (document.querySelector(".capo .select")) {
- let stringCapo = document.querySelector(".capo .select").innerText.split(" / ")[0]; // CAPO
- let stringKey = document.querySelector(".capo .select").innerText.split(" / ")[1]; // 調
- for (let i of document.querySelectorAll(".capo span[play]")) {
- i.style.display = "none";
- }
- // 建立降調鈕
- let spanMinus = document.createElement("span");
- spanMinus.innerText = "-";
- spanMinus.className = "select";
- spanMinus.onclick = () => {
- spanCapo.innerText = spanCapo.innerText.replace(/-?\d+/, match => {
- return Number(match) - 1;
- });
- spanCapo.innerText = spanCapo.innerText.replace(/\(.+\)/, match => {
- return `(${transpose(match.slice(1, -1), 1)})`;
- });
- for (let i of document.querySelectorAll("#tone_z .tf")) {
- i.innerHTML = transpose(i.innerText, 1).replace(/(#|b)/g, "<sup>$&</sup>");
- }
- }
- // 當前調
- let spanCapo = document.createElement("span");
- spanCapo.innerText = `Capo: ${stringCapo} (${stringKey})`;
- // 建立降調鈕
- let spanPlus = document.createElement("span");
- spanPlus.innerText = "+";
- spanPlus.className = "select";
- spanPlus.onclick = () => {
- spanCapo.innerText = spanCapo.innerText.replace(/-?\d+/, match => {
- return Number(match) + 1;
- });
- spanCapo.innerText = spanCapo.innerText.replace(/\(.+\)/, match => {
- return `(${transpose(match.slice(1, -1), -1)})`;
- });
- for (let i of document.querySelectorAll("#tone_z .tf")) {
- i.innerHTML = transpose(i.innerText, -1).replace(/(#|b)/g, "<sup>$&</sup>");
- }
- }
- // 放入功能列
- for (let i of [spanMinus, spanCapo, spanPlus]) {
- document.querySelector(".plays .capo").appendChild(i);
- }
- observerCheckList.modifyTransposeButton = true;
- }
- }
- });
- observer.observe(document.body, { childList: true, subtree: true });
-
- function transpose(chord, transposeValue) {
-
- const keys = {
- "C": "[I]", "C#": "[I#]",
- "D": "[II]", "D#": "[II#]",
- "E": "[III]",
- "F": "[IV]", "F#": "[IV#]",
- "G": "[V]", "G#": "[V#]",
- "A": "[VI]", "A#": "[VI#]",
- "B": "[VII]"
- };
-
- const pitchNameFix = {
- "#b": "", "b#": "",
- "E#": "F", "Fb": "E",
- "B#": "C", "Cb": "B",
- "C##": "D", "D##": "E",
- "F##": "G", "G##": "A",
- "A##": "B"
- };
-
- let resultChord = chord;
-
- for (let i = 0; i < 12; i++) {
- // first, transpose to Roman number.
- resultChord = resultChord.replaceAll(
- Object.keys(keys)[i],
- Object.values(keys)[i]
- );
- }
-
- for (let i = 0; i < 12; i++) {
- // transpose offset
- let fixedTransposeValue = (i + transposeValue) % 12;
- if (fixedTransposeValue < 0) { fixedTransposeValue += 12 }
- // second, transpose to pitch names.
- resultChord = resultChord.replaceAll(
- Object.values(keys)[i],
- Object.keys(keys)[fixedTransposeValue]
- );
- }
-
- for (let i = 0; i < 11; i++) {
- // fix illegal pitch names.
- resultChord = resultChord.replaceAll(
- Object.keys(pitchNameFix)[i],
- Object.values(pitchNameFix)[i]
- );
- }
-
- return resultChord;
- }