Arabic-Story1 / script.js
Hma47's picture
Upload 14 files
1616c48 verified
Raw
History Blame Contribute Delete
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);
});
});