Spaces:
Running
Running
Download script.js from Hma47/Arabic-Story1: direct link, hf CLI and curl.
- Browser
- Download file 15.8 kB
-
https://huggingface.co/spaces/Hma47/Arabic-Story1/resolve/main/script.js
- Command line
-
hf download hf://spaces/Hma47/Arabic-Story1/script.js
-
curl -L -o script.js https://huggingface.co/spaces/Hma47/Arabic-Story1/resolve/main/script.js
15.8 kB
| const storyData = [ | |
| { start: 0.000, end: 12.693, text: "ุชููููู ุณูููุฏูููุงููุฉู ๐ณ ุนูุงููููุฉู ุนูููู ู ูุฑูุชูููุนูุ ุชูุดูุฑููู ุนูููู ููููุฑู ๐ ุบูุฒููุฑู ุงููู ูููุงููุ ุฃูุบูุตูุงููููุง ๐ ููุซููููุฉูุ ููุฃูููุฑูุงููููุง ุฎูุถูุฑูุงุกูุ ููุณูุงููููุง ุทููููููุฉู ููุงููู ูุฆูุฐูููุฉู ููู ุงููุงุฑูุชูููุงุนู." }, | |
| { start: 12.693, end: 20.742, text: "ุชูุชูููููููู ุนูููููููุง ุงููุนูุตูุงูููุฑู ๐ฆ ู ูุบูุฑููุฏูุฉูุ ููููุฌูููุณู ุนูููุฏูููุง ุงูุฑููุนูุงุฉู ููุงูุตูููููุงุฏูููู ููููุฑูุชูุงุญููุง ู ููู ุงูุชููุนูุจู." }, | |
| { start: 20.742, end: 29.782, text: "ููุชูุนููุดู ููุตูุจูุฉู ๐พ ู ูุนู ุฑููููููุชูููุงุ ุนูููู ุถููููุฉู ููุฐูุง ุงููููููุฑูุ ุชูููุธูุฑู ุจูุฎูููุฑู ููู ูููุงูููู ุงููุฌูุงุฑูููุฉูุ ููุชูุชูู ูุงูููู ู ูุนู ุงููููููุงุกู." }, | |
| { start: 29.782, end: 35.045, text: "ููุงููุชู ุงูุณููููุฏูููุงููุฉู ู ูุฑููุฉู ููููููุตูุจูุฉู: ู ูุง ุฃูุญูููุฑููู ุฃููููุชูููุง ุงููููุตูุจูุฉู!" }, | |
| { start: 35.045, end: 42.599, text: "ุชูุนููุดูููู ููู ููุฐูุง ุงููู ูููุฎูููุถูุ ููุชูุฎูุชูููููู ููุฑูุงุกููู ุทููููุฉู ุงููููููุงุฑูุ ููููุง ุชูุฑููููู ุงูููููุฑูุ ููู ูุง ุฃูุถูุนููููู!" }, | |
| { start: 42.599, end: 48.233, text: "ููุง ุชูุณูุชูุทููุนูููู ุฃููู ุชูุญูู ูููู ุนูุตููููุฑูุงุ ุญููููุง ุฅูููู ุงูุทููุจููุนูุฉู ููุงุณูููุฉู ุนููููููู." }, | |
| { start: 48.233, end: 54.611, text: "ููุงููุชู ููููุง ุงููููุตูุจูุฉู: ุฃูููุชู ู ูุบูุฑููุฑูุฉู ุจูููููุณูููุ ููุบูุฑููุฑููู ๐ ููุฐูุง ุณููููู ููููุถูู ุนููููููู." }, | |
| { start: 54.611, end: 74.177, text: "ููุจููุชู ุนูุงุตูููุฉู ๐ช๏ธ ูููููููุฉูุ ููุชูุฃูุฑูุฌูุญูุชู ุงููููุตูุจูุฉู ู ูุนู ุงููุนูุงุตูููุฉูุ ููุชูู ููููููุชู ุจูููุถููู ุฐููููู ู ููู ุงูุซููุจูุงุชูุ ูููููู ุชูู ูููู ููู ููููุง ููููุณูุงุฑูุงุ ุฃูู ููุง ุงูุณููููุฏูููุงููุฉู ููููู ู ุชูุณูุชูุทูุนู ุฃููู ุชูููุงููู ู ุงููุนูุงุตูููุฉูุ ููุชูููุณููุฑูุชู ุฃูุบูุตูุงููููุงุ ููุงููุชูููุนูุชูููุง ุงููุนูุงุตูููุฉู ุจูุฌูุฐูุนูููุง ๐ณ ุงูุถููุฎูู ู." }, | |
| { start: 74.177, end: 83.000, text: "ููุธูุฑูุชู ุงููููุตูุจูุฉู ุจูุนูุทููู ุฅูููู ุงูุณููููุฏูููุงููุฉูุ ููููุงููุชู ููููุง: ููุฐููู ููุชููุฌูุฉู ุบูุฑููุฑูููุ ููุงุญูุชูููุงุฑููู ู ููู ูููู ุฃูุตูุบูุฑู ู ููููู." } | |
| ]; | |
| const translations = { | |
| "ุณูููุฏูููุงููุฉู": "Oak tree", | |
| "ู ูุฑูุชูููุนู": "High ground", | |
| "ููููุฑู": "River", | |
| "ุบูุฒููุฑู": "Abundant", | |
| "ุฃูุบูุตูุงููููุง": "Its branches", | |
| "ููุซููููุฉู": "Dense", | |
| "ุฃูููุฑูุงููููุง": "Its leaves", | |
| "ุฎูุถูุฑูุงุกู": "Green", | |
| "ุณูุงููููุง": "Its trunk", | |
| "ุทููููููุฉู": "Tall", | |
| "ููุงููู ูุฆูุฐูููุฉู": "Like a minaret", | |
| "ุงููุงุฑูุชูููุงุนู": "Height", | |
| "ุชูุชูููููููู": "Move around", | |
| "ุงููุนูุตูุงูููุฑู": "Birds", | |
| "ู ูุบูุฑููุฏูุฉู": "Chirping", | |
| "ุงูุฑููุนูุงุฉู": "Shepherds", | |
| "ููุงูุตูููููุงุฏูููู": "And hunters", | |
| "ููููุฑูุชูุงุญููุง": "To rest", | |
| "ุงูุชููุนูุจู": "Fatigue", | |
| "ููุตูุจูุฉู": "Reed", | |
| "ุฑููููููุชูููุง": "Its companion", | |
| "ุถููููุฉู": "Bank (of river)", | |
| "ุจูุฎูููุฑู": "Shyly", | |
| "ููู ูููุงูููู": "To its waters", | |
| "ุงููุฌูุงุฑูููุฉู": "Running", | |
| "ููุชูุชูู ูุงูููู": "And sways", | |
| "ุงููููููุงุกู": "The air / wind", | |
| "ุฃูุญูููุฑููู": "How despicable you are", | |
| "ุงููู ูููุฎูููุถู": "Lowland", | |
| "ููุชูุฎูุชูููููู": "And you hide", | |
| "ููุฑูุงุกููู": "Behind it", | |
| "ุทููููุฉู": "Throughout", | |
| "ุงููููููุงุฑู": "The day", | |
| "ุงูููููุฑู": "The light", | |
| "ุฃูุถูุนููููู": "How weak you are", | |
| "ุชูุญูู ูููู": "Carry", | |
| "ุนูุตููููุฑูุง": "A bird", | |
| "ุงูุทููุจููุนูุฉู": "Nature", | |
| "ููุงุณูููุฉู": "Cruel / Harsh", | |
| "ู ูุบูุฑููุฑูุฉู": "Arrogant", | |
| "ุจูููููุณููู": "With yourself", | |
| "ููุบูุฑููุฑููู": "And your arrogance", | |
| "ููููุถูู": "Will destroy / Finish", | |
| "ููุจููุชู": "Blew", | |
| "ุนูุงุตูููุฉู": "Storm", | |
| "ูููููููุฉู": "Strong", | |
| "ููุชูุฃูุฑูุฌูุญูุชู": "So it swayed", | |
| "ููุชูู ููููููุชู": "And it was able", | |
| "ุจูููุถููู": "Thanks to", | |
| "ุงูุซููุจูุงุชู": "Steadiness / Stability", | |
| "ุชูู ูููู": "Leans / Bends", | |
| "ููู ููููุง": "Right", | |
| "ููููุณูุงุฑูุง": "And left", | |
| "ุชูููุงููู ู": "Resist", | |
| "ููุชูููุณููุฑูุชู": "So they broke", | |
| "ููุงููุชูููุนูุชูููุง": "And uprooted it", | |
| "ุจูุฌูุฐูุนูููุง": "By its trunk", | |
| "ุงูุถููุฎูู ู": "Huge", | |
| "ุจูุนูุทููู": "With sympathy", | |
| "ููุชููุฌูุฉู": "Result", | |
| "ุบูุฑููุฑููู": "Your arrogance", | |
| "ููุงุญูุชูููุงุฑููู": "And your contempt" | |
| }; | |
| const quizData = [ | |
| { | |
| question: "ุฃูู ุชุนูุด ุงูุณูุฏูุงูุฉุ (Where does the oak tree live?)", | |
| options: [ | |
| { text: "ุนูู ุถูุฉ ุงูููุฑ (On the riverbank)", isCorrect: false }, | |
| { text: "ูู ู ูุฎูุถ (In a lowland)", isCorrect: false }, | |
| { text: "ุนูู ู ุฑุชูุน (On a high ground)", isCorrect: true } | |
| ] | |
| }, | |
| { | |
| question: "ู ุงุฐุง ุญุฏุซ ููุณูุฏูุงูุฉ ุนูุฏู ุง ูุจุช ุงูุนุงุตูุฉุ (What happened to the oak tree when the storm blew?)", | |
| options: [ | |
| { text: "ุชุฃุฑุฌุญุช ู ุน ุงูุนุงุตูุฉ (Swayed with the storm)", isCorrect: false }, | |
| { text: "ุงูุชูุนุชูุง ุงูุนุงุตูุฉ (The storm uprooted it)", isCorrect: true }, | |
| { text: "ูุงูู ุช ุงูุนุงุตูุฉ (Resisted the storm)", isCorrect: false } | |
| ] | |
| }, | |
| { | |
| question: "ู ุง ูู ุงูุญูู ุฉ ู ู ุงููุตุฉุ (What is the moral of the story?)", | |
| options: [ | |
| { text: "ุงูุทุจูุนุฉ ูุงุณูุฉ (Nature is harsh)", isCorrect: false }, | |
| { text: "ุงูุบุฑูุฑ ููุถู ุนูู ุตุงุญุจู (Arrogance destroys its owner)", isCorrect: true }, | |
| { text: "ุงูููุฑ ุฌู ูู (The river is beautiful)", isCorrect: false } | |
| ] | |
| } | |
| ]; | |
| document.addEventListener("DOMContentLoaded", () => { | |
| const textContentEl = document.getElementById("text-content"); | |
| const audioEl = document.getElementById("story-audio"); | |
| const playBtn = document.getElementById("play-btn"); | |
| const resetBtn = document.getElementById("reset-btn"); | |
| const progressBar = document.getElementById("progress-bar"); | |
| const progressContainer = document.getElementById("progress-container"); | |
| const tooltip = document.getElementById("word-tooltip"); | |
| const tooltipAr = document.getElementById("tooltip-ar"); | |
| const tooltipEn = document.getElementById("tooltip-en"); | |
| const tooltipArrow = document.getElementById("tooltip-arrow"); | |
| const quizContainer = document.getElementById("quiz-container"); | |
| let isPlaying = false; | |
| // Remove any character that is not an Arabic letter, diacritic or space | |
| function cleanWord(word) { | |
| return word.replace(/[^\u0600-\u06FF\s]/g, '').trim(); | |
| } | |
| // Render Text | |
| storyData.forEach((section, index) => { | |
| const wrapper = document.createElement("div"); | |
| wrapper.className = "paragraph-wrapper"; | |
| const imgName = index === 5 ? "Patagraph 6.png" : `paragraph ${index + 1}.png`; | |
| const img = document.createElement("img"); | |
| img.src = imgName; | |
| img.className = "paragraph-image"; | |
| img.alt = `ุตูุฑุฉ ููููุฑุฉ ${index + 1}`; | |
| wrapper.appendChild(img); | |
| const sentenceSpan = document.createElement("div"); | |
| sentenceSpan.className = "sentence"; | |
| sentenceSpan.id = `sentence-${index}`; | |
| sentenceSpan.dataset.start = section.start; | |
| sentenceSpan.dataset.end = section.end; | |
| const words = section.text.split(" "); | |
| words.forEach((word) => { | |
| const wordSpan = document.createElement("span"); | |
| const clean = cleanWord(word); | |
| wordSpan.className = "word"; | |
| wordSpan.innerText = word + " "; | |
| if (translations[clean]) { | |
| wordSpan.classList.add("has-translation"); | |
| wordSpan.addEventListener("click", (e) => { | |
| e.stopPropagation(); | |
| const rect = wordSpan.getBoundingClientRect(); | |
| tooltipAr.innerText = clean; | |
| tooltipEn.innerText = translations[clean]; | |
| // Force layout measurement | |
| const tHeight = tooltip.offsetHeight; | |
| const tWidth = tooltip.offsetWidth; | |
| // Calculate center position | |
| const centerX = rect.left + window.scrollX + (rect.width / 2); | |
| let leftPos = centerX; | |
| // Keep tooltip within screen bounds | |
| const padding = 15; | |
| const minLeft = padding + (tWidth / 2); | |
| const maxLeft = window.innerWidth - padding - (tWidth / 2); | |
| if (leftPos < minLeft) leftPos = minLeft; | |
| if (leftPos > maxLeft) leftPos = maxLeft; | |
| tooltip.style.left = `${leftPos}px`; | |
| tooltip.style.top = `${rect.top + window.scrollY - tHeight - 15}px`; | |
| // Adjust arrow to point directly at the word, even if tooltip is shifted | |
| let arrowOffset = centerX - leftPos; | |
| const maxArrowOffset = (tWidth / 2) - 15; | |
| if (arrowOffset > maxArrowOffset) arrowOffset = maxArrowOffset; | |
| if (arrowOffset < -maxArrowOffset) arrowOffset = -maxArrowOffset; | |
| tooltipArrow.style.left = `calc(50% + ${arrowOffset}px)`; | |
| tooltip.classList.add("show"); | |
| }); | |
| } | |
| sentenceSpan.appendChild(wordSpan); | |
| }); | |
| wrapper.appendChild(sentenceSpan); | |
| textContentEl.appendChild(wrapper); | |
| }); | |
| // Audio Play/Pause | |
| playBtn.addEventListener("click", () => { | |
| if (isPlaying) { | |
| audioEl.pause(); | |
| playBtn.innerHTML = '<svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M8 5v14l11-7z"/></svg> ุงุณุชู ุน ูููุตุฉ'; | |
| } else { | |
| audioEl.play(); | |
| playBtn.innerHTML = '<svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg> ุฅููุงู'; | |
| } | |
| isPlaying = !isPlaying; | |
| }); | |
| audioEl.addEventListener("ended", () => { | |
| isPlaying = false; | |
| playBtn.innerHTML = '<svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M8 5v14l11-7z"/></svg> ุงุณุชู ุน ูููุตุฉ'; | |
| progressBar.style.width = "0%"; | |
| }); | |
| // Reset Functionality | |
| resetBtn.addEventListener("click", () => { | |
| audioEl.pause(); | |
| audioEl.currentTime = 0; | |
| isPlaying = false; | |
| playBtn.innerHTML = '<svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M8 5v14l11-7z"/></svg> ุงุณุชู ุน ูููุตุฉ'; | |
| progressBar.style.width = "0%"; | |
| document.querySelectorAll(".sentence").forEach(el => el.classList.remove("active")); | |
| document.querySelectorAll(".option-btn").forEach(btn => { | |
| btn.classList.remove("correct", "wrong"); | |
| btn.disabled = false; | |
| }); | |
| document.querySelectorAll(".feedback").forEach(fb => { | |
| fb.classList.remove("show"); | |
| }); | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| }); | |
| // Sync Audio with Text | |
| audioEl.addEventListener("timeupdate", () => { | |
| const currentTime = audioEl.currentTime; | |
| const duration = audioEl.duration; | |
| if (duration) { | |
| progressBar.style.width = `${(currentTime / duration) * 100}%`; | |
| } | |
| storyData.forEach((section, index) => { | |
| const sentenceEl = document.getElementById(`sentence-${index}`); | |
| if (currentTime >= section.start && currentTime < section.end) { | |
| sentenceEl.classList.add("active"); | |
| } else { | |
| sentenceEl.classList.remove("active"); | |
| } | |
| }); | |
| }); | |
| // Seek Audio | |
| progressContainer.addEventListener("click", (e) => { | |
| const rect = progressContainer.getBoundingClientRect(); | |
| const pos = (e.clientX - rect.left) / rect.width; | |
| audioEl.currentTime = pos * audioEl.duration; | |
| }); | |
| // Hide Tooltip on document interactions | |
| document.addEventListener("click", () => { | |
| tooltip.classList.remove("show"); | |
| }); | |
| window.addEventListener("scroll", () => { | |
| tooltip.classList.remove("show"); | |
| }); | |
| window.addEventListener("resize", () => { | |
| tooltip.classList.remove("show"); | |
| }); | |
| // Render Quiz | |
| quizData.forEach((q, index) => { | |
| const card = document.createElement("div"); | |
| card.className = "question-card"; | |
| const qText = document.createElement("div"); | |
| qText.className = "question-text"; | |
| qText.innerText = q.question; | |
| card.appendChild(qText); | |
| const optionsContainer = document.createElement("div"); | |
| optionsContainer.className = "options-container"; | |
| const feedback = document.createElement("div"); | |
| feedback.className = "feedback"; | |
| q.options.forEach(opt => { | |
| const btn = document.createElement("button"); | |
| btn.className = "option-btn"; | |
| btn.innerText = opt.text; | |
| btn.addEventListener("click", () => { | |
| // Remove existing classes | |
| Array.from(optionsContainer.children).forEach(c => { | |
| c.classList.remove("correct", "wrong"); | |
| c.disabled = true; | |
| }); | |
| if (opt.isCorrect) { | |
| btn.classList.add("correct"); | |
| feedback.innerText = "ุฅุฌุงุจุฉ ุตุญูุญุฉ! (Correct Answer!)"; | |
| feedback.style.color = "#155724"; | |
| } else { | |
| btn.classList.add("wrong"); | |
| feedback.innerText = "ุฅุฌุงุจุฉ ุฎุงุทุฆุฉ (Wrong Answer). ุญุงูู ู ุฑุฉ ุฃุฎุฑู."; | |
| feedback.style.color = "#721c24"; | |
| // Highlight correct answer | |
| Array.from(optionsContainer.children).forEach((c, i) => { | |
| if (q.options[i].isCorrect) c.classList.add("correct"); | |
| }); | |
| } | |
| feedback.classList.add("show"); | |
| }); | |
| optionsContainer.appendChild(btn); | |
| }); | |
| card.appendChild(optionsContainer); | |
| card.appendChild(feedback); | |
| quizContainer.appendChild(card); | |
| }); | |
| }); | |