Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!---------- Header ------------->
- <main lang="ja">
- <div class="template">
- <!-- The first row (vocab box+picture) -->
- <div class="def-header">
- <div class="dh-left">
- <div class="show-furigana vocab">
- <ruby>{{Word}}<rt>{{Reading}}</rt></ruby>
- <div style='font-family: "Arial"; font-size: 20px;'>{{Pitch Graph}}</div>
- </div>
- <!-- Pitch Accent -->
- {{#Pitch Position}}
- <span id="pitch-tags" class="tags"> {{Pitch Position}} </span>
- {{/Pitch Position}}
- </div>
- </div>
- <!-- The entire definition blockquote -->
- <div class="def-info-container">
- <div class="def-info"></div>
- </div>
- <blockquote class="main-def def-blockquote">
- {{#Definition Picture}}
- <div class="def-image tappable">{{Definition Picture}}</div>
- {{/Definition Picture}}
- <div class="definition">
- <div id="selection" data-display-name="Text Selection"></div>
- {{#Definition}}
- <div id="primary" data-display-name="Primary Definition">{{Definition}}</div>
- {{/Definition}}
- <div id="glossaries" data-display-name="Glossaries"></div>
- </div>
- </blockquote>
- <!-- Image (MODIFIED)-->
- <div id="main_image" class="{{Tags}}">
- <a onclick="toggleNsfw()">{{Picture}}</a>
- </div>
- <hr>
- <!-- Sentence Moved (MODIFIED)-->
- <div class="sentence">
- {{furigana:Sentence}}
- </div>
- <!-- This is for the sentence that you see on mobile (positioned under definition), on Desktop, the sentence goes above the definition box, and this is hidden -->
- <div class="sentence-mobile">
- {{furigana:Sentence}}
- </div>
- <!-- Audio Buttons Moved (MODIFIED)-->
- <br />
- <div class="audio-buttons">{{Word Audio}} {{Sentence Audio}}</div>
- <!------- Image modal --------->
- <div class="modal-bg tappable">
- <div class="img-popup"></div>
- </div>
- <div style="text-align: center">
- <!----------- Scripts ------------>
- <script>
- // This code is concerned with calculating the Pitch Accent and constructing the pitch accent graphs
- function isOdaka(pitchNumber) {
- const kana = `{{kana:Reading}}` || `{{Pitch Graph}}`;
- return (
- kana !== null &&
- kana.replace(/[ァィゥェォャュョヮぁぃぅぇぉゃゅょゎ]/g, "").length === pitchNumber
- );
- }
- function getPitchCategories() {
- const validTypes = "(heiban|atamadaka|nakadaka|odaka|kifuku)";
- return [...``.matchAll(validTypes)].map(m => m[0]);
- }
- function hasVerbOrAdjEnding() {
- const endings = ["い", "う", "く", "す", "つ", "ぶ", "む", "る"];
- return endings.some(ending => `{{Word}}`.replace("</div>","").endsWith(ending));
- }
- function getPitchType(pitchPosition) {
- const pitchCategories = getPitchCategories();
- const kifukuTags = ["adj-i", "v1", "v2", "v4", "v5", "vs-", "vz", "vk", "vn", "vr"];
- let canBeKifuku = pitchCategories.includes("kifuku");
- canBeKifuku ||= kifukuTags.some(tag => ``.includes(tag));
- if (canBeKifuku || (pitchCategories.length == 0 && hasVerbOrAdjEnding())) {
- return pitchPosition === 0 ? "heiban" : "kifuku";
- }
- if (pitchPosition === 0) {
- return "heiban";
- } else if (pitchPosition === 1) {
- return "atamadaka";
- } else if (pitchPosition > 1) {
- return isOdaka(pitchPosition) ? "odaka" : "nakadaka";
- }
- }
- // Show the color
- function paintTargetWord() {
- const pitchPositions = `{{Pitch Position}}`.match(/^\d+|\d+\b|\d+(?=\w)/g);
- if (pitchPositions === null) return;
- const pitchType = getPitchType(Number(pitchPositions[0]));
- const sentences = Array.from(
- document.querySelectorAll(".sentence, .definition, .sentence-mobile"),
- );
- for (const sentence of sentences) {
- for (const targetWord of sentence.getElementsByTagName("b")) {
- targetWord.classList.add(pitchType);
- }
- }
- const vocabElement = document.querySelector(".vocab");
- if (vocabElement !== null) {
- vocabElement.classList.add(pitchType);
- }
- }
- // Seperate Tags by space, and show them in their own boxes
- function tweakHTML() {
- // Split tags
- const tagsContainer = document.querySelector(".tags-container");
- const tags = `{{Tags}}`.split(" ");
- if (tagsContainer) {
- tagsContainer.innerHTML = "";
- for (tag of tags) {
- const tagElem = document.createElement("div");
- tagElem.className = "tags";
- tagElem.innerText = tag;
- tagsContainer.appendChild(tagElem);
- }
- }
- }
- function groupMoras(kana) {
- let currentChar = "";
- let nextChar = "";
- const groupedMoras = [];
- const check = ["ァ", "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ヮ", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "ゃ", "ゅ", "ょ", "ゎ"];
- for (let i = 0; i < kana.length; i++) {
- currentChar = kana[i];
- nextChar = i < kana.length - 1 && kana[i + 1];
- if (check.includes(nextChar)) {
- groupedMoras.push(currentChar + nextChar);
- i += 1;
- } else {
- groupedMoras.push(currentChar);
- }
- }
- return groupedMoras;
- }
- function getPitchPattern(pitchPosition) {
- // 0 = low
- // 1 = high
- // 2 = high to low
- const kana = `{{kana:Reading}}` || `{{Pitch Graph}}`;
- const moras = groupMoras(kana);
- let pattern = [];
- if (pitchPosition === 0) {
- // 平板
- pattern = [
- ...Array(moras[0].length).fill("0"),
- ...Array(kana.length - moras[0].length).fill("1"),
- ];
- } else if (pitchPosition === 1) {
- // 頭高
- pattern = [
- ...(moras[0].length === 2 ? ["1", "2"] : ["2"]),
- ...Array(kana.length - moras[0].length).fill("0"),
- ];
- } else if (pitchPosition > 1) {
- if (isOdaka(pitchPosition)) {
- // 尾高
- pattern = [
- ...Array(moras[0].length).fill("0"),
- ...Array(kana.length - moras[0].length - 1).fill("1"),
- "2",
- ];
- } else {
- // 中高
- let afterDrop = false;
- for (let i = 0; i < moras.length; i++) {
- if (i === 0) {
- pattern = Array(moras[0].length).fill("0");
- } else if (i + 1 === pitchPosition) {
- pattern =
- moras[i].length === 2
- ? [...pattern, "1", "2"]
- : [...pattern, "2"];
- afterDrop = true;
- } else if (afterDrop) {
- pattern = [...pattern, ...Array(moras[i].length).fill("0")];
- } else {
- pattern = [...pattern, ...Array(moras[i].length).fill("1")];
- }
- }
- }
- }
- return pattern;
- }
- function constructPitch() {
- const pitchPositions = `{{Pitch Position}}`.match(/^\d+|\d+\b|\d+(?=\w)/g);
- if (!pitchPositions) return;
- const kana = `{{kana:Reading}}` || `{{Pitch Graph}}`;
- const pitch = document.querySelector(".pitch");
- const pitchTags = document.querySelector("#pitch-tags");
- const createPitchSpan = (pitchClass, pitchChar) => {
- const pitchSpan = document.createElement("span");
- const charSpan = document.createElement("span");
- const lineSpan = document.createElement("span");
- pitchSpan.classList.add(pitchClass);
- charSpan.classList.add("pitch-char");
- charSpan.innerText = pitchChar;
- lineSpan.classList.add("pitch-line");
- pitchSpan.appendChild(charSpan);
- pitchSpan.appendChild(lineSpan);
- return pitchSpan;
- };
- pitch.innerHTML = "";
- pitchTags.innerHTML = "";
- pitchTags.style.display = "inline-block";
- let uniquePitchPositions = [...new Set(pitchPositions)];
- const pitchList = document.createElement("ul");
- const pitchTagList = document.createElement("ul");
- for (let pitchPosition of uniquePitchPositions) {
- const pitchTag = document.createElement("li");
- pitchTag.textContent = pitchPosition;
- const pattern = getPitchPattern(Number(pitchPosition));
- const pitchItem = document.createElement("li");
- pitchItem.classList.add("pitch-item");
- pitchItem.classList.add(getPitchType(Number(pitchPosition)));
- for (let i = 0; i < kana.length; i++) {
- if (pattern[i] === "0")
- pitchItem.appendChild(createPitchSpan("pitch-low", kana[i]));
- else if (pattern[i] === "1")
- pitchItem.appendChild(createPitchSpan("pitch-high", kana[i]));
- else if (pattern[i] === "2")
- pitchItem.appendChild(createPitchSpan("pitch-to-drop", kana[i]));
- else
- console.error(
- "pattern[i] found undefined value. pattern is",
- pattern,
- );
- }
- pitchTagList.appendChild(pitchTag);
- pitchList.appendChild(pitchItem);
- }
- pitch.appendChild(pitchList);
- pitchTags.appendChild(pitchTagList);
- }
- // Returns the dictionary content, without the dictionary name.
- function getDictionaryContent(dictionarySelector) {
- const dictionary = document.querySelector(dictionarySelector);
- if (!dictionary) return null;
- const contentInSpan = dictionary.querySelector(":scope > span");
- if (contentInSpan) return contentInSpan;
- const hasDictName = document.querySelector(":scope > i");
- if (!hasDictName) return dictionary;
- let dictionaryCopy = dictionary.cloneNode(true);
- dictName = dictionaryCopy.querySelector(":scope > i");
- dictName.remove();
- return dictionaryCopy;
- }
- function isPrimaryEqualToGloss() {
- const isJPMNConverted = document.querySelector(".definition li[data-details]");
- if (isJPMNConverted) return false;
- // single dict formatting
- const isSingleDict = document.querySelectorAll("#glossaries > div > ol").length === 0;
- if (isSingleDict) {
- const primaryDictName = document.querySelector("#primary > div > i");
- const glossariesDictName = document.querySelector("#glossaries > div > i");
- // Compare dicts names if present
- if (primaryDictName && glossariesDictName) {
- return primaryDictName.textContent === glossariesDictName.textContent;
- }
- // Compare content otherwise
- const primaryDict = getDictionaryContent("#primary > div");
- const glossariesDict = getDictionaryContent("#glossaries > div");
- if (!primaryDict || !glossariesDict ) return false;
- return primaryDict.innerHTML.trim() === glossariesDict.innerHTML.trim();
- }
- // multiple dicts
- const primaryDicts = document.querySelectorAll("#primary li[data-dictionary]");
- const glossariesDicts = document.querySelectorAll("#glossaries li[data-dictionary]");
- return primaryDicts.length === glossariesDicts.length
- }
- // Removes Unnecessary definitions
- function cleanUpDefinitions() {
- let selection = document.getElementById("selection");
- let primary = document.getElementById("primary");
- let glossaries = document.getElementById("glossaries");
- if (selection && selection.textContent === "") {
- selection.remove();
- }
- if (primary && primary.textContent === "") {
- primary.remove();
- primary = null;
- }
- if (glossaries && glossaries.textContent === "") {
- glossaries.remove();
- glossaries = null;
- }
- else if (primary && glossaries && isPrimaryEqualToGloss()) {
- glossaries.remove();
- }
- }
- // Display definition corresponding to index
- function updateDefDisplay() {
- const definitions = document.querySelectorAll(
- ".main-def > .definition > div"
- );
- let n_defs = definitions.length;
- if (n_defs === 1) definitions[0].classList.remove("hidden");
- if (n_defs <= 1) return;
- let currentIndex = document.head.getAttribute("data-def-index");
- currentIndex = currentIndex % n_defs;
- while (currentIndex < 0) currentIndex += n_defs;
- for (let idx = 0; idx < n_defs; idx++) {
- definitions[idx].classList.add("hidden");
- }
- definitions[currentIndex].classList.remove("hidden");
- const defDisplayName = definitions[currentIndex].getAttribute("data-display-name")
- const indexDisplay = document.querySelector(".def-info");
- indexDisplay.style.opacity = 1;
- indexDisplay.innerText = `${defDisplayName} ${currentIndex + 1}/${n_defs}`;
- }
- function setUpDefToggle() {
- document.head.setAttribute("data-def-index", 0);
- cleanUpDefinitions();
- // hide all but first definition
- let definitions = document.querySelectorAll(".main-def > .definition > div");
- Array.from(definitions).slice(1).forEach(def => { def.classList.add("hidden"); });
- // no need for toggling on less than 2 definitions
- if (definitions.length < 2) return;
- let mainDefContainer = document.querySelector(".main-def");
- const leftEdge = document.createElement("div");
- const rightEdge = document.createElement("div");
- leftEdge.classList.add("left-edge");
- leftEdge.classList.add("tappable");
- rightEdge.classList.add("right-edge");
- rightEdge.classList.add("tappable");
- mainDefContainer.appendChild(leftEdge);
- mainDefContainer.appendChild(rightEdge);
- const changeIndex = (value) => {
- // sync index between clicks and arrowkeys
- index = Number(document.head.getAttribute("data-def-index"));
- index += value;
- document.head.setAttribute("data-def-index", index);
- updateDefDisplay();
- };
- leftEdge.addEventListener("click", (e) => changeIndex(-1));
- rightEdge.addEventListener("click", (e) => changeIndex(1));
- // Add key listener only once per session
- if (document.head.classList.contains("has-listener")) return;
- document.addEventListener("keydown", (e) => {
- if (e.key === "ArrowLeft") changeIndex(-1);
- else if (e.key === "ArrowRight") changeIndex(1);
- });
- document.head.classList.add("has-listener");
- }
- // Format plaintext frequencies into a list
- function formatFrequencyList() {
- const frequency = document.querySelector('.freq-list-container');
- if (!frequency) return;
- const frequencyList = frequency.querySelector('ul');
- // Already a list; nothing to do
- if (frequencyList) return;
- const freqs = frequency.innerText.split(',');
- const freqHtml = `<ul>${freqs.map(freq => `<li>${freq.trim()}</li>`).join('')}</ul>`
- frequency.innerHTML = freqHtml;
- }
- // Sets the height of dhLeft, dhRight, defHeader as a whole
- function setDHHeight() {
- var dhLeft = document.querySelector('.dh-left');
- var dhRight = document.querySelector('.dh-right .image img');
- var defHeader = document.querySelector('.def-header')
- if (dhLeft && dhRight) {
- var dhLeftHeight = dhLeft.offsetHeight;
- dhRight.style.maxHeight = `${dhLeftHeight}px`;
- defHeader.style.maxHeight = `${dhLeftHeight}px`;
- }
- }
- // Hides the dictionaries user selected in MainDefinition in Glossary field, if any
- function hideCorrectDefinition() {
- // Do nothing if css rule already exists
- if (document.querySelector("style#hide-main-def")) return;
- let primaryDicts = document.querySelectorAll("#primary li[data-dictionary]");
- if (primaryDicts.length === 0) return;
- let style = document.createElement('style');
- style.type = 'text/css';
- style.id = "hide-main-def";
- const cssSelector = Array.from(primaryDicts).map((dict) =>
- `#glossaries li[data-dictionary="${dict.getAttribute("data-dictionary")}"]`
- ).join(", ");
- const cssRules = `${cssSelector} { display:none !important; }`;
- style.appendChild(document.createTextNode(cssRules));
- let defContainer = document.querySelector("blockquote.main-def");
- defContainer.appendChild(style);
- }
- // Moves Primary Dicts into the same list
- function movePrimaryDicts() {
- let primaryDicts = document.querySelectorAll("#primary li[data-dictionary]");
- let firstList = document.querySelector("#primary .yomitan-glossary > ol:has( li[data-dictionary])");
- for (let idx = 1; idx < primaryDicts.length; idx++) {
- firstList.appendChild(primaryDicts[idx]);
- }
- }
- // Adjust font size dynamically for vocab on the back
- function adjustVocabFontSize() {
- try {
- console.log("adjustVocabFontSize running on back...");
- const vocabElement = document.querySelector('.vocab');
- if (!vocabElement) {
- console.log("vocab element not found on back");
- return;
- }
- const containerWidth = window.innerWidth * 0.8; // 80vw consistently
- console.log("Back container width: " + containerWidth + "px");
- if (!containerWidth) {
- console.log("Container width not available on back");
- return;
- }
- let fontSize = 50; // Start with 55px to match Front Template
- vocabElement.style.fontSize = fontSize + 'px';
- // Measure the width of the text content only (exclude furigana if present)
- let textWidth = vocabElement.scrollWidth;
- console.log("Back initial text width: " + textWidth + "px");
- // Adjust font size until it fits
- let attempts = 0;
- while (textWidth > containerWidth && fontSize > 25 && attempts < 50) {
- fontSize -= 2;
- vocabElement.style.fontSize = fontSize + 'px';
- textWidth = vocabElement.scrollWidth;
- attempts++;
- console.log("Back adjusting font size: " + fontSize + "px, textWidth: " + textWidth + "px");
- }
- // Ensure minimum readability
- if (fontSize <= 25) {
- vocabElement.style.fontSize = '25px';
- fontSize = 25;
- console.log("Back minimum adjusted font size reached: 25px");
- }
- console.log("Back final font size: " + fontSize + "px");
- } catch (error) {
- console.error("Error in adjustVocabFontSize on back:", error);
- }
- }
- // Run immediately and poll until the element is available
- function ensureFontSizeAdjustment() {
- adjustVocabFontSize();
- // Poll every 50ms until the element is found or 2 seconds pass
- let attempts = 0;
- const interval = setInterval(() => {
- const vocabElement = document.querySelector('.vocab');
- if (vocabElement && vocabElement.scrollWidth > 0) {
- adjustVocabFontSize();
- clearInterval(interval);
- console.log("Font size adjusted successfully on back after polling");
- }
- attempts++;
- if (attempts > 40) { // 40 * 50ms = 2 seconds
- clearInterval(interval);
- console.log("Stopped polling: vocab element not found or not measurable");
- }
- }, 50);
- }
- // Initialize all functions!!!
- function initialize() {
- tweakHTML();
- paintTargetWord();
- constructPitch();
- setUpDefToggle();
- formatFrequencyList();
- setDHHeight();
- hideCorrectDefinition();
- movePrimaryDicts();
- ensureFontSizeAdjustment();
- }
- // Run on load, resize, and as a fallback
- document.addEventListener('DOMContentLoaded', initialize);
- window.addEventListener('resize', adjustVocabFontSize);
- setTimeout(ensureFontSizeAdjustment, 0); // Immediate fallback
- initialize();
- </script>
- <script src="__persistence.js"></script>
- <script>
- // nsfw https://github.com/MarvNC/JP-Resources
- (function () {
- const nsfwDefaultPC = false;
- const nsfwDefaultMobile = false;
- const imageDiv = document.getElementById('main_image');
- const image = imageDiv.querySelector('a img');
- if (!image) {
- imageDiv.parentNode.removeChild(imageDiv);
- return; // Exit if no image
- }
- // Reset nsfwAllowed to false for each card load
- if (Persistence.isAvailable()) {
- Persistence.setItem('nsfwAllowed', false); // Always start blurred
- }
- let loaded = false;
- setInterval(() => {
- if (!loaded) {
- if (typeof Persistence === 'undefined') {
- return;
- }
- loaded = true;
- let onMobile = document.documentElement.classList.contains('mobile');
- let nsfwAllowed = onMobile ? nsfwDefaultMobile : nsfwDefaultPC;
- // Override with persisted value (should be false from reset)
- if (Persistence.isAvailable()) {
- nsfwAllowed = Persistence.getItem('nsfwAllowed') || nsfwAllowed;
- }
- setImageStyle(nsfwAllowed);
- }
- }, 50);
- })();
- function toggleNsfw() {
- if (Persistence.isAvailable()) {
- let nsfwAllowed = !!Persistence.getItem('nsfwAllowed');
- nsfwAllowed = !nsfwAllowed;
- Persistence.setItem('nsfwAllowed', nsfwAllowed);
- setImageStyle(nsfwAllowed);
- } else {
- setImageStyle(undefined, true);
- }
- }
- function setImageStyle(nsfwAllowed = undefined, toggle = false) {
- const imageDiv = document.getElementById('main_image');
- const image = imageDiv.querySelector('img');
- if (nsfwAllowed != undefined) {
- imageDiv.classList.toggle('nsfwAllowed', nsfwAllowed);
- } else if (toggle) {
- imageDiv.classList.toggle('nsfwAllowed');
- }
- }
- </script>
- </main>
Advertisement
Add Comment
Please, Sign In to add comment