Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const $ = (selector) => document.querySelector(selector);
const modal = $('#modal');
const showModal = (title, message) => { $('#modalContent').innerHTML = `<h2>${title}</h2><p>${message}</p>`; modal.showModal(); };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.browser.security.insecure-document-method): استخدام بيانات يتحكم بها المستخدم في أساليب مثل innerHTML أو outerHTML أو document.write يُعد نمطًا سيئًا يمكن أن يؤدي إلى ثغرات XSS

Source: opengrep

Original comment in English

security (javascript.browser.security.insecure-document-method): User controlled data in methods like innerHTML, outerHTML or document.write is an anti-pattern that can lead to XSS vulnerabilities

Source: opengrep

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.browser.security.insecure-innerhtml): استخدام بيانات يتحكم بها المستخدم في $('#modalContent').innerHTML يُعد نمطًا سيئًا يمكن أن يؤدي إلى ثغرات XSS

Source: opengrep

Original comment in English

security (javascript.browser.security.insecure-innerhtml): User controlled data in a $('#modalContent').innerHTML is an anti-pattern that can lead to XSS vulnerabilities

Source: opengrep

$('.close').onclick = () => modal.close();

const readers = [
['مشاري راشد العفاسي', 'ar.alafasy'], ['ياسر الدوسري', 'ar.abdulbasitmurattal'],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Map reader labels to their actual recitation editions

Clicking the play controls for ياسر الدوسري، سعد الغامدي، or عبدالرحمن السديس currently streams Abdul Basit, Al-Husary, or Al-Minshawi respectively, because those are the editions assigned here. This makes the advertised reader list inaccurate for every entry except Alafasy; use the matching edition IDs or relabel the entries.

Useful? React with 👍 / 👎.

['سعد الغامدي', 'ar.husary'], ['عبدالرحمن السديس', 'ar.minshawi']
];
Comment on lines +6 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High app.js:6

The reciter names in the readers array are paired with other reciters' edition IDs: ياسر الدوسري maps to ar.abdulbasitmurattal (Abdul Basit), سعد الغامدي maps to ar.husary (Husary), and عبدالرحمن السديس maps to ar.minshawi (Minshawi). Clicking any of those play buttons plays audio from a different reciter than the name shown. The edition identifiers need to be corrected to match the actual reciter each name represents.

Suggested change
const readers = [
['مشاري راشد العفاسي', 'ar.alafasy'], ['ياسر الدوسري', 'ar.abdulbasitmurattal'],
['سعد الغامدي', 'ar.husary'], ['عبدالرحمن السديس', 'ar.minshawi']
];
+const readers = [
+ ['مشاري راشد العفاسي', 'ar.alafasy'], ['ياسر الدوسري', 'ar.yasseraldosri'],
+ ['سعد الغامدي', 'ar.saoodshuraym'], ['عبدالرحمن السديس', 'ar.abdurrahmaansudais']
+];
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @app.js around lines 6-9:

The reciter names in the `readers` array are paired with other reciters' edition IDs: `ياسر الدوسري` maps to `ar.abdulbasitmurattal` (Abdul Basit), `سعد الغامدي` maps to `ar.husary` (Husary), and `عبدالرحمن السديس` maps to `ar.minshawi` (Minshawi). Clicking any of those play buttons plays audio from a different reciter than the name shown. The edition identifiers need to be corrected to match the actual reciter each name represents.

Comment on lines +6 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Mismatch between reciter names and API edition identifiers.

The Arabic names in the readers array do not correspond to the assigned AlQuran Cloud API edition identifiers. This will cause the audio to play a different reciter's voice than the one displayed.

  • ar.abdulbasitmurattal maps to Abdul Basit Abdul Samad, not "ياسر الدوسري".
  • ar.husary maps to Mahmoud Khalil Al-Husary, not "سعد الغامدي".
  • ar.minshawi maps to Muhammad Siddiq Al-Minshawi, not "عبدالرحمن السديس".

Please update either the names to match the actual edition identifiers, or the identifiers to match the displayed names.

🛠️ Proposed fix (updating names to match identifiers)
 const readers = [
-  ['مشاري راشد العفاسي', 'ar.alafasy'], ['ياسر الدوسري', 'ar.abdulbasitmurattal'],
-  ['سعد الغامدي', 'ar.husary'], ['عبدالرحمن السديس', 'ar.minshawi']
+  ['مشاري راشد العفاسي', 'ar.alafasy'], ['عبدالباسط عبدالصمد', 'ar.abdulbasitmurattal'],
+  ['محمود خليل الحصري', 'ar.husary'], ['محمد صديق المنشاوي', 'ar.minshawi']
 ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const readers = [
['مشاري راشد العفاسي', 'ar.alafasy'], ['ياسر الدوسري', 'ar.abdulbasitmurattal'],
['سعد الغامدي', 'ar.husary'], ['عبدالرحمن السديس', 'ar.minshawi']
];
const readers = [
['مشاري راشد العفاسي', 'ar.alafasy'], ['عبدالباسط عبدالصمد', 'ar.abdulbasitmurattal'],
['محمود خليل الحصري', 'ar.husary'], ['محمد صديق المنشاوي', 'ar.minshawi']
];
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 9-9: Avoid assigning untrusted data to innerHTML/outerHTML or document.write
Context: $('#readersList').innerHTML = readers.map(([name, edition], index) => <div class="reader"><img src="https://i.pravatar.cc/80?img=${index + 12}" alt="${name}"><div><strong>${name}</strong><small>القرآن الكريم</small></div><button class="play" data-edition="${edition}" aria-label="استمع إلى ${name}">▶</button></div>).join('')
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(inner-outer-html)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.js` around lines 6 - 9, Update the reader entries in the readers array so
each Arabic display name matches its assigned AlQuran Cloud edition identifier,
especially ar.abdulbasitmurattal, ar.husary, and ar.minshawi. Preserve the
existing identifiers unless changing them is necessary to retain the intended
displayed reciters.

$('#readersList').innerHTML = readers.map(([name, edition], index) => `<div class="reader"><img src="https://i.pravatar.cc/80?img=${index + 12}" alt="${name}"><div><strong>${name}</strong><small>القرآن الكريم</small></div><button class="play" data-edition="${edition}" aria-label="استمع إلى ${name}">▶</button></div>`).join('');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.browser.security.insecure-document-method): استخدام بيانات يتحكم بها المستخدم في أساليب مثل innerHTML أو outerHTML أو document.write يُعد نمطًا سيئًا يمكن أن يؤدي إلى ثغرات XSS

Source: opengrep

Original comment in English

security (javascript.browser.security.insecure-document-method): User controlled data in methods like innerHTML, outerHTML or document.write is an anti-pattern that can lead to XSS vulnerabilities

Source: opengrep

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.browser.security.insecure-innerhtml): استخدام بيانات يتحكم بها المستخدم في $('#readersList').innerHTML يُعد نمطًا سيئًا يمكن أن يؤدي إلى ثغرات XSS

Source: opengrep

Original comment in English

security (javascript.browser.security.insecure-innerhtml): User controlled data in a $('#readersList').innerHTML is an anti-pattern that can lead to XSS vulnerabilities

Source: opengrep


let verse = { number: 53, audio: '' };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid playing the placeholder verse before data loads

If a visitor clicks a non-Alafasy reader before loadVerse() resolves, or after that request fails, playAudio builds its URL with this placeholder value and plays global ayah 53 rather than the displayed Al-Najm 53:49. Disable reader playback until the verse metadata is available, or initialize this with the correct global ayah number.

Useful? React with 👍 / 👎.

async function loadVerse() {
try {
const response = await fetch('https://api.alquran.cloud/v1/ayah/53:49/editions/ar.alafasy,ar.abdulbasitmurattal');
if (!response.ok) throw new Error('Quran API unavailable');
const { data } = await response.json();
const textEdition = data[0];
verse = { number: textEdition.number, audio: data[1]?.audio || textEdition.audio };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High app.js:19

loadVerse stores data[1].audio (the ar.abdulbasitmurattal recording) into verse.audio, but playAudio('ar.alafasy') later uses verse.audio as the Alafasy source. As a result, both the main listen button and Mishary Alafasy's reader button play Abdul Basit's recording instead of Alafasy's. The fix is to use data[0].audio for the default Alafasy audio, since data[0] is the first (ar.alafasy) edition.

Suggested change
verse = { number: textEdition.number, audio: data[1]?.audio || textEdition.audio };
verse = { number: textEdition.number, audio: data[0]?.audio || textEdition.audio };
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @app.js around line 19:

`loadVerse` stores `data[1].audio` (the `ar.abdulbasitmurattal` recording) into `verse.audio`, but `playAudio('ar.alafasy')` later uses `verse.audio` as the Alafasy source. As a result, both the main listen button and Mishary Alafasy's reader button play Abdul Basit's recording instead of Alafasy's. The fix is to use `data[0].audio` for the default Alafasy audio, since `data[0]` is the first (`ar.alafasy`) edition.

$('#quranText').textContent = textEdition.text;
$('#quranRef').textContent = `سورة ${textEdition.surah.name} — آية ${textEdition.numberInSurah}`;
} catch (error) { console.warn(error); }
}
async function loadPrayerTimes() {
const list = $('#prayerList');
try {
const response = await fetch('https://api.aladhan.com/v1/timingsByCity?city=Riyadh&country=Saudi%20Arabia&method=4');
if (!response.ok) throw new Error('Prayer API unavailable');
const { data } = await response.json();
const labels = { Fajr: 'الفجر', Dhuhr: 'الظهر', Asr: 'العصر', Maghrib: 'المغرب', Isha: 'العشاء' };
list.innerHTML = Object.entries(labels).map(([key, label]) => `<li><span>${label}</span><time>${data.timings[key]}</time></li>`).join('');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.browser.security.insecure-document-method): استخدام بيانات يتحكم بها المستخدم في أساليب مثل innerHTML أو outerHTML أو document.write يُعد نمطًا سيئًا يمكن أن يؤدي إلى ثغرات XSS

Source: opengrep

Original comment in English

security (javascript.browser.security.insecure-document-method): User controlled data in methods like innerHTML, outerHTML or document.write is an anti-pattern that can lead to XSS vulnerabilities

Source: opengrep

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (javascript.browser.security.insecure-innerhtml): استخدام بيانات يتحكم بها المستخدم في list.innerHTML يُعد نمطًا سيئًا يمكن أن يؤدي إلى ثغرات XSS

Source: opengrep

Original comment in English

security (javascript.browser.security.insecure-innerhtml): User controlled data in a list.innerHTML is an anti-pattern that can lead to XSS vulnerabilities

Source: opengrep

$('#hijriDate').textContent = `${data.date.hijri.weekday.ar}، ${data.date.hijri.day} ${data.date.hijri.month.ar} ${data.date.hijri.year} هـ`;
} catch (error) { $('#hijriDate').textContent = 'تعذر تحديث المواقيت حالياً'; console.warn(error); }
}
function playAudio(edition = 'ar.alafasy') {
const audio = $('#recitation');
Comment on lines +35 to +36

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: احتمال عدم التطابق أو استخدام رقم آية قديم إذا تم تشغيل الصوت قبل اكتمال تنفيذ loadVerse.

إذا قام المستخدم بتشغيل الصوت قبل أن تنتهي loadVerse، فستستمر playAudio في استخدام القيمة الافتراضية verse.number = 53 ثم تقوم بتغييرها بعد أن تحدّث استجابة الـ API هذه القيمة. يؤدي هذا إلى فترة قصيرة يكون فيها هناك عدم تطابق بين الآية المعروضة والصوت، خصوصًا في الإصدارات غير الافتراضية التي تستخدم رابط CDN. يُرجى إما منع التشغيل حتى يتم تحميل الآية بالكامل (مثلًا باستخدام علم verseLoaded) أو تهيئة verse.number برقم الآية الصحيح مسبقًا لتجنب هذا التعارض المؤقت.

Original comment in English

issue: Potential mismatch or stale verse number if audio is played before loadVerse completes.

If the user triggers playback before loadVerse completes, playAudio will still use the default verse.number = 53, then switch once the API response updates it. This creates a short mismatch between the visible ayah and the audio, particularly for non‑default editions using the CDN URL. Please either prevent playback until the verse is fully loaded (e.g., with a verseLoaded flag) or initialize verse.number to the correct ayah id up front to avoid this transient inconsistency.

const source = edition === 'ar.alafasy' ? verse.audio : `https://cdn.islamic.network/quran/audio/128/${edition}/${verse.number}.mp3`;
if (!source) return showModal('التلاوة', 'تجري تهيئة التلاوة، حاول مرة أخرى بعد لحظات.');
audio.src = source; audio.play().catch(() => showModal('التلاوة', 'يتطلب المتصفح تفاعلاً إضافياً لتشغيل الصوت.'));
}
Comment on lines +12 to +40

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Prevent playing the wrong verse when API data is missing.

verse.number is incorrectly initialized to 53 (the surah number). The Quran API uses absolute verse numbers across the entire Quran (e.g., 53:49 is absolute verse 4800). If the user clicks play on an alternative reciter before loadVerse finishes or if the API call fails, playAudio will construct a URL ending in /53.mp3, which incorrectly plays absolute verse 53 (from Surah Al-Baqarah).

Initialize verse.number to null and add a guard in playAudio to ensure data has finished loading before attempting playback.

🛠️ Proposed fix
-let verse = { number: 53, audio: '' };
+let verse = { number: null, audio: '' };
 async function loadVerse() {
   try {
     const response = await fetch('https://api.alquran.cloud/v1/ayah/53:49/editions/ar.alafasy,ar.abdulbasitmurattal');
     if (!response.ok) throw new Error('Quran API unavailable');
     const { data } = await response.json();
     const textEdition = data[0];
     verse = { number: textEdition.number, audio: data[1]?.audio || textEdition.audio };
     $('`#quranText`').textContent = textEdition.text;
     $('`#quranRef`').textContent = `سورة ${textEdition.surah.name} — آية ${textEdition.numberInSurah}`;
   } catch (error) { console.warn(error); }
 }
 async function loadPrayerTimes() {
   const list = $('`#prayerList`');
   try {
     const response = await fetch('https://api.aladhan.com/v1/timingsByCity?city=Riyadh&country=Saudi%20Arabia&method=4');
     if (!response.ok) throw new Error('Prayer API unavailable');
     const { data } = await response.json();
     const labels = { Fajr: 'الفجر', Dhuhr: 'الظهر', Asr: 'العصر', Maghrib: 'المغرب', Isha: 'العشاء' };
     list.innerHTML = Object.entries(labels).map(([key, label]) => `<li><span>${label}</span><time>${data.timings[key]}</time></li>`).join('');
     $('`#hijriDate`').textContent = `${data.date.hijri.weekday.ar}، ${data.date.hijri.day} ${data.date.hijri.month.ar} ${data.date.hijri.year} هـ`;
   } catch (error) { $('`#hijriDate`').textContent = 'تعذر تحديث المواقيت حالياً'; console.warn(error); }
 }
 function playAudio(edition = 'ar.alafasy') {
+  if (!verse.number) return showModal('التلاوة', 'تجري تهيئة التلاوة، حاول مرة أخرى بعد لحظات.');
   const audio = $('`#recitation`');
   const source = edition === 'ar.alafasy' ? verse.audio : `https://cdn.islamic.network/quran/audio/128/${edition}/${verse.number}.mp3`;
   if (!source) return showModal('التلاوة', 'تجري تهيئة التلاوة، حاول مرة أخرى بعد لحظات.');
   audio.src = source; audio.play().catch(() => showModal('التلاوة', 'يتطلب المتصفح تفاعلاً إضافياً لتشغيل الصوت.'));
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let verse = { number: 53, audio: '' };
async function loadVerse() {
try {
const response = await fetch('https://api.alquran.cloud/v1/ayah/53:49/editions/ar.alafasy,ar.abdulbasitmurattal');
if (!response.ok) throw new Error('Quran API unavailable');
const { data } = await response.json();
const textEdition = data[0];
verse = { number: textEdition.number, audio: data[1]?.audio || textEdition.audio };
$('#quranText').textContent = textEdition.text;
$('#quranRef').textContent = `سورة ${textEdition.surah.name} — آية ${textEdition.numberInSurah}`;
} catch (error) { console.warn(error); }
}
async function loadPrayerTimes() {
const list = $('#prayerList');
try {
const response = await fetch('https://api.aladhan.com/v1/timingsByCity?city=Riyadh&country=Saudi%20Arabia&method=4');
if (!response.ok) throw new Error('Prayer API unavailable');
const { data } = await response.json();
const labels = { Fajr: 'الفجر', Dhuhr: 'الظهر', Asr: 'العصر', Maghrib: 'المغرب', Isha: 'العشاء' };
list.innerHTML = Object.entries(labels).map(([key, label]) => `<li><span>${label}</span><time>${data.timings[key]}</time></li>`).join('');
$('#hijriDate').textContent = `${data.date.hijri.weekday.ar}، ${data.date.hijri.day} ${data.date.hijri.month.ar} ${data.date.hijri.year} هـ`;
} catch (error) { $('#hijriDate').textContent = 'تعذر تحديث المواقيت حالياً'; console.warn(error); }
}
function playAudio(edition = 'ar.alafasy') {
const audio = $('#recitation');
const source = edition === 'ar.alafasy' ? verse.audio : `https://cdn.islamic.network/quran/audio/128/${edition}/${verse.number}.mp3`;
if (!source) return showModal('التلاوة', 'تجري تهيئة التلاوة، حاول مرة أخرى بعد لحظات.');
audio.src = source; audio.play().catch(() => showModal('التلاوة', 'يتطلب المتصفح تفاعلاً إضافياً لتشغيل الصوت.'));
}
let verse = { number: null, audio: '' };
async function loadVerse() {
try {
const response = await fetch('https://api.alquran.cloud/v1/ayah/53:49/editions/ar.alafasy,ar.abdulbasitmurattal');
if (!response.ok) throw new Error('Quran API unavailable');
const { data } = await response.json();
const textEdition = data[0];
verse = { number: textEdition.number, audio: data[1]?.audio || textEdition.audio };
$('`#quranText`').textContent = textEdition.text;
$('`#quranRef`').textContent = `سورة ${textEdition.surah.name} — آية ${textEdition.numberInSurah}`;
} catch (error) { console.warn(error); }
}
async function loadPrayerTimes() {
const list = $('`#prayerList`');
try {
const response = await fetch('https://api.aladhan.com/v1/timingsByCity?city=Riyadh&country=Saudi%20Arabia&method=4');
if (!response.ok) throw new Error('Prayer API unavailable');
const { data } = await response.json();
const labels = { Fajr: 'الفجر', Dhuhr: 'الظهر', Asr: 'العصر', Maghrib: 'المغرب', Isha: 'العشاء' };
list.innerHTML = Object.entries(labels).map(([key, label]) => `<li><span>${label}</span><time>${data.timings[key]}</time></li>`).join('');
$('`#hijriDate`').textContent = `${data.date.hijri.weekday.ar}، ${data.date.hijri.day} ${data.date.hijri.month.ar} ${data.date.hijri.year} هـ`;
} catch (error) { $('`#hijriDate`').textContent = 'تعذر تحديث المواقيت حالياً'; console.warn(error); }
}
function playAudio(edition = 'ar.alafasy') {
if (!verse.number) return showModal('التلاوة', 'تجري تهيئة التلاوة، حاول مرة أخرى بعد لحظات.');
const audio = $('`#recitation`');
const source = edition === 'ar.alafasy' ? verse.audio : `https://cdn.islamic.network/quran/audio/128/${edition}/${verse.number}.mp3`;
if (!source) return showModal('التلاوة', 'تجري تهيئة التلاوة، حاول مرة أخرى بعد لحظات.');
audio.src = source; audio.play().catch(() => showModal('التلاوة', 'يتطلب المتصفح تفاعلاً إضافياً لتشغيل الصوت.'));
}
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 30-30: Avoid assigning untrusted data to innerHTML/outerHTML or document.write
Context: list.innerHTML = Object.entries(labels).map(([key, label]) => <li><span>${label}</span><time>${data.timings[key]}</time></li>).join('')
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

(inner-outer-html)

🪛 OpenGrep (1.25.0)

[WARNING] 31-31: Setting innerHTML with dynamic content can lead to XSS. Use textContent or createElement with proper escaping instead.

(coderabbit.xss.innerhtml-assignment)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.js` around lines 12 - 40, Initialize verse.number as null instead of 53,
and update playAudio to return the existing preparation modal before
constructing any reciter URL when the verse number has not been loaded. Preserve
normal playback once loadVerse successfully assigns the API’s absolute verse
number.

$('#listenButton').onclick = () => playAudio();
document.addEventListener('click', (event) => { if (event.target.matches('.play')) playAudio(event.target.dataset.edition); });
$('#refreshPrayer').onclick = loadPrayerTimes;
$('#dhikrButton').onclick = () => showModal('أذكار اليوم', 'سبحان الله، والحمد لله، ولا إله إلا الله، والله أكبر. اجعل لسانك رطباً بذكر الله.');
$('#zakatButton').onclick = () => showModal('حاسبة الزكاة', 'تجب الزكاة عند بلوغ النصاب وحولان الحول، ومقدارها ٢.٥٪ من المال الزكوي. استشر جهة إفتاء موثوقة للحالات الخاصة.');
$('#qiblaButton').onclick = () => { window.open('https://qiblafinder.withgoogle.com/intl/ar/desktop', '_blank', 'noopener'); };
$('#subscribeForm').onsubmit = (event) => { event.preventDefault(); event.target.reset(); showModal('تم الاشتراك', 'شكراً لك، سيصلك التذكير والمحتوى المفيد إلى بريدك قريباً.'); };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist newsletter subscriptions before confirming them

Submitting a valid email only resets the form and displays a success message; it never sends or stores the address anywhere. Consequently, every subscriber is told that reminders will arrive even though no subscription exists, so this handler needs a real submission endpoint (and should report failure when it cannot save the address).

Useful? React with 👍 / 👎.

loadVerse(); loadPrayerTimes();
55 changes: 55 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!doctype html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#061712" />
<title>الذاكرون | منصة إسلامية</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Amiri:wght@400;700&family=Cairo:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="page-glow"></div>
<header class="topbar wrap">
<a class="brand" href="#home"><span class="brand-mark">۞</span><span>الذّاكِرون<small>منصة إسلامية عصرية هادفة</small></span></a>
<nav aria-label="التنقل الرئيسي"><a class="active" href="#home">الرئيسية</a><a href="#quran">القرآن الكريم</a><a href="#adhkar">الأذكار</a><a href="#prayer">مواقيت الصلاة</a><a href="#qibla">القبلة</a><a href="#articles">المقالات</a></nav>
<div class="header-actions"><button class="icon-button" aria-label="بحث">⌕</button><button class="language">العربية ◉</button></div>
</header>

<main id="home">
<section class="hero wrap">
<div class="hero-art" aria-label="مصحف وفانوس تحت هلال"><div class="moon"></div><div class="arch"></div><div class="lantern">✦</div><div class="quran-book">۞</div></div>
<div class="hero-copy">
<p class="eyebrow">بِسْمِ اللهِ الرَّحْمٰنِ الرَّحِيمِ</p>
<h1>الذّاكِرون</h1><h2>صدقة جارية مباركة</h2>
<p class="hero-verse" id="heroVerse">مَثَلُ الَّذِينَ يُنفِقُونَ أَمْوَالَهُمْ فِي سَبِيلِ اللَّهِ كَمَثَلِ حَبَّةٍ أَنْبَتَتْ سَبْعَ سَنَابِلَ</p>
<p class="verse-source" id="heroSource">سورة البقرة — ٢٦١</p>
<div class="hero-buttons"><a class="button gold" href="#donate">♡ ادعم بصدقة جارية</a><a class="button green" href="#quran">استكشف المزيد ۞</a></div>
</div>
<aside class="goodness-card"><span class="sprout">♧</span><h3>ساهم في نشر الخير</h3><p>كن سبباً في هداية قلب أو علمٍ ينتفع به، وهو صدقة جارية لا تنقطع بإذن الله.</p><a href="#donate">تعرّف على مشاريعنا</a></aside>
</section>

<section class="feature-strip wrap" aria-label="أقسام المنصة"><a href="#quran">▣<span>نشر القرآن</span></a><a href="#donate">♜<span>دعم المشاريع</span></a><a href="#donate">♧<span>كفالة المحتاجين</span></a><a href="#donate">♧<span>بناء المستقبل</span></a><a href="#articles">▤<span>نشر العلم</span></a></section>

<section class="dashboard wrap">
<article class="card readers"><div class="card-title"><h2>أشهر القراء</h2><a href="#quran">عرض الكل ‹</a></div><div id="readersList"></div></article>
<article class="card quran-card" id="quran"><div class="quran-cover">۞</div><div><h2>القرآن الكريم <span>▣</span></h2><blockquote id="quranText">وَأَنَّهُ هُوَ رَبُّ الشِّعْرَىٰ</blockquote><p id="quranRef">سورة النجم — ٤٩</p><div class="quran-actions"><button class="button gold" id="listenButton">▶ استمع للآية</button><a class="button ghost" target="_blank" rel="noreferrer" href="https://quran.com">المصحف كامل</a></div></div></article>
<article class="card prayer-card" id="prayer"><div class="card-title"><h2>مواقيت الصلاة</h2><span class="location" id="prayerLocation">الرياض، السعودية</span></div><div class="date" id="hijriDate">جاري التحديث…</div><ul id="prayerList"><li><span>الفجر</span><time>--:--</time></li><li><span>الظهر</span><time>--:--</time></li><li><span>العصر</span><time>--:--</time></li><li><span>المغرب</span><time>--:--</time></li><li><span>العشاء</span><time>--:--</time></li></ul><button class="full-link" id="refreshPrayer">↻ تحديث المواقيت</button></article>
</section>

<section class="tiles wrap" id="articles">
<article class="tile article"><span class="tile-visual">✒</span><div><h2>المقالات</h2><p>مقالات إسلامية مفيدة لتنير قلبك وعقلك</p><a class="button green" href="https://islamqa.info/ar" target="_blank" rel="noreferrer">قراءة المقالات</a></div></article>
<article class="tile qibla" id="qibla"><span class="tile-visual">⌁</span><div><h2>القبلة</h2><p>حدد اتجاه القبلة بدقة أينما كنت</p><button class="button green" id="qiblaButton">عرض القبلة ◉</button></div></article>
<article class="tile adhkar" id="adhkar"><span class="tile-visual">◌</span><div><h2>الأذكار</h2><p>أذكار الصباح والمساء والأذكار اليومية</p><button class="button green" id="dhikrButton">استعرض الأذكار</button></div></article>
<article class="tile charity" id="donate"><span class="tile-visual">♧</span><div><h2>الزكاة والصدقة</h2><p>احسب زكاتك وتبرع بسهولة للمشاريع الخيرية الموثوقة</p><button class="button green" id="zakatButton">احسب الزكاة</button></div></article>
</section>

<section class="newsletter wrap"><div class="mail">✉</div><div><h2>اشترك في نشرتنا البريدية</h2><p>احصل على آيات يومية وأحاديث وتذكير بمحتوى إسلامي مفيد</p></div><form id="subscribeForm"><input required type="email" aria-label="بريدك الإلكتروني" placeholder="أدخل بريدك الإلكتروني"><button class="button gold">اشترك الآن</button></form></section>
</main>
<footer class="wrap"><div class="footer-brand brand"><span class="brand-mark">۞</span><span>الذّاكِرون<small>منصة إسلامية تهدف إلى نشر الذكر وتعليم القرآن ودعم المشاريع الخيرية</small></span></div><div><h3>المشاريع</h3><a href="#donate">المشاريع الحالية</a><a href="#donate">تقارير المشاريع</a><a href="#donate">كن شريكاً</a></div><div><h3>روابط سريعة</h3><a href="#quran">القرآن الكريم</a><a href="#adhkar">الأذكار</a><a href="#prayer">مواقيت الصلاة</a></div><div><h3>المزيد</h3><a href="#home">عن الذاكرون</a><a href="#articles">اتصل بنا</a><a href="#articles">سياسة الخصوصية</a></div><p class="copyright">جميع الحقوق محفوظة © ٢٠٢۶ الذّاكرون ♥</p></footer>
<dialog id="modal"><button class="close" aria-label="إغلاق">×</button><div id="modalContent"></div></dialog><audio id="recitation" preload="none"></audio>
<script src="app.js"></script>
</body>
</html>
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "aldhakiroun",
"version": "1.0.0",
"private": true,
"scripts": { "start": "vite --host 0.0.0.0", "build": "vite", "preview": "vite preview --host 0.0.0.0" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Fix the Vite build script.

The build script is currently set to "vite", which starts the development server instead of building the project for production. It should be changed to "vite build".

🛠️ Proposed fix
-  "scripts": { "start": "vite --host 0.0.0.0", "build": "vite", "preview": "vite preview --host 0.0.0.0" },
+  "scripts": { "start": "vite --host 0.0.0.0", "build": "vite build", "preview": "vite preview --host 0.0.0.0" },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"scripts": { "start": "vite --host 0.0.0.0", "build": "vite", "preview": "vite preview --host 0.0.0.0" },
"scripts": { "start": "vite --host 0.0.0.0", "build": "vite build", "preview": "vite preview --host 0.0.0.0" },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 5, Update the package.json scripts object so the build
script invokes Vite’s production build command, changing the value from "vite"
to "vite build"; leave the start and preview scripts unchanged.

"devDependencies": { "vite": "^6.1.0" }
}
1 change: 1 addition & 0 deletions style.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.