بحث
أسلوب FileFix يتجاوز حماية المتصفحات الحديثة
أخرى #FileFix #ClickFix

أسلوب FileFix يتجاوز حماية المتصفحات الحديثة

تاريخ النشر: آخر تحديث: 91 مشاهدة 0 تعليق 5 دقائق قراءة
91 مشاهدة
0 إعجاب
0 تعليق
موثوق 95%

تم اكتشاف أسلوب هجوم جديد يستغل تقنية ClickFix من خلال استغلال كيفية حفظ المتصفحات الحديثة لملفات HTML، متجاوزًا آلية أمان رئيسية، مما يُخدع المستخدمين للمشاركة في توصيل محتوى ضار عبر هجمات تصيد بنقرة واحدة.

أطلق على هذا الأسلوب اسم FileFix 2.0 من قبل mr.d0x، وهو مختبر اختراق وباحث أمني اكتشفه، حيث يقوم هذا الأسلوب بالتلاعب بكيفية حفظ المتصفحات لمحتوى HTML بطريقة قد تجعل المستخدمين يقومون عن غير قصد بتنزيل ملفات ضارة على أجهزتهم، مما يؤدي إلى حدوث هجوم.

شارك الباحث مؤخرًا منشورين منفصلين على مدونته حول FileFix، موضحًا كيف تستخدم نسختان من أسلوب الهجوم تقنية bait-and-switch مع متصفحات حديثة مثل جوجل كروم ومايكروسوفت إيدج. يعتمد الهجوم أساسًا على كيفية حفظ ملفات HTML بدون بيانات وصفية رئيسية قد تُنبه الأشخاص إلى نشاط مشبوه، مما يسمح للمهاجمين بتبديل الملفات الشرعية بأخرى ضارة تقوم بتنفيذ تعليمات برمجية عشوائية.

قال mr.d0x في المدونة الثانية: "أثناء تحليل سلوك كروم وإيدج، لاحظت ملاحظة مثيرة للاهتمام. عندما يتم حفظ صفحة HTML باستخدام Ctrl+S أو بالنقر بزر الفأرة الأيمن > 'حفظ باسم' واختيار أي من نوعي 'صفحة ويب، ملف واحد' أو 'صفحة ويب، كاملة'، فإن الملف الذي تم تنزيله لا يحتوي على MoTW (علامة الويب)."

MoTW هو معرف بيانات وصفية يُستخدم من قبل نظام ويندوز لتحديد الملفات التي تم تنزيلها من الإنترنت على أنها قد تكون غير آمنة. إذا قام شخص ما بتنزيل ملف من الإنترنت بدون MoTW، فمن المحتمل ألا يكون على علم إذا كان يحتوي على محتوى ضار. من خلال تجاوز هذه الحماية، يمكن لمهاجم استخدام FileFix لتوصيل البرمجيات الخبيثة للمستخدمين غير المدركين من خلال هجوم هندسة اجتماعية يقنعهم بضرورة أو أهمية تنزيل الملف.

أسلوب FileFix في الهندسة الاجتماعية

تعتبر ClickFix أسلوب هجوم تم تفصيله لأول مرة من قبل الباحثين في Proofpoint العام الماضي، حيث تعرض المواقع المخترقة المستخدمين لرسائل خطأ مزيفة عن طريق تنفيذ تعليمات برمجية PowerShell، مما يخدعهم للاعتقاد أنهم بحاجة لإصلاح مشكلة تحديث المتصفح. لكن في الواقع، فإن تثبيت "التحديث" ينفذ برمجيات خبيثة على أجهزتهم. منذ اكتشافه، اكتسب هذا الأسلوب شعبية بين المهاجمين.

طور mr.d0x أسلوبًا مشابهًا لـ FileFix الذي، مثل ClickFix، يستخدم الهندسة الاجتماعية لجعل مستخدمي المتصفح يشاركون في هجومهم الضار. في إحدى النسخ التي وصفها، طور صفحة ويب تطلب من الأشخاص حفظ صفحة ويب HTML تعرض رموز النسخ الاحتياطي الخاصة بهم — وهي طريقة يمكن تطبيقها على كل من مستخدمي الويب العاديين وموظفي الشركات. تحاكي الصفحة عناصر شرعية من صفحات مصادقة جوجل ومايكروسوفت، مما يجعل الأشخاص يعتقدون حقًا أنهم يقومون بحفظ تفاصيل أمان حيوية على نظامهم. بدلاً من ذلك، سيقومون بحفظ صفحات HTML ضارة يمكن أن تنفذ تعليمات برمجية عشوائية على أنظمتهم.

في طريقة FileFix، عندما يقوم شخص ما بحفظ صفحة تحتوي على رموز النسخ الاحتياطي الخاصة بهم باستخدام Ctrl+S، سيقوم المتصفح تلقائيًا بتعيين اسم الملف المحفوظ الافتراضي ليكون ما هو موجود في عنصر ، والذي في عرض إثبات المفهوم (POC) هو "حفظ رموز النسخ الاحتياطي". "إذا كانت إعدادات النظام تسمح للمستخدم برؤية امتداد الملف، فسيرون "حفظ رموز النسخ الاحتياطي.html" بدلاً من ذلك"، كما أوضح mr.d0x.

التحول الضار

"الآن إذا كان قد تم تعيينه إلى 'Save Backup Codes.hta' ولم تكن امتدادات الملفات مرئية، فسوف يتم حفظ الملف كـ 'Save Backup Codes.hta.html'"، وفقًا للمنشور. "ومع ذلك، إذا قام المستخدم بإجراء أي تغييرات على اسم الملف، حتى لو كان يعيد كتابة نفس الاسم بالضبط، فإن المتصفح لن يضيف .html بعد الآن."</p> <p>لزيادة احتمالية نجاح الهجوم، يمكن للمهاجم تعيين العنوان إلى شيء مثل _.hta، مما يحفز المستخدم على تعديل الخط السفلي واستبداله باسم مناسب، كما أوضح الباحثون.</p> <p>إذا اكتشف المدافعون ذلك وبدؤوا في وضع علامة على الموقع لمجرد أن العنوان ينتهي بـ ".hta"، يمكن للمهاجمين إزالة عنصر <title> تمامًا، وإعادة تسمية ملف التصيد HTML ليكون له امتداد .hta، وإرسال رأس <strong>Content-Type</strong> مع text/html. "سيؤدي حفظ الصفحة بعد ذلك إلى تعيين اسم 'حفظ باسم' الافتراضي إلى اسم الملف"، كما أوضح <strong>mr.d0x</strong>، مما يجعل المستخدم غير مدرك لـ <strong>النية الضارة</strong>.</p> <h2 id="Mitigating the Attack">تخفيف الهجوم</h2> <p>لم يذكر <strong>mr.d0x</strong> في أي من منشوراته حول <strong>FileFix</strong> إذا كان قد اتصل بجوجل ومايكروسوفت بشأن الهجوم، ولم يكن بالإمكان الوصول إليه على الفور للتعليق من قبل <strong>Dark Reading</strong>. يخطط الباحث لإصدار تفاصيل حول نسخة أخرى من الهجوم قريبًا، وفقًا لمنشوراته.</p> <p>لم تستجب جوجل على الفور لطلب <strong>Dark Reading</strong> للتعليق على الهجوم يوم الأربعاء.</p> <p>قدمت متحدثة باسم مايكروسوفت البيان التالي: "تتطلب هذه التقنية في الهندسة الاجتماعية من المهاجم خداع المستخدم لنسخ أوامر ضارة محددة إلى مواقع غير شائعة في ويندوز. نحن نراقب باستمرار مشهد التهديدات لتقنيات مثل هذه وننفذ تدابير أمان متعددة كجزء من استراتيجيتنا للدفاع المتعمق لمساعدة عملائنا على البقاء محميين. كأفضل ممارسة في الأمان، نشجع العملاء على ممارسة عادات حوسبة جيدة عبر الإنترنت، بما في ذلك توخي الحذر عند تنزيل الملفات أو اتباع التعليمات أو تنفيذ الأوامر من مصادر غير مألوفة."</p> <p>لمنع أسلوب الهجوم الذي يستخدم ملفات .hta من العمل، يمكن للمدافعين إزالة mshta.exe من القدرة على تشغيل ملفات .hta، وفقًا للباحث. "هذه حل جيد ما لم يتمكن شخص ما من استخدام هذه التقنية مع أنواع ملفات أخرى"، كتب <strong>mr.d0x</strong>.</p> <p>للدفاع ضد كل من <strong>ClickFix</strong> و<strong>FileFix</strong> بشكل عام، يجب على المنظمات أيضًا مراقبة المتصفحات لأي عمليات فرعية مشبوهة، مثل cmd.exe وpowershel.exe وmshta.exe، كما أضاف الباحث.</p> </div> <div id="cyber-section-container" class="cyber-section-article"> <div class="cyber-slider-wrapper"> <div class="cyber-slider-header"> <div class="cyber-slider-header__right"> <div class="cyber-slider-header__icon"> <i class="fa-solid fa-shield-halved"></i> </div> <div class="cyber-slider-header__text"> <h3 class="cyber-slider-header__title">الأمن السيبراني</h3> <span class="cyber-slider-header__subtitle">أحدث أخبار وتحليلات الأمن السيبراني</span> </div> </div> <a href="https://w3professional.com/category/alamn-alsybrany" class="cyber-slider-header__link"> <span>عرض الكل</span> <i class="fa-solid fa-arrow-left"></i> </a> </div> <div class="cyber-slider-track-wrapper"> <button class="cyber-slider-nav cyber-slider-nav--prev" aria-label="السابق"> <i class="fa-solid fa-chevron-right"></i> </button> <div class="cyber-slider-track" id="cyber-slider-track"> <a href="https://w3professional.com/article/akhtrak-telnyx-pypi-brmgyat-khbyth-mkhfy-bmlfat-wav" class="cyber-slider-card"> <div class="cyber-slider-card__image"> <img src="/storage/articles/images/2026/03/backdoored-telnyx-pypi-package-pushes-malware-hidden-in-wav-audio/34.jpg" alt="اختراق Telnyx PyPI: برمجيات خبيثة مخفية بملفات WAV" loading="lazy" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="cyber-slider-card__overlay"></div> </div> <div class="cyber-slider-card__body"> <h4 class="cyber-slider-card__title">اختراق Telnyx PyPI: برمجيات خبيثة مخفية بملفات WAV</h4> <div class="cyber-slider-card__meta"> <span class="cyber-slider-card__time"> <i class="fa-regular fa-clock"></i> منذ 3 أشهر </span> <span class="cyber-slider-card__views"> <i class="fa-regular fa-eye"></i> 197 </span> </div> </div> </a> <a href="https://w3professional.com/article/alaf-almoakaa-tsrb-mfatyh-api-hsas-mkhatr-amny-gsym" class="cyber-slider-card"> <div class="cyber-slider-card__image"> <img src="https://www.digitaltrends.com/tachyon/2026/03/AdobeStock_1792204484-scaled.jpeg?resize=440,292" alt="آلاف المواقع تُسرب مفاتيح API حساسة: مخاطر أمنية جسيمة" loading="lazy" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="cyber-slider-card__overlay"></div> </div> <div class="cyber-slider-card__body"> <h4 class="cyber-slider-card__title">آلاف المواقع تُسرب مفاتيح API حساسة: مخاطر أمنية جسيمة</h4> <div class="cyber-slider-card__meta"> <span class="cyber-slider-card__time"> <i class="fa-regular fa-clock"></i> منذ 3 أشهر </span> <span class="cyber-slider-card__views"> <i class="fa-regular fa-eye"></i> 254 </span> </div> </div> </a> <a href="https://w3professional.com/article/ththyr-cisa-astghlal-khtyr-fy-langflow-yhdd-tdfkat-althkaaa-alastnaaay" class="cyber-slider-card"> <div class="cyber-slider-card__image"> <img src="/storage/articles/images/2026/03/cisa-new-langflow-flaw-actively-exploited-to-hijack-ai-workflows/34.jpg" alt="تحذير CISA: استغلال خطير في Langflow يهدد تدفقات الذكاء الاصطناعي" loading="lazy" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="cyber-slider-card__overlay"></div> </div> <div class="cyber-slider-card__body"> <h4 class="cyber-slider-card__title">تحذير CISA: استغلال خطير في Langflow يهدد تدفقات الذكاء الاصطناعي</h4> <div class="cyber-slider-card__meta"> <span class="cyber-slider-card__time"> <i class="fa-regular fa-clock"></i> منذ 3 أشهر </span> <span class="cyber-slider-card__views"> <i class="fa-regular fa-eye"></i> 236 </span> </div> </div> </a> <a href="https://w3professional.com/article/torg-grabber-brnamg-khbyth-gdyd-ysthdf-myat-mhafth-alaamlat-almshfr" class="cyber-slider-card"> <div class="cyber-slider-card__image"> <img src="/storage/articles/images/2026/03/new-torg-grabber-infostealer-malware-targets-728-crypto-wallets/34.jpg" alt="Torg Grabber: برنامج خبيث جديد يستهدف مئات محافظ العملات المشفرة" loading="lazy" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="cyber-slider-card__overlay"></div> </div> <div class="cyber-slider-card__body"> <h4 class="cyber-slider-card__title">Torg Grabber: برنامج خبيث جديد يستهدف مئات محافظ العملات المشفرة</h4> <div class="cyber-slider-card__meta"> <span class="cyber-slider-card__time"> <i class="fa-regular fa-clock"></i> منذ 3 أشهر </span> <span class="cyber-slider-card__views"> <i class="fa-regular fa-eye"></i> 224 </span> </div> </div> </a> <a href="https://w3professional.com/article/ahtyal-bubble-mns-ai-tstghl-lsrk-byanat-microsoft" class="cyber-slider-card"> <div class="cyber-slider-card__image"> <img src="/storage/articles/images/2026/03/bubble-ai-app-builder-abused-to-steal-microsoft-account-credentials/34.jpg" alt="احتيال Bubble: منصة AI تُستغل لسرقة بيانات Microsoft" loading="lazy" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="cyber-slider-card__overlay"></div> </div> <div class="cyber-slider-card__body"> <h4 class="cyber-slider-card__title">احتيال Bubble: منصة AI تُستغل لسرقة بيانات Microsoft</h4> <div class="cyber-slider-card__meta"> <span class="cyber-slider-card__time"> <i class="fa-regular fa-clock"></i> منذ 3 أشهر </span> <span class="cyber-slider-card__views"> <i class="fa-regular fa-eye"></i> 206 </span> </div> </div> </a> <a href="https://w3professional.com/article/ironscales-hmay-bryd-alktrony-astbaky-btknyat-althkaaa-alastnaaay" class="cyber-slider-card"> <div class="cyber-slider-card__image"> <img src="https://cdn0.tnwcdn.com/wp-content/blogs.dir/1/files/2026/03/Ironscales-rsac-conference.png" alt="IRONSCALES: حماية بريد إلكتروني استباقية بتقنيات الذكاء الاصطناعي" loading="lazy" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="cyber-slider-card__overlay"></div> </div> <div class="cyber-slider-card__body"> <h4 class="cyber-slider-card__title">IRONSCALES: حماية بريد إلكتروني استباقية بتقنيات الذكاء الاصطناعي</h4> <div class="cyber-slider-card__meta"> <span class="cyber-slider-card__time"> <i class="fa-regular fa-clock"></i> منذ 3 أشهر </span> <span class="cyber-slider-card__views"> <i class="fa-regular fa-eye"></i> 232 </span> </div> </div> </a> </div> <button class="cyber-slider-nav cyber-slider-nav--next" aria-label="التالي"> <i class="fa-solid fa-chevron-left"></i> </button> </div> </div> </div> <div class="article-tags"> <a href="https://w3professional.com/search?q=%23FileFix" class="article-tag">##FileFix</a> <a href="https://w3professional.com/search?q=%23ClickFix" class="article-tag">##ClickFix</a> <a href="https://w3professional.com/search?q=%23%D8%A7%D9%84%D9%87%D9%86%D8%AF%D8%B3%D8%A9_%D8%A7%D9%84%D8%A7%D8%AC%D8%AA%D9%85%D8%A7%D8%B9%D9%8A%D8%A9" class="article-tag">##الهندسة_الاجتماعية</a> </div> <div class="share-section"> <h4 class="share-title">شارك هذا المقال</h4> <div class="share-buttons"> <a href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fw3professional.com%2Farticle%2Faslob-filefix-ytgaoz-hmay-almtsfhat-alhdyth&text=%D8%A3%D8%B3%D9%84%D9%88%D8%A8+FileFix+%D9%8A%D8%AA%D8%AC%D8%A7%D9%88%D8%B2+%D8%AD%D9%85%D8%A7%D9%8A%D8%A9+%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA+%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9" target="_blank" rel="noopener" class="share-btn twitter"> <i class="fa-brands fa-x-twitter"></i> </a> <a href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fw3professional.com%2Farticle%2Faslob-filefix-ytgaoz-hmay-almtsfhat-alhdyth" target="_blank" rel="noopener" class="share-btn facebook"> <i class="fa-brands fa-facebook-f"></i> </a> <a href="https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fw3professional.com%2Farticle%2Faslob-filefix-ytgaoz-hmay-almtsfhat-alhdyth&title=%D8%A3%D8%B3%D9%84%D9%88%D8%A8+FileFix+%D9%8A%D8%AA%D8%AC%D8%A7%D9%88%D8%B2+%D8%AD%D9%85%D8%A7%D9%8A%D8%A9+%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA+%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9" target="_blank" rel="noopener" class="share-btn linkedin"> <i class="fa-brands fa-linkedin-in"></i> </a> <a href="https://api.whatsapp.com/send?text=%D8%A3%D8%B3%D9%84%D9%88%D8%A8+FileFix+%D9%8A%D8%AA%D8%AC%D8%A7%D9%88%D8%B2+%D8%AD%D9%85%D8%A7%D9%8A%D8%A9+%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA+%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9+https%3A%2F%2Fw3professional.com%2Farticle%2Faslob-filefix-ytgaoz-hmay-almtsfhat-alhdyth" target="_blank" rel="noopener" class="share-btn whatsapp"> <i class="fa-brands fa-whatsapp"></i> </a> <a href="https://t.me/share/url?url=https%3A%2F%2Fw3professional.com%2Farticle%2Faslob-filefix-ytgaoz-hmay-almtsfhat-alhdyth&text=%D8%A3%D8%B3%D9%84%D9%88%D8%A8+FileFix+%D9%8A%D8%AA%D8%AC%D8%A7%D9%88%D8%B2+%D8%AD%D9%85%D8%A7%D9%8A%D8%A9+%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA+%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9" target="_blank" rel="noopener" class="share-btn telegram"> <i class="fa-brands fa-telegram"></i> </a> <button class="share-btn copy" onclick="copyArticleLink()"> <i class="fa-solid fa-link"></i> </button> </div> </div> <section class="comments-section" id="comments-section"> <div class="comments-header"> <h3 class="comments-title"> <i class="fa-regular fa-comments"></i> التعليقات <span class="comments-count" id="comments-count">0</span> </h3> </div> <div class="comment-input-wrapper"> <div class="comment-input-box" style="text-align: center; padding: 24px;"> <p style="margin-bottom: 12px; color: var(--text-secondary);">سجل دخولك لإضافة تعليق</p> <div style="display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;"> <button type="button" onclick="openModal('login')" class="follow-btn"> <i class="fa-solid fa-arrow-right-to-bracket"></i> تسجيل الدخول </button> <button type="button" onclick="openModal('register')" class="follow-btn" style="background: transparent; border: 2px solid var(--primary); color: var(--primary);"> <i class="fa-solid fa-user-plus"></i> إنشاء حساب </button> </div> </div> </div> <div id="comments-list"> <div class="no-comments" id="no-comments" style="text-align: center; padding: 40px 20px; color: var(--text-muted);"> <i class="fa-regular fa-comments" style="font-size: 48px; margin-bottom: 16px; opacity: 0.5;"></i> <p>لا توجد تعليقات بعد. كن أول من يعلق!</p> </div> </div> </section> </article> <aside class="article-sidebar" id="article-sidebar"> <div class="sidebar-card toc-card"> <h4 class="sidebar-card-title"> <i class="fa-solid fa-list"></i> محتويات المقال </h4> <nav class="toc-nav" aria-label="جدول المحتويات"> <a href="#The Malicious Twist" class="toc-item active"> <span class="toc-indicator"></span> التحول الضار </a> <a href="#Mitigating the Attack" class="toc-item "> <span class="toc-indicator"></span> تخفيف الهجوم </a> </nav> </div> <div class="sidebar-card"> <h4 class="sidebar-card-title">مقالات ذات صلة</h4> <a href="https://w3professional.com/article/tkno-tkshf-aan-hoatf-akhtbary-nyon-mtohg-ohbr-alktrony-mtghyr-alaloan" class="related-article"> <img src="https://platform.theverge.com/wp-content/uploads/sites/2/2026/02/tecno-pova-neon.jpg?quality=90&strip=all&crop=0,0,100,100" class="related-thumb" alt="تكنو تكشف عن هواتف اختبارية: نيون متوهج وحبر إلكتروني متغير الألوان" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="related-content"> <span class="related-tag">أخرى</span> <h5 class="related-title">تكنو تكشف عن هواتف اختبارية: نيون متوهج وحبر إلكتروني متغير الألوان</h5> <span class="related-meta">منذ 4 أشهر</span> </div> </a> <a href="https://w3professional.com/article/mkns-shark-av2501ae-alroboty-bkhsm-54-nthaf-bla-aanaaa" class="related-article"> <img src="https://www.digitaltrends.com/tachyon/2026/03/sh-1.jpg?resize=440,292" class="related-thumb" alt="مكنسة Shark AV2501AE الروبوتية بخصم 54%: نظافة بلا عناء" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="related-content"> <span class="related-tag">أخرى</span> <h5 class="related-title">مكنسة Shark AV2501AE الروبوتية بخصم 54%: نظافة بلا عناء</h5> <span class="related-meta">منذ 4 أشهر</span> </div> </a> <a href="https://w3professional.com/article/nasa-togl-alhbot-almahol-aal-alkmr-l-2028-tghyyrat-fy-artemis-iii" class="related-article"> <img src="https://www.digitaltrends.com/tachyon/2020/07/moon-astronauts.jpg?resize=440,292" class="related-thumb" alt="ناسا تؤجل الهبوط المأهول على القمر لـ 2028: تغييرات في Artemis III" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="related-content"> <span class="related-tag">أخرى</span> <h5 class="related-title">ناسا تؤجل الهبوط المأهول على القمر لـ 2028: تغييرات في Artemis III</h5> <span class="related-meta">منذ 4 أشهر</span> </div> </a> <a href="https://w3professional.com/article/alghaz-alkomy-llatsalat-5-dkayk-mgany-llmsryyn-fy-6-dol-aarby" class="related-article"> <img src="https://vid.alarabiya.net/images/2025/06/03/1ee710e6-308b-4538-868a-1c578bda20c5/1ee710e6-308b-4538-868a-1c578bda20c5_16x9_600x338.jpg" class="related-thumb" alt="الجهاز القومي للاتصالات: 5 دقائق مجانية للمصريين في 6 دول عربية" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="related-content"> <span class="related-tag">أخرى</span> <h5 class="related-title">الجهاز القومي للاتصالات: 5 دقائق مجانية للمصريين في 6 دول عربية</h5> <span class="related-meta">منذ 4 أشهر</span> </div> </a> <a href="https://w3professional.com/article/ai-alkabl-llartdaaa-khtr-altlaaab-alkhfy-baloaay-albshry" class="related-article"> <img src="https://images.ctfassets.net/jdtwqhzvc2n1/1XpGggdRdCiwlNHyu2qhW1/92e36733fd554e13692566a6f533d398/upscalemedia-transformed.png?w=300&q=30" class="related-thumb" alt="AI القابل للارتداء: خطر التلاعب الخفي بالوعي البشري" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="related-content"> <span class="related-tag">أخرى</span> <h5 class="related-title">AI القابل للارتداء: خطر التلاعب الخفي بالوعي البشري</h5> <span class="related-meta">منذ 4 أشهر</span> </div> </a> <a href="https://w3professional.com/article/dras-harfard-artbat-mhtml-byn-almfaaalat-alnooy-oofyat-alsrtan" class="related-article"> <img src="https://gizmodo.com/app/uploads/2024/09/three-mile-microsoft-constellation-nuclear-1024x683.jpg" class="related-thumb" alt="دراسة هارفارد: ارتباط محتمل بين المفاعلات النووية ووفيات السرطان" onerror="this.onerror=null; this.src='https://w3professional.com/assets/pictures/default.png'"> <div class="related-content"> <span class="related-tag">أخرى</span> <h5 class="related-title">دراسة هارفارد: ارتباط محتمل بين المفاعلات النووية ووفيات السرطان</h5> <span class="related-meta">منذ 4 أشهر</span> </div> </a> </div> <div class="sidebar-scroll-hint"> <div class="sidebar-scroll-hint__icon"> <i class="fa-solid fa-chevron-down"></i> </div> </div> </aside> </div> <div class="floating-actions"> <div class="actions-inner"> <button class="floating-action " id="mobile-like"> <i class="fa-regular fa-heart"></i> <span>0</span> </button> <button class="floating-action" onclick="document.querySelector('.comments-section').scrollIntoView({behavior: 'smooth'})"> <i class="fa-regular fa-comment"></i> <span>0</span> </button> <button class="floating-action" id="mobile-share"> <i class="fa-solid fa-share-nodes"></i> <span>مشاركة</span> </button> <button class="floating-action " id="mobile-bookmark"> <i class="fa-regular fa-bookmark"></i> <span>حفظ</span> </button> </div> </div> </div> </main> <footer class="footer"> <div class="footer-content"> <div> <div class="footer-brand"> <img src="https://w3professional.com/storage/settings/AhG082rSXwUkCPQpSpCAUBXWlWAXJI6FzpDWzhDd_footer.png" alt="W3Professional" class="footer-logo" width="230" height="70" loading="lazy"> </div> <p class="footer-desc"> يقدم أحدث أخبار التكنولوجيا، الذكاء الاصطناعي، والأمن السيبراني، مع تحليلات تقنية موجهة للقارئ العربي. </p> <div class="footer-social"> <a href="https://x.com/w3aboreda" class="social-btn" title="إكس" target="_blank" rel="noopener"> <i class="fa-brands fa-x-twitter"></i> </a> <a href="https://www.linkedin.com/company/w3professional" class="social-btn" title="لينكدإن" target="_blank" rel="noopener"> <i class="fa-brands fa-linkedin-in"></i> </a> <a href="https://www.facebook.com/W3Professional" class="social-btn" title="فيسبوك" target="_blank" rel="noopener"> <i class="fa-brands fa-facebook-f"></i> </a> <a href="https://www.instagram.com/w3professional" class="social-btn" title="انستجرام" target="_blank" rel="noopener"> <i class="fa-brands fa-instagram"></i> </a> </div> </div> <div> <h4 class="footer-title">روابط سريعة</h4> <ul class="footer-links"> <li><a href="https://w3professional.com">الرئيسية</a></li> <li><a href="https://w3professional.com/discover">اكتشف</a></li> <li><a href="https://w3professional.com/videos">مكتبة الفيديو</a></li> </ul> </div> <div> <h4 class="footer-title">التصنيفات</h4> <ul class="footer-links"> <li><a href="https://w3professional.com/category/altkny">التقنية</a></li> <li><a href="https://w3professional.com/category/alhoatf">الهواتف</a></li> <li><a href="https://w3professional.com/category/alshrkat">الشركات</a></li> <li><a href="https://w3professional.com/category/aaam">عام</a></li> </ul> </div> <div> <h4 class="footer-title">الدعم</h4> <ul class="footer-links"> <li><a href="https://w3professional.com/page/help">مركز المساعدة</a></li> <li><a href="https://w3professional.com/page/privacy">سياسة الخصوصية</a></li> <li><a href="https://w3professional.com/page/terms">شروط الاستخدام</a></li> <li><a href="https://w3professional.com/contact">تواصل معنا</a></li> </ul> </div> </div> <div class="footer-bottom"> <span>© 2026 W3Professional - جميع الحقوق محفوظة</span> <span>صنع للعالم العربي ب ❤️ 9Website Team</span> </div> </footer> <div class="modal-overlay" id="loginModal" role="dialog" aria-modal="true" aria-labelledby="loginModalTitle" aria-describedby="loginModalDesc"> <div class="modal-container"> <div class="modal-header"> <button class="modal-close" onclick="closeModal('login')" type="button" aria-label="إغلاق"> <i class="fa-solid fa-xmark" aria-hidden="true"></i> </button> <h2 class="modal-title" id="loginModalTitle">مرحباً بعودتك!</h2> <p class="modal-subtitle" id="loginModalDesc">سجل دخولك للوصول إلى حسابك</p> </div> <div class="modal-body"> <div class="social-login"> <a href="https://w3professional.com/auth/google" class="google-btn-pro"> <div class="google-btn-icon"> <svg viewBox="0 0 24 24" width="20" height="20"> <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/> <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/> <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/> <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/> </svg> </div> <span class="google-btn-text">المتابعة مع Google</span> </a> </div> <div class="auth-divider"> <span>أو بالبريد الإلكتروني</span> </div> <div class="auth-error" id="login-error" style="display: none;"> <i class="fa-solid fa-circle-exclamation"></i> <span id="login-error-text"></span> </div> <form id="loginForm"> <div class="form-group"> <label class="form-label" for="login-email">البريد الإلكتروني</label> <div class="input-wrapper"> <i class="fa-regular fa-envelope input-icon"></i> <input type="email" class="form-input" id="login-email" name="email" placeholder="example@email.com" required autocomplete="email"> </div> <span class="field-error" id="login-email-error"></span> </div> <div class="form-group"> <label class="form-label" for="login-password">كلمة المرور</label> <div class="input-wrapper"> <i class="fa-solid fa-lock input-icon"></i> <input type="password" class="form-input" id="login-password" name="password" placeholder="••••••••" required autocomplete="current-password"> <button type="button" class="toggle-password" onclick="togglePassword('login-password')"> <i class="fa-regular fa-eye"></i> </button> </div> <span class="field-error" id="login-password-error"></span> </div> <div class="form-options"> <label class="checkbox-wrapper"> <input type="checkbox" name="remember" id="remember"> <span>تذكرني</span> </label> <a href="https://w3professional.com/forgot-password" class="forgot-link">نسيت كلمة المرور؟</a> </div> <button type="submit" class="btn-submit" id="login-submit"> <span class="btn-text"> <i class="fa-solid fa-arrow-right-to-bracket"></i> تسجيل الدخول </span> <span class="btn-loading" style="display: none;"> <i class="fa-solid fa-spinner fa-spin"></i> جاري التحقق... </span> </button> </form> </div> <div class="modal-footer"> <p>ليس لديك حساب؟ <a onclick="switchModal('login', 'register')">إنشاء حساب جديد</a></p> </div> </div> </div> <style> /* Step Indicator */ .step-indicator { display: flex; align-items: center; justify-content: center; gap: 0; margin-top: 16px; margin-bottom: 8px; } .step { display: flex; flex-direction: column; align-items: center; gap: 8px; opacity: 0.5; transition: all 0.3s ease; } .step.active, .step.completed { opacity: 1; } .step-circle { width: 36px; height: 36px; border-radius: 50%; background: var(--bg-body); border: 2px solid var(--border-default); display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; } .step.active .step-circle { background: var(--blue-500); border-color: var(--blue-500); box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2); } .step.completed .step-circle { background: var(--green-500); border-color: var(--green-500); } .step-number { font-size: 14px; font-weight: 700; color: var(--text-secondary); } .step.active .step-number, .step.completed .step-number { color: #fff; } .step-check { color: #fff; font-size: 14px; } .step-label { font-size: 12px; font-weight: 600; color: var(--text-secondary); white-space: nowrap; } .step.active .step-label { color: var(--blue-500); } .step.completed .step-label { color: var(--green-500); } .step-line { width: 60px; height: 2px; background: var(--border-default); margin: 0 12px; margin-bottom: 24px; transition: background 0.3s ease; } .step-line.active { background: var(--green-500); } /* Registration Steps */ .register-step { display: none; opacity: 0; transform: translateX(20px); transition: all 0.3s ease; } .register-step.active { display: block; opacity: 1; transform: translateX(0); } .step-title { font-size: 22px; font-weight: 800; color: var(--text-primary); margin-bottom: 8px; text-align: center; } .step-subtitle { font-size: 14px; color: var(--text-secondary); text-align: center; margin-bottom: 24px; } /* OTP Styles */ .otp-header { text-align: center; margin-bottom: 24px; } .otp-icon { width: 72px; height: 72px; margin: 0 auto 16px; background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(99, 102, 241, 0.1)); border-radius: 50%; display: flex; align-items: center; justify-content: center; } .otp-icon i { font-size: 32px; color: var(--blue-500); } .otp-email-display { font-size: 15px; font-weight: 600; color: var(--blue-500); direction: ltr; margin-top: 8px; } .otp-container { display: flex; justify-content: center; gap: 10px; margin-bottom: 24px; direction: ltr; } .otp-input { width: 48px; height: 56px; text-align: center; font-size: 24px; font-weight: 700; font-family: 'Courier New', monospace; background: var(--bg-body); border: 2px solid var(--border-default); border-radius: var(--radius-lg); color: var(--text-primary); transition: all 0.3s ease; outline: none; } .otp-input:focus { border-color: var(--blue-500); box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1); } .otp-input.filled { border-color: var(--blue-500); background: rgba(59, 130, 246, 0.05); } .otp-input.error { border-color: var(--red-500); background: rgba(239, 68, 68, 0.05); animation: shake 0.4s ease-in-out; } @keyframes shake { 0%, 100% { transform: translateX(0); } 20%, 60% { transform: translateX(-4px); } 40%, 80% { transform: translateX(4px); } } .otp-timer-container { display: flex; flex-direction: column; align-items: center; gap: 12px; margin-bottom: 24px; } .otp-timer { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--text-secondary); } .otp-timer i { color: var(--blue-500); } #otp-countdown { font-weight: 700; color: var(--blue-500); font-family: 'Courier New', monospace; } #otp-countdown.expired { color: var(--red-500); } .otp-resend { display: flex; align-items: center; gap: 6px; background: none; border: none; font-size: 14px; font-weight: 600; color: var(--blue-500); cursor: pointer; font-family: inherit; transition: all 0.3s ease; padding: 8px 16px; border-radius: var(--radius-md); } .otp-resend:hover:not(:disabled) { background: rgba(59, 130, 246, 0.1); } .otp-resend:disabled { color: var(--text-secondary); cursor: not-allowed; } .otp-resend i { font-size: 12px; } #resend-countdown { font-weight: 700; } .otp-change-email { text-align: center; margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--border-default); } .change-email-btn { display: inline-flex; align-items: center; gap: 8px; background: none; border: none; font-size: 14px; font-weight: 600; color: var(--text-secondary); cursor: pointer; font-family: inherit; transition: all 0.3s ease; padding: 8px 16px; border-radius: var(--radius-md); } .change-email-btn:hover { color: var(--blue-500); background: rgba(59, 130, 246, 0.1); } .change-email-btn i { font-size: 12px; } @media (max-width: 480px) { .otp-container { gap: 6px; } .otp-input { width: 42px; height: 50px; font-size: 20px; } .step-line { width: 40px; margin: 0 8px; } .step-label { font-size: 11px; } .step-circle { width: 32px; height: 32px; } .step-number, .step-check { font-size: 12px; } } </style> <div class="modal-overlay" id="registerModal" role="dialog" aria-modal="true" aria-labelledby="registerModalTitle"> <div class="modal-container"> <div class="modal-header"> <button class="modal-close" onclick="closeModal('register')" type="button" aria-label="إغلاق"> <i class="fa-solid fa-xmark" aria-hidden="true"></i> </button> <div class="step-indicator" id="register-step-indicator"> <div class="step active" id="step-indicator-1"> <div class="step-circle"> <span class="step-number">1</span> <i class="fa-solid fa-check step-check" style="display: none;"></i> </div> <span class="step-label">معلومات الحساب</span> </div> <div class="step-line"></div> <div class="step" id="step-indicator-2"> <div class="step-circle"> <span class="step-number">2</span> <i class="fa-solid fa-check step-check" style="display: none;"></i> </div> <span class="step-label">تأكيد البريد</span> </div> </div> </div> <div class="modal-body"> <div class="register-step active" id="register-step-1"> <h2 class="step-title" id="registerModalTitle">إنشاء حساب جديد</h2> <p class="step-subtitle">انضم إلى مجتمع W3Professional</p> <div class="social-login"> <a href="https://w3professional.com/auth/google" class="google-btn-pro"> <div class="google-btn-icon"> <svg viewBox="0 0 24 24" width="20" height="20"> <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/> <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/> <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/> <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/> </svg> </div> <span class="google-btn-text">التسجيل مع Google</span> </a> </div> <div class="auth-divider"> <span>أو بالبريد الإلكتروني</span> </div> <div class="auth-error" id="register-error" style="display: none;"> <i class="fa-solid fa-circle-exclamation"></i> <span id="register-error-text"></span> </div> <div class="auth-success" id="register-success" style="display: none;"> <i class="fa-solid fa-circle-check"></i> <span id="register-success-text"></span> </div> <form id="registerForm"> <div class="form-row"> <div class="form-group"> <label class="form-label" for="register-first-name">الاسم الأول</label> <div class="input-wrapper"> <i class="fa-regular fa-user input-icon"></i> <input type="text" class="form-input" id="register-first-name" name="first_name" placeholder="أحمد" required autocomplete="given-name"> </div> <span class="field-error" id="register-first_name-error"></span> </div> <div class="form-group"> <label class="form-label" for="register-last-name">اسم العائلة</label> <div class="input-wrapper"> <i class="fa-regular fa-user input-icon"></i> <input type="text" class="form-input" id="register-last-name" name="last_name" placeholder="محمد" required autocomplete="family-name"> </div> <span class="field-error" id="register-last_name-error"></span> </div> </div> <div class="form-group"> <label class="form-label" for="register-email">البريد الإلكتروني</label> <div class="input-wrapper"> <i class="fa-regular fa-envelope input-icon"></i> <input type="email" class="form-input" id="register-email" name="email" placeholder="example@email.com" required autocomplete="email"> </div> <span class="field-error" id="register-email-error"></span> </div> <div class="form-group"> <label class="form-label" for="register-password">كلمة المرور</label> <div class="input-wrapper"> <i class="fa-solid fa-lock input-icon"></i> <input type="password" class="form-input" id="register-password" name="password" placeholder="••••••••" required autocomplete="new-password" oninput="checkPasswordStrength(this.value)"> <button type="button" class="toggle-password" onclick="togglePassword('register-password')"> <i class="fa-regular fa-eye"></i> </button> </div> <div class="password-strength"> <div class="strength-bar" id="strength1"></div> <div class="strength-bar" id="strength2"></div> <div class="strength-bar" id="strength3"></div> <div class="strength-bar" id="strength4"></div> </div> <div class="strength-text" id="strengthText"></div> <span class="field-error" id="register-password-error"></span> </div> <div class="form-group"> <label class="form-label" for="register-password-confirm">تأكيد كلمة المرور</label> <div class="input-wrapper"> <i class="fa-solid fa-lock input-icon"></i> <input type="password" class="form-input" id="register-password-confirm" name="password_confirmation" placeholder="••••••••" required autocomplete="new-password"> <button type="button" class="toggle-password" onclick="togglePassword('register-password-confirm')"> <i class="fa-regular fa-eye"></i> </button> </div> <span class="field-error" id="register-password_confirmation-error"></span> </div> <div class="form-options"> <label class="checkbox-wrapper"> <input type="checkbox" name="terms" id="terms" required> <span>أوافق على <a href="https://w3professional.com/page/terms" target="_blank" style="color: var(--blue-500);">الشروط والأحكام</a></span> </label> </div> <button type="submit" class="btn-submit" id="register-submit"> <span class="btn-text"> <i class="fa-solid fa-paper-plane"></i> إرسال رمز التحقق </span> <span class="btn-loading" style="display: none;"> <i class="fa-solid fa-spinner fa-spin"></i> جاري الإرسال... </span> </button> <p class="terms-text"> بإنشاء حساب، أنت توافق على <a href="https://w3professional.com/page/privacy" target="_blank">سياسة الخصوصية</a> و<a href="https://w3professional.com/page/terms" target="_blank">شروط الاستخدام</a> </p> </form> </div> <div class="register-step" id="register-step-2"> <div class="otp-header"> <div class="otp-icon"> <i class="fa-solid fa-envelope-circle-check"></i> </div> <h2 class="step-title">تأكيد البريد الإلكتروني</h2> <p class="step-subtitle">أدخل رمز التحقق المرسل إلى</p> <p class="otp-email-display" id="otp-email-display"></p> </div> <div class="auth-error" id="otp-error" style="display: none;"> <i class="fa-solid fa-circle-exclamation"></i> <span id="otp-error-text"></span> </div> <div class="auth-success" id="otp-success" style="display: none;"> <i class="fa-solid fa-circle-check"></i> <span id="otp-success-text"></span> </div> <form id="otpForm"> <div class="otp-container"> <input type="text" class="otp-input" maxlength="1" data-index="0" inputmode="numeric" pattern="[0-9]" autocomplete="one-time-code"> <input type="text" class="otp-input" maxlength="1" data-index="1" inputmode="numeric" pattern="[0-9]"> <input type="text" class="otp-input" maxlength="1" data-index="2" inputmode="numeric" pattern="[0-9]"> <input type="text" class="otp-input" maxlength="1" data-index="3" inputmode="numeric" pattern="[0-9]"> <input type="text" class="otp-input" maxlength="1" data-index="4" inputmode="numeric" pattern="[0-9]"> <input type="text" class="otp-input" maxlength="1" data-index="5" inputmode="numeric" pattern="[0-9]"> </div> <div class="otp-timer-container"> <p class="otp-timer" id="otp-timer"> <i class="fa-regular fa-clock"></i> <span>ينتهي الرمز خلال <span id="otp-countdown">10:00</span></span> </p> <button type="button" class="otp-resend" id="otp-resend" disabled> <i class="fa-solid fa-rotate-right"></i> <span id="resend-text">إعادة الإرسال</span> <span id="resend-timer" style="display: none;"> (<span id="resend-countdown">60</span>)</span> </button> </div> <button type="submit" class="btn-submit" id="otp-submit"> <span class="btn-text"> <i class="fa-solid fa-check-circle"></i> تأكيد </span> <span class="btn-loading" style="display: none;"> <i class="fa-solid fa-spinner fa-spin"></i> جاري التحقق... </span> </button> </form> <div class="otp-change-email"> <button type="button" class="change-email-btn" onclick="goToRegisterStep(1)"> <i class="fa-solid fa-arrow-right"></i> تغيير البريد الإلكتروني </button> </div> </div> </div> <div class="modal-footer" id="register-modal-footer"> <p>لديك حساب بالفعل؟ <a onclick="switchModal('register', 'login')">تسجيل الدخول</a></p> </div> </div> </div> <div class="modal-overlay" id="pagesModal"> <div class="modal-container pages-modal"> <div class="pages-modal-header"> <button class="modal-close" onclick="closeModal('pages')" type="button"> <i class="fa-solid fa-xmark"></i> </button> <div class="pages-header-content"> <div class="pages-header-icon"> <div class="icon-glow"></div> <i class="fa-solid fa-layer-group"></i> </div> <div class="pages-header-text"> <h2>صفحات الموقع</h2> <p>الوصول السريع لجميع صفحات الموقع</p> </div> </div> </div> <div class="pages-modal-body"> <div class="pages-list"> <a href="https://w3professional.com/page/help" class="page-item" onclick="closeModal('pages')"> <div class="page-item-icon help"> <i class="fa-solid fa-circle-question"></i> </div> <div class="page-item-content"> <h4>مركز المساعدة</h4> <p>الأسئلة الشائعة والدعم الفني</p> </div> <div class="page-item-action"> <i class="fa-solid fa-arrow-left"></i> </div> </a> <a href="https://w3professional.com/page/privacy" class="page-item" onclick="closeModal('pages')"> <div class="page-item-icon privacy"> <i class="fa-solid fa-shield-halved"></i> </div> <div class="page-item-content"> <h4>سياسة الخصوصية</h4> <p>كيف نحمي بياناتك ومعلوماتك</p> </div> <div class="page-item-action"> <i class="fa-solid fa-arrow-left"></i> </div> </a> <a href="https://w3professional.com/page/terms" class="page-item" onclick="closeModal('pages')"> <div class="page-item-icon terms"> <i class="fa-solid fa-file-contract"></i> </div> <div class="page-item-content"> <h4>شروط الاستخدام</h4> <p>الشروط والأحكام العامة</p> </div> <div class="page-item-action"> <i class="fa-solid fa-arrow-left"></i> </div> </a> <a href="https://w3professional.com/page/about" class="page-item" onclick="closeModal('pages')"> <div class="page-item-icon about"> <i class="fa-solid fa-building"></i> </div> <div class="page-item-content"> <h4>من نحن</h4> <p>تعرف على قصة سايبر.نيوز</p> </div> <div class="page-item-action"> <i class="fa-solid fa-arrow-left"></i> </div> </a> <a href="https://w3professional.com/contact" class="page-item" onclick="closeModal('pages')"> <div class="page-item-icon contact"> <i class="fa-solid fa-paper-plane"></i> </div> <div class="page-item-content"> <h4>تواصل معنا</h4> <p>أرسل لنا رسالتك أو استفسارك</p> </div> <div class="page-item-action"> <i class="fa-solid fa-arrow-left"></i> </div> </a> </div> </div> <div class="pages-modal-footer"> <div class="footer-content"> <i class="fa-solid fa-headset"></i> <span>هل تحتاج مساعدة؟</span> <a href="https://w3professional.com/contact" onclick="closeModal('pages')">تواصل مع فريق الدعم</a> </div> </div> </div> </div> <div class="poll-widget" id="poll-widget" style="display: none;"> <button class="poll-trigger" id="poll-trigger" title="شارك في الاستطلاع"> <span class="poll-trigger-icon"> <i class="fa-solid fa-chart-simple"></i> </span> <span class="poll-trigger-text">صوّت الآن</span> <span class="poll-trigger-badge" id="poll-badge">LIVE</span> </button> </div> <div class="poll-modal-overlay" id="poll-modal" style="display: none;"> <div class="poll-modal"> <div class="poll-modal-header"> <div class="poll-modal-title-wrapper"> <span class="poll-live-badge"> <span class="live-dot"></span> استطلاع مباشر </span> <h3 class="poll-modal-title" id="poll-title"></h3> </div> <button class="poll-modal-close" id="poll-close" title="إغلاق"> <i class="fa-solid fa-xmark"></i> </button> </div> <div class="poll-modal-body"> <p class="poll-description" id="poll-description"></p> <div class="poll-options" id="poll-options"> </div> <div class="poll-results" id="poll-results" style="display: none;"> </div> <button class="poll-submit-btn" id="poll-submit" style="display: none;"> <i class="fa-solid fa-check-to-slot"></i> تأكيد التصويت </button> <div class="poll-stats" id="poll-stats"> <div class="poll-stat"> <i class="fa-solid fa-users"></i> <span id="poll-participants">0</span> مشارك </div> <div class="poll-stat"> <i class="fa-solid fa-check-double"></i> <span id="poll-votes">0</span> صوت </div> </div> <div class="poll-voted-message" id="poll-voted-message" style="display: none;"> <i class="fa-solid fa-circle-check"></i> <span>شكراً! لقد قمت بالتصويت مسبقاً</span> </div> </div> <div class="poll-modal-footer"> <span class="poll-footer-note"> <i class="fa-solid fa-user-check"></i> صوت واحد لكل مستخدم </span> </div> </div> </div> <link rel="modulepreload" as="script" href="https://w3professional.com/build/assets/js/app-CZGcIWIb.js" /><link rel="modulepreload" as="script" href="https://w3professional.com/build/assets/js/preload-helper-I4rgV-VL.js" /><script type="module" src="https://w3professional.com/build/assets/js/app-CZGcIWIb.js"></script> <script> // Skeleton loading handled by SkeletonLoader utility (auto-initialized) // Article page: wait for hero image (1 critical image) if (window.SkeletonLoader) { window.SkeletonLoader.init({ criticalImages: 1, maxWait: 1500 }); } // Sidebar scroll indicator (function() { const sidebar = document.getElementById('article-sidebar'); if (!sidebar) return; function checkScroll() { const atBottom = sidebar.scrollTop + sidebar.clientHeight >= sidebar.scrollHeight - 10; sidebar.classList.toggle('scrolled-end', atBottom); } // Check if sidebar is scrollable at all function checkScrollable() { if (sidebar.scrollHeight <= sidebar.clientHeight) { sidebar.classList.add('scrolled-end'); } else { checkScroll(); } } sidebar.addEventListener('scroll', checkScroll, { passive: true }); checkScrollable(); window.addEventListener('resize', checkScrollable); })(); // Reading Progress Bar (function() { const progressBar = document.getElementById('reading-progress-bar'); const articleBody = document.querySelector('.article-body'); if (progressBar && articleBody) { window.addEventListener('scroll', function() { const articleRect = articleBody.getBoundingClientRect(); const articleTop = articleBody.offsetTop; const articleHeight = articleBody.offsetHeight; const windowHeight = window.innerHeight; const scrolled = window.scrollY - articleTop + windowHeight; const progress = Math.min(Math.max(scrolled / articleHeight, 0), 1); progressBar.style.transform = 'scaleX(' + progress + ')'; }, { passive: true }); } })(); // Copy article link function copyArticleLink() { navigator.clipboard.writeText(window.location.href).then(function() { // Show feedback const btn = document.querySelector('.share-btn.copy'); const originalIcon = btn.innerHTML; btn.innerHTML = '<i class="fa-solid fa-check"></i>'; setTimeout(() => { btn.innerHTML = originalIcon; }, 2000); }); } // Table of contents active state on scroll document.addEventListener('DOMContentLoaded', function() { const tocItems = document.querySelectorAll('.toc-item'); const headings = document.querySelectorAll('.article-body h2'); if (headings.length > 0 && tocItems.length > 0) { window.addEventListener('scroll', function() { let current = ''; headings.forEach(heading => { const sectionTop = heading.offsetTop; if (pageYOffset >= sectionTop - 100) { current = heading.getAttribute('id'); } }); tocItems.forEach(item => { item.classList.remove('active'); if (item.getAttribute('href') === '#' + current) { item.classList.add('active'); } }); }); } // ============================================= // COMMENTS FUNCTIONALITY // ============================================= const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'); // Character count for comment textarea const commentContent = document.getElementById('comment-content'); const charCount = document.getElementById('char-count'); if (commentContent && charCount) { commentContent.addEventListener('input', function() { charCount.textContent = `${this.value.length}/2000`; }); } // Submit new comment const submitCommentBtn = document.getElementById('submit-comment'); if (submitCommentBtn) { submitCommentBtn.addEventListener('click', async function() { const articleId = this.dataset.article; const content = document.getElementById('comment-content').value.trim(); if (!content) { showToast('يرجى كتابة تعليق', 'error'); return; } if (content.length < 3) { showToast('التعليق قصير جداً', 'error'); return; } setButtonLoading(this, true); try { const response = await fetch(`/article/${articleId}/comments`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json', }, body: JSON.stringify({ content }), }); const data = await response.json(); if (data.success) { // Clear textarea document.getElementById('comment-content').value = ''; charCount.textContent = '0/2000'; // Remove "no comments" message if exists const noComments = document.getElementById('no-comments'); if (noComments) noComments.remove(); // Add new comment to the top of the list const commentsList = document.getElementById('comments-list'); commentsList.insertAdjacentHTML('afterbegin', createCommentHTML(data.comment)); // Update comments count updateCommentsCount(1); // Bind events to new comment bindCommentEvents(); showToast('تم إضافة التعليق بنجاح', 'success'); } else { showToast(data.message || 'حدث خطأ', 'error'); } } catch (error) { console.error('Error:', error); showToast('حدث خطأ في الاتصال', 'error'); } finally { setButtonLoading(this, false); } }); } // Bind events to existing comments bindCommentEvents(); function bindCommentEvents() { // Like buttons document.querySelectorAll('.like-btn').forEach(btn => { btn.removeEventListener('click', handleLikeClick); btn.addEventListener('click', handleLikeClick); }); // Reply buttons document.querySelectorAll('.reply-btn').forEach(btn => { btn.removeEventListener('click', handleReplyClick); btn.addEventListener('click', handleReplyClick); }); // Cancel reply buttons document.querySelectorAll('.cancel-reply-btn').forEach(btn => { btn.removeEventListener('click', handleCancelReply); btn.addEventListener('click', handleCancelReply); }); // Submit reply buttons document.querySelectorAll('.submit-reply-btn').forEach(btn => { btn.removeEventListener('click', handleSubmitReply); btn.addEventListener('click', handleSubmitReply); }); } async function handleLikeClick() { const commentId = this.dataset.comment; try { const response = await fetch(`/comments/${commentId}/like`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json', }, }); const data = await response.json(); if (response.status === 401) { showToast('يجب تسجيل الدخول للإعجاب', 'error'); return; } if (data.success) { const icon = this.querySelector('i'); const countSpan = this.querySelector('.likes-count'); if (data.is_liked) { this.classList.add('liked'); icon.classList.remove('fa-regular'); icon.classList.add('fa-solid'); } else { this.classList.remove('liked'); icon.classList.remove('fa-solid'); icon.classList.add('fa-regular'); } countSpan.textContent = data.likes_count; } } catch (error) { console.error('Error:', error); showToast('حدث خطأ', 'error'); } } function handleReplyClick() { const commentId = this.dataset.comment; const replyForm = document.getElementById(`reply-form-${commentId}`); // Hide all other reply forms document.querySelectorAll('.reply-form-wrapper').forEach(form => { if (form.id !== `reply-form-${commentId}`) { form.style.display = 'none'; } }); // Toggle this reply form replyForm.style.display = replyForm.style.display === 'none' ? 'block' : 'none'; if (replyForm.style.display === 'block') { replyForm.querySelector('textarea').focus(); } } function handleCancelReply() { const wrapper = this.closest('.reply-form-wrapper'); wrapper.style.display = 'none'; wrapper.querySelector('textarea').value = ''; } async function handleSubmitReply() { const commentId = this.dataset.comment; const wrapper = this.closest('.reply-form-wrapper'); const textarea = wrapper.querySelector('textarea'); const content = textarea.value.trim(); if (!content) { showToast('يرجى كتابة رد', 'error'); return; } if (content.length < 3) { showToast('الرد قصير جداً', 'error'); return; } setButtonLoading(this, true); try { const response = await fetch(`/comments/${commentId}/reply`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json', }, body: JSON.stringify({ content }), }); const data = await response.json(); if (data.success) { // Clear and hide reply form textarea.value = ''; wrapper.style.display = 'none'; // Find or create replies list const commentItem = document.querySelector(`[data-comment-id="${commentId}"]`); let repliesList = commentItem.querySelector('.replies-list'); if (!repliesList) { repliesList = document.createElement('div'); repliesList.className = 'replies-list'; repliesList.style.cssText = 'margin-top: 16px; padding-right: 20px; border-right: 2px solid var(--border-color);'; commentItem.querySelector('.comment-content').appendChild(repliesList); } // Add new reply repliesList.insertAdjacentHTML('beforeend', createReplyHTML(data.reply)); // Update comments count updateCommentsCount(1); // Bind events to new reply bindCommentEvents(); showToast('تم إضافة الرد بنجاح', 'success'); } else { showToast(data.message || 'حدث خطأ', 'error'); } } catch (error) { console.error('Error:', error); showToast('حدث خطأ في الاتصال', 'error'); } finally { setButtonLoading(this, false); } } function createCommentHTML(comment) { return ` <div class="comment-item" data-comment-id="${comment.id}"> <img src="${comment.user.avatar_url}" class="comment-avatar" alt="${comment.user.name}"> <div class="comment-content"> <div class="comment-header"> <span class="comment-author">${comment.user.name}</span> <span class="comment-time">${comment.created_at}</span> </div> <p class="comment-text">${escapeHtml(comment.content)}</p> <div class="comment-actions"> <button class="comment-action like-btn" data-comment="${comment.id}"> <i class="fa-regular fa-heart"></i> <span class="likes-count">0</span> </button> <button class="comment-action reply-btn" data-comment="${comment.id}" data-author="${comment.user.name}"> <i class="fa-regular fa-comment"></i> رد </button> </div> <div class="reply-form-wrapper" id="reply-form-${comment.id}" style="display: none; margin-top: 12px;"> <textarea class="reply-textarea" placeholder="اكتب ردك هنا..." maxlength="2000" style="width: 100%; padding: 10px; border: 1px solid var(--border-color); border-radius: 8px; resize: none; min-height: 80px;"></textarea> <div style="display: flex; gap: 8px; margin-top: 8px; justify-content: flex-end;"> <button type="button" class="cancel-reply-btn" style="padding: 6px 12px; background: var(--bg-secondary); border: none; border-radius: 6px; cursor: pointer;">إلغاء</button> <button type="button" class="submit-reply-btn" data-comment="${comment.id}" style="padding: 6px 12px; background: var(--primary); color: white; border: none; border-radius: 6px; cursor: pointer;"> <span class="btn-text">نشر الرد</span> <i class="fa-solid fa-spinner fa-spin btn-loading" style="display: none;"></i> </button> </div> </div> </div> </div> `; } function createReplyHTML(reply) { return ` <div class="comment-item reply-item" data-comment-id="${reply.id}" style="margin-bottom: 12px;"> <img src="${reply.user.avatar_url}" class="comment-avatar" style="width: 32px; height: 32px;" alt="${reply.user.name}"> <div class="comment-content"> <div class="comment-header"> <span class="comment-author">${reply.user.name}</span> <span class="comment-time">${reply.created_at}</span> </div> <p class="comment-text">${escapeHtml(reply.content)}</p> <div class="comment-actions"> <button class="comment-action like-btn" data-comment="${reply.id}"> <i class="fa-regular fa-heart"></i> <span class="likes-count">0</span> </button> </div> </div> </div> `; } function updateCommentsCount(delta) { const countEl = document.getElementById('comments-count'); if (countEl) { const currentCount = parseInt(countEl.textContent) || 0; countEl.textContent = currentCount + delta; } } function setButtonLoading(btn, loading) { const text = btn.querySelector('.btn-text'); const spinner = btn.querySelector('.btn-loading'); if (loading) { if (text) text.style.display = 'none'; if (spinner) spinner.style.display = 'inline-block'; btn.disabled = true; } else { if (text) text.style.display = 'inline'; if (spinner) spinner.style.display = 'none'; btn.disabled = false; } } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function showToast(message, type = 'info') { // Simple toast notification const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.style.cssText = ` position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); padding: 12px 24px; border-radius: 8px; color: white; font-size: 14px; z-index: 9999; animation: fadeIn 0.3s ease; background: ${type === 'success' ? '#10b981' : type === 'error' ? '#ef4444' : '#3b82f6'}; `; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { toast.style.animation = 'fadeOut 0.3s ease'; setTimeout(() => toast.remove(), 300); }, 3000); } // ============================================= // BOOKMARK FUNCTIONALITY // ============================================= const bookmarkBtn = document.getElementById('bookmark-article'); const mobileBookmarkBtn = document.getElementById('mobile-bookmark'); async function toggleBookmark(articleId) { try { const response = await fetch('/bookmarks/toggle', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json', }, body: JSON.stringify({ article_id: articleId }), }); if (response.status === 401) { showToast('يجب تسجيل الدخول لحفظ المقال', 'error'); return null; } const data = await response.json(); if (data.success) { showToast(data.message, 'success'); return data.saved; } else { showToast(data.message || 'حدث خطأ', 'error'); return null; } } catch (error) { console.error('Error toggling bookmark:', error); showToast('حدث خطأ في الاتصال', 'error'); return null; } } function updateBookmarkButtons(isSaved) { // Update desktop button if (bookmarkBtn) { const icon = bookmarkBtn.querySelector('i'); if (isSaved) { bookmarkBtn.classList.add('bookmarked'); icon.classList.remove('fa-regular'); icon.classList.add('fa-solid'); } else { bookmarkBtn.classList.remove('bookmarked'); icon.classList.remove('fa-solid'); icon.classList.add('fa-regular'); } } // Update mobile button if (mobileBookmarkBtn) { const icon = mobileBookmarkBtn.querySelector('i'); if (isSaved) { mobileBookmarkBtn.classList.add('bookmarked'); icon.classList.remove('fa-regular'); icon.classList.add('fa-solid'); } else { mobileBookmarkBtn.classList.remove('bookmarked'); icon.classList.remove('fa-solid'); icon.classList.add('fa-regular'); } } } // Desktop bookmark button if (bookmarkBtn) { bookmarkBtn.addEventListener('click', async function() { const articleId = this.dataset.article; const isSaved = await toggleBookmark(articleId); if (isSaved !== null) { updateBookmarkButtons(isSaved); } }); } // Mobile bookmark button if (mobileBookmarkBtn) { mobileBookmarkBtn.addEventListener('click', async function() { const articleId = document.getElementById('bookmark-article')?.dataset.article; if (articleId) { const isSaved = await toggleBookmark(articleId); if (isSaved !== null) { updateBookmarkButtons(isSaved); } } }); } // ============================================= // ARTICLE LIKE FUNCTIONALITY // ============================================= const likeBtn = document.getElementById('like-article'); const mobileLikeBtn = document.getElementById('mobile-like'); async function toggleArticleLike(articleId) { try { const response = await fetch(`/article/${articleId}/like`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json', }, }); if (response.status === 401) { showToast('يجب تسجيل الدخول للإعجاب', 'error'); return null; } const data = await response.json(); if (data.success) { return { isLiked: data.is_liked, likesCount: data.likes_count }; } else { showToast(data.message || 'حدث خطأ', 'error'); return null; } } catch (error) { console.error('Error toggling like:', error); showToast('حدث خطأ في الاتصال', 'error'); return null; } } function updateLikeButtons(isLiked, likesCount) { // Update desktop button if (likeBtn) { const icon = likeBtn.querySelector('i'); if (isLiked) { likeBtn.classList.add('liked'); icon.classList.remove('fa-regular'); icon.classList.add('fa-solid'); } else { likeBtn.classList.remove('liked'); icon.classList.remove('fa-solid'); icon.classList.add('fa-regular'); } } // Update mobile button if (mobileLikeBtn) { const icon = mobileLikeBtn.querySelector('i'); const countSpan = mobileLikeBtn.querySelector('span'); if (isLiked) { mobileLikeBtn.classList.add('liked'); icon.classList.remove('fa-regular'); icon.classList.add('fa-solid'); } else { mobileLikeBtn.classList.remove('liked'); icon.classList.remove('fa-solid'); icon.classList.add('fa-regular'); } if (countSpan) { countSpan.textContent = likesCount.toLocaleString('ar-EG'); } } // Update stats bar likes count const statsLikeItem = document.querySelector('.stat-item .fa-heart')?.closest('.stat-item'); if (statsLikeItem) { statsLikeItem.innerHTML = `<i class="fa-regular fa-heart"></i> ${likesCount.toLocaleString('ar-EG')} إعجاب`; } } // Desktop like button if (likeBtn) { likeBtn.addEventListener('click', async function() { const articleId = this.dataset.article; const result = await toggleArticleLike(articleId); if (result !== null) { updateLikeButtons(result.isLiked, result.likesCount); showToast(result.isLiked ? 'تم الإعجاب بالمقال' : 'تم إلغاء الإعجاب', 'success'); } }); } // Mobile like button if (mobileLikeBtn) { mobileLikeBtn.addEventListener('click', async function() { const articleId = document.getElementById('like-article')?.dataset.article; if (articleId) { const result = await toggleArticleLike(articleId); if (result !== null) { updateLikeButtons(result.isLiked, result.likesCount); showToast(result.isLiked ? 'تم الإعجاب بالمقال' : 'تم إلغاء الإعجاب', 'success'); } } }); } // ============================================= // SHARE FUNCTIONALITY // ============================================= const shareBtn = document.getElementById('share-article'); const mobileShareBtn = document.getElementById('mobile-share'); const articleTitle = "\u0623\u0633\u0644\u0648\u0628 FileFix \u064a\u062a\u062c\u0627\u0648\u0632 \u062d\u0645\u0627\u064a\u0629 \u0627\u0644\u0645\u062a\u0635\u0641\u062d\u0627\u062a \u0627\u0644\u062d\u062f\u064a\u062b\u0629"; const articleUrl = window.location.href; async function handleShare() { // Try native share API first (mobile browsers) if (navigator.share) { try { await navigator.share({ title: articleTitle, url: articleUrl }); showToast('تم فتح قائمة المشاركة', 'success'); return; } catch (err) { // User cancelled or error - fall back to custom modal if (err.name === 'AbortError') return; } } // Show custom share modal showShareModal(); } function showShareModal() { // Remove existing modal if any const existingModal = document.getElementById('share-modal'); if (existingModal) existingModal.remove(); const modal = document.createElement('div'); modal.id = 'share-modal'; modal.className = 'share-modal-overlay'; modal.innerHTML = ` <div class="share-modal"> <div class="share-modal-header"> <h3>مشاركة المقال</h3> <button class="share-modal-close" onclick="closeShareModal()"> <i class="fa-solid fa-xmark"></i> </button> </div> <div class="share-modal-body"> <div class="share-platforms"> <a href="https://twitter.com/intent/tweet?url=${encodeURIComponent(articleUrl)}&text=${encodeURIComponent(articleTitle)}" target="_blank" rel="noopener" class="share-platform twitter" onclick="shareClicked('إكس')"> <i class="fa-brands fa-x-twitter"></i> <span>إكس</span> </a> <a href="https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(articleUrl)}" target="_blank" rel="noopener" class="share-platform facebook" onclick="shareClicked('فيسبوك')"> <i class="fa-brands fa-facebook-f"></i> <span>فيسبوك</span> </a> <a href="https://www.linkedin.com/shareArticle?mini=true&url=${encodeURIComponent(articleUrl)}&title=${encodeURIComponent(articleTitle)}" target="_blank" rel="noopener" class="share-platform linkedin" onclick="shareClicked('لينكدإن')"> <i class="fa-brands fa-linkedin-in"></i> <span>لينكدإن</span> </a> <a href="https://api.whatsapp.com/send?text=${encodeURIComponent(articleTitle + ' ' + articleUrl)}" target="_blank" rel="noopener" class="share-platform whatsapp" onclick="shareClicked('واتساب')"> <i class="fa-brands fa-whatsapp"></i> <span>واتساب</span> </a> <a href="https://t.me/share/url?url=${encodeURIComponent(articleUrl)}&text=${encodeURIComponent(articleTitle)}" target="_blank" rel="noopener" class="share-platform telegram" onclick="shareClicked('تيليجرام')"> <i class="fa-brands fa-telegram"></i> <span>تيليجرام</span> </a> <button class="share-platform copy" onclick="copyLinkFromModal()"> <i class="fa-solid fa-link"></i> <span>نسخ الرابط</span> </button> </div> </div> </div> `; document.body.appendChild(modal); // Close on overlay click modal.addEventListener('click', function(e) { if (e.target === modal) { closeShareModal(); } }); // Close on escape key document.addEventListener('keydown', function escHandler(e) { if (e.key === 'Escape') { closeShareModal(); document.removeEventListener('keydown', escHandler); } }); } window.closeShareModal = function() { const modal = document.getElementById('share-modal'); if (modal) { modal.classList.add('closing'); setTimeout(() => modal.remove(), 200); } }; window.shareClicked = function(platform) { showToast(`جاري المشاركة على ${platform}`, 'success'); setTimeout(closeShareModal, 500); }; window.copyLinkFromModal = function() { navigator.clipboard.writeText(articleUrl).then(() => { showToast('تم نسخ الرابط بنجاح', 'success'); closeShareModal(); }).catch(() => { showToast('فشل نسخ الرابط', 'error'); }); }; // Desktop share button if (shareBtn) { shareBtn.addEventListener('click', handleShare); } // Mobile share button if (mobileShareBtn) { mobileShareBtn.addEventListener('click', handleShare); } // ============================================= // HANDLE BROKEN IMAGES IN ARTICLE CONTENT // ============================================= (function handleBrokenImages() { const articleBody = document.querySelector('.article-body'); if (!articleBody) return; const defaultImage = 'https://w3professional.com/assets/pictures/default.png'; // Find all images in the article body const images = articleBody.querySelectorAll('img'); images.forEach(img => { // Handle image load error img.onerror = function() { // Check if image is inside a link that only contains the image const parent = this.parentElement; if (parent && parent.tagName === 'A') { // Check if link only contains this image (image link) const hasOnlyImage = parent.children.length === 1 && parent.textContent.trim() === ''; if (hasOnlyImage) { // Hide the entire link with the broken image parent.style.display = 'none'; } else { // Link has other content, just hide the image this.style.display = 'none'; } } else { // Standalone image - hide it this.style.display = 'none'; } this.onerror = null; // Prevent infinite loop }; // Check if image already failed to load (cached failure) if (img.complete && img.naturalHeight === 0) { img.onerror(); } }); // Also handle links that look like image URLs but aren't wrapped in img tags // (sometimes broken images show as raw URLs) const links = articleBody.querySelectorAll('a'); links.forEach(link => { const href = link.getAttribute('href') || ''; const text = link.textContent.trim(); // Check if link text is the same as href and looks like an image URL const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp']; const isImageUrl = imageExtensions.some(ext => href.toLowerCase().includes(ext) || text.toLowerCase().includes(ext) ); // If the link text equals the href and it's an image URL, it's likely a broken image link if (isImageUrl && (text === href || text.includes('http'))) { // Check if this link has no other meaningful content if (link.children.length === 0 || (link.children.length === 1 && link.children[0].tagName === 'IMG')) { link.style.display = 'none'; } } }); })(); // ============================================= // RELATED ARTICLES SLIDER FUNCTIONALITY // ============================================= // Initialize slider navigation for all sliders function initSliderNavigation(slider) { const track = slider.querySelector('.related-slider__track'); const prevBtn = slider.querySelector('.related-slider__nav--prev'); const nextBtn = slider.querySelector('.related-slider__nav--next'); if (!track || !prevBtn || !nextBtn) return; const cardWidth = 296; // Card width (280) + gap (16) // Update button states based on scroll position function updateNavButtons() { const scrollLeft = track.scrollLeft; const maxScroll = track.scrollWidth - track.clientWidth; // RTL: scrollLeft is negative or inverted const isRTL = getComputedStyle(track).direction === 'rtl'; if (isRTL) { // In RTL, scrollLeft starts at 0 and goes negative prevBtn.disabled = scrollLeft >= 0; nextBtn.disabled = Math.abs(scrollLeft) >= maxScroll - 10; } else { prevBtn.disabled = scrollLeft <= 10; nextBtn.disabled = scrollLeft >= maxScroll - 10; } } // Scroll handlers prevBtn.addEventListener('click', () => { track.scrollBy({ left: cardWidth, behavior: 'smooth' }); }); nextBtn.addEventListener('click', () => { track.scrollBy({ left: -cardWidth, behavior: 'smooth' }); }); // Update buttons on scroll track.addEventListener('scroll', updateNavButtons); // Initial state updateNavButtons(); } // Initialize all sliders on the page document.querySelectorAll('.related-slider').forEach(initSliderNavigation); // ============================================= // FAQ ACCORDION FUNCTIONALITY // ============================================= (function initFaqAccordion() { const faqItems = document.querySelectorAll('.faq-item'); faqItems.forEach(item => { const question = item.querySelector('.faq-question'); if (question) { question.addEventListener('click', function() { const isActive = item.classList.contains('active'); // Close all other FAQ items faqItems.forEach(otherItem => { if (otherItem !== item) { otherItem.classList.remove('active'); const otherBtn = otherItem.querySelector('.faq-question'); if (otherBtn) { otherBtn.setAttribute('aria-expanded', 'false'); } } }); // Toggle current item item.classList.toggle('active'); question.setAttribute('aria-expanded', !isActive); }); } }); })(); // ============================================= // CYBER SLIDER NAVIGATION // ============================================= (function initCyberSlider() { const track = document.getElementById('cyber-slider-track'); const prevBtn = document.querySelector('.cyber-slider-nav--prev'); const nextBtn = document.querySelector('.cyber-slider-nav--next'); if (!track || !prevBtn || !nextBtn) return; const cardWidth = 276; // Card width (260) + gap (16) // Update button states based on scroll position function updateNavButtons() { const scrollLeft = track.scrollLeft; const maxScroll = track.scrollWidth - track.clientWidth; // RTL handling const isRTL = getComputedStyle(track).direction === 'rtl'; if (isRTL) { prevBtn.disabled = scrollLeft >= 0; nextBtn.disabled = Math.abs(scrollLeft) >= maxScroll - 10; } else { prevBtn.disabled = scrollLeft <= 10; nextBtn.disabled = scrollLeft >= maxScroll - 10; } } // Scroll handlers prevBtn.addEventListener('click', () => { track.scrollBy({ left: cardWidth, behavior: 'smooth' }); }); nextBtn.addEventListener('click', () => { track.scrollBy({ left: -cardWidth, behavior: 'smooth' }); }); // Update buttons on scroll track.addEventListener('scroll', updateNavButtons); // Initial state updateNavButtons(); })(); }); </script> <style> @keyframes fadeIn { from { opacity: 0; transform: translateX(-50%) translateY(20px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } } @keyframes fadeOut { from { opacity: 1; transform: translateX(-50%) translateY(0); } to { opacity: 0; transform: translateX(-50%) translateY(20px); } } /* Share Modal Styles */ .share-modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; z-index: 9999; animation: modalFadeIn 0.2s ease; } .share-modal-overlay.closing { animation: modalFadeOut 0.2s ease forwards; } @keyframes modalFadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes modalFadeOut { from { opacity: 1; } to { opacity: 0; } } .share-modal { background: var(--bg-card, #fff); border-radius: 16px; width: 90%; max-width: 400px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); animation: modalSlideIn 0.3s ease; } .share-modal-overlay.closing .share-modal { animation: modalSlideOut 0.2s ease forwards; } @keyframes modalSlideIn { from { opacity: 0; transform: scale(0.9) translateY(20px); } to { opacity: 1; transform: scale(1) translateY(0); } } @keyframes modalSlideOut { from { opacity: 1; transform: scale(1) translateY(0); } to { opacity: 0; transform: scale(0.9) translateY(20px); } } .share-modal-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--border-color, #e5e7eb); } .share-modal-header h3 { margin: 0; font-size: 16px; font-weight: 700; color: var(--text-primary, #1f2937); } .share-modal-close { width: 32px; height: 32px; border: none; background: var(--bg-hover, #f3f4f6); border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--text-secondary, #6b7280); transition: all 0.2s; } .share-modal-close:hover { background: var(--red-500, #ef4444); color: #fff; } .share-modal-body { padding: 20px; } .share-platforms { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; } .share-platform { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 16px 12px; border: 1px solid var(--border-color, #e5e7eb); border-radius: 12px; text-decoration: none; color: var(--text-primary, #1f2937); background: var(--bg-card, #fff); cursor: pointer; transition: all 0.2s; font-family: inherit; font-size: 12px; font-weight: 600; } .share-platform:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } .share-platform i { font-size: 22px; } .share-platform.twitter:hover { background: #000; color: #fff; border-color: #000; } .share-platform.facebook:hover { background: #1877f2; color: #fff; border-color: #1877f2; } .share-platform.linkedin:hover { background: #0a66c2; color: #fff; border-color: #0a66c2; } .share-platform.whatsapp:hover { background: #25d366; color: #fff; border-color: #25d366; } .share-platform.telegram:hover { background: #0088cc; color: #fff; border-color: #0088cc; } .share-platform.copy:hover { background: var(--blue-500, #3b82f6); color: #fff; border-color: var(--blue-500, #3b82f6); } @media (max-width: 400px) { .share-platforms { grid-template-columns: repeat(2, 1fr); } } /* ============================================= EXCERPT LINK STYLES (Smart Internal Linking) ============================================= */ .ai-summary-text a.excerpt-link { color: var(--primary, #3b82f6); text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 2px; font-weight: 500; transition: all 0.2s ease; } .ai-summary-text a.excerpt-link:hover { color: var(--primary-dark, #2563eb); text-decoration-style: solid; } /* Dark mode support */ [data-theme="dark"] .ai-summary-text a.excerpt-link { color: var(--blue-400, #60a5fa); } [data-theme="dark"] .ai-summary-text a.excerpt-link:hover { color: var(--blue-300, #93c5fd); } /* ============================================= FAQ SECTION STYLES ============================================= */ .faq-section { margin: 32px 0; padding: 24px; background: var(--bg-card, #fff); border-radius: 16px; border: 1px solid var(--border-color, #e5e7eb); } .faq-section-title { display: flex; align-items: center; gap: 10px; font-size: 18px; font-weight: 700; color: var(--text-primary, #1f2937); margin: 0 0 20px 0; padding-bottom: 16px; border-bottom: 1px solid var(--border-color, #e5e7eb); } .faq-section-title i { color: var(--primary, #3b82f6); font-size: 20px; } .faq-list { display: flex; flex-direction: column; gap: 12px; } .faq-item { border: 1px solid var(--border-color, #e5e7eb); border-radius: 12px; overflow: hidden; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); } .faq-item:hover { border-color: var(--primary, #3b82f6); } .faq-item.active { border-color: var(--primary, #3b82f6); box-shadow: 0 4px 16px rgba(59, 130, 246, 0.12); border-right: 3px solid var(--primary, #3b82f6); } .faq-question { width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 20px; background: var(--bg-secondary, #f9fafb); border: none; cursor: pointer; text-align: right; font-size: 15px; font-weight: 600; color: var(--text-primary, #1f2937); transition: all 0.2s ease; font-family: inherit; } .faq-question:hover { background: var(--bg-hover, #f3f4f6); } .faq-icon { flex-shrink: 0; font-size: 12px; color: var(--text-muted, #9ca3af); transition: transform 0.3s ease; } .faq-item.active .faq-icon { transform: rotate(180deg); color: var(--primary, #3b82f6); } .faq-answer { max-height: 0; overflow: hidden; transition: max-height 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94), padding 0.3s ease; } .faq-item.active .faq-answer { max-height: 500px; } .faq-answer p { padding: 16px 20px; margin: 0; font-size: 14px; line-height: 1.8; color: var(--text-secondary, #6b7280); background: var(--bg-card, #fff); } /* ============================================= ENHANCED TOC STYLES ============================================= */ .toc-card .sidebar-card-title { display: flex; align-items: center; gap: 8px; } .toc-card .sidebar-card-title i { color: var(--primary, #3b82f6); } .toc-nav { display: flex; flex-direction: column; gap: 4px; } .toc-item { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 8px; font-size: 13px; color: var(--text-secondary, #6b7280); text-decoration: none; transition: all 0.2s ease; position: relative; } .toc-item:hover { background: var(--bg-hover, #f3f4f6); color: var(--text-primary, #1f2937); } .toc-item.active { background: rgba(59, 130, 246, 0.1); color: var(--primary, #3b82f6); font-weight: 600; } .toc-indicator { width: 6px; height: 6px; border-radius: 50%; background: var(--text-muted, #9ca3af); flex-shrink: 0; transition: all 0.2s ease; } .toc-item.active .toc-indicator { background: var(--primary, #3b82f6); box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2); } .toc-item:hover .toc-indicator { background: var(--primary, #3b82f6); } /* Mobile FAQ adjustments */ @media (max-width: 768px) { .faq-section { padding: 16px; margin: 24px 0; } .faq-question { padding: 14px 16px; font-size: 14px; } .faq-answer p { padding: 14px 16px; font-size: 13px; } } /* ============================================= FAQ DARK MODE ============================================= */ body.dark-mode .faq-section { background: var(--bg-card, #1e293b); border-color: var(--border-default, #334155); } body.dark-mode .faq-section-title { color: var(--text-primary, #f8fafc); border-bottom-color: var(--border-default, #334155); } body.dark-mode .faq-item { border-color: var(--border-default, #334155); } body.dark-mode .faq-item:hover { border-color: var(--blue-500, #3b82f6); } body.dark-mode .faq-item.active { border-color: var(--blue-500, #3b82f6); box-shadow: 0 4px 16px rgba(59, 130, 246, 0.15); } body.dark-mode .faq-question { background: #283548; color: var(--text-primary, #f8fafc); } body.dark-mode .faq-question:hover { background: #334155; } body.dark-mode .faq-icon { color: var(--text-secondary, #94a3b8); } body.dark-mode .faq-answer p { background: var(--bg-card, #1e293b); color: var(--text-secondary, #94a3b8); } /* Share platforms dark mode */ body.dark-mode .share-platform { background: var(--bg-card, #1e293b); border-color: var(--border-default, #334155); color: var(--text-primary, #f8fafc); } /* TOC dark mode */ body.dark-mode .toc-item:hover { background: var(--bg-hover, #334155); color: var(--text-primary, #f8fafc); } </style> <script> document.addEventListener('DOMContentLoaded', function() { const pollWidget = document.getElementById('poll-widget'); const pollTrigger = document.getElementById('poll-trigger'); const pollModal = document.getElementById('poll-modal'); const pollClose = document.getElementById('poll-close'); const pollOptions = document.getElementById('poll-options'); const pollResults = document.getElementById('poll-results'); const pollSubmit = document.getElementById('poll-submit'); const pollVotedMessage = document.getElementById('poll-voted-message'); const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'); let currentPoll = null; let selectedOptions = []; let requiresLogin = false; let isAuthenticated = false; // Fetch active poll on page load fetchActivePoll(); async function fetchActivePoll() { try { const response = await fetch('/poll/active', { headers: { 'Accept': 'application/json' } }); const data = await response.json(); if (data.success && data.poll) { currentPoll = data.poll; requiresLogin = data.requires_login || false; isAuthenticated = data.is_authenticated || false; pollWidget.style.display = 'flex'; renderPoll(data.poll, data.has_voted, data.user_votes); } } catch (error) { console.error('Failed to fetch poll:', error); } } function renderPoll(poll, hasVoted, userVotes = []) { document.getElementById('poll-title').textContent = poll.title; document.getElementById('poll-description').textContent = poll.description || ''; document.getElementById('poll-participants').textContent = poll.total_participants.toLocaleString(); document.getElementById('poll-votes').textContent = poll.total_votes.toLocaleString(); if (hasVoted) { showResults(poll.options, userVotes); pollVotedMessage.style.display = 'flex'; pollSubmit.style.display = 'none'; } else if (requiresLogin) { // User needs to login to vote - show options but intercept clicks renderOptionsWithLoginRequired(poll); } else { renderOptions(poll); } } // Show login required alert and open login modal function promptLogin() { showToast('يجب تسجيل الدخول للتصويت في هذا الاستطلاع', 'warning'); // Close poll modal after short delay setTimeout(() => { closeModal(); }, 1500); // Open login modal after poll modal closes setTimeout(() => { if (typeof window.openModal === 'function') { window.openModal('login'); } }, 2000); } // Render options with login requirement overlay function renderOptionsWithLoginRequired(poll) { pollOptions.innerHTML = ''; pollOptions.style.display = 'flex'; pollOptions.style.flexDirection = 'column'; pollOptions.style.gap = '12px'; pollResults.style.display = 'none'; pollSubmit.style.display = 'none'; const inputType = poll.multiple_choice ? 'checkbox' : 'radio'; poll.options.forEach((option, index) => { // Create label container const label = document.createElement('label'); label.className = 'poll-option'; label.style.cssText = 'display: block; cursor: pointer; position: relative;'; // Create option box (no hidden input - we'll intercept clicks) const box = document.createElement('span'); box.className = 'poll-option-box'; box.style.cssText = 'display: flex; align-items: center; gap: 14px; padding: 16px 20px; background: var(--bg-secondary, #f1f5f9); border: 2px solid var(--border-color, #e2e8f0); border-radius: 14px; transition: all 0.2s ease;'; // Create indicator (radio/checkbox visual) const indicator = document.createElement('span'); indicator.className = 'poll-option-indicator'; indicator.style.cssText = 'width: 22px; height: 22px; min-width: 22px; border: 2px solid var(--border-color, #cbd5e1); border-radius: ' + (inputType === 'checkbox' ? '6px' : '50%') + '; display: flex; align-items: center; justify-content: center; flex-shrink: 0; background: transparent;'; // Create text const text = document.createElement('span'); text.className = 'poll-option-text'; text.style.cssText = 'flex: 1; font-size: 15px; font-weight: 500; color: var(--text-primary, #1e293b); line-height: 1.4;'; text.textContent = option.text; // Assemble box.appendChild(indicator); box.appendChild(text); label.appendChild(box); pollOptions.appendChild(label); // Hover effect label.addEventListener('mouseenter', () => { box.style.borderColor = '#6366f1'; box.style.background = 'var(--bg-hover, #e2e8f0)'; }); label.addEventListener('mouseleave', () => { box.style.borderColor = 'var(--border-color, #e2e8f0)'; box.style.background = 'var(--bg-secondary, #f1f5f9)'; }); // Click handler - prompt login label.addEventListener('click', (e) => { e.preventDefault(); promptLogin(); }); }); // Add login prompt message const loginNote = document.createElement('div'); loginNote.className = 'poll-login-note'; loginNote.style.cssText = 'display: flex; align-items: center; justify-content: center; gap: 8px; padding: 12px 16px; background: rgba(99, 102, 241, 0.1); border: 1px solid rgba(99, 102, 241, 0.3); border-radius: 10px; margin-top: 8px; cursor: pointer;'; loginNote.innerHTML = '<i class="fa-solid fa-right-to-bracket" style="color: #6366f1;"></i><span style="font-size: 14px; color: #6366f1; font-weight: 500;">سجل دخولك للمشاركة في التصويت</span>'; loginNote.addEventListener('click', promptLogin); pollOptions.appendChild(loginNote); } function renderOptions(poll) { pollOptions.innerHTML = ''; pollOptions.style.display = 'flex'; pollOptions.style.flexDirection = 'column'; pollOptions.style.gap = '12px'; pollResults.style.display = 'none'; const inputType = poll.multiple_choice ? 'checkbox' : 'radio'; poll.options.forEach((option, index) => { // Create label container const label = document.createElement('label'); label.className = 'poll-option'; label.style.cssText = 'display: block; cursor: pointer; position: relative;'; // Create hidden input const input = document.createElement('input'); input.type = inputType; input.name = 'poll_option'; input.value = option.id; input.className = 'poll-option-input'; input.style.cssText = 'position: absolute; opacity: 0; pointer-events: none;'; // Create option box const box = document.createElement('span'); box.className = 'poll-option-box'; box.style.cssText = 'display: flex; align-items: center; gap: 14px; padding: 16px 20px; background: var(--bg-secondary, #f1f5f9); border: 2px solid var(--border-color, #e2e8f0); border-radius: 14px; transition: all 0.2s ease;'; // Create indicator (radio/checkbox visual) const indicator = document.createElement('span'); indicator.className = 'poll-option-indicator'; indicator.style.cssText = 'width: 22px; height: 22px; min-width: 22px; border: 2px solid var(--border-color, #cbd5e1); border-radius: ' + (inputType === 'checkbox' ? '6px' : '50%') + '; display: flex; align-items: center; justify-content: center; flex-shrink: 0; background: transparent;'; // Create text const text = document.createElement('span'); text.className = 'poll-option-text'; text.style.cssText = 'flex: 1; font-size: 15px; font-weight: 500; color: var(--text-primary, #1e293b); line-height: 1.4;'; text.textContent = option.text; // Assemble box.appendChild(indicator); box.appendChild(text); label.appendChild(input); label.appendChild(box); pollOptions.appendChild(label); // Hover effect label.addEventListener('mouseenter', () => { if (!input.checked) { box.style.borderColor = '#6366f1'; box.style.background = 'var(--bg-hover, #e2e8f0)'; } }); label.addEventListener('mouseleave', () => { if (!input.checked) { box.style.borderColor = 'var(--border-color, #e2e8f0)'; box.style.background = 'var(--bg-secondary, #f1f5f9)'; } }); // Selection handler input.addEventListener('change', () => { // Update selected state visually pollOptions.querySelectorAll('.poll-option').forEach(opt => { const optInput = opt.querySelector('input'); const optBox = opt.querySelector('.poll-option-box'); const optIndicator = opt.querySelector('.poll-option-indicator'); if (optInput.checked) { optBox.style.borderColor = '#6366f1'; optBox.style.background = 'rgba(99, 102, 241, 0.1)'; optIndicator.style.borderColor = '#6366f1'; optIndicator.style.background = '#6366f1'; optIndicator.innerHTML = inputType === 'checkbox' ? '<i class="fa-solid fa-check" style="color: #fff; font-size: 12px;"></i>' : '<span style="width: 8px; height: 8px; background: #fff; border-radius: 50%; display: block;"></span>'; } else { optBox.style.borderColor = 'var(--border-color, #e2e8f0)'; optBox.style.background = 'var(--bg-secondary, #f1f5f9)'; optIndicator.style.borderColor = 'var(--border-color, #cbd5e1)'; optIndicator.style.background = 'transparent'; optIndicator.innerHTML = ''; } }); // Update selectedOptions array if (poll.multiple_choice) { const checked = pollOptions.querySelectorAll('.poll-option-input:checked'); selectedOptions = Array.from(checked).map(el => parseInt(el.value)); // Enforce max selections if (poll.max_selections && checked.length > poll.max_selections) { input.checked = false; selectedOptions = selectedOptions.filter(id => id !== parseInt(input.value)); // Reset visual state box.style.borderColor = 'var(--border-color, #e2e8f0)'; box.style.background = 'var(--bg-secondary, #f1f5f9)'; indicator.style.borderColor = 'var(--border-color, #cbd5e1)'; indicator.style.background = 'transparent'; indicator.innerHTML = ''; showToast(`يمكنك اختيار ${poll.max_selections} خيارات كحد أقصى`, 'warning'); } } else { selectedOptions = [parseInt(input.value)]; } pollSubmit.style.display = selectedOptions.length > 0 ? 'flex' : 'none'; }); }); // Add max note if multiple choice if (poll.multiple_choice && poll.max_selections > 1) { const note = document.createElement('p'); note.className = 'poll-max-note'; note.style.cssText = 'font-size: 12px; color: var(--text-muted, #94a3b8); text-align: center; margin-top: 8px;'; note.textContent = `يمكنك اختيار ${poll.max_selections} خيارات كحد أقصى`; pollOptions.appendChild(note); } } function showResults(options, userVotes = []) { // Hide options completely pollOptions.style.display = 'none'; pollOptions.innerHTML = ''; // Show results pollResults.style.display = 'block'; pollResults.innerHTML = ''; options.forEach(option => { const isUserVote = userVotes.includes(option.id); const resultEl = document.createElement('div'); resultEl.className = `poll-result-item ${isUserVote ? 'user-voted' : ''}`; resultEl.innerHTML = ` <div class="poll-result-header"> <span class="poll-result-text"> ${isUserVote ? '<i class="fa-solid fa-check-circle"></i>' : ''} ${option.text} </span> <span class="poll-result-percentage">${option.percentage}%</span> </div> <div class="poll-result-bar"> <div class="poll-result-fill" style="width: ${option.percentage}%"></div> </div> <span class="poll-result-votes">${option.votes_count.toLocaleString()} صوت</span> `; pollResults.appendChild(resultEl); }); } // Submit vote pollSubmit.addEventListener('click', async function() { if (selectedOptions.length === 0) return; pollSubmit.disabled = true; pollSubmit.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> جاري التصويت...'; try { const response = await fetch(`/poll/${currentPoll.id}/vote`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json', }, body: JSON.stringify({ option_ids: selectedOptions }) }); const data = await response.json(); if (data.success) { showToast(data.message, 'success'); showResults(data.results, selectedOptions); document.getElementById('poll-participants').textContent = data.total_participants.toLocaleString(); document.getElementById('poll-votes').textContent = data.total_votes.toLocaleString(); pollSubmit.style.display = 'none'; pollVotedMessage.style.display = 'flex'; } else if (response.status === 401) { // User needs to login showToast('يجب تسجيل الدخول للتصويت', 'warning'); pollSubmit.disabled = false; pollSubmit.innerHTML = '<i class="fa-solid fa-check-to-slot"></i> تأكيد التصويت'; // Close poll modal and open login modal setTimeout(() => { closeModal(); }, 1500); setTimeout(() => { if (typeof window.openModal === 'function') { window.openModal('login'); } }, 2000); } else { showToast(data.message, 'error'); pollSubmit.disabled = false; pollSubmit.innerHTML = '<i class="fa-solid fa-check-to-slot"></i> تأكيد التصويت'; } } catch (error) { showToast('حدث خطأ أثناء التصويت', 'error'); pollSubmit.disabled = false; pollSubmit.innerHTML = '<i class="fa-solid fa-check-to-slot"></i> تأكيد التصويت'; } }); // Open modal pollTrigger.addEventListener('click', function() { pollModal.style.display = 'flex'; document.body.style.overflow = 'hidden'; }); // Close modal function closeModal() { pollModal.style.display = 'none'; document.body.style.overflow = ''; } pollClose.addEventListener('click', closeModal); pollModal.addEventListener('click', function(e) { if (e.target === pollModal) closeModal(); }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && pollModal.style.display === 'flex') closeModal(); }); // Toast helper function showToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = `poll-toast poll-toast-${type}`; toast.innerHTML = `<i class="fa-solid fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}"></i> ${message}`; document.body.appendChild(toast); setTimeout(() => { toast.classList.add('poll-toast-hide'); setTimeout(() => toast.remove(), 300); }, 3000); } }); </script> </body> </html>